From acbebf2342b6c374d3123ff31e82db1a7f5c9302 Mon Sep 17 00:00:00 2001 From: Peter Iakovlev Date: Mon, 21 Jan 2019 15:17:49 +0400 Subject: [PATCH 01/17] Remove assert --- Postbox/MessageHistoryView.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Postbox/MessageHistoryView.swift b/Postbox/MessageHistoryView.swift index 18deb5f1ae..75e8049d89 100644 --- a/Postbox/MessageHistoryView.swift +++ b/Postbox/MessageHistoryView.swift @@ -186,7 +186,6 @@ enum MutableMessageHistoryEntry { assert(location.count != 0) return .HoleEntry(hole, MessageHistoryEntryLocation(index: location.index - 1, count: location.count - 1), lowerIndex: lowerIndex) } else { - assert(location.count != 0) return .HoleEntry(hole, MessageHistoryEntryLocation(index: location.index, count: max(0, location.count - 1)), lowerIndex: lowerIndex) } } else { From f811d2a6475398acafae51f8b562b65d486b8bfd Mon Sep 17 00:00:00 2001 From: Peter Iakovlev Date: Fri, 25 Jan 2019 17:47:19 +0400 Subject: [PATCH 02/17] no message --- Postbox/AccountManager.swift | 19 +++++++++- Postbox/AccountManagerMetadataTable.swift | 43 +++++++++++++++++++++++ Postbox/AccountRecordsView.swift | 9 ++++- Postbox/Postbox.swift | 5 ++- 4 files changed, 73 insertions(+), 3 deletions(-) diff --git a/Postbox/AccountManager.swift b/Postbox/AccountManager.swift index 24d3ba0e45..f2bcf3f43f 100644 --- a/Postbox/AccountManager.swift +++ b/Postbox/AccountManager.swift @@ -10,6 +10,9 @@ public struct AccountManagerModifier { public let updateRecord: (AccountRecordId, (AccountRecord?) -> (AccountRecord?)) -> Void public let getCurrent: () -> (AccountRecordId, [AccountRecordAttribute])? public let setCurrentId: (AccountRecordId) -> Void + public let getCurrentAuth: () -> AuthAccountRecord? + public let createAuth: ([AccountRecordAttribute]) -> AuthAccountRecord? + public let removeAuth: () -> Void public let createRecord: ([AccountRecordAttribute]) -> AccountRecordId public let getSharedData: (ValueBoxKey) -> AccountSharedData? public let updateSharedData: (ValueBoxKey, (AccountSharedData?) -> AccountSharedData?) -> Void @@ -46,6 +49,8 @@ final class AccountManagerImpl { self.recordTable = AccountManagerRecordTable(valueBox: self.valueBox, table: AccountManagerRecordTable.tableSpec(1)) self.sharedDataTable = AccountManagerSharedDataTable(valueBox: self.valueBox, table: AccountManagerSharedDataTable.tableSpec(2)) + postboxLog("AccountManager: currentAccountId = \(String(describing: self.metadataTable.getCurrentAccountId()))") + self.tables.append(self.metadataTable) self.tables.append(self.recordTable) self.tables.append(self.sharedDataTable) @@ -77,6 +82,18 @@ final class AccountManagerImpl { } }, setCurrentId: { id in self.metadataTable.setCurrentAccountId(id, operations: &self.currentMetadataOperations) + }, getCurrentAuth: { + if let id = self.metadataTable.getCurrentAuthAccount() { + return id + } else { + return nil + } + }, createAuth: { attributes in + let record = AuthAccountRecord(id: generateAccountRecordId(), attributes: attributes) + self.metadataTable.setCurrentAuthAccount(record, operations: &self.currentMetadataOperations) + return record + }, removeAuth: { + self.metadataTable.setCurrentAuthAccount(nil, operations: &self.currentMetadataOperations) }, createRecord: { attributes in let id = generateAccountRecordId() let record = AccountRecord(id: id, attributes: attributes, temporarySessionId: nil) @@ -145,7 +162,7 @@ final class AccountManagerImpl { private func accountRecordsInternal(transaction: AccountManagerModifier) -> Signal { let mutableView = MutableAccountRecordsView(getRecords: { return self.recordTable.getRecords() - }, currentId: self.metadataTable.getCurrentAccountId()) + }, currentId: self.metadataTable.getCurrentAccountId(), currentAuth: self.metadataTable.getCurrentAuthAccount()) let pipe = ValuePipe() let index = self.recordsViews.add((mutableView, pipe)) diff --git a/Postbox/AccountManagerMetadataTable.swift b/Postbox/AccountManagerMetadataTable.swift index 3e31283f42..9d4aa123f3 100644 --- a/Postbox/AccountManagerMetadataTable.swift +++ b/Postbox/AccountManagerMetadataTable.swift @@ -1,11 +1,33 @@ import Foundation +public struct AuthAccountRecord: PostboxCoding { + public let id: AccountRecordId + public let attributes: [AccountRecordAttribute] + + init(id: AccountRecordId, attributes: [AccountRecordAttribute]) { + self.id = id + self.attributes = attributes + } + + public init(decoder: PostboxDecoder) { + self.id = AccountRecordId(rawValue: decoder.decodeOptionalInt64ForKey("id")!) + self.attributes = decoder.decodeObjectArrayForKey("attributes").compactMap({ $0 as? AccountRecordAttribute }) + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt64(self.id.rawValue, forKey: "id") + encoder.encodeGenericObjectArray(self.attributes.map { $0 as PostboxCoding }, forKey: "attributes") + } +} + enum AccountManagerMetadataOperation { case updateCurrentAccountId(AccountRecordId) + case updateCurrentAuthAccountRecord(AuthAccountRecord?) } private enum MetadataKey: Int64 { case currentAccountId = 0 + case currentAuthAccount = 1 } final class AccountManagerMetadataTable: Table { @@ -34,4 +56,25 @@ final class AccountManagerMetadataTable: Table { self.valueBox.set(self.table, key: self.key(.currentAccountId), value: MemoryBuffer(memory: &rawValue, capacity: 8, length: 8, freeWhenDone: false)) operations.append(.updateCurrentAccountId(id)) } + + func getCurrentAuthAccount() -> AuthAccountRecord? { + if let value = self.valueBox.get(self.table, key: self.key(.currentAuthAccount)), let object = PostboxDecoder(buffer: value).decodeRootObject() as? AuthAccountRecord { + return object + } else { + return nil + } + } + + func setCurrentAuthAccount(_ record: AuthAccountRecord?, operations: inout [AccountManagerMetadataOperation]) { + if let record = record { + let encoder = PostboxEncoder() + encoder.encodeRootObject(record) + withExtendedLifetime(encoder, { + self.valueBox.set(self.table, key: self.key(.currentAuthAccount), value: encoder.readBufferNoCopy()) + }) + } else { + self.valueBox.remove(self.table, key: self.key(.currentAuthAccount)) + } + operations.append(.updateCurrentAuthAccountRecord(record)) + } } diff --git a/Postbox/AccountRecordsView.swift b/Postbox/AccountRecordsView.swift index 44dcb79b05..3a86acaf50 100644 --- a/Postbox/AccountRecordsView.swift +++ b/Postbox/AccountRecordsView.swift @@ -3,10 +3,12 @@ import Foundation final class MutableAccountRecordsView { fileprivate var records: [AccountRecord] fileprivate var currentId: AccountRecordId? + fileprivate var currentAuth: AuthAccountRecord? - init(getRecords: () -> [AccountRecord], currentId: AccountRecordId?) { + init(getRecords: () -> [AccountRecord], currentId: AccountRecordId?, currentAuth: AuthAccountRecord?) { self.records = getRecords() self.currentId = currentId + self.currentAuth = currentAuth } func replay(operations: [AccountManagerRecordOperation], metadataOperations: [AccountManagerMetadataOperation]) -> Bool { @@ -49,6 +51,9 @@ final class MutableAccountRecordsView { case let .updateCurrentAccountId(id): updated = true self.currentId = id + case let .updateCurrentAuthAccountRecord(record): + updated = true + self.currentAuth = record } } @@ -59,6 +64,7 @@ final class MutableAccountRecordsView { public final class AccountRecordsView { public let records: [AccountRecord] public let currentRecord: AccountRecord? + public let currentAuthAccount: AuthAccountRecord? init(_ view: MutableAccountRecordsView) { self.records = view.records @@ -74,5 +80,6 @@ public final class AccountRecordsView { } else { self.currentRecord = nil } + self.currentAuthAccount = view.currentAuth } } diff --git a/Postbox/Postbox.swift b/Postbox/Postbox.swift index 07058ec309..15b3703228 100644 --- a/Postbox/Postbox.swift +++ b/Postbox/Postbox.swift @@ -2889,7 +2889,10 @@ public final class Postbox { var additionalChatPeerIds: [PeerId] = [] for peerId in chatPeerIds { for associatedId in self.reverseAssociatedPeerTable.get(peerId: peerId) { - additionalChatPeerIds.append(associatedId) + let inclusionIndex = self.chatListIndexTable.get(peerId: associatedId) + if inclusionIndex.includedIndex(peerId: associatedId) != nil { + additionalChatPeerIds.append(associatedId) + } } } chatPeerIds.append(contentsOf: additionalChatPeerIds) From 494ba4dc7b8ed5962c64ac1bf3b1efd8b4ec77d7 Mon Sep 17 00:00:00 2001 From: Peter Iakovlev Date: Tue, 29 Jan 2019 14:02:36 +0400 Subject: [PATCH 03/17] Move accessChallenge to AccountManager --- Postbox.xcodeproj/project.pbxproj | 2 +- Postbox/AccessChallengeDataView.swift | 14 +- Postbox/AccountManager.swift | 95 +++++++++--- Postbox/AccountManagerMetadataTable.swift | 131 ++++++++++++++++ Postbox/AccountManagerSharedDataTable.swift | 6 +- Postbox/AccountSharedData.swift | 8 +- Postbox/MetadataTable.swift | 158 -------------------- Postbox/Postbox.swift | 23 +-- Postbox/PostboxAccess.swift | 62 -------- Postbox/PostboxTransaction.swift | 7 +- Postbox/Views.swift | 11 -- 11 files changed, 217 insertions(+), 300 deletions(-) diff --git a/Postbox.xcodeproj/project.pbxproj b/Postbox.xcodeproj/project.pbxproj index 9b906435ff..28d7ccf439 100644 --- a/Postbox.xcodeproj/project.pbxproj +++ b/Postbox.xcodeproj/project.pbxproj @@ -703,6 +703,7 @@ D05D8B33218F1EBB0064586F /* AccountManagerSharedDataTable.swift */, D0BEAF6F1E54BC1E00BD963D /* AccountRecordsView.swift */, D05D8B30218F1D3D0064586F /* AccountSharedData.swift */, + D0575AE21E9ECBB2006F2541 /* AccessChallengeDataView.swift */, ); name = "Account Manager"; sourceTree = ""; @@ -873,7 +874,6 @@ D0FA0AC91E780A26005BB9B7 /* PostboxView.swift */, D0FA0ACC1E781067005BB9B7 /* Views.swift */, D0F53BF21E794C6700117362 /* PeerChatStateView.swift */, - D0575AE21E9ECBB2006F2541 /* AccessChallengeDataView.swift */, D0E1D30E1ECA53F900FCEEF1 /* GlobalMessageTagsView.swift */, D07047A41F3CF63800F6A8D4 /* MessageHistoryTagSummaryView.swift */, D07047AA1F3DD8D100F6A8D4 /* PendingMessageActionsView.swift */, diff --git a/Postbox/AccessChallengeDataView.swift b/Postbox/AccessChallengeDataView.swift index 74913071ee..adfbb45e35 100644 --- a/Postbox/AccessChallengeDataView.swift +++ b/Postbox/AccessChallengeDataView.swift @@ -1,16 +1,16 @@ import Foundation -final class MutableAccessChallengeDataView: MutablePostboxView { +final class MutableAccessChallengeDataView { var data: PostboxAccessChallengeData - init(postbox: Postbox) { - self.data = postbox.metadataTable.accessChallengeData() + init(data: PostboxAccessChallengeData) { + self.data = data } - func replay(postbox: Postbox, transaction: PostboxTransaction) -> Bool { + func replay(updatedData: PostboxAccessChallengeData?) -> Bool { var updated = false - if let data = transaction.updatedAccessChallengeData { + if let data = updatedData { if self.data != data { self.data = data updated = true @@ -19,10 +19,6 @@ final class MutableAccessChallengeDataView: MutablePostboxView { return updated } - - func immutableView() -> PostboxView { - return AccessChallengeDataView(self) - } } public final class AccessChallengeDataView: PostboxView { diff --git a/Postbox/AccountManager.swift b/Postbox/AccountManager.swift index f2bcf3f43f..0ac0db8738 100644 --- a/Postbox/AccountManager.swift +++ b/Postbox/AccountManager.swift @@ -14,8 +14,10 @@ public struct AccountManagerModifier { public let createAuth: ([AccountRecordAttribute]) -> AuthAccountRecord? public let removeAuth: () -> Void public let createRecord: ([AccountRecordAttribute]) -> AccountRecordId - public let getSharedData: (ValueBoxKey) -> AccountSharedData? - public let updateSharedData: (ValueBoxKey, (AccountSharedData?) -> AccountSharedData?) -> Void + public let getSharedData: (ValueBoxKey) -> PreferencesEntry? + public let updateSharedData: (ValueBoxKey, (PreferencesEntry?) -> PreferencesEntry?) -> Void + public let getAccessChallengeData: () -> PostboxAccessChallengeData + public let setAccessChallengeData: (PostboxAccessChallengeData) -> Void } final class AccountManagerImpl { @@ -34,9 +36,11 @@ final class AccountManagerImpl { private var currentMetadataOperations: [AccountManagerMetadataOperation] = [] private var currentUpdatedSharedDataKeys = Set() + private var currentUpdatedAccessChallengeData: PostboxAccessChallengeData? private var recordsViews = Bag<(MutableAccountRecordsView, ValuePipe)>() private var sharedDataViews = Bag<(MutableAccountSharedDataView, ValuePipe)>() + private var accessChallengeDataViews = Bag<(MutableAccessChallengeDataView, ValuePipe)>() fileprivate init(queue: Queue, basePath: String, temporarySessionId: Int64) { self.queue = queue @@ -60,7 +64,7 @@ final class AccountManagerImpl { assert(self.queue.isCurrent()) } - fileprivate func transaction(_ f: @escaping (AccountManagerModifier) -> T) -> Signal { + fileprivate func transaction(ignoreDisabled: Bool, _ f: @escaping (AccountManagerModifier) -> T) -> Signal { return Signal { subscriber in self.queue.justDispatch { self.valueBox.begin() @@ -104,6 +108,11 @@ final class AccountManagerImpl { }, updateSharedData: { key, f in let updated = f(self.sharedDataTable.get(key: key)) self.sharedDataTable.set(key: key, value: updated, updatedKeys: &self.currentUpdatedSharedDataKeys) + }, getAccessChallengeData: { + return self.metadataTable.getAccessChallengeData() + }, setAccessChallengeData: { data in + self.currentUpdatedAccessChallengeData = data + self.metadataTable.setAccessChallengeData(data) }) let result = f(transaction) @@ -136,9 +145,18 @@ final class AccountManagerImpl { } } + if let data = self.currentUpdatedAccessChallengeData { + for (view, pipe) in self.accessChallengeDataViews.copyItems() { + if view.replay(updatedData: data) { + pipe.putNext(AccessChallengeDataView(view)) + } + } + } + self.currentRecordOperations.removeAll() self.currentMetadataOperations.removeAll() self.currentUpdatedSharedDataKeys.removeAll() + self.currentUpdatedAccessChallengeData = nil for table in self.tables { table.beforeCommit() @@ -146,16 +164,23 @@ final class AccountManagerImpl { } fileprivate func accountRecords() -> Signal { - return self.transaction { transaction -> Signal in + return self.transaction(ignoreDisabled: false, { transaction -> Signal in return self.accountRecordsInternal(transaction: transaction) - } + }) |> switchToLatest } fileprivate func sharedData(keys: Set) -> Signal { - return self.transaction { transaction -> Signal in + return self.transaction(ignoreDisabled: false, { transaction -> Signal in return self.sharedDataInternal(transaction: transaction, keys: keys) - } + }) + |> switchToLatest + } + + fileprivate func accessChallengeData() -> Signal { + return self.transaction(ignoreDisabled: false, { transaction -> Signal in + return self.accessChallengeDataInternal(transaction: transaction) + }) |> switchToLatest } @@ -201,8 +226,28 @@ final class AccountManagerImpl { } } + private func accessChallengeDataInternal(transaction: AccountManagerModifier) -> Signal { + let mutableView = MutableAccessChallengeDataView(data: transaction.getAccessChallengeData()) + let pipe = ValuePipe() + let index = self.accessChallengeDataViews.add((mutableView, pipe)) + + let queue = self.queue + return (.single(AccessChallengeDataView(mutableView)) + |> then(pipe.signal())) + |> `catch` { _ -> Signal in + return .complete() + } + |> afterDisposed { [weak self] in + queue.async { + if let strongSelf = self { + strongSelf.accessChallengeDataViews.remove(index) + } + } + } + } + fileprivate func currentAccountRecord(allocateIfNotExists: Bool) -> Signal<(AccountRecordId, [AccountRecordAttribute])?, NoError> { - return self.transaction { transaction -> Signal<(AccountRecordId, [AccountRecordAttribute])?, NoError> in + return self.transaction(ignoreDisabled: false, { transaction -> Signal<(AccountRecordId, [AccountRecordAttribute])?, NoError> in let current = transaction.getCurrent() let record: (AccountRecordId, [AccountRecordAttribute])? if let current = current { @@ -228,7 +273,7 @@ final class AccountManagerImpl { } return signal - } + }) |> switchToLatest |> distinctUntilChanged(isEqual: { lhs, rhs in if let lhs = lhs, let rhs = rhs { @@ -254,7 +299,7 @@ final class AccountManagerImpl { func allocatedTemporaryAccountId() -> Signal { let temporarySessionId = self.temporarySessionId - return self.transaction { transaction -> Signal in + return self.transaction(ignoreDisabled: false, { transaction -> Signal in let id = generateAccountRecordId() transaction.updateRecord(id, { _ in @@ -262,7 +307,7 @@ final class AccountManagerImpl { }) return .single(id) - } + }) |> switchToLatest |> distinctUntilChanged(isEqual: { lhs, rhs in return lhs == rhs @@ -275,7 +320,7 @@ public final class AccountManager { private let impl: QueueLocalObject public let temporarySessionId: Int64 - fileprivate init(basePath: String) { + public init(basePath: String) { var temporarySessionId: Int64 = 0 arc4random_buf(&temporarySessionId, 8) self.temporarySessionId = temporarySessionId @@ -285,11 +330,11 @@ public final class AccountManager { }) } - public func transaction(_ f: @escaping (AccountManagerModifier) -> T) -> Signal { + public func transaction(ignoreDisabled: Bool = false, _ f: @escaping (AccountManagerModifier) -> T) -> Signal { return Signal { subscriber in let disposable = MetaDisposable() self.impl.with { impl in - disposable.set(impl.transaction(f).start(next: { next in + disposable.set(impl.transaction(ignoreDisabled: ignoreDisabled, f).start(next: { next in subscriber.putNext(next) }, completed: { subscriber.putCompletion() @@ -327,6 +372,20 @@ public final class AccountManager { } } + public func accessChallengeData() -> Signal { + return Signal { subscriber in + let disposable = MetaDisposable() + self.impl.with { impl in + disposable.set(impl.accessChallengeData().start(next: { next in + subscriber.putNext(next) + }, completed: { + subscriber.putCompletion() + })) + } + return disposable + } + } + public func currentAccountRecord(allocateIfNotExists: Bool) -> Signal<(AccountRecordId, [AccountRecordAttribute])?, NoError> { return Signal { subscriber in let disposable = MetaDisposable() @@ -355,11 +414,3 @@ public final class AccountManager { } } } - -public func accountManager(basePath: String) -> Signal { - return Signal { subscriber in - subscriber.putNext(AccountManager(basePath: basePath)) - subscriber.putCompletion() - return EmptyDisposable - } -} diff --git a/Postbox/AccountManagerMetadataTable.swift b/Postbox/AccountManagerMetadataTable.swift index 9d4aa123f3..ec0df543d5 100644 --- a/Postbox/AccountManagerMetadataTable.swift +++ b/Postbox/AccountManagerMetadataTable.swift @@ -1,5 +1,119 @@ import Foundation +public struct AccessChallengeAttempts: PostboxCoding, Equatable { + public let count: Int32 + public let timestamp: Int32 + + public init(count: Int32, timestamp: Int32) { + self.count = count + self.timestamp = timestamp + } + + public init(decoder: PostboxDecoder) { + self.count = decoder.decodeInt32ForKey("c", orElse: 0) + self.timestamp = decoder.decodeInt32ForKey("t", orElse: 0) + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt32(self.count, forKey: "c") + encoder.encodeInt32(self.timestamp, forKey: "t") + } +} + +public enum PostboxAccessChallengeData: PostboxCoding, Equatable { + case none + case numericalPassword(value: String, timeout: Int32?, attempts: AccessChallengeAttempts?) + case plaintextPassword(value: String, timeout: Int32?, attempts: AccessChallengeAttempts?) + + public init(decoder: PostboxDecoder) { + switch decoder.decodeInt32ForKey("r", orElse: 0) { + case 0: + self = .none + case 1: + self = .numericalPassword(value: decoder.decodeStringForKey("t", orElse: ""), timeout: decoder.decodeOptionalInt32ForKey("a"), attempts: decoder.decodeObjectForKey("att", decoder: { AccessChallengeAttempts(decoder: $0) }) as? AccessChallengeAttempts) + case 2: + self = .plaintextPassword(value: decoder.decodeStringForKey("t", orElse: ""), timeout: decoder.decodeOptionalInt32ForKey("a"), attempts: decoder.decodeObjectForKey("att", decoder: { AccessChallengeAttempts(decoder: $0) }) as? AccessChallengeAttempts) + default: + assertionFailure() + self = .none + } + } + + public func encode(_ encoder: PostboxEncoder) { + switch self { + case .none: + encoder.encodeInt32(0, forKey: "r") + case let .numericalPassword(text, timeout, attempts): + encoder.encodeInt32(1, forKey: "r") + encoder.encodeString(text, forKey: "t") + if let timeout = timeout { + encoder.encodeInt32(timeout, forKey: "a") + } else { + encoder.encodeNil(forKey: "a") + } + 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 { + encoder.encodeInt32(timeout, forKey: "a") + } else { + encoder.encodeNil(forKey: "a") + } + if let attempts = attempts { + encoder.encodeObject(attempts, forKey: "att") + } else { + encoder.encodeNil(forKey: "att") + } + } + } + + 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) + } + } +} + public struct AuthAccountRecord: PostboxCoding { public let id: AccountRecordId public let attributes: [AccountRecordAttribute] @@ -28,6 +142,7 @@ enum AccountManagerMetadataOperation { private enum MetadataKey: Int64 { case currentAccountId = 0 case currentAuthAccount = 1 + case accessChallenge = 2 } final class AccountManagerMetadataTable: Table { @@ -77,4 +192,20 @@ final class AccountManagerMetadataTable: Table { } operations.append(.updateCurrentAuthAccountRecord(record)) } + + func getAccessChallengeData() -> PostboxAccessChallengeData { + if let value = self.valueBox.get(self.table, key: self.key(.accessChallenge)) { + return PostboxAccessChallengeData(decoder: PostboxDecoder(buffer: value)) + } else { + return .none + } + } + + func setAccessChallengeData(_ data: PostboxAccessChallengeData) { + let encoder = PostboxEncoder() + data.encode(encoder) + withExtendedLifetime(encoder, { + self.valueBox.set(self.table, key: self.key(.accessChallenge), value: encoder.readBufferNoCopy()) + }) + } } diff --git a/Postbox/AccountManagerSharedDataTable.swift b/Postbox/AccountManagerSharedDataTable.swift index 101ee5b1ba..46b8dde81c 100644 --- a/Postbox/AccountManagerSharedDataTable.swift +++ b/Postbox/AccountManagerSharedDataTable.swift @@ -5,15 +5,15 @@ final class AccountManagerSharedDataTable: Table { return ValueBoxTable(id: id, keyType: .binary) } - func get(key: ValueBoxKey) -> AccountSharedData? { - if let value = self.valueBox.get(self.table, key: key), let object = PostboxDecoder(buffer: value).decodeRootObject() as? AccountSharedData { + func get(key: ValueBoxKey) -> PreferencesEntry? { + if let value = self.valueBox.get(self.table, key: key), let object = PostboxDecoder(buffer: value).decodeRootObject() as? PreferencesEntry { return object } else { return nil } } - func set(key: ValueBoxKey, value: AccountSharedData?, updatedKeys: inout Set) { + func set(key: ValueBoxKey, value: PreferencesEntry?, updatedKeys: inout Set) { if let value = value { if let current = self.get(key: key), current.isEqual(to: value) { return diff --git a/Postbox/AccountSharedData.swift b/Postbox/AccountSharedData.swift index 0e3db23b8c..eccca9a837 100644 --- a/Postbox/AccountSharedData.swift +++ b/Postbox/AccountSharedData.swift @@ -1,12 +1,8 @@ import Foundation -public protocol AccountSharedData: PostboxCoding { - func isEqual(to other: AccountSharedData) -> Bool -} - final class MutableAccountSharedDataView { private let keys: Set - fileprivate var entries: [ValueBoxKey: AccountSharedData] = [:] + fileprivate var entries: [ValueBoxKey: PreferencesEntry] = [:] init(accountManagerImpl: AccountManagerImpl, keys: Set) { self.keys = keys @@ -32,7 +28,7 @@ final class MutableAccountSharedDataView { } public final class AccountSharedDataView { - public let entries: [ValueBoxKey: AccountSharedData] + public let entries: [ValueBoxKey: PreferencesEntry] init(_ view: MutableAccountSharedDataView) { self.entries = view.entries diff --git a/Postbox/MetadataTable.swift b/Postbox/MetadataTable.swift index f08ddb1982..b7255b19f5 100644 --- a/Postbox/MetadataTable.swift +++ b/Postbox/MetadataTable.swift @@ -5,151 +5,9 @@ private enum MetadataKey: Int32 { case State = 2 case TransactionStateVersion = 3 case MasterClientId = 4 - case AccessChallenge = 5 case RemoteContactCount = 6 } -public struct AccessChallengeAttempts: PostboxCoding, Equatable { - public let count: Int32 - public let timestamp: Int32 - - public init(count: Int32, timestamp: Int32) { - self.count = count - self.timestamp = timestamp - } - - public init(decoder: PostboxDecoder) { - self.count = decoder.decodeInt32ForKey("c", orElse: 0) - self.timestamp = decoder.decodeInt32ForKey("t", orElse: 0) - } - - public func encode(_ encoder: PostboxEncoder) { - 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: PostboxCoding, Equatable { - case none - case numericalPassword(value: String, timeout: Int32?, attempts: AccessChallengeAttempts?) - case plaintextPassword(value: String, timeout: Int32?, attempts: AccessChallengeAttempts?) - - public init(decoder: PostboxDecoder) { - switch decoder.decodeInt32ForKey("r", orElse: 0) { - case 0: - self = .none - case 1: - self = .numericalPassword(value: decoder.decodeStringForKey("t", orElse: ""), timeout: decoder.decodeOptionalInt32ForKey("a"), attempts: decoder.decodeObjectForKey("att", decoder: { AccessChallengeAttempts(decoder: $0) }) as? AccessChallengeAttempts) - case 2: - self = .plaintextPassword(value: decoder.decodeStringForKey("t", orElse: ""), timeout: decoder.decodeOptionalInt32ForKey("a"), attempts: decoder.decodeObjectForKey("att", decoder: { AccessChallengeAttempts(decoder: $0) }) as? AccessChallengeAttempts) - default: - assertionFailure() - self = .none - } - } - - public func encode(_ encoder: PostboxEncoder) { - switch self { - case .none: - encoder.encodeInt32(0, forKey: "r") - case let .numericalPassword(text, timeout, attempts): - encoder.encodeInt32(1, forKey: "r") - encoder.encodeString(text, forKey: "t") - if let timeout = timeout { - encoder.encodeInt32(timeout, forKey: "a") - } else { - encoder.encodeNil(forKey: "a") - } - 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 { - encoder.encodeInt32(timeout, forKey: "a") - } else { - encoder.encodeNil(forKey: "a") - } - if let attempts = attempts { - encoder.encodeObject(attempts, forKey: "att") - } else { - encoder.encodeNil(forKey: "att") - } - } - } - - public static func ==(lhs: PostboxAccessChallengeData, rhs: PostboxAccessChallengeData) -> Bool { - switch lhs { - case .none: - if case .none = rhs { - return true - } else { - return false - } - 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, 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 { static func tableSpec(_ id: Int32) -> ValueBoxTable { return ValueBoxTable(id: id, keyType: .int64) @@ -248,22 +106,6 @@ final class MetadataTable: Table { self.valueBox.set(self.table, key: self.key(.MasterClientId), value: buffer) } - func accessChallengeData() -> PostboxAccessChallengeData { - if let value = self.valueBox.get(self.table, key: self.key(.AccessChallenge)) { - return PostboxAccessChallengeData(decoder: PostboxDecoder(buffer: value)) - } else { - return .none - } - } - - func setAccessChallengeData(_ data: PostboxAccessChallengeData) { - let encoder = PostboxEncoder() - data.encode(encoder) - withExtendedLifetime(encoder, { - self.valueBox.set(self.table, key: self.key(.AccessChallenge), value: encoder.readBufferNoCopy()) - }) - } - func setRemoteContactCount(_ count: Int32) { self.cachedRemoteContactCount = count var mutableCount: Int32 = count diff --git a/Postbox/Postbox.swift b/Postbox/Postbox.swift index 15b3703228..b13d906285 100644 --- a/Postbox/Postbox.swift +++ b/Postbox/Postbox.swift @@ -769,20 +769,6 @@ public final class Transaction { } } - public func getAccessChallengeData() -> PostboxAccessChallengeData { - assert(!self.disposed) - if let postbox = self.postbox { - return postbox.metadataTable.accessChallengeData() - } else { - return .none - } - } - - public func setAccessChallengeData(_ data: PostboxAccessChallengeData) { - assert(!self.disposed) - self.postbox?.setAccessChallengeData(data) - } - public func enumerateMedia(lowerBound: MessageIndex?, limit: Int) -> ([PeerId: Set], [MediaId: Media], MessageIndex?) { assert(!self.disposed) if let postbox = self.postbox { @@ -1078,7 +1064,6 @@ public final class Postbox { private var currentItemCollectionInfosOperations: [ItemCollectionInfosOperation] = [] private var currentUpdatedPeerChatStates = Set() private var currentUpdatedPeerGroupStates = Set() - private var currentUpdatedAccessChallengeData: PostboxAccessChallengeData? private var currentPendingMessageActionsOperations: [PendingMessageActionsOperation] = [] private var currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32] = [:] private var currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey : MessageHistoryTagNamespaceSummary] = [:] @@ -1937,7 +1922,7 @@ public final class Postbox { }*/ #endif - let transaction = PostboxTransaction(currentUpdatedState: self.currentUpdatedState, currentOperationsByPeerId: self.currentOperationsByPeerId, currentGroupFeedOperations: self.currentGroupFeedOperations, peerIdsWithFilledHoles: self.currentFilledHolesByPeerId, removedHolesByPeerId: self.currentRemovedHolesByPeerId, groupFeedIdsWithFilledHoles: self.currentGroupFeedIdsWithFilledHoles, removedHolesByPeerGroupId: self.currentRemovedHolesByPeerGroupId, chatListOperations: self.currentChatListOperations, currentUpdatedChatListInclusions: self.currentUpdatedChatListInclusions, currentUpdatedPeers: self.currentUpdatedPeers, currentUpdatedPeerNotificationSettings: self.currentUpdatedPeerNotificationSettings, currentUpdatedCachedPeerData: self.currentUpdatedCachedPeerData, currentUpdatedPeerPresences: currentUpdatedPeerPresences, currentUpdatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, currentUpdatedTotalUnreadState: self.currentUpdatedTotalUnreadState, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, currentPeerMergedOperationLogOperations: self.currentPeerMergedOperationLogOperations, currentTimestampBasedMessageAttributesOperations: self.currentTimestampBasedMessageAttributesOperations, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, currentPreferencesOperations: self.currentPreferencesOperations, currentOrderedItemListOperations: self.currentOrderedItemListOperations, currentItemCollectionItemsOperations: self.currentItemCollectionItemsOperations, currentItemCollectionInfosOperations: self.currentItemCollectionInfosOperations, currentUpdatedPeerChatStates: self.currentUpdatedPeerChatStates, currentUpdatedPeerGroupStates: self.currentUpdatedPeerGroupStates, updatedAccessChallengeData: self.currentUpdatedAccessChallengeData, currentGlobalTagsOperations: self.currentGlobalTagsOperations, currentLocalTagsOperations: self.currentLocalTagsOperations, updatedMedia: self.currentUpdatedMedia, replaceRemoteContactCount: self.currentReplaceRemoteContactCount, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentPendingMessageActionsOperations: self.currentPendingMessageActionsOperations, currentUpdatedMessageActionsSummaries: self.currentUpdatedMessageActionsSummaries, currentUpdatedMessageTagSummaries: self.currentUpdatedMessageTagSummaries, currentInvalidateMessageTagSummaries: self.currentInvalidateMessageTagSummaries, currentUpdatedPendingPeerNotificationSettings: self.currentUpdatedPendingPeerNotificationSettings, currentGroupFeedReadStateContext: self.currentGroupFeedReadStateContext, currentInitialPeerGroupIdsBeforeUpdate: self.currentInitialPeerGroupIdsBeforeUpdate, replacedAdditionalChatListItems: self.currentReplacedAdditionalChatListItems, updatedNoticeEntryKeys: self.currentUpdatedNoticeEntryKeys, updatedCacheEntryKeys: self.currentUpdatedCacheEntryKeys, currentUpdatedMasterClientId: currentUpdatedMasterClientId) + let transaction = PostboxTransaction(currentUpdatedState: self.currentUpdatedState, currentOperationsByPeerId: self.currentOperationsByPeerId, currentGroupFeedOperations: self.currentGroupFeedOperations, peerIdsWithFilledHoles: self.currentFilledHolesByPeerId, removedHolesByPeerId: self.currentRemovedHolesByPeerId, groupFeedIdsWithFilledHoles: self.currentGroupFeedIdsWithFilledHoles, removedHolesByPeerGroupId: self.currentRemovedHolesByPeerGroupId, chatListOperations: self.currentChatListOperations, currentUpdatedChatListInclusions: self.currentUpdatedChatListInclusions, currentUpdatedPeers: self.currentUpdatedPeers, currentUpdatedPeerNotificationSettings: self.currentUpdatedPeerNotificationSettings, currentUpdatedCachedPeerData: self.currentUpdatedCachedPeerData, currentUpdatedPeerPresences: currentUpdatedPeerPresences, currentUpdatedPeerChatListEmbeddedStates: self.currentUpdatedPeerChatListEmbeddedStates, currentUpdatedTotalUnreadState: self.currentUpdatedTotalUnreadState, alteredInitialPeerCombinedReadStates: alteredInitialPeerCombinedReadStates, currentPeerMergedOperationLogOperations: self.currentPeerMergedOperationLogOperations, currentTimestampBasedMessageAttributesOperations: self.currentTimestampBasedMessageAttributesOperations, unsentMessageOperations: self.currentUnsentOperations, updatedSynchronizePeerReadStateOperations: self.currentUpdatedSynchronizeReadStateOperations, currentPreferencesOperations: self.currentPreferencesOperations, currentOrderedItemListOperations: self.currentOrderedItemListOperations, currentItemCollectionItemsOperations: self.currentItemCollectionItemsOperations, currentItemCollectionInfosOperations: self.currentItemCollectionInfosOperations, currentUpdatedPeerChatStates: self.currentUpdatedPeerChatStates, currentUpdatedPeerGroupStates: self.currentUpdatedPeerGroupStates, currentGlobalTagsOperations: self.currentGlobalTagsOperations, currentLocalTagsOperations: self.currentLocalTagsOperations, updatedMedia: self.currentUpdatedMedia, replaceRemoteContactCount: self.currentReplaceRemoteContactCount, replaceContactPeerIds: self.currentReplacedContactPeerIds, currentPendingMessageActionsOperations: self.currentPendingMessageActionsOperations, currentUpdatedMessageActionsSummaries: self.currentUpdatedMessageActionsSummaries, currentUpdatedMessageTagSummaries: self.currentUpdatedMessageTagSummaries, currentInvalidateMessageTagSummaries: self.currentInvalidateMessageTagSummaries, currentUpdatedPendingPeerNotificationSettings: self.currentUpdatedPendingPeerNotificationSettings, currentGroupFeedReadStateContext: self.currentGroupFeedReadStateContext, currentInitialPeerGroupIdsBeforeUpdate: self.currentInitialPeerGroupIdsBeforeUpdate, replacedAdditionalChatListItems: self.currentReplacedAdditionalChatListItems, updatedNoticeEntryKeys: self.currentUpdatedNoticeEntryKeys, updatedCacheEntryKeys: self.currentUpdatedCacheEntryKeys, currentUpdatedMasterClientId: currentUpdatedMasterClientId) var updatedTransactionState: Int64? var updatedMasterClientId: Int64? if !transaction.isEmpty { @@ -1986,7 +1971,6 @@ public final class Postbox { self.currentItemCollectionInfosOperations.removeAll() self.currentUpdatedPeerChatStates.removeAll() self.currentUpdatedPeerGroupStates.removeAll() - self.currentUpdatedAccessChallengeData = nil self.currentPendingMessageActionsOperations.removeAll() self.currentUpdatedMessageActionsSummaries.removeAll() self.currentUpdatedMessageTagSummaries.removeAll() @@ -3330,11 +3314,6 @@ public final class Postbox { self.orderedItemListTable.updateItem(collectionId: collectionId, itemId: itemId, item: item, operations: &self.currentOrderedItemListOperations) } - fileprivate func setAccessChallengeData(_ data: PostboxAccessChallengeData) { - self.currentUpdatedAccessChallengeData = data - self.metadataTable.setAccessChallengeData(data) - } - public func installStoreMessageAction(peerId: PeerId, _ f: @escaping ([StoreMessage], Transaction) -> Void) -> Disposable { let disposable = MetaDisposable() self.queue.async { diff --git a/Postbox/PostboxAccess.swift b/Postbox/PostboxAccess.swift index 4e2d344e5e..8b13789179 100644 --- a/Postbox/PostboxAccess.swift +++ b/Postbox/PostboxAccess.swift @@ -1,63 +1 @@ -import Foundation -#if os(macOS) - import SwiftSignalKitMac -#else - import SwiftSignalKit -#endif -public enum PostboxAuthorizationChallenge { - case numericPassword(length: Int32) - case arbitraryPassword -} - -public enum PostboxAccess { - case unlocked - case locked(PostboxAuthorizationChallenge) -} - -private final class PostboxAccessHelper { - let queue: Queue - let valueBox: ValueBox - let metadataTable: MetadataTable - - init(queue: Queue, basePath: String) { - self.queue = queue - self.valueBox = SqliteValueBox(basePath: basePath + "/db", queue: self.queue) - self.metadataTable = MetadataTable(valueBox: self.valueBox, table: MetadataTable.tableSpec(0)) - } -} - -public func accessPostbox(basePath: String, password: String?) -> Signal { - return Signal { subscriber in - let queue = Queue() - - queue.async { - let postbox = PostboxAccessHelper(queue: queue, basePath: basePath) - let challengeData = postbox.metadataTable.accessChallengeData() - switch challengeData { - case .none: - subscriber.putNext(.unlocked) - subscriber.putCompletion() - case let .numericalPassword(text, _, _): - if text == password { - subscriber.putNext(.unlocked) - subscriber.putCompletion() - } else { - subscriber.putNext(.locked(.numericPassword(length: Int32(text.characters.count)))) - subscriber.putCompletion() - } - case let .plaintextPassword(text, _, _): - if text == password { - subscriber.putNext(.unlocked) - subscriber.putCompletion() - } else { - subscriber.putNext(.locked(.arbitraryPassword)) - subscriber.putCompletion() - } - } - } - - return ActionDisposable { - } - } -} diff --git a/Postbox/PostboxTransaction.swift b/Postbox/PostboxTransaction.swift index 1fdc5736f0..b639b445ac 100644 --- a/Postbox/PostboxTransaction.swift +++ b/Postbox/PostboxTransaction.swift @@ -25,7 +25,6 @@ final class PostboxTransaction { let currentItemCollectionInfosOperations: [ItemCollectionInfosOperation] let currentUpdatedPeerChatStates: Set let currentUpdatedPeerGroupStates: Set - let updatedAccessChallengeData: PostboxAccessChallengeData? let currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation] let currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation] let currentPendingMessageActionsOperations: [PendingMessageActionsOperation] @@ -140,9 +139,6 @@ final class PostboxTransaction { if !currentUpdatedPeerGroupStates.isEmpty { return false } - if self.updatedAccessChallengeData != nil { - return false - } if !self.currentGlobalTagsOperations.isEmpty { return false } @@ -182,7 +178,7 @@ final class PostboxTransaction { return true } - init(currentUpdatedState: PostboxCoding?, currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], currentGroupFeedOperations: [PeerGroupId : [GroupFeedIndexOperation]], peerIdsWithFilledHoles: [PeerId: [MessageIndex: HoleFillDirection]], removedHolesByPeerId: [PeerId: [MessageIndex: HoleFillDirection]], groupFeedIdsWithFilledHoles: [PeerGroupId: [MessageIndex: HoleFillDirection]], removedHolesByPeerGroupId: [PeerGroupId: [MessageIndex: HoleFillDirection]], chatListOperations: [WrappedPeerGroupId: [ChatListOperation]], currentUpdatedChatListInclusions: [PeerId: PeerChatListInclusion], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: PeerNotificationSettings], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?], currentUpdatedTotalUnreadState: ChatListTotalUnreadState?, alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set, currentUpdatedPeerGroupStates: Set, updatedAccessChallengeData: PostboxAccessChallengeData?, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set, currentGroupFeedReadStateContext: GroupFeedReadStateUpdateContext, currentInitialPeerGroupIdsBeforeUpdate: [PeerId: WrappedPeerGroupId], replacedAdditionalChatListItems: [PeerId]?, updatedNoticeEntryKeys: Set, updatedCacheEntryKeys: Set, currentUpdatedMasterClientId: Int64?) { + init(currentUpdatedState: PostboxCoding?, currentOperationsByPeerId: [PeerId: [MessageHistoryOperation]], currentGroupFeedOperations: [PeerGroupId : [GroupFeedIndexOperation]], peerIdsWithFilledHoles: [PeerId: [MessageIndex: HoleFillDirection]], removedHolesByPeerId: [PeerId: [MessageIndex: HoleFillDirection]], groupFeedIdsWithFilledHoles: [PeerGroupId: [MessageIndex: HoleFillDirection]], removedHolesByPeerGroupId: [PeerGroupId: [MessageIndex: HoleFillDirection]], chatListOperations: [WrappedPeerGroupId: [ChatListOperation]], currentUpdatedChatListInclusions: [PeerId: PeerChatListInclusion], currentUpdatedPeers: [PeerId: Peer], currentUpdatedPeerNotificationSettings: [PeerId: PeerNotificationSettings], currentUpdatedCachedPeerData: [PeerId: CachedPeerData], currentUpdatedPeerPresences: [PeerId: PeerPresence], currentUpdatedPeerChatListEmbeddedStates: [PeerId: PeerChatListEmbeddedInterfaceState?], currentUpdatedTotalUnreadState: ChatListTotalUnreadState?, alteredInitialPeerCombinedReadStates: [PeerId: CombinedPeerReadState], currentPeerMergedOperationLogOperations: [PeerMergedOperationLogOperation], currentTimestampBasedMessageAttributesOperations: [TimestampBasedMessageAttributesOperation], unsentMessageOperations: [IntermediateMessageHistoryUnsentOperation], updatedSynchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation?], currentPreferencesOperations: [PreferencesOperation], currentOrderedItemListOperations: [Int32: [OrderedItemListOperation]], currentItemCollectionItemsOperations: [ItemCollectionId: [ItemCollectionItemsOperation]], currentItemCollectionInfosOperations: [ItemCollectionInfosOperation], currentUpdatedPeerChatStates: Set, currentUpdatedPeerGroupStates: Set, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set, currentGroupFeedReadStateContext: GroupFeedReadStateUpdateContext, currentInitialPeerGroupIdsBeforeUpdate: [PeerId: WrappedPeerGroupId], replacedAdditionalChatListItems: [PeerId]?, updatedNoticeEntryKeys: Set, updatedCacheEntryKeys: Set, currentUpdatedMasterClientId: Int64?) { self.currentUpdatedState = currentUpdatedState self.currentOperationsByPeerId = currentOperationsByPeerId self.currentGroupFeedOperations = currentGroupFeedOperations @@ -209,7 +205,6 @@ final class PostboxTransaction { self.currentItemCollectionInfosOperations = currentItemCollectionInfosOperations self.currentUpdatedPeerChatStates = currentUpdatedPeerChatStates self.currentUpdatedPeerGroupStates = currentUpdatedPeerGroupStates - self.updatedAccessChallengeData = updatedAccessChallengeData self.currentGlobalTagsOperations = currentGlobalTagsOperations self.currentLocalTagsOperations = currentLocalTagsOperations self.updatedMedia = updatedMedia diff --git a/Postbox/Views.swift b/Postbox/Views.swift index 7486db77c3..5903acb6d9 100644 --- a/Postbox/Views.swift +++ b/Postbox/Views.swift @@ -7,7 +7,6 @@ public enum PostboxViewKey: Hashable { case peerChatState(peerId: PeerId) case peerGroupState(groupId: PeerGroupId) case orderedItemList(id: Int32) - case accessChallengeData case preferences(keys: Set) case globalMessageTags(globalTag: GlobalMessageTags, position: MessageIndex, count: Int, groupingPredicate: ((Message, Message) -> Bool)?) case peer(peerId: PeerId, components: PeerViewComponents) @@ -44,8 +43,6 @@ public enum PostboxViewKey: Hashable { return id.hashValue case let .orderedItemList(id): return id.hashValue - case .accessChallengeData: - return 2 case .preferences: return 3 case .globalMessageTags: @@ -129,12 +126,6 @@ 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 @@ -279,8 +270,6 @@ func postboxViewForKey(postbox: Postbox, key: PostboxViewKey) -> MutablePostboxV return MutablePeerGroupStateView(postbox: postbox, groupId: groupId) 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) case let .globalMessageTags(globalTag, position, count, groupingPredicate): From 12cad39c724bc74ab43ee0b7866498ce50a14007 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 29 Jan 2019 13:16:13 +0300 Subject: [PATCH 04/17] MediaBox: Don't count and don't remove cached representations of excluded resources on cleaning --- Postbox/MediaBox.swift | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/Postbox/MediaBox.swift b/Postbox/MediaBox.swift index 3d94873c45..04a3bbeca0 100644 --- a/Postbox/MediaBox.swift +++ b/Postbox/MediaBox.swift @@ -773,13 +773,13 @@ public final class MediaBox { } } - public func collectOtherResourceUsage(excludeIds: Set) -> Signal<(Int64, [String], Int64), NoError> { + public func collectOtherResourceUsage(excludeIds: Set, combinedExcludeIds: Set) -> Signal<(Int64, [String], Int64), NoError> { return Signal { subscriber in self.dataQueue.async { var result: Int64 = 0 var excludeNames = Set() - for id in excludeIds { + for id in combinedExcludeIds { let partial = "\(self.fileNameForId(id.id))_partial" let meta = "\(self.fileNameForId(id.id))_meta" let complete = self.fileNameForId(id.id) @@ -818,10 +818,22 @@ public final class MediaBox { var cacheResult: Int64 = 0 + var excludePrefixes = Set() + for id in excludeIds { + let cachedRepresentationPrefix = self.fileNameForId(id.id) + + excludePrefixes.insert(cachedRepresentationPrefix) + } + if let enumerator = FileManager.default.enumerator(at: URL(fileURLWithPath: self.basePath + "/cache"), includingPropertiesForKeys: [.fileSizeKey], options: [.skipsHiddenFiles, .skipsSubdirectoryDescendants], errorHandler: nil) { loop: for url in enumerator { if let url = url as? URL { + if let prefix = url.lastPathComponent.components(separatedBy: ":").first, excludePrefixes.contains(prefix) { + continue loop + } + if let value = (try? url.resourceValues(forKeys: Set([.fileSizeKey])))?.fileSize, value != 0 { + paths.append(url.lastPathComponent) cacheResult += Int64(value) } } @@ -841,13 +853,6 @@ public final class MediaBox { for path in paths { unlink(self.basePath + "/" + path) } - if let enumerator = FileManager.default.enumerator(at: URL(fileURLWithPath: self.basePath + "/cache"), includingPropertiesForKeys: [], options: [.skipsHiddenFiles, .skipsSubdirectoryDescendants], errorHandler: nil) { - loop: for url in enumerator { - if let url = url as? URL { - unlink(url.path) - } - } - } subscriber.putCompletion() } return EmptyDisposable From 88bc22e1192569ae4c0111b6d371119a87a91f08 Mon Sep 17 00:00:00 2001 From: Peter <> Date: Tue, 29 Jan 2019 15:32:30 +0400 Subject: [PATCH 05/17] Initial shared account media support --- Postbox.xcodeproj/project.pbxproj | 6 ++++++ Postbox/AccountManager.swift | 2 ++ Postbox/SharedAccountMediaManager.swift | 25 +++++++++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 Postbox/SharedAccountMediaManager.swift diff --git a/Postbox.xcodeproj/project.pbxproj b/Postbox.xcodeproj/project.pbxproj index 28d7ccf439..c88f96e85e 100644 --- a/Postbox.xcodeproj/project.pbxproj +++ b/Postbox.xcodeproj/project.pbxproj @@ -16,6 +16,8 @@ C2A315BE1E2E733900D89000 /* PeerMergedOperationLogIndexTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D010B6191E1E463900C3E282 /* PeerMergedOperationLogIndexTable.swift */; }; C2A315BF1E2E733900D89000 /* PeerMergedOperationLogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0F019FC1E1DA0CC00F05AB3 /* PeerMergedOperationLogView.swift */; }; C2AC9C131E1E5D200085C7DE /* UnreadMessageCountsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A18D661E16874D004C6734 /* UnreadMessageCountsView.swift */; }; + D000CADA22006C6C0011B15D /* SharedAccountMediaManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D000CAD922006C6C0011B15D /* SharedAccountMediaManager.swift */; }; + D000CADB22006C6C0011B15D /* SharedAccountMediaManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D000CAD922006C6C0011B15D /* SharedAccountMediaManager.swift */; }; D001388620BD942B007C9721 /* PostboxUpgrade_16to17.swift in Sources */ = {isa = PBXBuildFile; fileRef = D001388520BD942B007C9721 /* PostboxUpgrade_16to17.swift */; }; D001388720BD942B007C9721 /* PostboxUpgrade_16to17.swift in Sources */ = {isa = PBXBuildFile; fileRef = D001388520BD942B007C9721 /* PostboxUpgrade_16to17.swift */; }; D003E4E61B38DBDB00C22CBC /* MessageHistoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D003E4E51B38DBDB00C22CBC /* MessageHistoryView.swift */; }; @@ -396,6 +398,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + D000CAD922006C6C0011B15D /* SharedAccountMediaManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedAccountMediaManager.swift; sourceTree = ""; }; D001388520BD942B007C9721 /* PostboxUpgrade_16to17.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostboxUpgrade_16to17.swift; sourceTree = ""; }; D003E4E51B38DBDB00C22CBC /* MessageHistoryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageHistoryView.swift; sourceTree = ""; }; D0079F591D592E8B00A27A2C /* ContactTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactTable.swift; sourceTree = ""; }; @@ -704,6 +707,7 @@ D0BEAF6F1E54BC1E00BD963D /* AccountRecordsView.swift */, D05D8B30218F1D3D0064586F /* AccountSharedData.swift */, D0575AE21E9ECBB2006F2541 /* AccessChallengeDataView.swift */, + D000CAD922006C6C0011B15D /* SharedAccountMediaManager.swift */, ); name = "Account Manager"; sourceTree = ""; @@ -1166,6 +1170,7 @@ C2A315BF1E2E733900D89000 /* PeerMergedOperationLogView.swift in Sources */, C2A315BD1E2E732000D89000 /* PeerOperationLogMetadataTable.swift in Sources */, D0C26D731FE2E7A8004ABF18 /* GroupFeedReadStateTable.swift in Sources */, + D000CADB22006C6C0011B15D /* SharedAccountMediaManager.swift in Sources */, D0575AE41E9ECBB2006F2541 /* AccessChallengeDataView.swift in Sources */, C2A315BC1E2E730400D89000 /* PeerOperationLogTable.swift in Sources */, D0B2F75E204F551D00D3BFB9 /* DeviceContactImportInfoTable.swift in Sources */, @@ -1347,6 +1352,7 @@ D0575AE31E9ECBB2006F2541 /* AccessChallengeDataView.swift in Sources */, D0CE8CF31F70249400AA2DB0 /* PostboxUpgrade_13to14.swift in Sources */, D0E3A79E1B28B50400A402D9 /* Message.swift in Sources */, + D000CADA22006C6C0011B15D /* SharedAccountMediaManager.swift in Sources */, D0C674C81CBB11C600183765 /* MessageHistoryReadStateTable.swift in Sources */, D0DF0C8F1D81A350008AEB01 /* PeerView.swift in Sources */, D0B2F75D204F551D00D3BFB9 /* DeviceContactImportInfoTable.swift in Sources */, diff --git a/Postbox/AccountManager.swift b/Postbox/AccountManager.swift index 0ac0db8738..3ccf7518ac 100644 --- a/Postbox/AccountManager.swift +++ b/Postbox/AccountManager.swift @@ -25,6 +25,7 @@ final class AccountManagerImpl { private let basePath: String private let temporarySessionId: Int64 private let valueBox: ValueBox + private let sharedMediaManager: SharedAccountMediaManager private var tables: [Table] = [] @@ -45,6 +46,7 @@ final class AccountManagerImpl { fileprivate init(queue: Queue, basePath: String, temporarySessionId: Int64) { self.queue = queue self.basePath = basePath + self.sharedMediaManager = SharedAccountMediaManager(basePath: basePath + "/media") self.temporarySessionId = temporarySessionId let _ = try? FileManager.default.createDirectory(atPath: basePath, withIntermediateDirectories: true, attributes: nil) self.valueBox = SqliteValueBox(basePath: basePath + "/db", queue: queue) diff --git a/Postbox/SharedAccountMediaManager.swift b/Postbox/SharedAccountMediaManager.swift new file mode 100644 index 0000000000..7cc4d263d5 --- /dev/null +++ b/Postbox/SharedAccountMediaManager.swift @@ -0,0 +1,25 @@ +import Foundation + +final class SharedAccountMediaManager { + private let basePath: String + + init(basePath: String) { + self.basePath = basePath + } + + private func fileNameForId(_ id: MediaResourceId) -> String { + return "\(id.uniqueId)" + } + + private func pathForId(_ id: MediaResourceId) -> String { + return "\(self.basePath)/\(fileNameForId(id))" + } + + func resourceData(resourceId: MediaResourceId) -> Data? { + return try? Data(contentsOf: URL(fileURLWithPath: self.pathForId(id))) + } + + func storeResourceData(resourceId: MediaResourceId, data: Data) { + let _ = try? data.write(to: URL(fileURLWithPath: self.pathForId(id))) + } +} From 43b151f1df2ccb78ab6af0a886107e554d539527 Mon Sep 17 00:00:00 2001 From: Peter <> Date: Tue, 29 Jan 2019 16:08:16 +0400 Subject: [PATCH 06/17] AccountManager: export basePath --- Postbox/AccountManager.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Postbox/AccountManager.swift b/Postbox/AccountManager.swift index 24d3ba0e45..39869db281 100644 --- a/Postbox/AccountManager.swift +++ b/Postbox/AccountManager.swift @@ -254,11 +254,14 @@ final class AccountManagerImpl { } public final class AccountManager { + public let basePath: String private let queue = Queue() private let impl: QueueLocalObject public let temporarySessionId: Int64 fileprivate init(basePath: String) { + self.basePath = basePath + var temporarySessionId: Int64 = 0 arc4random_buf(&temporarySessionId, 8) self.temporarySessionId = temporarySessionId From 61287e9f023ef064f03b6552704db2334ddb8592 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 29 Jan 2019 15:34:20 +0300 Subject: [PATCH 07/17] MediaBox: fixed cache cleaning --- Postbox/MediaBox.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Postbox/MediaBox.swift b/Postbox/MediaBox.swift index 04a3bbeca0..2e832486f0 100644 --- a/Postbox/MediaBox.swift +++ b/Postbox/MediaBox.swift @@ -833,7 +833,7 @@ public final class MediaBox { } if let value = (try? url.resourceValues(forKeys: Set([.fileSizeKey])))?.fileSize, value != 0 { - paths.append(url.lastPathComponent) + paths.append("cache/" + url.lastPathComponent) cacheResult += Int64(value) } } From 57821c9c362f56e8ea652b51086fda516e791999 Mon Sep 17 00:00:00 2001 From: Peter Iakovlev Date: Fri, 1 Feb 2019 14:41:02 +0400 Subject: [PATCH 08/17] Fix typo --- Postbox/SharedAccountMediaManager.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Postbox/SharedAccountMediaManager.swift b/Postbox/SharedAccountMediaManager.swift index 7cc4d263d5..d96aa0232b 100644 --- a/Postbox/SharedAccountMediaManager.swift +++ b/Postbox/SharedAccountMediaManager.swift @@ -16,10 +16,10 @@ final class SharedAccountMediaManager { } func resourceData(resourceId: MediaResourceId) -> Data? { - return try? Data(contentsOf: URL(fileURLWithPath: self.pathForId(id))) + return try? Data(contentsOf: URL(fileURLWithPath: self.pathForId(resourceId))) } func storeResourceData(resourceId: MediaResourceId, data: Data) { - let _ = try? data.write(to: URL(fileURLWithPath: self.pathForId(id))) + let _ = try? data.write(to: URL(fileURLWithPath: self.pathForId(resourceId))) } } From 6754975f9078f4838335bd12db05f133287150af Mon Sep 17 00:00:00 2001 From: Peter <> Date: Fri, 1 Feb 2019 20:14:42 +0400 Subject: [PATCH 09/17] AccountManager: add get/set version --- Postbox/AccountManager.swift | 6 ++++++ Postbox/AccountManagerMetadataTable.swift | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/Postbox/AccountManager.swift b/Postbox/AccountManager.swift index 3ccf7518ac..73ada89fb8 100644 --- a/Postbox/AccountManager.swift +++ b/Postbox/AccountManager.swift @@ -18,6 +18,8 @@ public struct AccountManagerModifier { public let updateSharedData: (ValueBoxKey, (PreferencesEntry?) -> PreferencesEntry?) -> Void public let getAccessChallengeData: () -> PostboxAccessChallengeData public let setAccessChallengeData: (PostboxAccessChallengeData) -> Void + public let getVersion: () -> Int32 + public let setVersion: (Int32) -> Void } final class AccountManagerImpl { @@ -115,6 +117,10 @@ final class AccountManagerImpl { }, setAccessChallengeData: { data in self.currentUpdatedAccessChallengeData = data self.metadataTable.setAccessChallengeData(data) + }, getVersion: { + return self.metadataTable.getVersion() + }, setVersion: { version in + self.metadataTable.setVersion(version) }) let result = f(transaction) diff --git a/Postbox/AccountManagerMetadataTable.swift b/Postbox/AccountManagerMetadataTable.swift index ec0df543d5..6b9a3a5895 100644 --- a/Postbox/AccountManagerMetadataTable.swift +++ b/Postbox/AccountManagerMetadataTable.swift @@ -143,6 +143,7 @@ private enum MetadataKey: Int64 { case currentAccountId = 0 case currentAuthAccount = 1 case accessChallenge = 2 + case version = 3 } final class AccountManagerMetadataTable: Table { @@ -156,6 +157,21 @@ final class AccountManagerMetadataTable: Table { return result } + func getVersion() -> Int32 { + if let value = self.valueBox.get(self.table, key: self.key(.version)) { + var id: Int32 = 0 + value.read(&id, offset: 0, length: 4) + return id + } else { + return 0 + } + } + + func setVersion(_ version: Int32) { + var value: Int32 = version + self.valueBox.set(self.table, key: self.key(.version), value: MemoryBuffer(memory: &value, capacity: 4, length: 4, freeWhenDone: false)) + } + func getCurrentAccountId() -> AccountRecordId? { if let value = self.valueBox.get(self.table, key: self.key(.currentAccountId)) { var id: Int64 = 0 From 69e6f805b3c6e89354a329871ce7389f8dd4528f Mon Sep 17 00:00:00 2001 From: Peter <> Date: Fri, 1 Feb 2019 21:43:40 +0400 Subject: [PATCH 10/17] Fix merge error --- Postbox/AccountManager.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Postbox/AccountManager.swift b/Postbox/AccountManager.swift index 522daba077..00a33bf2f4 100644 --- a/Postbox/AccountManager.swift +++ b/Postbox/AccountManager.swift @@ -330,6 +330,7 @@ public final class AccountManager { public let temporarySessionId: Int64 public init(basePath: String) { + self.basePath = basePath var temporarySessionId: Int64 = 0 arc4random_buf(&temporarySessionId, 8) self.temporarySessionId = temporarySessionId From e1e594e0d3a60c9e6ec90b8a0f0226537a10ee13 Mon Sep 17 00:00:00 2001 From: Peter Iakovlev Date: Tue, 5 Feb 2019 16:28:10 +0400 Subject: [PATCH 11/17] Add mediaBox to AccountManager --- Postbox/AccountManager.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Postbox/AccountManager.swift b/Postbox/AccountManager.swift index 00a33bf2f4..79c8a36d18 100644 --- a/Postbox/AccountManager.swift +++ b/Postbox/AccountManager.swift @@ -27,7 +27,6 @@ final class AccountManagerImpl { private let basePath: String private let temporarySessionId: Int64 private let valueBox: ValueBox - private let sharedMediaManager: SharedAccountMediaManager private var tables: [Table] = [] @@ -48,7 +47,6 @@ final class AccountManagerImpl { fileprivate init(queue: Queue, basePath: String, temporarySessionId: Int64) { self.queue = queue self.basePath = basePath - self.sharedMediaManager = SharedAccountMediaManager(basePath: basePath + "/media") self.temporarySessionId = temporarySessionId let _ = try? FileManager.default.createDirectory(atPath: basePath, withIntermediateDirectories: true, attributes: nil) self.valueBox = SqliteValueBox(basePath: basePath + "/db", queue: queue) @@ -325,6 +323,7 @@ final class AccountManagerImpl { public final class AccountManager { public let basePath: String + public let mediaBox: MediaBox private let queue = Queue() private let impl: QueueLocalObject public let temporarySessionId: Int64 @@ -338,6 +337,7 @@ public final class AccountManager { self.impl = QueueLocalObject(queue: queue, generate: { return AccountManagerImpl(queue: queue, basePath: basePath, temporarySessionId: temporarySessionId) }) + self.mediaBox = MediaBox(basePath: basePath + "/media") } public func transaction(ignoreDisabled: Bool = false, _ f: @escaping (AccountManagerModifier) -> T) -> Signal { From 0b8a19f5c9766f4a7c418e9d9ec2f0472b956e9e Mon Sep 17 00:00:00 2001 From: Peter <> Date: Tue, 12 Feb 2019 20:41:48 +0400 Subject: [PATCH 12/17] Move noticeTable to AccountManager --- Postbox.xcodeproj/project.pbxproj | 2 +- Postbox/AccountManager.swift | 62 +++++++++++++++++++++++++++++++ Postbox/NoticeEntryView.swift | 16 ++++---- Postbox/NoticeTable.swift | 11 ++++++ Postbox/Postbox.swift | 9 +++++ Postbox/Views.swift | 11 ------ 6 files changed, 91 insertions(+), 20 deletions(-) diff --git a/Postbox.xcodeproj/project.pbxproj b/Postbox.xcodeproj/project.pbxproj index c88f96e85e..ef606e8c71 100644 --- a/Postbox.xcodeproj/project.pbxproj +++ b/Postbox.xcodeproj/project.pbxproj @@ -708,6 +708,7 @@ D05D8B30218F1D3D0064586F /* AccountSharedData.swift */, D0575AE21E9ECBB2006F2541 /* AccessChallengeDataView.swift */, D000CAD922006C6C0011B15D /* SharedAccountMediaManager.swift */, + D06ECFC420B796DC00C576C2 /* NoticeEntryView.swift */, ); name = "Account Manager"; sourceTree = ""; @@ -895,7 +896,6 @@ D0FC19542020CB7700FEDBB2 /* PeerGroupStateView.swift */, D0BE3033206026C800FBE6D8 /* MessagesView.swift */, D048B4AE20A5EEAE00C79D31 /* AdditionalChatListItemsView.swift */, - D06ECFC420B796DC00C576C2 /* NoticeEntryView.swift */, D037178A20D923CA004773C8 /* CachedItemView.swift */, D029AA512167BC22006D4947 /* OrderedContactsView.swift */, D039FB1A21714D9800BD1BAD /* PeerPresencesView.swift */, diff --git a/Postbox/AccountManager.swift b/Postbox/AccountManager.swift index 79c8a36d18..f464635b8e 100644 --- a/Postbox/AccountManager.swift +++ b/Postbox/AccountManager.swift @@ -20,6 +20,8 @@ public struct AccountManagerModifier { public let setAccessChallengeData: (PostboxAccessChallengeData) -> Void public let getVersion: () -> Int32 public let setVersion: (Int32) -> Void + public let getNotice: (NoticeEntryKey) -> NoticeEntry? + public let setNotice: (NoticeEntryKey, NoticeEntry?) -> Void } final class AccountManagerImpl { @@ -33,15 +35,18 @@ final class AccountManagerImpl { private let metadataTable: AccountManagerMetadataTable private let recordTable: AccountManagerRecordTable let sharedDataTable: AccountManagerSharedDataTable + let noticeTable: NoticeTable private var currentRecordOperations: [AccountManagerRecordOperation] = [] private var currentMetadataOperations: [AccountManagerMetadataOperation] = [] private var currentUpdatedSharedDataKeys = Set() + private var currentUpdatedNoticeEntryKeys = Set() private var currentUpdatedAccessChallengeData: PostboxAccessChallengeData? private var recordsViews = Bag<(MutableAccountRecordsView, ValuePipe)>() private var sharedDataViews = Bag<(MutableAccountSharedDataView, ValuePipe)>() + private var noticeEntryViews = Bag<(MutableNoticeEntryView, ValuePipe)>() private var accessChallengeDataViews = Bag<(MutableAccessChallengeDataView, ValuePipe)>() fileprivate init(queue: Queue, basePath: String, temporarySessionId: Int64) { @@ -54,12 +59,14 @@ final class AccountManagerImpl { self.metadataTable = AccountManagerMetadataTable(valueBox: self.valueBox, table: AccountManagerMetadataTable.tableSpec(0)) self.recordTable = AccountManagerRecordTable(valueBox: self.valueBox, table: AccountManagerRecordTable.tableSpec(1)) self.sharedDataTable = AccountManagerSharedDataTable(valueBox: self.valueBox, table: AccountManagerSharedDataTable.tableSpec(2)) + self.noticeTable = NoticeTable(valueBox: self.valueBox, table: NoticeTable.tableSpec(3)) postboxLog("AccountManager: currentAccountId = \(String(describing: self.metadataTable.getCurrentAccountId()))") self.tables.append(self.metadataTable) self.tables.append(self.recordTable) self.tables.append(self.sharedDataTable) + self.tables.append(self.noticeTable) } deinit { @@ -119,6 +126,11 @@ final class AccountManagerImpl { return self.metadataTable.getVersion() }, setVersion: { version in self.metadataTable.setVersion(version) + }, getNotice: { key in + self.noticeTable.get(key: key) + }, setNotice: { key, value in + self.noticeTable.set(key: key, value: value) + self.currentUpdatedNoticeEntryKeys.insert(key) }) let result = f(transaction) @@ -151,6 +163,14 @@ final class AccountManagerImpl { } } + if !self.currentUpdatedNoticeEntryKeys.isEmpty { + for (view, pipe) in self.noticeEntryViews.copyItems() { + if view.replay(accountManagerImpl: self, updatedKeys: self.currentUpdatedNoticeEntryKeys) { + pipe.putNext(NoticeEntryView(view)) + } + } + } + if let data = self.currentUpdatedAccessChallengeData { for (view, pipe) in self.accessChallengeDataViews.copyItems() { if view.replay(updatedData: data) { @@ -162,6 +182,7 @@ final class AccountManagerImpl { self.currentRecordOperations.removeAll() self.currentMetadataOperations.removeAll() self.currentUpdatedSharedDataKeys.removeAll() + self.currentUpdatedNoticeEntryKeys.removeAll() self.currentUpdatedAccessChallengeData = nil for table in self.tables { @@ -183,6 +204,13 @@ final class AccountManagerImpl { |> switchToLatest } + fileprivate func noticeEntry(key: NoticeEntryKey) -> Signal { + return self.transaction(ignoreDisabled: false, { transaction -> Signal in + return self.noticeEntryInternal(transaction: transaction, key: key) + }) + |> switchToLatest + } + fileprivate func accessChallengeData() -> Signal { return self.transaction(ignoreDisabled: false, { transaction -> Signal in return self.accessChallengeDataInternal(transaction: transaction) @@ -232,6 +260,26 @@ final class AccountManagerImpl { } } + private func noticeEntryInternal(transaction: AccountManagerModifier, key: NoticeEntryKey) -> Signal { + let mutableView = MutableNoticeEntryView(accountManagerImpl: self, key: key) + let pipe = ValuePipe() + let index = self.noticeEntryViews.add((mutableView, pipe)) + + let queue = self.queue + return (.single(NoticeEntryView(mutableView)) + |> then(pipe.signal())) + |> `catch` { _ -> Signal in + return .complete() + } + |> afterDisposed { [weak self] in + queue.async { + if let strongSelf = self { + strongSelf.noticeEntryViews.remove(index) + } + } + } + } + private func accessChallengeDataInternal(transaction: AccountManagerModifier) -> Signal { let mutableView = MutableAccessChallengeDataView(data: transaction.getAccessChallengeData()) let pipe = ValuePipe() @@ -382,6 +430,20 @@ public final class AccountManager { } } + public func noticeEntry(key: NoticeEntryKey) -> Signal { + return Signal { subscriber in + let disposable = MetaDisposable() + self.impl.with { impl in + disposable.set(impl.noticeEntry(key: key).start(next: { next in + subscriber.putNext(next) + }, completed: { + subscriber.putCompletion() + })) + } + return disposable + } + } + public func accessChallengeData() -> Signal { return Signal { subscriber in let disposable = MetaDisposable() diff --git a/Postbox/NoticeEntryView.swift b/Postbox/NoticeEntryView.swift index 3de5e37885..fcee321560 100644 --- a/Postbox/NoticeEntryView.swift +++ b/Postbox/NoticeEntryView.swift @@ -1,28 +1,28 @@ import Foundation -final class MutableNoticeEntryView: MutablePostboxView { +final class MutableNoticeEntryView { private let key: NoticeEntryKey fileprivate var value: NoticeEntry? - init(postbox: Postbox, key: NoticeEntryKey) { + init(accountManagerImpl: AccountManagerImpl, key: NoticeEntryKey) { self.key = key - self.value = postbox.noticeTable.get(key: key) + self.value = accountManagerImpl.noticeTable.get(key: key) } - func replay(postbox: Postbox, transaction: PostboxTransaction) -> Bool { - if transaction.updatedNoticeEntryKeys.contains(self.key) { - self.value = postbox.noticeTable.get(key: key) + func replay(accountManagerImpl: AccountManagerImpl, updatedKeys: Set) -> Bool { + if updatedKeys.contains(self.key) { + self.value = accountManagerImpl.noticeTable.get(key: self.key) return true } return false } - func immutableView() -> PostboxView { + func immutableView() -> NoticeEntryView { return NoticeEntryView(self) } } -public final class NoticeEntryView: PostboxView { +public final class NoticeEntryView { public let value: NoticeEntry? init(_ view: MutableNoticeEntryView) { diff --git a/Postbox/NoticeTable.swift b/Postbox/NoticeTable.swift index 1e1a4e7f90..24922e6f1b 100644 --- a/Postbox/NoticeTable.swift +++ b/Postbox/NoticeTable.swift @@ -41,6 +41,17 @@ final class NoticeTable: Table { return ValueBoxTable(id: id, keyType: .binary) } + func getAll() -> [ValueBoxKey: NoticeEntry] { + var result: [ValueBoxKey: NoticeEntry] = [:] + self.valueBox.scan(self.table, values: { key, value in + if let object = PostboxDecoder(buffer: value).decodeRootObject() as? NoticeEntry { + result[key] = object + } + return true + }) + return result + } + func get(key: NoticeEntryKey) -> NoticeEntry? { if let cached = self.cachedEntries[key] { return cached.entry diff --git a/Postbox/Postbox.swift b/Postbox/Postbox.swift index b13d906285..1f4b3cdfab 100644 --- a/Postbox/Postbox.swift +++ b/Postbox/Postbox.swift @@ -816,6 +816,15 @@ public final class Transaction { } } + public func getAllNoticeEntries() -> [ValueBoxKey: NoticeEntry] { + assert(!self.disposed) + if let postbox = self.postbox { + return postbox.noticeTable.getAll() + } else { + return [:] + } + } + public func getNoticeEntry(key: NoticeEntryKey) -> PostboxCoding? { assert(!self.disposed) if let postbox = self.postbox { diff --git a/Postbox/Views.swift b/Postbox/Views.swift index 5903acb6d9..4583d5d9de 100644 --- a/Postbox/Views.swift +++ b/Postbox/Views.swift @@ -24,7 +24,6 @@ public enum PostboxViewKey: Hashable { case localMessageTag(LocalMessageTags) case messages(Set) case additionalChatListItems - case noticeEntry(NoticeEntryKey) case cachedItem(ItemCacheEntryId) case orderedContacts case peerPresences(peerIds: Set) @@ -77,8 +76,6 @@ public enum PostboxViewKey: Hashable { return 10 case .additionalChatListItems: return 11 - case let .noticeEntry(key): - return key.hashValue case let .cachedItem(id): return id.hashValue case .orderedContacts: @@ -228,12 +225,6 @@ public enum PostboxViewKey: Hashable { } else { return false } - case let .noticeEntry(key): - if case .noticeEntry(key) = rhs { - return true - } else { - return false - } case let .cachedItem(id): if case .cachedItem(id) = rhs { return true @@ -304,8 +295,6 @@ func postboxViewForKey(postbox: Postbox, key: PostboxViewKey) -> MutablePostboxV return MutableMessagesView(postbox: postbox, ids: ids) case .additionalChatListItems: return MutableAdditionalChatListItemsView(postbox: postbox) - case let .noticeEntry(key): - return MutableNoticeEntryView(postbox: postbox, key: key) case let .cachedItem(id): return MutableCachedItemView(postbox: postbox, id: id) case .orderedContacts: From f027996caa7b7b4174dcb8622d321b143f746628 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 14 Feb 2019 14:14:18 +0400 Subject: [PATCH 13/17] Use on-disk file size for partial files when calculating resource cache usage --- Postbox/FileSize.swift | 13 ++++++++++++- Postbox/MediaBox.swift | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Postbox/FileSize.swift b/Postbox/FileSize.swift index b366746e60..1234e5c60b 100644 --- a/Postbox/FileSize.swift +++ b/Postbox/FileSize.swift @@ -1,6 +1,17 @@ import Foundation -public func fileSize(_ path: String) -> Int? { +public func fileSize(_ path: String, useTotalFileAllocatedSize: Bool = false) -> Int? { + if useTotalFileAllocatedSize { + let url = URL(fileURLWithPath: path) + if let values = (try? url.resourceValues(forKeys: Set([.isRegularFileKey, .totalFileAllocatedSizeKey]))) { + if values.isRegularFile ?? false { + if let fileSize = values.totalFileAllocatedSize { + return fileSize + } + } + } + } + var value = stat() if stat(path, &value) == 0 { return Int(value.st_size) diff --git a/Postbox/MediaBox.swift b/Postbox/MediaBox.swift index 79c3473199..383908c658 100644 --- a/Postbox/MediaBox.swift +++ b/Postbox/MediaBox.swift @@ -762,7 +762,7 @@ public final class MediaBox { let paths = self.storePathsForId(id) if let size = fileSize(paths.complete) { result[wrappedId] = Int64(size) - } else if let size = fileSize(paths.partial) { + } else if let size = fileSize(paths.partial, useTotalFileAllocatedSize: true) { result[wrappedId] = Int64(size) } } From 26323a57e807e48c9e88edc67300c4045aa0d841 Mon Sep 17 00:00:00 2001 From: Peter <> Date: Fri, 15 Feb 2019 18:19:46 +0400 Subject: [PATCH 14/17] MessageHistoryView API update --- Postbox/MessageHistoryView.swift | 95 ++++++++++++++++++-------------- Postbox/Postbox.swift | 2 +- Postbox/ViewTracker.swift | 6 +- 3 files changed, 58 insertions(+), 45 deletions(-) diff --git a/Postbox/MessageHistoryView.swift b/Postbox/MessageHistoryView.swift index fbbb328d00..7fd7cfea33 100644 --- a/Postbox/MessageHistoryView.swift +++ b/Postbox/MessageHistoryView.swift @@ -93,16 +93,24 @@ public struct MessageHistoryViewId: Equatable { } } +public struct MutableMessageHistoryEntryAttributes: Equatable { + public var authorIsContact: Bool + + public init(authorIsContact: Bool) { + self.authorIsContact = authorIsContact + } +} + enum MutableMessageHistoryEntry { case IntermediateMessageEntry(IntermediateMessage, MessageHistoryEntryLocation?, MessageHistoryEntryMonthLocation?) - case MessageEntry(Message, MessageHistoryEntryLocation?, MessageHistoryEntryMonthLocation?) + case MessageEntry(Message, MessageHistoryEntryLocation?, MessageHistoryEntryMonthLocation?, MutableMessageHistoryEntryAttributes) case HoleEntry(MessageHistoryHole, MessageHistoryEntryLocation?, lowerIndex: MessageIndex?) var index: MessageIndex { switch self { case let .IntermediateMessageEntry(message, _, _): return MessageIndex(id: message.id, timestamp: message.timestamp) - case let .MessageEntry(message, _, _): + case let .MessageEntry(message, _, _, _): return MessageIndex(id: message.id, timestamp: message.timestamp) case let .HoleEntry(hole, _, _): return hole.maxIndex @@ -113,7 +121,7 @@ enum MutableMessageHistoryEntry { switch self { case let .IntermediateMessageEntry(message, _, _): return message.tags - case let .MessageEntry(message, _, _): + case let .MessageEntry(message, _, _, _): return message.tags case let .HoleEntry(hole, _, _): return MessageTags(rawValue: hole.tags) @@ -124,8 +132,8 @@ enum MutableMessageHistoryEntry { switch self { case let .IntermediateMessageEntry(message, _, monthLocation): return .IntermediateMessageEntry(message, location, monthLocation) - case let .MessageEntry(message, _, monthLocation): - return .MessageEntry(message, location, monthLocation) + case let .MessageEntry(message, _, monthLocation, attributes): + return .MessageEntry(message, location, monthLocation, attributes) case let .HoleEntry(hole, _, lowerIndex): return .HoleEntry(hole, location, lowerIndex: lowerIndex) } @@ -135,8 +143,8 @@ enum MutableMessageHistoryEntry { switch self { case let .IntermediateMessageEntry(message, location, _): return .IntermediateMessageEntry(message, location, monthLocation) - case let .MessageEntry(message, location, _): - return .MessageEntry(message, location, monthLocation) + case let .MessageEntry(message, location, _, attributes): + return .MessageEntry(message, location, monthLocation, attributes) case .HoleEntry: return self } @@ -164,12 +172,12 @@ enum MutableMessageHistoryEntry { } else { return self } - case let .MessageEntry(message, location, monthLocation): + case let .MessageEntry(message, location, monthLocation, attributes): if let location = location { if MessageIndex(id: message.id, timestamp: message.timestamp) > index { - return .MessageEntry(message, MessageHistoryEntryLocation(index: location.index + 1, count: location.count + 1), monthLocation) + return .MessageEntry(message, MessageHistoryEntryLocation(index: location.index + 1, count: location.count + 1), monthLocation, attributes) } else { - return .MessageEntry(message, MessageHistoryEntryLocation(index: location.index, count: location.count + 1), monthLocation) + return .MessageEntry(message, MessageHistoryEntryLocation(index: location.index, count: location.count + 1), monthLocation, attributes) } } else { return self @@ -204,15 +212,15 @@ enum MutableMessageHistoryEntry { } else { return self } - case let .MessageEntry(message, location, monthLocation): + case let .MessageEntry(message, location, monthLocation, attributes): if let location = location { if MessageIndex(id: message.id, timestamp: message.timestamp) > index { //assert(location.index > 0) //assert(location.count != 0) - return .MessageEntry(message, MessageHistoryEntryLocation(index: location.index - 1, count: location.count - 1), monthLocation) + return .MessageEntry(message, MessageHistoryEntryLocation(index: location.index - 1, count: location.count - 1), monthLocation, attributes) } else { //assert(location.count != 0) - return .MessageEntry(message, MessageHistoryEntryLocation(index: location.index, count: location.count - 1), monthLocation) + return .MessageEntry(message, MessageHistoryEntryLocation(index: location.index, count: location.count - 1), monthLocation, attributes) } } else { return self @@ -225,9 +233,9 @@ enum MutableMessageHistoryEntry { case let .IntermediateMessageEntry(message, location, monthLocation): let updatedMessage = IntermediateMessage(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, timestamp: timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: message.embeddedMediaData, referencedMedia: message.referencedMedia) return .IntermediateMessageEntry(updatedMessage, location, monthLocation) - case let .MessageEntry(message, location, monthLocation): + case let .MessageEntry(message, location, monthLocation, attributes): let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, timestamp: timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, 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, monthLocation) + return .MessageEntry(updatedMessage, location, monthLocation, attributes) case let .HoleEntry(hole, location, lowerIndex): let updatedHole = MessageHistoryHole(stableId: hole.stableId, maxIndex: MessageIndex(id: hole.maxIndex.id, timestamp: timestamp), min: hole.min, tags: hole.tags) return .HoleEntry(updatedHole, location, lowerIndex: lowerIndex) @@ -265,12 +273,12 @@ public struct MessageHistoryEntryMonthLocation: Equatable { } public enum MessageHistoryEntry: Comparable { - case MessageEntry(Message, Bool, MessageHistoryEntryLocation?, MessageHistoryEntryMonthLocation?) + case MessageEntry(Message, Bool, MessageHistoryEntryLocation?, MessageHistoryEntryMonthLocation?, MutableMessageHistoryEntryAttributes) case HoleEntry(MessageHistoryHole, MessageHistoryEntryLocation?) public var index: MessageIndex { switch self { - case let .MessageEntry(message, _, _, _): + case let .MessageEntry(message, _, _, _, _): return MessageIndex(id: message.id, timestamp: message.timestamp) case let .HoleEntry(hole, _): return hole.maxIndex @@ -280,12 +288,12 @@ public enum MessageHistoryEntry: Comparable { public func ==(lhs: MessageHistoryEntry, rhs: MessageHistoryEntry) -> Bool { switch lhs { - case let .MessageEntry(lhsMessage, lhsRead, lhsLocation, lhsMonthLocation): + case let .MessageEntry(lhsMessage, lhsRead, lhsLocation, lhsMonthLocation, lhsAttributes): switch rhs { case .HoleEntry: return false - case let .MessageEntry(rhsMessage, rhsRead, rhsLocation, rhsMonthLocation): - if MessageIndex(lhsMessage) == MessageIndex(rhsMessage) && lhsMessage.flags == rhsMessage.flags && lhsLocation == rhsLocation && lhsRead == rhsRead && lhsMonthLocation != rhsMonthLocation { + case let .MessageEntry(rhsMessage, rhsRead, rhsLocation, rhsMonthLocation, rhsAttributes): + if MessageIndex(lhsMessage) == MessageIndex(rhsMessage) && lhsMessage.flags == rhsMessage.flags && lhsLocation == rhsLocation && lhsRead == rhsRead && lhsMonthLocation == rhsMonthLocation && lhsAttributes == rhsAttributes { return true } return false @@ -466,7 +474,7 @@ private func clipMessages(peerIds: MessageHistoryViewPeerIds, entries: [MutableM var result: [MutableMessageHistoryEntry] = [] for entry in entries { switch entry { - case let .MessageEntry(message, _, _): + case let .MessageEntry(message, _, _, _): if message.id.peerId == associatedId.peerId { if message.id.namespace == associatedId.namespace && message.id.id < associatedId.id { result.append(entry) @@ -884,8 +892,8 @@ final class MutableMessageHistoryView { case let .IntermediateMessageEntry(message, location, monthLocation): self.entries[i] = .IntermediateMessageEntry(message.withUpdatedGroupInfo(groupInfo), location, monthLocation) hasChanges = true - case let .MessageEntry(message, location, monthLocation): - self.entries[i] = .MessageEntry(message.withUpdatedGroupInfo(groupInfo), location, monthLocation) + case let .MessageEntry(message, location, monthLocation, attributes): + self.entries[i] = .MessageEntry(message.withUpdatedGroupInfo(groupInfo), location, monthLocation, attributes) hasChanges = true case .HoleEntry: assertionFailure() @@ -920,7 +928,7 @@ final class MutableMessageHistoryView { if !updatedMedia.isEmpty { for i in 0 ..< self.entries.count { switch self.entries[i] { - case let .MessageEntry(message, location, monthLocation): + case let .MessageEntry(message, location, monthLocation, attributes): var rebuild = false for media in message.media { if let mediaId = media.id, let _ = updatedMedia[mediaId] { @@ -941,7 +949,7 @@ final class MutableMessageHistoryView { } } let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, 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, location, monthLocation) + self.entries[i] = .MessageEntry(updatedMessage, location, monthLocation, attributes) hasChanges = true } case let .IntermediateMessageEntry(message, location, monthLocation): @@ -977,14 +985,14 @@ final class MutableMessageHistoryView { case let .InsertMessage(intermediateMessage): for i in 0 ..< self.entries.count { switch self.entries[i] { - case let .MessageEntry(message, location, monthLocation): + case let .MessageEntry(message, location, monthLocation, attributes): if message.associatedMessageIds.count != message.associatedMessages.count { if message.associatedMessageIds.contains(intermediateMessage.id) && message.associatedMessages[intermediateMessage.id] == nil { var updatedAssociatedMessages = message.associatedMessages let renderedMessage = renderIntermediateMessage(intermediateMessage) updatedAssociatedMessages[intermediateMessage.id] = renderedMessage let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, 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, monthLocation) + self.entries[i] = .MessageEntry(updatedMessage, location, monthLocation, attributes) hasChanges = true } } @@ -1246,10 +1254,10 @@ final class MutableMessageHistoryView { hasChanges = true } else { switch entry { - case let .MessageEntry(message, location, monthLocation): + case let .MessageEntry(message, location, monthLocation, attributes): if let updatedAssociatedMessages = message.associatedMessages.filteredOut(keysIn: ids) { let updatedMessage = Message(stableId: message.stableId, stableVersion: message.stableVersion, id: message.id, globallyUniqueId: message.globallyUniqueId, groupingKey: message.groupingKey, groupInfo: message.groupInfo, timestamp: message.timestamp, flags: message.flags, tags: message.tags, globalTags: message.globalTags, localTags: message.localTags, 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, monthLocation) + self.entries[i] = .MessageEntry(updatedMessage, location, monthLocation, attributes) } default: break @@ -1416,17 +1424,22 @@ final class MutableMessageHistoryView { } } - func render(_ renderIntermediateMessage: (IntermediateMessage) -> Message) { + func render(_ renderIntermediateMessage: (IntermediateMessage) -> Message, postbox: Postbox) { if let earlier = self.earlier, case let .IntermediateMessageEntry(intermediateMessage, location, monthLocation) = earlier { - self.earlier = .MessageEntry(renderIntermediateMessage(intermediateMessage), location, monthLocation) + self.earlier = .MessageEntry(renderIntermediateMessage(intermediateMessage), location, monthLocation, MutableMessageHistoryEntryAttributes(authorIsContact: false)) } if let later = self.later, case let .IntermediateMessageEntry(intermediateMessage, location, monthLocation) = later { - self.later = .MessageEntry(renderIntermediateMessage(intermediateMessage), location, monthLocation) + self.later = .MessageEntry(renderIntermediateMessage(intermediateMessage), location, monthLocation, MutableMessageHistoryEntryAttributes(authorIsContact: false)) } for i in 0 ..< self.entries.count { if case let .IntermediateMessageEntry(intermediateMessage, location, monthLocation) = self.entries[i] { - self.entries[i] = .MessageEntry(renderIntermediateMessage(intermediateMessage), location, monthLocation) + let message = renderIntermediateMessage(intermediateMessage) + var authorIsContact = false + if let author = message.author { + authorIsContact = postbox.contactsTable.isContact(peerId: author.id) + } + self.entries[i] = .MessageEntry(message, location, monthLocation, MutableMessageHistoryEntryAttributes(authorIsContact: authorIsContact)) } } @@ -1584,7 +1597,7 @@ public final class MessageHistoryView { } else { entries.append(.HoleEntry(hole, location)) } - case let .MessageEntry(message, location, monthLocation): + case let .MessageEntry(message, location, monthLocation, attributes): let read: Bool if message.flags.contains(.Incoming) { read = false @@ -1593,7 +1606,7 @@ public final class MessageHistoryView { } else { read = false } - entries.append(.MessageEntry(message, read, location, monthLocation)) + entries.append(.MessageEntry(message, read, location, monthLocation, attributes)) case .IntermediateMessageEntry: assertionFailure("unexpected IntermediateMessageEntry in MessageHistoryView.init()") } @@ -1607,8 +1620,8 @@ public final class MessageHistoryView { } else { entries.append(.HoleEntry(hole, location)) } - case let .MessageEntry(message, location, monthLocation): - entries.append(.MessageEntry(message, false, location, monthLocation)) + case let .MessageEntry(message, location, monthLocation, attributes): + entries.append(.MessageEntry(message, false, location, monthLocation, attributes)) case .IntermediateMessageEntry: assertionFailure("unexpected IntermediateMessageEntry in MessageHistoryView.init()") } @@ -1635,7 +1648,7 @@ public final class MessageHistoryView { } } break - } else if case let .MessageEntry(message, _, _, _) = entries[i] { + } else if case let .MessageEntry(message, _, _, _, _) = entries[i] { if let groupInfo = message.groupInfo { if let groupStart = groupStart, groupStart.1 == groupInfo { } else { @@ -1663,7 +1676,7 @@ public final class MessageHistoryView { } } break - } else if case let .MessageEntry(message, _, _, _) = entries[i] { + } else if case let .MessageEntry(message, _, _, _, _) = entries[i] { if let groupInfo = message.groupInfo { if let groupStart = groupStart, groupStart.1 == groupInfo { } else { @@ -1741,7 +1754,7 @@ public final class MessageHistoryView { } if let _ = maxNamespaceIndex , index + 1 < entries.count { for i in index + 1 ..< entries.count { - if case let .MessageEntry(message, _, _, _) = entries[i], !message.flags.contains(.Incoming) { + if case let .MessageEntry(message, _, _, _, _) = entries[i], !message.flags.contains(.Incoming) { maxNamespaceIndex = MessageIndex(message) } else { break @@ -1775,7 +1788,7 @@ public final class MessageHistoryView { } if let _ = maxIndex, index + 1 < entries.count { for i in index + 1 ..< entries.count { - if case let .MessageEntry(message, _, _, _) = entries[i], !message.flags.contains(.Incoming) { + if case let .MessageEntry(message, _, _, _, _) = entries[i], !message.flags.contains(.Incoming) { maxIndex = MessageIndex(message) } else { break diff --git a/Postbox/Postbox.swift b/Postbox/Postbox.swift index 1f4b3cdfab..796cc83e53 100644 --- a/Postbox/Postbox.swift +++ b/Postbox/Postbox.swift @@ -2673,7 +2673,7 @@ public final class Postbox { return 0 } }) - mutableView.render(self.renderIntermediateMessage) + mutableView.render(self.renderIntermediateMessage, postbox: self) let initialUpdateType: ViewUpdateType if let unreadIndex = unreadIndex { diff --git a/Postbox/ViewTracker.swift b/Postbox/ViewTracker.swift index 7d61a858e6..3c01040e71 100644 --- a/Postbox/ViewTracker.swift +++ b/Postbox/ViewTracker.swift @@ -157,7 +157,7 @@ final class ViewTracker { } if updated { - mutableView.render(self.renderMessage) + mutableView.render(self.renderMessage, postbox: postbox) pipe.putNext((MessageHistoryView(mutableView), updateType)) self.updateTrackedHoles() @@ -274,7 +274,7 @@ final class ViewTracker { if mutableView.refreshDueToExternalTransaction(postbox: postbox) { mutableView.incrementVersion() - mutableView.render(self.renderMessage) + mutableView.render(self.renderMessage, postbox: postbox) pipe.putNext((MessageHistoryView(mutableView), .Generic)) updateTrackedHoles = true @@ -405,7 +405,7 @@ final class ViewTracker { if updated { updateTrackedHoles = true - mutableView.render(self.renderMessage) + mutableView.render(self.renderMessage, postbox: postbox) pipe.putNext((MessageHistoryView(mutableView), updateType)) } From 3a8f1cddbcc107617061baf8bfc95ba6ae32a1f5 Mon Sep 17 00:00:00 2001 From: Peter <> Date: Fri, 15 Feb 2019 23:39:04 +0400 Subject: [PATCH 15/17] MediaBox: remove partial data mmap loading --- Postbox/ManagedFile.swift | 15 +++++++++++++++ Postbox/MediaBox.swift | 13 ++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/Postbox/ManagedFile.swift b/Postbox/ManagedFile.swift index 0b56f3abbd..f6b6468d40 100644 --- a/Postbox/ManagedFile.swift +++ b/Postbox/ManagedFile.swift @@ -25,6 +25,7 @@ public final class ManagedFile { private let mode: ManagedFileMode public init?(queue: Queue, path: String, mode: ManagedFileMode) { + assert(queue.isCurrent()) self.queue = queue self.mode = mode let fileMode: Int32 @@ -63,15 +64,28 @@ public final class ManagedFile { return wrappedRead(self.fd, data, count) } + public func readData(count: Int) -> Data { + assert(self.queue.isCurrent()) + var result = Data(count: count) + result.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer) -> Void in + let readCount = self.read(bytes, count) + assert(readCount == count) + } + return result + } + public func seek(position: Int64) { + assert(self.queue.isCurrent()) lseek(self.fd, position, SEEK_SET) } public func truncate(count: Int64) { + assert(self.queue.isCurrent()) ftruncate(self.fd, count) } public func getSize() -> Int? { + assert(self.queue.isCurrent()) var value = stat() if fstat(self.fd, &value) == 0 { return Int(value.st_size) @@ -81,6 +95,7 @@ public final class ManagedFile { } public func sync() { + assert(self.queue.isCurrent()) fsync(self.fd) } } diff --git a/Postbox/MediaBox.swift b/Postbox/MediaBox.swift index 383908c658..d27ff07b71 100644 --- a/Postbox/MediaBox.swift +++ b/Postbox/MediaBox.swift @@ -524,12 +524,15 @@ public final class MediaBox { return } - let dataDisposable = fileContext.data(range: Int32(range.lowerBound) ..< Int32(range.upperBound), waitUntilAfterInitialFetch: false, next: { result in - if let data = try? Data(contentsOf: URL(fileURLWithPath: result.path), options: .mappedRead) { + let range = Int32(range.lowerBound) ..< Int32(range.upperBound) + + let dataDisposable = fileContext.data(range: range, waitUntilAfterInitialFetch: false, next: { result in + if let file = ManagedFile(queue: self.dataQueue, path: result.path, mode: .read), let fileSize = file.getSize() { if result.complete { - if result.offset + result.size <= data.count { - if data.count >= result.offset + result.size { - let resultData = data.subdata(in: result.offset ..< (result.offset + result.size)) + if result.offset + result.size <= fileSize { + if fileSize >= result.offset + result.size { + file.seek(position: Int64(result.offset)) + let resultData = file.readData(count: result.size) subscriber.putNext(resultData) subscriber.putCompletion() } else { From 7cfc2c8cf868e16e3b077a537937a8570674bd9d Mon Sep 17 00:00:00 2001 From: Peter <> Date: Tue, 19 Feb 2019 13:10:24 +0300 Subject: [PATCH 16/17] AccountManager: added clearNotices --- Postbox/AccountManager.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Postbox/AccountManager.swift b/Postbox/AccountManager.swift index f464635b8e..6d84d61b4f 100644 --- a/Postbox/AccountManager.swift +++ b/Postbox/AccountManager.swift @@ -22,6 +22,7 @@ public struct AccountManagerModifier { public let setVersion: (Int32) -> Void public let getNotice: (NoticeEntryKey) -> NoticeEntry? public let setNotice: (NoticeEntryKey, NoticeEntry?) -> Void + public let clearNotices: () -> Void } final class AccountManagerImpl { @@ -131,6 +132,8 @@ final class AccountManagerImpl { }, setNotice: { key, value in self.noticeTable.set(key: key, value: value) self.currentUpdatedNoticeEntryKeys.insert(key) + }, clearNotices: { + self.noticeTable.clear() }) let result = f(transaction) From 84181a1902aacf4139b0f1c512b3eec7f1675152 Mon Sep 17 00:00:00 2001 From: Peter <> Date: Tue, 19 Feb 2019 17:15:48 +0300 Subject: [PATCH 17/17] Fix markedUnread message counter --- Postbox/ChatListIndexTable.swift | 61 +++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/Postbox/ChatListIndexTable.swift b/Postbox/ChatListIndexTable.swift index 2ed91c1d00..c7c4f1a01e 100644 --- a/Postbox/ChatListIndexTable.swift +++ b/Postbox/ChatListIndexTable.swift @@ -396,37 +396,37 @@ final class ChatListIndexTable: Table { let initialReadState = alteredInitialPeerCombinedReadStates[peerId] ?? postbox.readStateTable.getCombinedState(peerId) let currentReadState = postbox.readStateTable.getCombinedState(peerId) - var initialValue: (Int32, Bool) = (0, false) - var currentValue: (Int32, Bool) = (0, false) + var initialValue: (Int32, Bool, Bool) = (0, false, false) + var currentValue: (Int32, Bool, Bool) = (0, false, false) if addedChatListPeerIds.contains(peerId) { if let currentReadState = currentReadState { - currentValue = (currentReadState.count, currentReadState.isUnread) + currentValue = (currentReadState.count, currentReadState.isUnread, currentReadState.markedUnread) } } else if removedChatListPeerIds.contains(peerId) { if let initialReadState = initialReadState { - initialValue = (initialReadState.count, initialReadState.isUnread) + initialValue = (initialReadState.count, initialReadState.isUnread, initialReadState.markedUnread) } } else { if self.get(peerId: peerId).includedIndex(peerId: peerId) != nil { if let initialReadState = initialReadState { - initialValue = (initialReadState.count, initialReadState.isUnread) + initialValue = (initialReadState.count, initialReadState.isUnread, initialReadState.markedUnread) } if let currentReadState = currentReadState { - currentValue = (currentReadState.count, currentReadState.isUnread) + currentValue = (currentReadState.count, currentReadState.isUnread, currentReadState.markedUnread) } } } - var initialFilteredValue: (Int32, Bool) = initialValue - var currentFilteredValue: (Int32, Bool) = currentValue + var initialFilteredValue: (Int32, Bool, Bool) = initialValue + var currentFilteredValue: (Int32, Bool, Bool) = currentValue if transactionParticipationInTotalUnreadCountUpdates.added.contains(peerId) { - initialFilteredValue = (0, false) + initialFilteredValue = (0, false, false) } else if transactionParticipationInTotalUnreadCountUpdates.removed.contains(peerId) { - currentFilteredValue = (0, false) + currentFilteredValue = (0, false, false) } else { if let notificationSettings = postbox.peerNotificationSettingsTable.getEffective(notificationPeerId), !notificationSettings.isRemovedFromTotalUnreadCount { } else { - initialFilteredValue = (0, false) - currentFilteredValue = (0, false) + initialFilteredValue = (0, false, false) + currentFilteredValue = (0, false, false) } } @@ -443,10 +443,16 @@ final class ChatListIndexTable: Table { if initialValue.1 { absolute.chatCount -= 1 } + if initialValue.2 && initialValue.0 == 0 { + absolute.messageCount -= 1 + } filtered.messageCount -= initialFilteredValue.0 if initialFilteredValue.1 { filtered.chatCount -= 1 } + if initialFilteredValue.2 && initialFilteredValue.0 == 0 { + filtered.messageCount -= 1 + } return (absolute, filtered) }) } @@ -455,6 +461,9 @@ final class ChatListIndexTable: Table { var absolute = absolute var filtered = filtered absolute.messageCount += currentValue.0 + if currentValue.2 && currentValue.0 == 0 { + absolute.messageCount += 1 + } if currentValue.1 { absolute.chatCount += 1 } @@ -462,6 +471,9 @@ final class ChatListIndexTable: Table { if currentFilteredValue.1 { filtered.chatCount += 1 } + if currentFilteredValue.2 && currentFilteredValue.0 == 0 { + filtered.messageCount += 1 + } return (absolute, filtered) }) } @@ -478,7 +490,10 @@ final class ChatListIndexTable: Table { } else { chatDifference = 0 } - let messageDifference = currentValue.0 - initialValue.0 + + let currentUnreadMark: Int32 = currentValue.2 ? 1 : 0 + let initialUnreadMark: Int32 = initialValue.2 ? 1 : 0 + let messageDifference = max(currentValue.0, currentUnreadMark) - max(initialValue.0, initialUnreadMark) let chatFilteredDifference: Int32 if initialFilteredValue.1 != currentFilteredValue.1 { @@ -486,7 +501,9 @@ final class ChatListIndexTable: Table { } else { chatFilteredDifference = 0 } - let messageFilteredDifference = currentFilteredValue.0 - initialFilteredValue.0 + let currentFilteredUnreadMark: Int32 = currentFilteredValue.2 ? 1 : 0 + let initialFilteredUnreadMark: Int32 = initialFilteredValue.2 ? 1 : 0 + let messageFilteredDifference = max(currentFilteredValue.0, currentFilteredUnreadMark) - max(initialFilteredValue.0, initialFilteredUnreadMark) absolute.messageCount += messageDifference absolute.chatCount += chatDifference @@ -516,16 +533,14 @@ final class ChatListIndexTable: Table { totalUnreadState.filteredCounters.removeValue(forKey: tag) } - #if DEBUG - #if targetEnvironment(simulator) + /*#if DEBUG && targetEnvironment(simulator) let reindexedCounts = self.debugReindexUnreadCounts(postbox: postbox) if reindexedCounts != totalUnreadState { print("reindexedCounts \(reindexedCounts) != totalUnreadState \(totalUnreadState)") totalUnreadState = reindexedCounts } - #endif - #endif + #endif*/ if self.metadataTable.getChatListTotalUnreadState() != totalUnreadState { self.metadataTable.setChatListTotalUnreadState(totalUnreadState) @@ -614,10 +629,13 @@ final class ChatListIndexTable: Table { if messageCount < 0 { messageCount = 0 } - state.absoluteCounters[tag]!.messageCount = messageCount if combinedState.isUnread { state.absoluteCounters[tag]!.chatCount += 1 } + if combinedState.markedUnread { + messageCount = max(1, messageCount) + } + state.absoluteCounters[tag]!.messageCount = messageCount } if let notificationSettings = notificationSettings, !notificationSettings.isRemovedFromTotalUnreadCount { @@ -630,10 +648,13 @@ final class ChatListIndexTable: Table { if messageCount < 0 { messageCount = 0 } - state.filteredCounters[tag]!.messageCount = messageCount if combinedState.isUnread { state.filteredCounters[tag]!.chatCount += 1 } + if combinedState.markedUnread { + messageCount = max(1, messageCount) + } + state.filteredCounters[tag]!.messageCount = messageCount } } }