diff --git a/submodules/AppIntents/BUCK b/submodules/AppIntents/BUCK new file mode 100644 index 0000000000..2bc4100875 --- /dev/null +++ b/submodules/AppIntents/BUCK @@ -0,0 +1,23 @@ +load("//Config:buck_rule_macros.bzl", "static_library") + +static_library( + name = "AppIntents", + srcs = glob([ + "Sources/**/*.swift", + ]), + deps = [ + "//submodules/TelegramCore:TelegramCore#shared", + "//submodules/SyncCore:SyncCore#shared", + "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared", + "//submodules/Display:Display#shared", + "//submodules/AccountContext:AccountContext", + "//submodules/TelegramPresentationData:TelegramPresentationData", + "//submodules/AvatarNode:AvatarNode", + ], + frameworks = [ + "$SDKROOT/System/Library/Frameworks/Foundation.framework", + ], + weak_frameworks = [ + "Intents", + ], +) diff --git a/submodules/AppIntents/Info.plist b/submodules/AppIntents/Info.plist new file mode 100644 index 0000000000..e1fe4cfb7b --- /dev/null +++ b/submodules/AppIntents/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + + diff --git a/submodules/AppIntents/Sources/AppIntents.swift b/submodules/AppIntents/Sources/AppIntents.swift new file mode 100644 index 0000000000..676d2373cc --- /dev/null +++ b/submodules/AppIntents/Sources/AppIntents.swift @@ -0,0 +1,67 @@ +import Foundation +import UIKit +import Intents +import Display +import Postbox +import TelegramCore +import SyncCore +import SwiftSignalKit +import TelegramPresentationData +import AvatarNode +import AccountContext + +public func donateSendMessageIntent(account: Account, sharedContext: SharedAccountContext, peerIds: [PeerId]) { + if #available(iOSApplicationExtension 13.2, iOS 13.2, *) { + let _ = (account.postbox.transaction { transaction -> [Peer] in + var peers: [Peer] = [] + for peerId in peerIds { + if peerId.namespace == Namespaces.Peer.CloudUser && peerId != account.peerId, let peer = transaction.getPeer(peerId) { + peers.append(peer) + } + } + return peers + } + |> mapToSignal { peers -> Signal<[(Peer, UIImage?)], NoError> in + var signals: [Signal<(Peer, UIImage?), NoError>] = [] + for peer in peers { + let peerAndAvatar = (peerAvatarImage(account: account, peer: peer, authorOfMessage: nil, representation: peer.smallProfileImage, round: false) ?? .single(nil)) + |> map { avatarImage in + return (peer, avatarImage) + } + signals.append(peerAndAvatar) + } + return combineLatest(signals) + } + |> deliverOnMainQueue).start(next: { peers in + for (peer, avatarImage) in peers { + guard let peer = peer as? TelegramUser, peer.botInfo == nil && !peer.flags.contains(.isSupport) else { + continue + } + let presentationData = sharedContext.currentPresentationData.with { $0 } + + let recipientHandle = INPersonHandle(value: "tg\(peer.id.id)", type: .unknown) + var nameComponents = PersonNameComponents() + nameComponents.givenName = peer.firstName + nameComponents.familyName = peer.lastName + + let displayTitle = peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) + let recipient = INPerson(personHandle: recipientHandle, nameComponents: nameComponents, displayName: displayTitle, image: nil, contactIdentifier: nil, customIdentifier: "tg\(peer.id.id)") + + let intent = INSendMessageIntent(recipients: [recipient], content: nil, speakableGroupName: INSpeakableString(spokenPhrase: displayTitle), conversationIdentifier: "tg\(peer.id.id)", serviceName: nil, sender: nil) + if let avatarImage = avatarImage, let avatarImageData = avatarImage.jpegData(compressionQuality: 0.8) { + intent.setImage(INImage(imageData: avatarImageData), forParameterNamed: \.groupName) + } + let interaction = INInteraction(intent: intent, response: nil) + interaction.direction = .outgoing + interaction.groupIdentifier = "sendMessage_\(account.peerId.toInt64())" + interaction.donate() + } + }) + } +} + +public func deleteAllSendMessageIntents(accountPeerId: PeerId) { + if #available(iOS 10.0, *) { + INInteraction.delete(with: "sendMessage_\(accountPeerId.toInt64())") + } +} diff --git a/submodules/ShareController/BUCK b/submodules/ShareController/BUCK index e67eab7f9c..9a5616a4e9 100644 --- a/submodules/ShareController/BUCK +++ b/submodules/ShareController/BUCK @@ -23,6 +23,7 @@ static_library( "//submodules/ActivityIndicator:ActivityIndicator", "//submodules/AppBundle:AppBundle", "//submodules/TelegramStringFormatting:TelegramStringFormatting", + "//submodules/AppIntents:AppIntents", ], frameworks = [ "$SDKROOT/System/Library/Frameworks/Foundation.framework", diff --git a/submodules/ShareController/Sources/ShareControllerNode.swift b/submodules/ShareController/Sources/ShareControllerNode.swift index ce4adcd07e..1f5bfd5785 100644 --- a/submodules/ShareController/Sources/ShareControllerNode.swift +++ b/submodules/ShareController/Sources/ShareControllerNode.swift @@ -8,6 +8,7 @@ import TelegramCore import SyncCore import TelegramPresentationData import AccountContext +import AppIntents enum ShareState { case preparing @@ -26,6 +27,7 @@ func openExternalShare(state: () -> Signal) { final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate { private let sharedContext: SharedAccountContext + private var account: Account? private var presentationData: PresentationData private let externalShare: Bool private let immediateExternalShare: Bool @@ -537,6 +539,10 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate peerIds = self.controllerInteraction!.selectedPeers.map { $0.peerId } } + if let account = self.account { + donateSendMessageIntent(account: account, sharedContext: self.sharedContext, peerIds: peerIds) + } + if let signal = self.share?(self.inputFieldNode.text, peerIds) { self.transitionToContentNode(ShareLoadingContainerNode(theme: self.presentationData.theme, forceNativeAppearance: true), fastOut: true) let timestamp = CACurrentMediaTime() @@ -629,6 +635,8 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate } func updatePeers(account: Account, switchableAccounts: [AccountWithInfo], peers: [(RenderedPeer, PeerPresence?)], accountPeer: Peer, defaultAction: ShareControllerAction?) { + self.account = account + if let peersContentNode = self.peersContentNode, peersContentNode.accountPeer.id == accountPeer.id { peersContentNode.peersValue.set(.single(peers)) return diff --git a/submodules/TelegramUI/BUCK b/submodules/TelegramUI/BUCK index 4b6ef3822d..35d07b5256 100644 --- a/submodules/TelegramUI/BUCK +++ b/submodules/TelegramUI/BUCK @@ -197,6 +197,7 @@ framework( "//submodules/AppLock:AppLock", "//submodules/NotificationsPresentationData:NotificationsPresentationData", "//submodules/UrlWhitelist:UrlWhitelist", + "//submodules/AppIntents:AppIntents", ], frameworks = [ "$SDKROOT/System/Library/Frameworks/Foundation.framework", diff --git a/submodules/TelegramUI/TelegramUI/AppDelegate.swift b/submodules/TelegramUI/TelegramUI/AppDelegate.swift index 8a085da8b9..49844c444f 100644 --- a/submodules/TelegramUI/TelegramUI/AppDelegate.swift +++ b/submodules/TelegramUI/TelegramUI/AppDelegate.swift @@ -33,6 +33,7 @@ import WalletCore import OpenSSLEncryptionProvider import AppLock import PresentationDataUtils +import AppIntents #if canImport(BackgroundTasks) import BackgroundTasks @@ -179,6 +180,8 @@ final class SharedApplicationContext { private let authContext = Promise() private let authContextDisposable = MetaDisposable() + private let logoutDisposable = MetaDisposable() + private let openChatWhenReadyDisposable = MetaDisposable() private let openUrlWhenReadyDisposable = MetaDisposable() @@ -1155,7 +1158,8 @@ final class SharedApplicationContext { } |> take(1) |> timeout(4.0, queue: .mainQueue(), alternate: .complete()) - |> deliverOnMainQueue).start(completed: { authContextValue.rootController.view.endEditing(true) + |> deliverOnMainQueue).start(completed: { + authContextValue.rootController.view.endEditing(true) authContextValue.rootController.dismiss() }) } else { @@ -1174,12 +1178,32 @@ final class SharedApplicationContext { |> take(1) |> deliverOnMainQueue).start(next: { _ in statusController.dismiss() - self.mainWindow.present(context.rootController, on: .root) })) + self.mainWindow.present(context.rootController, on: .root) + })) } else { authContextReadyDisposable.set(nil) } })) + self.logoutDisposable.set((self.sharedContextPromise.get() + |> take(1) + |> mapToSignal { sharedContext -> Signal, NoError> in + return sharedContext.sharedContext.activeAccounts + |> map { _, accounts, _ -> Set in + return Set(accounts.map { $0.1.peerId }) + } + |> reduceLeft(value: Set()) { current, updated, emit in + if !current.isEmpty { + emit(current.subtracting(current.intersection(updated))) + } + return updated + } + }).start(next: { loggedOutAccountPeerIds in + for peerId in loggedOutAccountPeerIds { + deleteAllSendMessageIntents(accountPeerId: peerId) + } + })) + self.watchCommunicationManagerPromise.set(watchCommunicationManager(context: self.context.get() |> flatMap { WatchCommunicationManagerContext(context: $0.context) }, allowBackgroundTimeExtension: { timeout in let _ = (self.sharedContextPromise.get() |> take(1)).start(next: { sharedContext in diff --git a/submodules/TelegramUI/TelegramUI/ChatController.swift b/submodules/TelegramUI/TelegramUI/ChatController.swift index c789824280..13c834f324 100644 --- a/submodules/TelegramUI/TelegramUI/ChatController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatController.swift @@ -55,6 +55,7 @@ import LocalizedPeerData import PhoneNumberFormat import SettingsUI import UrlWhitelist +import AppIntents public enum ChatControllerPeekActions { case standard @@ -2798,7 +2799,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) - strongSelf.donateSendMessageIntent() + donateSendMessageIntent(account: strongSelf.context.account, sharedContext: strongSelf.context.sharedContext, peerIds: [peerId]) } } @@ -5994,7 +5995,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) - self.donateSendMessageIntent() + donateSendMessageIntent(account: self.context.account, sharedContext: self.context.sharedContext, peerIds: [peerId]) } else { let mode: ChatScheduleTimeControllerMode if peerId == self.context.account.peerId { @@ -8067,44 +8068,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } - private func donateSendMessageIntent() { - guard case let .peer(peerId) = self.chatLocation, peerId.namespace == Namespaces.Peer.CloudUser && peerId != self.context.account.peerId else { - return - } - if #available(iOSApplicationExtension 13.2, iOS 13.2, *) { - let _ = (self.context.account.postbox.loadedPeerWithId(peerId) - |> mapToSignal { peer -> Signal<(Peer, UIImage?), NoError> in - let avatarImage = peerAvatarImage(account: self.context.account, peer: peer, authorOfMessage: nil, representation: peer.smallProfileImage, round: false) ?? .single(nil) - return avatarImage - |> map { avatarImage in - return (peer, avatarImage) - } - } - |> deliverOnMainQueue).start(next: { [weak self] peer, avatarImage in - if let strongSelf = self, let peer = peer as? TelegramUser, peer.botInfo == nil && !peer.flags.contains(.isSupport) { - let recipientHandle = INPersonHandle(value: "tg\(peerId.id)", type: .unknown) - var nameComponents = PersonNameComponents() - nameComponents.givenName = peer.firstName - nameComponents.familyName = peer.lastName - let displayTitle = peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder) - let recipient = INPerson(personHandle: recipientHandle, nameComponents: nameComponents, displayName: displayTitle, image: nil, contactIdentifier: nil, customIdentifier: "tg\(peerId.id)") - let intent = INSendMessageIntent(recipients: [recipient], content: nil, speakableGroupName: INSpeakableString(spokenPhrase: displayTitle), conversationIdentifier: "tg\(peerId.id)", serviceName: nil, sender: nil) - if #available(iOS 12.0, *), let avatarImage = avatarImage, let avatarImageData = avatarImage.jpegData(compressionQuality: 0.8) { - intent.setImage(INImage(imageData: avatarImageData), forParameterNamed: \.groupName) - } - let interaction = INInteraction(intent: intent, response: nil) - interaction.direction = .outgoing - interaction.groupIdentifier = "sendMessage_\(strongSelf.context.account.peerId.toInt64())" - interaction.donate { error in - if let error = error { - print(error.localizedDescription) - } - } - } - }) - } - } - private func updateReminderActivity() { if self.isReminderActivityEnabled && false { if #available(iOS 9.0, *) {