diff --git a/Postbox.xcodeproj/project.pbxproj b/Postbox.xcodeproj/project.pbxproj index bdf7c30fc9..edbcdb9b82 100644 --- a/Postbox.xcodeproj/project.pbxproj +++ b/Postbox.xcodeproj/project.pbxproj @@ -55,6 +55,8 @@ D050F2681E4A5B5A00988324 /* TimestampBasedMessageAttributesIndexTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0AAD1B01E3266B100D5B9DE /* TimestampBasedMessageAttributesIndexTable.swift */; }; D050F2691E4A5B5A00988324 /* MultiplePeersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BC386B1E3FCEE50044D6FE /* MultiplePeersView.swift */; }; D055BD331B7D3D2D00F06C0A /* MediaBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = D055BD321B7D3D2D00F06C0A /* MediaBox.swift */; }; + D0575AE31E9ECBB2006F2541 /* AccessChallengeDataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0575AE21E9ECBB2006F2541 /* AccessChallengeDataView.swift */; }; + D0575AE41E9ECBB2006F2541 /* AccessChallengeDataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0575AE21E9ECBB2006F2541 /* AccessChallengeDataView.swift */; }; D05F09A61C9E9F9300BB6F96 /* MediaResourceStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05F09A51C9E9F9300BB6F96 /* MediaResourceStatus.swift */; }; D060B77B1CF4845A0050BE9B /* ReadStateTableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D060B77A1CF4845A0050BE9B /* ReadStateTableTests.swift */; }; D073CE741DCBF3B4007511FD /* Peer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E3A7831B28AE0900A402D9 /* Peer.swift */; }; @@ -312,6 +314,7 @@ D044E1621B2AD677001EE087 /* MurMurHash32.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MurMurHash32.m; sourceTree = ""; }; D049EAEF1E44D9B900A2CD3A /* PostboxStateView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostboxStateView.swift; sourceTree = ""; }; D055BD321B7D3D2D00F06C0A /* MediaBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaBox.swift; sourceTree = ""; }; + D0575AE21E9ECBB2006F2541 /* AccessChallengeDataView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccessChallengeDataView.swift; sourceTree = ""; }; D05F09A51C9E9F9300BB6F96 /* MediaResourceStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MediaResourceStatus.swift; sourceTree = ""; }; D060B77A1CF4845A0050BE9B /* ReadStateTableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadStateTableTests.swift; sourceTree = ""; }; D07516401B2D9CEF00AE42E0 /* sqlite3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sqlite3.c; sourceTree = ""; }; @@ -654,6 +657,7 @@ D0FA0AC91E780A26005BB9B7 /* PostboxView.swift */, D0FA0ACC1E781067005BB9B7 /* Views.swift */, D0F53BF21E794C6700117362 /* PeerChatStateView.swift */, + D0575AE21E9ECBB2006F2541 /* AccessChallengeDataView.swift */, ); name = Views; sourceTree = ""; @@ -910,6 +914,7 @@ C2A315BE1E2E733900D89000 /* PeerMergedOperationLogIndexTable.swift in Sources */, C2A315BF1E2E733900D89000 /* PeerMergedOperationLogView.swift in Sources */, C2A315BD1E2E732000D89000 /* PeerOperationLogMetadataTable.swift in Sources */, + D0575AE41E9ECBB2006F2541 /* AccessChallengeDataView.swift in Sources */, C2A315BC1E2E730400D89000 /* PeerOperationLogTable.swift in Sources */, C2AC9C131E1E5D200085C7DE /* UnreadMessageCountsView.swift in Sources */, D0B418481D7DFE20004562A4 /* MessageHistoryView.swift in Sources */, @@ -1033,6 +1038,7 @@ D07CFF831DCA909100761F81 /* PeerChatInterfaceStateTable.swift in Sources */, D0F9E86F1C5A0E7600037222 /* KeychainTable.swift in Sources */, D0E3A7821B28ADD000A402D9 /* Postbox.swift in Sources */, + D0575AE31E9ECBB2006F2541 /* AccessChallengeDataView.swift in Sources */, D0E3A79E1B28B50400A402D9 /* Message.swift in Sources */, D0C674C81CBB11C600183765 /* MessageHistoryReadStateTable.swift in Sources */, D0DF0C8F1D81A350008AEB01 /* PeerView.swift in Sources */, diff --git a/Postbox/AccessChallengeDataView.swift b/Postbox/AccessChallengeDataView.swift new file mode 100644 index 0000000000..74913071ee --- /dev/null +++ b/Postbox/AccessChallengeDataView.swift @@ -0,0 +1,34 @@ +import Foundation + +final class MutableAccessChallengeDataView: MutablePostboxView { + var data: PostboxAccessChallengeData + + init(postbox: Postbox) { + self.data = postbox.metadataTable.accessChallengeData() + } + + func replay(postbox: Postbox, transaction: PostboxTransaction) -> Bool { + var updated = false + + if let data = transaction.updatedAccessChallengeData { + if self.data != data { + self.data = data + updated = true + } + } + + return updated + } + + func immutableView() -> PostboxView { + return AccessChallengeDataView(self) + } +} + +public final class AccessChallengeDataView: PostboxView { + public let data: PostboxAccessChallengeData + + init(_ view: MutableAccessChallengeDataView) { + self.data = view.data + } +} diff --git a/Postbox/AccountManager.swift b/Postbox/AccountManager.swift index 7ab16482e0..43f62d3439 100644 --- a/Postbox/AccountManager.swift +++ b/Postbox/AccountManager.swift @@ -31,7 +31,7 @@ public final class AccountManager { fileprivate init(queue: Queue, basePath: String) { self.queue = queue self.basePath = basePath - let _ = try? FileManager.default.createDirectory(atPath: basePath, withIntermediateDirectories: false, attributes: nil) + let _ = try? FileManager.default.createDirectory(atPath: basePath, withIntermediateDirectories: true, attributes: nil) self.valueBox = SqliteValueBox(basePath: basePath + "/db", queue: queue) self.metadataTable = AccountManagerMetadataTable(valueBox: self.valueBox, table: AccountManagerMetadataTable.tableSpec(0)) diff --git a/Postbox/MediaBox.swift b/Postbox/MediaBox.swift index 6616cdf65b..ef9ac03ffc 100644 --- a/Postbox/MediaBox.swift +++ b/Postbox/MediaBox.swift @@ -528,7 +528,7 @@ public final class MediaBox { } } - public func fetchedResource(_ resource: MediaResource, implNext:Bool = false) -> Signal { + public func fetchedResource(_ resource: MediaResource, implNext: Bool = false) -> Signal { return Signal { subscriber in let disposable = MetaDisposable() diff --git a/Postbox/MetadataTable.swift b/Postbox/MetadataTable.swift index 287ec88db6..50ec4da92c 100644 --- a/Postbox/MetadataTable.swift +++ b/Postbox/MetadataTable.swift @@ -8,19 +8,43 @@ private enum MetadataKey: Int32 { case AccessChallenge = 5 } +public struct AccessChallengeAttempts: Coding, Equatable { + public let count: Int32 + public let timestamp: Int32 + + public init(count: Int32, timestamp: Int32) { + self.count = count + self.timestamp = timestamp + } + + public init(decoder: Decoder) { + self.count = decoder.decodeInt32ForKey("c") + self.timestamp = decoder.decodeInt32ForKey("t") + } + + public func encode(_ encoder: Encoder) { + encoder.encodeInt32(self.count, forKey: "c") + encoder.encodeInt32(self.timestamp, forKey: "t") + } + + public static func ==(lhs: AccessChallengeAttempts, rhs: AccessChallengeAttempts) -> Bool { + return lhs.count == rhs.count && lhs.timestamp == rhs.timestamp + } +} + public enum PostboxAccessChallengeData: Coding, Equatable { case none - case numericalPassword(value: String, timeout: Int32?) - case plaintextPassword(value: String, timeout: Int32?) + case numericalPassword(value: String, timeout: Int32?, attempts: AccessChallengeAttempts?) + case plaintextPassword(value: String, timeout: Int32?, attempts: AccessChallengeAttempts?) public init(decoder: Decoder) { switch decoder.decodeInt32ForKey("r") as Int32 { case 0: self = .none case 1: - self = .numericalPassword(value: decoder.decodeStringForKey("t"), timeout: decoder.decodeInt32ForKey("a")) + self = .numericalPassword(value: decoder.decodeStringForKey("t"), timeout: decoder.decodeInt32ForKey("a"), attempts: decoder.decodeObjectForKey("att", decoder: { AccessChallengeAttempts(decoder: $0) }) as? AccessChallengeAttempts) case 2: - self = .plaintextPassword(value: decoder.decodeStringForKey("t"), timeout: decoder.decodeInt32ForKey("a")) + self = .plaintextPassword(value: decoder.decodeStringForKey("t"), timeout: decoder.decodeInt32ForKey("a"), attempts: decoder.decodeObjectForKey("att", decoder: { AccessChallengeAttempts(decoder: $0) }) as? AccessChallengeAttempts) default: assertionFailure() self = .none @@ -31,7 +55,7 @@ public enum PostboxAccessChallengeData: Coding, Equatable { switch self { case .none: encoder.encodeInt32(0, forKey: "r") - case let .numericalPassword(text, timeout): + case let .numericalPassword(text, timeout, attempts): encoder.encodeInt32(1, forKey: "r") encoder.encodeString(text, forKey: "t") if let timeout = timeout { @@ -39,7 +63,12 @@ public enum PostboxAccessChallengeData: Coding, Equatable { } else { encoder.encodeNil(forKey: "a") } - case let .plaintextPassword(text, timeout): + if let attempts = attempts { + encoder.encodeObject(attempts, forKey: "att") + } else { + encoder.encodeNil(forKey: "att") + } + case let .plaintextPassword(text, timeout, attempts): encoder.encodeInt32(2, forKey: "r") encoder.encodeString(text, forKey: "t") if let timeout = timeout { @@ -47,6 +76,11 @@ public enum PostboxAccessChallengeData: Coding, Equatable { } else { encoder.encodeNil(forKey: "a") } + if let attempts = attempts { + encoder.encodeObject(attempts, forKey: "att") + } else { + encoder.encodeNil(forKey: "att") + } } } @@ -58,20 +92,61 @@ public enum PostboxAccessChallengeData: Coding, Equatable { } else { return false } - case let .numericalPassword(lhsText, lhsTimeout): - if case let .numericalPassword(rhsText, rhsTimeout) = rhs, lhsText == rhsText, lhsTimeout == rhsTimeout { + case let .numericalPassword(lhsText, lhsTimeout, lhsAttempts): + if case let .numericalPassword(rhsText, rhsTimeout, rhsAttempts) = rhs, lhsText == rhsText, lhsTimeout == rhsTimeout, lhsAttempts == rhsAttempts { return true } else { return false } - case let .plaintextPassword(lhsText, lhsTimeout): - if case let .plaintextPassword(rhsText, rhsTimeout) = rhs, lhsText == rhsText, lhsTimeout == rhsTimeout { + case let .plaintextPassword(lhsText, lhsTimeout, lhsAttempts): + if case let .plaintextPassword(rhsText, rhsTimeout, rhsAttempts) = rhs, lhsText == rhsText, lhsTimeout == rhsTimeout, lhsAttempts == rhsAttempts { return true } else { return false } } } + + public var isLockable: Bool { + if case .none = self { + return false + } else { + return true + } + } + + public var autolockDeadline: Int32? { + switch self { + case .none: + return nil + case let .numericalPassword(_, timeout, _): + return timeout + case let .plaintextPassword(_, timeout, _): + return timeout + } + } + + public var attempts: AccessChallengeAttempts? { + switch self { + case .none: + return nil + case let .numericalPassword(_, _, attempts): + return attempts + case let .plaintextPassword(_, _, attempts): + return attempts + } + } + + public func withUpdatedAutolockDeadline(_ autolockDeadline: Int32?) -> PostboxAccessChallengeData { + switch self { + case .none: + return self + case let .numericalPassword(value, _, attempts): + return .numericalPassword(value: value, timeout: autolockDeadline, attempts: attempts) + case let .plaintextPassword(value, _, attempts): + return .plaintextPassword(value: value, timeout: autolockDeadline, attempts: attempts) + } + } } final class MetadataTable: Table { diff --git a/Postbox/Postbox.swift b/Postbox/Postbox.swift index 27c2407288..8e7b6be3b6 100644 --- a/Postbox/Postbox.swift +++ b/Postbox/Postbox.swift @@ -535,7 +535,7 @@ public final class Modifier { public func setAccessChallengeData(_ data: PostboxAccessChallengeData) { assert(!self.disposed) - self.postbox?.metadataTable.setAccessChallengeData(data) + self.postbox?.setAccessChallengeData(data) } } @@ -643,6 +643,7 @@ public final class Postbox { private var currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]] = [:] private var currentItemCollectionInfosOperations: [ItemCollectionInfosOperation] = [] private var currentUpdatedPeerChatStates = Set() + private var currentUpdatedAccessChallengeData: PostboxAccessChallengeData? private var currentReplaceChatListHoles: [(MessageIndex, ChatListHole?)] = [] private var currentReplacedContactPeerIds: Set? @@ -1251,7 +1252,7 @@ public final class Postbox { return self.peerTable.get(peerId) }, updatedTotalUnreadCount: &self.currentUpdatedTotalUnreadCount) - let transaction = PostboxTransaction(currentUpdatedState: self.currentUpdatedState, 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, currentOrderedItemListOperations: self.currentOrderedItemListOperations, currentItemCollectionItemsOperations: self.currentItemCollectionItemsOperations, currentItemCollectionInfosOperations: self.currentItemCollectionInfosOperations, currentUpdatedPeerChatStates: self.currentUpdatedPeerChatStates, updatedMedia: self.currentUpdatedMedia, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentUpdatedMasterClientId: currentUpdatedMasterClientId) + let transaction = PostboxTransaction(currentUpdatedState: self.currentUpdatedState, 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, currentOrderedItemListOperations: self.currentOrderedItemListOperations, currentItemCollectionItemsOperations: self.currentItemCollectionItemsOperations, currentItemCollectionInfosOperations: self.currentItemCollectionInfosOperations, currentUpdatedPeerChatStates: self.currentUpdatedPeerChatStates, updatedAccessChallengeData: self.currentUpdatedAccessChallengeData, updatedMedia: self.currentUpdatedMedia, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentUpdatedMasterClientId: currentUpdatedMasterClientId) var updatedTransactionState: Int64? var updatedMasterClientId: Int64? if !transaction.isEmpty { @@ -1289,6 +1290,7 @@ public final class Postbox { self.currentItemCollectionItemsOperations.removeAll() self.currentItemCollectionInfosOperations.removeAll() self.currentUpdatedPeerChatStates.removeAll() + self.currentUpdatedAccessChallengeData = nil for table in self.tables { table.beforeCommit() @@ -2036,9 +2038,7 @@ public final class Postbox { 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 view = MutablePreferencesView(postbox: self, keys: Set(keys)) let (index, signal) = self.viewTracker.addPreferencesView(view) return (.single(PreferencesView(view)) @@ -2094,6 +2094,11 @@ public final class Postbox { return self.orderedItemListTable.getItemIds(collectionId: collectionId) } + fileprivate func setAccessChallengeData(_ data: PostboxAccessChallengeData) { + self.currentUpdatedAccessChallengeData = data + self.metadataTable.setAccessChallengeData(data) + } + public func isMasterClient() -> Signal { return self.modify { modifier -> Signal in let sessionClientId = self.sessionClientId diff --git a/Postbox/PostboxAccess.swift b/Postbox/PostboxAccess.swift index eb2802f3fb..4e2d344e5e 100644 --- a/Postbox/PostboxAccess.swift +++ b/Postbox/PostboxAccess.swift @@ -38,7 +38,7 @@ public func accessPostbox(basePath: String, password: String?) -> Signal Signal + let updatedAccessChallengeData: PostboxAccessChallengeData? let unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation] let updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?] @@ -100,10 +101,13 @@ final class PostboxTransaction { if !currentUpdatedPeerChatStates.isEmpty { return false } + if self.updatedAccessChallengeData != nil { + return false + } return true } - init(currentUpdatedState: Coding?, 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], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set, updatedMedia: [MediaId: Media?], replaceContactPeerIds: Set?, currentUpdatedMasterClientId: Int64?) { + init(currentUpdatedState: Coding?, 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], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set, updatedAccessChallengeData: PostboxAccessChallengeData?, updatedMedia: [MediaId: Media?], replaceContactPeerIds: Set?, currentUpdatedMasterClientId: Int64?) { self.currentUpdatedState = currentUpdatedState self.currentOperationsByPeerId = currentOperationsByPeerId self.peerIdsWithFilledHoles = peerIdsWithFilledHoles @@ -125,6 +129,7 @@ final class PostboxTransaction { self.currentItemCollectionItemsOperations = currentItemCollectionItemsOperations self.currentItemCollectionInfosOperations = currentItemCollectionInfosOperations self.currentUpdatedPeerChatStates = currentUpdatedPeerChatStates + self.updatedAccessChallengeData = updatedAccessChallengeData self.updatedMedia = updatedMedia self.replaceContactPeerIds = replaceContactPeerIds self.currentUpdatedMasterClientId = currentUpdatedMasterClientId diff --git a/Postbox/PreferencesView.swift b/Postbox/PreferencesView.swift index bdc6195672..4a7b809e03 100644 --- a/Postbox/PreferencesView.swift +++ b/Postbox/PreferencesView.swift @@ -1,23 +1,23 @@ import Foundation -final class MutablePreferencesView { +final class MutablePreferencesView: MutablePostboxView { fileprivate let keys: Set fileprivate var values: [ValueBoxKey: PreferencesEntry] - init(keys: Set, get: (ValueBoxKey) -> PreferencesEntry?) { + init(postbox: Postbox, keys: Set) { self.keys = keys var values: [ValueBoxKey: PreferencesEntry] = [:] for key in keys { - if let value = get(key) { + if let value = postbox.preferencesTable.get(key: key) { values[key] = value } } self.values = values } - func replay(operations: [PreferencesOperation], get: (ValueBoxKey) -> PreferencesEntry?) -> Bool { + func replay(postbox: Postbox, transaction: PostboxTransaction) -> Bool { var updated = false - for operation in operations { + for operation in transaction.currentPreferencesOperations { switch operation { case let .update(key, value): if self.keys.contains(key) { @@ -44,9 +44,13 @@ final class MutablePreferencesView { return updated } + + func immutableView() -> PostboxView { + return PreferencesView(self) + } } -public final class PreferencesView { +public final class PreferencesView: PostboxView { public let values: [ValueBoxKey: PreferencesEntry] init(_ view: MutablePreferencesView) { diff --git a/Postbox/ViewTracker.swift b/Postbox/ViewTracker.swift index 7495e4cc4f..b5f4ca9d38 100644 --- a/Postbox/ViewTracker.swift +++ b/Postbox/ViewTracker.swift @@ -501,7 +501,7 @@ final class ViewTracker { } for (mutableView, pipe) in self.preferencesViews.copyItems() { - if mutableView.replay(operations: transaction.currentPreferencesOperations, get: self.getPreferencesEntry) { + if mutableView.replay(postbox: postbox, transaction: transaction) { pipe.putNext(PreferencesView(mutableView)) } } diff --git a/Postbox/Views.swift b/Postbox/Views.swift index c0ccb6b3b1..43fa9950df 100644 --- a/Postbox/Views.swift +++ b/Postbox/Views.swift @@ -6,6 +6,8 @@ public enum PostboxViewKey: Hashable { case itemCollectionInfo(id: ItemCollectionId) case peerChatState(peerId: PeerId) case orderedItemList(id: Int32) + case accessChallengeData + case preferences(keys: Set) public var hashValue: Int { switch self { @@ -19,6 +21,10 @@ public enum PostboxViewKey: Hashable { return id.hashValue case let .orderedItemList(id): return id.hashValue + case .accessChallengeData: + return 2 + case .preferences: + return 3 } } @@ -54,6 +60,18 @@ public enum PostboxViewKey: Hashable { } else { return false } + case .accessChallengeData: + if case .accessChallengeData = rhs { + return true + } else { + return false + } + case let .preferences(lhsKeys): + if case let .preferences(rhsKeys) = rhs, lhsKeys == rhsKeys { + return true + } else { + return false + } } } } @@ -70,5 +88,9 @@ func postboxViewForKey(postbox: Postbox, key: PostboxViewKey) -> MutablePostboxV return MutablePeerChatStateView(postbox: postbox, peerId: peerId) case let .orderedItemList(id): return MutableOrderedItemListView(postbox: postbox, collectionId: id) + case .accessChallengeData: + return MutableAccessChallengeDataView(postbox: postbox) + case let .preferences(keys): + return MutablePreferencesView(postbox: postbox, keys: keys) } }