From ad25c78a5288ff7832192eeff4672023bf8d262b Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 14 Feb 2021 20:45:07 +0400 Subject: [PATCH] Update widget --- Telegram/SiriIntents/IntentHandler.swift | 214 ++++++++++++------ .../WidgetKitWidget/TodayViewController.swift | 4 +- submodules/Postbox/Sources/MediaBox.swift | 15 ++ .../TelegramCore/Sources/RecentPeers.swift | 7 + 4 files changed, 175 insertions(+), 65 deletions(-) diff --git a/Telegram/SiriIntents/IntentHandler.swift b/Telegram/SiriIntents/IntentHandler.swift index 73097a632e..8526e49dd6 100644 --- a/Telegram/SiriIntents/IntentHandler.swift +++ b/Telegram/SiriIntents/IntentHandler.swift @@ -56,7 +56,7 @@ enum IntentHandlingError { @objc(IntentHandler) class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessagesIntentHandling, INSetMessageAttributeIntentHandling, INStartAudioCallIntentHandling, INSearchCallHistoryIntentHandling, SelectFriendsIntentHandling { private let accountPromise = Promise() - private let allAccounts = Promise<[(AccountRecordId, PeerId)]>() + private let allAccounts = Promise<[(AccountRecordId, PeerId, Bool)]>() private let resolvePersonsDisposable = MetaDisposable() private let actionDisposable = MetaDisposable() @@ -114,8 +114,8 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag self.allAccounts.set(accountManager.accountRecords() |> take(1) - |> map { view -> [(AccountRecordId, PeerId)] in - var result: [(AccountRecordId, Int, PeerId)] = [] + |> 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 @@ -140,7 +140,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag } } if let backupData = backupData { - result.append((record.id, Int(sortIndex), PeerId(backupData.peerId))) + result.append((record.id, Int(sortIndex), PeerId(backupData.peerId), view.currentRecord?.id == record.id)) } } result.sort(by: { lhs, rhs in @@ -150,8 +150,8 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag return lhs.0 < rhs.0 } }) - return result.map { record -> (AccountRecordId, PeerId) in - return (record.0, record.2) + return result.map { record -> (AccountRecordId, PeerId, Bool) in + return (record.0, record.2, record.3) } }) @@ -768,15 +768,13 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag return } - if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data) { - if state.isManuallyLocked || state.autolockTimeout != nil { - let error = NSError(domain: "Locked", code: 1, userInfo: [ - NSLocalizedDescriptionKey: "Open Telegram and enter passcode to edit widget." - ]) - - completion(nil, error) - return - } + if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) { + let error = NSError(domain: "Locked", code: 1, userInfo: [ + NSLocalizedDescriptionKey: "Open Telegram and enter passcode to edit widget." + ]) + + completion(nil, error) + return } self.searchDisposable.set((self.allAccounts.get() @@ -785,7 +783,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag |> mapToSignal { accounts -> Signal, Error> in var accountResults: [Signal, Error>] = [] - for (accountId, accountPeerId) in accounts { + for (accountId, accountPeerId, _) in accounts { accountResults.append(accountTransaction(rootPath: rootPath, id: accountId, encryptionParameters: encryptionParameters, transaction: { postbox, transaction -> INObjectSection in var accountTitle: String = "" if let peer = transaction.getPeer(accountPeerId) as? TelegramUser { @@ -818,21 +816,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag } } - var items: [Friend] = [] - for peer in peers { - let path = smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in - return postbox.mediaBox.resourcePath(representation.resource) - } - - let profileImage: INImage? - let image = avatarImage(path: path, peerId: peer.id.toInt64(), accountPeerId: accountPeerId.toInt64(), letters: peer.displayLetters, size: CGSize(width: 50.0, height: 50.0)) - if let data = image.pngData() { - profileImage = INImage(imageData: data) - } else { - profileImage = nil - } - items.append(Friend(identifier: "\(accountId.int64):\(peer.id.toInt64())", display: peer.debugDisplayTitle, subtitle: nil, image: profileImage)) - } + let items = mapPeersToFriends(accountId: accountId, accountPeerId: accountPeerId, mediaBox: postbox.mediaBox, peers: peers) return INObjectSection(title: accountTitle, items: items) }) @@ -862,7 +846,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag @objc(AvatarsIntentHandler) class AvatarsIntentHandler: NSObject, SelectAvatarFriendsIntentHandling { private let accountPromise = Promise() - private let allAccounts = Promise<[(AccountRecordId, PeerId)]>() + private let allAccounts = Promise<[(AccountRecordId, PeerId, Bool)]>() private let resolvePersonsDisposable = MetaDisposable() private let actionDisposable = MetaDisposable() @@ -920,8 +904,8 @@ class AvatarsIntentHandler: NSObject, SelectAvatarFriendsIntentHandling { self.allAccounts.set(accountManager.accountRecords() |> take(1) - |> map { view -> [(AccountRecordId, PeerId)] in - var result: [(AccountRecordId, Int, PeerId)] = [] + |> 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 @@ -946,7 +930,7 @@ class AvatarsIntentHandler: NSObject, SelectAvatarFriendsIntentHandling { } } if let backupData = backupData { - result.append((record.id, Int(sortIndex), PeerId(backupData.peerId))) + result.append((record.id, Int(sortIndex), PeerId(backupData.peerId), view.currentRecord?.id == record.id)) } } result.sort(by: { lhs, rhs in @@ -956,8 +940,8 @@ class AvatarsIntentHandler: NSObject, SelectAvatarFriendsIntentHandling { return lhs.0 < rhs.0 } }) - return result.map { record -> (AccountRecordId, PeerId) in - return (record.0, record.2) + return result.map { record -> (AccountRecordId, PeerId, Bool) in + return (record.0, record.2, record.3) } }) @@ -1007,15 +991,13 @@ class AvatarsIntentHandler: NSObject, SelectAvatarFriendsIntentHandling { return } - if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data) { - if state.isManuallyLocked || state.autolockTimeout != nil { - let error = NSError(domain: "Locked", code: 1, userInfo: [ - NSLocalizedDescriptionKey: "Open Telegram and enter passcode to edit widget." - ]) - - completion(nil, error) - return - } + if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) { + let error = NSError(domain: "Locked", code: 1, userInfo: [ + NSLocalizedDescriptionKey: "Open Telegram and enter passcode to edit widget." + ]) + + completion(nil, error) + return } self.searchDisposable.set((self.allAccounts.get() @@ -1024,7 +1006,7 @@ class AvatarsIntentHandler: NSObject, SelectAvatarFriendsIntentHandling { |> mapToSignal { accounts -> Signal, Error> in var accountResults: [Signal, Error>] = [] - for (accountId, accountPeerId) in accounts { + for (accountId, accountPeerId, _) in accounts { accountResults.append(accountTransaction(rootPath: rootPath, id: accountId, encryptionParameters: encryptionParameters, transaction: { postbox, transaction -> INObjectSection in var accountTitle: String = "" if let peer = transaction.getPeer(accountPeerId) as? TelegramUser { @@ -1057,21 +1039,7 @@ class AvatarsIntentHandler: NSObject, SelectAvatarFriendsIntentHandling { } } - var items: [Friend] = [] - for peer in peers { - let path = smallestImageRepresentation(peer.profileImageRepresentations).flatMap { representation in - return postbox.mediaBox.resourcePath(representation.resource) - } - - let profileImage: INImage? - let image = avatarImage(path: path, peerId: peer.id.toInt64(), accountPeerId: accountPeerId.toInt64(), letters: peer.displayLetters, size: CGSize(width: 50.0, height: 50.0)) - if let data = image.pngData() { - profileImage = INImage(imageData: data) - } else { - profileImage = nil - } - items.append(Friend(identifier: "\(accountId.int64):\(peer.id.toInt64())", display: peer.debugDisplayTitle, subtitle: nil, image: profileImage)) - } + let items = mapPeersToFriends(accountId: accountId, accountPeerId: accountPeerId, mediaBox: postbox.mediaBox, peers: peers) return INObjectSection(title: accountTitle, items: items) }) @@ -1095,6 +1063,72 @@ class AvatarsIntentHandler: NSObject, SelectAvatarFriendsIntentHandling { completion(nil, error) })) } + + @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 { + return [] + } + + if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) { + return [] + } + + var resultItems: [Friend] = [] + + let semaphore: DispatchSemaphore = DispatchSemaphore(value: 0) + let _ = ((self.allAccounts.get() + |> castError(Error.self) + |> take(1) + |> mapToSignal { accounts -> Signal<[Friend], Error> in + var accountResults: [Signal<[Friend], Error>] = [] + + for (accountId, accountPeerId, isActive) in accounts { + if !isActive { + continue + } + accountResults.append(accountTransaction(rootPath: rootPath, id: accountId, encryptionParameters: encryptionParameters, transaction: { postbox, transaction -> [Friend] in + var peers: [Peer] = [] + + for id in getRecentPeers(transaction: transaction) { + if let peer = transaction.getPeer(id), !(peer is TelegramSecretChat), !peer.isDeleted { + peers.append(peer) + } + if peers.count >= 8 { + break + } + } + + let items = mapPeersToFriends(accountId: accountId, accountPeerId: accountPeerId, mediaBox: postbox.mediaBox, peers: peers) + + return items + }) + |> castError(Error.self)) + } + + return combineLatest(accountResults) + |> map { accountResults -> [Friend] in + var combinedResult: [Friend] = [] + for result in accountResults { + combinedResult.append(contentsOf: result) + } + return combinedResult + } + }).start(next: { result in + resultItems = result + semaphore.signal() + }, error: { error in + semaphore.signal() + })) + + semaphore.wait() + + if resultItems.count > 8 { + resultItems = Array(resultItems.dropLast(resultItems.count - 8)) + } + + return resultItems + } } private func avatarRoundImage(size: CGSize, source: UIImage) -> UIImage? { @@ -1141,7 +1175,7 @@ private let gradientColors: [NSArray] = [ ] private func avatarViewLettersImage(size: CGSize, peerId: Int64, accountPeerId: Int64, letters: [String]) -> UIImage? { - UIGraphicsBeginImageContextWithOptions(size, false, 0.0) + UIGraphicsBeginImageContextWithOptions(size, false, 2.0) let context = UIGraphicsGetCurrentContext() context?.beginPath() @@ -1189,3 +1223,57 @@ private func avatarImage(path: String?, peerId: Int64, accountPeerId: Int64, let return avatarViewLettersImage(size: size, peerId: peerId, accountPeerId: accountPeerId, letters: letters)! } } + +@available(iOSApplicationExtension 14.0, iOS 14.0, *) +private func mapPeersToFriends(accountId: AccountRecordId, accountPeerId: PeerId, mediaBox: MediaBox, peers: [Peer]) -> [Friend] { + var items: [Friend] = [] + for peer in peers { + autoreleasepool { + var profileImage: INImage? + + if let resource = smallestImageRepresentation(peer.profileImageRepresentations)?.resource, let path = mediaBox.completedResourcePath(resource) { + let cachedPath = mediaBox.cachedRepresentationPathForId(resource.id.uniqueId, representationId: "intents.png", keepDuration: .shortLived) + if let _ = fileSize(cachedPath) { + do { + let data = try Data(contentsOf: URL(fileURLWithPath: cachedPath), options: .alwaysMapped) + profileImage = INImage(imageData: data) + } catch { + } + } else { + let image = avatarImage(path: path, peerId: peer.id.toInt64(), accountPeerId: accountPeerId.toInt64(), letters: peer.displayLetters, size: CGSize(width: 50.0, height: 50.0)) + if let data = image.pngData() { + let _ = try? data.write(to: URL(fileURLWithPath: cachedPath), options: .atomic) + } + do { + let data = try Data(contentsOf: URL(fileURLWithPath: cachedPath), options: .alwaysMapped) + profileImage = INImage(imageData: data) + } catch { + } + } + } + if profileImage == nil { + let cachedPath = mediaBox.cachedRepresentationPathForId("lettersAvatar-\(peer.displayLetters.joined(separator: ","))", representationId: "intents.png", keepDuration: .shortLived) + if let _ = fileSize(cachedPath) { + do { + let data = try Data(contentsOf: URL(fileURLWithPath: cachedPath), options: .alwaysMapped) + profileImage = INImage(imageData: data) + } catch { + } + } else { + let image = avatarImage(path: nil, peerId: peer.id.toInt64(), accountPeerId: accountPeerId.toInt64(), letters: peer.displayLetters, size: CGSize(width: 50.0, height: 50.0)) + if let data = image.pngData() { + let _ = try? data.write(to: URL(fileURLWithPath: cachedPath), options: .atomic) + } + do { + let data = try Data(contentsOf: URL(fileURLWithPath: cachedPath), options: .alwaysMapped) + profileImage = INImage(imageData: data) + } catch { + } + } + } + + items.append(Friend(identifier: "\(accountId.int64):\(peer.id.toInt64())", display: peer.debugDisplayTitle, subtitle: nil, image: profileImage)) + } + } + return items +} diff --git a/Telegram/WidgetKitWidget/TodayViewController.swift b/Telegram/WidgetKitWidget/TodayViewController.swift index 0b7638b82b..cfdfd19836 100644 --- a/Telegram/WidgetKitWidget/TodayViewController.swift +++ b/Telegram/WidgetKitWidget/TodayViewController.swift @@ -929,8 +929,8 @@ struct AvatarsWidgetView: View { }) }).aspectRatio(1.0, contentMode: .fit)) } else if isPlaceholder { - return AnyView(Circle().aspectRatio(1.0, contentMode: .fit).foregroundColor(.clear)) - //return AnyView(Circle().aspectRatio(1.0, contentMode: .fit).foregroundColor(getPlaceholderColor())) + //return AnyView(Circle().aspectRatio(1.0, contentMode: .fit).foregroundColor(.clear)) + return AnyView(Circle().aspectRatio(1.0, contentMode: .fit).foregroundColor(getPlaceholderColor())) } else { return AnyView(Circle().aspectRatio(1.0, contentMode: .fit).foregroundColor(getPlaceholderColor())) } diff --git a/submodules/Postbox/Sources/MediaBox.swift b/submodules/Postbox/Sources/MediaBox.swift index f7239a91f0..e7188645ed 100644 --- a/submodules/Postbox/Sources/MediaBox.swift +++ b/submodules/Postbox/Sources/MediaBox.swift @@ -205,6 +205,10 @@ public final class MediaBox { return "\(id.uniqueId)" } + private func fileNameForId(_ id: String) -> String { + return "\(id)" + } + private func pathForId(_ id: MediaResourceId) -> String { return "\(self.basePath)/\(fileNameForId(id))" } @@ -228,6 +232,17 @@ public final class MediaBox { return ResourceStorePaths(partial: "\(self.basePath)/\(cacheString)/\(fileNameForId(id))_partial:\(representation.uniqueId)", complete: "\(self.basePath)/\(cacheString)/\(fileNameForId(id)):\(representation.uniqueId)") } + public func cachedRepresentationPathForId(_ id: String, representationId: String, keepDuration: CachedMediaRepresentationKeepDuration) -> String { + let cacheString: String + switch keepDuration { + case .general: + cacheString = "cache" + case .shortLived: + cacheString = "short-cache" + } + return "\(self.basePath)/\(cacheString)/\(fileNameForId(id))_\(representationId)" + } + public func cachedRepresentationCompletePath(_ id: MediaResourceId, representation: CachedMediaResourceRepresentation) -> String { let cacheString: String switch representation.keepDuration { diff --git a/submodules/TelegramCore/Sources/RecentPeers.swift b/submodules/TelegramCore/Sources/RecentPeers.swift index fd4d853cb6..88108ac206 100644 --- a/submodules/TelegramCore/Sources/RecentPeers.swift +++ b/submodules/TelegramCore/Sources/RecentPeers.swift @@ -41,6 +41,13 @@ public func recentPeers(account: Account) -> Signal { } } +public func getRecentPeers(transaction: Transaction) -> [PeerId] { + guard let entry = transaction.retrieveItemCacheEntry(id: cachedRecentPeersEntryId()) as? CachedRecentPeers else { + return [] + } + return entry.ids +} + public func managedUpdatedRecentPeers(accountPeerId: PeerId, postbox: Postbox, network: Network) -> Signal { let key = PostboxViewKey.cachedItem(cachedRecentPeersEntryId()) let peersEnabled = postbox.combinedView(keys: [key])