Break circular dependency between CommandProcessor and ChatViewModel

Introduce CommandContextProvider protocol to define what CommandProcessor
needs from its context. This follows the Dependency Inversion Principle:

- Create CommandContextProvider protocol with 15 methods/properties
- Create CommandGeoParticipant struct for geo participant data
- Add getVisibleGeoParticipants() to ChatViewModel
- ChatViewModel now conforms to CommandContextProvider
- CommandProcessor depends on protocol, not concrete ChatViewModel

Benefits:
- Breaks the tight coupling between CommandProcessor and ChatViewModel
- Makes CommandProcessor more testable (can use mock context)
- Clear contract for what CommandProcessor needs
- Backwards-compatible via chatViewModel property alias

Also fixes a concurrency bug in BitchatApp where notification delegate
was accessing main-actor-isolated property from arbitrary thread.
This commit is contained in:
jack
2025-11-24 10:34:29 -10:00
parent 792eaa3166
commit 477cc7fa05
3 changed files with 96 additions and 38 deletions
+8 -3
View File
@@ -248,10 +248,15 @@ final class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate {
// Get peer ID from userInfo
if let peerID = userInfo["peerID"] as? String {
// Don't show notification if the private chat is already open
if chatViewModel?.selectedPrivateChatPeer == PeerID(str: peerID) {
completionHandler([])
return
// Access main-actor-isolated property via Task
Task { @MainActor in
if self.chatViewModel?.selectedPrivateChatPeer == PeerID(str: peerID) {
completionHandler([])
} else {
completionHandler([.banner, .sound])
}
}
return
}
}
// Suppress geohash activity notification if we're already in that geohash channel