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
@@ -301,7 +301,7 @@ final class ChatPeerIdentityCoordinator {
.visibleGeohashPeople()
.first(where: { $0.displayName == nickname }) {
let conversationKey = PeerID(nostr_: person.id)
viewModel.nostrKeyMapping[conversationKey] = person.id
viewModel.registerNostrKeyMapping(person.id, for: conversationKey)
return conversationKey
}
@@ -312,7 +312,7 @@ final class ChatPeerIdentityCoordinator {
.lowercased() ?? nickname.lowercased()
if let pubkey = viewModel.geoNicknames.first(where: { $0.value.lowercased() == base })?.key {
let conversationKey = PeerID(nostr_: pubkey)
viewModel.nostrKeyMapping[conversationKey] = pubkey
viewModel.registerNostrKeyMapping(pubkey, for: conversationKey)
return conversationKey
}