[codex] Refactor BLE outbound scheduling and Noise queues (#1306)

* Refactor BLE transport event handling

* Make image output paths unique

* Keep queued Nostr read receipts alive

* Refine BLE ingress fanout

* Rediscover BLE service after invalidation

* Extract BLE notification retry buffer

* Extract BLE inbound write buffer

* Extract BLE fragment assembly buffer

* Tidy secure log handling from device run

* Extract BLE outbound fragment scheduler

* Harden app CI media tests

* Redact BLE message content from logs

* Extract BLE Noise session queues

* Fix BLE read receipt UI updates

* Allow self-authored RSR ingress replies

* Harden read receipt queue test timing

* Extract BLE outbound policy and incoming file storage

* Avoid duplicate BLE link snapshots during send

* Canonicalize Nostr relay URLs

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
This commit is contained in:
jack
2026-05-31 14:16:11 +02:00
committed by GitHub
co-authored by jack
parent df36b19afe
commit 9e84f5e822
19 changed files with 1084 additions and 629 deletions
@@ -209,34 +209,34 @@ private extension ChatTransportEventCoordinator {
viewModel.meshService.sendDeliveryAck(for: packet.messageID, to: peerID)
case .delivered:
guard let messageID = String(data: payload, encoding: .utf8),
let name = viewModel.unifiedPeerService.getPeer(by: peerID)?.nickname,
let (foundPeerID, index) = findMessageIndex(
for: messageID,
peerID: peerID,
in: viewModel
) else { return }
guard let messageID = String(data: payload, encoding: .utf8) else { return }
if case .read = viewModel.privateChats[foundPeerID]?[index].deliveryStatus { return }
let name = deliveryStatusName(for: peerID, in: viewModel)
let didUpdate = viewModel.deliveryCoordinator.updateMessageDeliveryStatus(
messageID,
status: .delivered(to: name, at: Date())
)
viewModel.privateChats[foundPeerID]?[index].deliveryStatus = .delivered(to: name, at: Date())
viewModel.objectWillChange.send()
if !didUpdate {
if case .read? = viewModel.deliveryCoordinator.deliveryStatus(for: messageID) {
SecureLogger.debug("📬 Ignored stale delivered ACK for already-read message id=\(messageID.prefix(8))… from \(peerID.id.prefix(8))", category: .session)
} else {
SecureLogger.debug("📬 Delivered ACK for unknown message id=\(messageID.prefix(8))… from \(peerID.id.prefix(8))", category: .session)
}
}
case .readReceipt:
guard let messageID = String(data: payload, encoding: .utf8),
let name = viewModel.unifiedPeerService.getPeer(by: peerID)?.nickname,
let (foundPeerID, index) = findMessageIndex(
for: messageID,
peerID: peerID,
in: viewModel
),
let messages = viewModel.privateChats[foundPeerID],
index < messages.count else { return }
guard let messageID = String(data: payload, encoding: .utf8) else { return }
messages[index].deliveryStatus = .read(by: name, at: Date())
viewModel.privateChats[foundPeerID] = messages
viewModel.privateChatManager.objectWillChange.send()
viewModel.objectWillChange.send()
let name = deliveryStatusName(for: peerID, in: viewModel)
let didUpdate = viewModel.deliveryCoordinator.updateMessageDeliveryStatus(
messageID,
status: .read(by: name, at: Date())
)
if !didUpdate {
SecureLogger.debug("📖 Read receipt for unknown message id=\(messageID.prefix(8))… from \(peerID.id.prefix(8))", category: .session)
}
case .verifyChallenge:
viewModel.verificationCoordinator.handleVerifyChallengePayload(from: peerID, payload: payload)
@@ -247,34 +247,7 @@ private extension ChatTransportEventCoordinator {
}
@MainActor
func findMessageIndex(
for messageID: String,
peerID: PeerID,
in viewModel: ChatViewModel
) -> (peerID: PeerID, index: Int)? {
if let messages = viewModel.privateChats[peerID],
let index = messages.firstIndex(where: { $0.id == messageID }) {
return (peerID, index)
}
if peerID.bare.count == 16,
let peer = viewModel.unifiedPeerService.getPeer(by: peerID),
!peer.noisePublicKey.isEmpty {
let longID = PeerID(hexData: peer.noisePublicKey)
if let messages = viewModel.privateChats[longID],
let index = messages.firstIndex(where: { $0.id == messageID }) {
return (longID, index)
}
}
if peerID.bare.count == 64 {
let shortID = peerID.toShort()
if let messages = viewModel.privateChats[shortID],
let index = messages.firstIndex(where: { $0.id == messageID }) {
return (shortID, index)
}
}
return nil
func deliveryStatusName(for peerID: PeerID, in viewModel: ChatViewModel) -> String {
viewModel.unifiedPeerService.getPeer(by: peerID)?.nickname ?? viewModel.resolveNickname(for: peerID)
}
}