diff --git a/Postbox.xcodeproj/project.pbxproj b/Postbox.xcodeproj/project.pbxproj index f458412d93..6229411d59 100644 --- a/Postbox.xcodeproj/project.pbxproj +++ b/Postbox.xcodeproj/project.pbxproj @@ -61,6 +61,8 @@ D04614312004E24600EC0EF2 /* LocalMessageHistoryTagsTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D046142F2004E24600EC0EF2 /* LocalMessageHistoryTagsTable.swift */; }; D04614332004F2CC00EC0EF2 /* LocalMessageTagsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04614322004F2CC00EC0EF2 /* LocalMessageTagsView.swift */; }; D04614342004F2CC00EC0EF2 /* LocalMessageTagsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04614322004F2CC00EC0EF2 /* LocalMessageTagsView.swift */; }; + D048B33D203C838500038D05 /* PostboxUpgrade_15to16.swift in Sources */ = {isa = PBXBuildFile; fileRef = D048B33C203C838500038D05 /* PostboxUpgrade_15to16.swift */; }; + D048B33E203C838500038D05 /* PostboxUpgrade_15to16.swift in Sources */ = {isa = PBXBuildFile; fileRef = D048B33C203C838500038D05 /* PostboxUpgrade_15to16.swift */; }; D049EAF01E44D9B900A2CD3A /* PostboxStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D049EAEF1E44D9B900A2CD3A /* PostboxStateView.swift */; }; D049EAF11E44D9B900A2CD3A /* PostboxStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D049EAEF1E44D9B900A2CD3A /* PostboxStateView.swift */; }; D050F2651E4A5B4800988324 /* fts3_tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = D07516741B2EC90400AE42E0 /* fts3_tokenizer.h */; }; @@ -404,6 +406,7 @@ D044E1621B2AD677001EE087 /* MurMurHash32.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MurMurHash32.m; sourceTree = ""; }; D046142F2004E24600EC0EF2 /* LocalMessageHistoryTagsTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalMessageHistoryTagsTable.swift; sourceTree = ""; }; D04614322004F2CC00EC0EF2 /* LocalMessageTagsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalMessageTagsView.swift; sourceTree = ""; }; + D048B33C203C838500038D05 /* PostboxUpgrade_15to16.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostboxUpgrade_15to16.swift; sourceTree = ""; }; D049EAEF1E44D9B900A2CD3A /* PostboxStateView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostboxStateView.swift; sourceTree = ""; }; D055BD321B7D3D2D00F06C0A /* MediaBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaBox.swift; sourceTree = ""; }; D0575AE21E9ECBB2006F2541 /* AccessChallengeDataView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessChallengeDataView.swift; sourceTree = ""; }; @@ -660,6 +663,7 @@ D0F02CE11E9922F50065DEE2 /* PostboxUpgrade_12to13.swift */, D0CE8CF21F70249400AA2DB0 /* PostboxUpgrade_13to14.swift */, D0943B011FDB01D8001522CC /* PostboxUpgrade_14to15.swift */, + D048B33C203C838500038D05 /* PostboxUpgrade_15to16.swift */, ); name = Upgrade; sourceTree = ""; @@ -1167,6 +1171,7 @@ D073CEA01DCBF3C1007511FD /* ItemCollectionsView.swift in Sources */, D0BE383A1E7C1FD4000079AF /* ItemCollectionInfoView.swift in Sources */, D0B418491D7DFE20004562A4 /* ChatListView.swift in Sources */, + D048B33E203C838500038D05 /* PostboxUpgrade_15to16.swift in Sources */, D0F7B1D61E045C6A007EB8A5 /* ContactTable.swift in Sources */, D0943B031FDB01D8001522CC /* PostboxUpgrade_14to15.swift in Sources */, D0F7B1D11E045C6A007EB8A5 /* PeerTable.swift in Sources */, @@ -1341,6 +1346,7 @@ D0C26D6F1FE2E737004ABF18 /* GroupFeedReadState.swift in Sources */, D0FA0AC71E77F0A2005BB9B7 /* ItemCollectionInfosView.swift in Sources */, D0F019FD1E1DA0CC00F05AB3 /* PeerMergedOperationLogView.swift in Sources */, + D048B33D203C838500038D05 /* PostboxUpgrade_15to16.swift in Sources */, D0F9E8651C58CB7F00037222 /* ChatListTable.swift in Sources */, D0E1D30C1ECA1F5500FCEEF1 /* GlobalMessageHistoryTagsTable.swift in Sources */, D0F9E8711C5A0E9B00037222 /* PeerTable.swift in Sources */, diff --git a/Postbox/CachedPeerData.swift b/Postbox/CachedPeerData.swift index dcf745b514..582d4dcdc6 100644 --- a/Postbox/CachedPeerData.swift +++ b/Postbox/CachedPeerData.swift @@ -3,7 +3,7 @@ public protocol CachedPeerData: PostboxCoding { var peerIds: Set { get } var messageIds: Set { get } - var associatedHistoryPeerId: PeerId? { get } + var associatedHistoryMessageId: MessageId? { get } func isEqual(to: CachedPeerData) -> Bool } diff --git a/Postbox/ChatListIndexTable.swift b/Postbox/ChatListIndexTable.swift index d6742406c7..fc9079f892 100644 --- a/Postbox/ChatListIndexTable.swift +++ b/Postbox/ChatListIndexTable.swift @@ -253,7 +253,7 @@ final class ChatListIndexTable: Table { assert(self.updatedPreviousGroupCachedIndices.isEmpty) } - func commitWithTransactionUnreadCountDeltas(_ deltas: [PeerId: Int32], transactionParticipationInTotalUnreadCountUpdates: (added: Set, removed: Set), getPeer: (PeerId) -> Peer?, updatedTotalUnreadCount: inout Int32?) { + func commitWithTransactionUnreadCountDeltas(_ deltas: [PeerId: Int32], transactionParticipationInTotalUnreadCountUpdates: (added: Set, removed: Set), getPeer: (PeerId) -> Peer?, updatedTotalUnreadState: inout ChatListTotalUnreadState?) { if !self.updatedPreviousPeerCachedIndices.isEmpty || !deltas.isEmpty || !transactionParticipationInTotalUnreadCountUpdates.added.isEmpty || !transactionParticipationInTotalUnreadCountUpdates.removed.isEmpty { var addedChatListPeerIds = Set() var removedChatListPeerIds = Set() @@ -326,7 +326,7 @@ final class ChatListIndexTable: Table { let addedUnreadCountPeerIds = addedChatListPeerIds.union(transactionParticipationInTotalUnreadCountUpdates.added) let removedUnreadCountPeerIds = removedChatListPeerIds.union(transactionParticipationInTotalUnreadCountUpdates.removed) - var totalUnreadCount = self.metadataTable.getChatListTotalUnreadCount() + var totalUnreadState = self.metadataTable.getChatListTotalUnreadState() for (peerId, delta) in deltas { if !addedUnreadCountPeerIds.contains(peerId) && !removedUnreadCountPeerIds.contains(peerId) { var notificationSettings: PeerNotificationSettings? @@ -337,8 +337,11 @@ final class ChatListIndexTable: Table { notificationSettings = self.notificationSettingsTable.getEffective(peerId) } } - if let _ = self.get(peerId: peerId).includedIndex(peerId: peerId), let notificationSettings = notificationSettings, !notificationSettings.isRemovedFromTotalUnreadCount { - totalUnreadCount += delta + if let _ = self.get(peerId: peerId).includedIndex(peerId: peerId) { + totalUnreadState.absoluteCounters.messageCount += delta + if let notificationSettings = notificationSettings, !notificationSettings.isRemovedFromTotalUnreadCount { + totalUnreadState.filteredCounters.messageCount += delta + } } } } @@ -353,7 +356,8 @@ final class ChatListIndexTable: Table { if addedToList && startedParticipationInUnreadCount { if let combinedState = self.readStateTable.getCombinedState(peerId) { - totalUnreadCount += combinedState.count + totalUnreadState.absoluteCounters.messageCount += combinedState.count + totalUnreadState.filteredCounters.messageCount += combinedState.count } } else if addedToList { var notificationSettings: PeerNotificationSettings? @@ -364,15 +368,16 @@ final class ChatListIndexTable: Table { notificationSettings = self.notificationSettingsTable.getEffective(peerId) } } - if let notificationSettings = notificationSettings, !notificationSettings.isRemovedFromTotalUnreadCount { - if let combinedState = self.readStateTable.getCombinedState(peerId) { - totalUnreadCount += combinedState.count + if let combinedState = self.readStateTable.getCombinedState(peerId) { + totalUnreadState.absoluteCounters.messageCount += combinedState.count + if let notificationSettings = notificationSettings, !notificationSettings.isRemovedFromTotalUnreadCount { + totalUnreadState.filteredCounters.messageCount += combinedState.count } } } else if startedParticipationInUnreadCount { if let _ = self.get(peerId: peerId).includedIndex(peerId: peerId) { if let combinedState = self.readStateTable.getCombinedState(peerId) { - totalUnreadCount += combinedState.count + totalUnreadState.filteredCounters.messageCount += combinedState.count } } } else { @@ -397,7 +402,8 @@ final class ChatListIndexTable: Table { let removedFromList = removedChatListPeerIds.contains(peerId) let removedFromParticipationInUnreadCount = transactionParticipationInTotalUnreadCountUpdates.removed.contains(peerId) if removedFromList && removedFromParticipationInUnreadCount { - totalUnreadCount -= currentPeerUnreadCount + totalUnreadState.absoluteCounters.messageCount -= currentPeerUnreadCount + totalUnreadState.filteredCounters.messageCount -= currentPeerUnreadCount } else if removedFromList { var notificationSettings: PeerNotificationSettings? if let peer = getPeer(peerId) { @@ -407,12 +413,13 @@ final class ChatListIndexTable: Table { notificationSettings = self.notificationSettingsTable.getEffective(peerId) } } + totalUnreadState.absoluteCounters.messageCount -= currentPeerUnreadCount if let notificationSettings = notificationSettings, !notificationSettings.isRemovedFromTotalUnreadCount { - totalUnreadCount -= currentPeerUnreadCount + totalUnreadState.filteredCounters.messageCount -= currentPeerUnreadCount } } else if removedFromParticipationInUnreadCount { if let _ = self.get(peerId: peerId).includedIndex(peerId: peerId) { - totalUnreadCount -= currentPeerUnreadCount + totalUnreadState.filteredCounters.messageCount -= currentPeerUnreadCount } } else { assertionFailure() @@ -421,9 +428,9 @@ final class ChatListIndexTable: Table { //assert(totalUnreadCount >= 0) - if self.metadataTable.getChatListTotalUnreadCount() != totalUnreadCount { - self.metadataTable.setChatListTotalUnreadCount(totalUnreadCount) - updatedTotalUnreadCount = totalUnreadCount + if self.metadataTable.getChatListTotalUnreadState() != totalUnreadState { + self.metadataTable.setChatListTotalUnreadState(totalUnreadState) + updatedTotalUnreadState = totalUnreadState } } @@ -476,4 +483,30 @@ final class ChatListIndexTable: Table { assert(self.updatedPreviousPeerCachedIndices.isEmpty) assert(self.updatedPreviousGroupCachedIndices.isEmpty) } + + func debugReindexUnreadCounts(postbox: Postbox) -> ChatListTotalUnreadState { + var peerIds: [PeerId] = [] + self.valueBox.scanInt64(self.table, values: { key, _ in + let peerId = PeerId(key) + if peerId.namespace != Int32.max { + peerIds.append(peerId) + } + return true + }) + var state = ChatListTotalUnreadState(absoluteCounters: ChatListTotalUnreadCounters(messageCount: 0), filteredCounters: ChatListTotalUnreadCounters(messageCount: 0)) + for peerId in peerIds { + let inclusion = self.get(peerId: peerId) + if inclusion.includedIndex(peerId: peerId) != nil { + if let combinedState = postbox.readStateTable.getCombinedState(peerId) { + state.absoluteCounters.messageCount += combinedState.count + + if let notificationSettings = postbox.peerNotificationSettingsTable.getEffective(peerId), !notificationSettings.isRemovedFromTotalUnreadCount { + state.filteredCounters.messageCount += combinedState.count + } + } + } + } + + return state + } } diff --git a/Postbox/FileSize.swift b/Postbox/FileSize.swift index 20510f7ab7..b366746e60 100644 --- a/Postbox/FileSize.swift +++ b/Postbox/FileSize.swift @@ -1,6 +1,6 @@ import Foundation -func fileSize(_ path: String) -> Int? { +public func fileSize(_ path: String) -> Int? { var value = stat() if stat(path, &value) == 0 { return Int(value.st_size) diff --git a/Postbox/MediaBox.swift b/Postbox/MediaBox.swift index 1336c556c0..8d0d25910e 100644 --- a/Postbox/MediaBox.swift +++ b/Postbox/MediaBox.swift @@ -173,6 +173,13 @@ public final class MediaBox { } } + public func moveResourceData(_ id: MediaResourceId, fromTempPath: String) { + self.dataQueue.async { + let paths = self.storePathsForId(id) + let _ = try? FileManager.default.moveItem(at: URL(fileURLWithPath: fromTempPath), to: URL(fileURLWithPath: paths.complete)) + } + } + public func moveResourceData(from: MediaResourceId, to: MediaResourceId) { self.dataQueue.async { let pathsFrom = self.storePathsForId(from) @@ -241,9 +248,9 @@ public final class MediaBox { } } - disposable.set(ActionDisposable { + disposable.set(ActionDisposable { [weak statusContext] in self.statusQueue.async { - if let current = self.statusContexts[WrappedMediaResourceId(resource.id)] { + if let current = self.statusContexts[WrappedMediaResourceId(resource.id)], current === statusContext { current.subscribers.remove(index) if current.subscribers.isEmpty { self.statusContexts.removeValue(forKey: WrappedMediaResourceId(resource.id)) diff --git a/Postbox/MediaBoxFile.swift b/Postbox/MediaBoxFile.swift index a8a258ebaf..d6eac48a02 100644 --- a/Postbox/MediaBoxFile.swift +++ b/Postbox/MediaBoxFile.swift @@ -396,7 +396,9 @@ final class MediaBoxPartialFile { self.statusRequests.removeAll() self.fd.sync() let linkResult = link(self.path, self.completePath) - assert(linkResult == 0) + if linkResult != 0 { + //assert(linkResult == 0) + } self.completed(self.fileMap.sum) } } diff --git a/Postbox/MessageHistoryMetadataTable.swift b/Postbox/MessageHistoryMetadataTable.swift index c4bebd6fec..f2709a1d88 100644 --- a/Postbox/MessageHistoryMetadataTable.swift +++ b/Postbox/MessageHistoryMetadataTable.swift @@ -5,12 +5,65 @@ private enum MetadataPrefix: Int8 { case PeerHistoryInitialized = 1 case PeerNextMessageIdByNamespace = 2 case NextStableMessageId = 3 - case ChatListTotalUnreadCount = 4 + case ChatListTotalUnreadState = 4 case NextPeerOperationLogIndex = 5 case ChatListGroupInitialized = 6 case GroupFeedIndexInitialized = 7 } +public struct ChatListTotalUnreadCounters: PostboxCoding, Equatable { + public var messageCount: Int32 + + public init(messageCount: Int32) { + self.messageCount = messageCount + } + + public init(decoder: PostboxDecoder) { + self.messageCount = decoder.decodeInt32ForKey("m", orElse: 0) + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt32(self.messageCount, forKey: "m") + } + + public static func ==(lhs: ChatListTotalUnreadCounters, rhs: ChatListTotalUnreadCounters) -> Bool { + if lhs.messageCount != rhs.messageCount { + return false + } + return true + } +} + +public struct ChatListTotalUnreadState: PostboxCoding, Equatable { + public var absoluteCounters: ChatListTotalUnreadCounters + public var filteredCounters: ChatListTotalUnreadCounters + + public init(absoluteCounters: ChatListTotalUnreadCounters, filteredCounters: ChatListTotalUnreadCounters) { + self.absoluteCounters = absoluteCounters + self.filteredCounters = filteredCounters + } + + public init(decoder: PostboxDecoder) { + self.absoluteCounters = decoder.decodeObjectForKey("a", decoder: { ChatListTotalUnreadCounters(decoder: $0) }) as! ChatListTotalUnreadCounters + self.filteredCounters = decoder.decodeObjectForKey("f", decoder: { ChatListTotalUnreadCounters(decoder: $0) }) as! ChatListTotalUnreadCounters + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeObject(self.absoluteCounters, forKey: "a") + encoder.encodeObject(self.filteredCounters, forKey: "f") + } + + public static func ==(lhs: ChatListTotalUnreadState, rhs: ChatListTotalUnreadState) -> Bool { + if lhs.absoluteCounters != rhs.absoluteCounters { + return false + } + if lhs.filteredCounters != rhs.filteredCounters { + return false + } + return true + } +} + private enum InitializedChatListKey: Hashable { case global case group(PeerGroupId) @@ -80,8 +133,8 @@ final class MessageHistoryMetadataTable: Table { private var nextMessageStableId: UInt32? private var nextMessageStableIdUpdated = false - private var chatListTotalUnreadCount: Int32? - private var chatListTotalUnreadCountUpdated = false + private var chatListTotalUnreadState: ChatListTotalUnreadState? + private var chatListTotalUnreadStateUpdated = false private var nextPeerOperationLogIndex: UInt32? private var nextPeerOperationLogIndexUpdated = false @@ -272,27 +325,28 @@ final class MessageHistoryMetadataTable: Table { } } - func getChatListTotalUnreadCount() -> Int32 { - if let cached = self.chatListTotalUnreadCount { + func getChatListTotalUnreadState() -> ChatListTotalUnreadState { + if let cached = self.chatListTotalUnreadState { return cached } else { - if let value = self.valueBox.get(self.table, key: self.key(.ChatListTotalUnreadCount)) { - var count: Int32 = 0 - value.read(&count, offset: 0, length: 4) - self.chatListTotalUnreadCount = count - return count + if let value = self.valueBox.get(self.table, key: self.key(.ChatListTotalUnreadState)), let state = PostboxDecoder(buffer: value).decodeObjectForKey("_", decoder: { + ChatListTotalUnreadState(decoder: $0) + }) as? ChatListTotalUnreadState { + self.chatListTotalUnreadState = state + return state } else { - self.chatListTotalUnreadCount = 0 - return 0 + let state = ChatListTotalUnreadState(absoluteCounters: ChatListTotalUnreadCounters(messageCount: 0), filteredCounters: ChatListTotalUnreadCounters(messageCount: 0)) + self.chatListTotalUnreadState = state + return state } } } - func setChatListTotalUnreadCount(_ value: Int32) { - let current = self.getChatListTotalUnreadCount() - if current != value { - self.chatListTotalUnreadCount = value - self.chatListTotalUnreadCountUpdated = true + func setChatListTotalUnreadState(_ state: ChatListTotalUnreadState) { + let current = self.getChatListTotalUnreadState() + if current != state { + self.chatListTotalUnreadState = state + self.chatListTotalUnreadStateUpdated = true } } @@ -303,8 +357,8 @@ final class MessageHistoryMetadataTable: Table { self.updatedPeerNextMessageIdByNamespace.removeAll() self.nextMessageStableId = nil self.nextMessageStableIdUpdated = false - self.chatListTotalUnreadCount = nil - self.chatListTotalUnreadCountUpdated = false + self.chatListTotalUnreadState = nil + self.chatListTotalUnreadStateUpdated = false } override func beforeCommit() { @@ -339,12 +393,13 @@ final class MessageHistoryMetadataTable: Table { } } - if self.chatListTotalUnreadCountUpdated { - if let value = self.chatListTotalUnreadCount { - var count: Int32 = value - self.valueBox.set(self.table, key: self.key(.ChatListTotalUnreadCount), value: MemoryBuffer(memory: &count, capacity: 4, length: 4, freeWhenDone: false)) + if self.chatListTotalUnreadStateUpdated { + if let state = self.chatListTotalUnreadState { + let buffer = PostboxEncoder() + buffer.encodeObject(state, forKey: "_") + self.valueBox.set(self.table, key: self.key(.ChatListTotalUnreadState), value: buffer.readBufferNoCopy()) } - self.chatListTotalUnreadCountUpdated = false + self.chatListTotalUnreadStateUpdated = false } } } diff --git a/Postbox/MessageHistoryReadStateTable.swift b/Postbox/MessageHistoryReadStateTable.swift index 1f64c309c4..9a5fdb24a6 100644 --- a/Postbox/MessageHistoryReadStateTable.swift +++ b/Postbox/MessageHistoryReadStateTable.swift @@ -228,7 +228,13 @@ final class MessageHistoryReadStateTable: Table { self.markReadStatesAsUpdated(peerId, namespaces: states.namespaces) - states.namespaces[namespace] = currentState.withAddedCount(Int32(-knownCount)) + var updatedState = currentState.withAddedCount(Int32(-knownCount)) + if updatedState.count < 0 { + invalidate = true + updatedState = currentState.withAddedCount(-updatedState.count) + } + + states.namespaces[namespace] = updatedState updated = true } else { invalidate = true diff --git a/Postbox/MessageHistoryTable.swift b/Postbox/MessageHistoryTable.swift index 5e62247aa5..e9766a1629 100644 --- a/Postbox/MessageHistoryTable.swift +++ b/Postbox/MessageHistoryTable.swift @@ -409,7 +409,7 @@ final class MessageHistoryTable: Table { private func processIndexOperationsCommitAccumulatedRemoveIndices(peerId: PeerId, accumulatedRemoveIndices: inout [(MessageIndex, Bool)], updatedCombinedState: inout CombinedPeerReadState?, invalidateReadState: inout Bool, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], outputOperations: inout [MessageHistoryOperation], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], groupFeedOperations: inout [PeerGroupId : [GroupFeedIndexOperation]], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation]) { if !accumulatedRemoveIndices.isEmpty { - let (combinedState, invalidate) = self.readStateTable.deleteMessages(peerId, indices: accumulatedRemoveIndices.map { $0.0 }, incomingStatsInIndices: { peerId, namespace, indices in + let (combinedState, invalidate) = self.readStateTable.deleteMessages(peerId, indices: accumulatedRemoveIndices.filter({ $0.1 }).map({ $0.0 }), incomingStatsInIndices: { peerId, namespace, indices in return self.incomingMessageStatsInIndices(peerId, namespace: namespace, indices: indices) }) if let combinedState = combinedState { @@ -729,7 +729,10 @@ final class MessageHistoryTable: Table { func clearHistory(peerId: PeerId, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], groupFeedOperations: inout [PeerGroupId : [GroupFeedIndexOperation]], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation]) { let indices = self.allIndices(peerId) - self.removeMessages(indices.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, groupFeedOperations: &groupFeedOperations, localTagsOperations: &localTagsOperations) + for index in indices.holes { + self.fillHole(index.id, fillType: HoleFill(complete: true, direction: .UpperToLower(updatedMinIndex: nil, clippingMaxIndex: nil)), tagMask: nil, messages: [], operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, groupFeedOperations: &groupFeedOperations, localTagsOperations: &localTagsOperations) + } + self.removeMessages(indices.messages.map { $0.id }, operationsByPeerId: &operationsByPeerId, updatedMedia: &updatedMedia, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations, globalTagsOperations: &globalTagsOperations, pendingActionsOperations: &pendingActionsOperations, updatedMessageActionsSummaries: &updatedMessageActionsSummaries, updatedMessageTagSummaries: &updatedMessageTagSummaries, invalidateMessageTagSummaries: &invalidateMessageTagSummaries, groupFeedOperations: &groupFeedOperations, localTagsOperations: &localTagsOperations) } func removeAllMessagesWithAuthor(peerId: PeerId, authorId: PeerId, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?], globalTagsOperations: inout [GlobalMessageHistoryTagsOperation], pendingActionsOperations: inout [PendingMessageActionsOperation], updatedMessageActionsSummaries: inout [PendingMessageActionsSummaryKey: Int32], updatedMessageTagSummaries: inout [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], invalidateMessageTagSummaries: inout [InvalidatedMessageHistoryTagsSummaryEntryOperation], groupFeedOperations: inout [PeerGroupId : [GroupFeedIndexOperation]], localTagsOperations: inout [IntermediateMessageHistoryLocalTagsOperation]) { @@ -3034,14 +3037,19 @@ final class MessageHistoryTable: Table { return messageIds } - func allIndices(_ peerId: PeerId) -> [MessageIndex] { - var indices: [MessageIndex] = [] - self.valueBox.range(self.table, start: self.key(MessageIndex.lowerBound(peerId: peerId)).predecessor, end: self.key(MessageIndex.upperBound(peerId: peerId)).successor, keys: { key in + func allIndices(_ peerId: PeerId) -> (messages: [MessageIndex], holes: [MessageIndex]) { + var messages: [MessageIndex] = [] + var holes: [MessageIndex] = [] + self.valueBox.range(self.table, start: self.key(MessageIndex.lowerBound(peerId: peerId)).predecessor, end: self.key(MessageIndex.upperBound(peerId: peerId)).successor, values: { key, value in let index = MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(0)), namespace: key.getInt32(8 + 4), id: key.getInt32(8 + 4 + 4)), timestamp: key.getInt32(8)) - indices.append(index) + if extractIntermediateEntryIsMessage(value: value) { + messages.append(index) + } else { + holes.append(index) + } return true }, limit: 0) - return indices + return (messages, holes) } func allMessageIndices(_ peerId: PeerId) -> [MessageIndex] { diff --git a/Postbox/MessageHistoryView.swift b/Postbox/MessageHistoryView.swift index d77f554198..9d20829473 100644 --- a/Postbox/MessageHistoryView.swift +++ b/Postbox/MessageHistoryView.swift @@ -14,7 +14,7 @@ public enum AdditionalMessageHistoryViewData { case cachedPeerDataMessages(PeerId) case peerChatState(PeerId) case peerGroupState(PeerGroupId) - case totalUnreadCount + case totalUnreadState case peerNotificationSettings(PeerId) } @@ -23,7 +23,7 @@ public enum AdditionalMessageHistoryViewDataEntry { case cachedPeerDataMessages(PeerId, [MessageId: Message]?) case peerChatState(PeerId, PeerChatState?) case peerGroupState(PeerGroupId, PeerGroupState?) - case totalUnreadCount(Int32) + case totalUnreadState(ChatListTotalUnreadState) case peerNotificationSettings(PeerNotificationSettings?) } @@ -423,7 +423,7 @@ private func monthUpperBoundIndex(peerId: PeerId, index: MessageMonthIndex) -> M public enum MessageHistoryViewPeerIds: Equatable { case single(PeerId) - case associated(PeerId, PeerId?) + case associated(PeerId, MessageId?) case group(PeerGroupId) public static func ==(lhs: MessageHistoryViewPeerIds, rhs: MessageHistoryViewPeerIds) -> Bool { @@ -450,6 +450,46 @@ public enum MessageHistoryViewPeerIds: Equatable { } } +private func clipMessages(peerIds: MessageHistoryViewPeerIds, entries: [MutableMessageHistoryEntry]) -> [MutableMessageHistoryEntry] { + switch peerIds { + case .single, .group: + return entries + case let .associated(_, associatedId): + if let associatedId = associatedId { + var result: [MutableMessageHistoryEntry] = [] + for entry in entries { + switch entry { + case let .MessageEntry(message, _, _): + if message.id.peerId == associatedId.peerId { + if message.id.namespace == associatedId.namespace && message.id.id <= associatedId.id { + result.append(entry) + } + } else { + result.append(entry) + } + case let .IntermediateMessageEntry(message, _, _): + if message.id.peerId == associatedId.peerId { + if message.id.namespace == associatedId.namespace && message.id.id <= associatedId.id { + result.append(entry) + } + } else { + result.append(entry) + } + case .HoleEntry: + result.append(entry) + } + } + return result + } else { + return entries + } + } +} + +private func clipAround(peerIds: MessageHistoryViewPeerIds, _ data: (entries: [MutableMessageHistoryEntry], lower: MutableMessageHistoryEntry?, upper: MutableMessageHistoryEntry?)) -> (entries: [MutableMessageHistoryEntry], lower: MutableMessageHistoryEntry?, upper: MutableMessageHistoryEntry?) { + return (entries: clipMessages(peerIds: peerIds, entries: data.entries), lower: data.lower, upper: data.upper) +} + private func fetchAround(postbox: Postbox, peerIds: MessageHistoryViewPeerIds, index: InternalMessageHistoryAnchorIndex, count: Int, tagMask: MessageTags?) -> (entries: [MutableMessageHistoryEntry], lower: MutableMessageHistoryEntry?, upper: MutableMessageHistoryEntry?) { var ids: [PeerId] = [] switch peerIds { @@ -458,7 +498,7 @@ private func fetchAround(postbox: Postbox, peerIds: MessageHistoryViewPeerIds, i case let .associated(mainId, associatedId): ids.append(mainId) if let associatedId = associatedId { - ids.append(associatedId) + ids.append(associatedId.peerId) } case let .group(groupId): switch index { @@ -473,11 +513,11 @@ private func fetchAround(postbox: Postbox, peerIds: MessageHistoryViewPeerIds, i switch index { case let .message(index: index, _): - return postbox.fetchAroundHistoryEntries(peerIds: ids, index: index, count: count, tagMask: tagMask) + return clipAround(peerIds: peerIds, postbox.fetchAroundHistoryEntries(peerIds: ids, index: index, count: count, tagMask: tagMask)) case .upperBound: - return postbox.fetchAroundHistoryEntries(peerIds: ids, index: MessageIndex.absoluteUpperBound(), count: count, tagMask: tagMask) + return clipAround(peerIds: peerIds, postbox.fetchAroundHistoryEntries(peerIds: ids, index: MessageIndex.absoluteUpperBound(), count: count, tagMask: tagMask)) case .lowerBound: - return postbox.fetchAroundHistoryEntries(peerIds: ids, index: MessageIndex.absoluteLowerBound(), count: count, tagMask: tagMask) + return clipAround(peerIds: peerIds, postbox.fetchAroundHistoryEntries(peerIds: ids, index: MessageIndex.absoluteLowerBound(), count: count, tagMask: tagMask)) } } @@ -690,12 +730,18 @@ final class MutableMessageHistoryView { func updatePeerIds(transaction: PostboxTransaction) { switch self.peerIds { - case .single, .group: + case .group: break + case let .single(peerId): + if let updatedData = transaction.currentUpdatedCachedPeerData[peerId] { + if updatedData.associatedHistoryMessageId != nil { + self.peerIds = .associated(peerId, updatedData.associatedHistoryMessageId) + } + } case let .associated(peerId, associatedId): if let updatedData = transaction.currentUpdatedCachedPeerData[peerId] { - if updatedData.associatedHistoryPeerId != associatedId { - self.peerIds = .associated(peerId, updatedData.associatedHistoryPeerId) + if updatedData.associatedHistoryMessageId != associatedId { + self.peerIds = .associated(peerId, updatedData.associatedHistoryMessageId) } } } @@ -722,7 +768,7 @@ final class MutableMessageHistoryView { case let .associated(mainPeerId, associatedPeerId): ids.insert(mainPeerId) if let associatedPeerId = associatedPeerId { - ids.insert(associatedPeerId) + ids.insert(associatedPeerId.peerId) } case let .group(groupId): if let operations = transaction.currentGroupFeedOperations[groupId] { @@ -1008,7 +1054,7 @@ final class MutableMessageHistoryView { self.additionalDatas[i] = .peerGroupState(groupId, postbox.peerGroupStateTable.get(groupId)) hasChanges = true } - case .totalUnreadCount: + case .totalUnreadState: break case .peerNotificationSettings: break @@ -1205,13 +1251,13 @@ final class MutableMessageHistoryView { case let .associated(mainId, associatedId): ids.append(mainId) if let associatedId = associatedId { - ids.append(associatedId) + ids.append(associatedId.peerId) } case let .group(groupId): return postbox.fetchEarlierGroupFeedEntries(groupId: groupId, index: index, count: count) } - return postbox.fetchEarlierHistoryEntries(peerIds: ids, index: index, count: count, tagMask: self.tagMask) + return clipMessages(peerIds: peerIds, entries: postbox.fetchEarlierHistoryEntries(peerIds: ids, index: index, count: count, tagMask: self.tagMask)) } private func fetchLater(postbox: Postbox, index: MessageIndex?, count: Int) -> [MutableMessageHistoryEntry] { @@ -1222,13 +1268,13 @@ final class MutableMessageHistoryView { case let .associated(mainId, associatedId): ids.append(mainId) if let associatedId = associatedId { - ids.append(associatedId) + ids.append(associatedId.peerId) } case let .group(groupId): return postbox.fetchLaterGroupFeedEntries(groupId: groupId, index: index, count: count) } - return postbox.fetchLaterHistoryEntries(ids, index: index, count: count, tagMask: self.tagMask) + return clipMessages(peerIds: peerIds, entries: postbox.fetchLaterHistoryEntries(ids, index: index, count: count, tagMask: self.tagMask)) } func complete(postbox: Postbox, context: MutableMessageHistoryViewReplayContext) { @@ -1492,11 +1538,19 @@ public final class MessageHistoryView { var laterId = mutableView.later?.index var entries: [MessageHistoryEntry] = [] + var removeUpperHolePeerId: PeerId? + if case let .associated(_, associatedId) = mutableView.peerIds { + removeUpperHolePeerId = associatedId?.peerId + } if let transientReadStates = mutableView.transientReadStates, case let .peer(states) = transientReadStates { for entry in mutableView.entries { switch entry { case let .HoleEntry(hole, location, _): - entries.append(.HoleEntry(hole, location)) + if let removeUpperHolePeerId = removeUpperHolePeerId, removeUpperHolePeerId == hole.maxIndex.id.peerId, hole.maxIndex.timestamp >= Int32.max - 1 { + // skip hole + } else { + entries.append(.HoleEntry(hole, location)) + } case let .MessageEntry(message, location, monthLocation): let read: Bool if message.flags.contains(.Incoming) { @@ -1515,7 +1569,11 @@ public final class MessageHistoryView { for entry in mutableView.entries { switch entry { case let .HoleEntry(hole, location, _): - entries.append(.HoleEntry(hole, location)) + if let removeUpperHolePeerId = removeUpperHolePeerId, removeUpperHolePeerId == hole.maxIndex.id.peerId, hole.maxIndex.timestamp >= Int32.max - 1 { + // skip hole + } else { + entries.append(.HoleEntry(hole, location)) + } case let .MessageEntry(message, location, monthLocation): entries.append(.MessageEntry(message, false, location, monthLocation)) case .IntermediateMessageEntry: diff --git a/Postbox/OrderedItemListTable.swift b/Postbox/OrderedItemListTable.swift index b1c318553c..20521d3e9f 100644 --- a/Postbox/OrderedItemListTable.swift +++ b/Postbox/OrderedItemListTable.swift @@ -4,6 +4,7 @@ enum OrderedItemListOperation { case replace([OrderedItemListEntry]) case addOrMoveToFirstPosition(OrderedItemListEntry, Int?) case remove(MemoryBuffer) + case update(MemoryBuffer, OrderedItemListEntryContents) } private enum OrderedItemListKeyNamespace: UInt8 { @@ -85,6 +86,16 @@ final class OrderedItemListTable: Table { } } + func updateItem(collectionId: Int32, itemId: MemoryBuffer, item: OrderedItemListEntryContents, operations: inout [Int32: [OrderedItemListOperation]]) { + if let _ = self.indexTable.get(collectionId: collectionId, id: itemId) as? OrderedItemListEntryContents { + self.indexTable.set(collectionId: collectionId, id: itemId, content: item) + if operations[collectionId] == nil { + operations[collectionId] = [] + } + operations[collectionId]!.append(.update(itemId, item)) + } + } + func replaceItems(collectionId: Int32, items: [OrderedItemListEntry], operations: inout [Int32: [OrderedItemListOperation]]) { if operations[collectionId] == nil { operations[collectionId] = [.replace(items)] diff --git a/Postbox/OrderedItemListView.swift b/Postbox/OrderedItemListView.swift index 7f51202e2d..0669921408 100644 --- a/Postbox/OrderedItemListView.swift +++ b/Postbox/OrderedItemListView.swift @@ -37,6 +37,14 @@ final class MutableOrderedItemListView: MutablePostboxView { break inner } } + case let .update(itemId, content): + inner: for i in 0 ..< self.items.count { + if self.items[i].id == itemId { + self.items[i] = OrderedItemListEntry(id: itemId, contents: content) + updated = true + break inner + } + } } } } diff --git a/Postbox/Postbox.swift b/Postbox/Postbox.swift index 67ad1a1355..330c92b2a6 100644 --- a/Postbox/Postbox.swift +++ b/Postbox/Postbox.swift @@ -522,6 +522,11 @@ public final class Modifier { return self.postbox?.getOrderedItemListItem(collectionId: collectionId, itemId: itemId) } + public func updateOrderedItemListItem(collectionId: Int32, itemId: MemoryBuffer, item: OrderedItemListEntryContents) { + assert(!self.disposed) + self.postbox?.updateOrderedItemListItem(collectionId: collectionId, itemId: itemId, item: item) + } + public func removeOrderedItemListItem(collectionId: Int32, itemId: MemoryBuffer) { assert(!self.disposed) self.postbox?.removeOrderedItemListItem(collectionId: collectionId, itemId: itemId) @@ -679,12 +684,12 @@ public final class Modifier { } } - public func getTotalUnreadCount() -> Int32 { + public func getTotalUnreadState() -> ChatListTotalUnreadState { assert(!self.disposed) if let postbox = self.postbox { - return postbox.messageHistoryMetadataTable.getChatListTotalUnreadCount() + return postbox.messageHistoryMetadataTable.getChatListTotalUnreadState() } else { - return 0 + return ChatListTotalUnreadState(absoluteCounters: ChatListTotalUnreadCounters(messageCount: 0), filteredCounters: ChatListTotalUnreadCounters(messageCount: 0)) } } @@ -839,11 +844,15 @@ func debugSaveState(basePath:String, name: String) { func debugRestoreState(basePath:String, name: String) { let path = basePath + name - let _ = try? FileManager.default.removeItem(atPath: basePath) - do { - try FileManager.default.copyItem(atPath: path, toPath: basePath) - } catch (let e) { - print("(Postbox debugRestoreState: error \(e))") + if FileManager.default.fileExists(atPath: path) { + let _ = try? FileManager.default.removeItem(atPath: basePath) + do { + try FileManager.default.copyItem(atPath: path, toPath: basePath) + } catch (let e) { + print("(Postbox debugRestoreState: error \(e))") + } + } else { + print("(Postbox debugRestoreState: path doesn't exist") } } @@ -864,22 +873,26 @@ public func openPostbox(basePath: String, globalMessageIdsNamespace: MessageId.N let metadataTable = MetadataTable(valueBox: valueBox, table: MetadataTable.tableSpec(0)) let userVersion: Int32? = metadataTable.userVersion() - let currentUserVersion: Int32 = 15 + let currentUserVersion: Int32 = 16 if let userVersion = userVersion { if userVersion != currentUserVersion { - if let operation = registeredUpgrades()[userVersion] { - switch operation { - case let .inplace(f): - valueBox.begin() - f(metadataTable, valueBox) - valueBox.commit() - } - continue loop + if userVersion > currentUserVersion { + postboxLog("Version \(userVersion) is newer than supported") } else { - assertionFailure() - postboxLog("Couldn't find any upgrade for \(userVersion)") - valueBox.drop() + if let operation = registeredUpgrades()[userVersion] { + switch operation { + case let .inplace(f): + valueBox.begin() + f(metadataTable, valueBox) + valueBox.commit() + } + continue loop + } else { + assertionFailure() + postboxLog("Couldn't find any upgrade for \(userVersion)") + valueBox.drop() + } } } } else { @@ -931,7 +944,7 @@ public final class Postbox { private var currentUpdatedCachedPeerData: [PeerId: CachedPeerData] = [:] private var currentUpdatedPeerPresences: [PeerId: PeerPresence] = [:] private var currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?] = [:] - private var currentUpdatedTotalUnreadCount: Int32? + private var currentUpdatedTotalUnreadState: ChatListTotalUnreadState? private var currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation] = [] private var currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation] = [] private var currentPreferencesOperations: [PreferencesOperation] = [] @@ -1171,8 +1184,8 @@ public final class Postbox { return self.cachedPeerDataTable.get(peerId) }, getPeerPresence: { peerId in return self.peerPresenceTable.get(peerId) - }, getTotalUnreadCount: { - return self.messageHistoryMetadataTable.getChatListTotalUnreadCount() + }, getTotalUnreadState: { + return self.messageHistoryMetadataTable.getChatListTotalUnreadState() }, getPeerReadState: { peerId in return self.readStateTable.getCombinedState(peerId) }, operationLogGetOperations: { tag, fromIndex, limit in @@ -1710,9 +1723,19 @@ public final class Postbox { let transactionParticipationInTotalUnreadCountUpdates = self.peerNotificationSettingsTable.transactionParticipationInTotalUnreadCountUpdates() self.chatListIndexTable.commitWithTransactionUnreadCountDeltas(transactionUnreadCountDeltas, transactionParticipationInTotalUnreadCountUpdates: transactionParticipationInTotalUnreadCountUpdates, getPeer: { peerId in return self.peerTable.get(peerId) - }, updatedTotalUnreadCount: &self.currentUpdatedTotalUnreadCount) + }, updatedTotalUnreadState: &self.currentUpdatedTotalUnreadState) - let transaction = PostboxTransaction(currentUpdatedState: self.currentUpdatedState, currentOperationsByPeerId: self.currentOperationsByPeerId, currentGroupFeedOperations: self.currentGroupFeedOperations, peerIdsWithFilledHoles: self.currentFilledHolesByPeerId, removedHolesByPeerId: self.currentRemovedHolesByPeerId, groupFeedIdsWithFilledHoles: self.currentGroupFeedIdsWithFilledHoles, removedHolesByPeerGroupId: self.currentRemovedHolesByPeerGroupId, chatListOperations: self.currentChatListOperations, currentUpdatedPeers: self.currentUpdatedPeers, currentUpdatedPeerNotificationSettings: self.currentUpdatedPeerNotificationSettings, currentUpdatedCachedPeerData: self.currentUpdatedCachedPeerData, currentUpdatedPeerPresences: currentUpdatedPeerPresences, currentUpdatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, currentUpdatedTotalUnreadCount: self.currentUpdatedTotalUnreadCount, peerIdsWithUpdatedUnreadCounts: Set(transactionUnreadCountDeltas.keys), peerIdsWithUpdatedCombinedReadStates: peerIdsWithUpdatedCombinedReadStates, currentPeerMergedOperationLogOperations: self.currentPeerMergedOperationLogOperations, currentTimestampBasedMessageAttributesOperations: self.currentTimestampBasedMessageAttributesOperations, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, currentPreferencesOperations: self.currentPreferencesOperations, currentOrderedItemListOperations: self.currentOrderedItemListOperations, currentItemCollectionItemsOperations: self.currentItemCollectionItemsOperations, currentItemCollectionInfosOperations: self.currentItemCollectionInfosOperations, currentUpdatedPeerChatStates: self.currentUpdatedPeerChatStates, currentUpdatedPeerGroupStates: self.currentUpdatedPeerGroupStates, updatedAccessChallengeData: self.currentUpdatedAccessChallengeData, currentGlobalTagsOperations: self.currentGlobalTagsOperations, currentLocalTagsOperations: self.currentLocalTagsOperations, updatedMedia: self.currentUpdatedMedia, replaceRemoteContactCount: self.currentReplaceRemoteContactCount, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentPendingMessageActionsOperations: self.currentPendingMessageActionsOperations, currentUpdatedMessageActionsSummaries: self.currentUpdatedMessageActionsSummaries, currentUpdatedMessageTagSummaries: self.currentUpdatedMessageTagSummaries, currentInvalidateMessageTagSummaries: self.currentInvalidateMessageTagSummaries, currentUpdatedPendingPeerNotificationSettings: self.currentUpdatedPendingPeerNotificationSettings, currentGroupFeedReadStateContext: self.currentGroupFeedReadStateContext, currentInitialPeerGroupIdsBeforeUpdate: self.currentInitialPeerGroupIdsBeforeUpdate, currentUpdatedMasterClientId: currentUpdatedMasterClientId) + #if DEBUG + /*if let updatedState = self.currentUpdatedTotalUnreadState { + let state = self.chatListIndexTable.debugReindexUnreadCounts(postbox: self) + if state != updatedState { + print("read state mismatch \(state) != \(updatedState)") + self.messageHistoryMetadataTable.setChatListTotalUnreadState(state) + } + }*/ + #endif + + let transaction = PostboxTransaction(currentUpdatedState: self.currentUpdatedState, currentOperationsByPeerId: self.currentOperationsByPeerId, currentGroupFeedOperations: self.currentGroupFeedOperations, peerIdsWithFilledHoles: self.currentFilledHolesByPeerId, removedHolesByPeerId: self.currentRemovedHolesByPeerId, groupFeedIdsWithFilledHoles: self.currentGroupFeedIdsWithFilledHoles, removedHolesByPeerGroupId: self.currentRemovedHolesByPeerGroupId, chatListOperations: self.currentChatListOperations, currentUpdatedPeers: self.currentUpdatedPeers, currentUpdatedPeerNotificationSettings: self.currentUpdatedPeerNotificationSettings, currentUpdatedCachedPeerData: self.currentUpdatedCachedPeerData, currentUpdatedPeerPresences: currentUpdatedPeerPresences, currentUpdatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, currentUpdatedTotalUnreadState: self.currentUpdatedTotalUnreadState, peerIdsWithUpdatedUnreadCounts: Set(transactionUnreadCountDeltas.keys), peerIdsWithUpdatedCombinedReadStates: peerIdsWithUpdatedCombinedReadStates, currentPeerMergedOperationLogOperations: self.currentPeerMergedOperationLogOperations, currentTimestampBasedMessageAttributesOperations: self.currentTimestampBasedMessageAttributesOperations, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, currentPreferencesOperations: self.currentPreferencesOperations, currentOrderedItemListOperations: self.currentOrderedItemListOperations, currentItemCollectionItemsOperations: self.currentItemCollectionItemsOperations, currentItemCollectionInfosOperations: self.currentItemCollectionInfosOperations, currentUpdatedPeerChatStates: self.currentUpdatedPeerChatStates, currentUpdatedPeerGroupStates: self.currentUpdatedPeerGroupStates, updatedAccessChallengeData: self.currentUpdatedAccessChallengeData, currentGlobalTagsOperations: self.currentGlobalTagsOperations, currentLocalTagsOperations: self.currentLocalTagsOperations, updatedMedia: self.currentUpdatedMedia, replaceRemoteContactCount: self.currentReplaceRemoteContactCount, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentPendingMessageActionsOperations: self.currentPendingMessageActionsOperations, currentUpdatedMessageActionsSummaries: self.currentUpdatedMessageActionsSummaries, currentUpdatedMessageTagSummaries: self.currentUpdatedMessageTagSummaries, currentInvalidateMessageTagSummaries: self.currentInvalidateMessageTagSummaries, currentUpdatedPendingPeerNotificationSettings: self.currentUpdatedPendingPeerNotificationSettings, currentGroupFeedReadStateContext: self.currentGroupFeedReadStateContext, currentInitialPeerGroupIdsBeforeUpdate: self.currentInitialPeerGroupIdsBeforeUpdate, currentUpdatedMasterClientId: currentUpdatedMasterClientId) var updatedTransactionState: Int64? var updatedMasterClientId: Int64? if !transaction.isEmpty { @@ -1748,7 +1771,7 @@ public final class Postbox { self.currentUpdatedCachedPeerData.removeAll() self.currentUpdatedPeerPresences.removeAll() self.currentUpdatedPeerChatListEmbeddedStates.removeAll() - self.currentUpdatedTotalUnreadCount = nil + self.currentUpdatedTotalUnreadState = nil self.currentPeerMergedOperationLogOperations.removeAll() self.currentTimestampBasedMessageAttributesOperations.removeAll() self.currentPreferencesOperations.removeAll() @@ -2157,8 +2180,8 @@ public final class Postbox { switch chatLocation { case let .peer(peerId): peerIds = .single(peerId) - if tagMask == nil, let associatedPeerId = self.cachedPeerDataTable.get(peerId)?.associatedHistoryPeerId, associatedPeerId != peerId { - peerIds = .associated(peerId, associatedPeerId) + if tagMask == nil, let associatedMessageId = self.cachedPeerDataTable.get(peerId)?.associatedHistoryMessageId, associatedMessageId.peerId != peerId { + peerIds = .associated(peerId, associatedMessageId) } case let .group(groupId): peerIds = .group(groupId) @@ -2182,7 +2205,7 @@ public final class Postbox { var ids: [PeerId] = [] ids.append(mainId) if let associatedId = associatedId { - ids.append(associatedId) + ids.append(associatedId.peerId) } var minIndexWithUnreadMessages: InternalMessageHistoryAnchorIndex? @@ -2304,8 +2327,8 @@ public final class Postbox { additionalDataEntries.append(.peerChatState(peerId, self.peerChatStateTable.get(peerId) as? PeerChatState)) case let .peerGroupState(groupId): additionalDataEntries.append(.peerGroupState(groupId, self.peerGroupStateTable.get(groupId))) - case .totalUnreadCount: - additionalDataEntries.append(.totalUnreadCount(self.messageHistoryMetadataTable.getChatListTotalUnreadCount())) + case .totalUnreadState: + additionalDataEntries.append(.totalUnreadState(self.messageHistoryMetadataTable.getChatListTotalUnreadState())) case let .peerNotificationSettings(peerId): additionalDataEntries.append(.peerNotificationSettings(self.peerNotificationSettingsTable.getEffective(peerId))) } @@ -2954,6 +2977,10 @@ public final class Postbox { return self.orderedItemListTable.getItem(collectionId: collectionId, itemId: itemId) } + fileprivate func updateOrderedItemListItem(collectionId: Int32, itemId: MemoryBuffer, item: OrderedItemListEntryContents) { + self.orderedItemListTable.updateItem(collectionId: collectionId, itemId: itemId, item: item, operations: &self.currentOrderedItemListOperations) + } + fileprivate func setAccessChallengeData(_ data: PostboxAccessChallengeData) { self.currentUpdatedAccessChallengeData = data self.metadataTable.setAccessChallengeData(data) diff --git a/Postbox/PostboxTransaction.swift b/Postbox/PostboxTransaction.swift index cd62f063a0..0c39905c79 100644 --- a/Postbox/PostboxTransaction.swift +++ b/Postbox/PostboxTransaction.swift @@ -14,7 +14,7 @@ final class PostboxTransaction { let currentUpdatedCachedPeerData: [PeerId: CachedPeerData] let currentUpdatedPeerPresences: [PeerId: PeerPresence] let currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?] - let currentUpdatedTotalUnreadCount: Int32? + let currentUpdatedTotalUnreadState: ChatListTotalUnreadState? let peerIdsWithUpdatedUnreadCounts: Set let peerIdsWithUpdatedCombinedReadStates: Set let currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation] @@ -101,7 +101,7 @@ final class PostboxTransaction { if currentUpdatedMasterClientId != nil { return false } - if currentUpdatedTotalUnreadCount != nil { + if currentUpdatedTotalUnreadState != nil { return false } if !peerIdsWithUpdatedCombinedReadStates.isEmpty { @@ -167,7 +167,7 @@ final class PostboxTransaction { return true } - init(currentUpdatedState: PostboxCoding?, currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], currentGroupFeedOperations: [PeerGroupId : [GroupFeedIndexOperation]], peerIdsWithFilledHoles: [PeerId: [MessageIndex: HoleFillDirection]], removedHolesByPeerId: [PeerId: [MessageIndex: HoleFillDirection]], groupFeedIdsWithFilledHoles: [PeerGroupId: [MessageIndex: HoleFillDirection]], removedHolesByPeerGroupId: [PeerGroupId: [MessageIndex: HoleFillDirection]], chatListOperations: [WrappedPeerGroupId: [ChatListOperation]], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: PeerNotificationSettings], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?], currentUpdatedTotalUnreadCount: Int32?, peerIdsWithUpdatedUnreadCounts: Set, peerIdsWithUpdatedCombinedReadStates: Set, currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set, currentUpdatedPeerGroupStates: Set, updatedAccessChallengeData: PostboxAccessChallengeData?, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set, currentGroupFeedReadStateContext: GroupFeedReadStateUpdateContext, currentInitialPeerGroupIdsBeforeUpdate: [PeerId: WrappedPeerGroupId], currentUpdatedMasterClientId: Int64?) { + init(currentUpdatedState: PostboxCoding?, currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], currentGroupFeedOperations: [PeerGroupId : [GroupFeedIndexOperation]], peerIdsWithFilledHoles: [PeerId: [MessageIndex: HoleFillDirection]], removedHolesByPeerId: [PeerId: [MessageIndex: HoleFillDirection]], groupFeedIdsWithFilledHoles: [PeerGroupId: [MessageIndex: HoleFillDirection]], removedHolesByPeerGroupId: [PeerGroupId: [MessageIndex: HoleFillDirection]], chatListOperations: [WrappedPeerGroupId: [ChatListOperation]], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: PeerNotificationSettings], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?], currentUpdatedTotalUnreadState: ChatListTotalUnreadState?, peerIdsWithUpdatedUnreadCounts: Set, peerIdsWithUpdatedCombinedReadStates: Set, currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set, currentUpdatedPeerGroupStates: Set, updatedAccessChallengeData: PostboxAccessChallengeData?, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set, currentGroupFeedReadStateContext: GroupFeedReadStateUpdateContext, currentInitialPeerGroupIdsBeforeUpdate: [PeerId: WrappedPeerGroupId], currentUpdatedMasterClientId: Int64?) { self.currentUpdatedState = currentUpdatedState self.currentOperationsByPeerId = currentOperationsByPeerId self.currentGroupFeedOperations = currentGroupFeedOperations @@ -181,7 +181,7 @@ final class PostboxTransaction { self.currentUpdatedCachedPeerData = currentUpdatedCachedPeerData self.currentUpdatedPeerPresences = currentUpdatedPeerPresences self.currentUpdatedPeerChatListEmbeddedStates = currentUpdatedPeerChatListEmbeddedStates - self.currentUpdatedTotalUnreadCount = currentUpdatedTotalUnreadCount + self.currentUpdatedTotalUnreadState = currentUpdatedTotalUnreadState self.peerIdsWithUpdatedUnreadCounts = peerIdsWithUpdatedUnreadCounts self.peerIdsWithUpdatedCombinedReadStates = peerIdsWithUpdatedCombinedReadStates self.currentPeerMergedOperationLogOperations = currentPeerMergedOperationLogOperations diff --git a/Postbox/PostboxUpgrade_14to15.swift b/Postbox/PostboxUpgrade_14to15.swift index d84fc8952b..eeb80bb0c1 100644 --- a/Postbox/PostboxUpgrade_14to15.swift +++ b/Postbox/PostboxUpgrade_14to15.swift @@ -16,19 +16,6 @@ private func makeKeyValueForChatListPinningIndex(_ index: UInt16?) -> UInt16 { } } -/* - private func key(_ index: ChatListIndex, type: ChatListEntryType) -> ValueBoxKey { - let key = ValueBoxKey(length: 2 + 4 + 4 + 4 + 8 + 1) - key.setUInt16(0, value: keyValueForChatListPinningIndex(index.pinningIndex)) - key.setInt32(2, value: index.messageIndex.timestamp) - key.setInt32(2 + 4, value: index.messageIndex.id.namespace) - key.setInt32(2 + 4 + 4, value: index.messageIndex.id.id) - key.setInt64(2 + 4 + 4 + 4, value: index.messageIndex.id.peerId.toInt64()) - key.setInt8(2 + 4 + 4 + 4 + 8, value: type.rawValue) - return key - } - */ - private func extractPreviousKey(_ key: ValueBoxKey) -> (pinningIndex: UInt16?, index: MessageIndex, type: Int8) { return ( pinningIndex: extractPinningIndexFromKeyValue(key.getUInt16(0)), diff --git a/Postbox/PostboxUpgrade_15to16.swift b/Postbox/PostboxUpgrade_15to16.swift new file mode 100644 index 0000000000..17b67b16cc --- /dev/null +++ b/Postbox/PostboxUpgrade_15to16.swift @@ -0,0 +1,248 @@ +import Foundation + +private struct UpgradeChatListIndexFlags: OptionSet { + var rawValue: Int8 + + init(rawValue: Int8) { + self.rawValue = rawValue + } + + static let hasIndex = UpgradeChatListIndexFlags(rawValue: 1 << 0) +} + +private enum UpgradePeerChatListInclusion { + case notSpecified + case never + case ifHasMessages + case ifHasMessagesOrOneOf(pinningIndex: UInt16?, minTimestamp: Int32?) +} + +private struct UpgradeChatListPeerInclusionIndex { + let topMessageIndex: MessageIndex? + let inclusion: UpgradePeerChatListInclusion + + func includedIndex(peerId: PeerId) -> Bool { + switch inclusion { + case .notSpecified, .never: + return false + case .ifHasMessages: + if let _ = self.topMessageIndex { + return true + } else { + return false + } + case let .ifHasMessagesOrOneOf(pinningIndex, minTimestamp): + if let _ = minTimestamp { + return true + } else if let _ = self.topMessageIndex { + return true + } else if let _ = pinningIndex { + return true + } else { + return false + } + } + } +} + +private func parseInclusionIndex(peerId: PeerId, value: ReadBuffer) -> Bool { + let topMessageIndex: MessageIndex? + + var flagsValue: Int8 = 0 + value.read(&flagsValue, offset: 0, length: 1) + let flags = UpgradeChatListIndexFlags(rawValue: flagsValue) + + if flags.contains(.hasIndex) { + var idNamespace: Int32 = 0 + var idId: Int32 = 0 + var idTimestamp: Int32 = 0 + value.read(&idNamespace, offset: 0, length: 4) + value.read(&idId, offset: 0, length: 4) + value.read(&idTimestamp, offset: 0, length: 4) + topMessageIndex = MessageIndex(id: MessageId(peerId: peerId, namespace: idNamespace, id: idId), timestamp: idTimestamp) + } else { + topMessageIndex = nil + } + + let inclusion: UpgradePeerChatListInclusion + + var inclusionId: Int8 = 0 + value.read(&inclusionId, offset: 0, length: 1) + if inclusionId == 0 { + inclusion = .notSpecified + } else if inclusionId == 1 { + inclusion = .never + } else if inclusionId == 2 { + inclusion = .ifHasMessages + } else if inclusionId == 3 { + var pinningIndexValue: UInt16 = 0 + value.read(&pinningIndexValue, offset: 0, length: 2) + + var hasMinTimestamp: Int8 = 0 + value.read(&hasMinTimestamp, offset: 0, length: 1) + let minTimestamp: Int32? + if hasMinTimestamp != 0 { + var minTimestampValue: Int32 = 0 + value.read(&minTimestampValue, offset: 0, length: 4) + minTimestamp = minTimestampValue + } else { + minTimestamp = nil + } + inclusion = .ifHasMessagesOrOneOf(pinningIndex: chatListPinningIndexFromKeyValue(pinningIndexValue), minTimestamp: minTimestamp) + } else { + assertionFailure() + return false + } + + let inclusionIndex = UpgradeChatListPeerInclusionIndex(topMessageIndex: topMessageIndex, inclusion: inclusion) + return inclusionIndex.includedIndex(peerId: peerId) +} + +private struct UpgradePeerNotificationSettingsTableEntryFlags: OptionSet { + var rawValue: Int32 + + init(rawValue: Int32) { + self.rawValue = rawValue + } + + static let hasCurrent = UpgradePeerNotificationSettingsTableEntryFlags(rawValue: 1 << 0) + static let hasPending = UpgradePeerNotificationSettingsTableEntryFlags(rawValue: 1 << 1) +} + +private func parseNotificationSettings(valueBox: ValueBox, table: ValueBoxTable, peerId: PeerId) -> Bool { + let key = ValueBoxKey(length: 8) + key.setInt64(0, value: peerId.toInt64()) + if let value = valueBox.get(table, key: key) { + var flagsValue: Int32 = 0 + value.read(&flagsValue, offset: 0, length: 4) + let flags = UpgradePeerNotificationSettingsTableEntryFlags(rawValue: flagsValue) + + var current: PeerNotificationSettings? + if flags.contains(.hasCurrent) { + var length: Int32 = 0 + value.read(&length, offset: 0, length: 4) + let object = PostboxDecoder(buffer: MemoryBuffer(memory: value.memory.advanced(by: value.offset), capacity: Int(length), length: Int(length), freeWhenDone: false)).decodeRootObject() as? PeerNotificationSettings + assert(object != nil) + current = object + value.skip(Int(length)) + } + + var pending: PeerNotificationSettings? + if flags.contains(.hasPending) { + var length: Int32 = 0 + value.read(&length, offset: 0, length: 4) + let object = PostboxDecoder(buffer: MemoryBuffer(memory: value.memory.advanced(by: value.offset), capacity: Int(length), length: Int(length), freeWhenDone: false)).decodeRootObject() as? PeerNotificationSettings + assert(object != nil) + pending = object + value.skip(Int(length)) + } + if let pending = pending { + return !pending.isRemovedFromTotalUnreadCount + } else if let current = current { + return !current.isRemovedFromTotalUnreadCount + } else { + return false + } + } + return false +} + +private func getReadStateCount(valueBox: ValueBox, table: ValueBoxTable, peerId: PeerId) -> Int32 { + let key = ValueBoxKey(length: 8) + key.setInt64(0, value: peerId.toInt64()) + var totalCount: Int32 = 0 + if let value = valueBox.get(table, key: key) { + var count: Int32 = 0 + value.read(&count, offset: 0, length: 4) + var stateByNamespace: [MessageId.Namespace: PeerReadState] = [:] + for _ in 0 ..< count { + var namespaceId: Int32 = 0 + value.read(&namespaceId, offset: 0, length: 4) + + let state: PeerReadState + var kind: Int8 = 0 + value.read(&kind, offset: 0, length: 1) + if kind == 0 { + var maxIncomingReadId: Int32 = 0 + var maxOutgoingReadId: Int32 = 0 + var maxKnownId: Int32 = 0 + var count: Int32 = 0 + + value.read(&maxIncomingReadId, offset: 0, length: 4) + value.read(&maxOutgoingReadId, offset: 0, length: 4) + value.read(&maxKnownId, offset: 0, length: 4) + value.read(&count, offset: 0, length: 4) + + totalCount += count + } else { + var maxIncomingReadTimestamp: Int32 = 0 + var maxIncomingReadIdPeerId: Int64 = 0 + var maxIncomingReadIdNamespace: Int32 = 0 + var maxIncomingReadIdId: Int32 = 0 + + var maxOutgoingReadTimestamp: Int32 = 0 + var maxOutgoingReadIdPeerId: Int64 = 0 + var maxOutgoingReadIdNamespace: Int32 = 0 + var maxOutgoingReadIdId: Int32 = 0 + + var count: Int32 = 0 + + value.read(&maxIncomingReadTimestamp, offset: 0, length: 4) + value.read(&maxIncomingReadIdPeerId, offset: 0, length: 8) + value.read(&maxIncomingReadIdNamespace, offset: 0, length: 4) + value.read(&maxIncomingReadIdId, offset: 0, length: 4) + + value.read(&maxOutgoingReadTimestamp, offset: 0, length: 4) + value.read(&maxOutgoingReadIdPeerId, offset: 0, length: 8) + value.read(&maxOutgoingReadIdNamespace, offset: 0, length: 4) + value.read(&maxOutgoingReadIdId, offset: 0, length: 4) + + value.read(&count, offset: 0, length: 4) + totalCount += count + } + } + } + return totalCount +} + +func postboxUpgrade_15to16(metadataTable: MetadataTable, valueBox: ValueBox) { + let chatListIndexTable = ValueBoxTable(id: 8, keyType: .int64) + let notificationSettingsTable = ValueBoxTable(id: 19, keyType: .int64) + let readStateTable = ValueBoxTable(id: 14, keyType: .int64) + let messageHistoryMetadataTable = ValueBoxTable(id: 10, keyType: .binary) + + var includedPeerIds: [PeerId] = [] + + valueBox.scanInt64(chatListIndexTable, values: { key, value in + let peerId = PeerId(key) + if peerId.namespace != Int32.max { + if parseInclusionIndex(peerId: peerId, value: value) { + includedPeerIds.append(peerId) + } + } + return true + }) + + + var state = ChatListTotalUnreadState(absoluteCounters: ChatListTotalUnreadCounters(messageCount: 0), filteredCounters: ChatListTotalUnreadCounters(messageCount: 0)) + for peerId in includedPeerIds { + let count = getReadStateCount(valueBox: valueBox, table: readStateTable, peerId: peerId) + if count != 0 { + state.absoluteCounters.messageCount += count + + if parseNotificationSettings(valueBox: valueBox, table: notificationSettingsTable, peerId: peerId) { + state.filteredCounters.messageCount += count + } + } + } + + let key = ValueBoxKey(length: 1) + key.setInt8(0, value: 4) + let encoder = PostboxEncoder() + encoder.encodeObject(state, forKey: "_") + + valueBox.set(messageHistoryMetadataTable, key: key, value: encoder.readBufferNoCopy()) + + metadataTable.setUserVersion(16) +} + diff --git a/Postbox/UnreadMessageCountsView.swift b/Postbox/UnreadMessageCountsView.swift index 6e2537fc15..b290f07090 100644 --- a/Postbox/UnreadMessageCountsView.swift +++ b/Postbox/UnreadMessageCountsView.swift @@ -1,14 +1,19 @@ import Foundation +public enum UnreadMessageCountsTotalItem { + case raw + case filtered +} + public enum UnreadMessageCountsItem: Equatable { - case total + case total(UnreadMessageCountsTotalItem) case peer(PeerId) case group(PeerGroupId) public static func ==(lhs: UnreadMessageCountsItem, rhs: UnreadMessageCountsItem) -> Bool { switch lhs { - case .total: - if case .total = rhs { + case let .total(value): + if case .total(value) = rhs { return true } else { return false @@ -30,13 +35,13 @@ public enum UnreadMessageCountsItem: Equatable { } private enum MutableUnreadMessageCountsItemEntry { - case total(Int32) + case total(ChatListTotalUnreadState) case peer(PeerId, Int32) case group(PeerGroupId, ChatListGroupReferenceUnreadCounters) } enum UnreadMessageCountsItemEntry { - case total(Int32) + case total(ChatListTotalUnreadState) case peer(PeerId, Int32) case group(PeerGroupId, Int32) } @@ -48,7 +53,7 @@ final class MutableUnreadMessageCountsView: MutablePostboxView { self.entries = items.map { item in switch item { case .total: - return .total(postbox.messageHistoryMetadataTable.getChatListTotalUnreadCount()) + return .total(postbox.messageHistoryMetadataTable.getChatListTotalUnreadState()) case let .peer(peerId): var count: Int32 = 0 if let combinedState = postbox.readStateTable.getCombinedState(peerId) { @@ -64,14 +69,14 @@ final class MutableUnreadMessageCountsView: MutablePostboxView { func replay(postbox: Postbox, transaction: PostboxTransaction) -> Bool { var updated = false - if transaction.currentUpdatedTotalUnreadCount != nil || !transaction.peerIdsWithUpdatedUnreadCounts.isEmpty { + if transaction.currentUpdatedTotalUnreadState != nil || !transaction.peerIdsWithUpdatedUnreadCounts.isEmpty { for i in 0 ..< self.entries.count { switch self.entries[i] { - case let .total(count): - if transaction.currentUpdatedTotalUnreadCount != nil { - let updatedCount = postbox.messageHistoryMetadataTable.getChatListTotalUnreadCount() - if updatedCount != count { - self.entries[i] = .total(updatedCount) + case let .total(state): + if transaction.currentUpdatedTotalUnreadState != nil { + let updatedState = postbox.messageHistoryMetadataTable.getChatListTotalUnreadState() + if updatedState != state { + self.entries[i] = .total(updatedState) updated = true } } @@ -120,9 +125,14 @@ public final class UnreadMessageCountsView: PostboxView { public func count(for item: UnreadMessageCountsItem) -> Int32? { for entry in self.entries { switch entry { - case let .total(count): - if case .total = item { - return count + case let .total(state): + if case let .total(value) = item { + switch value { + case .raw: + return state.absoluteCounters.messageCount + case .filtered: + return state.filteredCounters.messageCount + } } case let .peer(peerId, count): if case .peer(peerId) = item { diff --git a/Postbox/Upgrades.swift b/Postbox/Upgrades.swift index 58fcf34eac..6b2c88afbe 100644 --- a/Postbox/Upgrades.swift +++ b/Postbox/Upgrades.swift @@ -9,5 +9,6 @@ func registeredUpgrades() -> [Int32: PostboxUpgradeOperation] { dict[12] = .inplace(postboxUpgrade_12to13) dict[13] = .inplace(postboxUpgrade_13to14) dict[14] = .inplace(postboxUpgrade_14to15) + dict[15] = .inplace(postboxUpgrade_15to16) return dict } diff --git a/Postbox/ViewTracker.swift b/Postbox/ViewTracker.swift index d7a41d52fb..85f6a629c7 100644 --- a/Postbox/ViewTracker.swift +++ b/Postbox/ViewTracker.swift @@ -20,7 +20,7 @@ final class ViewTracker { private let getPeerNotificationSettings: (PeerId) -> PeerNotificationSettings? private let getCachedPeerData: (PeerId) -> CachedPeerData? private let getPeerPresence: (PeerId) -> PeerPresence? - private let getTotalUnreadCount: () -> Int32 + private let getTotalUnreadState: () -> ChatListTotalUnreadState private let getPeerReadState: (PeerId) -> CombinedPeerReadState? private let operationLogGetOperations: (PeerOperationLogTag, Int32, Int) -> [PeerMergedOperationLogEntry] private let operationLogGetTailIndex: (PeerOperationLogTag) -> Int32? @@ -59,7 +59,7 @@ final class ViewTracker { private var multiplePeersViews = Bag<(MutableMultiplePeersView, ValuePipe)>() private var itemCollectionsViews = Bag<(MutableItemCollectionsView, ValuePipe)>() - init(queue: Queue, fetchAnchorIndex: @escaping (MessageId) -> InternalMessageHistoryAnchorIndex?, renderMessage: @escaping (IntermediateMessage) -> Message, getPeer: @escaping (PeerId) -> Peer?, getPeerNotificationSettings: @escaping (PeerId) -> PeerNotificationSettings?, getCachedPeerData: @escaping (PeerId) -> CachedPeerData?, getPeerPresence: @escaping (PeerId) -> PeerPresence?, getTotalUnreadCount: @escaping () -> Int32, getPeerReadState: @escaping (PeerId) -> CombinedPeerReadState?, operationLogGetOperations: @escaping (PeerOperationLogTag, Int32, Int) -> [PeerMergedOperationLogEntry], operationLogGetTailIndex: @escaping (PeerOperationLogTag) -> Int32?, getTimestampBasedMessageAttributesHead: @escaping (UInt16) -> TimestampBasedMessageAttributesEntry?, getPreferencesEntry: @escaping (ValueBoxKey) -> PreferencesEntry?, unsentMessageIds: [MessageId], synchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation]) { + init(queue: Queue, fetchAnchorIndex: @escaping (MessageId) -> InternalMessageHistoryAnchorIndex?, renderMessage: @escaping (IntermediateMessage) -> Message, getPeer: @escaping (PeerId) -> Peer?, getPeerNotificationSettings: @escaping (PeerId) -> PeerNotificationSettings?, getCachedPeerData: @escaping (PeerId) -> CachedPeerData?, getPeerPresence: @escaping (PeerId) -> PeerPresence?, getTotalUnreadState: @escaping () -> ChatListTotalUnreadState, getPeerReadState: @escaping (PeerId) -> CombinedPeerReadState?, operationLogGetOperations: @escaping (PeerOperationLogTag, Int32, Int) -> [PeerMergedOperationLogEntry], operationLogGetTailIndex: @escaping (PeerOperationLogTag) -> Int32?, getTimestampBasedMessageAttributesHead: @escaping (UInt16) -> TimestampBasedMessageAttributesEntry?, getPreferencesEntry: @escaping (ValueBoxKey) -> PreferencesEntry?, unsentMessageIds: [MessageId], synchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation]) { self.queue = queue self.fetchAnchorIndex = fetchAnchorIndex self.renderMessage = renderMessage @@ -67,7 +67,7 @@ final class ViewTracker { self.getPeerNotificationSettings = getPeerNotificationSettings self.getCachedPeerData = getCachedPeerData self.getPeerPresence = getPeerPresence - self.getTotalUnreadCount = getTotalUnreadCount + self.getTotalUnreadState = getTotalUnreadState self.getPeerReadState = getPeerReadState self.operationLogGetOperations = operationLogGetOperations self.operationLogGetTailIndex = operationLogGetTailIndex @@ -348,10 +348,10 @@ final class ViewTracker { switch mutableView.peerIds { case .single: assertionFailure() - case let .associated(mainPeerId, associatedPeerId): + case let .associated(mainPeerId, associatedId): ids.insert(mainPeerId) - if let associatedPeerId = associatedPeerId { - ids.insert(associatedPeerId) + if let associatedId = associatedId { + ids.insert(associatedId.peerId) } case let .group(groupId): if let value = transaction.groupFeedIdsWithFilledHoles[groupId] { @@ -396,7 +396,7 @@ final class ViewTracker { } mutableView.updatePeerIds(transaction: transaction) - if case .associated = mutableView.peerIds, case .Generic = updateType, mutableView.peerIds != previousPeerIds { + if mutableView.peerIds != previousPeerIds { updateType = .UpdateVisible let _ = mutableView.refreshDueToExternalTransaction(postbox: postbox)