| .. | .. |
|---|
| 42 | 42 | int _lastSeq = 0; |
|---|
| 43 | 43 | bool _isCatchingUp = false; |
|---|
| 44 | 44 | bool _screenshotForChat = false; |
|---|
| 45 | + final Set<int> _seenSeqs = {}; |
|---|
| 45 | 46 | |
|---|
| 46 | 47 | @override |
|---|
| 47 | 48 | void initState() { |
|---|
| .. | .. |
|---|
| 153 | 154 | void _handleMessage(Map<String, dynamic> msg) { |
|---|
| 154 | 155 | // Track sequence numbers for catch_up protocol |
|---|
| 155 | 156 | final seq = msg['seq'] as int?; |
|---|
| 156 | | - if (seq != null && seq > _lastSeq) { |
|---|
| 157 | | - _lastSeq = seq; |
|---|
| 158 | | - _saveLastSeq(); |
|---|
| 157 | + if (seq != null) { |
|---|
| 158 | + // Dedup: skip messages we've already processed |
|---|
| 159 | + if (_seenSeqs.contains(seq)) return; |
|---|
| 160 | + _seenSeqs.add(seq); |
|---|
| 161 | + // Keep set bounded |
|---|
| 162 | + if (_seenSeqs.length > 500) { |
|---|
| 163 | + final sorted = _seenSeqs.toList()..sort(); |
|---|
| 164 | + _seenSeqs.removeAll(sorted.sublist(0, sorted.length - 300)); |
|---|
| 165 | + } |
|---|
| 166 | + if (seq > _lastSeq) { |
|---|
| 167 | + _lastSeq = seq; |
|---|
| 168 | + _saveLastSeq(); |
|---|
| 169 | + } |
|---|
| 159 | 170 | } |
|---|
| 160 | 171 | |
|---|
| 161 | 172 | final type = msg['type'] as String?; |
|---|