| .. | .. |
|---|
| 73 | 73 | _initConnection(); |
|---|
| 74 | 74 | } |
|---|
| 75 | 75 | |
|---|
| 76 | | - void _saveLastSeq() { |
|---|
| 77 | | - SharedPreferences.getInstance().then((prefs) { |
|---|
| 78 | | - prefs.setInt('lastSeq', _lastSeq); |
|---|
| 79 | | - }); |
|---|
| 76 | + SharedPreferences? _prefs; |
|---|
| 77 | + |
|---|
| 78 | + Future<void> _saveLastSeq() async { |
|---|
| 79 | + _prefs ??= await SharedPreferences.getInstance(); |
|---|
| 80 | + await _prefs!.setInt('lastSeq', _lastSeq); |
|---|
| 80 | 81 | } |
|---|
| 81 | 82 | |
|---|
| 82 | 83 | @override |
|---|
| .. | .. |
|---|
| 206 | 207 | final sessionId = msg['sessionId'] as String?; |
|---|
| 207 | 208 | if (sessionId != null) _incrementUnread(sessionId); |
|---|
| 208 | 209 | case 'catch_up': |
|---|
| 210 | + // Only update lastSeq — don't replay messages. |
|---|
| 211 | + // Local storage already has our messages. Replaying causes duplicates |
|---|
| 212 | + // and overwrites user messages. New messages arriving while offline |
|---|
| 213 | + // are stored via _storeForSession from the DIRECT broadcast path. |
|---|
| 209 | 214 | final serverSeq = msg['serverSeq'] as int?; |
|---|
| 210 | 215 | if (serverSeq != null && serverSeq > _lastSeq) { |
|---|
| 211 | 216 | _lastSeq = serverSeq; |
|---|
| 212 | 217 | _saveLastSeq(); |
|---|
| 213 | | - } |
|---|
| 214 | | - final messages = msg['messages'] as List<dynamic>?; |
|---|
| 215 | | - if (messages != null && messages.isNotEmpty) { |
|---|
| 216 | | - _isCatchingUp = true; |
|---|
| 217 | | - for (final m in messages) { |
|---|
| 218 | | - _handleMessage(m as Map<String, dynamic>); |
|---|
| 219 | | - } |
|---|
| 220 | | - _isCatchingUp = false; |
|---|
| 221 | 218 | } |
|---|
| 222 | 219 | case 'pong': |
|---|
| 223 | 220 | break; // heartbeat response, ignore |
|---|