Correctly show sync status in continuous sync notification
What changed, and why it matters
This commit fixes a small user-interface timing bug in the Skylight Wallet mobile app. It makes the ongoing background-sync notification show 'Wallet up to date' only when the wallet is actually caught up, instead of briefly showing it too early or staying stuck on 'Syncing…' when the wallet is already synced. There is no security-relevant change here.
No security action needed. Treat as a normal bug-fix/UI improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces an AppWallet extension isFullySynced that requires isConnected, isSynced, and a positive syncedHeight. It replaces two places that manually checked the same three conditions (wallet_home.dart and foreground_sync_service.dart) with the extension getter. It also threads the caller’s current sync state into startForegroundSync({bool synced = false}) so the foreground service notification is seeded correctly at startup, avoiding a stale ‘Syncing…’ message for up to 30 seconds. The changes are purely UI/UX and notification-state consistency.
Changed components
lib/models/app_wallet.dartlib/screens/wallet_home.dartlib/services/foreground_sync_service.dartlib/widgets/connection_settings_form.dartInspect captured patch +19 / −7
diff --git a/lib/models/app_wallet.dart b/lib/models/app_wallet.dart
index e6da35c..9ae9f8f 100644
--- a/lib/models/app_wallet.dart
+++ b/lib/models/app_wallet.dart
@@ -118,3 +118,11 @@ abstract interface class AppWallet implements Listenable {
Future<void> commitTx(AppPendingTx tx, String destinationAddress);
Future<ResolvedOpenAlias?> resolveOpenAlias(String alias);
}
+
+extension AppWalletSyncStatus on AppWallet {
+ /// Truly caught up: connected, synced, and a real height has loaded. The
+ /// height guard rules out the brief post-open window where `isSynced` flips
+ /// true before `syncedHeight` arrives (LWS reports 0 at first), which would
+ /// otherwise show "up to date" prematurely.
+ bool get isFullySynced => isConnected && isSynced && (syncedHeight ?? 0) > 0;
+}
diff --git a/lib/screens/wallet_home.dart b/lib/screens/wallet_home.dart
index da2e623..3473224 100644
--- a/lib/screens/wallet_home.dart
+++ b/lib/screens/wallet_home.dart
@@ -369,7 +369,7 @@ class _WalletHomeScreenState extends State<WalletHomeScreen> {
var lwsConnectionIconStatus = StatusIconStatus.fail;
var fiatApiIconStatus = StatusIconStatus.loading;
- if (wallet.isConnected && wallet.isSynced && (wallet.syncedHeight ?? 0) > 0) {
+ if (wallet.isFullySynced) {
lwsConnectionIconStatus = StatusIconStatus.complete;
} else if (wallet.usingTor &&
TorService.sharedInstance.status == TorConnectionStatus.connecting ||
diff --git a/lib/services/foreground_sync_service.dart b/lib/services/foreground_sync_service.dart
index 779d8d6..63dade6 100644
--- a/lib/services/foreground_sync_service.dart
+++ b/lib/services/foreground_sync_service.dart
@@ -61,9 +61,7 @@ class _SyncTaskHandler extends TaskHandler {
void onRepeatEvent(DateTime timestamp) {
final wallet = _wallet;
final syncing =
- wallet != null &&
- wallet.connectionAddress.isNotEmpty &&
- !(wallet.isConnected && wallet.isSynced);
+ wallet != null && wallet.connectionAddress.isNotEmpty && !wallet.isFullySynced;
FlutterForegroundTask.updateService(
notificationTitle: 'Skylight Wallet',
notificationText: syncing ? 'Syncing…' : 'Wallet up to date',
@@ -118,14 +116,17 @@ void initForegroundSync() {
);
}
-Future<void> startForegroundSync() async {
+/// [synced] seeds the initial notification from the caller's live wallet state,
+/// so toggling this on while already caught up shows "up to date" immediately
+/// instead of a stale "Syncing…" until the isolate's first 30s tick.
+Future<void> startForegroundSync({bool synced = false}) async {
if (!Platform.isAndroid) return;
initForegroundSync();
await FlutterForegroundTask.requestNotificationPermission();
if (await FlutterForegroundTask.isRunningService) return;
await FlutterForegroundTask.startService(
notificationTitle: 'Skylight Wallet',
- notificationText: 'Syncing…',
+ notificationText: synced ? 'Wallet up to date' : 'Syncing…',
callback: foregroundSyncCallback,
);
}
diff --git a/lib/widgets/connection_settings_form.dart b/lib/widgets/connection_settings_form.dart
index fd77b46..07843ab 100644
--- a/lib/widgets/connection_settings_form.dart
+++ b/lib/widgets/connection_settings_form.dart
@@ -10,6 +10,7 @@ import 'package:skylight_wallet/services/tor_settings_service.dart';
import 'package:skylight_wallet/util/logging.dart';
import 'package:skylight_wallet/l10n/app_localizations.dart';
+import 'package:skylight_wallet/models/app_wallet.dart';
import 'package:skylight_wallet/wallet_core_glue.dart';
import 'package:skylight_wallet/services/tor_service.dart';
@@ -89,9 +90,11 @@ class _ConnectionSettingsFormState extends State<ConnectionSettingsForm> {
void _setForegroundSyncEnabled(bool value) async {
setState(() => _foregroundSyncEnabled = value);
+ // Captured before the await so the notification starts from the live status.
+ final synced = value && appWalletOf(context).isFullySynced;
await SharedPreferencesService.set<bool>(SharedPreferencesKeys.foregroundSyncEnabled, value);
if (value) {
- await startForegroundSync();
+ await startForegroundSync(synced: synced);
} else {
await stopForegroundSync();
}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.