mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-24 22:25:18 +00:00
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:
@@ -1,11 +1,39 @@
|
||||
# Conversation Store: Single Source of Truth
|
||||
|
||||
**Status:** Steps 1–4 implemented (additive store, private cutover, public
|
||||
cutover, delivery via store; `PublicTimelineStore` and
|
||||
`ChatDeliveryCoordinator.messageLocationIndex` deleted). Baselines recorded in
|
||||
`bitchatTests/Performance/PerformanceBaselineTests.swift` (`pipeline.privateIngest`,
|
||||
`pipeline.publicIngest`, `store.append`, `delivery.incrementalUpdate`,
|
||||
`delivery.storeUpdate`).
|
||||
**Status: migration complete (steps 1–5).** `ConversationStore` is the sole
|
||||
holder of message state; the feature models (`PublicChatModel`,
|
||||
`PrivateInboxModel`, `PrivateConversationModel`, `PeerListModel`,
|
||||
`ConversationUIModel`) observe it directly — `PublicChatModel` observes the
|
||||
active `Conversation` object, so background appends never invalidate it.
|
||||
`LegacyConversationStore`, `LegacyConversationStoreBridge`, and
|
||||
`PublicTimelineStore` are deleted. Baselines recorded in
|
||||
`bitchatTests/Performance/PerformanceBaselineTests.swift`
|
||||
(`pipeline.privateIngest`, `pipeline.publicIngest`, `store.append`,
|
||||
`delivery.incrementalUpdate`, `delivery.storeUpdate`).
|
||||
|
||||
Deviations from the plan below, chosen at cutover:
|
||||
|
||||
- **No `IdentityResolver` canonicalization layer.** Direct conversations stay
|
||||
keyed by raw routing `PeerID` (`ConversationID.directPeer`). The
|
||||
coordinators' ephemeral↔stable mirroring/consolidation guarantees the
|
||||
selected peer's key always holds the full timeline, and no view enumerates
|
||||
direct conversations as a list — the legacy resolver-keyed dedup only ever
|
||||
fed `isEmpty`-style badge checks (identity-aware unread resolution lives in
|
||||
`ChatUnreadStateResolver`, which works on raw keys). `IdentityResolver` was
|
||||
deleted with the legacy store; `PeerHandle` slimmed to `id` +
|
||||
`routingPeerID`.
|
||||
- **Selection state lives in the store.** The legacy store also carried the
|
||||
two UI selection axes (`activeChannel`, `selectedPrivatePeerID`); they
|
||||
moved into `ConversationStore` (`setActiveChannel` /
|
||||
`setSelectedPrivatePeer`, deriving `selectedConversationID`), and
|
||||
`migrateConversation` hands the private-peer selection off with the
|
||||
conversation.
|
||||
- **`ChatViewModel.messages` / `privateChats` / `unreadPrivateMessages`
|
||||
survive as derived read-only views** (not stored properties): coordinators,
|
||||
commands, and tests read them; the dict shape is only rebuilt where a
|
||||
coordinator genuinely needs the whole dictionary (migration scans, unread
|
||||
resolution). Simple per-peer reads dispatch through the store-direct
|
||||
`privateMessages(for:)` context witness instead.
|
||||
|
||||
---
|
||||
|
||||
@@ -96,7 +124,7 @@ while a timer-batched `PublicMessagePipeline` mutates `messages` ~80 ms later
|
||||
is one copy, "await the sync" disappears: after `append` returns, every observer of
|
||||
that `Conversation` sees the message.
|
||||
|
||||
## 3. Deleted at end state
|
||||
## 3. Deleted at end state (done)
|
||||
|
||||
- `PublicTimelineStore` (`bitchat/ViewModels/PublicTimelineStore.swift`) — folded into
|
||||
`Conversation` cap/dedup policy.
|
||||
@@ -113,7 +141,7 @@ while a timer-batched `PublicMessagePipeline` mutates `messages` ~80 ms later
|
||||
`PublicChatModel.messages` (they observe `Conversation` objects directly).
|
||||
- `ChatViewModel.messages` / `ChatViewModel.privateChats` as stored/owning properties.
|
||||
|
||||
## 4. Migration plan
|
||||
## 4. Migration plan (complete)
|
||||
|
||||
Each step lands green against the full suite plus the `PerformanceBaselineTests`
|
||||
numbers (no pipeline throughput regression at any step).
|
||||
|
||||
Reference in New Issue
Block a user