Various fixes

This commit is contained in:
Ilya Laktyushin 2024-06-27 23:29:15 +04:00
parent e23c7f2e2b
commit e348c620fa
15 changed files with 412 additions and 271 deletions

View File

@ -160,6 +160,12 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
}
return true
}
if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer.description.contains("WKDeferringGesture") {
return true
}
if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer.description.contains("UIWebTouchEventsGesture") {
return true
}
if gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UILongPressGestureRecognizer {
return true
}

View File

@ -263,7 +263,7 @@ public class AttachmentController: ViewController {
let panel: AttachmentPanel
fileprivate var currentType: AttachmentButtonType?
private var currentControllers: [AttachmentContainable] = []
fileprivate var currentControllers: [AttachmentContainable] = []
private var validLayout: ContainerViewLayout?
private var modalProgress: CGFloat = 0.0
@ -402,7 +402,7 @@ public class AttachmentController: ViewController {
if let current = current as? MinimizedContainerImpl {
minimizedContainer = current
} else if let context = self?.controller?.context {
minimizedContainer = MinimizedContainerImpl(context: context, navigationController: navigationController)
minimizedContainer = MinimizedContainerImpl(sharedContext: context.sharedContext)
} else {
minimizedContainer = nil
}
@ -954,7 +954,7 @@ public class AttachmentController: ViewController {
if fromMenu && !hasButton, let inputContainerHeight = self.inputContainerHeight {
panelHeight = inputContainerHeight
}
if hasPanel || hasButton || (fromMenu && isCompact) {
if hasPanel || hasButton {
containerInsets.bottom = panelHeight
}
@ -1145,6 +1145,12 @@ public class AttachmentController: ViewController {
return false
}
public override var isMinimized: Bool {
didSet {
self.mainController.isMinimized = self.isMinimized
}
}
private var validLayout: ContainerViewLayout?
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
@ -1160,6 +1166,10 @@ public class AttachmentController: ViewController {
self.node.containerLayoutUpdated(layout, transition: transition)
}
public var mainController: ViewController {
return self.node.currentControllers.first!
}
public final class InputPanelTransition {
let inputNode: ASDisplayNode
let accessoryPanelNode: ASDisplayNode?

View File

@ -2,6 +2,9 @@ import Foundation
import AsyncDisplayKit
public protocol MinimizedContainer: ASDisplayNode {
var navigationController: NavigationController? { get set }
var controllers: [ViewController] { get }
var willMaximize: (() -> Void)? { get set }
func addController(_ viewController: ViewController, transition: ContainedViewLayoutTransition)

View File

@ -150,7 +150,11 @@ open class NavigationController: UINavigationController, ContainableController,
private var rootModalFrame: NavigationModalFrame?
private var modalContainers: [NavigationModalContainer] = []
private var overlayContainers: [NavigationOverlayContainer] = []
private var minimizedContainer: MinimizedContainer?
open var minimizedContainer: MinimizedContainer? {
didSet {
self.minimizedContainer?.navigationController = self
}
}
private var globalOverlayContainers: [NavigationOverlayContainer] = []
private var globalOverlayBelowKeyboardContainerParent: GlobalOverlayContainerParent?
@ -826,6 +830,22 @@ open class NavigationController: UINavigationController, ContainableController,
layout.additionalInsets.left = max(layout.intrinsicInsets.left, additionalSideInsets.left)
layout.additionalInsets.right = max(layout.intrinsicInsets.right, additionalSideInsets.right)
var updatedSize = layout.size
var updatedIntrinsicInsets = layout.intrinsicInsets
if case .flat = navigationLayout.root, let minimizedContainer = self.minimizedContainer {
if minimizedContainer.supernode !== self.displayNode {
if let rootContainer = self.rootContainer, case let .flat(flatContainer) = rootContainer {
self.displayNode.insertSubnode(minimizedContainer, aboveSubnode: flatContainer)
} else {
self.displayNode.insertSubnode(minimizedContainer, at: 0)
}
}
if (layout.inputHeight ?? 0.0).isZero {
updatedSize.height -= minimizedContainer.collapsedHeight(layout: layout)
updatedIntrinsicInsets.bottom = 0.0
}
}
switch navigationLayout.root {
case let .flat(controllers):
if let rootContainer = self.rootContainer {
@ -839,12 +859,6 @@ open class NavigationController: UINavigationController, ContainableController,
flatContainer.canHaveKeyboardFocus = false
}
var updatedSize = layout.size
var updatedIntrinsicInsets = layout.intrinsicInsets
if let minimizedContainer = self.minimizedContainer, (layout.inputHeight ?? 0.0).isZero {
updatedSize.height -= minimizedContainer.collapsedHeight(layout: layout)
updatedIntrinsicInsets.bottom = 0.0
}
let updatedLayout = layout.withUpdatedSize(updatedSize).withUpdatedIntrinsicInsets(updatedIntrinsicInsets)
transition.updateFrame(node: flatContainer, frame: CGRect(origin: CGPoint(), size: updatedSize))
flatContainer.update(layout: updatedLayout, canBeClosed: false, controllers: controllers, transition: transition)
@ -904,8 +918,10 @@ open class NavigationController: UINavigationController, ContainableController,
self.displayNode.insertSubnode(flatContainer, at: 0)
}
self.rootContainer = .flat(flatContainer)
flatContainer.frame = CGRect(origin: CGPoint(), size: layout.size)
flatContainer.update(layout: layout, canBeClosed: false, controllers: controllers, transition: .immediate)
let updatedLayout = layout.withUpdatedSize(updatedSize).withUpdatedIntrinsicInsets(updatedIntrinsicInsets)
flatContainer.frame = CGRect(origin: CGPoint(), size: updatedSize)
flatContainer.update(layout: updatedLayout, canBeClosed: false, controllers: controllers, transition: .immediate)
}
case let .split(masterControllers, detailControllers):
if let rootContainer = self.rootContainer {
@ -931,6 +947,11 @@ open class NavigationController: UINavigationController, ContainableController,
splitContainer.update(layout: layout, masterControllers: masterControllers, detailControllers: detailControllers, detailsPlaceholderNode: self.detailsPlaceholderNode, transition: .immediate)
flatContainer.statusBarStyleUpdated = nil
flatContainer.removeFromSupernode()
if let minimizedContainer = self.minimizedContainer {
minimizedContainer.removeFromSupernode()
self.minimizedContainer = nil
}
case let .split(splitContainer):
if previousModalContainer == nil {
splitContainer.canHaveKeyboardFocus = true
@ -1554,17 +1575,11 @@ open class NavigationController: UINavigationController, ContainableController,
self.isMaximizing = true
self.updateContainersNonReentrant(transition: .animated(duration: 0.4, curve: .spring))
}
self.minimizedContainer?.removeFromSupernode()
self.minimizedContainer = minimizedContainer
if let minimizedContainer {
if let modalContainer = self.modalContainers.first {
self.displayNode.insertSubnode(minimizedContainer, belowSubnode: modalContainer)
} else {
self.displayNode.addSubnode(minimizedContainer)
}
}
self.updateContainersNonReentrant(transition: transition)
}
viewController.isMinimized = true

View File

@ -1195,7 +1195,9 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
var hintSeekable = false
if let contentInfo = item.contentInfo, case let .message(message, _) = contentInfo {
if Namespaces.Message.allNonRegular.contains(message.id.namespace) || message.id.namespace == Namespaces.Message.Local {
if message.paidContent != nil {
disablePictureInPicture = true
} else if Namespaces.Message.allNonRegular.contains(message.id.namespace) || message.id.namespace == Namespaces.Message.Local {
disablePictureInPicture = true
} else {
let throttledSignal = videoNode.status

View File

@ -49,6 +49,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
private let shadowNode: ASImageNode
private var controllerView: UIView?
fileprivate var snapshotView: UIView?
var tapped: (() -> Void)?
var highlighted: ((Bool) -> Void)?
@ -97,13 +98,19 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
self.shadowNode.image = shadowImage
self.addSubnode(self.containerNode)
if let snapshotView = self.item.controller.displayNode.view.snapshotView(afterScreenUpdates: false) {
self.controllerView = snapshotView
self.containerNode.view.addSubview(snapshotView)
} else {
self.controllerView = self.item.controller.displayNode.view
self.containerNode.view.addSubview(self.item.controller.displayNode.view)
self.controllerView = self.item.controller.displayNode.view
self.containerNode.view.addSubview(self.item.controller.displayNode.view)
Queue.mainQueue().after(0.45) {
if !self.isDismissed, let snapshotView = self.controllerView?.snapshotView(afterScreenUpdates: false) {
self.snapshotView = snapshotView
self.controllerView?.removeFromSuperview()
self.controllerView = snapshotView
self.containerNode.view.addSubview(snapshotView)
self.requestLayout(transition: .immediate)
}
}
self.addSubnode(self.headerNode)
self.addSubnode(self.dimCoverNode)
self.addSubnode(self.shadowNode)
@ -181,6 +188,13 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
}
}
private func requestLayout(transition: ContainedViewLayoutTransition) {
guard let (size, insets, isExpanded) = self.validLayout else {
return
}
self.updateLayout(size: size, insets: insets, isExpanded: isExpanded, transition: transition)
}
func updateLayout(size: CGSize, insets: UIEdgeInsets, isExpanded: Bool, transition: ContainedViewLayoutTransition) {
self.validLayout = (size, insets, isExpanded)
@ -203,7 +217,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
transition.updateFrame(node: self.headerNode, frame: headerFrame)
transition.updateFrame(node: self.dimCoverNode, frame: CGRect(origin: .zero, size: size))
if let controllerView = self.controllerView {
if let controllerView = self.snapshotView {
let controllerFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - controllerView.bounds.size.width) / 2.0), y: 0.0), size: controllerView.bounds.size)
transition.updateFrame(view: controllerView, frame: controllerFrame)
}
@ -214,8 +228,8 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
}
}
private let context: AccountContext
private weak var navigationController: NavigationController?
private let sharedContext: SharedAccountContext
public weak var navigationController: NavigationController?
private var items: [Item] = []
private var presentationData: PresentationData
@ -239,10 +253,13 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
private var isApplyingTransition = false
private var validLayout: ContainerViewLayout?
public init(context: AccountContext, navigationController: NavigationController) {
self.context = context
self.navigationController = navigationController
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
public var controllers: [ViewController] {
return self.items.map { $0.controller }
}
public init(sharedContext: SharedAccountContext) {
self.sharedContext = sharedContext
self.presentationData = sharedContext.currentPresentationData.with { $0 }
self.bottomEdgeView = UIImageView()
self.bottomEdgeView.contentMode = .scaleToFill
@ -275,7 +292,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
self.view.addSubview(self.dimView)
self.view.addSubview(self.scrollView)
self.presentationDataDisposable = (self.context.sharedContext.presentationData
self.presentationDataDisposable = (self.sharedContext.presentationData
|> deliverOnMainQueue).startStrict(next: { [weak self] presentationData in
guard let self else {
return
@ -488,6 +505,13 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
return
}
self.requestUpdate(transition: .immediate)
if scrollView.contentOffset.y < -64.0 {
self.isExpanded = false
scrollView.panGestureRecognizer.isEnabled = false
scrollView.panGestureRecognizer.isEnabled = true
self.requestUpdate(transition: .animated(duration: 0.4, curve: .spring))
}
}
private func requestUpdate(transition: ContainedViewLayoutTransition, completion: @escaping (Transition) -> Void = { _ in }) {
@ -773,6 +797,16 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
if self.currentTransition == currentTransition {
self.currentTransition = nil
}
if let snaphotView = itemNode.snapshotView {
itemNode.item.controller.displayNode.view.addSubview(snaphotView)
Queue.mainQueue().after(0.15, {
snaphotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
snaphotView.removeFromSuperview()
})
})
}
completion(currentTransition)
self.itemNodes[itemId] = nil
itemNode.removeFromSupernode()

View File

@ -5296,6 +5296,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
guard let controller = self.controller else {
return
}
if let navigationController = controller.navigationController as? NavigationController, let minimizedContainer = navigationController.minimizedContainer {
for controller in minimizedContainer.controllers {
if let controller = controller as? AttachmentController, let mainController = controller.mainController as? WebAppController, mainController.botId == bot.peer.id && mainController.source == .settings {
navigationController.maximizeViewController(controller, animated: true)
return
}
}
}
let presentationData = self.presentationData
let proceed: (Bool) -> Void = { [weak self] installed in
guard let self else {

View File

@ -178,6 +178,7 @@ final class SharedApplicationContext {
let notificationManager: SharedNotificationManager
let wakeupManager: SharedWakeupManager
let overlayMediaController: ViewController & OverlayMediaController
var minimizedContainer: MinimizedContainer?
init(sharedContext: SharedAccountContextImpl, notificationManager: SharedNotificationManager, wakeupManager: SharedWakeupManager) {
self.sharedContext = sharedContext

View File

@ -168,6 +168,10 @@ final class AuthorizedApplicationContext {
self.notificationController = NotificationContainerController(context: context)
self.rootController = TelegramRootController(context: context)
self.rootController.minimizedContainer = self.sharedApplicationContext.minimizedContainer
self.rootController.minimizedContainerUpdated = { [weak self] minimizedContainer in
self?.sharedApplicationContext.minimizedContainer = minimizedContainer
}
self.rootController.globalOverlayControllersUpdated = { [weak self] in
guard let strongSelf = self else {

View File

@ -0,0 +1,253 @@
import Foundation
import UIKit
import Display
import SwiftSignalKit
import TelegramCore
import ChatPresentationInterfaceState
import ChatControllerInteraction
import WebUI
import AttachmentUI
import AccountContext
import TelegramNotices
import PresentationDataUtils
extension ChatControllerImpl {
func openWebApp(buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource) {
guard let peerId = self.chatLocation.peerId, let peer = self.presentationInterfaceState.renderedPeer?.peer else {
return
}
self.chatDisplayNode.dismissInput()
let botName: String
let botAddress: String
if case let .inline(bot) = source {
botName = bot.compactDisplayTitle
botAddress = bot.addressName ?? ""
} else {
botName = EnginePeer(peer).displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)
botAddress = peer.addressName ?? ""
}
if source == .generic {
self.updateChatPresentationInterfaceState(animated: true, interactive: true, {
return $0.updatedTitlePanelContext {
if !$0.contains(where: {
switch $0 {
case .requestInProgress:
return true
default:
return false
}
}) {
var updatedContexts = $0
updatedContexts.append(.requestInProgress)
return updatedContexts.sorted()
}
return $0
}
})
}
let updateProgress = { [weak self] in
Queue.mainQueue().async {
if let strongSelf = self {
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
return $0.updatedTitlePanelContext {
if let index = $0.firstIndex(where: {
switch $0 {
case .requestInProgress:
return true
default:
return false
}
}) {
var updatedContexts = $0
updatedContexts.remove(at: index)
return updatedContexts
}
return $0
}
})
}
}
}
let openWebView = {
if source == .menu {
self.updateChatPresentationInterfaceState(interactive: false) { state in
return state.updatedForceInputCommandsHidden(true)
// return state.updatedShowWebView(true).updatedForceInputCommandsHidden(true)
}
if let currentMenuWebAppController = self.currentMenuWebAppController {
if currentMenuWebAppController.isMinimized {
(currentMenuWebAppController.navigationController as? NavigationController)?.maximizeViewController(currentMenuWebAppController, animated: true)
return
}
}
if let navigationController = self.navigationController as? NavigationController, let minimizedContainer = navigationController.minimizedContainer {
for controller in minimizedContainer.controllers {
if let controller = controller as? AttachmentController, let mainController = controller.mainController as? WebAppController, mainController.botId == peerId && mainController.source == .menu {
navigationController.maximizeViewController(controller, animated: true)
return
}
}
}
let context = self.context
let params = WebAppParameters(source: .menu, peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false)
let controller = standaloneWebAppController(context: self.context, updatedPresentationData: self.updatedPresentationData, params: params, threadId: self.chatLocation.threadId, openUrl: { [weak self] url, concealed, commit in
self?.openUrl(url, concealed: concealed, forceExternal: true, commit: commit)
}, requestSwitchInline: { [weak self] query, chatTypes, completion in
if let strongSelf = self {
if let chatTypes {
let controller = strongSelf.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: strongSelf.context, filter: [.excludeRecent, .doNotSearchMessages], requestPeerType: chatTypes, hasContactSelector: false, hasCreation: false))
controller.peerSelected = { [weak self, weak controller] peer, _ in
if let strongSelf = self {
completion()
controller?.dismiss()
strongSelf.controllerInteraction?.activateSwitchInline(peer.id, "@\(botAddress) \(query)", nil)
}
}
strongSelf.push(controller)
} else {
strongSelf.controllerInteraction?.activateSwitchInline(peerId, "@\(botAddress) \(query)", nil)
}
}
}, getInputContainerNode: { [weak self] in
if let strongSelf = self, let layout = strongSelf.validLayout, case .compact = layout.metrics.widthClass {
return (strongSelf.chatDisplayNode.getWindowInputAccessoryHeight(), strongSelf.chatDisplayNode.inputPanelContainerNode, {
return strongSelf.chatDisplayNode.textInputPanelNode?.makeAttachmentMenuTransition(accessoryPanelNode: nil)
})
} else {
return nil
}
}, completion: { [weak self] in
self?.chatDisplayNode.historyNode.scrollToEndOfHistory()
}, willDismiss: { [weak self] in
self?.interfaceInteraction?.updateShowWebView { _ in
return false
}
}, didDismiss: { [weak self] in
if let strongSelf = self {
let isFocused = strongSelf.chatDisplayNode.textInputPanelNode?.isFocused ?? false
strongSelf.chatDisplayNode.insertSubnode(strongSelf.chatDisplayNode.inputPanelContainerNode, aboveSubnode: strongSelf.chatDisplayNode.inputContextPanelContainer)
if isFocused {
strongSelf.chatDisplayNode.textInputPanelNode?.ensureFocused()
}
strongSelf.updateChatPresentationInterfaceState(interactive: false) { state in
return state.updatedForceInputCommandsHidden(false)
}
}
}, getNavigationController: { [weak self] in
return self?.effectiveNavigationController ?? context.sharedContext.mainWindow?.viewController as? NavigationController
})
controller.navigationPresentation = .flatModal
self.push(controller)
self.currentMenuWebAppController = controller
} else if simple {
var isInline = false
var botId = peerId
var botName = botName
var botAddress = ""
if case let .inline(bot) = source {
isInline = true
botId = bot.id
botName = bot.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder)
botAddress = bot.addressName ?? ""
}
self.messageActionCallbackDisposable.set(((self.context.engine.messages.requestSimpleWebView(botId: botId, url: url, source: isInline ? .inline : .generic, themeParams: generateWebAppThemeParams(self.presentationData.theme))
|> afterDisposed {
updateProgress()
})
|> deliverOnMainQueue).startStrict(next: { [weak self] url in
guard let strongSelf = self else {
return
}
let context = strongSelf.context
let params = WebAppParameters(source: isInline ? .inline : .simple, peerId: peerId, botId: botId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false)
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url, concealed, commit in
self?.openUrl(url, concealed: concealed, forceExternal: true, commit: commit)
}, requestSwitchInline: { [weak self] query, chatTypes, completion in
if let strongSelf = self {
if let chatTypes {
let controller = strongSelf.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: strongSelf.context, filter: [.excludeRecent, .doNotSearchMessages], requestPeerType: chatTypes, hasContactSelector: false, hasCreation: false))
controller.peerSelected = { [weak self, weak controller] peer, _ in
if let strongSelf = self {
completion()
controller?.dismiss()
strongSelf.controllerInteraction?.activateSwitchInline(peer.id, "@\(botAddress) \(query)", nil)
}
}
strongSelf.push(controller)
} else {
strongSelf.controllerInteraction?.activateSwitchInline(peerId, "@\(botAddress) \(query)", nil)
}
}
}, getNavigationController: { [weak self] in
return self?.effectiveNavigationController ?? context.sharedContext.mainWindow?.viewController as? NavigationController
})
controller.navigationPresentation = .flatModal
strongSelf.currentWebAppController = controller
strongSelf.push(controller)
}, error: { [weak self] error in
if let strongSelf = self {
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
})]), in: .window(.root))
}
}))
} else {
self.messageActionCallbackDisposable.set(((self.context.engine.messages.requestWebView(peerId: peerId, botId: peerId, url: !url.isEmpty ? url : nil, payload: nil, themeParams: generateWebAppThemeParams(self.presentationData.theme), fromMenu: buttonText == "Menu", replyToMessageId: nil, threadId: self.chatLocation.threadId)
|> afterDisposed {
updateProgress()
})
|> deliverOnMainQueue).startStrict(next: { [weak self] result in
guard let strongSelf = self else {
return
}
let context = strongSelf.context
let params = WebAppParameters(source: .generic, peerId: peerId, botId: peerId, botName: botName, url: result.url, queryId: result.queryId, payload: nil, buttonText: buttonText, keepAliveSignal: result.keepAliveSignal, forceHasSettings: false)
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url, concealed, commit in
self?.openUrl(url, concealed: concealed, forceExternal: true, commit: commit)
}, completion: { [weak self] in
self?.chatDisplayNode.historyNode.scrollToEndOfHistory()
}, getNavigationController: { [weak self] in
return self?.effectiveNavigationController ?? context.sharedContext.mainWindow?.viewController as? NavigationController
})
controller.navigationPresentation = .flatModal
strongSelf.currentWebAppController = controller
strongSelf.push(controller)
}, error: { [weak self] error in
if let strongSelf = self {
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
})]), in: .window(.root))
}
}))
}
}
var botPeer = EnginePeer(peer)
if case let .inline(bot) = source {
botPeer = bot
}
let _ = (ApplicationSpecificNotice.getBotGameNotice(accountManager: self.context.sharedContext.accountManager, peerId: botPeer.id)
|> deliverOnMainQueue).startStandalone(next: { [weak self] value in
guard let strongSelf = self else {
return
}
if value {
openWebView()
} else {
let controller = webAppLaunchConfirmationController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: botPeer, completion: { _ in
let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: strongSelf.context.sharedContext.accountManager, peerId: botPeer.id).startStandalone()
openWebView()
}, showMore: nil)
strongSelf.present(controller, in: .window(.root))
}
})
}
}

View File

@ -574,15 +574,7 @@ func updateChatPresentationInterfaceStateImpl(
controller.updateVisibility()
}
}
if let currentMenuWebAppController = selfController.currentMenuWebAppController, !selfController.presentationInterfaceState.showWebView {
selfController.currentMenuWebAppController = nil
if let currentMenuWebAppController = currentMenuWebAppController as? AttachmentController {
currentMenuWebAppController.ensureUnfocused = false
}
currentMenuWebAppController.dismiss(animated: true, completion: nil)
}
selfController.presentationInterfaceStatePromise.set(selfController.presentationInterfaceState)
if case .tag = selfController.chatDisplayNode.historyNode.tag {

View File

@ -3691,227 +3691,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
strongSelf.openResolved(result: .join(joinHash), sourceMessageId: nil)
}, openWebView: { [weak self] buttonText, url, simple, source in
guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
guard let self else {
return
}
strongSelf.chatDisplayNode.dismissInput()
let botName: String
let botAddress: String
if case let .inline(bot) = source {
botName = bot.compactDisplayTitle
botAddress = bot.addressName ?? ""
} else {
botName = EnginePeer(peer).displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)
botAddress = peer.addressName ?? ""
}
if source == .generic {
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
return $0.updatedTitlePanelContext {
if !$0.contains(where: {
switch $0 {
case .requestInProgress:
return true
default:
return false
}
}) {
var updatedContexts = $0
updatedContexts.append(.requestInProgress)
return updatedContexts.sorted()
}
return $0
}
})
}
let updateProgress = { [weak self] in
Queue.mainQueue().async {
if let strongSelf = self {
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
return $0.updatedTitlePanelContext {
if let index = $0.firstIndex(where: {
switch $0 {
case .requestInProgress:
return true
default:
return false
}
}) {
var updatedContexts = $0
updatedContexts.remove(at: index)
return updatedContexts
}
return $0
}
})
}
}
}
let openWebView = {
if source == .menu {
strongSelf.updateChatPresentationInterfaceState(interactive: false) { state in
return state.updatedForceInputCommandsHidden(true)
// return state.updatedShowWebView(true).updatedForceInputCommandsHidden(true)
}
let context = strongSelf.context
let params = WebAppParameters(source: .menu, peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false)
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url, concealed, commit in
self?.openUrl(url, concealed: concealed, forceExternal: true, commit: commit)
}, requestSwitchInline: { [weak self] query, chatTypes, completion in
if let strongSelf = self {
if let chatTypes {
let controller = strongSelf.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: strongSelf.context, filter: [.excludeRecent, .doNotSearchMessages], requestPeerType: chatTypes, hasContactSelector: false, hasCreation: false))
controller.peerSelected = { [weak self, weak controller] peer, _ in
if let strongSelf = self {
completion()
controller?.dismiss()
strongSelf.controllerInteraction?.activateSwitchInline(peer.id, "@\(botAddress) \(query)", nil)
}
}
strongSelf.push(controller)
} else {
strongSelf.controllerInteraction?.activateSwitchInline(peerId, "@\(botAddress) \(query)", nil)
}
}
}, getInputContainerNode: { [weak self] in
if let strongSelf = self, let layout = strongSelf.validLayout, case .compact = layout.metrics.widthClass {
return (strongSelf.chatDisplayNode.getWindowInputAccessoryHeight(), strongSelf.chatDisplayNode.inputPanelContainerNode, {
return strongSelf.chatDisplayNode.textInputPanelNode?.makeAttachmentMenuTransition(accessoryPanelNode: nil)
})
} else {
return nil
}
}, completion: { [weak self] in
self?.chatDisplayNode.historyNode.scrollToEndOfHistory()
}, willDismiss: { [weak self] in
self?.interfaceInteraction?.updateShowWebView { _ in
return false
}
}, didDismiss: { [weak self] in
if let strongSelf = self {
let isFocused = strongSelf.chatDisplayNode.textInputPanelNode?.isFocused ?? false
strongSelf.chatDisplayNode.insertSubnode(strongSelf.chatDisplayNode.inputPanelContainerNode, aboveSubnode: strongSelf.chatDisplayNode.inputContextPanelContainer)
if isFocused {
strongSelf.chatDisplayNode.textInputPanelNode?.ensureFocused()
}
strongSelf.updateChatPresentationInterfaceState(interactive: false) { state in
return state.updatedForceInputCommandsHidden(false)
}
}
}, getNavigationController: { [weak self] in
return self?.effectiveNavigationController ?? context.sharedContext.mainWindow?.viewController as? NavigationController
})
controller.navigationPresentation = .flatModal
strongSelf.push(controller)
strongSelf.currentMenuWebAppController = controller
} else if simple {
var isInline = false
var botId = peerId
var botName = botName
var botAddress = ""
if case let .inline(bot) = source {
isInline = true
botId = bot.id
botName = bot.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)
botAddress = bot.addressName ?? ""
}
strongSelf.messageActionCallbackDisposable.set(((strongSelf.context.engine.messages.requestSimpleWebView(botId: botId, url: url, source: isInline ? .inline : .generic, themeParams: generateWebAppThemeParams(strongSelf.presentationData.theme))
|> afterDisposed {
updateProgress()
})
|> deliverOnMainQueue).startStrict(next: { [weak self] url in
guard let strongSelf = self else {
return
}
let context = strongSelf.context
let params = WebAppParameters(source: isInline ? .inline : .simple, peerId: peerId, botId: botId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false)
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url, concealed, commit in
self?.openUrl(url, concealed: concealed, forceExternal: true, commit: commit)
}, requestSwitchInline: { [weak self] query, chatTypes, completion in
if let strongSelf = self {
if let chatTypes {
let controller = strongSelf.context.sharedContext.makePeerSelectionController(PeerSelectionControllerParams(context: strongSelf.context, filter: [.excludeRecent, .doNotSearchMessages], requestPeerType: chatTypes, hasContactSelector: false, hasCreation: false))
controller.peerSelected = { [weak self, weak controller] peer, _ in
if let strongSelf = self {
completion()
controller?.dismiss()
strongSelf.controllerInteraction?.activateSwitchInline(peer.id, "@\(botAddress) \(query)", nil)
}
}
strongSelf.push(controller)
} else {
strongSelf.controllerInteraction?.activateSwitchInline(peerId, "@\(botAddress) \(query)", nil)
}
}
}, getNavigationController: { [weak self] in
return self?.effectiveNavigationController ?? context.sharedContext.mainWindow?.viewController as? NavigationController
})
controller.navigationPresentation = .flatModal
strongSelf.currentWebAppController = controller
strongSelf.push(controller)
}, error: { [weak self] error in
if let strongSelf = self {
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
})]), in: .window(.root))
}
}))
} else {
strongSelf.messageActionCallbackDisposable.set(((strongSelf.context.engine.messages.requestWebView(peerId: peerId, botId: peerId, url: !url.isEmpty ? url : nil, payload: nil, themeParams: generateWebAppThemeParams(strongSelf.presentationData.theme), fromMenu: buttonText == "Menu", replyToMessageId: nil, threadId: strongSelf.chatLocation.threadId)
|> afterDisposed {
updateProgress()
})
|> deliverOnMainQueue).startStrict(next: { [weak self] result in
guard let strongSelf = self else {
return
}
let context = strongSelf.context
let params = WebAppParameters(source: .generic, peerId: peerId, botId: peerId, botName: botName, url: result.url, queryId: result.queryId, payload: nil, buttonText: buttonText, keepAliveSignal: result.keepAliveSignal, forceHasSettings: false)
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url, concealed, commit in
self?.openUrl(url, concealed: concealed, forceExternal: true, commit: commit)
}, completion: { [weak self] in
self?.chatDisplayNode.historyNode.scrollToEndOfHistory()
}, getNavigationController: { [weak self] in
return self?.effectiveNavigationController ?? context.sharedContext.mainWindow?.viewController as? NavigationController
})
controller.navigationPresentation = .flatModal
strongSelf.currentWebAppController = controller
strongSelf.push(controller)
}, error: { [weak self] error in
if let strongSelf = self {
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
})]), in: .window(.root))
}
}))
}
}
var botPeer = EnginePeer(peer)
if case let .inline(bot) = source {
botPeer = bot
}
let _ = (ApplicationSpecificNotice.getBotGameNotice(accountManager: strongSelf.context.sharedContext.accountManager, peerId: botPeer.id)
|> deliverOnMainQueue).startStandalone(next: { value in
guard let strongSelf = self else {
return
}
if value {
openWebView()
} else {
let controller = webAppLaunchConfirmationController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: botPeer, completion: { _ in
let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: strongSelf.context.sharedContext.accountManager, peerId: botPeer.id).startStandalone()
openWebView()
}, showMore: nil)
strongSelf.present(controller, in: .window(.root))
}
})
self.openWebApp(buttonText: buttonText, url: url, simple: simple, source: source)
}, activateAdAction: { [weak self] messageId, progress in
guard let self, let message = self.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId), let adAttribute = message.adAttribute else {
return

View File

@ -88,6 +88,15 @@ public final class TelegramRootController: NavigationController, TelegramRootCon
private var applicationInFocusDisposable: Disposable?
private var storyUploadEventsDisposable: Disposable?
override public var minimizedContainer: MinimizedContainer? {
didSet {
self.minimizedContainer?.navigationController = self
self.minimizedContainerUpdated(self.minimizedContainer)
}
}
public var minimizedContainerUpdated: (MinimizedContainer?) -> Void = { _ in }
public init(context: AccountContext) {
self.context = context

View File

@ -1771,9 +1771,9 @@ public final class WebAppController: ViewController, AttachmentContainable {
fileprivate let moreButtonNode: MoreButtonNode
private let context: AccountContext
private let source: WebAppParameters.Source
public let source: WebAppParameters.Source
private let peerId: PeerId
private let botId: PeerId
public let botId: PeerId
private let botName: String
private let url: String?
private let queryId: Int64?
@ -2094,13 +2094,17 @@ public final class WebAppController: ViewController, AttachmentContainable {
}
}
public func shouldDismissImmediately() -> Bool {
if self.controllerNode.needDismissConfirmation {
return false
} else {
return true
public override var isMinimized: Bool {
didSet {
if self.isMinimized != oldValue && self.isMinimized {
self.controllerNode.webView?.hideScrollIndicators()
}
}
}
public func shouldDismissImmediately() -> Bool {
return true
}
}
final class WebAppPickerContext: AttachmentMediaPickerContext {
@ -2182,7 +2186,6 @@ public func standaloneWebAppController(
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: params.peerId), buttons: [.standalone], initialButton: .standalone, fromMenu: params.source == .menu, hasTextInput: false, makeEntityInputView: {
return nil
})
// controller.getInputContainerNode = getInputContainerNode
controller.requestController = { _, present in
let webAppController = WebAppController(context: context, updatedPresentationData: updatedPresentationData, params: params, replyToMessageId: nil, threadId: threadId)
webAppController.openUrl = openUrl

View File

@ -194,6 +194,22 @@ final class WebAppWebView: WKWebView {
}
}
func hideScrollIndicators() {
var hiddenViews: [UIView] = []
for view in self.scrollView.subviews.reversed() {
let minSize = min(view.frame.width, view.frame.height)
if minSize < 4.0 {
view.isHidden = true
hiddenViews.append(view)
}
}
Queue.mainQueue().after(2.0) {
for view in hiddenViews {
view.isHidden = false
}
}
}
func sendEvent(name: String, data: String?) {
let script = "window.TelegramGameProxy.receiveEvent(\"\(name)\", \(data ?? "null"))"
self.evaluateJavaScript(script, completionHandler: { _, _ in