From 6a336bbd8dfc03aa2d061ca4cf0f2750ea925638 Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Sun, 22 Mar 2026 16:33:05 +0100
Subject: [PATCH] fix: smart catch_up merge - dedup by content, preserve user messages

---
 lib/screens/chat_screen.dart |   24 ++++++++++++++++++++----
 1 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/lib/screens/chat_screen.dart b/lib/screens/chat_screen.dart
index 1982f27..5f3e38c 100644
--- a/lib/screens/chat_screen.dart
+++ b/lib/screens/chat_screen.dart
@@ -207,15 +207,31 @@
         final sessionId = msg['sessionId'] as String?;
         if (sessionId != null) _incrementUnread(sessionId);
       case 'catch_up':
-        // Only update lastSeq — don't replay messages.
-        // Local storage already has our messages. Replaying causes duplicates
-        // and overwrites user messages. New messages arriving while offline
-        // are stored via _storeForSession from the DIRECT broadcast path.
         final serverSeq = msg['serverSeq'] as int?;
         if (serverSeq != null && serverSeq > _lastSeq) {
           _lastSeq = serverSeq;
           _saveLastSeq();
         }
+        // Merge catch_up messages: only add messages not already in local storage.
+        // We check by content match against existing messages to avoid duplicates
+        // while still picking up messages that arrived while the app was backgrounded.
+        final catchUpMsgs = msg['messages'] as List<dynamic>?;
+        if (catchUpMsgs != null && catchUpMsgs.isNotEmpty) {
+          _isCatchingUp = true;
+          final existing = ref.read(messagesProvider);
+          final existingContents = existing
+              .where((m) => m.role == MessageRole.assistant)
+              .map((m) => m.content)
+              .toSet();
+          for (final m in catchUpMsgs) {
+            final content = (m as Map<String, dynamic>)['content'] as String? ?? '';
+            // Skip if we already have this message locally
+            if (content.isNotEmpty && existingContents.contains(content)) continue;
+            _handleMessage(m);
+            if (content.isNotEmpty) existingContents.add(content);
+          }
+          _isCatchingUp = false;
+        }
       case 'pong':
         break; // heartbeat response, ignore
       case 'delete_message':

--
Gitblit v1.3.1