diff --git a/Postbox.xcodeproj/project.pbxproj b/Postbox.xcodeproj/project.pbxproj index 40b6e0bbdd..b4aab6edf5 100644 --- a/Postbox.xcodeproj/project.pbxproj +++ b/Postbox.xcodeproj/project.pbxproj @@ -76,10 +76,14 @@ D098C6F21D7E1201007784E4 /* Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = D098C6F01D7E11E9007784E4 /* Database.swift */; }; D09ADF0A1D2E89F300C8208D /* RandomAccessMediaResourceContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09ADF091D2E89F300C8208D /* RandomAccessMediaResourceContext.swift */; }; D09ADF0C1D2EB83500C8208D /* OrderStatisticTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09ADF0B1D2EB83500C8208D /* OrderStatisticTable.swift */; }; + D0A18D671E16874D004C6734 /* UnreadMessageCountsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A18D661E16874D004C6734 /* UnreadMessageCountsView.swift */; }; D0A7D9451C556CFE0016A115 /* MessageHistoryIndexTableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A7D9441C556CFE0016A115 /* MessageHistoryIndexTableTests.swift */; }; D0AB0B8C1D65D488002C78E7 /* MessageHistoryHolesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AB0B8B1D65D488002C78E7 /* MessageHistoryHolesView.swift */; }; D0AB0B8E1D65D49C002C78E7 /* ChatListHolesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AB0B8D1D65D49C002C78E7 /* ChatListHolesView.swift */; }; D0AB0B901D65D4AB002C78E7 /* UnsentMessageIndicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AB0B8F1D65D4AB002C78E7 /* UnsentMessageIndicesView.swift */; }; + D0AD23271E194D1C00A7089A /* PeerOperationLogTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AD23261E194D1C00A7089A /* PeerOperationLogTable.swift */; }; + D0AD23291E196B6400A7089A /* PeerOperationLogMetadataTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AD23281E196B6400A7089A /* PeerOperationLogMetadataTable.swift */; }; + D0AD232B1E1974C000A7089A /* MessageGloballyUniqueIdTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AD232A1E1974C000A7089A /* MessageGloballyUniqueIdTable.swift */; }; D0B418171D7DFAF3004562A4 /* PostboxMac.h in Headers */ = {isa = PBXBuildFile; fileRef = D0B418151D7DFAF3004562A4 /* PostboxMac.h */; settings = {ATTRIBUTES = (Public, ); }; }; D0B4181C1D7DFDF1004562A4 /* SQLite-Bridging.m in Sources */ = {isa = PBXBuildFile; fileRef = D07516761B2EC90400AE42E0 /* SQLite-Bridging.m */; }; D0B4181D1D7DFDF4004562A4 /* sqlite3.c in Sources */ = {isa = PBXBuildFile; fileRef = D07516401B2D9CEF00AE42E0 /* sqlite3.c */; settings = {COMPILER_FLAGS = "-Wno-conversion -Wno-ambiguous-macro -Wno-conditional-uninitialized -Wno-unused-const-variable -Wno-unused-function -Wno-unreachable-code"; }; }; @@ -260,10 +264,14 @@ D098C6F01D7E11E9007784E4 /* Database.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Database.swift; sourceTree = ""; }; D09ADF091D2E89F300C8208D /* RandomAccessMediaResourceContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomAccessMediaResourceContext.swift; sourceTree = ""; }; D09ADF0B1D2EB83500C8208D /* OrderStatisticTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrderStatisticTable.swift; sourceTree = ""; }; + D0A18D661E16874D004C6734 /* UnreadMessageCountsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnreadMessageCountsView.swift; sourceTree = ""; }; D0A7D9441C556CFE0016A115 /* MessageHistoryIndexTableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageHistoryIndexTableTests.swift; sourceTree = ""; }; D0AB0B8B1D65D488002C78E7 /* MessageHistoryHolesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageHistoryHolesView.swift; sourceTree = ""; }; D0AB0B8D1D65D49C002C78E7 /* ChatListHolesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatListHolesView.swift; sourceTree = ""; }; D0AB0B8F1D65D4AB002C78E7 /* UnsentMessageIndicesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnsentMessageIndicesView.swift; sourceTree = ""; }; + D0AD23261E194D1C00A7089A /* PeerOperationLogTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerOperationLogTable.swift; sourceTree = ""; }; + D0AD23281E196B6400A7089A /* PeerOperationLogMetadataTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerOperationLogMetadataTable.swift; sourceTree = ""; }; + D0AD232A1E1974C000A7089A /* MessageGloballyUniqueIdTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageGloballyUniqueIdTable.swift; sourceTree = ""; }; D0B418131D7DFAF2004562A4 /* PostboxMac.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PostboxMac.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0B418151D7DFAF3004562A4 /* PostboxMac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PostboxMac.h; sourceTree = ""; }; D0B418161D7DFAF3004562A4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -404,6 +412,7 @@ children = ( D03BCCF71C73561C0097A291 /* Table.swift */, D0F9E8721C5A1EE500037222 /* GlobalMessageIdsTable.swift */, + D0AD232A1E1974C000A7089A /* MessageGloballyUniqueIdTable.swift */, D044CA2B1C617E2D002160FF /* MessageHistoryMetadataTable.swift */, D08C713B1C51283C00779C0F /* MessageHistoryIndexTable.swift */, D08C713D1C512EA500779C0F /* MessageHistoryTable.swift */, @@ -434,6 +443,8 @@ D0F3CC711DDE1CDC008148FA /* ItemCacheTable.swift */, D07827C21E008F7300071108 /* PeerNameTokenIndexTable.swift */, D07827C41E00B23F00071108 /* PeerNameIndexTable.swift */, + D0AD23261E194D1C00A7089A /* PeerOperationLogTable.swift */, + D0AD23281E196B6400A7089A /* PeerOperationLogMetadataTable.swift */, ); name = Tables; sourceTree = ""; @@ -497,6 +508,7 @@ D0AB0B8F1D65D4AB002C78E7 /* UnsentMessageIndicesView.swift */, D0DF0C8E1D81A350008AEB01 /* PeerView.swift */, D021E0DB1DB5237C00C6B04F /* ItemCollectionsView.swift */, + D0A18D661E16874D004C6734 /* UnreadMessageCountsView.swift */, ); name = Views; sourceTree = ""; @@ -853,6 +865,7 @@ D021E0D81DB4FD1300C6B04F /* ItemCollectionItemTable.swift in Sources */, D0F9E85B1C565EBB00037222 /* MessageMediaTable.swift in Sources */, D0079F631D5A242500A27A2C /* ContactPeerIdsView.swift in Sources */, + D0AD23291E196B6400A7089A /* PeerOperationLogMetadataTable.swift in Sources */, D0E1DE151C5E1C6900C7826E /* ViewTracker.swift in Sources */, D0F9E8751C5A334100037222 /* SimpleDictionary.swift in Sources */, D08C713E1C512EA500779C0F /* MessageHistoryTable.swift in Sources */, @@ -868,7 +881,9 @@ D07827C31E008F7300071108 /* PeerNameTokenIndexTable.swift in Sources */, D09ADF0C1D2EB83500C8208D /* OrderStatisticTable.swift in Sources */, D0CE63F61CA1CCB2002BC462 /* MediaResource.swift in Sources */, + D0AD232B1E1974C000A7089A /* MessageGloballyUniqueIdTable.swift in Sources */, D03121001DA579A0006A2A60 /* PeerNotificationSettingsTable.swift in Sources */, + D0AD23271E194D1C00A7089A /* PeerOperationLogTable.swift in Sources */, D021E0D41DB4FAE100C6B04F /* ItemCollection.swift in Sources */, D0D510F91D63BCC200A97B8A /* IntermediateMessage.swift in Sources */, D0F9E86B1C59719800037222 /* ChatListView.swift in Sources */, @@ -884,6 +899,7 @@ D07CFF851DCA99C400761F81 /* InitialMessageHistoryData.swift in Sources */, D05F09A61C9E9F9300BB6F96 /* MediaResourceStatus.swift in Sources */, D033A6F71C73D512006A2EAB /* MessageHistoryUnsentTable.swift in Sources */, + D0A18D671E16874D004C6734 /* UnreadMessageCountsView.swift in Sources */, D0D511021D64D73D00A97B8A /* IpcNotifier.mm in Sources */, D0B844511DAC04FE005F29E1 /* PeerPresence.swift in Sources */, D07827C11E0079CB00071108 /* StringIndexTokens.swift in Sources */, diff --git a/Postbox/ChatListIndexTable.swift b/Postbox/ChatListIndexTable.swift index aa571dbfb3..138325fe3a 100644 --- a/Postbox/ChatListIndexTable.swift +++ b/Postbox/ChatListIndexTable.swift @@ -6,14 +6,20 @@ final class ChatListIndexTable: Table { } private let peerNameIndexTable: PeerNameIndexTable + private let metadataTable: MessageHistoryMetadataTable + private let readStateTable: MessageHistoryReadStateTable + private let notificationSettingsTable: PeerNotificationSettingsTable private let sharedKey = ValueBoxKey(length: 8) private var cachedIndices: [PeerId: MessageIndex?] = [:] private var updatedPreviousCachedIndices: [PeerId: MessageIndex?] = [:] - init(valueBox: ValueBox, table: ValueBoxTable, peerNameIndexTable: PeerNameIndexTable) { + init(valueBox: ValueBox, table: ValueBoxTable, peerNameIndexTable: PeerNameIndexTable, metadataTable: MessageHistoryMetadataTable, readStateTable: MessageHistoryReadStateTable, notificationSettingsTable: PeerNotificationSettingsTable) { self.peerNameIndexTable = peerNameIndexTable + self.metadataTable = metadataTable + self.readStateTable = readStateTable + self.notificationSettingsTable = notificationSettingsTable super.init(valueBox: valueBox, table: table) } @@ -58,16 +64,16 @@ final class ChatListIndexTable: Table { assert(self.updatedPreviousCachedIndices.isEmpty) } - override func beforeCommit() { - if !self.updatedPreviousCachedIndices.isEmpty { - var addedPeerIds = Set() - var removedPeerIds = Set() + func commitWithTransactionUnreadCountDeltas(_ deltas: [PeerId: Int32], transactionParticipationInTotalUnreadCountUpdates: (added: Set, removed: Set), updatedTotalUnreadCount: inout Int32?) { + if !self.updatedPreviousCachedIndices.isEmpty || !deltas.isEmpty || !transactionParticipationInTotalUnreadCountUpdates.added.isEmpty || !transactionParticipationInTotalUnreadCountUpdates.removed.isEmpty { + var addedChatListPeerIds = Set() + var removedChatListPeerIds = Set() for (peerId, previousIndex) in self.updatedPreviousCachedIndices { let index = self.cachedIndices[peerId]! if let index = index { if previousIndex == nil { - addedPeerIds.insert(peerId) + addedChatListPeerIds.insert(peerId) } let writeBuffer = WriteBuffer() @@ -80,7 +86,7 @@ final class ChatListIndexTable: Table { self.valueBox.set(self.table, key: self.key(index.id.peerId), value: writeBuffer.readBufferNoCopy()) } else { if previousIndex != nil { - removedPeerIds.insert(peerId) + removedChatListPeerIds.insert(peerId) } self.valueBox.remove(self.table, key: self.key(peerId)) @@ -88,13 +94,88 @@ final class ChatListIndexTable: Table { } self.updatedPreviousCachedIndices.removeAll() - for peerId in addedPeerIds { + let addedUnreadCountPeerIds = addedChatListPeerIds.union(transactionParticipationInTotalUnreadCountUpdates.added) + let removedUnreadCountPeerIds = removedChatListPeerIds.union(transactionParticipationInTotalUnreadCountUpdates.removed) + + var totalUnreadCount = self.metadataTable.getChatListTotalUnreadCount() + for (peerId, delta) in deltas { + if !addedUnreadCountPeerIds.contains(peerId) && !removedUnreadCountPeerIds.contains(peerId) { + if let _ = self.get(peerId), let notificationSettings = self.notificationSettingsTable.get(peerId), !notificationSettings.isRemovedFromTotalUnreadCount { + totalUnreadCount += delta + } + } + } + + for peerId in addedChatListPeerIds { self.peerNameIndexTable.setPeerCategoryState(peerId: peerId, category: [.chats], includes: true) } - for peerId in removedPeerIds { + for peerId in addedUnreadCountPeerIds { + let addedToList = addedChatListPeerIds.contains(peerId) + let startedParticipationInUnreadCount = transactionParticipationInTotalUnreadCountUpdates.added.contains(peerId) + + if addedToList && startedParticipationInUnreadCount { + if let combinedState = self.readStateTable.getCombinedState(peerId) { + totalUnreadCount += combinedState.count + } + } else if addedToList { + if let notificationSettings = self.notificationSettingsTable.get(peerId), !notificationSettings.isRemovedFromTotalUnreadCount { + if let combinedState = self.readStateTable.getCombinedState(peerId) { + totalUnreadCount += combinedState.count + } + } + } else if startedParticipationInUnreadCount { + if let _ = self.get(peerId) { + if let combinedState = self.readStateTable.getCombinedState(peerId) { + totalUnreadCount += combinedState.count + } + } + } else { + assertionFailure() + } + } + + for peerId in removedChatListPeerIds { self.peerNameIndexTable.setPeerCategoryState(peerId: peerId, category: [.chats], includes: false) } + + for peerId in removedUnreadCountPeerIds { + var currentPeerUnreadCount: Int32 = 0 + if let combinedState = self.readStateTable.getCombinedState(peerId) { + currentPeerUnreadCount = combinedState.count + } + + if let delta = deltas[peerId] { + currentPeerUnreadCount -= delta + } + + var removedFromList = removedChatListPeerIds.contains(peerId) + var removedFromParticipationInUnreadCount = transactionParticipationInTotalUnreadCountUpdates.removed.contains(peerId) + if removedFromList && removedFromParticipationInUnreadCount { + totalUnreadCount -= currentPeerUnreadCount + } else if removedFromList { + if let notificationSettings = self.notificationSettingsTable.get(peerId), !notificationSettings.isRemovedFromTotalUnreadCount { + totalUnreadCount -= currentPeerUnreadCount + } + } else if removedFromParticipationInUnreadCount { + if let _ = self.get(peerId) { + totalUnreadCount -= currentPeerUnreadCount + } + } else { + assertionFailure() + } + } + + //assert(totalUnreadCount >= 0) + + if self.metadataTable.getChatListTotalUnreadCount() != totalUnreadCount { + self.metadataTable.setChatListTotalUnreadCount(totalUnreadCount) + updatedTotalUnreadCount = totalUnreadCount + } } } + + override func beforeCommit() { + assert(self.updatedPreviousCachedIndices.isEmpty) + } } diff --git a/Postbox/IntermediateMessage.swift b/Postbox/IntermediateMessage.swift index d3b02068ce..21acc5d6a6 100644 --- a/Postbox/IntermediateMessage.swift +++ b/Postbox/IntermediateMessage.swift @@ -25,6 +25,7 @@ class IntermediateMessage { let stableId: UInt32 let stableVersion: UInt32 let id: MessageId + let globallyUniqueId: Int64? let timestamp: Int32 let flags: MessageFlags let tags: MessageTags @@ -35,10 +36,11 @@ class IntermediateMessage { let embeddedMediaData: ReadBuffer let referencedMedia: [MediaId] - init(stableId: UInt32, stableVersion: UInt32, id: MessageId, timestamp: Int32, flags: MessageFlags, tags: MessageTags, forwardInfo: IntermediateMessageForwardInfo?, authorId: PeerId?, text: String, attributesData: ReadBuffer, embeddedMediaData: ReadBuffer, referencedMedia: [MediaId]) { + init(stableId: UInt32, stableVersion: UInt32, id: MessageId, globallyUniqueId: Int64?, timestamp: Int32, flags: MessageFlags, tags: MessageTags, forwardInfo: IntermediateMessageForwardInfo?, authorId: PeerId?, text: String, attributesData: ReadBuffer, embeddedMediaData: ReadBuffer, referencedMedia: [MediaId]) { self.stableId = stableId self.stableVersion = stableVersion self.id = id + self.globallyUniqueId = globallyUniqueId self.timestamp = timestamp self.flags = flags self.tags = tags diff --git a/Postbox/MediaBox.swift b/Postbox/MediaBox.swift index fc3ec43501..abcf5a552f 100644 --- a/Postbox/MediaBox.swift +++ b/Postbox/MediaBox.swift @@ -244,6 +244,23 @@ public final class MediaBox { } } + public func completedResourcePath(_ resource: MediaResource, pathExtension: String? = nil) -> String? { + let paths = self.storePathsForId(resource.id) + if let completeSize = fileSize(paths.complete) { + if let pathExtension = pathExtension { + let symlinkPath = paths.complete + ".\(pathExtension)" + if fileSize(symlinkPath) == nil { + let _ = try? FileManager.default.createSymbolicLink(atPath: symlinkPath, withDestinationPath: URL(fileURLWithPath: paths.complete).lastPathComponent) + } + return symlinkPath + } else { + return paths.complete + } + } else { + return nil + } + } + public func resourceData(_ resource: MediaResource, pathExtension: String? = nil, complete: Bool = true) -> Signal { return Signal { subscriber in let disposable = MetaDisposable() diff --git a/Postbox/Message.swift b/Postbox/Message.swift index 886401447c..e2ad1c66d9 100644 --- a/Postbox/Message.swift +++ b/Postbox/Message.swift @@ -279,7 +279,9 @@ public extension MessageAttribute { public final class Message { public let stableId: UInt32 public let stableVersion: UInt32 + public let id: MessageId + public let globallyUniqueId: Int64? public let timestamp: Int32 public let flags: MessageFlags public let tags: MessageTags @@ -292,10 +294,11 @@ public final class Message { public let associatedMessages: SimpleDictionary public let associatedMessageIds: [MessageId] - public init(stableId: UInt32, stableVersion: UInt32, id: MessageId, timestamp: Int32, flags: MessageFlags, tags: MessageTags, forwardInfo: MessageForwardInfo?, author: Peer?, text: String, attributes: [MessageAttribute], media: [Media], peers: SimpleDictionary, associatedMessages: SimpleDictionary, associatedMessageIds: [MessageId]) { + public init(stableId: UInt32, stableVersion: UInt32, id: MessageId, globallyUniqueId: Int64?, timestamp: Int32, flags: MessageFlags, tags: MessageTags, forwardInfo: MessageForwardInfo?, author: Peer?, text: String, attributes: [MessageAttribute], media: [Media], peers: SimpleDictionary, associatedMessages: SimpleDictionary, associatedMessageIds: [MessageId]) { self.stableId = stableId self.stableVersion = stableVersion self.id = id + self.globallyUniqueId = globallyUniqueId self.timestamp = timestamp self.flags = flags self.tags = tags @@ -345,6 +348,7 @@ public enum StoreMessageId { public final class StoreMessage { public let id: StoreMessageId public let timestamp: Int32 + public let globallyUniqueId: Int64? public let flags: StoreMessageFlags public let tags: MessageTags public let forwardInfo: StoreMessageForwardInfo? @@ -353,8 +357,9 @@ public final class StoreMessage { public let attributes: [MessageAttribute] public let media: [Media] - public init(id: MessageId, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) { + public init(id: MessageId, globallyUniqueId: Int64?, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) { self.id = .Id(id) + self.globallyUniqueId = globallyUniqueId self.timestamp = timestamp self.flags = flags self.tags = tags @@ -365,9 +370,10 @@ public final class StoreMessage { self.media = media } - public init(peerId: PeerId, namespace: MessageId.Namespace, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) { + public init(peerId: PeerId, namespace: MessageId.Namespace, globallyUniqueId: Int64?, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) { self.id = .Partial(peerId, namespace) self.timestamp = timestamp + self.globallyUniqueId = globallyUniqueId self.flags = flags self.tags = tags self.forwardInfo = forwardInfo @@ -381,6 +387,7 @@ public final class StoreMessage { final class InternalStoreMessage { let id: MessageId let timestamp: Int32 + let globallyUniqueId: Int64? let flags: StoreMessageFlags let tags: MessageTags let forwardInfo: StoreMessageForwardInfo? @@ -389,9 +396,10 @@ final class InternalStoreMessage { let attributes: [MessageAttribute] let media: [Media] - init(id: MessageId, timestamp: Int32, flags: StoreMessageFlags, tags: MessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) { + init(id: MessageId, timestamp: Int32, globallyUniqueId: Int64?, flags: StoreMessageFlags, tags: MessageTags, forwardInfo: StoreMessageForwardInfo?, authorId: PeerId?, text: String, attributes: [MessageAttribute], media: [Media]) { self.id = id self.timestamp = timestamp + self.globallyUniqueId = globallyUniqueId self.flags = flags self.tags = tags self.forwardInfo = forwardInfo diff --git a/Postbox/MessageGloballyUniqueIdTable.swift b/Postbox/MessageGloballyUniqueIdTable.swift new file mode 100644 index 0000000000..85bedd1b66 --- /dev/null +++ b/Postbox/MessageGloballyUniqueIdTable.swift @@ -0,0 +1,43 @@ +import Foundation + +final class MessageGloballyUniqueIdTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .int64) + } + + private let sharedKey = ValueBoxKey(length: 8) + private let sharedBuffer = WriteBuffer() + + private func key(_ id: Int64) -> ValueBoxKey { + self.sharedKey.setInt64(0, value: id) + return self.sharedKey + } + + func set(_ globallyUniqueId: Int64, id: MessageId) { + self.sharedBuffer.reset() + var idPeerId: Int64 = id.peerId.toInt64() + var idNamespace: Int32 = id.namespace + var idId: Int32 = id.id + self.sharedBuffer.write(&idPeerId, offset: 0, length: 8) + self.sharedBuffer.write(&idNamespace, offset: 0, length: 4) + self.sharedBuffer.write(&idId, offset: 0, length: 4) + self.valueBox.set(self.table, key: self.key(globallyUniqueId), value: self.sharedBuffer) + } + + func get(_ globallyUniqueId: Int64) -> MessageId? { + if let value = self.valueBox.get(self.table, key: self.key(globallyUniqueId)) { + var idPeerId: Int64 = 0 + var idNamespace: Int32 = 0 + var idId: Int32 = 0 + value.read(&idPeerId, offset: 0, length: 8) + value.read(&idNamespace, offset: 0, length: 4) + value.read(&idId, offset: 0, length: 4) + return MessageId(peerId: PeerId(idPeerId), namespace: idNamespace, id: idId) + } + return nil + } + + func remove(_ globallyUniqueId: Int64) { + self.valueBox.remove(self.table, key: self.key(globallyUniqueId)) + } +} diff --git a/Postbox/MessageHistoryMetadataTable.swift b/Postbox/MessageHistoryMetadataTable.swift index 7f34c663f8..4318505eca 100644 --- a/Postbox/MessageHistoryMetadataTable.swift +++ b/Postbox/MessageHistoryMetadataTable.swift @@ -5,6 +5,8 @@ private enum MetadataPrefix: Int8 { case PeerHistoryInitialized = 1 case PeerNextMessageIdByNamespace = 2 case NextStableMessageId = 3 + case ChatListTotalUnreadCount = 4 + case NextPeerOperationLogIndex = 5 } final class MessageHistoryMetadataTable: Table { @@ -25,6 +27,12 @@ final class MessageHistoryMetadataTable: Table { private var nextMessageStableId: UInt32? private var nextMessageStableIdUpdated = false + private var chatListTotalUnreadCount: Int32? + private var chatListTotalUnreadCountUpdated = false + + private var nextPeerOperationLogIndex: UInt32? + private var nextPeerOperationLogIndexUpdated = false + private func peerHistoryInitializedKey(_ id: PeerId) -> ValueBoxKey { self.sharedPeerHistoryInitializedKey.setInt64(0, value: id.toInt64()) self.sharedPeerHistoryInitializedKey.setInt8(8, value: MetadataPrefix.PeerHistoryInitialized.rawValue) @@ -142,6 +150,51 @@ final class MessageHistoryMetadataTable: Table { } } + func getNextPeerOperationLogIndex() -> UInt32 { + if let nextId = self.nextPeerOperationLogIndex { + self.nextPeerOperationLogIndex = nextId + 1 + self.nextPeerOperationLogIndexUpdated = true + return nextId + } else { + if let value = self.valueBox.get(self.table, key: self.key(.NextPeerOperationLogIndex)) { + var nextId: UInt32 = 0 + value.read(&nextId, offset: 0, length: 4) + self.nextPeerOperationLogIndex = nextId + 1 + self.nextPeerOperationLogIndexUpdated = true + return nextId + } else { + let nextId: UInt32 = 1 + self.nextPeerOperationLogIndex = nextId + 1 + self.nextPeerOperationLogIndexUpdated = true + return nextId + } + } + } + + func getChatListTotalUnreadCount() -> Int32 { + if let cached = self.chatListTotalUnreadCount { + 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 + } else { + self.chatListTotalUnreadCount = 0 + return 0 + } + } + } + + func setChatListTotalUnreadCount(_ value: Int32) { + let current = self.getChatListTotalUnreadCount() + if current != value { + self.chatListTotalUnreadCount = value + self.chatListTotalUnreadCountUpdated = true + } + } + override func clearMemoryCache() { self.initializedChatList = nil self.initializedHistoryPeerIds.removeAll() @@ -149,6 +202,8 @@ final class MessageHistoryMetadataTable: Table { self.updatedPeerNextMessageIdByNamespace.removeAll() self.nextMessageStableId = nil self.nextMessageStableIdUpdated = false + self.chatListTotalUnreadCount = nil + self.chatListTotalUnreadCountUpdated = false } override func beforeCommit() { @@ -174,5 +229,21 @@ final class MessageHistoryMetadataTable: Table { self.nextMessageStableIdUpdated = false } } + + if self.nextPeerOperationLogIndexUpdated { + if let nextPeerOperationLogIndex = self.nextPeerOperationLogIndex { + var nextId: UInt32 = nextPeerOperationLogIndex + self.valueBox.set(self.table, key: self.key(.NextPeerOperationLogIndex), value: MemoryBuffer(memory: &nextId, capacity: 4, length: 4, freeWhenDone: false)) + self.nextPeerOperationLogIndexUpdated = false + } + } + + 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)) + } + self.chatListTotalUnreadCountUpdated = false + } } } diff --git a/Postbox/MessageHistoryReadStateTable.swift b/Postbox/MessageHistoryReadStateTable.swift index 0be6dfaa79..4a308040f0 100644 --- a/Postbox/MessageHistoryReadStateTable.swift +++ b/Postbox/MessageHistoryReadStateTable.swift @@ -21,7 +21,7 @@ final class MessageHistoryReadStateTable: Table { } private var cachedPeerReadStates: [PeerId: InternalPeerReadStates?] = [:] - private var updatedPeerIds = Set() + private var updatedInitialPeerReadStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:] private let sharedKey = ValueBoxKey(length: 8) @@ -74,29 +74,33 @@ final class MessageHistoryReadStateTable: Table { return nil } + private func markReadStatesAsUpdated(_ peerId: PeerId, namespaces: [MessageId.Namespace: PeerReadState]) { + if self.updatedInitialPeerReadStates[peerId] == nil { + self.updatedInitialPeerReadStates[peerId] = namespaces + } + } + func resetStates(_ peerId: PeerId, namespaces: [MessageId.Namespace: PeerReadState]) -> CombinedPeerReadState? { if traceReadStates { print("[ReadStateTable] resetStates peerId: \(peerId), namespaces: \(namespaces)") } - self.updatedPeerIds.insert(peerId) - if let states = self.get(peerId) { var updated = false for (namespace, state) in namespaces { if states.namespaces[namespace] == nil || states.namespaces[namespace]! != state { + self.markReadStatesAsUpdated(peerId, namespaces: states.namespaces) updated = true } states.namespaces[namespace] = state } if updated { - self.updatedPeerIds.insert(peerId) return CombinedPeerReadState(states: states.namespaces.map({$0})) } else { return nil } } else { - self.updatedPeerIds.insert(peerId) + self.markReadStatesAsUpdated(peerId, namespaces: [:]) let states = InternalPeerReadStates(namespaces: namespaces) self.cachedPeerReadStates[peerId] = states return CombinedPeerReadState(states: states.namespaces.map({$0})) @@ -132,6 +136,8 @@ final class MessageHistoryReadStateTable: Table { } if addedUnreadCount != 0 { + self.markReadStatesAsUpdated(peerId, namespaces: states.namespaces) + states.namespaces[namespace] = PeerReadState(maxIncomingReadId: currentState.maxIncomingReadId, maxOutgoingReadId: currentState.maxOutgoingReadId, maxKnownId: currentState.maxKnownId, count: currentState.count + addedUnreadCount) updated = true @@ -142,10 +148,6 @@ final class MessageHistoryReadStateTable: Table { } } - if updated { - self.updatedPeerIds.insert(peerId) - } - return (updated ? CombinedPeerReadState(states: states.namespaces.map({$0})) : nil, invalidated) } else { if traceReadStates { @@ -186,6 +188,8 @@ final class MessageHistoryReadStateTable: Table { invalidate = true } + self.markReadStatesAsUpdated(peerId, namespaces: states.namespaces) + states.namespaces[namespace] = PeerReadState(maxIncomingReadId: currentState.maxIncomingReadId, maxOutgoingReadId: currentState.maxOutgoingReadId, maxKnownId: currentState.maxKnownId, count: currentState.count - knownCount) updated = true } else { @@ -193,10 +197,6 @@ final class MessageHistoryReadStateTable: Table { } } - if updated { - self.updatedPeerIds.insert(peerId) - } - return (updated ? CombinedPeerReadState(states: states.namespaces.map({$0})) : nil, invalidate) } else { return (nil, true) @@ -223,8 +223,9 @@ final class MessageHistoryReadStateTable: Table { } } + self.markReadStatesAsUpdated(messageId.peerId, namespaces: states.namespaces) + states.namespaces[messageId.namespace] = PeerReadState(maxIncomingReadId: messageId.id, maxOutgoingReadId: state.maxOutgoingReadId, maxKnownId: state.maxKnownId, count: state.count - Int32(deltaCount)) - self.updatedPeerIds.insert(messageId.peerId) return (CombinedPeerReadState(states: states.namespaces.map({$0})), holes) } } else { @@ -237,8 +238,9 @@ final class MessageHistoryReadStateTable: Table { func applyOutgoingMaxReadId(_ messageId: MessageId) -> (CombinedPeerReadState?, Bool) { if let states = self.get(messageId.peerId), let state = states.namespaces[messageId.namespace] { if state.maxOutgoingReadId < messageId.id { + self.markReadStatesAsUpdated(messageId.peerId, namespaces: states.namespaces) + states.namespaces[messageId.namespace] = PeerReadState(maxIncomingReadId: state.maxIncomingReadId, maxOutgoingReadId: messageId.id, maxKnownId: state.maxKnownId, count: state.count) - self.updatedPeerIds.insert(messageId.peerId) return (CombinedPeerReadState(states: states.namespaces.map({$0})), false) } } else { @@ -258,35 +260,63 @@ final class MessageHistoryReadStateTable: Table { return (combinedState, holes ? .Push(thenSync: true) : .None) } + func transactionUnreadCountDeltas() -> [PeerId: Int32] { + var deltas: [PeerId: Int32] = [:] + for (id, initialNamespaces) in self.updatedInitialPeerReadStates { + var initialCount: Int32 = 0 + for (_, state) in initialNamespaces { + initialCount += state.count + } + + var updatedCount: Int32 = 0 + if let maybeStates = self.cachedPeerReadStates[id] { + if let states = maybeStates { + for (_, state) in states.namespaces { + updatedCount += state.count + } + } + } else { + assertionFailure() + } + + if initialCount != updatedCount { + deltas[id] = updatedCount - initialCount + } + } + return deltas + } + override func clearMemoryCache() { self.cachedPeerReadStates.removeAll() - self.updatedPeerIds.removeAll() + assert(self.updatedInitialPeerReadStates.isEmpty) } override func beforeCommit() { - let sharedBuffer = WriteBuffer() - for id in self.updatedPeerIds { - if let wrappedStates = self.cachedPeerReadStates[id], let states = wrappedStates { - sharedBuffer.reset() - var count: Int32 = Int32(states.namespaces.count) - sharedBuffer.write(&count, offset: 0, length: 4) - for (namespace, state) in states.namespaces { - var namespaceId: Int32 = namespace - var maxIncomingReadId: Int32 = state.maxIncomingReadId - var maxOutgoingReadId: Int32 = state.maxOutgoingReadId - var maxKnownId: Int32 = state.maxKnownId - var count: Int32 = state.count - sharedBuffer.write(&namespaceId, offset: 0, length: 4) - sharedBuffer.write(&maxIncomingReadId, offset: 0, length: 4) - sharedBuffer.write(&maxOutgoingReadId, offset: 0, length: 4) - sharedBuffer.write(&maxKnownId, offset: 0, length: 4) + if !self.updatedInitialPeerReadStates.isEmpty { + let sharedBuffer = WriteBuffer() + for (id, initialNamespaces) in self.updatedInitialPeerReadStates { + if let wrappedStates = self.cachedPeerReadStates[id], let states = wrappedStates { + sharedBuffer.reset() + var count: Int32 = Int32(states.namespaces.count) sharedBuffer.write(&count, offset: 0, length: 4) + for (namespace, state) in states.namespaces { + var namespaceId: Int32 = namespace + var maxIncomingReadId: Int32 = state.maxIncomingReadId + var maxOutgoingReadId: Int32 = state.maxOutgoingReadId + var maxKnownId: Int32 = state.maxKnownId + var count: Int32 = state.count + sharedBuffer.write(&namespaceId, offset: 0, length: 4) + sharedBuffer.write(&maxIncomingReadId, offset: 0, length: 4) + sharedBuffer.write(&maxOutgoingReadId, offset: 0, length: 4) + sharedBuffer.write(&maxKnownId, offset: 0, length: 4) + sharedBuffer.write(&count, offset: 0, length: 4) + } + self.valueBox.set(self.table, key: self.key(id), value: sharedBuffer) + } else { + self.valueBox.remove(self.table, key: self.key(id)) } - self.valueBox.set(self.table, key: self.key(id), value: sharedBuffer) - } else { - self.valueBox.remove(self.table, key: self.key(id)) } + self.updatedInitialPeerReadStates.removeAll() } - self.updatedPeerIds.removeAll() } } diff --git a/Postbox/MessageHistoryTable.swift b/Postbox/MessageHistoryTable.swift index 55a3753d8b..0d2f874a7f 100644 --- a/Postbox/MessageHistoryTable.swift +++ b/Postbox/MessageHistoryTable.swift @@ -212,11 +212,11 @@ final class MessageHistoryTable: Table { var internalStoreMessages: [InternalStoreMessage] = [] for message in messages { switch message.id { - case let .Id(id): - internalStoreMessages.append(InternalStoreMessage(id: id, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributes: message.attributes, media: message.media)) - case let .Partial(peerId, namespace): - let id = self.historyMetadataTable.getNextMessageIdAndIncrement(peerId, namespace: namespace) - internalStoreMessages.append(InternalStoreMessage(id: id, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributes: message.attributes, media: message.media)) + case let .Id(id): + internalStoreMessages.append(InternalStoreMessage(id: id, timestamp: message.timestamp, globallyUniqueId: message.globallyUniqueId, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributes: message.attributes, media: message.media)) + case let .Partial(peerId, namespace): + let id = self.historyMetadataTable.getNextMessageIdAndIncrement(peerId, namespace: namespace) + internalStoreMessages.append(InternalStoreMessage(id: id, timestamp: message.timestamp, globallyUniqueId: message.globallyUniqueId, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributes: message.attributes, media: message.media)) } } return internalStoreMessages @@ -457,6 +457,13 @@ final class MessageHistoryTable: Table { var stableVersion: UInt32 = 0 sharedBuffer.write(&stableVersion, offset: 0, length: 4) + var hasGloballyUniqueId: Int8 = message.globallyUniqueId != nil ? 1 : 0 + sharedBuffer.write(&hasGloballyUniqueId, offset: 0, length: 1) + if let globallyUniqueId = message.globallyUniqueId { + var globallyUniqueIdValue = globallyUniqueId + sharedBuffer.write(&globallyUniqueIdValue, offset: 0, length: 8) + } + var flags = MessageFlags(message.flags) sharedBuffer.write(&flags.rawValue, offset: 0, length: 4) @@ -569,7 +576,7 @@ final class MessageHistoryTable: Table { self.valueBox.set(self.table, key: self.key(MessageIndex(message), key: sharedKey), value: sharedBuffer) - return IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: message.id, timestamp: message.timestamp, flags: flags, tags: message.tags, forwardInfo: intermediateForwardInfo, authorId: message.authorId, text: message.text, attributesData: attributesBuffer.makeReadBufferAndReset(), embeddedMediaData: embeddedMediaBuffer.makeReadBufferAndReset(), referencedMedia: referencedMedia) + return IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, timestamp: message.timestamp, flags: flags, tags: message.tags, forwardInfo: intermediateForwardInfo, authorId: message.authorId, text: message.text, attributesData: attributesBuffer.makeReadBufferAndReset(), embeddedMediaData: embeddedMediaBuffer.makeReadBufferAndReset(), referencedMedia: referencedMedia) } private func justInsertHole(_ hole: MessageHistoryHole, sharedBuffer: WriteBuffer = WriteBuffer()) { @@ -668,7 +675,7 @@ final class MessageHistoryTable: Table { return nil } - func updateEmbeddedMedia(_ index: MessageIndex, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], update: @noescape([Media]) -> [Media]) { + func updateEmbeddedMedia(_ index: MessageIndex, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], update: ([Media]) -> [Media]) { if let message = self.getMessage(index) { var embeddedMediaCount: Int32 = 0 message.embeddedMediaData.read(&embeddedMediaCount, offset: 0, length: 4) @@ -713,7 +720,7 @@ final class MessageHistoryTable: Table { updatedEmbeddedMediaBuffer.write(encodedBuffer.memory, offset: 0, length: encodedBuffer.length) } - self.storeIntermediateMessage(IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: updatedEmbeddedMediaBuffer.readBufferNoCopy(), referencedMedia: message.referencedMedia), sharedKey: self.key(index)) + self.storeIntermediateMessage(IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: updatedEmbeddedMediaBuffer.readBufferNoCopy(), referencedMedia: message.referencedMedia), sharedKey: self.key(index)) let operation: MessageHistoryOperation = .UpdateEmbeddedMedia(index, updatedEmbeddedMediaBuffer.makeReadBufferAndReset()) if operationsByPeerId[index.id.peerId] == nil { @@ -780,7 +787,7 @@ final class MessageHistoryTable: Table { self.messageMediaTable.removeEmbeddedMedia(media) } for mediaId in previousMessage.referencedMedia { - self.messageMediaTable.removeReference(mediaId) + let _ = self.messageMediaTable.removeReference(mediaId) } } @@ -817,6 +824,14 @@ final class MessageHistoryTable: Table { var stableVersion: UInt32 = previousMessage.stableVersion + 1 sharedBuffer.write(&stableVersion, offset: 0, length: 4) + let globallyUniqueId: Int64? = previousMessage.globallyUniqueId + var hasGloballyUniqueId: Int8 = globallyUniqueId != nil ? 1 : 0 + sharedBuffer.write(&hasGloballyUniqueId, offset: 0, length: 1) + if let globallyUniqueId = globallyUniqueId { + var globallyUniqueIdValue = globallyUniqueId + sharedBuffer.write(&globallyUniqueIdValue, offset: 0, length: 8) + } + var flags = MessageFlags(message.flags) sharedBuffer.write(&flags.rawValue, offset: 0, length: 4) @@ -929,7 +944,7 @@ final class MessageHistoryTable: Table { self.valueBox.set(self.table, key: self.key(MessageIndex(message), key: sharedKey), value: sharedBuffer) - return IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: message.id, timestamp: message.timestamp, flags: flags, tags: tags, forwardInfo: intermediateForwardInfo, authorId: message.authorId, text: message.text, attributesData: attributesBuffer.makeReadBufferAndReset(), embeddedMediaData: embeddedMediaBuffer.makeReadBufferAndReset(), referencedMedia: referencedMedia) + return IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, timestamp: message.timestamp, flags: flags, tags: tags, forwardInfo: intermediateForwardInfo, authorId: message.authorId, text: message.text, attributesData: attributesBuffer.makeReadBufferAndReset(), embeddedMediaData: embeddedMediaBuffer.makeReadBufferAndReset(), referencedMedia: referencedMedia) } else { return nil } @@ -938,7 +953,7 @@ final class MessageHistoryTable: Table { private func justUpdateTimestamp(_ index: MessageIndex, timestamp: Int32) { if let previousMessage = self.getMessage(index) { self.valueBox.remove(self.table, key: self.key(index)) - var updatedMessage = IntermediateMessage(stableId: previousMessage.stableId, stableVersion: previousMessage.stableVersion + 1, id: previousMessage.id, timestamp: timestamp, flags: previousMessage.flags, tags: previousMessage.tags, forwardInfo: previousMessage.forwardInfo, authorId: previousMessage.authorId, text: previousMessage.text, attributesData: previousMessage.attributesData, embeddedMediaData: previousMessage.embeddedMediaData, referencedMedia: previousMessage.referencedMedia) + let updatedMessage = IntermediateMessage(stableId: previousMessage.stableId, stableVersion: previousMessage.stableVersion + 1, id: previousMessage.id, globallyUniqueId: previousMessage.globallyUniqueId, timestamp: timestamp, flags: previousMessage.flags, tags: previousMessage.tags, forwardInfo: previousMessage.forwardInfo, authorId: previousMessage.authorId, text: previousMessage.text, attributesData: previousMessage.attributesData, embeddedMediaData: previousMessage.embeddedMediaData, referencedMedia: previousMessage.referencedMedia) self.storeIntermediateMessage(updatedMessage, sharedKey: self.key(index)) let tags = previousMessage.tags.rawValue @@ -991,7 +1006,7 @@ final class MessageHistoryTable: Table { if let extractedMedia = extractedMedia { var updatedReferencedMedia = message.referencedMedia updatedReferencedMedia.append(extractedMedia.id!) - self.storeIntermediateMessage(IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: updatedEmbeddedMediaBuffer.readBufferNoCopy(), referencedMedia: updatedReferencedMedia), sharedKey: self.key(index)) + self.storeIntermediateMessage(IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: updatedEmbeddedMediaBuffer.readBufferNoCopy(), referencedMedia: updatedReferencedMedia), sharedKey: self.key(index)) return extractedMedia } @@ -1011,6 +1026,13 @@ final class MessageHistoryTable: Table { var stableVersion: UInt32 = message.stableVersion sharedBuffer.write(&stableVersion, offset: 0, length: 4) + var hasGloballyUniqueId: Int8 = message.globallyUniqueId != nil ? 1 : 0 + sharedBuffer.write(&hasGloballyUniqueId, offset: 0, length: 1) + if let globallyUniqueId = message.globallyUniqueId { + var globallyUniqueIdValue = globallyUniqueId + sharedBuffer.write(&globallyUniqueIdValue, offset: 0, length: 8) + } + var flagsValue: UInt32 = message.flags.rawValue sharedBuffer.write(&flagsValue, offset: 0, length: 4) @@ -1091,6 +1113,15 @@ final class MessageHistoryTable: Table { var stableVersion: UInt32 = 0 value.read(&stableVersion, offset: 0, length: 4) + var hasGloballyUniqueId: Int8 = 0 + var globallyUniqueId: Int64? + value.read(&hasGloballyUniqueId, offset: 0, length: 1) + if hasGloballyUniqueId != 0 { + var globallyUniqueIdValue: Int64 = 0 + value.read(&globallyUniqueIdValue, offset: 0, length: 8) + globallyUniqueId = globallyUniqueIdValue + } + var flagsValue: UInt32 = 0 value.read(&flagsValue, offset: 0, length: 4) let flags = MessageFlags(rawValue: flagsValue) @@ -1182,7 +1213,7 @@ final class MessageHistoryTable: Table { referencedMediaIds.append(MediaId(namespace: idNamespace, id: idId)) } - return .Message(IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: index.id, timestamp: index.timestamp, flags: flags, tags: tags, forwardInfo: forwardInfo, authorId: authorId, text: text, attributesData: attributesData, embeddedMediaData: embeddedMediaData, referencedMedia: referencedMediaIds)) + return .Message(IntermediateMessage(stableId: stableId, stableVersion: stableVersion, id: index.id, globallyUniqueId: globallyUniqueId, timestamp: index.timestamp, flags: flags, tags: tags, forwardInfo: forwardInfo, authorId: authorId, text: text, attributesData: attributesData, embeddedMediaData: embeddedMediaData, referencedMedia: referencedMediaIds)) } else { var stableId: UInt32 = 0 value.read(&stableId, offset: 0, length: 4) @@ -1284,7 +1315,7 @@ final class MessageHistoryTable: Table { } } - return Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: forwardInfo, author: author, text: message.text, attributes: parsedAttributes, media: parsedMedia, peers: peers, associatedMessages: associatedMessages, associatedMessageIds: associatedMessageIds) + return Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: forwardInfo, author: author, text: message.text, attributes: parsedAttributes, media: parsedMedia, peers: peers, associatedMessages: associatedMessages, associatedMessageIds: associatedMessageIds) } func entriesAround(_ index: MessageIndex, count: Int, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?]) -> (entries: [IntermediateMessageHistoryEntry], lower: IntermediateMessageHistoryEntry?, upper: IntermediateMessageHistoryEntry?) { @@ -1495,7 +1526,7 @@ final class MessageHistoryTable: Table { var result: MessageId? self.valueBox.range(self.table, start: self.key(MessageIndex(id: MessageId(peerId: peerId, namespace: 0, id: 0), timestamp: timestamp)), end: self.key(MessageIndex(id: MessageId(peerId: peerId, namespace: Int32.max, id: Int32.max), timestamp: timestamp)), values: { key, value in let entry = self.readIntermediateEntry(key, value: value) - if case let .Message(message) = entry { + if case .Message = entry { result = entry.index.id return false } diff --git a/Postbox/MessageHistoryView.swift b/Postbox/MessageHistoryView.swift index a5b685dfa8..17c070ed0f 100644 --- a/Postbox/MessageHistoryView.swift +++ b/Postbox/MessageHistoryView.swift @@ -58,10 +58,10 @@ enum MutableMessageHistoryEntry { func updatedTimestamp(_ timestamp: Int32) -> MutableMessageHistoryEntry { switch self { case let .IntermediateMessageEntry(message, location): - var updatedMessage = IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, timestamp: timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: message.embeddedMediaData, referencedMedia: message.referencedMedia) + var updatedMessage = IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, timestamp: timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: message.embeddedMediaData, referencedMedia: message.referencedMedia) return .IntermediateMessageEntry(updatedMessage, location) case let .MessageEntry(message, location): - var updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, timestamp: timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds) + var updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, timestamp: timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds) return .MessageEntry(updatedMessage, location) case let .HoleEntry(hole, location): var updatedHole = MessageHistoryHole(stableId: hole.stableId, maxIndex: MessageIndex(id: hole.maxIndex.id, timestamp: timestamp), min: hole.min, tags: hole.tags) @@ -305,7 +305,7 @@ final class MutableMessageHistoryView { case let .UpdateEmbeddedMedia(index, embeddedMediaData): for i in 0 ..< self.entries.count { if case let .IntermediateMessageEntry(message, _) = self.entries[i] , MessageIndex(message) == index { - self.entries[i] = .IntermediateMessageEntry(IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: embeddedMediaData, referencedMedia: message.referencedMedia), nil) + self.entries[i] = .IntermediateMessageEntry(IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: embeddedMediaData, referencedMedia: message.referencedMedia), nil) hasChanges = true break } @@ -346,7 +346,7 @@ final class MutableMessageHistoryView { messageMedia.append(media) } } - let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: messageMedia, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds) + let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: messageMedia, peers: message.peers, associatedMessages: message.associatedMessages, associatedMessageIds: message.associatedMessageIds) self.entries[i] = .MessageEntry(updatedMessage, nil) hasChanges = true } @@ -388,7 +388,7 @@ final class MutableMessageHistoryView { var updatedAssociatedMessages = message.associatedMessages let renderedMessage = renderIntermediateMessage(intermediateMessage) updatedAssociatedMessages[intermediateMessage.id] = renderedMessage - let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: message.peers, associatedMessages: updatedAssociatedMessages, associatedMessageIds: message.associatedMessageIds) + let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId:message.globallyUniqueId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: message.peers, associatedMessages: updatedAssociatedMessages, associatedMessageIds: message.associatedMessageIds) self.entries[i] = .MessageEntry(updatedMessage, location) hasChanges = true } @@ -515,7 +515,7 @@ final class MutableMessageHistoryView { switch self.entries[i] { case let .MessageEntry(message, location): if let updatedAssociatedMessages = message.associatedMessages.filteredOut(keysIn: ids) { - let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: message.peers, associatedMessages: updatedAssociatedMessages, associatedMessageIds: message.associatedMessageIds) + let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, timestamp: message.timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: message.peers, associatedMessages: updatedAssociatedMessages, associatedMessageIds: message.associatedMessageIds) self.entries[i] = .MessageEntry(updatedMessage, location) } default: diff --git a/Postbox/PeerNameIndexTable.swift b/Postbox/PeerNameIndexTable.swift index 43164fc5da..ebd23ea4e1 100644 --- a/Postbox/PeerNameIndexTable.swift +++ b/Postbox/PeerNameIndexTable.swift @@ -31,7 +31,7 @@ private final class PeerNameIndexCategoriesEntry { let tokenCount = buffer.memory.load(fromByteOffset: 4, as: Int32.self) var offset = 8 var tokens: [ValueBoxKey] = [] - for i in 0 ..< tokenCount { + for _ in 0 ..< tokenCount { let length = buffer.memory.load(fromByteOffset: offset, as: Int32.self) offset += 4 tokens.append(ValueBoxKey(MemoryBuffer(memory: buffer.memory.advanced(by: offset), capacity: Int(length), length: Int(length), freeWhenDone: false))) diff --git a/Postbox/PeerNotificationSettings.swift b/Postbox/PeerNotificationSettings.swift index 32ad5004c5..d0ab6d404f 100644 --- a/Postbox/PeerNotificationSettings.swift +++ b/Postbox/PeerNotificationSettings.swift @@ -1,4 +1,6 @@ public protocol PeerNotificationSettings: Coding { + var isRemovedFromTotalUnreadCount: Bool { get } + func isEqual(to: PeerNotificationSettings) -> Bool } diff --git a/Postbox/PeerNotificationSettingsTable.swift b/Postbox/PeerNotificationSettingsTable.swift index 00d8547b1f..6b79c40c4b 100644 --- a/Postbox/PeerNotificationSettingsTable.swift +++ b/Postbox/PeerNotificationSettingsTable.swift @@ -9,7 +9,7 @@ final class PeerNotificationSettingsTable: Table { private let sharedKey = ValueBoxKey(length: 8) private var cachedSettings: [PeerId: PeerNotificationSettings] = [:] - private var updatedPeerIds = Set() + private var updatedInitialSettings: [PeerId: PeerNotificationSettings?] = [:] private func key(_ id: PeerId) -> ValueBoxKey { self.sharedKey.setInt64(0, value: id.toInt64()) @@ -17,8 +17,13 @@ final class PeerNotificationSettingsTable: Table { } func set(id: PeerId, settings: PeerNotificationSettings) { - self.cachedSettings[id] = settings - self.updatedPeerIds.insert(id) + let current = self.get(id) + if current == nil || !current!.isEqual(to: settings) { + if self.updatedInitialSettings[id] == nil { + self.updatedInitialSettings[id] = current + } + self.cachedSettings[id] = settings + } } func get(_ id: PeerId) -> PeerNotificationSettings? { @@ -36,19 +41,43 @@ final class PeerNotificationSettingsTable: Table { override func clearMemoryCache() { self.cachedSettings.removeAll() - self.updatedPeerIds.removeAll() + self.updatedInitialSettings.removeAll() } - override func beforeCommit() { - for peerId in self.updatedPeerIds { - if let settings = self.cachedSettings[peerId] { - self.sharedEncoder.reset() - self.sharedEncoder.encodeRootObject(settings) - - self.valueBox.set(self.table, key: self.key(peerId), value: self.sharedEncoder.readBufferNoCopy()) + func transactionParticipationInTotalUnreadCountUpdates() -> (added: Set, removed: Set) { + var added = Set() + var removed = Set() + + for (peerId, initialSettings) in self.updatedInitialSettings { + var wasParticipating = false + if let initialSettings = initialSettings { + wasParticipating = !initialSettings.isRemovedFromTotalUnreadCount + } + let isParticipating = !self.cachedSettings[peerId]!.isRemovedFromTotalUnreadCount + if wasParticipating != isParticipating { + if isParticipating { + added.insert(peerId) + } else { + removed.insert(peerId) + } } } - self.updatedPeerIds.removeAll() + return (added, removed) + } + + override func beforeCommit() { + if !self.updatedInitialSettings.isEmpty { + for (peerId, _) in self.updatedInitialSettings { + if let settings = self.cachedSettings[peerId] { + self.sharedEncoder.reset() + self.sharedEncoder.encodeRootObject(settings) + + self.valueBox.set(self.table, key: self.key(peerId), value: self.sharedEncoder.readBufferNoCopy()) + } + } + + self.updatedInitialSettings.removeAll() + } } } diff --git a/Postbox/PeerOperationLogMetadataTable.swift b/Postbox/PeerOperationLogMetadataTable.swift new file mode 100644 index 0000000000..ae413c7ec7 --- /dev/null +++ b/Postbox/PeerOperationLogMetadataTable.swift @@ -0,0 +1,41 @@ +import Foundation + +private enum PeerOperationLogIndexNamespace: Int8 { + case nextTagLocalIndex = 0 +} + +final class PeerOperationLogMetadataTable: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) + } + + private func keyForNextLocalIndex(peerId: PeerId, tag: PeerOperationLogTag) -> ValueBoxKey { + let key = ValueBoxKey(length: 1 + 8 + 1) + key.setInt8(0, value: PeerOperationLogIndexNamespace.nextTagLocalIndex.rawValue) + key.setInt64(1, value: peerId.toInt64()) + key.setInt8(9, value: tag.rawValue) + return key + } + + func getNextLocalIndex(peerId: PeerId, tag: PeerOperationLogTag) -> Int32 { + if let value = self.valueBox.get(self.table, key: self.keyForNextLocalIndex(peerId: peerId, tag: tag)) { + assert(value.length == 4) + var index: Int32 = 0 + value.read(&index, offset: 0, length: 4) + return index + } else { + return 0 + } + } + + func setNextLocalIndex(peerId: PeerId, tag: PeerOperationLogTag, index: Int32) { + var indexValue: Int32 = index + self.valueBox.set(self.table, key: self.keyForNextLocalIndex(peerId: peerId, tag: tag), value: MemoryBuffer(memory: &indexValue, capacity: 4, length: 4, freeWhenDone: false)) + } + + func takeNextLocalIndex(peerId: PeerId, tag: PeerOperationLogTag) -> Int32 { + let index = self.getNextLocalIndex(peerId: peerId, tag: tag) + self.setNextLocalIndex(peerId: peerId, tag: tag, index: index) + return index + } +} diff --git a/Postbox/PeerOperationLogTable.swift b/Postbox/PeerOperationLogTable.swift new file mode 100644 index 0000000000..a6d83f96f9 --- /dev/null +++ b/Postbox/PeerOperationLogTable.swift @@ -0,0 +1,53 @@ +import Foundation + +public enum PeerOperationLogTag: Int8 { + case inbox = 0 + case outbox = 1 +} + +public struct PeerOperationLogEntry { + let tagLocalIndex: Int32 + let contents: Coding +} + +final class PeerOperationLogQueue: Table { + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) + } + + private let metadataTable: PeerOperationLogMetadataTable + + init(valueBox: ValueBox, table: ValueBoxTable, metadataTable: PeerOperationLogMetadataTable) { + self.metadataTable = metadataTable + + super.init(valueBox: valueBox, table: table) + } + + private func key(peerId: PeerId, tag: PeerOperationLogTag, index: Int32) -> ValueBoxKey { + let key = ValueBoxKey(length: 8 + 1 + 4) + key.setInt64(0, value: peerId.toInt64()) + key.setInt8(1, value: tag.rawValue) + key.setInt32(9, value: index) + return key + } + + func addEntryAndTakeNextTagLocalIndex(peerId: PeerId, tag: PeerOperationLogTag, contents: Coding) -> Int32 { + let index = self.metadataTable.takeNextLocalIndex(peerId: peerId, tag: tag) + let encoder = Encoder() + encoder.encodeRootObject(contents) + self.valueBox.set(self.table, key: self.key(peerId: peerId, tag: tag, index: index), value: encoder.readBufferNoCopy()) + return index + } + + func removeEntries(peerId: PeerId, tag: PeerOperationLogTag, withIndicesLowerThan index: Int32) { + var indices: [Int32] = [] + self.valueBox.range(self.table, start: self.key(peerId: peerId, tag: tag, index: 0).predecessor, end: self.key(peerId: peerId, tag: tag, index: index), keys: { key in + indices.append(key.getInt32(9)) + return true + }, limit: 0) + + for index in indices { + self.valueBox.remove(self.table, key: self.key(peerId: peerId, tag: tag, index: index)) + } + } +} diff --git a/Postbox/Postbox.swift b/Postbox/Postbox.swift index b94c6902e6..155a1b195a 100644 --- a/Postbox/Postbox.swift +++ b/Postbox/Postbox.swift @@ -224,6 +224,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 currentReplaceChatListHoles: [(MessageIndex, ChatListHole?)] = [] private var currentReplacedContactPeerIds: Set? @@ -287,12 +288,12 @@ public final class Postbox { self.pipeNotifier = PipeNotifier(basePath: basePath, notify: { [weak self] in if let strongSelf = self { - strongSelf.queue.async { + /*strongSelf.queue.async { if strongSelf.valueBox != nil { let _ = strongSelf.modify({ _ -> Void in }).start() } - } + }*/ } }) @@ -351,7 +352,7 @@ public final class Postbox { self.metadataTable = MetadataTable(valueBox: self.valueBox, table: MetadataTable.tableSpec(0)) let userVersion: Int32? = self.metadataTable.userVersion() - let currentUserVersion: Int32 = 1 + let currentUserVersion: Int32 = 5 if userVersion != currentUserVersion { self.valueBox.drop() @@ -382,7 +383,7 @@ public final class Postbox { self.itemCacheTable = ItemCacheTable(valueBox: self.valueBox, table: ItemCacheTable.tableSpec(25)) self.peerNameTokenIndexTable = PeerNameTokenIndexTable(valueBox: self.valueBox, table: PeerNameTokenIndexTable.tableSpec(26)) self.peerNameIndexTable = PeerNameIndexTable(valueBox: self.valueBox, table: PeerNameIndexTable.tableSpec(27), peerTable: self.peerTable, peerNameTokenIndexTable: self.peerNameTokenIndexTable) - self.chatListIndexTable = ChatListIndexTable(valueBox: self.valueBox, table: ChatListIndexTable.tableSpec(8), peerNameIndexTable: self.peerNameIndexTable) + self.chatListIndexTable = ChatListIndexTable(valueBox: self.valueBox, table: ChatListIndexTable.tableSpec(8), peerNameIndexTable: self.peerNameIndexTable, metadataTable: self.messageHistoryMetadataTable, readStateTable: self.readStateTable, notificationSettingsTable: self.peerNotificationSettingsTable) self.chatListTable = ChatListTable(valueBox: self.valueBox, table: ChatListTable.tableSpec(9), indexTable: self.chatListIndexTable, metadataTable: self.messageHistoryMetadataTable, seedConfiguration: self.seedConfiguration) self.peerChatTopTaggedMessageIdsTable = PeerChatTopTaggedMessageIdsTable(valueBox: self.valueBox, table: PeerChatTopTaggedMessageIdsTable.tableSpec(28)) @@ -424,7 +425,11 @@ public final class Postbox { return self.cachedPeerDataTable.get(peerId) }, getPeerPresence: { peerId in return self.peerPresenceTable.get(peerId) - },unsentMessageIds: self.messageHistoryUnsentTable!.get(), synchronizePeerReadStateOperations: self.synchronizeReadStateTable!.get()) + }, getTotalUnreadCount: { + return self.messageHistoryMetadataTable.getChatListTotalUnreadCount() + }, getPeerReadState: { peerId in + return self.readStateTable.getCombinedState(peerId) + }, unsentMessageIds: self.messageHistoryUnsentTable!.get(), synchronizePeerReadStateOperations: self.synchronizeReadStateTable!.get()) print("(Postbox initialization took \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms") }) @@ -791,7 +796,11 @@ public final class Postbox { self.peerChatTopTaggedMessageIdsTable.replay(historyOperationsByPeerId: self.currentOperationsByPeerId) - let transaction = PostboxTransaction(currentOperationsByPeerId: self.currentOperationsByPeerId, peerIdsWithFilledHoles: self.currentFilledHolesByPeerId, removedHolesByPeerId: self.currentRemovedHolesByPeerId, chatListOperations: chatListOperations, currentUpdatedPeers: self.currentUpdatedPeers, currentUpdatedPeerNotificationSettings: self.currentUpdatedPeerNotificationSettings, currentUpdatedCachedPeerData: self.currentUpdatedCachedPeerData, currentUpdatedPeerPresences: currentUpdatedPeerPresences, currentUpdatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, updatedMedia: self.currentUpdatedMedia, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentUpdatedMasterClientId: currentUpdatedMasterClientId) + let transactionUnreadCountDeltas = self.readStateTable.transactionUnreadCountDeltas() + let transactionParticipationInTotalUnreadCountUpdates = self.peerNotificationSettingsTable.transactionParticipationInTotalUnreadCountUpdates() + self.chatListIndexTable.commitWithTransactionUnreadCountDeltas(transactionUnreadCountDeltas, transactionParticipationInTotalUnreadCountUpdates: transactionParticipationInTotalUnreadCountUpdates, updatedTotalUnreadCount: &self.currentUpdatedTotalUnreadCount) + + let transaction = PostboxTransaction(currentOperationsByPeerId: self.currentOperationsByPeerId, peerIdsWithFilledHoles: self.currentFilledHolesByPeerId, removedHolesByPeerId: self.currentRemovedHolesByPeerId, chatListOperations: chatListOperations, currentUpdatedPeers: self.currentUpdatedPeers, currentUpdatedPeerNotificationSettings: self.currentUpdatedPeerNotificationSettings, currentUpdatedCachedPeerData: self.currentUpdatedCachedPeerData, currentUpdatedPeerPresences: currentUpdatedPeerPresences, currentUpdatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, currentUpdatedTotalUnreadCount: self.currentUpdatedTotalUnreadCount, peerIdsWithUpdatedUnreadCounts: Set(transactionUnreadCountDeltas.keys), unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, updatedMedia: self.currentUpdatedMedia, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentUpdatedMasterClientId: currentUpdatedMasterClientId) var updatedTransactionState: Int64? var updatedMasterClientId: Int64? if !transaction.isEmpty { @@ -963,7 +972,7 @@ public final class Postbox { subscriber.putCompletion() if updatedTransactionState != nil || updatedMasterClientId != nil { - self.pipeNotifier.notify() + //self.pipeNotifier.notify() } if let updatedMasterClientId = updatedMasterClientId { @@ -1276,6 +1285,35 @@ public final class Postbox { } |> switchToLatest } + public func unreadMessageCountsView(items: [UnreadMessageCountsItem]) -> Signal { + return self.modify { modifier -> Signal in + let entries: [UnreadMessageCountsItemEntry] = items.map { item in + switch item { + case .total: + return .total(self.messageHistoryMetadataTable.getChatListTotalUnreadCount()) + case let .peer(peerId): + var count: Int32 = 0 + if let combinedState = self.readStateTable.getCombinedState(peerId) { + count = combinedState.count + } + return .peer(peerId, count) + } + } + let view = MutableUnreadMessageCountsView(entries: entries) + let (index, signal) = self.viewTracker.addUnreadMessageCountsView(view) + + return (.single(UnreadMessageCountsView(view)) + |> then(signal)) + |> afterDisposed { [weak self] in + if let strongSelf = self { + strongSelf.queue.async { + strongSelf.viewTracker.removeUnreadMessageCountsView(index) + } + } + } + } |> switchToLatest + } + public func updateMessageHistoryViewVisibleRange(_ id: MessageHistoryViewId, earliestVisibleIndex: MessageIndex, latestVisibleIndex: MessageIndex) { self.modify({ modifier -> Void in self.viewTracker.updateMessageHistoryViewVisibleRange(id, earliestVisibleIndex: earliestVisibleIndex, latestVisibleIndex: latestVisibleIndex) diff --git a/Postbox/PostboxTransaction.swift b/Postbox/PostboxTransaction.swift index 58ad00f548..673b7f5636 100644 --- a/Postbox/PostboxTransaction.swift +++ b/Postbox/PostboxTransaction.swift @@ -10,6 +10,8 @@ final class PostboxTransaction { let currentUpdatedCachedPeerData: [PeerId: CachedPeerData] let currentUpdatedPeerPresences: [PeerId: PeerPresence] let currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?] + let currentUpdatedTotalUnreadCount: Int32? + let peerIdsWithUpdatedUnreadCounts: Set let unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation] let updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?] @@ -60,10 +62,16 @@ final class PostboxTransaction { if currentUpdatedMasterClientId != nil { return false } + if currentUpdatedTotalUnreadCount != nil { + return false + } + if !peerIdsWithUpdatedUnreadCounts.isEmpty { + return false + } return true } - init(currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], peerIdsWithFilledHoles: [PeerId: [MessageIndex: HoleFillDirection]], removedHolesByPeerId: [PeerId: [MessageIndex: HoleFillDirection]], chatListOperations: [ChatListOperation], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: PeerNotificationSettings], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], updatedMedia: [MediaId: Media?], replaceContactPeerIds: Set?, currentUpdatedMasterClientId: Int64?) { + init(currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], peerIdsWithFilledHoles: [PeerId: [MessageIndex: HoleFillDirection]], removedHolesByPeerId: [PeerId: [MessageIndex: HoleFillDirection]], chatListOperations: [ChatListOperation], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: PeerNotificationSettings], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?], currentUpdatedTotalUnreadCount: Int32?, peerIdsWithUpdatedUnreadCounts: Set, unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], updatedMedia: [MediaId: Media?], replaceContactPeerIds: Set?, currentUpdatedMasterClientId: Int64?) { self.currentOperationsByPeerId = currentOperationsByPeerId self.peerIdsWithFilledHoles = peerIdsWithFilledHoles self.removedHolesByPeerId = removedHolesByPeerId @@ -73,6 +81,8 @@ final class PostboxTransaction { self.currentUpdatedCachedPeerData = currentUpdatedCachedPeerData self.currentUpdatedPeerPresences = currentUpdatedPeerPresences self.currentUpdatedPeerChatListEmbeddedStates = currentUpdatedPeerChatListEmbeddedStates + self.currentUpdatedTotalUnreadCount = currentUpdatedTotalUnreadCount + self.peerIdsWithUpdatedUnreadCounts = peerIdsWithUpdatedUnreadCounts self.unsentMessageOperations = unsentMessageOperations self.updatedSynchronizePeerReadStateOperations = updatedSynchronizePeerReadStateOperations self.updatedMedia = updatedMedia diff --git a/Postbox/UnreadMessageCountsView.swift b/Postbox/UnreadMessageCountsView.swift new file mode 100644 index 0000000000..21551728f4 --- /dev/null +++ b/Postbox/UnreadMessageCountsView.swift @@ -0,0 +1,69 @@ +import Foundation + +public enum UnreadMessageCountsItem { + case total + case peer(PeerId) +} + +enum UnreadMessageCountsItemEntry { + case total(Int32) + case peer(PeerId, Int32) +} + +final class MutableUnreadMessageCountsView { + fileprivate var entries: [UnreadMessageCountsItemEntry] + + init(entries: [UnreadMessageCountsItemEntry]) { + self.entries = entries + } + + func replay(peerIdsWithUpdatedUnreadCounts: Set, getTotalUnreadCount: () -> Int32, getPeerReadState: (PeerId) -> CombinedPeerReadState?) -> Bool { + var updated = false + + for i in 0 ..< self.entries.count { + switch self.entries[i] { + case let .total(count): + let updatedCount = getTotalUnreadCount() + if updatedCount != count { + self.entries[i] = .total(updatedCount) + updated = true + } + case let .peer(peerId, count): + if peerIdsWithUpdatedUnreadCounts.contains(peerId) { + var updatedCount: Int32 = 0 + if let combinedState = getPeerReadState(peerId) { + updatedCount = combinedState.count + } + self.entries[i] = .peer(peerId, updatedCount) + updated = true + } + } + } + + return updated + } +} + +public final class UnreadMessageCountsView { + private let entries: [UnreadMessageCountsItemEntry] + + init(_ view: MutableUnreadMessageCountsView) { + self.entries = view.entries + } + + 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 .peer(peerId, count): + if case .peer(peerId) = item { + return count + } + } + } + return nil + } +} diff --git a/Postbox/ValueBoxKey.swift b/Postbox/ValueBoxKey.swift index 414d7bd532..e88a1b97a2 100644 --- a/Postbox/ValueBoxKey.swift +++ b/Postbox/ValueBoxKey.swift @@ -12,7 +12,7 @@ private final class ValueBoxKeyImpl { } } -public struct ValueBoxKey: Hashable, CustomStringConvertible { +public struct ValueBoxKey: Hashable, CustomStringConvertible, Comparable { public let memory: UnsafeMutableRawPointer public let length: Int private let impl: ValueBoxKeyImpl @@ -157,10 +157,14 @@ public struct ValueBoxKey: Hashable, CustomStringConvertible { } return hash } -} - -public func ==(lhs: ValueBoxKey, rhs: ValueBoxKey) -> Bool { - return lhs.length == rhs.length && memcmp(lhs.memory, rhs.memory, lhs.length) == 0 + + public static func ==(lhs: ValueBoxKey, rhs: ValueBoxKey) -> Bool { + return lhs.length == rhs.length && memcmp(lhs.memory, rhs.memory, lhs.length) == 0 + } + + public static func <(lhs: ValueBoxKey, rhs: ValueBoxKey) -> Bool { + return mdb_cmp_memn(lhs.memory, lhs.length, rhs.memory, rhs.length) < 0 + } } private func mdb_cmp_memn(_ a_memory: UnsafeMutableRawPointer, _ a_length: Int, _ b_memory: UnsafeMutableRawPointer, _ b_length: Int) -> Int @@ -179,7 +183,3 @@ private func mdb_cmp_memn(_ a_memory: UnsafeMutableRawPointer, _ a_length: Int, diff = Int(memcmp(a_memory, b_memory, len)) return diff != 0 ? diff : len_diff < 0 ? -1 : len_diff } - -public func <(lhs: ValueBoxKey, rhs: ValueBoxKey) -> Bool { - return mdb_cmp_memn(lhs.memory, lhs.length, rhs.memory, rhs.length) < 0 -} diff --git a/Postbox/ViewTracker.swift b/Postbox/ViewTracker.swift index a0cc0a8a4f..f424a9572b 100644 --- a/Postbox/ViewTracker.swift +++ b/Postbox/ViewTracker.swift @@ -24,6 +24,8 @@ final class ViewTracker { private let getPeerNotificationSettings: (PeerId) -> PeerNotificationSettings? private let getCachedPeerData: (PeerId) -> CachedPeerData? private let getPeerPresence: (PeerId) -> PeerPresence? + private let getTotalUnreadCount: () -> Int32 + private let getPeerReadState: (PeerId) -> CombinedPeerReadState? private var chatListViews = Bag<(MutableChatListView, ValuePipe<(ChatListView, ViewUpdateType)>)>() private var messageHistoryViews: [PeerId: Bag<(MutableMessageHistoryView, ValuePipe<(MessageHistoryView, ViewUpdateType)>)>] = [:] @@ -44,7 +46,9 @@ final class ViewTracker { private var peerViews = Bag<(MutablePeerView, ValuePipe)>() - init(queue: Queue, fetchEarlierHistoryEntries: @escaping (PeerId, MessageIndex?, Int, MessageTags?) -> [MutableMessageHistoryEntry], fetchLaterHistoryEntries: @escaping (PeerId, MessageIndex?, Int, MessageTags?) -> [MutableMessageHistoryEntry], fetchEarlierChatEntries: @escaping (MessageIndex?, Int) -> [MutableChatListEntry], fetchLaterChatEntries: @escaping (MessageIndex?, Int) -> [MutableChatListEntry], fetchAnchorIndex: @escaping (MessageId) -> MessageHistoryAnchorIndex?, renderMessage: @escaping (IntermediateMessage) -> Message, getPeer: @escaping (PeerId) -> Peer?, getPeerNotificationSettings: @escaping (PeerId) -> PeerNotificationSettings?, getCachedPeerData: @escaping (PeerId) -> CachedPeerData?, getPeerPresence: @escaping (PeerId) -> PeerPresence?, unsentMessageIds: [MessageId], synchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation]) { + private var unreadMessageCountsViews = Bag<(MutableUnreadMessageCountsView, ValuePipe)>() + + init(queue: Queue, fetchEarlierHistoryEntries: @escaping (PeerId, MessageIndex?, Int, MessageTags?) -> [MutableMessageHistoryEntry], fetchLaterHistoryEntries: @escaping (PeerId, MessageIndex?, Int, MessageTags?) -> [MutableMessageHistoryEntry], fetchEarlierChatEntries: @escaping (MessageIndex?, Int) -> [MutableChatListEntry], fetchLaterChatEntries: @escaping (MessageIndex?, Int) -> [MutableChatListEntry], fetchAnchorIndex: @escaping (MessageId) -> MessageHistoryAnchorIndex?, 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?, unsentMessageIds: [MessageId], synchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation]) { self.queue = queue self.fetchEarlierHistoryEntries = fetchEarlierHistoryEntries self.fetchLaterHistoryEntries = fetchLaterHistoryEntries @@ -56,6 +60,8 @@ final class ViewTracker { self.getPeerNotificationSettings = getPeerNotificationSettings self.getCachedPeerData = getCachedPeerData self.getPeerPresence = getPeerPresence + self.getTotalUnreadCount = getTotalUnreadCount + self.getPeerReadState = getPeerReadState self.unsentMessageView = UnsentMessageHistoryView(ids: unsentMessageIds) self.synchronizeReadStatesView = MutableSynchronizePeerReadStatesView(operations: synchronizePeerReadStateOperations) @@ -165,6 +171,17 @@ final class ViewTracker { self.peerViews.remove(index) } + func addUnreadMessageCountsView(_ view: MutableUnreadMessageCountsView) -> (Bag<(MutableUnreadMessageCountsView, ValuePipe)>.Index, Signal) { + let record = (view, ValuePipe()) + let index = self.unreadMessageCountsViews.add(record) + + return (index, record.1.signal()) + } + + func removeUnreadMessageCountsView(_ index: Bag<(MutableUnreadMessageCountsView, ValuePipe)>.Index) { + self.peerViews.remove(index) + } + func refreshViewsDueToExternalTransaction(fetchAroundChatEntries: (_ index: MessageIndex, _ count: Int) -> (entries: [MutableChatListEntry], earlier: MutableChatListEntry?, later: MutableChatListEntry?), fetchAroundHistoryEntries: (_ index: MessageIndex, _ count: Int, _ tagMask: MessageTags?) -> (entries: [MutableMessageHistoryEntry], lower: MutableMessageHistoryEntry?, upper: MutableMessageHistoryEntry?), fetchUnsentMessageIds: () -> [MessageId], fetchSynchronizePeerReadStateOperations: () -> [PeerId: PeerReadStateSynchronizationOperation]) { var updateTrackedHolesPeerIds: [PeerId] = [] @@ -316,6 +333,12 @@ final class ViewTracker { self.synchronizeReadStateViewUpdated() } + for (view, pipe) in self.unreadMessageCountsViews.copyItems() { + if view.replay(peerIdsWithUpdatedUnreadCounts: transaction.peerIdsWithUpdatedUnreadCounts, getTotalUnreadCount: self.getTotalUnreadCount, getPeerReadState: self.getPeerReadState) { + pipe.putNext(UnreadMessageCountsView(view)) + } + } + if let replaceContactPeerIds = transaction.replaceContactPeerIds { for (mutableView, pipe) in self.contactPeerIdsViews.copyItems() { if mutableView.replay(replace: replaceContactPeerIds) { diff --git a/PostboxTests/ChatListTableTests.swift b/PostboxTests/ChatListTableTests.swift index 3dfd380568..b7ea687151 100644 --- a/PostboxTests/ChatListTableTests.swift +++ b/PostboxTests/ChatListTableTests.swift @@ -77,6 +77,10 @@ class ChatListTableTests: XCTestCase { var readStateTable: MessageHistoryReadStateTable? var synchronizeReadStateTable: MessageHistorySynchronizeReadStateTable? var peerChatInterfaceStateTable: PeerChatInterfaceStateTable? + var peerTable: PeerTable? + var peerNameTokenIndexTable: PeerNameTokenIndexTable? + var peerNameIndexTable: PeerNameIndexTable? + var notificationSettingsTable: PeerNotificationSettingsTable? override class func setUp() { super.setUp() @@ -101,7 +105,11 @@ class ChatListTableTests: XCTestCase { self.readStateTable = MessageHistoryReadStateTable(valueBox: self.valueBox!, table: MessageHistoryReadStateTable.tableSpec(11)) self.synchronizeReadStateTable = MessageHistorySynchronizeReadStateTable(valueBox: self.valueBox!, table: MessageHistorySynchronizeReadStateTable.tableSpec(12)) self.historyTable = MessageHistoryTable(valueBox: self.valueBox!, table: MessageHistoryTable.tableSpec(4), messageHistoryIndexTable: self.indexTable!, messageMediaTable: self.mediaTable!, historyMetadataTable: self.historyMetadataTable!, unsentTable: self.unsentTable!, tagsTable: self.tagsTable!, readStateTable: self.readStateTable!, synchronizeReadStateTable: self.synchronizeReadStateTable!) - self.chatListIndexTable = ChatListIndexTable(valueBox: self.valueBox!, table: ChatListIndexTable.tableSpec(5)) + self.peerTable = PeerTable(valueBox: self.valueBox!, table: PeerTable.tableSpec(20)) + self.peerNameTokenIndexTable = PeerNameTokenIndexTable(valueBox: self.valueBox!, table: PeerNameTokenIndexTable.tableSpec(21)) + self.peerNameIndexTable = PeerNameIndexTable(valueBox: self.valueBox!, table: PeerNameIndexTable.tableSpec(22), peerTable: self.peerTable!, peerNameTokenIndexTable: self.peerNameTokenIndexTable!) + self.notificationSettingsTable = PeerNotificationSettingsTable(valueBox: self.valueBox!, table: PeerNotificationSettingsTable.tableSpec(23)) + self.chatListIndexTable = ChatListIndexTable(valueBox: self.valueBox!, table: ChatListIndexTable.tableSpec(5), peerNameIndexTable: self.peerNameIndexTable!, metadataTable: self.historyMetadataTable!, readStateTable: self.readStateTable!, notificationSettingsTable: self.notificationSettingsTable!) self.chatListTable = ChatListTable(valueBox: self.valueBox!, table: ChatListTable.tableSpec(6), indexTable: self.chatListIndexTable!, metadataTable: self.historyMetadataTable!, seedConfiguration: seedConfiguration) self.peerChatInterfaceStateTable = PeerChatInterfaceStateTable(valueBox: self.valueBox!, table: PeerChatInterfaceStateTable.tableSpec(20)) } @@ -125,7 +133,7 @@ class ChatListTableTests: XCTestCase { var unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation] = [] var updatedPeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?] = [:] let updatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?] = [:] - self.historyTable!.addMessages([StoreMessage(id: MessageId(peerId: PeerId(namespace: namespace, id: peerId), namespace: namespace, id: id), timestamp: timestamp, flags: [], tags: [], forwardInfo: nil, authorId: authorPeerId, text: text, attributes: [], media: media)], location: .Random, operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) + self.historyTable!.addMessages([StoreMessage(id: MessageId(peerId: PeerId(namespace: namespace, id: peerId), namespace: namespace, id: id), globallyUniqueId: nil, timestamp: timestamp, flags: [], tags: [], forwardInfo: nil, authorId: authorPeerId, text: text, attributes: [], media: media)], location: .Random, operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) var operations: [ChatListOperation] = [] self.chatListTable!.replay(historyOperationsByPeerId: operationsByPeerId, updatedPeerChatListEmbeddedStates: updatedPeerChatListEmbeddedStates, messageHistoryTable: self.historyTable!, peerChatInterfaceStateTable: self.peerChatInterfaceStateTable!, operations: &operations) } @@ -170,7 +178,7 @@ class ChatListTableTests: XCTestCase { var unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation] = [] var updatedPeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?] = [:] let updatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?] = [:] - self.historyTable!.fillHole(MessageId(peerId: PeerId(namespace: namespace, id: peerId), namespace: namespace, id: id), fillType: fillType, tagMask: nil, messages: messages.map({ StoreMessage(id: MessageId(peerId: PeerId(namespace: namespace, id: peerId), namespace: namespace, id: $0.0), timestamp: $0.1, flags: [], tags: [], forwardInfo: nil, authorId: authorPeerId, text: $0.2, attributes: [], media: $0.3) }), operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) + self.historyTable!.fillHole(MessageId(peerId: PeerId(namespace: namespace, id: peerId), namespace: namespace, id: id), fillType: fillType, tagMask: nil, messages: messages.map({ StoreMessage(id: MessageId(peerId: PeerId(namespace: namespace, id: peerId), namespace: namespace, id: $0.0), globallyUniqueId: nil, timestamp: $0.1, flags: [], tags: [], forwardInfo: nil, authorId: authorPeerId, text: $0.2, attributes: [], media: $0.3) }), operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) var operations: [ChatListOperation] = [] self.chatListTable!.replay(historyOperationsByPeerId: operationsByPeerId, updatedPeerChatListEmbeddedStates: updatedPeerChatListEmbeddedStates, messageHistoryTable: self.historyTable!, peerChatInterfaceStateTable: self.peerChatInterfaceStateTable!, operations: &operations) } diff --git a/PostboxTests/MessageHistoryIndexTableTests.swift b/PostboxTests/MessageHistoryIndexTableTests.swift index f55ee0490a..e304ac53d2 100644 --- a/PostboxTests/MessageHistoryIndexTableTests.swift +++ b/PostboxTests/MessageHistoryIndexTableTests.swift @@ -101,13 +101,13 @@ class MessageHistoryIndexTableTests: XCTestCase { func addMessage(_ id: Int32, _ timestamp: Int32) { var operations: [MessageHistoryIndexOperation] = [] - self.indexTable!.addMessages([InternalStoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: id), timestamp: timestamp, flags: [], tags: [], forwardInfo: nil, authorId: peerId, text: "", attributes: [], media: [])], location: .Random, operations: &operations) + self.indexTable!.addMessages([InternalStoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: id), timestamp: timestamp, globallyUniqueId: nil, flags: [], tags: [], forwardInfo: nil, authorId: peerId, text: "", attributes: [], media: [])], location: .Random, operations: &operations) } func addMessagesUpperBlock(_ messages: [(Int32, Int32)]) { var operations: [MessageHistoryIndexOperation] = [] self.indexTable!.addMessages(messages.map { (id, timestamp) in - return InternalStoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: id), timestamp: timestamp, flags: [], tags: [], forwardInfo: nil, authorId: peerId, text: "", attributes: [], media: []) + return InternalStoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: id), timestamp: timestamp, globallyUniqueId: nil, flags: [], tags: [], forwardInfo: nil, authorId: peerId, text: "", attributes: [], media: []) }, location: .UpperHistoryBlock, operations: &operations) } @@ -115,7 +115,7 @@ class MessageHistoryIndexTableTests: XCTestCase { var operations: [MessageHistoryIndexOperation] = [] self.indexTable!.fillHole(MessageId(peerId: peerId, namespace: namespace, id: id), fillType: fillType, tagMask: tagMask, messages: messages.map({ - return InternalStoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: $0.0), timestamp: $0.1, flags: [], tags: [], forwardInfo: nil, authorId: peerId, text: "", attributes: [], media: []) + return InternalStoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: $0.0), timestamp: $0.1, globallyUniqueId: nil, flags: [], tags: [], forwardInfo: nil, authorId: peerId, text: "", attributes: [], media: []) }), operations: &operations) } diff --git a/PostboxTests/MessageHistoryTableTests.swift b/PostboxTests/MessageHistoryTableTests.swift index 153912cd12..74b607c9dc 100644 --- a/PostboxTests/MessageHistoryTableTests.swift +++ b/PostboxTests/MessageHistoryTableTests.swift @@ -123,7 +123,7 @@ private class TestExternalMedia: Media { private class TestPeer: Peer { var indexName: PeerIndexNameRepresentation { - return .title("Test") + return .title(title: "Test", addressName: nil) } let id: PeerId @@ -259,14 +259,14 @@ class MessageHistoryTableTests: XCTestCase { var operationsByPeerId: [PeerId: [MessageHistoryOperation]] = [:] var unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation] = [] var updatedPeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?] = [:] - self.historyTable!.addMessages([StoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: id), timestamp: timestamp, flags: flags, tags: tags, forwardInfo: nil, authorId: authorPeerId, text: text, attributes: [], media: media)], location: .Random, operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) + self.historyTable!.addMessages([StoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: id), globallyUniqueId: nil, timestamp: timestamp, flags: flags, tags: tags, forwardInfo: nil, authorId: authorPeerId, text: text, attributes: [], media: media)], location: .Random, operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) } private func updateMessage(_ previousId: Int32, _ id: Int32, _ timestamp: Int32, _ text: String = "", _ media: [Media] = [], _ flags: StoreMessageFlags, _ tags: MessageTags) { var operationsByPeerId: [PeerId: [MessageHistoryOperation]] = [:] var unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation] = [] var updatedPeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?] = [:] - self.historyTable!.updateMessage(MessageId(peerId: peerId, namespace: namespace, id: previousId), message: StoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: id), timestamp: timestamp, flags: flags, tags: tags, forwardInfo: nil, authorId: authorPeerId, text: text, attributes: [], media: media), operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) + self.historyTable!.updateMessage(MessageId(peerId: peerId, namespace: namespace, id: previousId), message: StoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: id), globallyUniqueId: nil, timestamp: timestamp, flags: flags, tags: tags, forwardInfo: nil, authorId: authorPeerId, text: text, attributes: [], media: media), operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) } private func addHole(_ id: Int32) { @@ -287,7 +287,7 @@ class MessageHistoryTableTests: XCTestCase { var operationsByPeerId: [PeerId: [MessageHistoryOperation]] = [:] var unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation] = [] var updatedPeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?] = [:] - self.historyTable!.fillHole(MessageId(peerId: peerId, namespace: namespace, id: id), fillType: fillType, tagMask: tagMask, messages: messages.map({ StoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: $0.0), timestamp: $0.1, flags: [], tags: [], forwardInfo: nil, authorId: authorPeerId, text: $0.2, attributes: [], media: $0.3) }), operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) + self.historyTable!.fillHole(MessageId(peerId: peerId, namespace: namespace, id: id), fillType: fillType, tagMask: tagMask, messages: messages.map({ StoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: $0.0), globallyUniqueId: nil, timestamp: $0.1, flags: [], tags: [], forwardInfo: nil, authorId: authorPeerId, text: $0.2, attributes: [], media: $0.3) }), operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) } private func expectEntries(_ entries: [Entry], tagMask: MessageTags? = nil) { diff --git a/PostboxTests/ReadStateTableTests.swift b/PostboxTests/ReadStateTableTests.swift index ddb5b1642a..59e84ad7ac 100644 --- a/PostboxTests/ReadStateTableTests.swift +++ b/PostboxTests/ReadStateTableTests.swift @@ -117,14 +117,14 @@ class ReadStateTableTests: XCTestCase { var operationsByPeerId: [PeerId: [MessageHistoryOperation]] = [:] var unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation] = [] var updatedPeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?] = [:] - self.historyTable!.addMessages([StoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: id), timestamp: timestamp, flags: flags, tags: tags, forwardInfo: nil, authorId: authorPeerId, text: text, attributes: [], media: media)], location: .Random, operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) + self.historyTable!.addMessages([StoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: id), globallyUniqueId: nil, timestamp: timestamp, flags: flags, tags: tags, forwardInfo: nil, authorId: authorPeerId, text: text, attributes: [], media: media)], location: .Random, operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) } private func updateMessage(_ previousId: Int32, _ id: Int32, _ timestamp: Int32, _ text: String = "", _ media: [Media] = [], _ flags: StoreMessageFlags, _ tags: MessageTags) { var operationsByPeerId: [PeerId: [MessageHistoryOperation]] = [:] var unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation] = [] var updatedPeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?] = [:] - self.historyTable!.updateMessage(MessageId(peerId: peerId, namespace: namespace, id: previousId), message: StoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: id), timestamp: timestamp, flags: flags, tags: tags, forwardInfo: nil, authorId: authorPeerId, text: text, attributes: [], media: media), operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) + self.historyTable!.updateMessage(MessageId(peerId: peerId, namespace: namespace, id: previousId), message: StoreMessage(id: MessageId(peerId: peerId, namespace: namespace, id: id), globallyUniqueId: nil, timestamp: timestamp, flags: flags, tags: tags, forwardInfo: nil, authorId: authorPeerId, text: text, attributes: [], media: media), operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) } private func addHole(_ id: Int32) {