From 0af9986262e53b232731408ad38e9fda3da2cfa2 Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Wed, 01 Apr 2026 18:02:59 +0200
Subject: [PATCH] feat: smart badge counting, persisted unreads, flutter_app_badger, race condition fixes

---
 lib/screens/chat_screen.dart |   37 +++++++++++++++++++++++++++++++++++++
 1 files changed, 37 insertions(+), 0 deletions(-)

diff --git a/lib/screens/chat_screen.dart b/lib/screens/chat_screen.dart
index 63fc4cd..77af560 100644
--- a/lib/screens/chat_screen.dart
+++ b/lib/screens/chat_screen.dart
@@ -67,6 +67,7 @@
   final Map<String, List<Message>> _catchUpPending = {};
   List<String>? _cachedSessionOrder;
   Timer? _typingTimer;
+  bool _unreadCountsLoaded = false;
 
   @override
   void initState() {
@@ -80,6 +81,16 @@
     // Load persisted state BEFORE connecting
     final prefs = await SharedPreferences.getInstance();
     _lastSeq = prefs.getInt('lastSeq') ?? 0;
+    // Restore persisted unread counts
+    final savedUnreads = prefs.getString('unreadCounts');
+    if (savedUnreads != null && mounted) {
+      try {
+        final map = (jsonDecode(savedUnreads) as Map<String, dynamic>)
+            .map((k, v) => MapEntry(k, v as int));
+        ref.read(unreadCountsProvider.notifier).state = map;
+      } catch (_) {}
+    }
+    _unreadCountsLoaded = true;
     // Restore saved session order and active session
     _cachedSessionOrder = prefs.getStringList('sessionOrder');
     final savedSessionId = prefs.getString('activeSessionId');
@@ -133,7 +144,17 @@
       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.
+    } else if (state == AppLifecycleState.paused && _unreadCountsLoaded) {
+      // Set badge to total unread count when going to background
+      _updateBadgeFromUnreads();
     }
+  }
+
+  void _updateBadgeFromUnreads() {
+    final counts = ref.read(unreadCountsProvider);
+    _persistUnreadCounts(counts);
   }
 
   bool _isLoadingMore = false;
@@ -675,6 +696,18 @@
     final counts = Map<String, int>.from(ref.read(unreadCountsProvider));
     counts[sessionId] = (counts[sessionId] ?? 0) + 1;
     ref.read(unreadCountsProvider.notifier).state = counts;
+    _persistUnreadCounts(counts);
+  }
+
+  void _persistUnreadCounts(Map<String, int> counts) {
+    final total = counts.values.fold<int>(0, (sum, v) => sum + v);
+    // Set badge immediately via platform channel (synchronous native call)
+    PushService.setBadge(total);
+    // Also persist to SharedPreferences for app restart
+    SharedPreferences.getInstance().then((prefs) {
+      prefs.setString('unreadCounts', jsonEncode(counts));
+      prefs.setInt('badgeCount', total);
+    });
   }
 
   Future<void> _switchSession(String sessionId) async {
@@ -691,6 +724,10 @@
     final counts = Map<String, int>.from(ref.read(unreadCountsProvider));
     counts.remove(sessionId);
     ref.read(unreadCountsProvider.notifier).state = counts;
+    _persistUnreadCounts(counts);
+
+    // Update badge to reflect remaining unreads
+    _updateBadgeFromUnreads();
 
     _sendCommand('switch', {'sessionId': sessionId});
     _scrollToBottom();

--
Gitblit v1.3.1