mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-26 11:45:19 +00:00
Cut public message path over to ConversationStore; delete PublicTimelineStore
Mesh and geohash timelines are now store conversations. All public mutation sites flow through store intents; PublicMessagePipeline keeps its 80ms UI batching but commits batches via store appends with each buffered entry carrying its destination conversation (a mid-batch channel switch now flushes instead of dropping the buffer). ChatViewModel.messages becomes a cached get-only view of the active conversation, invalidated through the change subject. The mesh late-insert threshold is consciously removed: it only ever ordered the non-rendered messages copy, so strict timestamp insertion makes the working set agree with rendered order. PublicTimelineStore and the per-message full-array legacy sync are deleted; the coalescing bridge mirrors public conversations for the remaining legacy readers. pipeline.publicIngest: 6.6k -> 9.5k msg/s (+45%); private steady; store.append 237k/s. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -138,6 +138,23 @@ final class Conversation: ObservableObject, Identifiable {
|
||||
return removed
|
||||
}
|
||||
|
||||
/// Removes every message matching `predicate`. Returns the removed
|
||||
/// message IDs (empty when nothing matched).
|
||||
fileprivate func removeAll(where predicate: (BitchatMessage) -> Bool) -> [String] {
|
||||
var removedIDs: [String] = []
|
||||
messages.removeAll { message in
|
||||
guard predicate(message) else { return false }
|
||||
removedIDs.append(message.id)
|
||||
return true
|
||||
}
|
||||
guard !removedIDs.isEmpty else { return [] }
|
||||
for id in removedIDs {
|
||||
indexByMessageID.removeValue(forKey: id)
|
||||
}
|
||||
reindex(from: 0)
|
||||
return removedIDs
|
||||
}
|
||||
|
||||
fileprivate func clearMessages() {
|
||||
messages.removeAll()
|
||||
indexByMessageID.removeAll()
|
||||
@@ -346,6 +363,16 @@ final class ConversationStore: ObservableObject {
|
||||
return removed
|
||||
}
|
||||
|
||||
/// Removes every message matching `predicate` from a conversation,
|
||||
/// emitting one `.messageRemoved` per removed message after the
|
||||
/// conversation is consistent. No-op for unknown conversations.
|
||||
func removeMessages(from id: ConversationID, where predicate: (BitchatMessage) -> Bool) {
|
||||
guard let conversation = conversationsByID[id] else { return }
|
||||
for messageID in conversation.removeAll(where: predicate) {
|
||||
changes.send(.messageRemoved(id, messageID: messageID))
|
||||
}
|
||||
}
|
||||
|
||||
/// Empties a conversation's timeline but keeps the conversation (and
|
||||
/// its unread/selection state) alive.
|
||||
func clear(_ id: ConversationID) {
|
||||
@@ -464,3 +491,26 @@ extension ConversationStore {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Migration step 3 compatibility (public timeline derived views)
|
||||
|
||||
extension ConversationStore {
|
||||
/// Removes a message by ID from whichever public (mesh/geohash)
|
||||
/// conversation contains it — the compat shape of the legacy
|
||||
/// `PublicTimelineStore.removeMessage(withID:)`. Returns the removed
|
||||
/// message, if any.
|
||||
@discardableResult
|
||||
func removePublicMessage(withID messageID: String) -> BitchatMessage? {
|
||||
for (id, conversation) in conversationsByID {
|
||||
switch id {
|
||||
case .mesh, .geohash:
|
||||
if conversation.containsMessage(withID: messageID) {
|
||||
return removeMessage(withID: messageID, from: id)
|
||||
}
|
||||
case .direct:
|
||||
continue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user