From 6eef030386aff33e48a6018c8281734536c4d66f Mon Sep 17 00:00:00 2001 From: jack Date: Tue, 25 Nov 2025 10:24:24 -1000 Subject: [PATCH] Fix: use persisted read receipts during consolidation Pass the UserDefaults-backed sentReadReceipts from ChatViewModel to consolidateMessages() to correctly identify already-read messages after app restart. This prevents duplicate read receipts and incorrect unread badges when reopening a chat shortly after reading it. Addresses PR review feedback. --- bitchat/Services/PrivateChatManager.swift | 6 ++++-- bitchat/ViewModels/ChatViewModel.swift | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/bitchat/Services/PrivateChatManager.swift b/bitchat/Services/PrivateChatManager.swift index c5579125..e7d924db 100644 --- a/bitchat/Services/PrivateChatManager.swift +++ b/bitchat/Services/PrivateChatManager.swift @@ -39,9 +39,10 @@ final class PrivateChatManager: ObservableObject { /// - Parameters: /// - peerID: The target peer ID to consolidate messages into /// - peerNickname: The peer's display name (lowercased for matching) + /// - persistedReadReceipts: The persisted read receipts set from ChatViewModel (UserDefaults-backed) /// - Returns: True if any unread messages were found during consolidation @MainActor - func consolidateMessages(for peerID: PeerID, peerNickname: String) -> Bool { + func consolidateMessages(for peerID: PeerID, peerNickname: String, persistedReadReceipts: Set) -> Bool { guard let meshService = meshService else { return false } var hasUnreadMessages = false @@ -74,9 +75,10 @@ final class PrivateChatManager: ObservableObject { privateChats[peerID]?.append(updatedMessage) // Check for recent unread messages (< 60s, not sent by us, not already read) + // Use persistedReadReceipts to correctly identify already-read messages after app restart if message.senderPeerID != meshService.myPeerID { let messageAge = Date().timeIntervalSince(message.timestamp) - if messageAge < 60 && !sentReadReceipts.contains(message.id) { + if messageAge < 60 && !persistedReadReceipts.contains(message.id) { hasUnreadMessages = true } } diff --git a/bitchat/ViewModels/ChatViewModel.swift b/bitchat/ViewModels/ChatViewModel.swift index 2cc75de3..9533d8bb 100644 --- a/bitchat/ViewModels/ChatViewModel.swift +++ b/bitchat/ViewModels/ChatViewModel.swift @@ -2560,7 +2560,8 @@ final class ChatViewModel: ObservableObject, BitchatDelegate, CommandContextProv } // Consolidate messages from different peer ID representations (stable Noise key, temp Nostr IDs) - _ = privateChatManager.consolidateMessages(for: peerID, peerNickname: peerNickname) + // Pass persisted sentReadReceipts to correctly identify already-read messages after app restart + _ = privateChatManager.consolidateMessages(for: peerID, peerNickname: peerNickname, persistedReadReceipts: sentReadReceipts) // Trigger handshake if needed (mesh peers only). Skip for Nostr geohash conv keys. if !peerID.isGeoDM && !peerID.isGeoChat {