Convert shared coordinator state to owner-side intent operations

nostrKeyMapping, sentGeoDeliveryAcks/sentReadReceipts dedupe,
isBatchingPublic, geo subscription lifecycle, and private-chat selection
hand-off now mutate through single intent operations on ChatViewModel,
with backing storage locked down via private(set) so the single-writer
property is compiler-enforced. Context protocols downgrade to read-only
access where reads remain. 8 new contract tests.

Known remaining writers outside the protocols: sentReadReceipts is
passed inout to PrivateChatManager.syncReadReceiptsForSentMessages and
un-marked by ChatTransportEventCoordinator on disconnect.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jack
2026-06-10 21:26:21 +02:00
co-authored by Claude Fable 5
parent 80bed1f395
commit 707b22878d
12 changed files with 358 additions and 64 deletions
@@ -29,6 +29,21 @@ private final class MockChatPrivateConversationContext: ChatPrivateConversationC
var nostrKeyMapping: [PeerID: String] = [:]
private(set) var notifyUIChangedCount = 0
@discardableResult
func markReadReceiptSent(_ messageID: String) -> Bool {
sentReadReceipts.insert(messageID).inserted
}
@discardableResult
func markGeoDeliveryAckSent(_ messageID: String) -> Bool {
sentGeoDeliveryAcks.insert(messageID).inserted
}
func handOffSelectedPrivateChat(from oldPeerIDs: [PeerID], to newPeerID: PeerID) {
guard oldPeerIDs.contains(where: { selectedPrivateChatPeer == $0 }) else { return }
selectedPrivateChatPeer = newPeerID
}
func notifyUIChanged() {
notifyUIChangedCount += 1
}