# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
This commit is contained in:
overtake 2019-02-25 14:44:38 +04:00
commit 1d06980431
21 changed files with 590 additions and 405 deletions

View File

@ -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 = "<group>"; };
D001388520BD942B007C9721 /* PostboxUpgrade_16to17.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostboxUpgrade_16to17.swift; sourceTree = "<group>"; };
D003E4E51B38DBDB00C22CBC /* MessageHistoryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageHistoryView.swift; sourceTree = "<group>"; };
D0079F591D592E8B00A27A2C /* ContactTable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactTable.swift; sourceTree = "<group>"; };
@ -703,6 +706,9 @@
D05D8B33218F1EBB0064586F /* AccountManagerSharedDataTable.swift */,
D0BEAF6F1E54BC1E00BD963D /* AccountRecordsView.swift */,
D05D8B30218F1D3D0064586F /* AccountSharedData.swift */,
D0575AE21E9ECBB2006F2541 /* AccessChallengeDataView.swift */,
D000CAD922006C6C0011B15D /* SharedAccountMediaManager.swift */,
D06ECFC420B796DC00C576C2 /* NoticeEntryView.swift */,
);
name = "Account Manager";
sourceTree = "<group>";
@ -873,7 +879,6 @@
D0FA0AC91E780A26005BB9B7 /* PostboxView.swift */,
D0FA0ACC1E781067005BB9B7 /* Views.swift */,
D0F53BF21E794C6700117362 /* PeerChatStateView.swift */,
D0575AE21E9ECBB2006F2541 /* AccessChallengeDataView.swift */,
D0E1D30E1ECA53F900FCEEF1 /* GlobalMessageTagsView.swift */,
D07047A41F3CF63800F6A8D4 /* MessageHistoryTagSummaryView.swift */,
D07047AA1F3DD8D100F6A8D4 /* PendingMessageActionsView.swift */,
@ -891,7 +896,6 @@
D0FC19542020CB7700FEDBB2 /* PeerGroupStateView.swift */,
D0BE3033206026C800FBE6D8 /* MessagesView.swift */,
D048B4AE20A5EEAE00C79D31 /* AdditionalChatListItemsView.swift */,
D06ECFC420B796DC00C576C2 /* NoticeEntryView.swift */,
D037178A20D923CA004773C8 /* CachedItemView.swift */,
D029AA512167BC22006D4947 /* OrderedContactsView.swift */,
D039FB1A21714D9800BD1BAD /* PeerPresencesView.swift */,
@ -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 */,

View File

@ -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 {

View File

@ -10,9 +10,19 @@ 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
public let getSharedData: (ValueBoxKey) -> PreferencesEntry?
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
public let getNotice: (NoticeEntryKey) -> NoticeEntry?
public let setNotice: (NoticeEntryKey, NoticeEntry?) -> Void
public let clearNotices: () -> Void
}
final class AccountManagerImpl {
@ -26,14 +36,19 @@ 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<ValueBoxKey>()
private var currentUpdatedNoticeEntryKeys = Set<NoticeEntryKey>()
private var currentUpdatedAccessChallengeData: PostboxAccessChallengeData?
private var recordsViews = Bag<(MutableAccountRecordsView, ValuePipe<AccountRecordsView>)>()
private var sharedDataViews = Bag<(MutableAccountSharedDataView, ValuePipe<AccountSharedDataView>)>()
private var noticeEntryViews = Bag<(MutableNoticeEntryView, ValuePipe<NoticeEntryView>)>()
private var accessChallengeDataViews = Bag<(MutableAccessChallengeDataView, ValuePipe<AccessChallengeDataView>)>()
fileprivate init(queue: Queue, basePath: String, temporarySessionId: Int64) {
self.queue = queue
@ -45,17 +60,21 @@ 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 {
assert(self.queue.isCurrent())
}
fileprivate func transaction<T>(_ f: @escaping (AccountManagerModifier) -> T) -> Signal<T, NoError> {
fileprivate func transaction<T>(ignoreDisabled: Bool, _ f: @escaping (AccountManagerModifier) -> T) -> Signal<T, NoError> {
return Signal { subscriber in
self.queue.justDispatch {
self.valueBox.begin()
@ -77,6 +96,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)
@ -87,6 +118,22 @@ 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)
}, getVersion: {
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)
}, clearNotices: {
self.noticeTable.clear()
})
let result = f(transaction)
@ -119,9 +166,27 @@ 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) {
pipe.putNext(AccessChallengeDataView(view))
}
}
}
self.currentRecordOperations.removeAll()
self.currentMetadataOperations.removeAll()
self.currentUpdatedSharedDataKeys.removeAll()
self.currentUpdatedNoticeEntryKeys.removeAll()
self.currentUpdatedAccessChallengeData = nil
for table in self.tables {
table.beforeCommit()
@ -129,23 +194,37 @@ final class AccountManagerImpl {
}
fileprivate func accountRecords() -> Signal<AccountRecordsView, NoError> {
return self.transaction { transaction -> Signal<AccountRecordsView, NoError> in
return self.transaction(ignoreDisabled: false, { transaction -> Signal<AccountRecordsView, NoError> in
return self.accountRecordsInternal(transaction: transaction)
}
})
|> switchToLatest
}
fileprivate func sharedData(keys: Set<ValueBoxKey>) -> Signal<AccountSharedDataView, NoError> {
return self.transaction { transaction -> Signal<AccountSharedDataView, NoError> in
return self.transaction(ignoreDisabled: false, { transaction -> Signal<AccountSharedDataView, NoError> in
return self.sharedDataInternal(transaction: transaction, keys: keys)
}
})
|> switchToLatest
}
fileprivate func noticeEntry(key: NoticeEntryKey) -> Signal<NoticeEntryView, NoError> {
return self.transaction(ignoreDisabled: false, { transaction -> Signal<NoticeEntryView, NoError> in
return self.noticeEntryInternal(transaction: transaction, key: key)
})
|> switchToLatest
}
fileprivate func accessChallengeData() -> Signal<AccessChallengeDataView, NoError> {
return self.transaction(ignoreDisabled: false, { transaction -> Signal<AccessChallengeDataView, NoError> in
return self.accessChallengeDataInternal(transaction: transaction)
})
|> switchToLatest
}
private func accountRecordsInternal(transaction: AccountManagerModifier) -> Signal<AccountRecordsView, NoError> {
let mutableView = MutableAccountRecordsView(getRecords: {
return self.recordTable.getRecords()
}, currentId: self.metadataTable.getCurrentAccountId())
}, currentId: self.metadataTable.getCurrentAccountId(), currentAuth: self.metadataTable.getCurrentAuthAccount())
let pipe = ValuePipe<AccountRecordsView>()
let index = self.recordsViews.add((mutableView, pipe))
@ -184,8 +263,48 @@ final class AccountManagerImpl {
}
}
private func noticeEntryInternal(transaction: AccountManagerModifier, key: NoticeEntryKey) -> Signal<NoticeEntryView, NoError> {
let mutableView = MutableNoticeEntryView(accountManagerImpl: self, key: key)
let pipe = ValuePipe<NoticeEntryView>()
let index = self.noticeEntryViews.add((mutableView, pipe))
let queue = self.queue
return (.single(NoticeEntryView(mutableView))
|> then(pipe.signal()))
|> `catch` { _ -> Signal<NoticeEntryView, NoError> in
return .complete()
}
|> afterDisposed { [weak self] in
queue.async {
if let strongSelf = self {
strongSelf.noticeEntryViews.remove(index)
}
}
}
}
private func accessChallengeDataInternal(transaction: AccountManagerModifier) -> Signal<AccessChallengeDataView, NoError> {
let mutableView = MutableAccessChallengeDataView(data: transaction.getAccessChallengeData())
let pipe = ValuePipe<AccessChallengeDataView>()
let index = self.accessChallengeDataViews.add((mutableView, pipe))
let queue = self.queue
return (.single(AccessChallengeDataView(mutableView))
|> then(pipe.signal()))
|> `catch` { _ -> Signal<AccessChallengeDataView, NoError> 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 {
@ -211,7 +330,7 @@ final class AccountManagerImpl {
}
return signal
}
})
|> switchToLatest
|> distinctUntilChanged(isEqual: { lhs, rhs in
if let lhs = lhs, let rhs = rhs {
@ -237,7 +356,7 @@ final class AccountManagerImpl {
func allocatedTemporaryAccountId() -> Signal<AccountRecordId, NoError> {
let temporarySessionId = self.temporarySessionId
return self.transaction { transaction -> Signal<AccountRecordId, NoError> in
return self.transaction(ignoreDisabled: false, { transaction -> Signal<AccountRecordId, NoError> in
let id = generateAccountRecordId()
transaction.updateRecord(id, { _ in
@ -245,7 +364,7 @@ final class AccountManagerImpl {
})
return .single(id)
}
})
|> switchToLatest
|> distinctUntilChanged(isEqual: { lhs, rhs in
return lhs == rhs
@ -254,11 +373,14 @@ final class AccountManagerImpl {
}
public final class AccountManager {
public let basePath: String
public let mediaBox: MediaBox
private let queue = Queue()
private let impl: QueueLocalObject<AccountManagerImpl>
public let temporarySessionId: Int64
fileprivate init(basePath: String) {
public init(basePath: String) {
self.basePath = basePath
var temporarySessionId: Int64 = 0
arc4random_buf(&temporarySessionId, 8)
self.temporarySessionId = temporarySessionId
@ -266,13 +388,14 @@ 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<T>(_ f: @escaping (AccountManagerModifier) -> T) -> Signal<T, NoError> {
public func transaction<T>(ignoreDisabled: Bool = false, _ f: @escaping (AccountManagerModifier) -> T) -> Signal<T, NoError> {
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()
@ -310,6 +433,34 @@ public final class AccountManager {
}
}
public func noticeEntry(key: NoticeEntryKey) -> Signal<NoticeEntryView, NoError> {
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<AccessChallengeDataView, NoError> {
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()
@ -338,11 +489,3 @@ public final class AccountManager {
}
}
}
public func accountManager(basePath: String) -> Signal<AccountManager, NoError> {
return Signal { subscriber in
subscriber.putNext(AccountManager(basePath: basePath))
subscriber.putCompletion()
return EmptyDisposable
}
}

View File

@ -1,11 +1,149 @@
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]
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
case accessChallenge = 2
case version = 3
}
final class AccountManagerMetadataTable: Table {
@ -19,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
@ -34,4 +187,41 @@ 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))
}
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())
})
}
}

View File

@ -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<ValueBoxKey>) {
func set(key: ValueBoxKey, value: PreferencesEntry?, updatedKeys: inout Set<ValueBoxKey>) {
if let value = value {
if let current = self.get(key: key), current.isEqual(to: value) {
return

View File

@ -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
}
}

View File

@ -1,12 +1,8 @@
import Foundation
public protocol AccountSharedData: PostboxCoding {
func isEqual(to other: AccountSharedData) -> Bool
}
final class MutableAccountSharedDataView {
private let keys: Set<ValueBoxKey>
fileprivate var entries: [ValueBoxKey: AccountSharedData] = [:]
fileprivate var entries: [ValueBoxKey: PreferencesEntry] = [:]
init(accountManagerImpl: AccountManagerImpl, keys: Set<ValueBoxKey>) {
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

View File

@ -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
}
}
}

View File

@ -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)

View File

@ -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<Int8>) -> 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)
}
}

View File

@ -525,12 +525,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 {
@ -763,7 +766,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)
}
}
@ -774,13 +777,13 @@ public final class MediaBox {
}
}
public func collectOtherResourceUsage(excludeIds: Set<WrappedMediaResourceId>) -> Signal<(Int64, [String], Int64), NoError> {
public func collectOtherResourceUsage(excludeIds: Set<WrappedMediaResourceId>, combinedExcludeIds: Set<WrappedMediaResourceId>) -> Signal<(Int64, [String], Int64), NoError> {
return Signal { subscriber in
self.dataQueue.async {
var result: Int64 = 0
var excludeNames = Set<String>()
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)
@ -819,10 +822,22 @@ public final class MediaBox {
var cacheResult: Int64 = 0
var excludePrefixes = Set<String>()
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("cache/" + url.lastPathComponent)
cacheResult += Int64(value)
}
}
@ -842,13 +857,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

View File

@ -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
@ -186,7 +194,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 {
@ -205,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
@ -226,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)
@ -266,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
@ -281,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
@ -467,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)
@ -885,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()
@ -921,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] {
@ -942,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):
@ -978,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
}
}
@ -1247,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
@ -1417,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))
}
}
@ -1585,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
@ -1594,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()")
}
@ -1608,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()")
}
@ -1636,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 {
@ -1664,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 {
@ -1742,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
@ -1776,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

View File

@ -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

View File

@ -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<NoticeEntryKey>) -> 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) {

View File

@ -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

View File

@ -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>], [MediaId: Media], MessageIndex?) {
assert(!self.disposed)
if let postbox = self.postbox {
@ -830,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 {
@ -1078,7 +1073,6 @@ public final class Postbox {
private var currentItemCollectionInfosOperations: [ItemCollectionInfosOperation] = []
private var currentUpdatedPeerChatStates = Set<PeerId>()
private var currentUpdatedPeerGroupStates = Set<PeerGroupId>()
private var currentUpdatedAccessChallengeData: PostboxAccessChallengeData?
private var currentPendingMessageActionsOperations: [PendingMessageActionsOperation] = []
private var currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32] = [:]
private var currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey : MessageHistoryTagNamespaceSummary] = [:]
@ -1937,7 +1931,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 +1980,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()
@ -2680,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 {
@ -2889,7 +2882,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)
@ -3327,11 +3323,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 {

View File

@ -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<PostboxAccess, NoError> {
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 {
}
}
}

View File

@ -25,7 +25,6 @@ final class PostboxTransaction {
let currentItemCollectionInfosOperations: [ItemCollectionInfosOperation]
let currentUpdatedPeerChatStates: Set<PeerId>
let currentUpdatedPeerGroupStates: Set<PeerGroupId>
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<PeerId>, currentUpdatedPeerGroupStates: Set<PeerGroupId>, updatedAccessChallengeData: PostboxAccessChallengeData?, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set<PeerId>?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set<PeerId>, currentGroupFeedReadStateContext: GroupFeedReadStateUpdateContext, currentInitialPeerGroupIdsBeforeUpdate: [PeerId: WrappedPeerGroupId], replacedAdditionalChatListItems: [PeerId]?, updatedNoticeEntryKeys: Set<NoticeEntryKey>, updatedCacheEntryKeys: Set<ItemCacheEntryId>, 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<PeerId>, currentUpdatedPeerGroupStates: Set<PeerGroupId>, currentGlobalTagsOperations: [GlobalMessageHistoryTagsOperation], currentLocalTagsOperations: [IntermediateMessageHistoryLocalTagsOperation], updatedMedia: [MediaId: Media?], replaceRemoteContactCount: Int32?, replaceContactPeerIds: Set<PeerId>?, currentPendingMessageActionsOperations: [PendingMessageActionsOperation], currentUpdatedMessageActionsSummaries: [PendingMessageActionsSummaryKey: Int32], currentUpdatedMessageTagSummaries: [MessageHistoryTagsSummaryKey: MessageHistoryTagNamespaceSummary], currentInvalidateMessageTagSummaries: [InvalidatedMessageHistoryTagsSummaryEntryOperation], currentUpdatedPendingPeerNotificationSettings: Set<PeerId>, currentGroupFeedReadStateContext: GroupFeedReadStateUpdateContext, currentInitialPeerGroupIdsBeforeUpdate: [PeerId: WrappedPeerGroupId], replacedAdditionalChatListItems: [PeerId]?, updatedNoticeEntryKeys: Set<NoticeEntryKey>, updatedCacheEntryKeys: Set<ItemCacheEntryId>, 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

View File

@ -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(resourceId)))
}
func storeResourceData(resourceId: MediaResourceId, data: Data) {
let _ = try? data.write(to: URL(fileURLWithPath: self.pathForId(resourceId)))
}
}

View File

@ -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))
}

View File

@ -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<ValueBoxKey>)
case globalMessageTags(globalTag: GlobalMessageTags, position: MessageIndex, count: Int, groupingPredicate: ((Message, Message) -> Bool)?)
case peer(peerId: PeerId, components: PeerViewComponents)
@ -25,7 +24,6 @@ public enum PostboxViewKey: Hashable {
case localMessageTag(LocalMessageTags)
case messages(Set<MessageId>)
case additionalChatListItems
case noticeEntry(NoticeEntryKey)
case cachedItem(ItemCacheEntryId)
case orderedContacts
case peerPresences(peerIds: Set<PeerId>)
@ -44,8 +42,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:
@ -80,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:
@ -129,12 +123,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
@ -237,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
@ -279,8 +261,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):
@ -315,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: