Add single-writer ConversationStore core (additive)

Per-conversation ObservableObjects with O(1) dedup via an incrementally
maintained message-ID index, binary-search timestamp insertion, folded
cap policies, a no-downgrade delivery rule, and a typed change subject.
All mutation flows through store intents (conversation mutators are
fileprivate). The previous store is renamed LegacyConversationStore
pending deletion in step 5. 16 behavioral tests including per-
conversation publish isolation; store.append benchmarks at ~144k
messages/sec.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jack
2026-06-11 11:02:27 +02:00
co-authored by Claude Fable 5
parent 45650854e7
commit ac3a2f2d34
11 changed files with 919 additions and 37 deletions
+4 -4
View File
@@ -8,10 +8,10 @@ final class PrivateInboxModel: ObservableObject {
@Published private(set) var unreadPeerIDs: Set<PeerID> = []
@Published private(set) var messagesByPeerID: [PeerID: [BitchatMessage]] = [:]
private let conversationStore: ConversationStore
private let conversationStore: LegacyConversationStore
private var cancellables = Set<AnyCancellable>()
init(conversationStore: ConversationStore) {
init(conversationStore: LegacyConversationStore) {
self.conversationStore = conversationStore
bind()
@@ -94,14 +94,14 @@ final class PrivateConversationModel: ObservableObject {
@Published private(set) var selectedHeaderState: PrivateConversationHeaderState?
private let chatViewModel: ChatViewModel
private let conversationStore: ConversationStore
private let conversationStore: LegacyConversationStore
private let locationChannelsModel: LocationChannelsModel
private let peerIdentityStore: PeerIdentityStore
private var cancellables = Set<AnyCancellable>()
init(
chatViewModel: ChatViewModel,
conversationStore: ConversationStore,
conversationStore: LegacyConversationStore,
locationChannelsModel: LocationChannelsModel? = nil,
peerIdentityStore: PeerIdentityStore? = nil
) {