From 3da3ebe3e2079ea55c62abba698cd8be3e75331a Mon Sep 17 00:00:00 2001 From: Peter Date: Tue, 31 Jan 2017 00:28:16 +0300 Subject: [PATCH] no message --- Postbox.xcodeproj/project.pbxproj | 34 ++- Postbox/ContactPeersView.swift | 5 +- Postbox/MultiplePeersView.swift | 52 ++++ ...licy.swift => PeerChatListInclusion.swift} | 2 +- Postbox/PeerNotificationSettingsTable.swift | 22 ++ Postbox/Postbox.swift | 91 ++++++- Postbox/PostboxTransaction.swift | 7 +- Postbox/PreferencesEntry.swift | 5 + Postbox/PreferencesTable.swift | 58 +++++ Postbox/PreferencesView.swift | 55 +++++ Postbox/SqliteValueBox.swift | 230 ++++++++++++------ Postbox/ValueBox.swift | 4 +- Postbox/ValueBoxKey.swift | 12 + Postbox/ViewTracker.swift | 81 ++++-- 14 files changed, 540 insertions(+), 118 deletions(-) create mode 100644 Postbox/MultiplePeersView.swift rename Postbox/{PeerChatListPolicy.swift => PeerChatListInclusion.swift} (82%) create mode 100644 Postbox/PreferencesEntry.swift create mode 100644 Postbox/PreferencesTable.swift create mode 100644 Postbox/PreferencesView.swift diff --git a/Postbox.xcodeproj/project.pbxproj b/Postbox.xcodeproj/project.pbxproj index 585e8e157e..c8b5922ab2 100644 --- a/Postbox.xcodeproj/project.pbxproj +++ b/Postbox.xcodeproj/project.pbxproj @@ -17,8 +17,8 @@ D0079F631D5A242500A27A2C /* ContactPeerIdsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0079F621D5A242500A27A2C /* ContactPeerIdsView.swift */; }; D0079F651D5A457A00A27A2C /* ContactPeersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0079F641D5A457A00A27A2C /* ContactPeersView.swift */; }; D0079F6B1D5B3AAB00A27A2C /* PeerNameIndexRepresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0079F6A1D5B3AAB00A27A2C /* PeerNameIndexRepresentation.swift */; }; - D00C7CD41E365C4E0080C3D5 /* PeerChatListPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00C7CD31E365C4E0080C3D5 /* PeerChatListPolicy.swift */; }; - D00C7CD51E365C4E0080C3D5 /* PeerChatListPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00C7CD31E365C4E0080C3D5 /* PeerChatListPolicy.swift */; }; + D00C7CD41E365C4E0080C3D5 /* PeerChatListInclusion.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00C7CD31E365C4E0080C3D5 /* PeerChatListInclusion.swift */; }; + D00C7CD51E365C4E0080C3D5 /* PeerChatListInclusion.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00C7CD31E365C4E0080C3D5 /* PeerChatListInclusion.swift */; }; D00C7CE31E37861C0080C3D5 /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00C7CE21E37861C0080C3D5 /* MessageView.swift */; }; D00C7CE41E37861C0080C3D5 /* MessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00C7CE21E37861C0080C3D5 /* MessageView.swift */; }; D00EED1E1C81F28D00341DFF /* MessageHistoryTagsTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00EED1D1C81F28D00341DFF /* MessageHistoryTagsTable.swift */; }; @@ -75,6 +75,12 @@ D07CFF811DCA765D00761F81 /* PeerChatInterfaceState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07CFF801DCA765D00761F81 /* PeerChatInterfaceState.swift */; }; D07CFF831DCA909100761F81 /* PeerChatInterfaceStateTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07CFF821DCA909100761F81 /* PeerChatInterfaceStateTable.swift */; }; D07CFF851DCA99C400761F81 /* InitialMessageHistoryData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07CFF841DCA99C400761F81 /* InitialMessageHistoryData.swift */; }; + D08775001E3E3D9F00A97350 /* PreferencesTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08774FF1E3E3D9F00A97350 /* PreferencesTable.swift */; }; + D08775011E3E3D9F00A97350 /* PreferencesTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08774FF1E3E3D9F00A97350 /* PreferencesTable.swift */; }; + D08775031E3E3E7400A97350 /* PreferencesEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08775021E3E3E7400A97350 /* PreferencesEntry.swift */; }; + D08775041E3E3E7400A97350 /* PreferencesEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08775021E3E3E7400A97350 /* PreferencesEntry.swift */; }; + D08775061E3E3F2100A97350 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08775051E3E3F2100A97350 /* PreferencesView.swift */; }; + D08775071E3E3F2100A97350 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08775051E3E3F2100A97350 /* PreferencesView.swift */; }; D08C713A1C501F0700779C0F /* MessageHistoryHole.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C71391C501F0700779C0F /* MessageHistoryHole.swift */; }; D08C713C1C51283C00779C0F /* MessageHistoryIndexTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C713B1C51283C00779C0F /* MessageHistoryIndexTable.swift */; }; D08C713E1C512EA500779C0F /* MessageHistoryTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08C713D1C512EA500779C0F /* MessageHistoryTable.swift */; }; @@ -138,6 +144,7 @@ D0B844071DAB91B5005F29E1 /* MediaResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CE63F51CA1CCB2002BC462 /* MediaResource.swift */; }; D0B8440A1DAB91B5005F29E1 /* RandomAccessMediaResourceContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09ADF091D2E89F300C8208D /* RandomAccessMediaResourceContext.swift */; }; D0B844511DAC04FE005F29E1 /* PeerPresence.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B844501DAC04FE005F29E1 /* PeerPresence.swift */; }; + D0BC386C1E3FCEE50044D6FE /* MultiplePeersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC386B1E3FCEE50044D6FE /* MultiplePeersView.swift */; }; D0C07F6A1B67DB4800966E43 /* SwiftSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C07F691B67DB4800966E43 /* SwiftSignalKit.framework */; }; D0C674C81CBB11C600183765 /* MessageHistoryReadStateTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C674C71CBB11C600183765 /* MessageHistoryReadStateTable.swift */; }; D0C674CC1CBB14A700183765 /* PeerReadState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C674CB1CBB14A700183765 /* PeerReadState.swift */; }; @@ -230,7 +237,7 @@ D0079F621D5A242500A27A2C /* ContactPeerIdsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactPeerIdsView.swift; sourceTree = ""; }; D0079F641D5A457A00A27A2C /* ContactPeersView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactPeersView.swift; sourceTree = ""; }; D0079F6A1D5B3AAB00A27A2C /* PeerNameIndexRepresentation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerNameIndexRepresentation.swift; sourceTree = ""; }; - D00C7CD31E365C4E0080C3D5 /* PeerChatListPolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerChatListPolicy.swift; sourceTree = ""; }; + D00C7CD31E365C4E0080C3D5 /* PeerChatListInclusion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerChatListInclusion.swift; sourceTree = ""; }; D00C7CE21E37861C0080C3D5 /* MessageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageView.swift; sourceTree = ""; }; D00EED1D1C81F28D00341DFF /* MessageHistoryTagsTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageHistoryTagsTable.swift; sourceTree = ""; }; D010B6191E1E463900C3E282 /* PeerMergedOperationLogIndexTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerMergedOperationLogIndexTable.swift; sourceTree = ""; }; @@ -272,6 +279,9 @@ D07CFF801DCA765D00761F81 /* PeerChatInterfaceState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerChatInterfaceState.swift; sourceTree = ""; }; D07CFF821DCA909100761F81 /* PeerChatInterfaceStateTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerChatInterfaceStateTable.swift; sourceTree = ""; }; D07CFF841DCA99C400761F81 /* InitialMessageHistoryData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InitialMessageHistoryData.swift; sourceTree = ""; }; + D08774FF1E3E3D9F00A97350 /* PreferencesTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesTable.swift; sourceTree = ""; }; + D08775021E3E3E7400A97350 /* PreferencesEntry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesEntry.swift; sourceTree = ""; }; + D08775051E3E3F2100A97350 /* PreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = ""; }; D08C71391C501F0700779C0F /* MessageHistoryHole.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageHistoryHole.swift; sourceTree = ""; }; D08C713B1C51283C00779C0F /* MessageHistoryIndexTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageHistoryIndexTable.swift; sourceTree = ""; }; D08C713D1C512EA500779C0F /* MessageHistoryTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageHistoryTable.swift; sourceTree = ""; }; @@ -298,6 +308,7 @@ D0B418161D7DFAF3004562A4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D0B418601D7DFE95004562A4 /* SwiftSignalKitMac.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftSignalKitMac.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Telegram-iOS-diblohvjozhgaifjcniwdlixlilx/Build/Products/Debug/SwiftSignalKitMac.framework"; sourceTree = ""; }; D0B844501DAC04FE005F29E1 /* PeerPresence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerPresence.swift; sourceTree = ""; }; + D0BC386B1E3FCEE50044D6FE /* MultiplePeersView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultiplePeersView.swift; sourceTree = ""; }; D0C07F691B67DB4800966E43 /* SwiftSignalKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = SwiftSignalKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D0C674C71CBB11C600183765 /* MessageHistoryReadStateTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageHistoryReadStateTable.swift; sourceTree = ""; }; D0C674CB1CBB14A700183765 /* PeerReadState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerReadState.swift; sourceTree = ""; }; @@ -470,6 +481,7 @@ D0AD23281E196B6400A7089A /* PeerOperationLogMetadataTable.swift */, D0AAD1B11E3266B100D5B9DE /* TimestampBasedMessageAttributesTable.swift */, D0AAD1B01E3266B100D5B9DE /* TimestampBasedMessageAttributesIndexTable.swift */, + D08774FF1E3E3D9F00A97350 /* PreferencesTable.swift */, ); name = Tables; sourceTree = ""; @@ -490,7 +502,8 @@ D03120FB1DA55427006A2A60 /* PeerNotificationSettings.swift */, D021E0D31DB4FAE100C6B04F /* ItemCollection.swift */, D07CFF801DCA765D00761F81 /* PeerChatInterfaceState.swift */, - D00C7CD31E365C4E0080C3D5 /* PeerChatListPolicy.swift */, + D00C7CD31E365C4E0080C3D5 /* PeerChatListInclusion.swift */, + D08775021E3E3E7400A97350 /* PreferencesEntry.swift */, ); name = Objects; sourceTree = ""; @@ -538,6 +551,8 @@ D0F019FC1E1DA0CC00F05AB3 /* PeerMergedOperationLogView.swift */, D0AAD1B41E32673C00D5B9DE /* TimestampBasedMessageAttributesView.swift */, D00C7CE21E37861C0080C3D5 /* MessageView.swift */, + D08775051E3E3F2100A97350 /* PreferencesView.swift */, + D0BC386B1E3FCEE50044D6FE /* MultiplePeersView.swift */, ); name = Views; sourceTree = ""; @@ -811,6 +826,7 @@ D0B418271D7DFE0C004562A4 /* IpcPipe.swift in Sources */, D0B4184D1D7DFE20004562A4 /* ContactPeersView.swift in Sources */, D00C7CE41E37861C0080C3D5 /* MessageView.swift in Sources */, + D08775011E3E3D9F00A97350 /* PreferencesTable.swift in Sources */, D0F7B1CC1E045C6A007EB8A5 /* ChatListIndexTable.swift in Sources */, D0B4185E1D7DFE54004562A4 /* IpcNotifier.mm in Sources */, D0F7B1D31E045C6A007EB8A5 /* CachedPeerDataTable.swift in Sources */, @@ -818,6 +834,7 @@ D0F7B1D51E045C6A007EB8A5 /* PeerChatStateTable.swift in Sources */, D0F7B1C51E045C6A007EB8A5 /* MessageHistoryMetadataTable.swift in Sources */, D073CE781DCBF3B4007511FD /* MessageHistoryHole.swift in Sources */, + D08775071E3E3F2100A97350 /* PreferencesView.swift in Sources */, D0AAD1B61E32673C00D5B9DE /* TimestampBasedMessageAttributesView.swift in Sources */, D0F7B1C31E045C6A007EB8A5 /* Table.swift in Sources */, D0F7B1CF1E045C6A007EB8A5 /* MetadataTable.swift in Sources */, @@ -850,7 +867,7 @@ D073CE7D1DCBF3B4007511FD /* PeerPresence.swift in Sources */, D0B4184C1D7DFE20004562A4 /* ContactPeerIdsView.swift in Sources */, D073CE751DCBF3B4007511FD /* Message.swift in Sources */, - D00C7CD51E365C4E0080C3D5 /* PeerChatListPolicy.swift in Sources */, + D00C7CD51E365C4E0080C3D5 /* PeerChatListInclusion.swift in Sources */, D0F7B1CB1E045C6A007EB8A5 /* ChatListTable.swift in Sources */, D0F7B1C01E045C62007EB8A5 /* StringIndexTokens.swift in Sources */, D0F7B1CA1E045C6A007EB8A5 /* MessageHistoryUnsentTable.swift in Sources */, @@ -860,6 +877,7 @@ D0B8440A1DAB91B5005F29E1 /* RandomAccessMediaResourceContext.swift in Sources */, D0B418581D7DFE29004562A4 /* SeedConfiguration.swift in Sources */, D0B418311D7DFE16004562A4 /* ValueBox.swift in Sources */, + D08775041E3E3E7400A97350 /* PreferencesEntry.swift in Sources */, D0B4184E1D7DFE20004562A4 /* MessageHistoryHolesView.swift in Sources */, D0B844051DAB91B5005F29E1 /* MediaBox.swift in Sources */, D0B418321D7DFE16004562A4 /* SqliteValueBox.swift in Sources */, @@ -878,6 +896,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D08775061E3E3F2100A97350 /* PreferencesView.swift in Sources */, D0F9E8631C579F0200037222 /* MediaCleanupTable.swift in Sources */, D0F3CC741DDE1EB9008148FA /* ItemCacheMetaTable.swift in Sources */, D08D451F1D5D2CA700A7428A /* RatingTable.swift in Sources */, @@ -937,10 +956,11 @@ D0F019FD1E1DA0CC00F05AB3 /* PeerMergedOperationLogView.swift in Sources */, D0F9E8651C58CB7F00037222 /* ChatListTable.swift in Sources */, D0F9E8711C5A0E9B00037222 /* PeerTable.swift in Sources */, + D0BC386C1E3FCEE50044D6FE /* MultiplePeersView.swift in Sources */, D07CFF851DCA99C400761F81 /* InitialMessageHistoryData.swift in Sources */, D05F09A61C9E9F9300BB6F96 /* MediaResourceStatus.swift in Sources */, D033A6F71C73D512006A2EAB /* MessageHistoryUnsentTable.swift in Sources */, - D00C7CD41E365C4E0080C3D5 /* PeerChatListPolicy.swift in Sources */, + D00C7CD41E365C4E0080C3D5 /* PeerChatListInclusion.swift in Sources */, D0A18D671E16874D004C6734 /* UnreadMessageCountsView.swift in Sources */, D0D511021D64D73D00A97B8A /* IpcNotifier.mm in Sources */, D0B844511DAC04FE005F29E1 /* PeerPresence.swift in Sources */, @@ -951,7 +971,9 @@ D0F9E86D1C5A0E5D00037222 /* MetadataTable.swift in Sources */, D0E3A7841B28AE0900A402D9 /* Peer.swift in Sources */, D0D510F61D63BBE100A97B8A /* MessageHistoryOperation.swift in Sources */, + D08775031E3E3E7400A97350 /* PreferencesEntry.swift in Sources */, D0AB0B901D65D4AB002C78E7 /* UnsentMessageIndicesView.swift in Sources */, + D08775001E3E3D9F00A97350 /* PreferencesTable.swift in Sources */, D03120F81DA53FF4006A2A60 /* PeerPresenceTable.swift in Sources */, D0F9E8731C5A1EE500037222 /* GlobalMessageIdsTable.swift in Sources */, D00EED1E1C81F28D00341DFF /* MessageHistoryTagsTable.swift in Sources */, diff --git a/Postbox/ContactPeersView.swift b/Postbox/ContactPeersView.swift index 379cc89719..c7342a67e9 100644 --- a/Postbox/ContactPeersView.swift +++ b/Postbox/ContactPeersView.swift @@ -1,14 +1,12 @@ import Foundation final class MutableContactPeersView { - fileprivate let index: PeerNameIndex fileprivate var peers: [PeerId: Peer] fileprivate var peerPresences: [PeerId: PeerPresence] fileprivate var peerIds: Set fileprivate var accountPeer: Peer? - init(peers: [PeerId: Peer], peerPresences: [PeerId: PeerPresence], index: PeerNameIndex, accountPeer: Peer?) { - self.index = index + init(peers: [PeerId: Peer], peerPresences: [PeerId: PeerPresence], accountPeer: Peer?) { self.peers = peers self.peerIds = Set(peers.map { $0.0 }) self.peerPresences = peerPresences @@ -59,7 +57,6 @@ public final class ContactPeersView { public let accountPeer: Peer? init(_ mutableView: MutableContactPeersView) { - let index = mutableView.index if let accountPeer = mutableView.accountPeer { var peers: [Peer] = [] peers.reserveCapacity(mutableView.peers.count) diff --git a/Postbox/MultiplePeersView.swift b/Postbox/MultiplePeersView.swift new file mode 100644 index 0000000000..ca1317b06d --- /dev/null +++ b/Postbox/MultiplePeersView.swift @@ -0,0 +1,52 @@ +import Foundation + +final class MutableMultiplePeersView { + let peerIds: Set + + var peers: [PeerId: Peer] = [:] + var presences: [PeerId: PeerPresence] = [:] + + init(peerIds: [PeerId], getPeer: (PeerId) -> Peer?, getPeerPresence: (PeerId) -> PeerPresence?) { + self.peerIds = Set(peerIds) + + for peerId in self.peerIds { + if let peer = getPeer(peerId) { + self.peers[peerId] = peer + } + if let presence = getPeerPresence(peerId) { + self.presences[peerId] = presence + } + } + } + + func replay(updatedPeers: [PeerId: Peer], updatedPeerPresences: [PeerId: PeerPresence]) -> Bool { + if updatedPeers.isEmpty && updatedPeerPresences.isEmpty { + return false + } + + var updated = false + + for peerId in self.peerIds { + if let peer = updatedPeers[peerId] { + self.peers[peerId] = peer + updated = true + } + if let presence = updatedPeerPresences[peerId] { + self.presences[peerId] = presence + updated = true + } + } + + return updated + } +} + +public final class MultiplePeersView { + public let peers: [PeerId: Peer] + public let presences: [PeerId: PeerPresence] + + init(_ view: MutableMultiplePeersView) { + self.peers = view.peers + self.presences = view.presences + } +} diff --git a/Postbox/PeerChatListPolicy.swift b/Postbox/PeerChatListInclusion.swift similarity index 82% rename from Postbox/PeerChatListPolicy.swift rename to Postbox/PeerChatListInclusion.swift index be5b3645dc..d6c2a7273d 100644 --- a/Postbox/PeerChatListPolicy.swift +++ b/Postbox/PeerChatListInclusion.swift @@ -2,6 +2,6 @@ import Foundation public enum PeerChatListInclusion { case never - case ifNotEmpty + case ifHasMessages case always(minTimestamp: Int32) } diff --git a/Postbox/PeerNotificationSettingsTable.swift b/Postbox/PeerNotificationSettingsTable.swift index 6b79c40c4b..d13559e380 100644 --- a/Postbox/PeerNotificationSettingsTable.swift +++ b/Postbox/PeerNotificationSettingsTable.swift @@ -66,6 +66,28 @@ final class PeerNotificationSettingsTable: Table { return (added, removed) } + func resetAll(to settings: PeerNotificationSettings) -> [PeerId] { + let lowerBound = ValueBoxKey(length: 8) + lowerBound.setInt64(0, value: 0) + let upperBound = ValueBoxKey(length: 8) + upperBound.setInt64(0, value: Int64.max) + var peerIds: [PeerId] = [] + self.valueBox.range(self.table, start: lowerBound, end: upperBound, keys: { key in + peerIds.append(PeerId(key.getInt64(0))) + return true + }, limit: 0) + + var updatedPeerIds: [PeerId] = [] + for peerId in peerIds { + if let current = self.get(peerId), !current.isEqual(to: settings) { + updatedPeerIds.append(peerId) + self.set(id: peerId, settings: settings) + } + } + + return updatedPeerIds + } + override func beforeCommit() { if !self.updatedInitialSettings.isEmpty { for (peerId, _) in self.updatedInitialSettings { diff --git a/Postbox/Postbox.swift b/Postbox/Postbox.swift index ee95adb0af..2cfbc94e81 100644 --- a/Postbox/Postbox.swift +++ b/Postbox/Postbox.swift @@ -134,6 +134,10 @@ public final class Modifier { self.postbox?.updatePeerNotificationSettings(notificationSettings) } + public func resetAllPeerNotificationSettings(_ notificationSettings: PeerNotificationSettings) { + self.postbox?.resetAllPeerNotificationSettings(notificationSettings) + } + public func updatePeerCachedData(peerIds: Set, update: (PeerId, CachedPeerData?) -> CachedPeerData?) { self.postbox?.updatePeerCachedData(peerIds: peerIds, update: update) } @@ -241,6 +245,18 @@ public final class Modifier { public func removeTimestampBasedMessageAttribute(tag: UInt16, messageId: MessageId) { self.postbox?.removeTimestampBasedMessageAttribute(tag: tag, messageId: messageId) } + + public func getPreferencesEntry(key: ValueBoxKey) -> PreferencesEntry? { + return self.postbox?.getPreferencesEntry(key: key) + } + + public func setPreferencesEntry(key: ValueBoxKey, value: PreferencesEntry?) { + self.postbox?.setPreferencesEntry(key: key, value: value) + } + + public func updatePreferencesEntry(key: ValueBoxKey, _ f: (PreferencesEntry?) -> PreferencesEntry?) { + self.postbox?.setPreferencesEntry(key: key, value: f(self.postbox?.getPreferencesEntry(key: key))) + } } fileprivate class PipeNotifier: NSObject { @@ -295,6 +311,7 @@ public final class Postbox { private var currentUpdatedTotalUnreadCount: Int32? private var currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation] = [] private var currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation] = [] + private var currentPreferencesOperations: [PreferencesOperation] = [] private var currentReplaceChatListHoles: [(MessageIndex, ChatListHole?)] = [] private var currentReplacedContactPeerIds: Set? @@ -346,6 +363,7 @@ public final class Postbox { var peerOperationLogTable: PeerOperationLogTable! var timestampBasedMessageAttributesTable: TimestampBasedMessageAttributesTable! var timestampBasedMessageAttributesIndexTable: TimestampBasedMessageAttributesIndexTable! + var preferencesTable: PreferencesTable! //temporary var peerRatingTable: RatingTable! @@ -363,14 +381,14 @@ public final class Postbox { self.mediaBox = MediaBox(basePath: self.basePath + "/media") self.pipeNotifier = PipeNotifier(basePath: basePath, notify: { [weak self] in - if let strongSelf = self { + //if let strongSelf = self { /*strongSelf.queue.async { if strongSelf.valueBox != nil { let _ = strongSelf.modify({ _ -> Void in }).start() } }*/ - } + //} }) self.openDatabase() @@ -468,6 +486,7 @@ public final class Postbox { self.peerOperationLogMetadataTable = PeerOperationLogMetadataTable(valueBox: self.valueBox, table: PeerOperationLogMetadataTable.tableSpec(29)) self.peerMergedOperationLogIndexTable = PeerMergedOperationLogIndexTable(valueBox: self.valueBox, table: PeerMergedOperationLogIndexTable.tableSpec(30), metadataTable: self.peerOperationLogMetadataTable!) self.peerOperationLogTable = PeerOperationLogTable(valueBox: self.valueBox, table: PeerOperationLogTable.tableSpec(31), metadataTable: self.peerOperationLogMetadataTable, mergedIndexTable: self.peerMergedOperationLogIndexTable) + self.preferencesTable = PreferencesTable(valueBox: self.valueBox, table: PreferencesTable.tableSpec(35)) self.tables.append(self.keychainTable) self.tables.append(self.peerTable) @@ -500,6 +519,7 @@ public final class Postbox { self.tables.append(self.peerOperationLogMetadataTable) self.tables.append(self.peerMergedOperationLogIndexTable) self.tables.append(self.peerOperationLogTable) + self.tables.append(self.preferencesTable) self.transactionStateVersion = self.metadataTable.transactionStateVersion() @@ -521,6 +541,8 @@ public final class Postbox { return self.peerMergedOperationLogIndexTable.tailIndex(tag: tag) }, getTimestampBasedMessageAttributesHead: { tag in return self.timestampBasedMessageAttributesTable.head(tag: tag) + }, getPreferencesEntry: { key in + return self.preferencesTable.get(key: key) }, unsentMessageIds: self.messageHistoryUnsentTable!.get(), synchronizePeerReadStateOperations: self.synchronizeReadStateTable!.get(getCombinedPeerReadState: { peerId in return self.readStateTable.getCombinedState(peerId) })) @@ -942,7 +964,7 @@ public final class Postbox { return self.peerTable.get(peerId) }, 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), currentPeerMergedOperationLogOperations: self.currentPeerMergedOperationLogOperations, currentTimestampBasedMessageAttributesOperations: self.currentTimestampBasedMessageAttributesOperations, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, updatedMedia: self.currentUpdatedMedia, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentUpdatedMasterClientId: currentUpdatedMasterClientId) + 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), currentPeerMergedOperationLogOperations: self.currentPeerMergedOperationLogOperations, currentTimestampBasedMessageAttributesOperations: self.currentTimestampBasedMessageAttributesOperations, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, currentPreferencesOperations: self.currentPreferencesOperations, updatedMedia: self.currentUpdatedMedia, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentUpdatedMasterClientId: currentUpdatedMasterClientId) var updatedTransactionState: Int64? var updatedMasterClientId: Int64? if !transaction.isEmpty { @@ -973,6 +995,7 @@ public final class Postbox { self.currentUpdatedTotalUnreadCount = nil self.currentPeerMergedOperationLogOperations.removeAll() self.currentTimestampBasedMessageAttributesOperations.removeAll() + self.currentPreferencesOperations.removeAll() for table in self.tables { table.beforeCommit() @@ -1022,6 +1045,12 @@ public final class Postbox { } } + fileprivate func resetAllPeerNotificationSettings(_ notificationSettings: PeerNotificationSettings) { + for peerId in self.peerNotificationSettingsTable.resetAll(to: notificationSettings) { + self.currentUpdatedPeerNotificationSettings[peerId] = notificationSettings + } + } + fileprivate func updatePeerCachedData(peerIds: Set, update: (PeerId, CachedPeerData?) -> CachedPeerData?) { for peerId in peerIds { let currentData = self.cachedPeerDataTable.get(peerId) @@ -1166,7 +1195,7 @@ public final class Postbox { private func addLocationsToMessageHistoryViewEntries(tagMask: MessageTags, earlier: MutableMessageHistoryEntry?, later: MutableMessageHistoryEntry?, entries: [MutableMessageHistoryEntry]) -> ([MutableMessageHistoryEntry], MutableMessageHistoryEntry?, MutableMessageHistoryEntry?) { if let firstEntry = entries.first { if let location = self.messageHistoryTagsTable.entryLocation(at: firstEntry.index, tagMask: tagMask) { - var mappedEarlier = earlier?.updatedLocation(location.predecessor) + let mappedEarlier = earlier?.updatedLocation(location.predecessor) var mappedEntries: [MutableMessageHistoryEntry] = [] var previousLocation: MessageHistoryEntryLocation? for i in 0 ..< entries.count { @@ -1179,7 +1208,7 @@ public final class Postbox { } } previousLocation = previousLocation?.successor - var mappedLater = later?.updatedLocation(previousLocation) + let mappedLater = later?.updatedLocation(previousLocation) return (mappedEntries, mappedEarlier, mappedLater) } else { return (entries, earlier, later) @@ -1333,7 +1362,7 @@ public final class Postbox { } |> switchToLatest } - public func contactPeersView(index: PeerNameIndex, accountPeerId: PeerId) -> Signal { + public func contactPeersView(accountPeerId: PeerId) -> Signal { return self.modify { modifier -> Signal in var peers: [PeerId: Peer] = [:] var peerPresences: [PeerId: PeerPresence] = [:] @@ -1347,7 +1376,7 @@ public final class Postbox { } } - let view = MutableContactPeersView(peers: peers, peerPresences: peerPresences, index: index, accountPeer: self.peerTable.get(accountPeerId)) + let view = MutableContactPeersView(peers: peers, peerPresences: peerPresences, accountPeer: self.peerTable.get(accountPeerId)) let (index, signal) = self.viewTracker.addContactPeersView(view) return (.single(ContactPeersView(view)) @@ -1424,6 +1453,23 @@ public final class Postbox { } |> switchToLatest } + public func multiplePeersView(_ ids: [PeerId]) -> Signal { + return self.modify { modifier -> Signal in + let view = MutableMultiplePeersView(peerIds: ids, getPeer: { self.peerTable.get($0) }, getPeerPresence: { self.peerPresenceTable.get($0) }) + let (index, signal) = self.viewTracker.addMultiplePeersView(view) + + return (.single(MultiplePeersView(view)) + |> then(signal)) + |> afterDisposed { [weak self] in + if let strongSelf = self { + strongSelf.queue.async { + strongSelf.viewTracker.removeMultiplePeersView(index) + } + } + } + } |> switchToLatest + } + public func loadedPeerWithId(_ id: PeerId) -> Signal { return self.modify { modifier -> Signal in if let peer = self.peerTable.get(id) { @@ -1464,7 +1510,7 @@ public final class Postbox { } public func updateMessageHistoryViewVisibleRange(_ id: MessageHistoryViewId, earliestVisibleIndex: MessageIndex, latestVisibleIndex: MessageIndex) { - self.modify({ modifier -> Void in + let _ = self.modify({ modifier -> Void in self.viewTracker.updateMessageHistoryViewVisibleRange(id, earliestVisibleIndex: earliestVisibleIndex, latestVisibleIndex: latestVisibleIndex) }).start() } @@ -1640,6 +1686,33 @@ public final class Postbox { } |> switchToLatest } + public func preferencesView(keys: [ValueBoxKey]) -> Signal { + return self.modify { modifier -> Signal in + let view = MutablePreferencesView(keys: Set(keys), get: { key in + return self.preferencesTable.get(key: key) + }) + let (index, signal) = self.viewTracker.addPreferencesView(view) + + return (.single(PreferencesView(view)) + |> then(signal)) + |> afterDisposed { [weak self] in + if let strongSelf = self { + strongSelf.queue.async { + strongSelf.viewTracker.removePreferencesView(index) + } + } + } + } |> switchToLatest + } + + fileprivate func getPreferencesEntry(key: ValueBoxKey) -> PreferencesEntry? { + return self.preferencesTable.get(key: key) + } + + fileprivate func setPreferencesEntry(key: ValueBoxKey, value: PreferencesEntry?) { + self.preferencesTable.set(key: key, value: value, operations: &self.currentPreferencesOperations) + } + public func isMasterClient() -> Signal { return self.modify { modifier -> Signal in let sessionClientId = self.sessionClientId @@ -1650,7 +1723,7 @@ public final class Postbox { } public func becomeMasterClient() { - self.modify({ modifier in + let _ = self.modify({ modifier in if self.metadataTable.masterClientId() != self.sessionClientId { self.currentUpdatedMasterClientId = self.sessionClientId } diff --git a/Postbox/PostboxTransaction.swift b/Postbox/PostboxTransaction.swift index a6c85c40c1..b7fbdfd0af 100644 --- a/Postbox/PostboxTransaction.swift +++ b/Postbox/PostboxTransaction.swift @@ -14,6 +14,7 @@ final class PostboxTransaction { let peerIdsWithUpdatedUnreadCounts: Set let currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation] let currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation] + let currentPreferencesOperations: [PreferencesOperation] let unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation] let updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?] @@ -76,10 +77,13 @@ final class PostboxTransaction { if !currentTimestampBasedMessageAttributesOperations.isEmpty { return false } + if !currentPreferencesOperations.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?], currentUpdatedTotalUnreadCount: Int32?, peerIdsWithUpdatedUnreadCounts: Set, currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], 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, currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentPreferencesOperations: [PreferencesOperation], updatedMedia: [MediaId: Media?], replaceContactPeerIds: Set?, currentUpdatedMasterClientId: Int64?) { self.currentOperationsByPeerId = currentOperationsByPeerId self.peerIdsWithFilledHoles = peerIdsWithFilledHoles self.removedHolesByPeerId = removedHolesByPeerId @@ -95,6 +99,7 @@ final class PostboxTransaction { self.currentTimestampBasedMessageAttributesOperations = currentTimestampBasedMessageAttributesOperations self.unsentMessageOperations = unsentMessageOperations self.updatedSynchronizePeerReadStateOperations = updatedSynchronizePeerReadStateOperations + self.currentPreferencesOperations = currentPreferencesOperations self.updatedMedia = updatedMedia self.replaceContactPeerIds = replaceContactPeerIds self.currentUpdatedMasterClientId = currentUpdatedMasterClientId diff --git a/Postbox/PreferencesEntry.swift b/Postbox/PreferencesEntry.swift new file mode 100644 index 0000000000..c795d92e5d --- /dev/null +++ b/Postbox/PreferencesEntry.swift @@ -0,0 +1,5 @@ +import Foundation + +public protocol PreferencesEntry: Coding { + func isEqual(to: PreferencesEntry) -> Bool +} diff --git a/Postbox/PreferencesTable.swift b/Postbox/PreferencesTable.swift new file mode 100644 index 0000000000..cbc5f6c103 --- /dev/null +++ b/Postbox/PreferencesTable.swift @@ -0,0 +1,58 @@ +import Foundation + +enum PreferencesOperation { + case update(ValueBoxKey, PreferencesEntry?) +} + +private struct CachedEntry { + let entry: PreferencesEntry? +} + +final class PreferencesTable: Table { + private var cachedEntries: [ValueBoxKey: CachedEntry] = [:] + private var updatedEntryKeys = Set() + + static func tableSpec(_ id: Int32) -> ValueBoxTable { + return ValueBoxTable(id: id, keyType: .binary) + } + + func get(key: ValueBoxKey) -> PreferencesEntry? { + if let cached = self.cachedEntries[key] { + return cached.entry + } else { + if let value = self.valueBox.get(self.table, key: key), let object = Decoder(buffer: value).decodeRootObject() as? PreferencesEntry { + self.cachedEntries[key] = CachedEntry(entry: object) + return object + } else { + self.cachedEntries[key] = CachedEntry(entry: nil) + return nil + } + } + } + + func set(key: ValueBoxKey, value: PreferencesEntry?, operations: inout [PreferencesOperation]) { + self.cachedEntries[key] = CachedEntry(entry: value) + updatedEntryKeys.insert(key) + operations.append(.update(key, value)) + } + + override func clearMemoryCache() { + assert(self.updatedEntryKeys.isEmpty) + } + + override func beforeCommit() { + if !self.updatedEntryKeys.isEmpty { + for key in self.updatedEntryKeys { + if let value = self.cachedEntries[key]?.entry { + let encoder = Encoder() + encoder.encodeRootObject(value) + self.valueBox.set(self.table, key: key, value: encoder.readBufferNoCopy()) + } else { + self.valueBox.remove(self.table, key: key) + } + } + + self.updatedEntryKeys.removeAll() + } + } +} diff --git a/Postbox/PreferencesView.swift b/Postbox/PreferencesView.swift new file mode 100644 index 0000000000..bdc6195672 --- /dev/null +++ b/Postbox/PreferencesView.swift @@ -0,0 +1,55 @@ +import Foundation + +final class MutablePreferencesView { + fileprivate let keys: Set + fileprivate var values: [ValueBoxKey: PreferencesEntry] + + init(keys: Set, get: (ValueBoxKey) -> PreferencesEntry?) { + self.keys = keys + var values: [ValueBoxKey: PreferencesEntry] = [:] + for key in keys { + if let value = get(key) { + values[key] = value + } + } + self.values = values + } + + func replay(operations: [PreferencesOperation], get: (ValueBoxKey) -> PreferencesEntry?) -> Bool { + var updated = false + for operation in operations { + switch operation { + case let .update(key, value): + if self.keys.contains(key) { + let currentValue = self.values[key] + var updatedValue = false + if let value = value, let currentValue = currentValue { + if !value.isEqual(to: currentValue) { + updatedValue = true + } + } else if (value != nil) != (currentValue != nil) { + updatedValue = true + } + if updatedValue { + if let value = value { + self.values[key] = value + } else { + self.values.removeValue(forKey: key) + } + updated = true + } + } + } + } + + return updated + } +} + +public final class PreferencesView { + public let values: [ValueBoxKey: PreferencesEntry] + + init(_ view: MutablePreferencesView) { + self.values = view.values + } +} diff --git a/Postbox/SqliteValueBox.swift b/Postbox/SqliteValueBox.swift index f1a99b436d..3d219bdb6a 100644 --- a/Postbox/SqliteValueBox.swift +++ b/Postbox/SqliteValueBox.swift @@ -69,6 +69,14 @@ private struct SqlitePreparedStatement { return key } + func int64KeyAt(_ index: Int) -> ValueBoxKey { + let value = sqlite3_column_int64(statement, Int32(index)) + + let key = ValueBoxKey(length: 9) + key.setInt64(0, value: value) + return key + } + func destroy() { sqlite3_finalize(statement) } @@ -158,15 +166,14 @@ final class SqliteValueBox: ValueBox { } lock.unlock() - checkpoints.set((Signal.single(Void()) |> delay(10.0, queue: Queue.concurrentDefaultQueue()) |> restart).start(next: { [weak self] _ in - if let strongSelf = self , strongSelf.database != nil { - strongSelf.queue.async { - strongSelf.lock.lock() - var nLog: Int32 = 0 - var nFrames: Int32 = 0 - sqlite3_wal_checkpoint_v2(strongSelf.database.handle, nil, SQLITE_CHECKPOINT_PASSIVE, &nLog, &nFrames) - strongSelf.lock.unlock() - } + checkpoints.set((Signal.single(Void()) |> delay(10.0, queue: self.queue) |> restart).start(next: { [weak self] _ in + if let strongSelf = self, strongSelf.database != nil { + assert(strongSelf.queue.isCurrent()) + strongSelf.lock.lock() + var nLog: Int32 = 0 + var nFrames: Int32 = 0 + sqlite3_wal_checkpoint_v2(strongSelf.database.handle, nil, SQLITE_CHECKPOINT_PASSIVE, &nLog, &nFrames) + strongSelf.lock.unlock() //print("(SQLite WAL size \(nLog) removed \(nFrames))") } })) @@ -706,40 +713,78 @@ final class SqliteValueBox: ValueBox { var startTime = CFAbsoluteTimeGetCurrent() - if start < end { - if limit <= 0 { - statement = self.rangeValueAscStatementNoLimit(table, start: start, end: end) - } else { - statement = self.rangeValueAscStatementLimit(table, start: start, end: end, limit: limit) - } - } else { - if limit <= 0 { - statement = self.rangeValueDescStatementNoLimit(table, start: end, end: start) - } else { - statement = self.rangeValueDescStatementLimit(table, start: end, end: start, limit: limit) - } + switch table.keyType { + case .binary: + if start < end { + if limit <= 0 { + statement = self.rangeValueAscStatementNoLimit(table, start: start, end: end) + } else { + statement = self.rangeValueAscStatementLimit(table, start: start, end: end, limit: limit) + } + } else { + if limit <= 0 { + statement = self.rangeValueDescStatementNoLimit(table, start: end, end: start) + } else { + statement = self.rangeValueDescStatementLimit(table, start: end, end: start, limit: limit) + } + } + + var currentTime = CFAbsoluteTimeGetCurrent() + self.readQueryTime += currentTime - startTime + + startTime = currentTime + + while statement.step() { + startTime = CFAbsoluteTimeGetCurrent() + + let key = statement.keyAt(0) + let value = statement.valueAt(1) + + currentTime = CFAbsoluteTimeGetCurrent() + self.readQueryTime += currentTime - startTime + + if !values(key, value) { + break + } + } + + statement.reset() + case .int64: + if start.reversed < end.reversed { + if limit <= 0 { + statement = self.rangeValueAscStatementNoLimit(table, start: start, end: end) + } else { + statement = self.rangeValueAscStatementLimit(table, start: start, end: end, limit: limit) + } + } else { + if limit <= 0 { + statement = self.rangeValueDescStatementNoLimit(table, start: end, end: start) + } else { + statement = self.rangeValueDescStatementLimit(table, start: end, end: start, limit: limit) + } + } + + var currentTime = CFAbsoluteTimeGetCurrent() + self.readQueryTime += currentTime - startTime + + startTime = currentTime + + while statement.step() { + startTime = CFAbsoluteTimeGetCurrent() + + let key = statement.int64KeyAt(0) + let value = statement.valueAt(1) + + currentTime = CFAbsoluteTimeGetCurrent() + self.readQueryTime += currentTime - startTime + + if !values(key, value) { + break + } + } + + statement.reset() } - - var currentTime = CFAbsoluteTimeGetCurrent() - self.readQueryTime += currentTime - startTime - - startTime = currentTime - - while statement.step() { - startTime = CFAbsoluteTimeGetCurrent() - - let key = statement.keyAt(0) - let value = statement.valueAt(1) - - currentTime = CFAbsoluteTimeGetCurrent() - self.readQueryTime += currentTime - startTime - - if !values(key, value) { - break - } - } - - statement.reset() } } @@ -750,39 +795,76 @@ final class SqliteValueBox: ValueBox { var startTime = CFAbsoluteTimeGetCurrent() - if start < end { - if limit <= 0 { - statement = self.rangeKeyAscStatementNoLimit(table, start: start, end: end) - } else { - statement = self.rangeKeyAscStatementLimit(table, start: start, end: end, limit: limit) - } - } else { - if limit <= 0 { - statement = self.rangeKeyDescStatementNoLimit(table, start: end, end: start) - } else { - statement = self.rangeKeyDescStatementLimit(table, start: end, end: start, limit: limit) - } + switch table.keyType { + case .binary: + if start < end { + if limit <= 0 { + statement = self.rangeKeyAscStatementNoLimit(table, start: start, end: end) + } else { + statement = self.rangeKeyAscStatementLimit(table, start: start, end: end, limit: limit) + } + } else { + if limit <= 0 { + statement = self.rangeKeyDescStatementNoLimit(table, start: end, end: start) + } else { + statement = self.rangeKeyDescStatementLimit(table, start: end, end: start, limit: limit) + } + } + + var currentTime = CFAbsoluteTimeGetCurrent() + self.readQueryTime += currentTime - startTime + + startTime = currentTime + + while statement.step() { + startTime = CFAbsoluteTimeGetCurrent() + + let key = statement.keyAt(0) + + currentTime = CFAbsoluteTimeGetCurrent() + self.readQueryTime += currentTime - startTime + + if !keys(key) { + break + } + } + + statement.reset() + case .int64: + if start.reversed < end.reversed { + if limit <= 0 { + statement = self.rangeKeyAscStatementNoLimit(table, start: start, end: end) + } else { + statement = self.rangeKeyAscStatementLimit(table, start: start, end: end, limit: limit) + } + } else { + if limit <= 0 { + statement = self.rangeKeyDescStatementNoLimit(table, start: end, end: start) + } else { + statement = self.rangeKeyDescStatementLimit(table, start: end, end: start, limit: limit) + } + } + + var currentTime = CFAbsoluteTimeGetCurrent() + self.readQueryTime += currentTime - startTime + + startTime = currentTime + + while statement.step() { + startTime = CFAbsoluteTimeGetCurrent() + + let key = statement.int64KeyAt(0) + + currentTime = CFAbsoluteTimeGetCurrent() + self.readQueryTime += currentTime - startTime + + if !keys(key) { + break + } + } + + statement.reset() } - - var currentTime = CFAbsoluteTimeGetCurrent() - self.readQueryTime += currentTime - startTime - - startTime = currentTime - - while statement.step() { - startTime = CFAbsoluteTimeGetCurrent() - - let key = statement.keyAt(0) - - currentTime = CFAbsoluteTimeGetCurrent() - self.readQueryTime += currentTime - startTime - - if !keys(key) { - break - } - } - - statement.reset() } } diff --git a/Postbox/ValueBox.swift b/Postbox/ValueBox.swift index 1de04fe06f..a9f8561c92 100644 --- a/Postbox/ValueBox.swift +++ b/Postbox/ValueBox.swift @@ -17,8 +17,8 @@ protocol ValueBox { func beginStats() func endStats() - func range(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey, values: @noescape(ValueBoxKey, ReadBuffer) -> Bool, limit: Int) - func range(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey, keys: @noescape(ValueBoxKey) -> Bool, limit: Int) + func range(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey, values: (ValueBoxKey, ReadBuffer) -> Bool, limit: Int) + func range(_ table: ValueBoxTable, start: ValueBoxKey, end: ValueBoxKey, keys: (ValueBoxKey) -> Bool, limit: Int) func get(_ table: ValueBoxTable, key: ValueBoxKey) -> ReadBuffer? func exists(_ table: ValueBoxTable, key: ValueBoxKey) -> Bool func set(_ table: ValueBoxTable, key: ValueBoxKey, value: MemoryBuffer) diff --git a/Postbox/ValueBoxKey.swift b/Postbox/ValueBoxKey.swift index ebb9eb280a..aabc158b50 100644 --- a/Postbox/ValueBoxKey.swift +++ b/Postbox/ValueBoxKey.swift @@ -121,6 +121,18 @@ public struct ValueBoxKey: Hashable, CustomStringConvertible, Comparable { } } + public var reversed: ValueBoxKey { + let key = ValueBoxKey(length: self.length) + let keyMemory = key.memory.assumingMemoryBound(to: UInt8.self) + let selfMemory = self.memory.assumingMemoryBound(to: UInt8.self) + var i = self.length - 1 + while i >= 0 { + keyMemory[i] = selfMemory[self.length - 1 - i] + i -= 1 + } + return key + } + public var successor: ValueBoxKey { let key = ValueBoxKey(length: self.length) memcpy(key.memory, self.memory, self.length) diff --git a/Postbox/ViewTracker.swift b/Postbox/ViewTracker.swift index 1009775888..583e1ebcaf 100644 --- a/Postbox/ViewTracker.swift +++ b/Postbox/ViewTracker.swift @@ -28,6 +28,7 @@ final class ViewTracker { private let getPeerReadState: (PeerId) -> CombinedPeerReadState? private let operationLogGetOperations: (PeerOperationLogTag, Int32, Int) -> [PeerMergedOperationLogEntry] private let operationLogGetTailIndex: (PeerOperationLogTag) -> Int32? + private let getPreferencesEntry: (ValueBoxKey) -> PreferencesEntry? private var chatListViews = Bag<(MutableChatListView, ValuePipe<(ChatListView, ViewUpdateType)>)>() private var messageHistoryViews: [PeerId: Bag<(MutableMessageHistoryView, ValuePipe<(MessageHistoryView, ViewUpdateType)>)>] = [:] @@ -55,8 +56,10 @@ final class ViewTracker { private var timestampBasedMessageAttributesViews = Bag<(MutableTimestampBasedMessageAttributesView, ValuePipe)>() private var messageViews = Bag<(MutableMessageView, ValuePipe)>() + private var preferencesViews = Bag<(MutablePreferencesView, ValuePipe)>() + private var multiplePeersViews = Bag<(MutableMultiplePeersView, ValuePipe)>() - init(queue: Queue, fetchEarlierHistoryEntries: @escaping (PeerId, MessageIndex?, Int, MessageTags?) -> [MutableMessageHistoryEntry], fetchLaterHistoryEntries: @escaping (PeerId, MessageIndex?, Int, MessageTags?) -> [MutableMessageHistoryEntry], fetchEarlierChatEntries: @escaping (ChatListIndex?, Int) -> [MutableChatListEntry], fetchLaterChatEntries: @escaping (ChatListIndex?, 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?, operationLogGetOperations: @escaping (PeerOperationLogTag, Int32, Int) -> [PeerMergedOperationLogEntry], operationLogGetTailIndex: @escaping (PeerOperationLogTag) -> Int32?, getTimestampBasedMessageAttributesHead: @escaping (UInt16) -> TimestampBasedMessageAttributesEntry?, unsentMessageIds: [MessageId], synchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation]) { + init(queue: Queue, fetchEarlierHistoryEntries: @escaping (PeerId, MessageIndex?, Int, MessageTags?) -> [MutableMessageHistoryEntry], fetchLaterHistoryEntries: @escaping (PeerId, MessageIndex?, Int, MessageTags?) -> [MutableMessageHistoryEntry], fetchEarlierChatEntries: @escaping (ChatListIndex?, Int) -> [MutableChatListEntry], fetchLaterChatEntries: @escaping (ChatListIndex?, 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?, operationLogGetOperations: @escaping (PeerOperationLogTag, Int32, Int) -> [PeerMergedOperationLogEntry], operationLogGetTailIndex: @escaping (PeerOperationLogTag) -> Int32?, getTimestampBasedMessageAttributesHead: @escaping (UInt16) -> TimestampBasedMessageAttributesEntry?, getPreferencesEntry: @escaping (ValueBoxKey) -> PreferencesEntry?, unsentMessageIds: [MessageId], synchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation]) { self.queue = queue self.fetchEarlierHistoryEntries = fetchEarlierHistoryEntries self.fetchLaterHistoryEntries = fetchLaterHistoryEntries @@ -73,6 +76,7 @@ final class ViewTracker { self.operationLogGetOperations = operationLogGetOperations self.operationLogGetTailIndex = operationLogGetTailIndex self.getTimestampBasedMessageAttributesHead = getTimestampBasedMessageAttributesHead + self.getPreferencesEntry = getPreferencesEntry self.unsentMessageView = UnsentMessageHistoryView(ids: unsentMessageIds) self.synchronizeReadStatesView = MutableSynchronizePeerReadStatesView(operations: synchronizePeerReadStateOperations) @@ -226,6 +230,28 @@ final class ViewTracker { self.messageViews.remove(index) } + func addPreferencesView(_ view: MutablePreferencesView) -> (Bag<(MutablePreferencesView, ValuePipe)>.Index, Signal) { + let record = (view, ValuePipe()) + let index = self.preferencesViews.add(record) + + return (index, record.1.signal()) + } + + func removePreferencesView(_ index: Bag<(MutablePreferencesView, ValuePipe)>.Index) { + self.preferencesViews.remove(index) + } + + func addMultiplePeersView(_ view: MutableMultiplePeersView) -> (Bag<(MutableMultiplePeersView, ValuePipe)>.Index, Signal) { + let record = (view, ValuePipe()) + let index = self.multiplePeersViews.add(record) + + return (index, record.1.signal()) + } + + func removeMultiplePeersView(_ index: Bag<(MutableMultiplePeersView, ValuePipe)>.Index) { + self.multiplePeersViews.remove(index) + } + func refreshViewsDueToExternalTransaction(fetchAroundChatEntries: (_ index: ChatListIndex, _ 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] = [] @@ -263,28 +289,29 @@ final class ViewTracker { for (mutableView, pipe) in self.peerViews.copyItems() { var updatedPeers: [PeerId: Peer] = [:] - if let peer = self.getPeer(mutableView.peerId) { - updatedPeers[mutableView.peerId] = peer - } var updatedPeerPresences: [PeerId: PeerPresence] = [:] - if let presence = self.getPeerPresence(mutableView.peerId) { - updatedPeerPresences[mutableView.peerId] = presence - } - var updatedNotificationSettings: [PeerId: PeerNotificationSettings] = [:] - if let notificationSettings = self.getPeerNotificationSettings(mutableView.peerId) { - updatedNotificationSettings[mutableView.peerId] = notificationSettings - } - var updatedCachedPeerData: [PeerId: CachedPeerData] = [:] - if let cachedPeerData = self.getCachedPeerData(mutableView.peerId) { - updatedCachedPeerData[mutableView.peerId] = cachedPeerData - for peerId in cachedPeerData.peerIds { - if let peer = self.getPeer(peerId) { - updatedPeers[peerId] = peer + + let peerId = mutableView.peerId + + if let peer = self.getPeer(peerId) { + updatedPeers[peerId] = peer + } + if let presence = self.getPeerPresence(peerId) { + updatedPeerPresences[peerId] = presence + } + if let notificationSettings = self.getPeerNotificationSettings(peerId) { + updatedNotificationSettings[peerId] = notificationSettings + } + if let cachedPeerData = self.getCachedPeerData(peerId) { + updatedCachedPeerData[peerId] = cachedPeerData + for cachedPeerId in cachedPeerData.peerIds { + if let peer = self.getPeer(cachedPeerId) { + updatedPeers[cachedPeerId] = peer } - if let presence = self.getPeerPresence(peerId) { - updatedPeerPresences[peerId] = presence + if let presence = self.getPeerPresence(cachedPeerId) { + updatedPeerPresences[cachedPeerId] = presence } } } @@ -360,10 +387,10 @@ final class ViewTracker { if mutableView.replay(transaction.chatListOperations, updatedPeerNotificationSettings: transaction.currentUpdatedPeerNotificationSettings, context: context) { mutableView.complete(context: context, fetchEarlier: self.fetchEarlierChatEntries, fetchLater: self.fetchLaterChatEntries) mutableView.render(self.renderMessage, getPeerNotificationSettings: self.getPeerNotificationSettings) - var updateType: ViewUpdateType = .Generic + //var updateType: ViewUpdateType = .Generic for operation in transaction.chatListOperations { if case .RemoveHoles = operation { - updateType = .UpdateVisible + //updateType = .UpdateVisible break } } @@ -423,6 +450,18 @@ final class ViewTracker { pipe.putNext(TimestampBasedMessageAttributesView(mutableView)) } } + + for (mutableView, pipe) in self.preferencesViews.copyItems() { + if mutableView.replay(operations: transaction.currentPreferencesOperations, get: self.getPreferencesEntry) { + pipe.putNext(PreferencesView(mutableView)) + } + } + + for (mutableView, pipe) in self.multiplePeersViews.copyItems() { + if mutableView.replay(updatedPeers: transaction.currentUpdatedPeers, updatedPeerPresences: transaction.currentUpdatedPeerPresences) { + pipe.putNext(MultiplePeersView(mutableView)) + } + } } private func updateTrackedChatListHoles() {