diff --git a/Telegram/SiriIntents/IntentHandler.swift b/Telegram/SiriIntents/IntentHandler.swift index 63704c88dc..57ace753db 100644 --- a/Telegram/SiriIntents/IntentHandler.swift +++ b/Telegram/SiriIntents/IntentHandler.swift @@ -54,7 +54,7 @@ enum IntentHandlingError { @available(iOSApplicationExtension 10.0, iOS 10.0, *) @objc(IntentHandler) -class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessagesIntentHandling, INSetMessageAttributeIntentHandling, INStartAudioCallIntentHandling, INSearchCallHistoryIntentHandling, SelectFriendsIntentHandling { +class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessagesIntentHandling, INSetMessageAttributeIntentHandling, INStartAudioCallIntentHandling, INSearchCallHistoryIntentHandling { private let accountPromise = Promise() private let allAccounts = Promise<[(AccountRecordId, PeerId, Bool)]>() @@ -198,6 +198,8 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag if #available(iOSApplicationExtension 12.0, iOS 12.0, *) { if intent is SelectAvatarFriendsIntent { return AvatarsIntentHandler() + } else if intent is SelectFriendsIntent { + return FriendsIntentHandler() } else { return self } @@ -845,34 +847,21 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag } } -@available(iOSApplicationExtension 10.0, iOS 10.0, *) -@objc(AvatarsIntentHandler) -class AvatarsIntentHandler: NSObject, SelectAvatarFriendsIntentHandling { - private let accountPromise = Promise() +private final class WidgetIntentHandler { private let allAccounts = Promise<[(AccountRecordId, PeerId, Bool)]>() - private let resolvePersonsDisposable = MetaDisposable() - private let actionDisposable = MetaDisposable() private let searchDisposable = MetaDisposable() private var rootPath: String? - private var accountManager: AccountManager? private var encryptionParameters: ValueBoxEncryptionParameters? private var appGroupUrl: URL? - override init() { - super.init() - + init() { guard let appBundleIdentifier = Bundle.main.bundleIdentifier, let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else { return } let baseAppBundleId = String(appBundleIdentifier[.. take(1) - |> map { view -> [(AccountRecordId, PeerId, Bool)] in - var result: [(AccountRecordId, Int, PeerId, Bool)] = [] - for record in view.records { - let isLoggedOut = record.attributes.contains(where: { attribute in - return attribute is LoggedOutAccountAttribute - }) - if isLoggedOut { - continue - } - /*let isTestingEnvironment = record.attributes.contains(where: { attribute in - if let attribute = attribute as? AccountEnvironmentAttribute, case .test = attribute.environment { - return true - } else { - return false - } - })*/ - var backupData: AccountBackupData? - var sortIndex: Int32 = 0 - for attribute in record.attributes { - if let attribute = attribute as? AccountSortOrderAttribute { - sortIndex = attribute.order - } else if let attribute = attribute as? AccountBackupDataAttribute { - backupData = attribute.data - } - } - if let backupData = backupData { - result.append((record.id, Int(sortIndex), PeerId(backupData.peerId), view.currentRecord?.id == record.id)) + let view = AccountManager.getCurrentRecords(basePath: rootPath + "/accounts-metadata") + + var result: [(AccountRecordId, Int, PeerId, Bool)] = [] + for record in view.records { + let isLoggedOut = record.attributes.contains(where: { attribute in + return attribute is LoggedOutAccountAttribute + }) + if isLoggedOut { + continue + } + var backupData: AccountBackupData? + var sortIndex: Int32 = 0 + for attribute in record.attributes { + if let attribute = attribute as? AccountSortOrderAttribute { + sortIndex = attribute.order + } else if let attribute = attribute as? AccountBackupDataAttribute { + backupData = attribute.data } } - result.sort(by: { lhs, rhs in - if lhs.1 != rhs.1 { - return lhs.1 < rhs.1 - } else { - return lhs.0 < rhs.0 - } - }) - return result.map { record -> (AccountRecordId, PeerId, Bool) in - return (record.0, record.2, record.3) + if let backupData = backupData { + result.append((record.id, Int(sortIndex), PeerId(backupData.peerId), view.currentId == record.id)) + } + } + result.sort(by: { lhs, rhs in + if lhs.1 != rhs.1 { + return lhs.1 < rhs.1 + } else { + return lhs.0 < rhs.0 } }) - - let account: Signal - if let accountCache = accountCache { - account = .single(accountCache) - } else { - account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, apiHash: apiHash, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, voipVersions: [], appData: .single(buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider()), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods, encryptionParameters: encryptionParameters) - |> mapToSignal { account -> Signal in - if let account = account { - switch account { - case .upgrading: - return .complete() - case let .authorized(account): - return applicationSettings(accountManager: accountManager) - |> deliverOnMainQueue - |> map { settings -> Account in - accountCache = account - Logger.shared.logToFile = settings.logging.logToFile - Logger.shared.logToConsole = settings.logging.logToConsole - - Logger.shared.redactSensitiveData = settings.logging.redactSensitiveData - return account - } - case .unauthorized: - return .complete() - } - } else { - return .single(nil) - } - } - |> take(1) - } - self.accountPromise.set(account) + self.allAccounts.set(.single(result.map { record -> (AccountRecordId, PeerId, Bool) in + return (record.0, record.2, record.3) + })) } deinit { - self.resolvePersonsDisposable.dispose() - self.actionDisposable.dispose() self.searchDisposable.dispose() } @available(iOSApplicationExtension 14.0, iOS 14.0, *) - func provideFriendsOptionsCollection(for intent: SelectAvatarFriendsIntent, searchTerm: String?, with completion: @escaping (INObjectCollection?, Error?) -> Void) { - guard let rootPath = self.rootPath, let _ = self.accountManager, let encryptionParameters = self.encryptionParameters else { + func provideFriendsOptionsCollection(searchTerm: String?, with completion: @escaping (INObjectCollection?, Error?) -> Void) { + guard let rootPath = self.rootPath, let encryptionParameters = self.encryptionParameters else { completion(nil, nil) return } if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) { + //TODO:localize let error = NSError(domain: "Locked", code: 1, userInfo: [ NSLocalizedDescriptionKey: "Open Telegram and enter passcode to edit widget." ]) @@ -1071,8 +1014,8 @@ class AvatarsIntentHandler: NSObject, SelectAvatarFriendsIntentHandling { } @available(iOSApplicationExtension 14.0, iOS 14.0, *) - func defaultFriends(for intent: SelectAvatarFriendsIntent) -> [Friend]? { - guard let rootPath = self.rootPath, let _ = self.accountManager, let encryptionParameters = self.encryptionParameters else { + func defaultFriends() -> [Friend]? { + guard let rootPath = self.rootPath, let encryptionParameters = self.encryptionParameters else { return [] } @@ -1140,6 +1083,45 @@ class AvatarsIntentHandler: NSObject, SelectAvatarFriendsIntentHandling { } } +@available(iOSApplicationExtension 10.0, iOS 10.0, *) +@objc(FriendsIntentHandler) +class FriendsIntentHandler: NSObject, SelectFriendsIntentHandling { + private let handler: WidgetIntentHandler + + override init() { + self.handler = WidgetIntentHandler() + + super.init() + } + + @available(iOSApplicationExtension 14.0, iOS 14.0, *) + func provideFriendsOptionsCollection(for intent: SelectFriendsIntent, searchTerm: String?, with completion: @escaping (INObjectCollection?, Error?) -> Void) { + self.handler.provideFriendsOptionsCollection(searchTerm: searchTerm, with: completion) + } +} + +@available(iOSApplicationExtension 10.0, iOS 10.0, *) +@objc(AvatarsIntentHandler) +class AvatarsIntentHandler: NSObject, SelectAvatarFriendsIntentHandling { + private let handler: WidgetIntentHandler + + override init() { + self.handler = WidgetIntentHandler() + + super.init() + } + + @available(iOSApplicationExtension 14.0, iOS 14.0, *) + func provideFriendsOptionsCollection(for intent: SelectAvatarFriendsIntent, searchTerm: String?, with completion: @escaping (INObjectCollection?, Error?) -> Void) { + self.handler.provideFriendsOptionsCollection(searchTerm: searchTerm, with: completion) + } + + @available(iOSApplicationExtension 14.0, iOS 14.0, *) + func defaultFriends(for intent: SelectAvatarFriendsIntent) -> [Friend]? { + return self.handler.defaultFriends() + } +} + private func avatarRoundImage(size: CGSize, source: UIImage) -> UIImage? { UIGraphicsBeginImageContextWithOptions(size, false, 0.0) let context = UIGraphicsGetCurrentContext() diff --git a/Telegram/WidgetKitWidget/TodayViewController.swift b/Telegram/WidgetKitWidget/TodayViewController.swift index 7b228c5262..62138438d1 100644 --- a/Telegram/WidgetKitWidget/TodayViewController.swift +++ b/Telegram/WidgetKitWidget/TodayViewController.swift @@ -199,7 +199,7 @@ struct Provider: IntentTimelineProvider { var mappedMessage: WidgetDataPeer.Message? if let index = transaction.getTopPeerMessageIndex(peerId: peer.id) { if let message = transaction.getMessage(index.id) { - mappedMessage = WidgetDataPeer.Message(message: message) + mappedMessage = WidgetDataPeer.Message(accountPeerId: state.peerId, message: message) } } @@ -357,7 +357,7 @@ struct AvatarsProvider: IntentTimelineProvider { var mappedMessage: WidgetDataPeer.Message? if let index = transaction.getTopPeerMessageIndex(peerId: peer.id) { if let message = transaction.getMessage(index.id) { - mappedMessage = WidgetDataPeer.Message(message: message) + mappedMessage = WidgetDataPeer.Message(accountPeerId: state.peerId, message: message) } } @@ -459,15 +459,20 @@ struct WidgetView: View { } else { dateText = "" } - chatTitle = AnyView(Text(peer.peer.name) + var formattedName = peer.peer.name + if let lastName = peer.peer.lastName { + formattedName.append(" \(lastName)") + } + chatTitle = AnyView(Text(formattedName) .lineLimit(1) .font(Font.system(size: 16.0, weight: .medium, design: .default)) .foregroundColor(.primary)) date = Text(dateText) .font(Font.system(size: 14.0, weight: .regular, design: .default)).foregroundColor(.secondary) case let .preview(index): + let titleText = index == 0 ? "News Channel" : "Duck" dateText = index == 0 ? "9:00" : "8:42" - chatTitle = AnyView(Text("News Channel") + chatTitle = AnyView(Text(titleText) .lineLimit(1) .font(Font.system(size: 16.0, weight: .medium, design: .default)) .foregroundColor(.primary)) @@ -517,6 +522,7 @@ struct WidgetView: View { switch content { case let .peer(peer): if let message = peer.peer.message { + text = message.text //TODO:localize switch message.content { case .text: diff --git a/submodules/Postbox/Sources/AccountManager.swift b/submodules/Postbox/Sources/AccountManager.swift index fe476c2017..189078b39e 100644 --- a/submodules/Postbox/Sources/AccountManager.swift +++ b/submodules/Postbox/Sources/AccountManager.swift @@ -53,6 +53,18 @@ final class AccountManagerImpl { private var noticeEntryViews = Bag<(MutableNoticeEntryView, ValuePipe)>() private var accessChallengeDataViews = Bag<(MutableAccessChallengeDataView, ValuePipe)>() + static func getCurrentRecords(basePath: String) -> (records: [AccountRecord], currentId: AccountRecordId?) { + let atomicStatePath = "\(basePath)/atomic-state" + do { + let data = try Data(contentsOf: URL(fileURLWithPath: atomicStatePath)) + let atomicState = try JSONDecoder().decode(AccountManagerAtomicState.self, from: data) + return (atomicState.records.sorted(by: { $0.key.int64 < $1.key.int64 }).map({ $1 }), atomicState.currentRecordId) + } catch let e { + postboxLog("decode atomic state error: \(e)") + preconditionFailure() + } + } + fileprivate init?(queue: Queue, basePath: String, isTemporary: Bool, temporarySessionId: Int64) { let startTime = CFAbsoluteTimeGetCurrent() @@ -78,7 +90,6 @@ final class AccountManagerImpl { do { let data = try Data(contentsOf: URL(fileURLWithPath: self.atomicStatePath)) do { - let atomicState = try JSONDecoder().decode(AccountManagerAtomicState.self, from: data) self.currentAtomicState = atomicState } catch let e { @@ -311,7 +322,6 @@ final class AccountManagerImpl { return (.single(AccountRecordsView(mutableView)) |> then(pipe.signal())) |> `catch` { _ -> Signal in - return .complete() } |> afterDisposed { [weak self] in queue.async { @@ -331,7 +341,6 @@ final class AccountManagerImpl { return (.single(AccountSharedDataView(mutableView)) |> then(pipe.signal())) |> `catch` { _ -> Signal in - return .complete() } |> afterDisposed { [weak self] in queue.async { @@ -351,7 +360,6 @@ final class AccountManagerImpl { return (.single(NoticeEntryView(mutableView)) |> then(pipe.signal())) |> `catch` { _ -> Signal in - return .complete() } |> afterDisposed { [weak self] in queue.async { @@ -371,7 +379,6 @@ final class AccountManagerImpl { return (.single(AccessChallengeDataView(mutableView)) |> then(pipe.signal())) |> `catch` { _ -> Signal in - return .complete() } |> afterDisposed { [weak self] in queue.async { @@ -457,6 +464,10 @@ public final class AccountManager { private let impl: QueueLocalObject public let temporarySessionId: Int64 + public static func getCurrentRecords(basePath: String) -> (records: [AccountRecord], currentId: AccountRecordId?) { + return AccountManagerImpl.getCurrentRecords(basePath: basePath) + } + public init(basePath: String, isTemporary: Bool) { self.queue = sharedQueue self.basePath = basePath diff --git a/submodules/TelegramUI/Sources/WidgetDataContext.swift b/submodules/TelegramUI/Sources/WidgetDataContext.swift index e668e9dd96..7875e776db 100644 --- a/submodules/TelegramUI/Sources/WidgetDataContext.swift +++ b/submodules/TelegramUI/Sources/WidgetDataContext.swift @@ -208,7 +208,27 @@ final class WidgetDataContext { } var result: [WidgetDataPeer] = [] for (peerId, message) in topMessages.messages { - result.append(WidgetDataPeer(id: peerId.toInt64(), name: "", lastName: "", letters: [], avatarPath: nil, badge: nil, message: WidgetDataPeer.Message(message: message))) + guard let peer = message.peers[message.id.peerId] else { + continue + } + + var name: String = "" + var lastName: String? + + if let user = peer as? TelegramUser { + if let firstName = user.firstName { + name = firstName + lastName = user.lastName + } else if let lastName = user.lastName { + name = lastName + } else if let phone = user.phone, !phone.isEmpty { + name = phone + } + } else { + name = peer.debugDisplayTitle + } + + result.append(WidgetDataPeer(id: peerId.toInt64(), name: name, lastName: lastName, letters: [], avatarPath: nil, badge: nil, message: WidgetDataPeer.Message(accountPeerId: account.peerId, message: message))) } result.sort(by: { lhs, rhs in return lhs.id < rhs.id diff --git a/submodules/WidgetItemsUtils/Sources/WidgetItemsUtils.swift b/submodules/WidgetItemsUtils/Sources/WidgetItemsUtils.swift index f95d7fcdd1..11b3148aae 100644 --- a/submodules/WidgetItemsUtils/Sources/WidgetItemsUtils.swift +++ b/submodules/WidgetItemsUtils/Sources/WidgetItemsUtils.swift @@ -6,7 +6,7 @@ import TelegramCore import WidgetItems public extension WidgetDataPeer.Message { - init(message: Message) { + init(accountPeerId: PeerId, message: Message) { var content: WidgetDataPeer.Message.Content = .text for media in message.media { switch media { @@ -58,11 +58,11 @@ public extension WidgetDataPeer.Message { var author: Author? if let _ = message.peers[message.id.peerId] as? TelegramGroup { if let authorPeer = message.author { - author = Author(isMe: false, title: authorPeer.debugDisplayTitle) + author = Author(isMe: authorPeer.id == accountPeerId, title: authorPeer.debugDisplayTitle) } } else if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .group = channel.info { if let authorPeer = message.author { - author = Author(isMe: false, title: authorPeer.debugDisplayTitle) + author = Author(isMe: authorPeer.id == accountPeerId, title: authorPeer.debugDisplayTitle) } }