Remove noise service exposure; single-owner selection state

Transport callers no longer reach the raw NoiseEncryptionService:
getNoiseService() is deleted in favor of narrow purpose-named Transport
methods (session public key, identity fingerprint, static/signing keys,
sign/verify, callback installation). VerificationService now reaches
crypto through the transport, so it can no longer pin a stale service
across a panic reset. myPeerID/myNickname become private(set); the
existing setNickname mutator is the sole nickname path.

ConversationStore is now the sole owner of private-chat selection:
PrivateChatManager.selectedPeer is a published read-only mirror, and
startChat/endChat mutate through the store intent. The bridge method
and its five call sites are deleted, removing a latent bug where a
stale manager selection pushed back into the store could resurrect a
just-removed conversation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jack
2026-06-11 15:29:07 +02:00
co-authored by Claude Fable 5
parent ed86ed1065
commit 8a867a17a1
16 changed files with 205 additions and 99 deletions
+10 -14
View File
@@ -204,7 +204,6 @@ final class ChatViewModel: ObservableObject, BitchatDelegate, TransportEventDele
} else {
privateChatManager.endChat()
}
synchronizeConversationSelectionStore()
}
}
/// Read-only derived view of the store's unread direct conversations.
@@ -243,7 +242,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate, TransportEventDele
if let mapped = peerIdentityStore.stablePeerID(forShortID: shortPeerID) { return mapped }
// Fallback: derive from active Noise session if available
if shortPeerID.id.count == 16,
let key = meshService.getNoiseService().getPeerPublicKeyData(shortPeerID) {
let key = meshService.noiseSessionPublicKeyData(for: shortPeerID) {
let stable = PeerID(hexData: key)
peerIdentityStore.setStablePeerID(stable, forShortID: shortPeerID)
return stable
@@ -536,10 +535,17 @@ final class ChatViewModel: ObservableObject, BitchatDelegate, TransportEventDele
/// Moves the open private chat to `newPeerID` when the current selection is
/// one of the peer IDs being migrated away (side-effectful: re-targets the
/// private chat session and resyncs the conversation stores).
/// private chat session fingerprint refresh, read receipts).
///
/// Note: when this runs after a store `migrateConversation`, the store has
/// already handed the selection itself off to `newPeerID` (and the manager
/// mirrors it), so a selection that reads `newPeerID` is also re-targeted
/// to run the session side effects. Selections on unrelated peers are
/// untouched.
@MainActor
func handOffSelectedPrivateChat(from oldPeerIDs: [PeerID], to newPeerID: PeerID) {
guard oldPeerIDs.contains(where: { selectedPrivateChatPeer == $0 }) else { return }
guard oldPeerIDs.contains(where: { selectedPrivateChatPeer == $0 })
|| selectedPrivateChatPeer == newPeerID else { return }
selectedPrivateChatPeer = newPeerID
}
@@ -803,7 +809,6 @@ final class ChatViewModel: ObservableObject, BitchatDelegate, TransportEventDele
.store(in: &cancellables)
ChatViewModelBootstrapper(viewModel: self).configure()
synchronizeConversationSelectionStore()
}
// MARK: - Deinitialization
@@ -1140,8 +1145,6 @@ final class ChatViewModel: ObservableObject, BitchatDelegate, TransportEventDele
bleService.resetIdentityForPanic(currentNickname: nickname)
}
synchronizeConversationSelectionStore()
// No need to force UserDefaults synchronization
// Reinitialize Nostr with new identity
@@ -1262,13 +1265,6 @@ final class ChatViewModel: ObservableObject, BitchatDelegate, TransportEventDele
// MARK: - Message Handling
/// Pushes the private-chat manager's selection into the store, which
/// derives `selectedConversationID` from it and the active channel.
@MainActor
func synchronizeConversationSelectionStore() {
conversations.setSelectedPrivatePeer(privateChatManager.selectedPeer)
}
/// Invalidates the derived `messages` cache and notifies observers.
/// (Formerly pulled the channel's timeline into a stored `messages`
/// array; `messages` is now derived from the `ConversationStore`, so