mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-24 23:25:19 +00:00
refactor(nostr-outbox): add NostrOutboxService and migrate ChatViewModel geohash PM/ACK sends to it
Centralize Nostr geohash outbox (PM, DELIVERED, READ) into a service wrapping NostrTransport; inject into ChatViewModel and remove ad-hoc NostrTransport instantiations.
This commit is contained in:
@@ -60,6 +60,8 @@
|
||||
ABCD00032E5CCCC300162C4A /* ReadReceiptTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABCD00012E5CCCC300162C4A /* ReadReceiptTracker.swift */; };
|
||||
ABCD00052E5CCCC300162C4C /* NostrInboxService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABCD00042E5CCCC300162C4C /* NostrInboxService.swift */; };
|
||||
ABCD00062E5CCCC300162C4D /* NostrInboxService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABCD00042E5CCCC300162C4C /* NostrInboxService.swift */; };
|
||||
ABCD00082E5CCCC300162C4E /* NostrOutboxService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABCD00072E5CCCC300162C4E /* NostrOutboxService.swift */; };
|
||||
ABCD00092E5CCCC300162C4F /* NostrOutboxService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABCD00072E5CCCC300162C4E /* NostrOutboxService.swift */; };
|
||||
0AE840940F21AFC07C226636 /* PrivateChatE2ETests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A262EDDC04B7D7B5E31F321 /* PrivateChatE2ETests.swift */; };
|
||||
0B6F25559A21F8C69C8357C6 /* BinaryProtocolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B3CC6FA298729906109F61B /* BinaryProtocolTests.swift */; };
|
||||
10E68BB889356219189E38EC /* BitchatApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF625BB3AD919322C01A46B2 /* BitchatApp.swift */; };
|
||||
@@ -219,6 +221,7 @@
|
||||
049BD3A42E51DC0E001A566B /* MessageDeduplicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageDeduplicator.swift; sourceTree = "<group>"; };
|
||||
ABCD00012E5CCCC300162C4A /* ReadReceiptTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadReceiptTracker.swift; sourceTree = "<group>"; };
|
||||
ABCD00042E5CCCC300162C4C /* NostrInboxService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrInboxService.swift; sourceTree = "<group>"; };
|
||||
ABCD00072E5CCCC300162C4E /* NostrOutboxService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrOutboxService.swift; sourceTree = "<group>"; };
|
||||
049BD3AA2E51E38E001A566B /* PeerIDResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeerIDResolver.swift; sourceTree = "<group>"; };
|
||||
049BD3AD2E51ED60001A566B /* Transport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transport.swift; sourceTree = "<group>"; };
|
||||
049BD3B02E51F319001A566B /* MessageRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRouter.swift; sourceTree = "<group>"; };
|
||||
@@ -528,6 +531,7 @@
|
||||
048A4BE62E5CCCC300162C4A /* TransportConfig.swift */,
|
||||
ABCD00012E5CCCC300162C4A /* ReadReceiptTracker.swift */,
|
||||
ABCD00042E5CCCC300162C4C /* NostrInboxService.swift */,
|
||||
ABCD00072E5CCCC300162C4E /* NostrOutboxService.swift */,
|
||||
AA77BB10CC22DD33EE44FF55 /* VerificationService.swift */,
|
||||
047502B82E560F690083520F /* RelayController.swift */,
|
||||
0475028B2E54171C0083520F /* LocationChannelManager.swift */,
|
||||
@@ -753,6 +757,7 @@
|
||||
048A4BE72E5CCCC300162C4A /* TransportConfig.swift in Sources */,
|
||||
ABCD00022E5CCCC300162C4A /* ReadReceiptTracker.swift in Sources */,
|
||||
ABCD00052E5CCCC300162C4C /* NostrInboxService.swift in Sources */,
|
||||
ABCD00082E5CCCC300162C4E /* NostrOutboxService.swift in Sources */,
|
||||
1234567890ABCDEFFEDCBA13 /* PeerDisplayNameResolver.swift in Sources */,
|
||||
AA77BB12CC22DD33EE44FF56 /* VerificationService.swift in Sources */,
|
||||
AA77BB15CC22DD33EE44FF59 /* VerificationViews.swift in Sources */,
|
||||
@@ -814,6 +819,7 @@
|
||||
048A4BE82E5CCCC300162C4A /* TransportConfig.swift in Sources */,
|
||||
ABCD00032E5CCCC300162C4A /* ReadReceiptTracker.swift in Sources */,
|
||||
ABCD00062E5CCCC300162C4D /* NostrInboxService.swift in Sources */,
|
||||
ABCD00092E5CCCC300162C4F /* NostrOutboxService.swift in Sources */,
|
||||
1234567890ABCDEFFEDCBA14 /* PeerDisplayNameResolver.swift in Sources */,
|
||||
AA77BB11CC22DD33EE44FF55 /* VerificationService.swift in Sources */,
|
||||
AA77BB14CC22DD33EE44FF58 /* VerificationViews.swift in Sources */,
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// NostrOutboxService.swift
|
||||
// bitchat
|
||||
//
|
||||
// Centralized Nostr outbox for sending PMs and ACKs.
|
||||
// This is free and unencumbered software released into the public domain.
|
||||
// For more information, see <https://unlicense.org>
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@MainActor
|
||||
final class NostrOutboxService {
|
||||
private let transport: NostrTransport
|
||||
|
||||
init(meshService: Transport) {
|
||||
let t = NostrTransport()
|
||||
t.senderPeerID = meshService.myPeerID
|
||||
self.transport = t
|
||||
}
|
||||
|
||||
func sendGeohashPM(content: String, toRecipientHex recipientHex: String, from identity: NostrIdentity, messageID: String) {
|
||||
transport.sendPrivateMessageGeohash(content: content, toRecipientHex: recipientHex, from: identity, messageID: messageID)
|
||||
}
|
||||
|
||||
func sendGeohashDeliveredAck(messageID: String, toRecipientHex recipientHex: String, from identity: NostrIdentity) {
|
||||
transport.sendDeliveryAckGeohash(for: messageID, toRecipientHex: recipientHex, from: identity)
|
||||
}
|
||||
|
||||
func sendGeohashReadAck(messageID: String, toRecipientHex recipientHex: String, from identity: NostrIdentity) {
|
||||
transport.sendReadReceiptGeohash(messageID, toRecipientHex: recipientHex, from: identity)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,6 +358,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
||||
private var currentGeohash: String? = nil
|
||||
private var geoNicknames: [String: String] = [:] // pubkeyHex(lowercased) -> nickname
|
||||
private let nostrInbox = NostrInboxService()
|
||||
private var nostrOutbox: NostrOutboxService
|
||||
|
||||
// MARK: - Caches
|
||||
|
||||
@@ -463,6 +464,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
||||
self.unifiedPeerService = UnifiedPeerService(meshService: meshService)
|
||||
let nostrTransport = NostrTransport()
|
||||
self.messageRouter = MessageRouter(mesh: meshService, nostr: nostrTransport)
|
||||
self.nostrOutbox = NostrOutboxService(meshService: meshService)
|
||||
// Route receipts from PrivateChatManager through MessageRouter
|
||||
self.privateChatManager.messageRouter = self.messageRouter
|
||||
self.autocompleteService = AutocompleteService()
|
||||
@@ -707,6 +709,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
||||
self.messageRouter = messageRouter
|
||||
self.readReceiptTracker = readReceiptTracker
|
||||
self.autocompleteService = AutocompleteService()
|
||||
self.nostrOutbox = NostrOutboxService(meshService: meshService)
|
||||
|
||||
// Wire up dependencies
|
||||
self.commandProcessor.chatViewModel = self
|
||||
@@ -986,9 +989,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
||||
let messageId = pm.messageID
|
||||
// Send delivery ACK immediately (once per message ID)
|
||||
if !self.sentGeoDeliveryAcks.contains(messageId) {
|
||||
let nt = NostrTransport()
|
||||
nt.senderPeerID = self.meshService.myPeerID
|
||||
nt.sendDeliveryAckGeohash(for: messageId, toRecipientHex: senderPubkey, from: id)
|
||||
self.nostrOutbox.sendGeohashDeliveredAck(messageID: messageId, toRecipientHex: senderPubkey, from: id)
|
||||
self.sentGeoDeliveryAcks.insert(messageId)
|
||||
}
|
||||
// Dedup storage
|
||||
@@ -1024,9 +1025,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
||||
if isViewing {
|
||||
// pared back: omit pre-send READ log
|
||||
if !wasReadBefore {
|
||||
let nt = NostrTransport()
|
||||
nt.senderPeerID = self.meshService.myPeerID
|
||||
nt.sendReadReceiptGeohash(messageId, toRecipientHex: senderPubkey, from: id)
|
||||
self.nostrOutbox.sendGeohashReadAck(messageID: messageId, toRecipientHex: senderPubkey, from: id)
|
||||
self.readReceiptTracker.insert(messageId)
|
||||
}
|
||||
} else {
|
||||
@@ -1663,10 +1662,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
||||
category: SecureLogger.session, level: .info)
|
||||
// Send delivery ACK immediately (even if duplicate), once per messageID
|
||||
if !self.sentGeoDeliveryAcks.contains(messageId) {
|
||||
let nostrTransport = NostrTransport()
|
||||
nostrTransport.senderPeerID = self.meshService.myPeerID
|
||||
// pared back: omit pre-send log
|
||||
nostrTransport.sendDeliveryAckGeohash(for: messageId, toRecipientHex: senderPubkey, from: id)
|
||||
self.nostrOutbox.sendGeohashDeliveredAck(messageID: messageId, toRecipientHex: senderPubkey, from: id)
|
||||
self.sentGeoDeliveryAcks.insert(messageId)
|
||||
}
|
||||
// Duplicate check
|
||||
@@ -1698,9 +1694,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
||||
if isViewing {
|
||||
// pared back: omit pre-send READ log
|
||||
if !wasReadBefore {
|
||||
let nostrTransport = NostrTransport()
|
||||
nostrTransport.senderPeerID = self.meshService.myPeerID
|
||||
nostrTransport.sendReadReceiptGeohash(messageId, toRecipientHex: senderPubkey, from: id)
|
||||
self.nostrOutbox.sendGeohashReadAck(messageID: messageId, toRecipientHex: senderPubkey, from: id)
|
||||
self.readReceiptTracker.insert(messageId)
|
||||
}
|
||||
} else {
|
||||
@@ -2059,9 +2053,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
||||
}
|
||||
SecureLogger.log("GeoDM: local send mid=\(messageID.prefix(8))… to=\(recipientHex.prefix(8))… conv=\(peerID)",
|
||||
category: SecureLogger.session, level: .debug)
|
||||
let nostrTransport = NostrTransport()
|
||||
nostrTransport.senderPeerID = meshService.myPeerID
|
||||
nostrTransport.sendPrivateMessageGeohash(content: content, toRecipientHex: recipientHex, from: id, messageID: messageID)
|
||||
self.nostrOutbox.sendGeohashPM(content: content, toRecipientHex: recipientHex, from: id, messageID: messageID)
|
||||
if let msgIdx = privateChats[peerID]?.firstIndex(where: { $0.id == messageID }) {
|
||||
privateChats[peerID]?[msgIdx].deliveryStatus = .sent
|
||||
}
|
||||
@@ -2773,9 +2765,7 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
|
||||
if !readReceiptTracker.contains(message.id) {
|
||||
SecureLogger.log("GeoDM: sending READ for mid=\(message.id.prefix(8))… to=\(recipientHex.prefix(8))…",
|
||||
category: SecureLogger.session, level: .debug)
|
||||
let nostrTransport = NostrTransport()
|
||||
nostrTransport.senderPeerID = meshService.myPeerID
|
||||
nostrTransport.sendReadReceiptGeohash(message.id, toRecipientHex: recipientHex, from: id)
|
||||
self.nostrOutbox.sendGeohashReadAck(messageID: message.id, toRecipientHex: recipientHex, from: id)
|
||||
readReceiptTracker.insert(message.id)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user