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:
jack
2026-06-11 12:19:11 +02:00
co-authored by Claude Fable 5
parent ac3a2f2d34
commit 879d8cba12
33 changed files with 1293 additions and 545 deletions
@@ -25,7 +25,10 @@ protocol ChatMediaTransferContext: AnyObject {
func currentPublicSender() -> (name: String, peerID: PeerID)
// MARK: Message state
var privateChats: [PeerID: [BitchatMessage]] { get set }
var privateChats: [PeerID: [BitchatMessage]] { get }
/// Appends a private message via the single-writer store intent.
@discardableResult
func appendPrivateMessage(_ message: BitchatMessage, to peerID: PeerID) -> Bool
func appendTimelineMessage(_ message: BitchatMessage, to channel: ChannelID)
func refreshVisibleMessages(from channel: ChannelID?)
func trimMessagesIfNeeded()
@@ -228,9 +231,7 @@ final class ChatMediaTransferCoordinator {
senderPeerID: context.myPeerID,
deliveryStatus: .sending
)
var chats = context.privateChats
chats[peerID, default: []].append(message)
context.privateChats = chats
context.appendPrivateMessage(message, to: peerID)
context.trimMessagesIfNeeded()
} else {
let (displayName, senderPeerID) = context.currentPublicSender()