Use snack status for non-blocking wallet operations
build / Build Linux wallet (push) Successful in 2m14s
build / Build Windows wallet (push) Has been cancelled

This commit is contained in:
Codex Bot
2026-04-20 01:24:06 +02:00
parent 517962bf67
commit aa5d4542b7
2 changed files with 92 additions and 18 deletions
+42 -4
View File
@@ -42,6 +42,8 @@ class _PeyaAppState extends ConsumerState<PeyaApp> with WindowListener {
GlobalKey<ScaffoldMessengerState>();
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
String? _lastErrorMessage;
String? _lastStatusMessage;
bool _statusSnackVisible = false;
bool _desktopCloseHookInitialized = false;
bool _handlingWindowClose = false;
@@ -70,6 +72,7 @@ class _PeyaAppState extends ConsumerState<PeyaApp> with WindowListener {
});
_walletSubscription = ref
.listenManual<WalletState>(walletControllerProvider, (previous, next) {
_handleOperationStatusChange(previous, next);
if (previous?.walletInfo == null && next.walletInfo != null) {
final config = ref.read(appConfigControllerProvider);
_connectNodeIfPossible(config);
@@ -86,6 +89,40 @@ class _PeyaAppState extends ConsumerState<PeyaApp> with WindowListener {
});
}
void _handleOperationStatusChange(WalletState? previous, WalletState next) {
final messenger = _scaffoldMessengerKey.currentState;
if (messenger == null) {
return;
}
final shouldShowStatus = next.isLoading &&
!next.isBlockingOperation &&
next.operationMessage != null &&
next.operationMessage!.isNotEmpty;
if (shouldShowStatus) {
final message = next.operationMessage!;
if (_statusSnackVisible && _lastStatusMessage == message) {
return;
}
messenger.hideCurrentSnackBar();
messenger.showSnackBar(
SnackBar(
content: Text(message),
duration: const Duration(days: 1),
),
);
_statusSnackVisible = true;
_lastStatusMessage = message;
return;
}
if (_statusSnackVisible) {
messenger.hideCurrentSnackBar();
_statusSnackVisible = false;
_lastStatusMessage = null;
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
@@ -653,7 +690,7 @@ class _RootRouter extends ConsumerWidget {
return Stack(
children: [
child,
if (state.isLoading)
if (state.isLoading && state.isBlockingOperation)
Positioned.fill(
child: ColoredBox(
color: const Color(0x66000000),
@@ -670,9 +707,10 @@ class _RootRouter extends ConsumerWidget {
const CircularProgressIndicator(),
const SizedBox(height: 16),
Text(
AppLocalizations.of(context)
?.walletOperationInProgress ??
'Working...',
state.operationMessage ??
AppLocalizations.of(context)
?.walletOperationInProgress ??
'Working...',
),
],
),
+50 -14
View File
@@ -13,6 +13,8 @@ import '../domain/transactions.dart';
import '../services/wallet_cache_recovery.dart';
class WalletState {
static const Object _sentinel = Object();
const WalletState({
required this.walletInfo,
required this.balanceAtomic,
@@ -21,6 +23,8 @@ class WalletState {
required this.subaddresses,
required this.syncStatus,
required this.isLoading,
required this.isBlockingOperation,
required this.operationMessage,
required this.isSyncing,
required this.error,
});
@@ -32,6 +36,8 @@ class WalletState {
final List<SubaddressInfo> subaddresses;
final SyncStatus syncStatus;
final bool isLoading;
final bool isBlockingOperation;
final String? operationMessage;
final bool isSyncing;
final String? error;
@@ -43,6 +49,8 @@ class WalletState {
List<SubaddressInfo>? subaddresses,
SyncStatus? syncStatus,
bool? isLoading,
bool? isBlockingOperation,
Object? operationMessage = _sentinel,
bool? isSyncing,
String? error,
}) {
@@ -54,6 +62,10 @@ class WalletState {
subaddresses: subaddresses ?? this.subaddresses,
syncStatus: syncStatus ?? this.syncStatus,
isLoading: isLoading ?? this.isLoading,
isBlockingOperation: isBlockingOperation ?? this.isBlockingOperation,
operationMessage: identical(operationMessage, _sentinel)
? this.operationMessage
: operationMessage as String?,
isSyncing: isSyncing ?? this.isSyncing,
error: error,
);
@@ -68,6 +80,8 @@ class WalletState {
subaddresses: const [],
syncStatus: SyncStatus.initial(),
isLoading: false,
isBlockingOperation: false,
operationMessage: null,
isSyncing: false,
error: null,
);
@@ -129,7 +143,7 @@ class WalletController extends StateNotifier<WalletState> {
} finally {
await _cacheRecovery.clearOpenAttempt();
}
});
}, message: 'Creating wallet...');
}
Future<String> prepareWalletSeedDraft({
@@ -155,7 +169,7 @@ class WalletController extends StateNotifier<WalletState> {
await tempDir.delete(recursive: true);
}
}
});
}, message: 'Preparing seed backup...');
}
Future<void> restoreWalletFromSeed({
@@ -179,7 +193,7 @@ class WalletController extends StateNotifier<WalletState> {
} finally {
await _cacheRecovery.clearOpenAttempt();
}
});
}, message: 'Restoring wallet...');
}
Future<void> openWallet({
@@ -196,7 +210,7 @@ class WalletController extends StateNotifier<WalletState> {
} finally {
await _cacheRecovery.clearOpenAttempt();
}
});
}, message: 'Opening wallet...');
}
Future<bool> verifyWalletPassword({
@@ -217,7 +231,7 @@ class WalletController extends StateNotifier<WalletState> {
Future<void> connectNode(NodeConfig nodeConfig) async {
await _runWithLoading(() async {
await _repository.connectNode(nodeConfig);
});
}, blocking: false, message: 'Connecting to node...');
}
Future<void> switchWallet() async {
@@ -237,7 +251,11 @@ class WalletController extends StateNotifier<WalletState> {
if (state.walletInfo == null) {
throw StateError('No wallet loaded');
}
return _runWithLoading(() => _repository.createSubaddress(label: label));
return _runWithLoading(
() => _repository.createSubaddress(label: label),
blocking: false,
message: 'Creating subaddress...',
);
}
Future<void> setSubaddressLabel({
@@ -254,7 +272,7 @@ class WalletController extends StateNotifier<WalletState> {
addressIndex: addressIndex,
label: label,
);
});
}, blocking: false, message: 'Updating subaddress...');
await refreshSubaddresses();
}
@@ -270,7 +288,11 @@ class WalletController extends StateNotifier<WalletState> {
if (state.walletInfo == null) {
throw StateError('No wallet loaded');
}
return _runWithLoading(() => _repository.prepareSend(request));
return _runWithLoading(
() => _repository.prepareSend(request),
blocking: false,
message: 'Preparing transaction...',
);
}
Future<SendResult> commitPreparedSend() async {
@@ -281,7 +303,7 @@ class WalletController extends StateNotifier<WalletState> {
final result = await _repository.commitPreparedSend();
await _refreshSnapshot();
return result;
});
}, blocking: false, message: 'Submitting transaction...');
}
Future<void> discardPreparedSend() async {
@@ -300,7 +322,7 @@ class WalletController extends StateNotifier<WalletState> {
amountAtomic: amountAtomic,
accountIndex: accountIndex,
);
});
}, blocking: false, message: 'Preparing stake...');
}
Future<Map<String, StakeYieldInfo>> getActiveStakeYields({
@@ -324,7 +346,7 @@ class WalletController extends StateNotifier<WalletState> {
final result = await _repository.commitPreparedStake();
await _refreshSnapshot();
return result;
});
}, blocking: false, message: 'Submitting stake...');
}
Future<void> discardPreparedStake() async {
@@ -384,6 +406,7 @@ class WalletController extends StateNotifier<WalletState> {
syncStatus: SyncStatus.initial(),
transactions: const [],
subaddresses: subaddresses,
operationMessage: null,
error: null,
);
}
@@ -414,8 +437,17 @@ class WalletController extends StateNotifier<WalletState> {
return snapshot;
}
Future<T> _runWithLoading<T>(Future<T> Function() action) async {
state = state.copyWith(isLoading: true, error: null);
Future<T> _runWithLoading<T>(
Future<T> Function() action, {
bool blocking = true,
String? message,
}) async {
state = state.copyWith(
isLoading: true,
isBlockingOperation: blocking,
operationMessage: message,
error: null,
);
try {
await SchedulerBinding.instance.endOfFrame;
return await action();
@@ -424,7 +456,11 @@ class WalletController extends StateNotifier<WalletState> {
state = state.copyWith(error: error.toString());
rethrow;
} finally {
state = state.copyWith(isLoading: false);
state = state.copyWith(
isLoading: false,
isBlockingOperation: false,
operationMessage: null,
);
}
}