diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index a46aba1fc9..7d8afcf436 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -696,7 +696,7 @@ public protocol SharedAccountContext: AnyObject { func chatAvailableMessageActions(postbox: Postbox, accountPeerId: EnginePeer.Id, messageIds: Set) -> Signal func chatAvailableMessageActions(postbox: Postbox, accountPeerId: EnginePeer.Id, messageIds: Set, messages: [EngineMessage.Id: EngineMessage], peers: [EnginePeer.Id: EnginePeer]) -> Signal func resolveUrl(context: AccountContext, peerId: PeerId?, url: String, skipUrlAuth: Bool) -> Signal - func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?) + func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, forceExternal: Bool, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?) func openAddContact(context: AccountContext, firstName: String, lastName: String, phoneNumber: String, label: String, present: @escaping (ViewController, Any?) -> Void, pushController: @escaping (ViewController) -> Void, completed: @escaping () -> Void) func openAddPersonContact(context: AccountContext, peerId: PeerId, pushController: @escaping (ViewController) -> Void, present: @escaping (ViewController, Any?) -> Void) func presentContactsWarningSuppression(context: AccountContext, present: (ViewController, Any?) -> Void) diff --git a/submodules/AttachmentUI/Sources/AttachmentController.swift b/submodules/AttachmentUI/Sources/AttachmentController.swift index c7d59d7a12..6f121d2b74 100644 --- a/submodules/AttachmentUI/Sources/AttachmentController.swift +++ b/submodules/AttachmentUI/Sources/AttachmentController.swift @@ -17,8 +17,55 @@ public enum AttachmentButtonType: Equatable { case location case contact case poll - case app(PeerId, String, [AttachMenuBots.Bot.IconName: TelegramMediaFile]) + case app(Peer, String, [AttachMenuBots.Bot.IconName: TelegramMediaFile]) case standalone + + public static func ==(lhs: AttachmentButtonType, rhs: AttachmentButtonType) -> Bool { + switch lhs { + case .gallery: + if case .gallery = rhs { + return true + } else { + return false + } + case .file: + if case .file = rhs { + return true + } else { + return false + } + case .location: + if case .location = rhs { + return true + } else { + return false + } + case .contact: + if case .contact = rhs { + return true + } else { + return false + } + case .poll: + if case .poll = rhs { + return true + } else { + return false + } + case let .app(lhsPeer, lhsTitle, lhsIcons): + if case let .app(rhsPeer, rhsTitle, rhsIcons) = rhs, arePeersEqual(lhsPeer, rhsPeer), lhsTitle == rhsTitle, lhsIcons == rhsIcons { + return true + } else { + return false + } + case .standalone: + if case .standalone = rhs { + return true + } else { + return false + } + } + } } public protocol AttachmentContainable: ViewController { @@ -169,7 +216,7 @@ public class AttachmentController: ViewController { if let strongSelf = self { strongSelf.panel.updateLoadingProgress(progress) if let layout = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout, transition: .immediate) + strongSelf.containerLayoutUpdated(layout, transition: .animated(duration: 0.4, curve: .spring)) } } })) @@ -319,9 +366,9 @@ public class AttachmentController: ViewController { if let controller = self.controller { let _ = self.switchToController(controller.initialButton) - if case let .app(botId, _, _) = controller.initialButton { + if case let .app(bot, _, _) = controller.initialButton { if let index = controller.buttons.firstIndex(where: { - if case let .app(otherBotId, _, _) = $0, otherBotId == botId { + if case let .app(otherBot, _, _) = $0, otherBot.id == bot.id { return true } else { return false diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index b596b4d1ea..9bb2e11d95 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -24,14 +24,14 @@ private let sideInset: CGFloat = 3.0 private final class IconComponent: Component { public let account: Account public let name: String - public let file: TelegramMediaFile? + public let fileReference: FileMediaReference? public let animationName: String? public let tintColor: UIColor? - public init(account: Account, name: String, file: TelegramMediaFile?, animationName: String?, tintColor: UIColor?) { + public init(account: Account, name: String, fileReference: FileMediaReference?, animationName: String?, tintColor: UIColor?) { self.account = account self.name = name - self.file = file + self.fileReference = fileReference self.animationName = animationName self.tintColor = tintColor } @@ -43,7 +43,7 @@ private final class IconComponent: Component { if lhs.name != rhs.name { return false } - if lhs.file?.fileId != rhs.file?.fileId { + if lhs.fileReference?.media != rhs.fileReference?.media { return false } if lhs.animationName != rhs.animationName { @@ -72,14 +72,14 @@ private final class IconComponent: Component { } func update(component: IconComponent, availableSize: CGSize, transition: Transition) -> CGSize { - if self.component?.name != component.name || self.component?.file?.fileId != component.file?.fileId || self.component?.tintColor != component.tintColor { - if let file = component.file { + if self.component?.name != component.name || self.component?.fileReference?.media.fileId != component.fileReference?.media.fileId || self.component?.tintColor != component.tintColor { + if let fileReference = component.fileReference { let previousName = self.component?.name ?? "" if !previousName.isEmpty { self.image = nil } - self.disposable = (svgIconImageFile(account: component.account, fileReference: .standalone(media: file), fetched: true) + self.disposable = (svgIconImageFile(account: component.account, fileReference: fileReference, fetched: true) |> runOn(Queue.concurrentDefaultQueue()) |> deliverOnMainQueue).start(next: { [weak self] transform in let arguments = TransformImageArguments(corners: ImageCorners(), imageSize: availableSize, boundingSize: availableSize, intrinsicInsets: UIEdgeInsets()) @@ -169,6 +169,7 @@ private final class AttachButtonComponent: CombinedComponent { let imageName: String var imageFile: TelegramMediaFile? var animationFile: TelegramMediaFile? + var botPeer: Peer? let component = context.component let strings = component.strings @@ -189,7 +190,8 @@ private final class AttachButtonComponent: CombinedComponent { case .poll: name = strings.Attachment_Poll imageName = "Chat/Attach Menu/Poll" - case let .app(_, appName, appIcons): + case let .app(peer, appName, appIcons): + botPeer = peer name = appName imageName = "" if let file = appIcons[.iOSAnimated] { @@ -232,11 +234,16 @@ private final class AttachButtonComponent: CombinedComponent { .position(CGPoint(x: iconFrame.midX, y: iconFrame.midY)) ) } else { + var fileReference: FileMediaReference? + if let peer = botPeer.flatMap({ PeerReference($0 )}), let imageFile = imageFile { + fileReference = .attachBot(peer: peer, media: imageFile) + } + let icon = icon.update( component: IconComponent( account: component.context.account, name: imageName, - file: imageFile, + fileReference: fileReference, animationName: nil, tintColor: tintColor ), @@ -789,11 +796,11 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { } let type = self.buttons[i] - if case let .app(_, _, iconFiles) = type { + if case let .app(peer, _, iconFiles) = type { for (name, file) in iconFiles { if [.default, .iOSAnimated].contains(name) { - if self.iconDisposables[file.fileId] == nil { - self.iconDisposables[file.fileId] = freeMediaFileInteractiveFetched(account: self.context.account, fileReference: .standalone(media: file)).start() + if self.iconDisposables[file.fileId] == nil, let peer = PeerReference(peer) { + self.iconDisposables[file.fileId] = freeMediaFileInteractiveFetched(account: self.context.account, fileReference: .attachBot(peer: peer, media: file)).start() } } } @@ -905,15 +912,15 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { self.scrollNode.isUserInteractionEnabled = !isSelecting + let isButtonVisible = self.mainButtonState.isVisible + var insets = layout.insets(options: []) - if let inputHeight = layout.inputHeight, inputHeight > 0.0 && isSelecting { + if let inputHeight = layout.inputHeight, inputHeight > 0.0 && (isSelecting || isButtonVisible) { insets.bottom = inputHeight } else if layout.intrinsicInsets.bottom > 0.0 { insets.bottom = layout.intrinsicInsets.bottom } - let isButtonVisible = self.mainButtonState.isVisible - if isSelecting { self.loadTextNodeIfNeeded() } else { @@ -944,7 +951,13 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { let containerTransition: ContainedViewLayoutTransition let containerFrame: CGRect if isButtonVisible { - containerFrame = CGRect(origin: CGPoint(), size: CGSize(width: bounds.width, height: bounds.height + 9.0)) + let height: CGFloat + if layout.intrinsicInsets.bottom > 0.0 && (layout.inputHeight ?? 0.0).isZero { + height = bounds.height + 9.0 + } else { + height = bounds.height + 9.0 + 8.0 + } + containerFrame = CGRect(origin: CGPoint(), size: CGSize(width: bounds.width, height: height)) } else if isSelecting { containerFrame = CGRect(origin: CGPoint(), size: CGSize(width: bounds.width, height: textPanelHeight + insets.bottom)) } else { diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index af9abda560..7a969b462a 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -171,7 +171,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo openUserGeneratedUrl(context: context, peerId: nil, url: url, concealed: false, present: { c in present(c, nil) }, openResolved: { [weak self] resolved in - context.sharedContext.openResolvedUrl(resolved, context: context, urlContext: .generic, navigationController: navigationController, openPeer: { peerId, navigation in + context.sharedContext.openResolvedUrl(resolved, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peerId, navigation in }, sendFile: nil, sendSticker: nil, diff --git a/submodules/DebugSettingsUI/Sources/DebugController.swift b/submodules/DebugSettingsUI/Sources/DebugController.swift index ad1d2f8303..9636006ae3 100644 --- a/submodules/DebugSettingsUI/Sources/DebugController.swift +++ b/submodules/DebugSettingsUI/Sources/DebugController.swift @@ -214,7 +214,7 @@ private enum DebugControllerEntry: ItemListNodeEntry { } else { UIPasteboard.general.setData(data, forPasteboardType: dataType) } - context.sharedContext.openResolvedUrl(.importStickers, context: context, urlContext: .generic, navigationController: arguments.getNavigationController(), openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { c, a in arguments.presentController(c, a as? ViewControllerPresentationArguments) }, dismissInput: {}, contentContext: nil) + context.sharedContext.openResolvedUrl(.importStickers, context: context, urlContext: .generic, navigationController: arguments.getNavigationController(), forceExternal: false, openPeer: { _, _ in }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { c, a in arguments.presentController(c, a as? ViewControllerPresentationArguments) }, dismissInput: {}, contentContext: nil) } }) case .sendLogs: diff --git a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift index 0354a89a07..fcc0353ecc 100644 --- a/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageControllerNode.swift @@ -1046,7 +1046,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { } })] - let (canTranslate, language) = canTranslateText(context: context, text: text, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: true, ignoredLanguages: translationSettings.ignoredLanguages) + let (canTranslate, language) = canTranslateText(context: context, text: text, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: false, ignoredLanguages: translationSettings.ignoredLanguages) if canTranslate { actions.append(ContextMenuAction(content: .text(title: strings.Conversation_ContextMenuTranslate, accessibilityLabel: strings.Conversation_ContextMenuTranslate), action: { [weak self] in let controller = TranslateScreen(context: context, text: text, fromLanguage: language) @@ -1238,7 +1238,7 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate { } default: strongSelf.loadProgress.set(1.0) - strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.getNavigationController(), openPeer: { peerId, navigation in + strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.getNavigationController(), forceExternal: false, openPeer: { peerId, navigation in switch navigation { case let .chat(_, subject, peekData): if let navigationController = strongSelf.getNavigationController() { diff --git a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift index fceb5af6e4..899a758337 100644 --- a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift +++ b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift @@ -692,7 +692,7 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie guard let navigationController = self.controller?.navigationController as? NavigationController else { return false } - self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .generic, navigationController: navigationController, openPeer: { [weak self] peerId, navigation in + self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { [weak self] peerId, navigation in guard let strongSelf = self else { return } diff --git a/submodules/SettingsUI/Sources/LogoutOptionsController.swift b/submodules/SettingsUI/Sources/LogoutOptionsController.swift index 54f275b104..25f9ecea1e 100644 --- a/submodules/SettingsUI/Sources/LogoutOptionsController.swift +++ b/submodules/SettingsUI/Sources/LogoutOptionsController.swift @@ -181,7 +181,7 @@ public func logoutOptionsController(context: AccountContext, navigationControlle controller?.dismiss() dismissImpl?() - context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, openPeer: { peer, navigation in + context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peer, navigation in }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { controller, arguments in pushControllerImpl?(controller) }, dismissInput: {}, contentContext: nil) diff --git a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift index f401eca9f2..e45bb49eb3 100644 --- a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift +++ b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift @@ -897,7 +897,7 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList let _ = (cachedFaqInstantPage(context: context) |> deliverOnMainQueue).start(next: { resolvedUrl in - context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, openPeer: { peer, navigation in + context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peer, navigation in }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { controller, arguments in present(.push, controller) }, dismissInput: {}, contentContext: nil) diff --git a/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift b/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift index e5d3e9943a..1fedee246d 100644 --- a/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift +++ b/submodules/TelegramCore/Sources/Network/FetchedMediaResource.swift @@ -225,6 +225,7 @@ private enum MediaReferenceRevalidationKey: Hashable { case wallpapers case themes case peerAvatars(peer: PeerReference) + case attachBot(peer: PeerReference) } private final class MediaReferenceRevalidationItemContext { @@ -536,6 +537,25 @@ final class MediaReferenceRevalidationContext { } } } + + func attachBot(postbox: Postbox, network: Network, background: Bool, peer: PeerReference) -> Signal { + return self.genericItem(key: .attachBot(peer: peer), background: background, request: { next, error in + return (_internal_getAttachMenuBot(postbox: postbox, network: network, botId: peer.id, cached: false) + |> mapError { _ -> RevalidateMediaReferenceError in + return .generic + }).start(next: { value in + next(value) + }, error: { _ in + error(.generic) + }) + }) |> mapToSignal { next -> Signal in + if let next = next as? AttachMenuBot { + return .single(next) + } else { + return .fail(.generic) + } + } + } } struct RevalidatedMediaResource { @@ -656,6 +676,16 @@ func revalidateMediaResourceReference(postbox: Postbox, network: Network, revali } return .fail(.generic) } + case let .attachBot(peer, _): + return revalidationContext.attachBot(postbox: postbox, network: network, background: info.preferBackgroundReferenceRevalidation, peer: peer) + |> mapToSignal { attachBot -> Signal in + for (_, icon) in attachBot.icons { + if let updatedResource = findUpdatedMediaResource(media: icon, previousMedia: nil, resource: resource) { + return .single(RevalidatedMediaResource(updatedResource: updatedResource, updatedReference: nil)) + } + } + return .fail(.generic) + } case let .standalone(media): if let file = media as? TelegramMediaFile { for attribute in file.attributes { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_MediaReference.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_MediaReference.swift index b6b3672ac4..7a39a049e0 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_MediaReference.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_MediaReference.swift @@ -224,6 +224,7 @@ public enum AnyMediaReference: Equatable { case stickerPack(stickerPack: StickerPackReference, media: Media) case savedGif(media: Media) case avatarList(peer: PeerReference, media: Media) + case attachBot(peer: PeerReference, media: Media) public static func ==(lhs: AnyMediaReference, rhs: AnyMediaReference) -> Bool { switch lhs { @@ -263,6 +264,12 @@ public enum AnyMediaReference: Equatable { } else { return false } + case let .attachBot(lhsPeer, lhsMedia): + if case let .attachBot(rhsPeer, rhsMedia) = rhs, lhsPeer == rhsPeer, lhsMedia.isEqual(to: rhsMedia) { + return true + } else { + return false + } } } @@ -280,6 +287,8 @@ public enum AnyMediaReference: Equatable { return .savedGif case .avatarList: return nil + case .attachBot: + return nil } } @@ -309,6 +318,10 @@ public enum AnyMediaReference: Equatable { if let media = media as? T { return .avatarList(peer: peer, media: media) } + case let .attachBot(peer, media): + if let media = media as? T { + return .attachBot(peer: peer, media: media) + } } return nil } @@ -327,6 +340,8 @@ public enum AnyMediaReference: Equatable { return media case let .avatarList(_, media): return media + case let .attachBot(_, media): + return media } } @@ -405,6 +420,7 @@ public enum MediaReference { case stickerPack case savedGif case avatarList + case attachBot } case standalone(media: T) @@ -413,6 +429,7 @@ public enum MediaReference { case stickerPack(stickerPack: StickerPackReference, media: T) case savedGif(media: T) case avatarList(peer: PeerReference, media: T) + case attachBot(peer: PeerReference, media: T) public init?(decoder: PostboxDecoder) { guard let caseIdValue = decoder.decodeOptionalInt32ForKey("_r"), let caseId = CodingCase(rawValue: caseIdValue) else { @@ -448,11 +465,17 @@ public enum MediaReference { } self = .savedGif(media: media) case .avatarList: - let peer = decoder.decodeObjectForKey("pr", decoder: { StickerPackReference(decoder: $0) }) as! PeerReference + let peer = decoder.decodeObjectForKey("pr", decoder: { PeerReference(decoder: $0) }) as! PeerReference guard let media = decoder.decodeObjectForKey("m") as? T else { return nil } self = .avatarList(peer: peer, media: media) + case .attachBot: + let peer = decoder.decodeObjectForKey("pr", decoder: { PeerReference(decoder: $0) }) as! PeerReference + guard let media = decoder.decodeObjectForKey("m") as? T else { + return nil + } + self = .attachBot(peer: peer, media: media) } } @@ -480,6 +503,10 @@ public enum MediaReference { encoder.encodeInt32(CodingCase.avatarList.rawValue, forKey: "_r") encoder.encodeObject(peer, forKey: "pr") encoder.encodeObject(media, forKey: "m") + case let .attachBot(peer, media): + encoder.encodeInt32(CodingCase.attachBot.rawValue, forKey: "_r") + encoder.encodeObject(peer, forKey: "pr") + encoder.encodeObject(media, forKey: "m") } } @@ -497,6 +524,8 @@ public enum MediaReference { return .savedGif(media: media) case let .avatarList(peer, media): return .avatarList(peer: peer, media: media) + case let .attachBot(peer, media): + return .attachBot(peer: peer, media: media) } } @@ -518,6 +547,8 @@ public enum MediaReference { return media case let .avatarList(_, media): return media + case let .attachBot(_, media): + return media } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift index 3dd1f7d29f..7980e44625 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift @@ -258,8 +258,14 @@ func managedSynchronizeAttachMenuBots(postbox: Postbox, network: Network, force: |> restart } -func _internal_addBotToAttachMenu(postbox: Postbox, network: Network, botId: PeerId) -> Signal { - return postbox.transaction { transaction -> Signal in + +public enum AddBotToAttachMenuError { + case generic +} + + +func _internal_addBotToAttachMenu(postbox: Postbox, network: Network, botId: PeerId) -> Signal { + return postbox.transaction { transaction -> Signal in guard let peer = transaction.getPeer(botId), let inputUser = apiInputUser(peer) else { return .complete() } @@ -272,21 +278,23 @@ func _internal_addBotToAttachMenu(postbox: Postbox, network: Network, botId: Pee return false } } - |> `catch` { error -> Signal in - return .single(false) + |> mapError { _ -> AddBotToAttachMenuError in + return .generic } - |> mapToSignal { value -> Signal in + |> mapToSignal { value -> Signal in if value { return managedSynchronizeAttachMenuBots(postbox: postbox, network: network) + |> castError(AddBotToAttachMenuError.self) |> take(1) |> map { _ -> Bool in return true } } else { - return .single(value) + return .fail(.generic) } } } + |> castError(AddBotToAttachMenuError.self) |> switchToLatest } @@ -346,8 +354,14 @@ public enum GetAttachMenuBotError { case generic } -public func _internal_getAttachMenuBot(postbox: Postbox, network: Network, botId: PeerId) -> Signal { +public func _internal_getAttachMenuBot(postbox: Postbox, network: Network, botId: PeerId, cached: Bool) -> Signal { return postbox.transaction { transaction -> Signal in + if cached, let cachedBots = cachedAttachMenuBots(transaction: transaction)?.bots { + if let bot = cachedBots.first(where: { $0.peerId == botId }), let peer = transaction.getPeer(bot.peerId) { + return .single(AttachMenuBot(peer: peer, shortName: bot.name, icons: bot.icons)) + } + } + guard let peer = transaction.getPeer(botId), let inputUser = apiInputUser(peer) else { return .complete() } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index 2b49d5386f..fdca89adc1 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -335,7 +335,7 @@ public extension TelegramEngine { return _internal_sendWebViewData(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, botId: botId, buttonText: buttonText, data: data) } - public func addBotToAttachMenu(botId: PeerId) -> Signal { + public func addBotToAttachMenu(botId: PeerId) -> Signal { return _internal_addBotToAttachMenu(postbox: self.account.postbox, network: self.account.network, botId: botId) } @@ -343,8 +343,8 @@ public extension TelegramEngine { return _internal_removeBotFromAttachMenu(postbox: self.account.postbox, network: self.account.network, botId: botId) } - public func getAttachMenuBot(botId: PeerId) -> Signal { - return _internal_getAttachMenuBot(postbox: self.account.postbox, network: self.account.network, botId: botId) + public func getAttachMenuBot(botId: PeerId, cached: Bool = false) -> Signal { + return _internal_getAttachMenuBot(postbox: self.account.postbox, network: self.account.network, botId: botId, cached: cached) } public func attachMenuBots() -> Signal<[AttachMenuBot], NoError> { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 3f8a6946ce..e4a73a58ad 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -2940,7 +2940,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G translationSettings = TranslationSettings.defaultSettings } - let (_, language) = canTranslateText(context: context, text: text.string, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: true, ignoredLanguages: translationSettings.ignoredLanguages) + var showTranslateIfTopical = false + if let peer = strongSelf.presentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel, !(peer.addressName ?? "").isEmpty { + showTranslateIfTopical = true + } + + let (_, language) = canTranslateText(context: context, text: text.string, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: showTranslateIfTopical, ignoredLanguages: translationSettings.ignoredLanguages) let controller = TranslateScreen(context: context, text: text.string, fromLanguage: language) controller.pushController = { [weak self] c in @@ -3361,7 +3366,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, buttonText: buttonText, keepAliveSignal: nil, openUrl: { [weak self] url in - self?.openUrl(url, concealed: true) + self?.openUrl(url, concealed: true, forceExternal: true) }) strongSelf.present(controller, in: .window(.root)) // controller.getNavigationController = { [weak self] in @@ -3385,7 +3390,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peerId, botId: peerId, botName: botName, url: result.url, queryId: result.queryId, buttonText: buttonText, keepAliveSignal: result.keepAliveSignal, openUrl: { [weak self] url in - self?.openUrl(url, concealed: true) + self?.openUrl(url, concealed: true, forceExternal: true) }, completion: { [weak self] in self?.chatDisplayNode.historyNode.scrollToEndOfHistory() }) @@ -10579,7 +10584,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G initialButton = .gallery } for bot in attachMenuBots.reversed() { - let button: AttachmentButtonType = .app(bot.peer.id, bot.shortName, bot.icons) + let button: AttachmentButtonType = .app(bot.peer, bot.shortName, bot.icons) buttons.insert(button, at: 1) if initialButton == nil && bot.peer.id == botId { @@ -10607,9 +10612,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } let controller = addWebAppToAttachmentController(context: context, peerName: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), peerIcon: icon, completion: { - let _ = context.engine.messages.addBotToAttachMenu(botId: botId).start() - - Queue.mainQueue().after(1.0, { + let _ = (context.engine.messages.addBotToAttachMenu(botId: botId) + |> deliverOnMainQueue).start(error: { _ in + + }, completed: { strongSelf.presentAttachmentBot(botId: botId, payload: botPayload) }) }) @@ -10884,11 +10890,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let controller = strongSelf.configurePollCreation() completion(controller, nil) strongSelf.controllerNavigationDisposable.set(nil) - case let .app(botId, botName, _): + case let .app(bot, botName, _): let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId - let controller = WebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peer.id, botId: botId, botName: botName, url: nil, queryId: nil, buttonText: nil, keepAliveSignal: nil, replyToMessageId: replyMessageId) + let controller = WebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peer.id, botId: bot.id, botName: botName, url: nil, queryId: nil, buttonText: nil, keepAliveSignal: nil, replyToMessageId: replyMessageId) controller.openUrl = { [weak self] url in - self?.openUrl(url, concealed: true) + self?.openUrl(url, concealed: true, forceExternal: true) } controller.getNavigationController = { [weak self] in return self?.effectiveNavigationController @@ -14394,11 +14400,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G })) } - private func openResolved(result: ResolvedUrl, sourceMessageId: MessageId?) { + private func openResolved(result: ResolvedUrl, sourceMessageId: MessageId?, forceExternal: Bool = false) { guard let peerId = self.chatLocation.peerId else { return } - self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .chat(peerId: peerId, updatedPresentationData: self.updatedPresentationData), navigationController: self.effectiveNavigationController, openPeer: { [weak self] peerId, navigation in + self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .chat(peerId: peerId, updatedPresentationData: self.updatedPresentationData), navigationController: self.effectiveNavigationController, forceExternal: forceExternal, openPeer: { [weak self] peerId, navigation in guard let strongSelf = self else { return } @@ -14438,8 +14444,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G default: break } - }, sendFile: nil, - sendSticker: { [weak self] f, sourceNode, sourceRect in + }, sendFile: nil, sendSticker: { [weak self] f, sourceNode, sourceRect in return self?.interfaceInteraction?.sendSticker(f, true, sourceNode, sourceRect) ?? false }, requestMessageActionUrlAuth: { [weak self] subject in if case let .url(url) = subject { @@ -14454,7 +14459,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }, contentContext: nil) } - private func openUrl(_ url: String, concealed: Bool, skipUrlAuth: Bool = false, skipConcealedAlert: Bool = false, message: Message? = nil) { + private func openUrl(_ url: String, concealed: Bool, forceExternal: Bool = false, skipUrlAuth: Bool = false, skipConcealedAlert: Bool = false, message: Message? = nil) { self.commitPurposefulAction() let _ = self.presentVoiceMessageDiscardAlert(action: { @@ -14513,7 +14518,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G openUserGeneratedUrl(context: self.context, peerId: self.peerView?.peerId, url: url, concealed: concealed, skipUrlAuth: skipUrlAuth, skipConcealedAlert: skipConcealedAlert, present: { [weak self] c in self?.present(c, in: .window(.root)) }, openResolved: { [weak self] resolved in - self?.openResolved(result: resolved, sourceMessageId: message?.id) + self?.openResolved(result: resolved, sourceMessageId: message?.id, forceExternal: forceExternal) }) }, performAction: true) } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 47f7021915..424b5fd037 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -857,7 +857,12 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState }))) } - let (canTranslate, _) = canTranslateText(context: context, text: messageText, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: true, ignoredLanguages: translationSettings.ignoredLanguages) + var showTranslateIfTopical = false + if let peer = chatPresentationInterfaceState.renderedPeer?.chatMainPeer as? TelegramChannel, !(peer.addressName ?? "").isEmpty { + showTranslateIfTopical = true + } + + let (canTranslate, _) = canTranslateText(context: context, text: messageText, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: showTranslateIfTopical, ignoredLanguages: translationSettings.ignoredLanguages) if canTranslate { actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuTranslate, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Translate"), color: theme.actionSheet.primaryTextColor) diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift index e04279eb49..ebad36bf1f 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift @@ -910,7 +910,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { case let .localization(identifier): strongSelf.presentController(LanguageLinkPreviewController(context: strongSelf.context, identifier: identifier), .window(.root), nil) case .proxy, .confirmationCode, .cancelAccountReset, .share: - strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.getNavigationController(), openPeer: { peerId, _ in + strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.getNavigationController(), forceExternal: false, openPeer: { peerId, _ in if let strongSelf = self { strongSelf.openPeer(peerId: peerId, peer: nil) } diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index 9eb8f31a01..418b081837 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -43,7 +43,7 @@ private func defaultNavigationForPeerId(_ peerId: PeerId?, navigation: ChatContr } } -func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)? = nil, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?) { +func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, forceExternal: Bool, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)? = nil, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?) { let updatedPresentationData: (initial: PresentationData, signal: Signal)? if case let .chat(_, maybeUpdatedPresentationData) = urlContext { updatedPresentationData = maybeUpdatedPresentationData @@ -53,7 +53,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } switch resolvedUrl { case let .externalUrl(url): - context.sharedContext.openExternalUrl(context: context, urlContext: urlContext, url: url, forceExternal: false, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: navigationController, dismissInput: dismissInput) + context.sharedContext.openExternalUrl(context: context, urlContext: urlContext, url: url, forceExternal: forceExternal, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: navigationController, dismissInput: dismissInput) case let .urlAuth(url): requestMessageActionUrlAuth?(.url(url)) dismissInput() @@ -469,13 +469,6 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur present(textAlertController(context: context, updatedPresentationData: updatedPresentationData, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) })) dismissInput() - #if ENABLE_WALLET - case let .wallet(address, amount, comment): - dismissInput() - context.sharedContext.openWallet(context: context, walletContext: .send(address: address, amount: amount, comment: comment)) { c in - navigationController?.pushViewController(c) - } - #endif case let .settings(section): dismissInput() switch section { @@ -570,7 +563,9 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur } let controller = addWebAppToAttachmentController(context: context, peerName: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), peerIcon: icon, completion: { let _ = (context.engine.messages.addBotToAttachMenu(botId: peerId) - |> deliverOnMainQueue).start(completed: { + |> deliverOnMainQueue).start(error: { _ in + presentError(presentationData.strings.WebApp_AddToAttachmentUnavailableError) + }, completed: { if let navigationController = navigationController, case let .chat(chatPeerId, _) = urlContext { let _ = context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: chatPeerId), attachBotStart: ChatControllerInitialAttachBotStart(botId: peer.id, payload: payload), useExisting: true)) } diff --git a/submodules/TelegramUI/Sources/OpenUrl.swift b/submodules/TelegramUI/Sources/OpenUrl.swift index 7cdac5fc92..8526092ffa 100644 --- a/submodules/TelegramUI/Sources/OpenUrl.swift +++ b/submodules/TelegramUI/Sources/OpenUrl.swift @@ -188,7 +188,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur if case let .externalUrl(value) = resolved { context.sharedContext.applicationBindings.openUrl(value) } else { - context.sharedContext.openResolvedUrl(resolved, context: context, urlContext: .generic, navigationController: navigationController, openPeer: { peerId, navigation in + context.sharedContext.openResolvedUrl(resolved, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, openPeer: { peerId, navigation in switch navigation { case .info: let _ = (context.account.postbox.loadedPeerWithId(peerId) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index c0f42c095a..6b0f0d9c36 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -3288,7 +3288,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate guard let navigationController = self.controller?.navigationController as? NavigationController else { return } - self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .chat(peerId: self.peerId, updatedPresentationData: self.controller?.updatedPresentationData), navigationController: navigationController, openPeer: { [weak self] peerId, navigation in + self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .chat(peerId: self.peerId, updatedPresentationData: self.controller?.updatedPresentationData), navigationController: navigationController, forceExternal: false, openPeer: { [weak self] peerId, navigation in guard let strongSelf = self else { return } @@ -3335,7 +3335,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate let result: ResolvedUrl = external ? .externalUrl(url) : tempResolved - strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, openPeer: { peerId, navigation in + strongSelf.context.sharedContext.openResolvedUrl(result, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, forceExternal: false, openPeer: { peerId, navigation in self?.openPeer(peerId: peerId, navigation: navigation) }, sendFile: nil, sendSticker: nil, @@ -5241,7 +5241,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate guard let controller = self.controller else { return } - self.context.sharedContext.openResolvedUrl(.groupBotStart(peerId: peerId, payload: "", adminRights: nil), context: self.context, urlContext: .generic, navigationController: controller.navigationController as? NavigationController, openPeer: { id, navigation in + self.context.sharedContext.openResolvedUrl(.groupBotStart(peerId: peerId, payload: "", adminRights: nil), context: self.context, urlContext: .generic, navigationController: controller.navigationController as? NavigationController, forceExternal: false, openPeer: { id, navigation in }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, @@ -5481,7 +5481,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate self?.controller?.present(UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) })] - let (canTranslate, language) = canTranslateText(context: context, text: text, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: true, ignoredLanguages: translationSettings.ignoredLanguages) + let (canTranslate, language) = canTranslateText(context: context, text: text, showTranslate: translationSettings.showTranslate, showTranslateIfTopical: false, ignoredLanguages: translationSettings.ignoredLanguages) if canTranslate { actions.append(ContextMenuAction(content: .text(title: presentationData.strings.Conversation_ContextMenuTranslate, accessibilityLabel: presentationData.strings.Conversation_ContextMenuTranslate), action: { [weak self] in @@ -6177,7 +6177,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate if case let .instantView(webPage, _) = resolvedUrl, let customAnchor = anchor { resolvedUrl = .instantView(webPage, customAnchor) } - strongSelf.context.sharedContext.openResolvedUrl(resolvedUrl, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, openPeer: { peer, navigation in + strongSelf.context.sharedContext.openResolvedUrl(resolvedUrl, context: strongSelf.context, urlContext: .generic, navigationController: strongSelf.controller?.navigationController as? NavigationController, forceExternal: false, openPeer: { peer, navigation in }, sendFile: nil, sendSticker: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { [weak self] controller, arguments in self?.controller?.push(controller) }, dismissInput: {}, contentContext: nil) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 882c18c383..3fa5538ab4 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1197,8 +1197,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { return resolveUrlImpl(context: context, peerId: peerId, url: url, skipUrlAuth: skipUrlAuth) } - public func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?) { - openResolvedUrlImpl(resolvedUrl, context: context, urlContext: urlContext, navigationController: navigationController, openPeer: openPeer, sendFile: sendFile, sendSticker: sendSticker, requestMessageActionUrlAuth: requestMessageActionUrlAuth, joinVoiceChat: joinVoiceChat, present: present, dismissInput: dismissInput, contentContext: contentContext) + public func openResolvedUrl(_ resolvedUrl: ResolvedUrl, context: AccountContext, urlContext: OpenURLContext, navigationController: NavigationController?, forceExternal: Bool, openPeer: @escaping (PeerId, ChatControllerInteractionNavigateToPeer) -> Void, sendFile: ((FileMediaReference) -> Void)?, sendSticker: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?, requestMessageActionUrlAuth: ((MessageActionUrlSubject) -> Void)?, joinVoiceChat: ((PeerId, String?, CachedChannelData.ActiveCall) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, contentContext: Any?) { + openResolvedUrlImpl(resolvedUrl, context: context, urlContext: urlContext, navigationController: navigationController, forceExternal: forceExternal, openPeer: openPeer, sendFile: sendFile, sendSticker: sendSticker, requestMessageActionUrlAuth: requestMessageActionUrlAuth, joinVoiceChat: joinVoiceChat, present: present, dismissInput: dismissInput, contentContext: contentContext) } public func makeDeviceContactInfoController(context: AccountContext, subject: DeviceContactInfoSubject, completed: (() -> Void)?, cancelled: (() -> Void)?) -> ViewController { diff --git a/submodules/TelegramUI/Sources/TextLinkHandling.swift b/submodules/TelegramUI/Sources/TextLinkHandling.swift index ee5354f0b7..7c5934906d 100644 --- a/submodules/TelegramUI/Sources/TextLinkHandling.swift +++ b/submodules/TelegramUI/Sources/TextLinkHandling.swift @@ -23,7 +23,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate } let openResolvedPeerImpl: (PeerId?, ChatControllerInteractionNavigateToPeer) -> Void = { [weak controller] peerId, navigation in - context.sharedContext.openResolvedUrl(.peer(peerId, navigation), context: context, urlContext: .generic, navigationController: (controller?.navigationController as? NavigationController), openPeer: { (peerId, navigation) in + context.sharedContext.openResolvedUrl(.peer(peerId, navigation), context: context, urlContext: .generic, navigationController: (controller?.navigationController as? NavigationController), forceExternal: false, openPeer: { (peerId, navigation) in switch navigation { case let .chat(_, subject, peekData): if let navigationController = controller?.navigationController as? NavigationController { diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 2f0f5c762e..e62dcedc88 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -88,36 +88,33 @@ public final class WebAppController: ViewController, AttachmentContainable { self.addSubnode(placeholderNode) self.placeholderNode = placeholderNode - let botId = controller.botId - let _ = (self.context.engine.messages.attachMenuBots() - |> take(1) - |> deliverOnMainQueue).start(next: { [weak self] bots in + let _ = (self.context.engine.messages.getAttachMenuBot(botId: controller.botId, cached: true) + |> deliverOnMainQueue).start(next: { [weak self] bot in guard let strongSelf = self else { return } - if let bot = bots.first(where: { $0.peer.id == botId }) { - var iconFile: TelegramMediaFile? - if let file = bot.icons[.iOSStatic] { - iconFile = file - } else if let file = bot.icons[.default] { - iconFile = file - } - if let iconFile = iconFile { - let _ = freeMediaFileInteractiveFetched(account: strongSelf.context.account, fileReference: .standalone(media: iconFile)).start() - strongSelf.iconDisposable = (svgIconImageFile(account: strongSelf.context.account, fileReference: .standalone(media: iconFile)) - |> deliverOnMainQueue).start(next: { [weak self] transform in - if let strongSelf = self { - let imageSize = CGSize(width: 75.0, height: 75.0) - let arguments = TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()) - let drawingContext = transform(arguments) - if let image = drawingContext?.generateImage()?.withRenderingMode(.alwaysTemplate) { - strongSelf.placeholderIcon = image - - strongSelf.updatePlaceholder() + var iconFile: TelegramMediaFile? + if let file = bot.icons[.iOSStatic] { + iconFile = file + } else if let file = bot.icons[.default] { + iconFile = file + } + if let iconFile = iconFile, let peer = PeerReference(bot.peer) { + let _ = freeMediaFileInteractiveFetched(account: strongSelf.context.account, fileReference: .attachBot(peer: peer, media: iconFile)).start() + strongSelf.iconDisposable = (svgIconImageFile(account: strongSelf.context.account, fileReference: .attachBot(peer: peer, media: iconFile)) + |> deliverOnMainQueue).start(next: { [weak self] transform in + if let strongSelf = self { + let imageSize = CGSize(width: 75.0, height: 75.0) + let arguments = TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()) + let drawingContext = transform(arguments) + if let image = drawingContext?.generateImage()?.withRenderingMode(.alwaysTemplate) { + strongSelf.placeholderIcon = image + if let (layout, navigationBarHeight) = strongSelf.validLayout { + strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) } } - }) - } + } + }) } }) @@ -185,12 +182,35 @@ public final class WebAppController: ViewController, AttachmentContainable { self.webView?.sendEvent(name: "main_button_pressed", data: nil) } - private func updatePlaceholder() { - guard let image = self.placeholderIcon else { - return + private func updatePlaceholder(grid: Bool, layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) -> CGSize { + var shapes: [ShimmerEffect.ShimmerEffectNode.Shape] = [] + var placeholderSize: CGSize = CGSize() + if grid { + let spacing: CGFloat = 8.0 + let cols: Int = min(4, Int(floor(layout.size.width / 118.0))) + let itemSize: CGSize = CGSize(width: cols == 4 ? 111.0 : 118.0, height: 150.0) + let rows: Int = 4 + + let sideInset = floorToScreenPixels((layout.size.width - layout.safeInsets.left - layout.safeInsets.right - itemSize.width * CGFloat(cols) - spacing * CGFloat(cols - 1)) / 2.0) + + for row in 0 ..< rows { + for col in 0 ..< cols { + shapes.append(.roundedRect(rect: CGRect(x: layout.safeInsets.left + sideInset + CGFloat(col) * (itemSize.width + spacing), y: navigationBarHeight + CGFloat(row) * (itemSize.height + spacing), width: itemSize.width, height: itemSize.height), cornerRadius: 10.0)) + } + } + + placeholderSize = layout.size + } else { + if let icon = self.placeholderIcon { + shapes = [.image(image: icon, rect: CGRect(origin: CGPoint(), size: icon.size))] + placeholderSize = icon.size + } } + let theme = self.presentationData.theme - self.placeholderNode?.update(backgroundColor: self.backgroundColor ?? .clear, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: [.image(image: image, rect: CGRect(origin: CGPoint(), size: image.size))], horizontal: true, size: image.size) + self.placeholderNode?.update(backgroundColor: self.backgroundColor ?? .clear, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), shapes: shapes, horizontal: true, size: placeholderSize) + + return placeholderSize } func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { @@ -258,22 +278,25 @@ public final class WebAppController: ViewController, AttachmentContainable { } if let placeholderNode = self.placeholderNode { - let iconSize = CGSize(width: 75.0, height: 75.0) +// let height: CGFloat +// if case .compact = layout.metrics.widthClass { +// height = layout.size.height - layout.additionalInsets.bottom - layout.intrinsicInsets.bottom +// } else { +// height = layout.size.height - layout.intrinsicInsets.bottom +// } - let height: CGFloat - if case .compact = layout.metrics.widthClass { - height = layout.size.height - layout.additionalInsets.bottom - layout.intrinsicInsets.bottom - } else { - height = layout.size.height - layout.intrinsicInsets.bottom - } - - let placeholderFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - iconSize.width) / 2.0), y: floorToScreenPixels((height - iconSize.height) / 2.0)), size: iconSize) + let grid = true + let placeholderSize = self.updatePlaceholder(grid: grid, layout: layout, navigationBarHeight: navigationBarHeight, transition: transition) + let placeholderY: CGFloat = 0.0 // grid ? 0.0 : floorToScreenPixels((height - placeholderSize.height) / 2.0) + let placeholderFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - placeholderSize.width) / 2.0), y: placeholderY), size: placeholderSize) transition.updateFrame(node: placeholderNode, frame: placeholderFrame) placeholderNode.updateAbsoluteRect(placeholderFrame, within: layout.size) } if let previousLayout = previousLayout, (previousLayout.inputHeight ?? 0.0).isZero, let inputHeight = layout.inputHeight, inputHeight > 44.0 { - self.controller?.requestAttachmentMenuExpansion() + Queue.mainQueue().justDispatch { + self.controller?.requestAttachmentMenuExpansion() + } } } diff --git a/submodules/WebUI/Sources/WebAppWebView.swift b/submodules/WebUI/Sources/WebAppWebView.swift index 3ed5ce7a96..51192c310a 100644 --- a/submodules/WebUI/Sources/WebAppWebView.swift +++ b/submodules/WebUI/Sources/WebAppWebView.swift @@ -121,4 +121,8 @@ final class WebAppWebView: WKWebView { @objc func handleTap() { self.didTouchOnce = true } + + override var inputAccessoryView: UIView? { + return nil + } }