diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 9a1d849e70..15a4b50bfe 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -10411,3 +10411,5 @@ Sorry for the inconvenience."; "ChannelBoost.EnableColors" = "Enable Colors"; "ChannelBoost.EnableColorsText" = "Your channel needs %1$@ to change channel color.\n\nAsk your **Premium** subscribers to boost your channel with this link:"; "ChannelBoost.BoostAgain" = "Boost Again"; + +"Settings.New" = "NEW"; diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift index 6a8c165c6f..87a14b5113 100644 --- a/submodules/PhotoResources/Sources/PhotoResources.swift +++ b/submodules/PhotoResources/Sources/PhotoResources.swift @@ -2421,6 +2421,25 @@ public func chatMessageImageFile(account: Account, userLocation: MediaResourceUs } } +public func preloadedBotIcon(account: Account, fileReference: FileMediaReference) -> Signal { + let signal = Signal { subscriber in + let fetched = fetchedMediaResource(mediaBox: account.postbox.mediaBox, userLocation: .other, userContentType: MediaResourceUserContentType(file: fileReference.media), reference: fileReference.resourceReference(fileReference.media.resource)).start() + let dataDisposable = account.postbox.mediaBox.resourceData(fileReference.media.resource, option: .incremental(waitUntilFetchStatus: false)).start(next: { data in + if data.complete { + subscriber.putNext(true) + subscriber.putCompletion() + } else { + subscriber.putNext(false) + } + }) + return ActionDisposable { + fetched.dispose() + dataDisposable.dispose() + } + } + return signal +} + public func instantPageImageFile(account: Account, userLocation: MediaResourceUserLocation, fileReference: FileMediaReference, fetched: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { return chatMessageFileDatas(account: account, userLocation: userLocation, fileReference: fileReference, progressive: false, fetched: fetched) |> map { value in diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index 61ea11af33..23b2021cb5 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -533,8 +533,8 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Italic)) case let .messageEntityCode(offset, length): result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Code)) - case let .messageEntityPre(offset, length, _): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Pre)) + case let .messageEntityPre(offset, length, language): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Pre(language: language))) case let .messageEntityTextUrl(offset, length, url): result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .TextUrl(url: url))) case let .messageEntityMentionName(offset, length, userId): diff --git a/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift b/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift index 85014d0805..15e1f0d005 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift @@ -28,8 +28,8 @@ func apiEntitiesFromMessageTextEntities(_ entities: [MessageTextEntity], associa apiEntities.append(.messageEntityItalic(offset: offset, length: length)) case .Code: apiEntities.append(.messageEntityCode(offset: offset, length: length)) - case .Pre: - apiEntities.append(.messageEntityPre(offset: offset, length: length, language: "")) + case let .Pre(language): + apiEntities.append(.messageEntityPre(offset: offset, length: length, language: language ?? "")) case let .TextUrl(url): apiEntities.append(.messageEntityTextUrl(offset: offset, length: length, url: url)) case let .TextMention(peerId): diff --git a/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift b/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift index 7724ed9162..957ab38068 100644 --- a/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift +++ b/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift @@ -732,8 +732,8 @@ private func parseEntities(_ entities: [SecretApi46.MessageEntity]?) -> TextEnti result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Italic)) case let .messageEntityCode(offset, length): result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Code)) - case let .messageEntityPre(offset, length, _): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Pre)) + case let .messageEntityPre(offset, length, language): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Pre(language: language))) case let .messageEntityTextUrl(offset, length, url): result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .TextUrl(url: url))) case .messageEntityUnknown: @@ -945,8 +945,8 @@ private func parseEntities(_ entities: [SecretApi73.MessageEntity]) -> TextEntit result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Italic)) case let .messageEntityCode(offset, length): result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Code)) - case let .messageEntityPre(offset, length, _): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Pre)) + case let .messageEntityPre(offset, length, language): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Pre(language: language))) case let .messageEntityTextUrl(offset, length, url): result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .TextUrl(url: url))) case .messageEntityUnknown: @@ -1177,8 +1177,8 @@ private func parseEntities(_ entities: [SecretApi101.MessageEntity]) -> TextEnti result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Italic)) case let .messageEntityCode(offset, length): result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Code)) - case let .messageEntityPre(offset, length, _): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Pre)) + case let .messageEntityPre(offset, length, language): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Pre(language: language))) case let .messageEntityTextUrl(offset, length, url): result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .TextUrl(url: url))) case let .messageEntityStrike(offset, length): @@ -1214,8 +1214,8 @@ private func parseEntities(_ entities: [SecretApi144.MessageEntity]) -> TextEnti result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Italic)) case let .messageEntityCode(offset, length): result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Code)) - case let .messageEntityPre(offset, length, _): - result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Pre)) + case let .messageEntityPre(offset, length, language): + result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .Pre(language: language))) case let .messageEntityTextUrl(offset, length, url): result.append(MessageTextEntity(range: Int(offset) ..< Int(offset + length), type: .TextUrl(url: url))) case let .messageEntityStrike(offset, length): diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift index 424fbfc7b8..b6b81056e7 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift @@ -13,7 +13,7 @@ public enum MessageTextEntityType: Equatable { case Bold case Italic case Code - case Pre + case Pre(language: String?) case TextUrl(url: String) case TextMention(peerId: PeerId) case PhoneNumber @@ -56,7 +56,7 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable { case 8: self.type = .Code case 9: - self.type = .Pre + self.type = .Pre(language: decoder.decodeOptionalStringForKey("language")) case 10: self.type = .TextUrl(url: decoder.decodeStringForKey("url", orElse: "")) case 11: @@ -112,7 +112,7 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable { case 8: self.type = .Code case 9: - self.type = .Pre + self.type = .Pre(language: try? container.decodeIfPresent(String.self, forKey: "language")) case 10: let url = (try? container.decode(String.self, forKey: "url")) ?? "" self.type = .TextUrl(url: url) @@ -163,8 +163,13 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable { encoder.encodeInt32(7, forKey: "_rawValue") case .Code: encoder.encodeInt32(8, forKey: "_rawValue") - case .Pre: + case let .Pre(language): encoder.encodeInt32(9, forKey: "_rawValue") + if let language = language { + encoder.encodeString(language, forKey: "language") + } else { + encoder.encodeNil(forKey: "language") + } case let .TextUrl(url): encoder.encodeInt32(10, forKey: "_rawValue") encoder.encodeString(url, forKey: "url") @@ -221,8 +226,9 @@ public struct MessageTextEntity: PostboxCoding, Codable, Equatable { try container.encode(7 as Int32, forKey: "_rawValue") case .Code: try container.encode(8 as Int32, forKey: "_rawValue") - case .Pre: + case let .Pre(language): try container.encode(9 as Int32, forKey: "_rawValue") + try container.encodeIfPresent(language, forKey: "language") case let .TextUrl(url): try container.encode(10 as Int32, forKey: "_rawValue") try container.encode(url, forKey: "url") diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift index 33861f0caa..da7da6772d 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift @@ -471,7 +471,7 @@ func _internal_acceptAttachMenuBotDisclaimer(postbox: Postbox, botId: PeerId) -> } |> ignoreValues } -public struct AttachMenuBot { +public struct AttachMenuBot: Equatable { public let peer: EnginePeer public let shortName: String public let icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile] diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 42e13830ef..4c453a7ef9 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -13551,6 +13551,26 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } completion(controller, controller.mediaPickerContext) strongSelf.controllerNavigationDisposable.set(nil) + + if bot.flags.contains(.notActivated) { + let alertController = webAppTermsAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, bot: bot, completion: { [weak self] allowWrite in + guard let self else { + return + } + if bot.flags.contains(.showInSettingsDisclaimer) { + let _ = self.context.engine.messages.acceptAttachMenuBotDisclaimer(botId: bot.peer.id).startStandalone() + } + let _ = (self.context.engine.messages.addBotToAttachMenu(botId: bot.peer.id, allowWrite: allowWrite) + |> deliverOnMainQueue).startStandalone(error: { _ in + }, completed: { [weak controller] in + controller?.refresh() + }) + }, + dismissed: { + strongSelf.attachmentController?.dismiss(animated: true) + }) + strongSelf.present(alertController, in: .window(.root)) + } default: break } diff --git a/submodules/TelegramUI/Sources/ContactMultiselectionController.swift b/submodules/TelegramUI/Sources/ContactMultiselectionController.swift index 66c579b8d6..888cc24934 100644 --- a/submodules/TelegramUI/Sources/ContactMultiselectionController.swift +++ b/submodules/TelegramUI/Sources/ContactMultiselectionController.swift @@ -459,23 +459,12 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection } chatsNode.updateState { state in var state = state - if "".isEmpty { - if !state.selectedAdditionalCategoryIds.contains(id) { - for id in state.selectedAdditionalCategoryIds { - removedTokenIds.append(id) - state.selectedAdditionalCategoryIds.remove(id) - } - state.selectedAdditionalCategoryIds.insert(id) - addedToken = categoryToken - } + if state.selectedAdditionalCategoryIds.contains(id) { + state.selectedAdditionalCategoryIds.remove(id) + removedTokenIds.append(id) } else { - if state.selectedAdditionalCategoryIds.contains(id) { - state.selectedAdditionalCategoryIds.remove(id) - removedTokenIds.append(id) - } else { - state.selectedAdditionalCategoryIds.insert(id) - addedToken = categoryToken - } + state.selectedAdditionalCategoryIds.insert(id) + addedToken = categoryToken } return state diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenDisclosureItem.swift b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenDisclosureItem.swift index 760f6f41d1..59d0c0c0c0 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenDisclosureItem.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenDisclosureItem.swift @@ -9,12 +9,13 @@ final class PeerInfoScreenDisclosureItem: PeerInfoScreenItem { case text(String) case badge(String, UIColor) case semitransparentBadge(String, UIColor) + case titleBadge(String, UIColor) var text: String { switch self { case .none: return "" - case let .text(text), let .badge(text, _), let .semitransparentBadge(text, _): + case let .text(text), let .badge(text, _), let .semitransparentBadge(text, _), let .titleBadge(text, _): return text } } @@ -23,7 +24,7 @@ final class PeerInfoScreenDisclosureItem: PeerInfoScreenItem { switch self { case .none, .text: return nil - case let .badge(_, color), let .semitransparentBadge(_, color): + case let .badge(_, color), let .semitransparentBadge(_, color), let .titleBadge(_, color): return color } } @@ -146,6 +147,9 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode { } else if case .badge = item.label { labelColorValue = presentationData.theme.list.itemCheckColors.foregroundColor labelFont = Font.regular(15.0) + } else if case .titleBadge = item.label { + labelColorValue = presentationData.theme.list.itemCheckColors.foregroundColor + labelFont = Font.medium(11.0) } else { labelColorValue = presentationData.theme.list.itemSecondaryTextColor labelFont = titleFont @@ -178,7 +182,7 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode { if previousItem?.text != item.text { self.iconNode.image = nil self.iconDisposable.set((iconSignal - |> deliverOnMainQueue).startStrict(next: { [weak self] icon in + |> deliverOnMainQueue).startStrict(next: { [weak self] icon in if let self { self.iconNode.image = icon } @@ -217,6 +221,13 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode { if self.labelBadgeNode.supernode == nil { self.insertSubnode(self.labelBadgeNode, belowSubnode: self.labelNode) } + } else if case let .titleBadge(text, badgeColor) = item.label, !text.isEmpty { + if previousItem?.label.badgeColor != badgeColor { + self.labelBadgeNode.image = generateFilledRoundedRectImage(size: CGSize(width: 16.0, height: 16.0), cornerRadius: 5.0, color: badgeColor)?.stretchableImage(withLeftCapWidth: 6, topCapHeight: 6) + } + if self.labelBadgeNode.supernode == nil { + self.insertSubnode(self.labelBadgeNode, belowSubnode: self.labelNode) + } } else { self.labelBadgeNode.removeFromSupernode() } @@ -230,11 +241,18 @@ private final class PeerInfoScreenDisclosureItemNode: PeerInfoScreenItemNode { labelFrame = CGRect(origin: CGPoint(x: width - rightInset - badgeWidth + (badgeWidth - labelSize.width) / 2.0, y: floor((height - labelSize.height) / 2.0)), size: labelSize) } else if case .badge = item.label { labelFrame = CGRect(origin: CGPoint(x: width - rightInset - badgeWidth + (badgeWidth - labelSize.width) / 2.0, y: floor((height - labelSize.height) / 2.0)), size: labelSize) + } else if case .titleBadge = item.label { + labelFrame = CGRect(origin: CGPoint(x: textFrame.maxX + 10.0, y: floor((height - labelSize.height) / 2.0) + 1.0), size: labelSize) } else { labelFrame = CGRect(origin: CGPoint(x: width - rightInset - labelSize.width, y: 12.0), size: labelSize) } - let labelBadgeNodeFrame = CGRect(origin: CGPoint(x: width - rightInset - badgeWidth, y: floorToScreenPixels(labelFrame.midY - badgeDiameter / 2.0)), size: CGSize(width: badgeWidth, height: badgeDiameter)) + let labelBadgeNodeFrame: CGRect + if case .titleBadge = item.label { + labelBadgeNodeFrame = labelFrame.insetBy(dx: -4.0, dy: -2.0 + UIScreenPixel) + } else { + labelBadgeNodeFrame = CGRect(origin: CGPoint(x: width - rightInset - badgeWidth, y: floorToScreenPixels(labelFrame.midY - badgeDiameter / 2.0)), size: CGSize(width: badgeWidth, height: badgeDiameter)) + } self.activateArea.accessibilityLabel = item.text self.activateArea.accessibilityValue = item.label.text diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift index cf8074299d..65d7a6cff0 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoData.swift @@ -13,6 +13,7 @@ import TelegramNotices import AccountUtils import DeviceAccess import PeerInfoVisualMediaPaneNode +import PhotoResources enum PeerInfoUpdatingAvatar { case none @@ -494,20 +495,56 @@ func peerInfoScreenSettingsData(context: AccountContext, peerId: EnginePeer.Id, let botsKey = ValueBoxKey(length: 8) botsKey.setInt64(0, value: 0) + + var iconLoaded: [EnginePeer.Id: Bool] = [:] let bots = context.engine.data.subscribe(TelegramEngine.EngineData.Item.ItemCache.Item(collectionId: Namespaces.CachedItemCollection.attachMenuBots, id: botsKey)) |> mapToSignal { entry -> Signal<[AttachMenuBot], NoError> in let bots: [AttachMenuBots.Bot] = entry?.get(AttachMenuBots.self)?.bots ?? [] return context.engine.data.subscribe( EngineDataMap(bots.map(\.peerId).map(TelegramEngine.EngineData.Item.Peer.Peer.init)) ) - |> map { peersMap -> [AttachMenuBot] in - var result: [AttachMenuBot] = [] + |> mapToSignal { peersMap -> Signal<[AttachMenuBot], NoError> in + var result: [Signal] = [] for bot in bots { if let maybePeer = peersMap[bot.peerId], let peer = maybePeer { - result.append(AttachMenuBot(peer: peer, shortName: bot.name, icons: bot.icons, peerTypes: bot.peerTypes, flags: bot.flags)) + let resultBot = AttachMenuBot(peer: peer, shortName: bot.name, icons: bot.icons, peerTypes: bot.peerTypes, flags: bot.flags) + if bot.flags.contains(.showInSettings) { + if let peer = PeerReference(peer._asPeer()), let icon = bot.icons[.iOSSettingsStatic] { + let fileReference: FileMediaReference = .attachBot(peer: peer, media: icon) + let signal: Signal + if let _ = iconLoaded[peer.id] { + signal = .single(resultBot) + } else { + signal = .single(nil) + |> then( + preloadedBotIcon(account: context.account, fileReference: fileReference) + |> filter { $0 } + |> map { _ -> AttachMenuBot? in + return resultBot + } + |> afterNext { _ in + iconLoaded[peer.id] = true + } + ) + } + result.append(signal) + } else { + result.append(.single(resultBot)) + } + } } } - return result + return combineLatest(result) + |> map { bots in + var result: [AttachMenuBot] = [] + for bot in bots { + if let bot { + result.append(bot) + } + } + return result + } + |> distinctUntilChanged } } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 900cb406fc..afcbe8364a 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -818,25 +818,24 @@ private func settingsItems(data: PeerInfoScreenData?, context: AccountContext, p var appIndex = 1000 if let settings = data.globalSettings { for bot in settings.bots { - if bot.flags.contains(.showInSettings) { - let iconSignal: Signal - if let peer = PeerReference(bot.peer._asPeer()), let icon = bot.icons[.iOSSettingsStatic] { - let fileReference: FileMediaReference = .attachBot(peer: peer, media: icon) - iconSignal = instantPageImageFile(account: context.account, userLocation: .other, fileReference: fileReference, fetched: true) - |> map { generator -> UIImage? in - let size = CGSize(width: 29.0, height: 29.0) - let context = generator(TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: .zero)) - return context?.generateImage() - } - let _ = freeMediaFileInteractiveFetched(account: context.account, userLocation: .other, fileReference: fileReference).startStandalone() - } else { - iconSignal = .single(UIImage(bundleImageName: "Settings/Menu/Websites")!) + let iconSignal: Signal + if let peer = PeerReference(bot.peer._asPeer()), let icon = bot.icons[.iOSSettingsStatic] { + let fileReference: FileMediaReference = .attachBot(peer: peer, media: icon) + iconSignal = instantPageImageFile(account: context.account, userLocation: .other, fileReference: fileReference, fetched: true) + |> map { generator -> UIImage? in + let size = CGSize(width: 29.0, height: 29.0) + let context = generator(TransformImageArguments(corners: ImageCorners(), imageSize: size, boundingSize: size, intrinsicInsets: .zero)) + return context?.generateImage() } - items[.apps]!.append(PeerInfoScreenDisclosureItem(id: bot.peer.id.id._internalGetInt64Value(), text: bot.shortName, icon: nil, iconSignal: iconSignal, action: { - interaction.openBotApp(bot) - })) - appIndex += 1 + let _ = freeMediaFileInteractiveFetched(account: context.account, userLocation: .other, fileReference: fileReference).startStandalone() + } else { + iconSignal = .single(UIImage(bundleImageName: "Settings/Menu/Websites")!) } + let label: PeerInfoScreenDisclosureItem.Label = bot.flags.contains(.notActivated) || bot.flags.contains(.showInSettingsDisclaimer) ? .titleBadge(presentationData.strings.Settings_New, presentationData.theme.list.itemAccentColor) : .none + items[.apps]!.append(PeerInfoScreenDisclosureItem(id: bot.peer.id.id._internalGetInt64Value(), label: label, text: bot.shortName, icon: nil, iconSignal: iconSignal, action: { + interaction.openBotApp(bot) + })) + appIndex += 1 } } @@ -4756,6 +4755,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro guard let self else { return } + let showInstalledTooltip = !bot.flags.contains(.showInSettingsDisclaimer) if bot.flags.contains(.showInSettingsDisclaimer) { let _ = self.context.engine.messages.acceptAttachMenuBotDisclaimer(botId: bot.peer.id).startStandalone() } @@ -4763,7 +4763,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro let _ = (self.context.engine.messages.addBotToAttachMenu(botId: bot.peer.id, allowWrite: allowWrite) |> deliverOnMainQueue).startStandalone(error: { _ in }, completed: { - proceed(true) + proceed(showInstalledTooltip) }) } else { proceed(false) diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 93500cf246..6bc6826fa0 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -413,6 +413,32 @@ public final class WebAppController: ViewController, AttachmentContainable { }) }) + self.setupWebView() + } + + deinit { + self.placeholderDisposable?.dispose() + self.iconDisposable?.dispose() + self.keepAliveDisposable?.dispose() + self.paymentDisposable?.dispose() + + self.webView?.removeObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress)) + } + + override func didLoad() { + super.didLoad() + + guard let webView = self.webView else { + return + } + self.view.addSubview(webView) + webView.scrollView.insertSubview(self.topOverscrollNode.view, at: 0) + } + + func setupWebView() { + guard let controller = self.controller else { + return + } if let url = controller.url, controller.source != .menu { self.queryId = controller.queryId if let parsedUrl = URL(string: url) { @@ -433,7 +459,7 @@ public final class WebAppController: ViewController, AttachmentContainable { } } else { if controller.source.isSimple { - let _ = (context.engine.messages.requestSimpleWebView(botId: controller.botId, url: nil, source: .settings, themeParams: generateWebAppThemeParams(presentationData.theme)) + let _ = (self.context.engine.messages.requestSimpleWebView(botId: controller.botId, url: nil, source: .settings, themeParams: generateWebAppThemeParams(presentationData.theme)) |> deliverOnMainQueue).start(next: { [weak self] result in guard let strongSelf = self else { return @@ -443,7 +469,7 @@ public final class WebAppController: ViewController, AttachmentContainable { } }) } else { - let _ = (context.engine.messages.requestWebView(peerId: controller.peerId, botId: controller.botId, url: controller.url, payload: controller.payload, themeParams: generateWebAppThemeParams(presentationData.theme), fromMenu: controller.source == .menu, replyToMessageId: controller.replyToMessageId, threadId: controller.threadId) + let _ = (self.context.engine.messages.requestWebView(peerId: controller.peerId, botId: controller.botId, url: controller.url, payload: controller.payload, themeParams: generateWebAppThemeParams(presentationData.theme), fromMenu: controller.source == .menu, replyToMessageId: controller.replyToMessageId, threadId: controller.threadId) |> deliverOnMainQueue).start(next: { [weak self] result in guard let strongSelf = self else { return @@ -469,25 +495,6 @@ public final class WebAppController: ViewController, AttachmentContainable { } } - deinit { - self.placeholderDisposable?.dispose() - self.iconDisposable?.dispose() - self.keepAliveDisposable?.dispose() - self.paymentDisposable?.dispose() - - self.webView?.removeObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress)) - } - - override func didLoad() { - super.didLoad() - - guard let webView = self.webView else { - return - } - self.view.addSubview(webView) - webView.scrollView.insertSubview(self.topOverscrollNode.view, at: 0) - } - @objc fileprivate func mainButtonPressed() { if let mainButtonState = self.mainButtonState, !mainButtonState.isVisible || !mainButtonState.isEnabled { return @@ -1624,6 +1631,10 @@ public final class WebAppController: ViewController, AttachmentContainable { self.updateTabBarAlpha(1.0, .immediate) } + public func refresh() { + self.controllerNode.setupWebView() + } + public func requestDismiss(completion: @escaping () -> Void) { if self.controllerNode.needDismissConfirmation { let actionSheet = ActionSheetController(presentationData: self.presentationData) diff --git a/submodules/WebUI/Sources/WebAppTermsAlertController.swift b/submodules/WebUI/Sources/WebAppTermsAlertController.swift index bbd5d81864..2151785cc6 100644 --- a/submodules/WebUI/Sources/WebAppTermsAlertController.swift +++ b/submodules/WebUI/Sources/WebAppTermsAlertController.swift @@ -353,7 +353,8 @@ public func webAppTermsAlertController( context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, bot: AttachMenuBot, - completion: @escaping (Bool) -> Void + completion: @escaping (Bool) -> Void, + dismissed: @escaping () -> Void = {} ) -> AlertController { let theme = defaultDarkColorPresentationTheme let presentationData: PresentationData @@ -369,6 +370,7 @@ public func webAppTermsAlertController( completion(true) dismissImpl?(true) }), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: { + dismissed() dismissImpl?(true) })] @@ -382,6 +384,11 @@ public func webAppTermsAlertController( }) } let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode) + controller.dismissed = { outside in + if outside { + dismissed() + } + } dismissImpl = { [weak controller] animated in if animated { controller?.dismissAnimated()