mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 04:25:21 +00:00
Cut private message path over to ConversationStore
All private-message mutations now flow through store intents: coordinators, PrivateChatManager (its @Published dicts deleted - now read-only views over the store), outbound sends, delivery status, and chat migration. The O(1) store dedup replaces the full-scan duplicate check; insertion order is maintained by the store so sanitizeChat's re-sort is a documented no-op. Both bootstrapper Combine bridges and the Task.yield store synchronization are deleted. ChatViewModel.privateChats/unreadPrivateMessages become get-only derived views (measured: naive rebuild equals a change-invalidated cache within noise, so the simpler form stays). Feature models still read the legacy store, fed by a coalescing LegacyConversationStoreBridge (one mirror per burst, marked for step-5 deletion). pipeline.privateIngest: 9.6k -> 14.7k msg/s (+53%). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -54,7 +54,7 @@ struct ChatViewModelDeliveryStatusTests {
|
||||
senderPeerID: transport.myPeerID,
|
||||
deliveryStatus: .read(by: "Peer", at: Date())
|
||||
)
|
||||
viewModel.privateChats[peerID] = [message]
|
||||
viewModel.seedPrivateChat([message], for: peerID)
|
||||
|
||||
// Action: try to downgrade to .delivered
|
||||
viewModel.didUpdateMessageDeliveryStatus(messageID, status: .delivered(to: "Peer", at: Date()))
|
||||
@@ -85,7 +85,7 @@ struct ChatViewModelDeliveryStatusTests {
|
||||
senderPeerID: transport.myPeerID,
|
||||
deliveryStatus: .sent
|
||||
)
|
||||
viewModel.privateChats[peerID] = [message]
|
||||
viewModel.seedPrivateChat([message], for: peerID)
|
||||
|
||||
// Action: upgrade to .delivered
|
||||
viewModel.didUpdateMessageDeliveryStatus(messageID, status: .delivered(to: "Peer", at: Date()))
|
||||
@@ -114,7 +114,7 @@ struct ChatViewModelDeliveryStatusTests {
|
||||
senderPeerID: transport.myPeerID,
|
||||
deliveryStatus: .sent
|
||||
)
|
||||
viewModel.privateChats[peerID] = [message]
|
||||
viewModel.seedPrivateChat([message], for: peerID)
|
||||
|
||||
let didUpdate = viewModel.deliveryCoordinator.updateMessageDeliveryStatus(messageID, status: .sent)
|
||||
|
||||
@@ -140,7 +140,7 @@ struct ChatViewModelDeliveryStatusTests {
|
||||
senderPeerID: transport.myPeerID,
|
||||
deliveryStatus: .delivered(to: "Peer", at: Date().addingTimeInterval(-60))
|
||||
)
|
||||
viewModel.privateChats[peerID] = [message]
|
||||
viewModel.seedPrivateChat([message], for: peerID)
|
||||
|
||||
// Action: upgrade to .read
|
||||
viewModel.didUpdateMessageDeliveryStatus(messageID, status: .read(by: "Peer", at: Date()))
|
||||
@@ -173,7 +173,7 @@ struct ChatViewModelDeliveryStatusTests {
|
||||
senderPeerID: transport.myPeerID,
|
||||
deliveryStatus: .sent
|
||||
)
|
||||
viewModel.privateChats[peerID] = [message]
|
||||
viewModel.seedPrivateChat([message], for: peerID)
|
||||
|
||||
// Action: receive read receipt
|
||||
let receipt = ReadReceipt(
|
||||
@@ -207,7 +207,7 @@ struct ChatViewModelDeliveryStatusTests {
|
||||
senderPeerID: transport.myPeerID,
|
||||
deliveryStatus: .sent
|
||||
)
|
||||
viewModel.privateChats[peerID] = [message]
|
||||
viewModel.seedPrivateChat([message], for: peerID)
|
||||
viewModel.sentReadReceipts = ["keep-receipt", "drop-receipt"]
|
||||
viewModel.isStartupPhase = false
|
||||
|
||||
@@ -265,7 +265,7 @@ struct ChatViewModelDeliveryStatusTests {
|
||||
deliveryStatus: .sent
|
||||
)
|
||||
]
|
||||
viewModel.privateChats[firstPeerID] = [
|
||||
viewModel.seedPrivateChat([
|
||||
BitchatMessage(
|
||||
id: messageID,
|
||||
sender: viewModel.nickname,
|
||||
@@ -277,8 +277,8 @@ struct ChatViewModelDeliveryStatusTests {
|
||||
senderPeerID: transport.myPeerID,
|
||||
deliveryStatus: .sent
|
||||
)
|
||||
]
|
||||
viewModel.privateChats[secondPeerID] = [
|
||||
], for: firstPeerID)
|
||||
viewModel.seedPrivateChat([
|
||||
BitchatMessage(
|
||||
id: messageID,
|
||||
sender: viewModel.nickname,
|
||||
@@ -290,7 +290,7 @@ struct ChatViewModelDeliveryStatusTests {
|
||||
senderPeerID: transport.myPeerID,
|
||||
deliveryStatus: .sent
|
||||
)
|
||||
]
|
||||
], for: secondPeerID)
|
||||
|
||||
let didUpdate = viewModel.deliveryCoordinator.updateMessageDeliveryStatus(
|
||||
messageID,
|
||||
@@ -331,10 +331,15 @@ struct ChatViewModelDeliveryStatusTests {
|
||||
deliveryStatus: .sent
|
||||
)
|
||||
|
||||
viewModel.privateChats[peerID] = [targetMessage, olderMessage]
|
||||
viewModel.seedPrivateChat([targetMessage], for: peerID)
|
||||
#expect(isSent(viewModel.deliveryCoordinator.deliveryStatus(for: messageID)))
|
||||
|
||||
viewModel.privateChats[peerID] = [olderMessage, targetMessage]
|
||||
// A late arrival with an older timestamp is inserted before the
|
||||
// target by the store, shifting its position and invalidating the
|
||||
// delivery coordinator's location index.
|
||||
viewModel.seedPrivateChat([olderMessage], for: peerID)
|
||||
#expect(viewModel.privateChats[peerID]?.map(\.id) == ["older-msg", messageID])
|
||||
|
||||
let didUpdate = viewModel.deliveryCoordinator.updateMessageDeliveryStatus(
|
||||
messageID,
|
||||
status: .read(by: "Peer", at: Date())
|
||||
@@ -401,6 +406,20 @@ private final class MockChatDeliveryContext: ChatDeliveryContext {
|
||||
func markMessageDelivered(_ messageID: String) {
|
||||
markedDeliveredMessageIDs.append(messageID)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func setPrivateDeliveryStatus(_ status: DeliveryStatus, forMessageID messageID: String, peerID: PeerID) -> Bool {
|
||||
guard var chat = privateChats[peerID],
|
||||
let index = chat.firstIndex(where: { $0.id == messageID }) else {
|
||||
return false
|
||||
}
|
||||
if Conversation.shouldSkipStatusUpdate(current: chat[index].deliveryStatus, new: status) {
|
||||
return false
|
||||
}
|
||||
chat[index].deliveryStatus = status
|
||||
privateChats[peerID] = chat
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
|
||||
Reference in New Issue
Block a user