From 7747e467ed7d5316c62a439a5e3e58bf891f0d73 Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Thu, 11 Jun 2026 09:27:33 +0200
Subject: [PATCH] fix: clear unread counts on app foreground so badge doesn't ghost
---
lib/screens/chat_screen.dart | 50 +++++++++++++++++++++++++++++++++++++++++++++++---
1 files changed, 47 insertions(+), 3 deletions(-)
diff --git a/lib/screens/chat_screen.dart b/lib/screens/chat_screen.dart
index 20b3f6a..8738410 100644
--- a/lib/screens/chat_screen.dart
+++ b/lib/screens/chat_screen.dart
@@ -93,7 +93,10 @@
// Load persisted state BEFORE connecting
final prefs = await SharedPreferences.getInstance();
_lastSeq = prefs.getInt('lastSeq') ?? 0;
- // Restore persisted unread counts
+ // Restore persisted unread counts (kept loaded so any pre-resume read code
+ // sees a valid state), but the app is foregrounding right now → user is
+ // here, so everything is "seen." Clear immediately so a stale 15 doesn't
+ // re-paint the badge on next background.
final savedUnreads = prefs.getString('unreadCounts');
if (savedUnreads != null && mounted) {
try {
@@ -103,6 +106,7 @@
} catch (_) {}
}
_unreadCountsLoaded = true;
+ _clearAllUnreads();
// Restore saved session order and active session
_cachedSessionOrder = prefs.getStringList('sessionOrder');
final savedSessionId = prefs.getString('activeSessionId');
@@ -156,8 +160,10 @@
if (_ws != null && !_ws!.isConnected) {
_ws!.connect();
}
- // Don't update badge here — provider might not have loaded persisted counts yet.
- // Native applicationDidBecomeActive reads correct value from UserDefaults.
+ // App is foregrounded → user is here → everything previously unread is
+ // now considered seen. Clear so we don't re-paint a stale badge when the
+ // user backgrounds the app without having explicitly switched sessions.
+ _clearAllUnreads();
} else if (state == AppLifecycleState.paused && _unreadCountsLoaded) {
// Set badge to total unread count when going to background
_updateBadgeFromUnreads();
@@ -167,6 +173,19 @@
void _updateBadgeFromUnreads() {
final counts = ref.read(unreadCountsProvider);
_persistUnreadCounts(counts);
+ }
+
+ /// Clear in-memory + persisted unread counts and remove the icon badge.
+ /// Called on cold-start (after load) and on resume — the app being open
+ /// means the user has effectively seen what was pending.
+ void _clearAllUnreads() {
+ if (!mounted) return;
+ ref.read(unreadCountsProvider.notifier).state = <String, int>{};
+ SharedPreferences.getInstance().then((prefs) {
+ prefs.setString('unreadCounts', jsonEncode(<String, int>{}));
+ prefs.setInt('badgeCount', 0);
+ });
+ PushService.clearBadge();
}
// ignore: unused_field
@@ -214,6 +233,7 @@
}
};
_ws!.onMessage = _handleMessage;
+ _ws!.onDebugStateRequest = _handleDebugStateRequest;
_ws!.onOpen = () {
_sessionReady = false; // Gate messages until sessions arrive
_pendingMessages.clear();
@@ -522,6 +542,30 @@
}
}
+ /// Respond to a pailot_debug_state request from the server.
+ /// Reads the in-memory session list and active session from providers
+ /// and publishes exactly what the app is currently rendering.
+ void _handleDebugStateRequest(String requestId) {
+ final sessions = ref.read(sessionsProvider);
+ final activeSessionId = ref.read(activeSessionIdProvider);
+
+ final sessionPayloads = sessions.map((s) => {
+ 'sessionId': s.id,
+ 'index': s.index,
+ 'displayedName': s.name, // exactly what is shown in the drawer
+ 'type': s.type,
+ if (s.kind != null) 'kind': s.kind,
+ 'isActive': s.id == activeSessionId,
+ }).toList();
+
+ _ws?.publishDebugStateResponse(
+ requestId: requestId,
+ activeSessionId: activeSessionId,
+ sessions: sessionPayloads,
+ platform: Platform.operatingSystem,
+ );
+ }
+
void _handleIncomingMessage(Map<String, dynamic> msg) {
final sessionId = msg['sessionId'] as String?;
final content = msg['content'] as String? ??
--
Gitblit v1.3.1