mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-16 03:09:56 +00:00
Merge branch 'master' of https://gitlab.com/peter-iakovlev/postbox
# 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:
commit
1d06980431
@ -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 */,
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
25
Postbox/SharedAccountMediaManager.swift
Normal file
25
Postbox/SharedAccountMediaManager.swift
Normal 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)))
|
||||
}
|
||||
}
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user