Cut views over to ConversationStore; delete legacy store, bridge, resolver

Feature models observe per-conversation objects directly: PublicChatModel
forwards the active Conversation's objectWillChange, PrivateInboxModel
republishes only for the selected peer's conversation - background
appends no longer invalidate foreground views. LegacyConversationStore,
the coalescing bridge, and IdentityResolver are deleted (resolver
canonicalization proved display-invisible: nothing enumerates direct
conversations, lookups are by exact peer ID, and raw keying is strictly
more robust - documented as a design deviation). Selection state moves
into the store. ChatViewModel.messages/privateChats survive as derived
read views for coordinators that genuinely need them; hot paths use a
new store-direct privateMessages(for:) witness.

Final numbers vs pre-migration baselines:
pipeline.privateIngest 9.7k -> 24.0k msg/s (2.5x)
pipeline.publicIngest  6.8k -> 13.7k msg/s (2.0x)
delivery updates       38k  -> 117-133k/s

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jack
2026-06-11 13:57:12 +02:00
co-authored by Claude Fable 5
parent 7fb1f4a219
commit 38331e62f1
32 changed files with 542 additions and 1214 deletions
@@ -90,10 +90,9 @@ private extension ChatViewModelBootstrapper {
}
.store(in: &viewModel.cancellables)
// Private message state now flows: store intent
// `ConversationStore.changes` `LegacyConversationStoreBridge` (and
// the ChatViewModel shim-cache sink), so the old `$privateChats` /
// `$unreadMessages` debounced synchronization sinks are gone.
// Private message state flows through the single-writer
// `ConversationStore` intents and its `changes` subject; only the
// selection still originates in `PrivateChatManager`.
viewModel.privateChatManager.$selectedPeer
.receive(on: DispatchQueue.main)
.sink { [weak viewModel] _ in
@@ -159,7 +158,6 @@ private extension ChatViewModelBootstrapper {
guard let viewModel else { return }
viewModel.allPeers = peers
viewModel.identityResolver.register(peers: peers)
var uniquePeers: [PeerID: BitchatPeer] = [:]
for peer in peers {
@@ -178,9 +176,6 @@ private extension ChatViewModelBootstrapper {
viewModel.updatePrivateChatPeerIfNeeded()
}
// Peer registrations can change a conversation's
// canonical handle in the legacy store; re-key it.
viewModel.resynchronizeLegacyPrivateConversations()
viewModel.synchronizeConversationSelectionStore()
}
}