| .. | .. |
|---|
| 67 | 67 | final Map<String, List<Message>> _catchUpPending = {}; |
|---|
| 68 | 68 | List<String>? _cachedSessionOrder; |
|---|
| 69 | 69 | Timer? _typingTimer; |
|---|
| 70 | + bool _unreadCountsLoaded = false; |
|---|
| 70 | 71 | |
|---|
| 71 | 72 | @override |
|---|
| 72 | 73 | void initState() { |
|---|
| .. | .. |
|---|
| 80 | 81 | // Load persisted state BEFORE connecting |
|---|
| 81 | 82 | final prefs = await SharedPreferences.getInstance(); |
|---|
| 82 | 83 | _lastSeq = prefs.getInt('lastSeq') ?? 0; |
|---|
| 84 | + // Restore persisted unread counts |
|---|
| 85 | + final savedUnreads = prefs.getString('unreadCounts'); |
|---|
| 86 | + if (savedUnreads != null && mounted) { |
|---|
| 87 | + try { |
|---|
| 88 | + final map = (jsonDecode(savedUnreads) as Map<String, dynamic>) |
|---|
| 89 | + .map((k, v) => MapEntry(k, v as int)); |
|---|
| 90 | + ref.read(unreadCountsProvider.notifier).state = map; |
|---|
| 91 | + } catch (_) {} |
|---|
| 92 | + } |
|---|
| 93 | + _unreadCountsLoaded = true; |
|---|
| 83 | 94 | // Restore saved session order and active session |
|---|
| 84 | 95 | _cachedSessionOrder = prefs.getStringList('sessionOrder'); |
|---|
| 85 | 96 | final savedSessionId = prefs.getString('activeSessionId'); |
|---|
| .. | .. |
|---|
| 133 | 144 | if (_ws != null && !_ws!.isConnected) { |
|---|
| 134 | 145 | _ws!.connect(); |
|---|
| 135 | 146 | } |
|---|
| 147 | + // Don't update badge here — provider might not have loaded persisted counts yet. |
|---|
| 148 | + // Native applicationDidBecomeActive reads correct value from UserDefaults. |
|---|
| 149 | + } else if (state == AppLifecycleState.paused && _unreadCountsLoaded) { |
|---|
| 150 | + // Set badge to total unread count when going to background |
|---|
| 151 | + _updateBadgeFromUnreads(); |
|---|
| 136 | 152 | } |
|---|
| 153 | + } |
|---|
| 154 | + |
|---|
| 155 | + void _updateBadgeFromUnreads() { |
|---|
| 156 | + final counts = ref.read(unreadCountsProvider); |
|---|
| 157 | + _persistUnreadCounts(counts); |
|---|
| 137 | 158 | } |
|---|
| 138 | 159 | |
|---|
| 139 | 160 | bool _isLoadingMore = false; |
|---|
| .. | .. |
|---|
| 675 | 696 | final counts = Map<String, int>.from(ref.read(unreadCountsProvider)); |
|---|
| 676 | 697 | counts[sessionId] = (counts[sessionId] ?? 0) + 1; |
|---|
| 677 | 698 | ref.read(unreadCountsProvider.notifier).state = counts; |
|---|
| 699 | + _persistUnreadCounts(counts); |
|---|
| 700 | + } |
|---|
| 701 | + |
|---|
| 702 | + void _persistUnreadCounts(Map<String, int> counts) { |
|---|
| 703 | + final total = counts.values.fold<int>(0, (sum, v) => sum + v); |
|---|
| 704 | + // Set badge immediately via platform channel (synchronous native call) |
|---|
| 705 | + PushService.setBadge(total); |
|---|
| 706 | + // Also persist to SharedPreferences for app restart |
|---|
| 707 | + SharedPreferences.getInstance().then((prefs) { |
|---|
| 708 | + prefs.setString('unreadCounts', jsonEncode(counts)); |
|---|
| 709 | + prefs.setInt('badgeCount', total); |
|---|
| 710 | + }); |
|---|
| 678 | 711 | } |
|---|
| 679 | 712 | |
|---|
| 680 | 713 | Future<void> _switchSession(String sessionId) async { |
|---|
| .. | .. |
|---|
| 691 | 724 | final counts = Map<String, int>.from(ref.read(unreadCountsProvider)); |
|---|
| 692 | 725 | counts.remove(sessionId); |
|---|
| 693 | 726 | ref.read(unreadCountsProvider.notifier).state = counts; |
|---|
| 727 | + _persistUnreadCounts(counts); |
|---|
| 728 | + |
|---|
| 729 | + // Update badge to reflect remaining unreads |
|---|
| 730 | + _updateBadgeFromUnreads(); |
|---|
| 694 | 731 | |
|---|
| 695 | 732 | _sendCommand('switch', {'sessionId': sessionId}); |
|---|
| 696 | 733 | _scrollToBottom(); |
|---|