mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-27 04:05:19 +00:00
Cut private message path over to ConversationStore
All private-message mutations now flow through store intents: coordinators, PrivateChatManager (its @Published dicts deleted - now read-only views over the store), outbound sends, delivery status, and chat migration. The O(1) store dedup replaces the full-scan duplicate check; insertion order is maintained by the store so sanitizeChat's re-sort is a documented no-op. Both bootstrapper Combine bridges and the Task.yield store synchronization are deleted. ChatViewModel.privateChats/unreadPrivateMessages become get-only derived views (measured: naive rebuild equals a change-invalidated cache within noise, so the simpler form stays). Feature models still read the legacy store, fed by a coalescing LegacyConversationStoreBridge (one mirror per burst, marked for step-5 deletion). pipeline.privateIngest: 9.6k -> 14.7k msg/s (+53%). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,9 +15,17 @@ protocol ChatTransportEventContext: AnyObject {
|
||||
var isConnected: Bool { get set }
|
||||
var nickname: String { get }
|
||||
var myPeerID: PeerID { get }
|
||||
var privateChats: [PeerID: [BitchatMessage]] { get set }
|
||||
var unreadPrivateMessages: Set<PeerID> { get set }
|
||||
var privateChats: [PeerID: [BitchatMessage]] { get }
|
||||
var unreadPrivateMessages: Set<PeerID> { get }
|
||||
var selectedPrivateChatPeer: PeerID? { get set }
|
||||
/// Appends a private message via the single-writer store intent;
|
||||
/// returns `false` on duplicate message ID.
|
||||
@discardableResult
|
||||
func appendPrivateMessage(_ message: BitchatMessage, to peerID: PeerID) -> Bool
|
||||
/// Removes the peer's chat entirely, including unread state.
|
||||
func removePrivateChat(_ peerID: PeerID)
|
||||
func markPrivateChatUnread(_ peerID: PeerID)
|
||||
func markPrivateChatRead(_ peerID: PeerID)
|
||||
/// Forgets that read receipts were sent for `ids` so READ acks can be
|
||||
/// re-sent after the peer reconnects. (Single mutation path for the
|
||||
/// owner's `sentReadReceipts`; this coordinator never reads the raw set.)
|
||||
@@ -251,13 +259,12 @@ private extension ChatTransportEventCoordinator {
|
||||
to stablePeerID: PeerID,
|
||||
in context: any ChatTransportEventContext
|
||||
) {
|
||||
if let messages = context.privateChats[shortPeerID] {
|
||||
if context.privateChats[stablePeerID] == nil {
|
||||
context.privateChats[stablePeerID] = []
|
||||
}
|
||||
let hadUnread = context.unreadPrivateMessages.contains(shortPeerID)
|
||||
|
||||
let existingIDs = Set(context.privateChats[stablePeerID]?.map(\.id) ?? [])
|
||||
for message in messages where !existingIDs.contains(message.id) {
|
||||
if let messages = context.privateChats[shortPeerID] {
|
||||
for message in messages {
|
||||
// Rewrite senderPeerID to the stable key so read receipts
|
||||
// keep working; store append dedups by ID and keeps order.
|
||||
let migrated = BitchatMessage(
|
||||
id: message.id,
|
||||
sender: message.sender,
|
||||
@@ -273,16 +280,15 @@ private extension ChatTransportEventCoordinator {
|
||||
mentions: message.mentions,
|
||||
deliveryStatus: message.deliveryStatus
|
||||
)
|
||||
context.privateChats[stablePeerID]?.append(migrated)
|
||||
context.appendPrivateMessage(migrated, to: stablePeerID)
|
||||
}
|
||||
|
||||
context.privateChats[stablePeerID]?.sort { $0.timestamp < $1.timestamp }
|
||||
context.privateChats.removeValue(forKey: shortPeerID)
|
||||
context.removePrivateChat(shortPeerID)
|
||||
}
|
||||
|
||||
if context.unreadPrivateMessages.contains(shortPeerID) {
|
||||
context.unreadPrivateMessages.remove(shortPeerID)
|
||||
context.unreadPrivateMessages.insert(stablePeerID)
|
||||
if hadUnread {
|
||||
context.markPrivateChatRead(shortPeerID)
|
||||
context.markPrivateChatUnread(stablePeerID)
|
||||
}
|
||||
|
||||
context.selectedPrivateChatPeer = stablePeerID
|
||||
|
||||
Reference in New Issue
Block a user