Web app improvements

This commit is contained in:
Ilya Laktyushin 2024-06-28 23:41:21 +04:00
parent 9d3cc5996b
commit ffbc7921cf
35 changed files with 916 additions and 718 deletions

View File

@ -108,6 +108,7 @@
"PUSH_MESSAGE_NOTHEME" = "%1$@|disabled chat theme"; "PUSH_MESSAGE_NOTHEME" = "%1$@|disabled chat theme";
"PUSH_MESSAGE_RECURRING_PAY" = "%1$@|You were charged %2$@"; "PUSH_MESSAGE_RECURRING_PAY" = "%1$@|You were charged %2$@";
"CHAT_MESSAGE_RECURRING_PAY" = "%1$@|You were charged %2$@"; "CHAT_MESSAGE_RECURRING_PAY" = "%1$@|You were charged %2$@";
"PUSH_MESSAGE_PAID_MEDIA" = "%1$@sent you a paid post for %2$@ stars";
"PUSH_CHANNEL_MESSAGE_TEXT" = "%1$@|%2$@"; "PUSH_CHANNEL_MESSAGE_TEXT" = "%1$@|%2$@";
"PUSH_CHANNEL_MESSAGE_NOTEXT" = "%1$@|posted a message"; "PUSH_CHANNEL_MESSAGE_NOTEXT" = "%1$@|posted a message";
@ -138,6 +139,7 @@
"PUSH_CHANNEL_ALBUM" = "%1$@|posted an album"; "PUSH_CHANNEL_ALBUM" = "%1$@|posted an album";
"PUSH_CHANNEL_MESSAGE_DOCS_TEXT_1" = "posted a file"; "PUSH_CHANNEL_MESSAGE_DOCS_TEXT_1" = "posted a file";
"PUSH_CHANNEL_MESSAGE_DOCS_TEXT_any" = "posted %d files"; "PUSH_CHANNEL_MESSAGE_DOCS_TEXT_any" = "posted %d files";
"PUSH_CHANNEL_MESSAGE_PAID_MEDIA" = "%1$@ sent a paid post for %2$@ stars";
"PUSH_CHAT_MESSAGE_TEXT" = "%2$@|%1$@:%3$@"; "PUSH_CHAT_MESSAGE_TEXT" = "%2$@|%1$@:%3$@";
"PUSH_CHAT_MESSAGE_NOTEXT" = "%2$@|%1$@ sent a message to the group"; "PUSH_CHAT_MESSAGE_NOTEXT" = "%2$@|%1$@ sent a message to the group";
@ -183,6 +185,7 @@
"PUSH_CHAT_MESSAGE_THEME" = "%1$@|set theme to %3$@ in the group %2$@"; "PUSH_CHAT_MESSAGE_THEME" = "%1$@|set theme to %3$@ in the group %2$@";
"PUSH_CHAT_MESSAGE_NOTHEME" = "%1$@|disabled theme in the group %2$@"; "PUSH_CHAT_MESSAGE_NOTHEME" = "%1$@|disabled theme in the group %2$@";
"PUSH_CHAT_REQ_JOINED" = "%1$@ was accepted into the group %2$@"; "PUSH_CHAT_REQ_JOINED" = "%1$@ was accepted into the group %2$@";
"PUSH_CHAT_MESSAGE_PAID_MEDIA" = "%1$@ sent a paid post to the group %2$@ for %3$@ stars";
"PUSH_PINNED_TEXT" = "%1$@|pinned \"%2$@\" "; "PUSH_PINNED_TEXT" = "%1$@|pinned \"%2$@\" ";
"PUSH_PINNED_NOTEXT" = "%1$@|pinned a message"; "PUSH_PINNED_NOTEXT" = "%1$@|pinned a message";
@ -200,6 +203,7 @@
"PUSH_PINNED_GAME" = "%1$@|pinned a game"; "PUSH_PINNED_GAME" = "%1$@|pinned a game";
"PUSH_PINNED_INVOICE" = "%1$@|pinned an invoice"; "PUSH_PINNED_INVOICE" = "%1$@|pinned an invoice";
"PUSH_PINNED_GIF" = "%1$@|pinned a GIF"; "PUSH_PINNED_GIF" = "%1$@|pinned a GIF";
"PUSH_PINNED_PAID_MEDIA" = "%1$@|pinned a paid post for %2$@";
"PUSH_CONTACT_JOINED" = "%1$@|joined Telegram!"; "PUSH_CONTACT_JOINED" = "%1$@|joined Telegram!";
@ -252,6 +256,7 @@
"PUSH_CHAT_REACT_GAME" = "%2$@|%1$@ %3$@ to your game"; "PUSH_CHAT_REACT_GAME" = "%2$@|%1$@ %3$@ to your game";
"PUSH_CHAT_REACT_INVOICE" = "%2$@|%1$@ %3$@ to your invoice"; "PUSH_CHAT_REACT_INVOICE" = "%2$@|%1$@ %3$@ to your invoice";
"PUSH_CHAT_REACT_GIF" = "%2$@|%1$@ %3$@ to your GIF"; "PUSH_CHAT_REACT_GIF" = "%2$@|%1$@ %3$@ to your GIF";
"PUSH_CHAT_REACT_PAID_MEDIA" = "%2$@|%1$@ %3$@ to your paid post";
"PUSH_MESSAGE_SUGGEST_USERPIC" = "%1$@|suggested you new profile photo"; "PUSH_MESSAGE_SUGGEST_USERPIC" = "%1$@|suggested you new profile photo";
@ -273,6 +278,7 @@
"PUSH_REACT_STORY" = "%1$@|%2$@ to your story"; "PUSH_REACT_STORY" = "%1$@|%2$@ to your story";
"PUSH_REACT_STORY_HIDDEN" = "New reaction to your story"; "PUSH_REACT_STORY_HIDDEN" = "New reaction to your story";
"LOCAL_MESSAGE_FWDS" = "%1$@ forwarded you %2$d messages"; "LOCAL_MESSAGE_FWDS" = "%1$@ forwarded you %2$d messages";
"LOCAL_CHANNEL_MESSAGE_FWDS" = "%1$@ posted %2$d forwarded messages"; "LOCAL_CHANNEL_MESSAGE_FWDS" = "%1$@ posted %2$d forwarded messages";
"LOCAL_CHAT_MESSAGE_FWDS" = "%1$@ forwarded %2$d messages"; "LOCAL_CHAT_MESSAGE_FWDS" = "%1$@ forwarded %2$d messages";

View File

@ -295,11 +295,13 @@ public struct ChatControllerInitialBotAppStart {
public let botApp: BotApp public let botApp: BotApp
public let payload: String? public let payload: String?
public let justInstalled: Bool public let justInstalled: Bool
public let compact: Bool
public init(botApp: BotApp, payload: String?, justInstalled: Bool) { public init(botApp: BotApp, payload: String?, justInstalled: Bool, compact: Bool) {
self.botApp = botApp self.botApp = botApp
self.payload = payload self.payload = payload
self.justInstalled = justInstalled self.justInstalled = justInstalled
self.compact = compact
} }
} }

View File

@ -47,6 +47,7 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
private var isDismissed = false private var isDismissed = false
private var isInteractiveDimissEnabled = true private var isInteractiveDimissEnabled = true
private let isFullSize: Bool
public private(set) var isExpanded = false public private(set) var isExpanded = false
private var validLayout: (layout: ContainerViewLayout, controllers: [AttachmentContainable], coveredByModalTransition: CGFloat)? private var validLayout: (layout: ContainerViewLayout, controllers: [AttachmentContainable], coveredByModalTransition: CGFloat)?
@ -72,7 +73,12 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
var isPanGestureEnabled: (() -> Bool)? var isPanGestureEnabled: (() -> Bool)?
var onExpandAnimationCompleted: () -> Void = {} var onExpandAnimationCompleted: () -> Void = {}
override init() { init(isFullSize: Bool) {
self.isFullSize = isFullSize
if isFullSize {
self.isExpanded = true
}
self.wrappingNode = ASDisplayNode() self.wrappingNode = ASDisplayNode()
self.clipNode = ASDisplayNode() self.clipNode = ASDisplayNode()
@ -268,14 +274,14 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
} }
} }
if !self.isExpanded, translation > 40.0, let shouldCancelPanGesture = self.shouldCancelPanGesture, shouldCancelPanGesture() { if !self.isExpanded || self.isFullSize, translation > 40.0, let shouldCancelPanGesture = self.shouldCancelPanGesture, shouldCancelPanGesture() {
self.cancelPanGesture() self.cancelPanGesture()
self.requestDismiss?() self.requestDismiss?()
return return
} }
var bounds = self.bounds var bounds = self.bounds
if self.isExpanded { if self.isExpanded && !self.isFullSize {
bounds.origin.y = -max(0.0, translation - edgeTopInset) bounds.origin.y = -max(0.0, translation - edgeTopInset)
} else { } else {
bounds.origin.y = -translation bounds.origin.y = -translation
@ -307,7 +313,7 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
} }
var bounds = self.bounds var bounds = self.bounds
if self.isExpanded { if self.isExpanded && !self.isFullSize {
bounds.origin.y = -max(0.0, translation - edgeTopInset) bounds.origin.y = -max(0.0, translation - edgeTopInset)
} else { } else {
bounds.origin.y = -translation bounds.origin.y = -translation
@ -326,21 +332,29 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
var minimizing = false var minimizing = false
var dismissing = false var dismissing = false
if (bounds.minY < -60 || (bounds.minY < 0.0 && velocity.y > 300.0) || (self.isExpanded && bounds.minY.isZero && velocity.y > 1800.0)) && !ignoreDismiss {
let thresholdOffset: CGFloat
if self.isFullSize {
thresholdOffset = -180.0
} else {
thresholdOffset = -60.0
}
if (bounds.minY < thresholdOffset || (bounds.minY < 0.0 && velocity.y > 300.0) || (self.isExpanded && bounds.minY.isZero && velocity.y > 1800.0)) && !ignoreDismiss {
if self.interactivelyDismissed?(velocity.y) == true { if self.interactivelyDismissed?(velocity.y) == true {
dismissing = true dismissing = true
} else { } else {
minimizing = true minimizing = true
} }
} else if self.isExpanded { } else if self.isExpanded {
if velocity.y > 300.0 || offset > topInset / 2.0 { if (velocity.y > 300.0 || offset > topInset / 2.0) && !self.isFullSize {
self.isExpanded = false self.isExpanded = false
if let listNode = listNode { if let listNode = listNode {
listNode.scroller.setContentOffset(CGPoint(), animated: false) listNode.scroller.setContentOffset(CGPoint(), animated: false)
} else if let scrollView = scrollView { } else if let scrollView = scrollView {
scrollView.setContentOffset(CGPoint(x: scrollView.contentOffset.x, y: -scrollView.contentInset.top), animated: false) scrollView.setContentOffset(CGPoint(x: scrollView.contentOffset.x, y: -scrollView.contentInset.top), animated: false)
} }
let distance = topInset - offset let distance = topInset - offset
let initialVelocity: CGFloat = distance.isZero ? 0.0 : abs(velocity.y / distance) let initialVelocity: CGFloat = distance.isZero ? 0.0 : abs(velocity.y / distance)
let transition = ContainedViewLayoutTransition.animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity)) let transition = ContainedViewLayoutTransition.animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity))
@ -432,6 +446,7 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
} }
self.isUpdatingState = true self.isUpdatingState = true
let isFirstTime = self.validLayout == nil
self.validLayout = (layout, controllers, coveredByModalTransition) self.validLayout = (layout, controllers, coveredByModalTransition)
self.panGestureRecognizer?.isEnabled = (layout.inputHeight == nil || layout.inputHeight == 0.0) self.panGestureRecognizer?.isEnabled = (layout.inputHeight == nil || layout.inputHeight == 0.0)
@ -446,7 +461,7 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
} }
let topInset: CGFloat let topInset: CGFloat
if let (panInitialTopInset, panOffset, _, _) = self.panGestureArguments { if !self.isFullSize, let (panInitialTopInset, panOffset, _, _) = self.panGestureArguments {
if effectiveExpanded { if effectiveExpanded {
topInset = min(edgeTopInset, panInitialTopInset + max(0.0, panOffset)) topInset = min(edgeTopInset, panInitialTopInset + max(0.0, panOffset))
} else { } else {
@ -459,9 +474,29 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
completion() completion()
}) })
let modalProgress = isLandscape ? 0.0 : (1.0 - topInset / defaultTopInset) let modalProgress: CGFloat
self.updateModalProgress?(modalProgress, topInset, self.bounds, transition) if isLandscape {
modalProgress = 0.0
} else {
if self.isFullSize, self.panGestureArguments != nil {
modalProgress = 1.0 - min(1.0, max(0.0, -1.0 * self.bounds.minY / defaultTopInset))
} else {
modalProgress = 1.0 - topInset / defaultTopInset
}
}
if isFirstTime {
Queue.mainQueue().justDispatch {
var transition = transition
if modalProgress == 1.0 {
transition = .animated(duration: 0.4, curve: .spring)
}
self.updateModalProgress?(modalProgress, topInset, self.bounds, transition)
}
} else {
self.updateModalProgress?(modalProgress, topInset, self.bounds, transition)
}
let containerLayout: ContainerViewLayout let containerLayout: ContainerViewLayout
let containerFrame: CGRect let containerFrame: CGRect
let clipFrame: CGRect let clipFrame: CGRect

View File

@ -131,6 +131,8 @@ public protocol AttachmentContainable: ViewController {
func requestDismiss(completion: @escaping () -> Void) func requestDismiss(completion: @escaping () -> Void)
func shouldDismissImmediately() -> Bool func shouldDismissImmediately() -> Bool
func beforeMaximize(navigationController: NavigationController, completion: @escaping () -> Void)
} }
public extension AttachmentContainable { public extension AttachmentContainable {
@ -154,6 +156,10 @@ public extension AttachmentContainable {
return true return true
} }
func beforeMaximize(navigationController: NavigationController, completion: @escaping () -> Void) {
completion()
}
var isPanGestureEnabled: (() -> Bool)? { var isPanGestureEnabled: (() -> Bool)? {
return nil return nil
} }
@ -234,6 +240,7 @@ public class AttachmentController: ViewController {
private let initialButton: AttachmentButtonType private let initialButton: AttachmentButtonType
private let fromMenu: Bool private let fromMenu: Bool
private var hasTextInput: Bool private var hasTextInput: Bool
private let isFullSize: Bool
private let makeEntityInputView: () -> AttachmentTextInputPanelInputView? private let makeEntityInputView: () -> AttachmentTextInputPanelInputView?
public var animateAppearance: Bool = false public var animateAppearance: Bool = false
@ -345,7 +352,7 @@ public class AttachmentController: ViewController {
self.wrapperNode = ASDisplayNode() self.wrapperNode = ASDisplayNode()
self.wrapperNode.clipsToBounds = true self.wrapperNode.clipsToBounds = true
self.container = AttachmentContainer() self.container = AttachmentContainer(isFullSize: controller.isFullSize)
self.container.canHaveKeyboardFocus = true self.container.canHaveKeyboardFocus = true
self.panel = AttachmentPanel(controller: controller, context: controller.context, chatLocation: controller.chatLocation, isScheduledMessages: controller.isScheduledMessages, updatedPresentationData: controller.updatedPresentationData, makeEntityInputView: makeEntityInputView) self.panel = AttachmentPanel(controller: controller, context: controller.context, chatLocation: controller.chatLocation, isScheduledMessages: controller.isScheduledMessages, updatedPresentationData: controller.updatedPresentationData, makeEntityInputView: makeEntityInputView)
self.panel.fromMenu = controller.fromMenu self.panel.fromMenu = controller.fromMenu
@ -574,7 +581,13 @@ public class AttachmentController: ViewController {
guard let controller = self.controller, let navigationController = controller.navigationController as? NavigationController else { guard let controller = self.controller, let navigationController = controller.navigationController as? NavigationController else {
return return
} }
navigationController.minimizeViewController(controller, damping: damping, velocity: initialVelocity, setupContainer: { [weak self] current in navigationController.minimizeViewController(controller, damping: damping, velocity: initialVelocity, beforeMaximize: { navigationController, completion in
if let controller = controller.mainController as? AttachmentContainable {
controller.beforeMaximize(navigationController: navigationController, completion: completion)
} else {
completion()
}
}, setupContainer: { [weak self] current in
let minimizedContainer: MinimizedContainerImpl? let minimizedContainer: MinimizedContainerImpl?
if let current = current as? MinimizedContainerImpl { if let current = current as? MinimizedContainerImpl {
minimizedContainer = current minimizedContainer = current
@ -1062,7 +1075,7 @@ public class AttachmentController: ViewController {
public var shouldMinimizeOnSwipe: ((AttachmentButtonType?) -> Bool)? public var shouldMinimizeOnSwipe: ((AttachmentButtonType?) -> Bool)?
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, chatLocation: ChatLocation?, isScheduledMessages: Bool = false, buttons: [AttachmentButtonType], initialButton: AttachmentButtonType = .gallery, fromMenu: Bool = false, hasTextInput: Bool = true, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView? = { return nil}) { public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, chatLocation: ChatLocation?, isScheduledMessages: Bool = false, buttons: [AttachmentButtonType], initialButton: AttachmentButtonType = .gallery, fromMenu: Bool = false, hasTextInput: Bool = true, isFullSize: Bool = false, makeEntityInputView: @escaping () -> AttachmentTextInputPanelInputView? = { return nil}) {
self.context = context self.context = context
self.updatedPresentationData = updatedPresentationData self.updatedPresentationData = updatedPresentationData
self.chatLocation = chatLocation self.chatLocation = chatLocation
@ -1071,6 +1084,7 @@ public class AttachmentController: ViewController {
self.initialButton = initialButton self.initialButton = initialButton
self.fromMenu = fromMenu self.fromMenu = fromMenu
self.hasTextInput = hasTextInput self.hasTextInput = hasTextInput
self.isFullSize = isFullSize
self.makeEntityInputView = makeEntityInputView self.makeEntityInputView = makeEntityInputView
super.init(navigationBarPresentationData: nil) super.init(navigationBarPresentationData: nil)

View File

@ -447,7 +447,9 @@ public class BrowserScreen: ViewController {
guard let controller = self.controller, let navigationController = controller.navigationController as? NavigationController else { guard let controller = self.controller, let navigationController = controller.navigationController as? NavigationController else {
return return
} }
navigationController.minimizeViewController(controller, damping: nil, setupContainer: { [weak self] current in navigationController.minimizeViewController(controller, damping: nil, beforeMaximize: { _, completion in
completion()
}, setupContainer: { [weak self] current in
let minimizedContainer: MinimizedContainerImpl? let minimizedContainer: MinimizedContainerImpl?
if let current = current as? MinimizedContainerImpl { if let current = current as? MinimizedContainerImpl {
minimizedContainer = current minimizedContainer = current

View File

@ -8,7 +8,7 @@ public protocol MinimizedContainer: ASDisplayNode {
var willMaximize: (() -> Void)? { get set } var willMaximize: (() -> Void)? { get set }
func addController(_ viewController: ViewController, transition: ContainedViewLayoutTransition) func addController(_ viewController: ViewController, beforeMaximize: @escaping (NavigationController, @escaping () -> Void) -> Void, transition: ContainedViewLayoutTransition)
func maximizeController(_ viewController: ViewController, animated: Bool, completion: @escaping (Bool) -> Void) func maximizeController(_ viewController: ViewController, animated: Bool, completion: @escaping (Bool) -> Void)
func collapse() func collapse()
func dismissAll(completion: @escaping () -> Void) func dismissAll(completion: @escaping () -> Void)

View File

@ -842,7 +842,11 @@ open class NavigationController: UINavigationController, ContainableController,
if case .flat = navigationLayout.root, let minimizedContainer = self.minimizedContainer { if case .flat = navigationLayout.root, let minimizedContainer = self.minimizedContainer {
if minimizedContainer.supernode !== self.displayNode { if minimizedContainer.supernode !== self.displayNode {
if let rootContainer = self.rootContainer, case let .flat(flatContainer) = rootContainer { if let rootContainer = self.rootContainer, case let .flat(flatContainer) = rootContainer {
self.displayNode.insertSubnode(minimizedContainer, aboveSubnode: flatContainer) if let rootModalFrame = self.rootModalFrame {
self.displayNode.insertSubnode(minimizedContainer, aboveSubnode: rootModalFrame)
} else {
self.displayNode.insertSubnode(minimizedContainer, aboveSubnode: flatContainer)
}
} else { } else {
self.displayNode.insertSubnode(minimizedContainer, at: 0) self.displayNode.insertSubnode(minimizedContainer, at: 0)
} }
@ -1572,7 +1576,7 @@ open class NavigationController: UINavigationController, ContainableController,
self._viewControllersPromise.set(self.viewControllers) self._viewControllersPromise.set(self.viewControllers)
} }
public func minimizeViewController(_ viewController: ViewController, damping: CGFloat?, velocity: CGFloat? = nil, setupContainer: (MinimizedContainer?) -> MinimizedContainer?, animated: Bool) { public func minimizeViewController(_ viewController: ViewController, damping: CGFloat?, velocity: CGFloat? = nil, beforeMaximize: @escaping (NavigationController, @escaping () -> Void) -> Void, setupContainer: (MinimizedContainer?) -> MinimizedContainer?, animated: Bool) {
let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.4, curve: .customSpring(damping: damping ?? 124.0, initialVelocity: velocity ?? 0.0)) : .immediate let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.4, curve: .customSpring(damping: damping ?? 124.0, initialVelocity: velocity ?? 0.0)) : .immediate
let minimizedContainer = setupContainer(self.minimizedContainer) let minimizedContainer = setupContainer(self.minimizedContainer)
@ -1592,7 +1596,7 @@ open class NavigationController: UINavigationController, ContainableController,
} }
viewController.isMinimized = true viewController.isMinimized = true
self.filterController(viewController, animated: true) self.filterController(viewController, animated: true)
minimizedContainer?.addController(viewController, transition: transition) minimizedContainer?.addController(viewController, beforeMaximize: beforeMaximize, transition: transition)
} }
private var isMaximizing = false private var isMaximizing = false

View File

@ -485,6 +485,9 @@ final class NavigationModalContainer: ASDisplayNode, ASScrollViewDelegate, ASGes
let alphaTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut) let alphaTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
let positionTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut) let positionTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
alphaTransition.updateAlpha(node: self.dim, alpha: 0.0, beginWithCurrentState: true) alphaTransition.updateAlpha(node: self.dim, alpha: 0.0, beginWithCurrentState: true)
if let lastController = self.container.controllers.last, lastController.isMinimized {
self.dim.layer.removeAllAnimations()
}
positionTransition.updatePosition(node: self.container, position: CGPoint(x: self.container.position.x, y: self.bounds.height + self.container.bounds.height / 2.0 + self.bounds.height), beginWithCurrentState: true, completion: { [weak self] _ in positionTransition.updatePosition(node: self.container, position: CGPoint(x: self.container.position.x, y: self.bounds.height + self.container.bounds.height / 2.0 + self.bounds.height), beginWithCurrentState: true, completion: { [weak self] _ in
guard let strongSelf = self else { guard let strongSelf = self else {
return return

View File

@ -54,7 +54,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[571523412] = { return $0.readDouble() } dict[571523412] = { return $0.readDouble() }
dict[-1255641564] = { return parseString($0) } dict[-1255641564] = { return parseString($0) }
dict[-1194283041] = { return Api.AccountDaysTTL.parse_accountDaysTTL($0) } dict[-1194283041] = { return Api.AccountDaysTTL.parse_accountDaysTTL($0) }
dict[1008422669] = { return Api.AppWebViewResult.parse_appWebViewResultUrl($0) }
dict[-653423106] = { return Api.AttachMenuBot.parse_attachMenuBot($0) } dict[-653423106] = { return Api.AttachMenuBot.parse_attachMenuBot($0) }
dict[-1297663893] = { return Api.AttachMenuBotIcon.parse_attachMenuBotIcon($0) } dict[-1297663893] = { return Api.AttachMenuBotIcon.parse_attachMenuBotIcon($0) }
dict[1165423600] = { return Api.AttachMenuBotIconColor.parse_attachMenuBotIconColor($0) } dict[1165423600] = { return Api.AttachMenuBotIconColor.parse_attachMenuBotIconColor($0) }
@ -446,6 +445,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[859091184] = { return Api.InputSecureFile.parse_inputSecureFileUploaded($0) } dict[859091184] = { return Api.InputSecureFile.parse_inputSecureFileUploaded($0) }
dict[-618540889] = { return Api.InputSecureValue.parse_inputSecureValue($0) } dict[-618540889] = { return Api.InputSecureValue.parse_inputSecureValue($0) }
dict[482797855] = { return Api.InputSingleMedia.parse_inputSingleMedia($0) } dict[482797855] = { return Api.InputSingleMedia.parse_inputSingleMedia($0) }
dict[543876817] = { return Api.InputStarsTransaction.parse_inputStarsTransaction($0) }
dict[42402760] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmoji($0) } dict[42402760] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmoji($0) }
dict[215889721] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmojiAnimations($0) } dict[215889721] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmojiAnimations($0) }
dict[-427863538] = { return Api.InputStickerSet.parse_inputStickerSetDice($0) } dict[-427863538] = { return Api.InputStickerSet.parse_inputStickerSetDice($0) }
@ -870,7 +870,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-378127636] = { return Api.SendMessageAction.parse_sendMessageUploadVideoAction($0) } dict[-378127636] = { return Api.SendMessageAction.parse_sendMessageUploadVideoAction($0) }
dict[-651419003] = { return Api.SendMessageAction.parse_speakingInGroupCallAction($0) } dict[-651419003] = { return Api.SendMessageAction.parse_speakingInGroupCallAction($0) }
dict[-1239335713] = { return Api.ShippingOption.parse_shippingOption($0) } dict[-1239335713] = { return Api.ShippingOption.parse_shippingOption($0) }
dict[-2010155333] = { return Api.SimpleWebViewResult.parse_simpleWebViewResultUrl($0) }
dict[-425595208] = { return Api.SmsJob.parse_smsJob($0) } dict[-425595208] = { return Api.SmsJob.parse_smsJob($0) }
dict[-1108478618] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) } dict[-1108478618] = { return Api.SponsoredMessage.parse_sponsoredMessage($0) }
dict[1124938064] = { return Api.SponsoredMessageReportOption.parse_sponsoredMessageReportOption($0) } dict[1124938064] = { return Api.SponsoredMessageReportOption.parse_sponsoredMessageReportOption($0) }
@ -1105,7 +1104,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[781501415] = { return Api.WebPageAttribute.parse_webPageAttributeStory($0) } dict[781501415] = { return Api.WebPageAttribute.parse_webPageAttributeStory($0) }
dict[1421174295] = { return Api.WebPageAttribute.parse_webPageAttributeTheme($0) } dict[1421174295] = { return Api.WebPageAttribute.parse_webPageAttributeTheme($0) }
dict[211046684] = { return Api.WebViewMessageSent.parse_webViewMessageSent($0) } dict[211046684] = { return Api.WebViewMessageSent.parse_webViewMessageSent($0) }
dict[202659196] = { return Api.WebViewResult.parse_webViewResultUrl($0) } dict[1294139288] = { return Api.WebViewResult.parse_webViewResultUrl($0) }
dict[-1389486888] = { return Api.account.AuthorizationForm.parse_authorizationForm($0) } dict[-1389486888] = { return Api.account.AuthorizationForm.parse_authorizationForm($0) }
dict[1275039392] = { return Api.account.Authorizations.parse_authorizations($0) } dict[1275039392] = { return Api.account.Authorizations.parse_authorizations($0) }
dict[1674235686] = { return Api.account.AutoDownloadSettings.parse_autoDownloadSettings($0) } dict[1674235686] = { return Api.account.AutoDownloadSettings.parse_autoDownloadSettings($0) }
@ -1155,7 +1154,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1035688326] = { return Api.auth.SentCodeType.parse_sentCodeTypeApp($0) } dict[1035688326] = { return Api.auth.SentCodeType.parse_sentCodeTypeApp($0) }
dict[1398007207] = { return Api.auth.SentCodeType.parse_sentCodeTypeCall($0) } dict[1398007207] = { return Api.auth.SentCodeType.parse_sentCodeTypeCall($0) }
dict[-196020837] = { return Api.auth.SentCodeType.parse_sentCodeTypeEmailCode($0) } dict[-196020837] = { return Api.auth.SentCodeType.parse_sentCodeTypeEmailCode($0) }
dict[331943703] = { return Api.auth.SentCodeType.parse_sentCodeTypeFirebaseSms($0) } dict[10475318] = { return Api.auth.SentCodeType.parse_sentCodeTypeFirebaseSms($0) }
dict[-1425815847] = { return Api.auth.SentCodeType.parse_sentCodeTypeFlashCall($0) } dict[-1425815847] = { return Api.auth.SentCodeType.parse_sentCodeTypeFlashCall($0) }
dict[-648651719] = { return Api.auth.SentCodeType.parse_sentCodeTypeFragmentSms($0) } dict[-648651719] = { return Api.auth.SentCodeType.parse_sentCodeTypeFragmentSms($0) }
dict[-2113903484] = { return Api.auth.SentCodeType.parse_sentCodeTypeMissedCall($0) } dict[-2113903484] = { return Api.auth.SentCodeType.parse_sentCodeTypeMissedCall($0) }
@ -1437,8 +1436,6 @@ public extension Api {
switch object { switch object {
case let _1 as Api.AccountDaysTTL: case let _1 as Api.AccountDaysTTL:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.AppWebViewResult:
_1.serialize(buffer, boxed)
case let _1 as Api.AttachMenuBot: case let _1 as Api.AttachMenuBot:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.AttachMenuBotIcon: case let _1 as Api.AttachMenuBotIcon:
@ -1749,6 +1746,8 @@ public extension Api {
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.InputSingleMedia: case let _1 as Api.InputSingleMedia:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.InputStarsTransaction:
_1.serialize(buffer, boxed)
case let _1 as Api.InputStickerSet: case let _1 as Api.InputStickerSet:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.InputStickerSetItem: case let _1 as Api.InputStickerSetItem:
@ -1979,8 +1978,6 @@ public extension Api {
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.ShippingOption: case let _1 as Api.ShippingOption:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.SimpleWebViewResult:
_1.serialize(buffer, boxed)
case let _1 as Api.SmsJob: case let _1 as Api.SmsJob:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.SponsoredMessage: case let _1 as Api.SponsoredMessage:

View File

@ -34,42 +34,6 @@ public extension Api {
} }
} }
public extension Api {
enum AppWebViewResult: TypeConstructorDescription {
case appWebViewResultUrl(url: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .appWebViewResultUrl(let url):
if boxed {
buffer.appendInt32(1008422669)
}
serializeString(url, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .appWebViewResultUrl(let url):
return ("appWebViewResultUrl", [("url", url as Any)])
}
}
public static func parse_appWebViewResultUrl(_ reader: BufferReader) -> AppWebViewResult? {
var _1: String?
_1 = parseString(reader)
let _c1 = _1 != nil
if _c1 {
return Api.AppWebViewResult.appWebViewResultUrl(url: _1!)
}
else {
return nil
}
}
}
}
public extension Api { public extension Api {
enum AttachMenuBot: TypeConstructorDescription { enum AttachMenuBot: TypeConstructorDescription {
case attachMenuBot(flags: Int32, botId: Int64, shortName: String, peerTypes: [Api.AttachMenuPeerType]?, icons: [Api.AttachMenuBotIcon]) case attachMenuBot(flags: Int32, botId: Int64, shortName: String, peerTypes: [Api.AttachMenuPeerType]?, icons: [Api.AttachMenuBotIcon])
@ -1196,3 +1160,43 @@ public extension Api {
} }
} }
public extension Api {
enum BotCommand: TypeConstructorDescription {
case botCommand(command: String, description: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .botCommand(let command, let description):
if boxed {
buffer.appendInt32(-1032140601)
}
serializeString(command, buffer: buffer, boxed: false)
serializeString(description, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .botCommand(let command, let description):
return ("botCommand", [("command", command as Any), ("description", description as Any)])
}
}
public static func parse_botCommand(_ reader: BufferReader) -> BotCommand? {
var _1: String?
_1 = parseString(reader)
var _2: String?
_2 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.BotCommand.botCommand(command: _1!, description: _2!)
}
else {
return nil
}
}
}
}

View File

@ -58,6 +58,46 @@ public extension Api {
} }
} }
public extension Api {
enum InputStarsTransaction: TypeConstructorDescription {
case inputStarsTransaction(flags: Int32, id: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .inputStarsTransaction(let flags, let id):
if boxed {
buffer.appendInt32(543876817)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(id, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .inputStarsTransaction(let flags, let id):
return ("inputStarsTransaction", [("flags", flags as Any), ("id", id as Any)])
}
}
public static func parse_inputStarsTransaction(_ reader: BufferReader) -> InputStarsTransaction? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.InputStarsTransaction.inputStarsTransaction(flags: _1!, id: _2!)
}
else {
return nil
}
}
}
}
public extension Api { public extension Api {
enum InputStickerSet: TypeConstructorDescription { enum InputStickerSet: TypeConstructorDescription {
case inputStickerSetAnimatedEmoji case inputStickerSetAnimatedEmoji
@ -918,119 +958,3 @@ public extension Api {
} }
} }
public extension Api {
enum InputWebFileLocation: TypeConstructorDescription {
case inputWebFileAudioAlbumThumbLocation(flags: Int32, document: Api.InputDocument?, title: String?, performer: String?)
case inputWebFileGeoPointLocation(geoPoint: Api.InputGeoPoint, accessHash: Int64, w: Int32, h: Int32, zoom: Int32, scale: Int32)
case inputWebFileLocation(url: String, accessHash: Int64)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .inputWebFileAudioAlbumThumbLocation(let flags, let document, let title, let performer):
if boxed {
buffer.appendInt32(-193992412)
}
serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {document!.serialize(buffer, true)}
if Int(flags) & Int(1 << 1) != 0 {serializeString(title!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeString(performer!, buffer: buffer, boxed: false)}
break
case .inputWebFileGeoPointLocation(let geoPoint, let accessHash, let w, let h, let zoom, let scale):
if boxed {
buffer.appendInt32(-1625153079)
}
geoPoint.serialize(buffer, true)
serializeInt64(accessHash, buffer: buffer, boxed: false)
serializeInt32(w, buffer: buffer, boxed: false)
serializeInt32(h, buffer: buffer, boxed: false)
serializeInt32(zoom, buffer: buffer, boxed: false)
serializeInt32(scale, buffer: buffer, boxed: false)
break
case .inputWebFileLocation(let url, let accessHash):
if boxed {
buffer.appendInt32(-1036396922)
}
serializeString(url, buffer: buffer, boxed: false)
serializeInt64(accessHash, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .inputWebFileAudioAlbumThumbLocation(let flags, let document, let title, let performer):
return ("inputWebFileAudioAlbumThumbLocation", [("flags", flags as Any), ("document", document as Any), ("title", title as Any), ("performer", performer as Any)])
case .inputWebFileGeoPointLocation(let geoPoint, let accessHash, let w, let h, let zoom, let scale):
return ("inputWebFileGeoPointLocation", [("geoPoint", geoPoint as Any), ("accessHash", accessHash as Any), ("w", w as Any), ("h", h as Any), ("zoom", zoom as Any), ("scale", scale as Any)])
case .inputWebFileLocation(let url, let accessHash):
return ("inputWebFileLocation", [("url", url as Any), ("accessHash", accessHash as Any)])
}
}
public static func parse_inputWebFileAudioAlbumThumbLocation(_ reader: BufferReader) -> InputWebFileLocation? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.InputDocument?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.InputDocument
} }
var _3: String?
if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) }
var _4: String?
if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(reader) }
let _c1 = _1 != nil
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.InputWebFileLocation.inputWebFileAudioAlbumThumbLocation(flags: _1!, document: _2, title: _3, performer: _4)
}
else {
return nil
}
}
public static func parse_inputWebFileGeoPointLocation(_ reader: BufferReader) -> InputWebFileLocation? {
var _1: Api.InputGeoPoint?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.InputGeoPoint
}
var _2: Int64?
_2 = reader.readInt64()
var _3: Int32?
_3 = reader.readInt32()
var _4: Int32?
_4 = reader.readInt32()
var _5: Int32?
_5 = reader.readInt32()
var _6: Int32?
_6 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = _5 != nil
let _c6 = _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.InputWebFileLocation.inputWebFileGeoPointLocation(geoPoint: _1!, accessHash: _2!, w: _3!, h: _4!, zoom: _5!, scale: _6!)
}
else {
return nil
}
}
public static func parse_inputWebFileLocation(_ reader: BufferReader) -> InputWebFileLocation? {
var _1: String?
_1 = parseString(reader)
var _2: Int64?
_2 = reader.readInt64()
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.InputWebFileLocation.inputWebFileLocation(url: _1!, accessHash: _2!)
}
else {
return nil
}
}
}
}

View File

@ -1,3 +1,119 @@
public extension Api {
enum InputWebFileLocation: TypeConstructorDescription {
case inputWebFileAudioAlbumThumbLocation(flags: Int32, document: Api.InputDocument?, title: String?, performer: String?)
case inputWebFileGeoPointLocation(geoPoint: Api.InputGeoPoint, accessHash: Int64, w: Int32, h: Int32, zoom: Int32, scale: Int32)
case inputWebFileLocation(url: String, accessHash: Int64)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .inputWebFileAudioAlbumThumbLocation(let flags, let document, let title, let performer):
if boxed {
buffer.appendInt32(-193992412)
}
serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {document!.serialize(buffer, true)}
if Int(flags) & Int(1 << 1) != 0 {serializeString(title!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeString(performer!, buffer: buffer, boxed: false)}
break
case .inputWebFileGeoPointLocation(let geoPoint, let accessHash, let w, let h, let zoom, let scale):
if boxed {
buffer.appendInt32(-1625153079)
}
geoPoint.serialize(buffer, true)
serializeInt64(accessHash, buffer: buffer, boxed: false)
serializeInt32(w, buffer: buffer, boxed: false)
serializeInt32(h, buffer: buffer, boxed: false)
serializeInt32(zoom, buffer: buffer, boxed: false)
serializeInt32(scale, buffer: buffer, boxed: false)
break
case .inputWebFileLocation(let url, let accessHash):
if boxed {
buffer.appendInt32(-1036396922)
}
serializeString(url, buffer: buffer, boxed: false)
serializeInt64(accessHash, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .inputWebFileAudioAlbumThumbLocation(let flags, let document, let title, let performer):
return ("inputWebFileAudioAlbumThumbLocation", [("flags", flags as Any), ("document", document as Any), ("title", title as Any), ("performer", performer as Any)])
case .inputWebFileGeoPointLocation(let geoPoint, let accessHash, let w, let h, let zoom, let scale):
return ("inputWebFileGeoPointLocation", [("geoPoint", geoPoint as Any), ("accessHash", accessHash as Any), ("w", w as Any), ("h", h as Any), ("zoom", zoom as Any), ("scale", scale as Any)])
case .inputWebFileLocation(let url, let accessHash):
return ("inputWebFileLocation", [("url", url as Any), ("accessHash", accessHash as Any)])
}
}
public static func parse_inputWebFileAudioAlbumThumbLocation(_ reader: BufferReader) -> InputWebFileLocation? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.InputDocument?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.InputDocument
} }
var _3: String?
if Int(_1!) & Int(1 << 1) != 0 {_3 = parseString(reader) }
var _4: String?
if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(reader) }
let _c1 = _1 != nil
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.InputWebFileLocation.inputWebFileAudioAlbumThumbLocation(flags: _1!, document: _2, title: _3, performer: _4)
}
else {
return nil
}
}
public static func parse_inputWebFileGeoPointLocation(_ reader: BufferReader) -> InputWebFileLocation? {
var _1: Api.InputGeoPoint?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.InputGeoPoint
}
var _2: Int64?
_2 = reader.readInt64()
var _3: Int32?
_3 = reader.readInt32()
var _4: Int32?
_4 = reader.readInt32()
var _5: Int32?
_5 = reader.readInt32()
var _6: Int32?
_6 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = _5 != nil
let _c6 = _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.InputWebFileLocation.inputWebFileGeoPointLocation(geoPoint: _1!, accessHash: _2!, w: _3!, h: _4!, zoom: _5!, scale: _6!)
}
else {
return nil
}
}
public static func parse_inputWebFileLocation(_ reader: BufferReader) -> InputWebFileLocation? {
var _1: String?
_1 = parseString(reader)
var _2: Int64?
_2 = reader.readInt64()
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.InputWebFileLocation.inputWebFileLocation(url: _1!, accessHash: _2!)
}
else {
return nil
}
}
}
}
public extension Api { public extension Api {
enum Invoice: TypeConstructorDescription { enum Invoice: TypeConstructorDescription {
case invoice(flags: Int32, currency: String, prices: [Api.LabeledPrice], maxTipAmount: Int64?, suggestedTipAmounts: [Int64]?, termsUrl: String?) case invoice(flags: Int32, currency: String, prices: [Api.LabeledPrice], maxTipAmount: Int64?, suggestedTipAmounts: [Int64]?, termsUrl: String?)
@ -934,111 +1050,3 @@ public extension Api {
} }
} }
public extension Api {
enum LangPackString: TypeConstructorDescription {
case langPackString(key: String, value: String)
case langPackStringDeleted(key: String)
case langPackStringPluralized(flags: Int32, key: String, zeroValue: String?, oneValue: String?, twoValue: String?, fewValue: String?, manyValue: String?, otherValue: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .langPackString(let key, let value):
if boxed {
buffer.appendInt32(-892239370)
}
serializeString(key, buffer: buffer, boxed: false)
serializeString(value, buffer: buffer, boxed: false)
break
case .langPackStringDeleted(let key):
if boxed {
buffer.appendInt32(695856818)
}
serializeString(key, buffer: buffer, boxed: false)
break
case .langPackStringPluralized(let flags, let key, let zeroValue, let oneValue, let twoValue, let fewValue, let manyValue, let otherValue):
if boxed {
buffer.appendInt32(1816636575)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(key, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeString(zeroValue!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeString(oneValue!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {serializeString(twoValue!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 3) != 0 {serializeString(fewValue!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 4) != 0 {serializeString(manyValue!, buffer: buffer, boxed: false)}
serializeString(otherValue, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .langPackString(let key, let value):
return ("langPackString", [("key", key as Any), ("value", value as Any)])
case .langPackStringDeleted(let key):
return ("langPackStringDeleted", [("key", key as Any)])
case .langPackStringPluralized(let flags, let key, let zeroValue, let oneValue, let twoValue, let fewValue, let manyValue, let otherValue):
return ("langPackStringPluralized", [("flags", flags as Any), ("key", key as Any), ("zeroValue", zeroValue as Any), ("oneValue", oneValue as Any), ("twoValue", twoValue as Any), ("fewValue", fewValue as Any), ("manyValue", manyValue as Any), ("otherValue", otherValue as Any)])
}
}
public static func parse_langPackString(_ reader: BufferReader) -> LangPackString? {
var _1: String?
_1 = parseString(reader)
var _2: String?
_2 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.LangPackString.langPackString(key: _1!, value: _2!)
}
else {
return nil
}
}
public static func parse_langPackStringDeleted(_ reader: BufferReader) -> LangPackString? {
var _1: String?
_1 = parseString(reader)
let _c1 = _1 != nil
if _c1 {
return Api.LangPackString.langPackStringDeleted(key: _1!)
}
else {
return nil
}
}
public static func parse_langPackStringPluralized(_ reader: BufferReader) -> LangPackString? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: String?
if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) }
var _4: String?
if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(reader) }
var _5: String?
if Int(_1!) & Int(1 << 2) != 0 {_5 = parseString(reader) }
var _6: String?
if Int(_1!) & Int(1 << 3) != 0 {_6 = parseString(reader) }
var _7: String?
if Int(_1!) & Int(1 << 4) != 0 {_7 = parseString(reader) }
var _8: String?
_8 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil
let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil
let _c8 = _8 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
return Api.LangPackString.langPackStringPluralized(flags: _1!, key: _2!, zeroValue: _3, oneValue: _4, twoValue: _5, fewValue: _6, manyValue: _7, otherValue: _8!)
}
else {
return nil
}
}
}
}

View File

@ -1,3 +1,111 @@
public extension Api {
enum LangPackString: TypeConstructorDescription {
case langPackString(key: String, value: String)
case langPackStringDeleted(key: String)
case langPackStringPluralized(flags: Int32, key: String, zeroValue: String?, oneValue: String?, twoValue: String?, fewValue: String?, manyValue: String?, otherValue: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .langPackString(let key, let value):
if boxed {
buffer.appendInt32(-892239370)
}
serializeString(key, buffer: buffer, boxed: false)
serializeString(value, buffer: buffer, boxed: false)
break
case .langPackStringDeleted(let key):
if boxed {
buffer.appendInt32(695856818)
}
serializeString(key, buffer: buffer, boxed: false)
break
case .langPackStringPluralized(let flags, let key, let zeroValue, let oneValue, let twoValue, let fewValue, let manyValue, let otherValue):
if boxed {
buffer.appendInt32(1816636575)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(key, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeString(zeroValue!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeString(oneValue!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {serializeString(twoValue!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 3) != 0 {serializeString(fewValue!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 4) != 0 {serializeString(manyValue!, buffer: buffer, boxed: false)}
serializeString(otherValue, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .langPackString(let key, let value):
return ("langPackString", [("key", key as Any), ("value", value as Any)])
case .langPackStringDeleted(let key):
return ("langPackStringDeleted", [("key", key as Any)])
case .langPackStringPluralized(let flags, let key, let zeroValue, let oneValue, let twoValue, let fewValue, let manyValue, let otherValue):
return ("langPackStringPluralized", [("flags", flags as Any), ("key", key as Any), ("zeroValue", zeroValue as Any), ("oneValue", oneValue as Any), ("twoValue", twoValue as Any), ("fewValue", fewValue as Any), ("manyValue", manyValue as Any), ("otherValue", otherValue as Any)])
}
}
public static func parse_langPackString(_ reader: BufferReader) -> LangPackString? {
var _1: String?
_1 = parseString(reader)
var _2: String?
_2 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.LangPackString.langPackString(key: _1!, value: _2!)
}
else {
return nil
}
}
public static func parse_langPackStringDeleted(_ reader: BufferReader) -> LangPackString? {
var _1: String?
_1 = parseString(reader)
let _c1 = _1 != nil
if _c1 {
return Api.LangPackString.langPackStringDeleted(key: _1!)
}
else {
return nil
}
}
public static func parse_langPackStringPluralized(_ reader: BufferReader) -> LangPackString? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: String?
if Int(_1!) & Int(1 << 0) != 0 {_3 = parseString(reader) }
var _4: String?
if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(reader) }
var _5: String?
if Int(_1!) & Int(1 << 2) != 0 {_5 = parseString(reader) }
var _6: String?
if Int(_1!) & Int(1 << 3) != 0 {_6 = parseString(reader) }
var _7: String?
if Int(_1!) & Int(1 << 4) != 0 {_7 = parseString(reader) }
var _8: String?
_8 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
let _c6 = (Int(_1!) & Int(1 << 3) == 0) || _6 != nil
let _c7 = (Int(_1!) & Int(1 << 4) == 0) || _7 != nil
let _c8 = _8 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
return Api.LangPackString.langPackStringPluralized(flags: _1!, key: _2!, zeroValue: _3, oneValue: _4, twoValue: _5, fewValue: _6, manyValue: _7, otherValue: _8!)
}
else {
return nil
}
}
}
}
public extension Api { public extension Api {
enum MaskCoords: TypeConstructorDescription { enum MaskCoords: TypeConstructorDescription {
case maskCoords(n: Int32, x: Double, y: Double, zoom: Double) case maskCoords(n: Int32, x: Double, y: Double, zoom: Double)

View File

@ -1,43 +1,3 @@
public extension Api {
enum BotCommand: TypeConstructorDescription {
case botCommand(command: String, description: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .botCommand(let command, let description):
if boxed {
buffer.appendInt32(-1032140601)
}
serializeString(command, buffer: buffer, boxed: false)
serializeString(description, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .botCommand(let command, let description):
return ("botCommand", [("command", command as Any), ("description", description as Any)])
}
}
public static func parse_botCommand(_ reader: BufferReader) -> BotCommand? {
var _1: String?
_1 = parseString(reader)
var _2: String?
_2 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.BotCommand.botCommand(command: _1!, description: _2!)
}
else {
return nil
}
}
}
}
public extension Api { public extension Api {
indirect enum BotCommandScope: TypeConstructorDescription { indirect enum BotCommandScope: TypeConstructorDescription {
case botCommandScopeChatAdmins case botCommandScopeChatAdmins
@ -1200,3 +1160,53 @@ public extension Api {
} }
} }
public extension Api {
enum BusinessIntro: TypeConstructorDescription {
case businessIntro(flags: Int32, title: String, description: String, sticker: Api.Document?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .businessIntro(let flags, let title, let description, let sticker):
if boxed {
buffer.appendInt32(1510606445)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false)
serializeString(description, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {sticker!.serialize(buffer, true)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .businessIntro(let flags, let title, let description, let sticker):
return ("businessIntro", [("flags", flags as Any), ("title", title as Any), ("description", description as Any), ("sticker", sticker as Any)])
}
}
public static func parse_businessIntro(_ reader: BufferReader) -> BusinessIntro? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: String?
_3 = parseString(reader)
var _4: Api.Document?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.Document
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.BusinessIntro.businessIntro(flags: _1!, title: _2!, description: _3!, sticker: _4)
}
else {
return nil
}
}
}
}

View File

@ -396,42 +396,6 @@ public extension Api {
} }
} }
public extension Api {
enum SimpleWebViewResult: TypeConstructorDescription {
case simpleWebViewResultUrl(url: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .simpleWebViewResultUrl(let url):
if boxed {
buffer.appendInt32(-2010155333)
}
serializeString(url, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .simpleWebViewResultUrl(let url):
return ("simpleWebViewResultUrl", [("url", url as Any)])
}
}
public static func parse_simpleWebViewResultUrl(_ reader: BufferReader) -> SimpleWebViewResult? {
var _1: String?
_1 = parseString(reader)
let _c1 = _1 != nil
if _c1 {
return Api.SimpleWebViewResult.simpleWebViewResultUrl(url: _1!)
}
else {
return nil
}
}
}
}
public extension Api { public extension Api {
enum SmsJob: TypeConstructorDescription { enum SmsJob: TypeConstructorDescription {
case smsJob(jobId: String, phoneNumber: String, text: String) case smsJob(jobId: String, phoneNumber: String, text: String)

View File

@ -160,15 +160,16 @@ public extension Api {
} }
public extension Api { public extension Api {
enum WebViewResult: TypeConstructorDescription { enum WebViewResult: TypeConstructorDescription {
case webViewResultUrl(queryId: Int64, url: String) case webViewResultUrl(flags: Int32, queryId: Int64?, url: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self { switch self {
case .webViewResultUrl(let queryId, let url): case .webViewResultUrl(let flags, let queryId, let url):
if boxed { if boxed {
buffer.appendInt32(202659196) buffer.appendInt32(1294139288)
} }
serializeInt64(queryId, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(queryId!, buffer: buffer, boxed: false)}
serializeString(url, buffer: buffer, boxed: false) serializeString(url, buffer: buffer, boxed: false)
break break
} }
@ -176,20 +177,23 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) { public func descriptionFields() -> (String, [(String, Any)]) {
switch self { switch self {
case .webViewResultUrl(let queryId, let url): case .webViewResultUrl(let flags, let queryId, let url):
return ("webViewResultUrl", [("queryId", queryId as Any), ("url", url as Any)]) return ("webViewResultUrl", [("flags", flags as Any), ("queryId", queryId as Any), ("url", url as Any)])
} }
} }
public static func parse_webViewResultUrl(_ reader: BufferReader) -> WebViewResult? { public static func parse_webViewResultUrl(_ reader: BufferReader) -> WebViewResult? {
var _1: Int64? var _1: Int32?
_1 = reader.readInt64() _1 = reader.readInt32()
var _2: String? var _2: Int64?
_2 = parseString(reader) if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt64() }
var _3: String?
_3 = parseString(reader)
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
if _c1 && _c2 { let _c3 = _3 != nil
return Api.WebViewResult.webViewResultUrl(queryId: _1!, url: _2!) if _c1 && _c2 && _c3 {
return Api.WebViewResult.webViewResultUrl(flags: _1!, queryId: _2, url: _3!)
} }
else { else {
return nil return nil

View File

@ -589,7 +589,7 @@ public extension Api.auth {
case sentCodeTypeApp(length: Int32) case sentCodeTypeApp(length: Int32)
case sentCodeTypeCall(length: Int32) case sentCodeTypeCall(length: Int32)
case sentCodeTypeEmailCode(flags: Int32, emailPattern: String, length: Int32, resetAvailablePeriod: Int32?, resetPendingDate: Int32?) case sentCodeTypeEmailCode(flags: Int32, emailPattern: String, length: Int32, resetAvailablePeriod: Int32?, resetPendingDate: Int32?)
case sentCodeTypeFirebaseSms(flags: Int32, nonce: Buffer?, playIntegrityNonce: Buffer?, receipt: String?, pushTimeout: Int32?, length: Int32) case sentCodeTypeFirebaseSms(flags: Int32, nonce: Buffer?, playIntegrityProjectId: Int64?, playIntegrityNonce: Buffer?, receipt: String?, pushTimeout: Int32?, length: Int32)
case sentCodeTypeFlashCall(pattern: String) case sentCodeTypeFlashCall(pattern: String)
case sentCodeTypeFragmentSms(url: String, length: Int32) case sentCodeTypeFragmentSms(url: String, length: Int32)
case sentCodeTypeMissedCall(prefix: String, length: Int32) case sentCodeTypeMissedCall(prefix: String, length: Int32)
@ -622,12 +622,13 @@ public extension Api.auth {
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(resetAvailablePeriod!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 3) != 0 {serializeInt32(resetAvailablePeriod!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 4) != 0 {serializeInt32(resetPendingDate!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 4) != 0 {serializeInt32(resetPendingDate!, buffer: buffer, boxed: false)}
break break
case .sentCodeTypeFirebaseSms(let flags, let nonce, let playIntegrityNonce, let receipt, let pushTimeout, let length): case .sentCodeTypeFirebaseSms(let flags, let nonce, let playIntegrityProjectId, let playIntegrityNonce, let receipt, let pushTimeout, let length):
if boxed { if boxed {
buffer.appendInt32(331943703) buffer.appendInt32(10475318)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeBytes(nonce!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 0) != 0 {serializeBytes(nonce!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {serializeInt64(playIntegrityProjectId!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {serializeBytes(playIntegrityNonce!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 2) != 0 {serializeBytes(playIntegrityNonce!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeString(receipt!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 1) != 0 {serializeString(receipt!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(pushTimeout!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 1) != 0 {serializeInt32(pushTimeout!, buffer: buffer, boxed: false)}
@ -690,8 +691,8 @@ public extension Api.auth {
return ("sentCodeTypeCall", [("length", length as Any)]) return ("sentCodeTypeCall", [("length", length as Any)])
case .sentCodeTypeEmailCode(let flags, let emailPattern, let length, let resetAvailablePeriod, let resetPendingDate): case .sentCodeTypeEmailCode(let flags, let emailPattern, let length, let resetAvailablePeriod, let resetPendingDate):
return ("sentCodeTypeEmailCode", [("flags", flags as Any), ("emailPattern", emailPattern as Any), ("length", length as Any), ("resetAvailablePeriod", resetAvailablePeriod as Any), ("resetPendingDate", resetPendingDate as Any)]) return ("sentCodeTypeEmailCode", [("flags", flags as Any), ("emailPattern", emailPattern as Any), ("length", length as Any), ("resetAvailablePeriod", resetAvailablePeriod as Any), ("resetPendingDate", resetPendingDate as Any)])
case .sentCodeTypeFirebaseSms(let flags, let nonce, let playIntegrityNonce, let receipt, let pushTimeout, let length): case .sentCodeTypeFirebaseSms(let flags, let nonce, let playIntegrityProjectId, let playIntegrityNonce, let receipt, let pushTimeout, let length):
return ("sentCodeTypeFirebaseSms", [("flags", flags as Any), ("nonce", nonce as Any), ("playIntegrityNonce", playIntegrityNonce as Any), ("receipt", receipt as Any), ("pushTimeout", pushTimeout as Any), ("length", length as Any)]) return ("sentCodeTypeFirebaseSms", [("flags", flags as Any), ("nonce", nonce as Any), ("playIntegrityProjectId", playIntegrityProjectId as Any), ("playIntegrityNonce", playIntegrityNonce as Any), ("receipt", receipt as Any), ("pushTimeout", pushTimeout as Any), ("length", length as Any)])
case .sentCodeTypeFlashCall(let pattern): case .sentCodeTypeFlashCall(let pattern):
return ("sentCodeTypeFlashCall", [("pattern", pattern as Any)]) return ("sentCodeTypeFlashCall", [("pattern", pattern as Any)])
case .sentCodeTypeFragmentSms(let url, let length): case .sentCodeTypeFragmentSms(let url, let length):
@ -759,22 +760,25 @@ public extension Api.auth {
_1 = reader.readInt32() _1 = reader.readInt32()
var _2: Buffer? var _2: Buffer?
if Int(_1!) & Int(1 << 0) != 0 {_2 = parseBytes(reader) } if Int(_1!) & Int(1 << 0) != 0 {_2 = parseBytes(reader) }
var _3: Buffer? var _3: Int64?
if Int(_1!) & Int(1 << 2) != 0 {_3 = parseBytes(reader) } if Int(_1!) & Int(1 << 2) != 0 {_3 = reader.readInt64() }
var _4: String? var _4: Buffer?
if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(reader) } if Int(_1!) & Int(1 << 2) != 0 {_4 = parseBytes(reader) }
var _5: Int32? var _5: String?
if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() } if Int(_1!) & Int(1 << 1) != 0 {_5 = parseString(reader) }
var _6: Int32? var _6: Int32?
_6 = reader.readInt32() if Int(_1!) & Int(1 << 1) != 0 {_6 = reader.readInt32() }
var _7: Int32?
_7 = reader.readInt32()
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil
let _c6 = _6 != nil let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { let _c7 = _7 != nil
return Api.auth.SentCodeType.sentCodeTypeFirebaseSms(flags: _1!, nonce: _2, playIntegrityNonce: _3, receipt: _4, pushTimeout: _5, length: _6!) if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
return Api.auth.SentCodeType.sentCodeTypeFirebaseSms(flags: _1!, nonce: _2, playIntegrityProjectId: _3, playIntegrityNonce: _4, receipt: _5, pushTimeout: _6, length: _7!)
} }
else { else {
return nil return nil

View File

@ -1,53 +1,3 @@
public extension Api {
enum BusinessIntro: TypeConstructorDescription {
case businessIntro(flags: Int32, title: String, description: String, sticker: Api.Document?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .businessIntro(let flags, let title, let description, let sticker):
if boxed {
buffer.appendInt32(1510606445)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false)
serializeString(description, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {sticker!.serialize(buffer, true)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .businessIntro(let flags, let title, let description, let sticker):
return ("businessIntro", [("flags", flags as Any), ("title", title as Any), ("description", description as Any), ("sticker", sticker as Any)])
}
}
public static func parse_businessIntro(_ reader: BufferReader) -> BusinessIntro? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: String?
_3 = parseString(reader)
var _4: Api.Document?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.Document
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.BusinessIntro.businessIntro(flags: _1!, title: _2!, description: _3!, sticker: _4)
}
else {
return nil
}
}
}
}
public extension Api { public extension Api {
enum BusinessLocation: TypeConstructorDescription { enum BusinessLocation: TypeConstructorDescription {
case businessLocation(flags: Int32, geoPoint: Api.GeoPoint?, address: String) case businessLocation(flags: Int32, geoPoint: Api.GeoPoint?, address: String)

View File

@ -7308,20 +7308,20 @@ public extension Api.functions.messages {
} }
} }
public extension Api.functions.messages { public extension Api.functions.messages {
static func requestAppWebView(flags: Int32, peer: Api.InputPeer, app: Api.InputBotApp, startParam: String?, themeParams: Api.DataJSON?, platform: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.AppWebViewResult>) { static func requestAppWebView(flags: Int32, peer: Api.InputPeer, app: Api.InputBotApp, startParam: String?, themeParams: Api.DataJSON?, platform: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.WebViewResult>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(-1940243652) buffer.appendInt32(1398901710)
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true) peer.serialize(buffer, true)
app.serialize(buffer, true) app.serialize(buffer, true)
if Int(flags) & Int(1 << 1) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 1) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {themeParams!.serialize(buffer, true)} if Int(flags) & Int(1 << 2) != 0 {themeParams!.serialize(buffer, true)}
serializeString(platform, buffer: buffer, boxed: false) serializeString(platform, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.requestAppWebView", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("app", String(describing: app)), ("startParam", String(describing: startParam)), ("themeParams", String(describing: themeParams)), ("platform", String(describing: platform))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.AppWebViewResult? in return (FunctionDescription(name: "messages.requestAppWebView", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("app", String(describing: app)), ("startParam", String(describing: startParam)), ("themeParams", String(describing: themeParams)), ("platform", String(describing: platform))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.WebViewResult? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: Api.AppWebViewResult? var result: Api.WebViewResult?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.AppWebViewResult result = Api.parse(reader, signature: signature) as? Api.WebViewResult
} }
return result return result
}) })
@ -7345,20 +7345,20 @@ public extension Api.functions.messages {
} }
} }
public extension Api.functions.messages { public extension Api.functions.messages {
static func requestSimpleWebView(flags: Int32, bot: Api.InputUser, url: String?, startParam: String?, themeParams: Api.DataJSON?, platform: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.SimpleWebViewResult>) { static func requestSimpleWebView(flags: Int32, bot: Api.InputUser, url: String?, startParam: String?, themeParams: Api.DataJSON?, platform: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.WebViewResult>) {
let buffer = Buffer() let buffer = Buffer()
buffer.appendInt32(440815626) buffer.appendInt32(1094336115)
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
bot.serialize(buffer, true) bot.serialize(buffer, true)
if Int(flags) & Int(1 << 3) != 0 {serializeString(url!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 3) != 0 {serializeString(url!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 4) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 4) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 0) != 0 {themeParams!.serialize(buffer, true)} if Int(flags) & Int(1 << 0) != 0 {themeParams!.serialize(buffer, true)}
serializeString(platform, buffer: buffer, boxed: false) serializeString(platform, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.requestSimpleWebView", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("url", String(describing: url)), ("startParam", String(describing: startParam)), ("themeParams", String(describing: themeParams)), ("platform", String(describing: platform))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.SimpleWebViewResult? in return (FunctionDescription(name: "messages.requestSimpleWebView", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("url", String(describing: url)), ("startParam", String(describing: startParam)), ("themeParams", String(describing: themeParams)), ("platform", String(describing: platform))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.WebViewResult? in
let reader = BufferReader(buffer) let reader = BufferReader(buffer)
var result: Api.SimpleWebViewResult? var result: Api.WebViewResult?
if let signature = reader.readInt32() { if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.SimpleWebViewResult result = Api.parse(reader, signature: signature) as? Api.WebViewResult
} }
return result return result
}) })
@ -8827,6 +8827,26 @@ public extension Api.functions.payments {
}) })
} }
} }
public extension Api.functions.payments {
static func getStarsTransactionsByID(peer: Api.InputPeer, id: [Api.InputStarsTransaction]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.StarsStatus>) {
let buffer = Buffer()
buffer.appendInt32(662973742)
peer.serialize(buffer, true)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(id.count))
for item in id {
item.serialize(buffer, true)
}
return (FunctionDescription(name: "payments.getStarsTransactionsByID", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.StarsStatus? in
let reader = BufferReader(buffer)
var result: Api.payments.StarsStatus?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.payments.StarsStatus
}
return result
})
}
}
public extension Api.functions.payments { public extension Api.functions.payments {
static func launchPrepaidGiveaway(peer: Api.InputPeer, giveawayId: Int64, purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) { static func launchPrepaidGiveaway(peer: Api.InputPeer, giveawayId: Int64, purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer() let buffer = Buffer()

View File

@ -274,7 +274,7 @@ public func sendAuthorizationCode(accountManager: AccountManager<TelegramAccount
parsedNextType = AuthorizationCodeNextType(apiType: nextType) parsedNextType = AuthorizationCodeNextType(apiType: nextType)
} }
if case let .sentCodeTypeFirebaseSms(_, _, _, receipt, pushTimeout, _) = type { if case let .sentCodeTypeFirebaseSms(_, _, _, _, receipt, pushTimeout, _) = type {
return firebaseSecretStream return firebaseSecretStream
|> map { mapping -> String? in |> map { mapping -> String? in
guard let receipt = receipt else { guard let receipt = receipt else {
@ -432,7 +432,7 @@ private func internalResendAuthorizationCode(accountManager: AccountManager<Tele
parsedNextType = AuthorizationCodeNextType(apiType: nextType) parsedNextType = AuthorizationCodeNextType(apiType: nextType)
} }
if case let .sentCodeTypeFirebaseSms(_, _, _, receipt, pushTimeout, _) = type { if case let .sentCodeTypeFirebaseSms(_, _, _, _, receipt, pushTimeout, _) = type {
return firebaseSecretStream return firebaseSecretStream
|> map { mapping -> String? in |> map { mapping -> String? in
guard let receipt = receipt else { guard let receipt = receipt else {
@ -574,7 +574,7 @@ public func resendAuthorizationCode(accountManager: AccountManager<TelegramAccou
parsedNextType = AuthorizationCodeNextType(apiType: nextType) parsedNextType = AuthorizationCodeNextType(apiType: nextType)
} }
if case let .sentCodeTypeFirebaseSms(_, _, _, receipt, pushTimeout, _) = newType { if case let .sentCodeTypeFirebaseSms(_, _, _, _, receipt, pushTimeout, _) = newType {
return firebaseSecretStream return firebaseSecretStream
|> map { mapping -> String? in |> map { mapping -> String? in
guard let receipt = receipt else { guard let receipt = receipt else {

View File

@ -36,7 +36,7 @@ extension SentAuthorizationCodeType {
self = .emailSetupRequired(appleSignInAllowed: (flags & (1 << 0)) != 0) self = .emailSetupRequired(appleSignInAllowed: (flags & (1 << 0)) != 0)
case let .sentCodeTypeFragmentSms(url, length): case let .sentCodeTypeFragmentSms(url, length):
self = .fragment(url: url, length: length) self = .fragment(url: url, length: length)
case let .sentCodeTypeFirebaseSms(_, _, _, _, pushTimeout, length): case let .sentCodeTypeFirebaseSms(_, _, _, _, _, pushTimeout, length):
self = .firebase(pushTimeout: pushTimeout, length: length) self = .firebase(pushTimeout: pushTimeout, length: length)
case let .sentCodeTypeSmsWord(_, beginning): case let .sentCodeTypeSmsWord(_, beginning):
self = .word(startsWith: beginning) self = .word(startsWith: beginning)

View File

@ -72,7 +72,7 @@ func _internal_requestChangeAccountPhoneNumberVerification(account: Account, api
parsedNextType = AuthorizationCodeNextType(apiType: nextType) parsedNextType = AuthorizationCodeNextType(apiType: nextType)
} }
if case let .sentCodeTypeFirebaseSms(_, _, _, receipt, pushTimeout, _) = type { if case let .sentCodeTypeFirebaseSms(_, _, _, _, receipt, pushTimeout, _) = type {
return firebaseSecretStream return firebaseSecretStream
|> map { mapping -> String? in |> map { mapping -> String? in
guard let receipt = receipt else { guard let receipt = receipt else {
@ -147,7 +147,7 @@ private func internalResendChangeAccountPhoneNumberVerification(account: Account
parsedNextType = AuthorizationCodeNextType(apiType: nextType) parsedNextType = AuthorizationCodeNextType(apiType: nextType)
} }
if case let .sentCodeTypeFirebaseSms(_, _, _, receipt, pushTimeout, _) = type { if case let .sentCodeTypeFirebaseSms(_, _, _, _, receipt, pushTimeout, _) = type {
return firebaseSecretStream return firebaseSecretStream
|> map { mapping -> String? in |> map { mapping -> String? in
guard let receipt = receipt else { guard let receipt = receipt else {

View File

@ -16,16 +16,12 @@ public enum RequestSimpleWebViewSource {
case settings case settings
} }
public enum RequestSimpleWebViewError { func _internal_requestSimpleWebView(postbox: Postbox, network: Network, botId: PeerId, url: String?, source: RequestSimpleWebViewSource, themeParams: [String: Any]?) -> Signal<RequestWebViewResult, RequestWebViewError> {
case generic
}
func _internal_requestSimpleWebView(postbox: Postbox, network: Network, botId: PeerId, url: String?, source: RequestSimpleWebViewSource, themeParams: [String: Any]?) -> Signal<String, RequestSimpleWebViewError> {
var serializedThemeParams: Api.DataJSON? var serializedThemeParams: Api.DataJSON?
if let themeParams = themeParams, let data = try? JSONSerialization.data(withJSONObject: themeParams, options: []), let dataString = String(data: data, encoding: .utf8) { if let themeParams = themeParams, let data = try? JSONSerialization.data(withJSONObject: themeParams, options: []), let dataString = String(data: data, encoding: .utf8) {
serializedThemeParams = .dataJSON(data: dataString) serializedThemeParams = .dataJSON(data: dataString)
} }
return postbox.transaction { transaction -> Signal<String, RequestSimpleWebViewError> in return postbox.transaction { transaction -> Signal<RequestWebViewResult, RequestWebViewError> in
guard let bot = transaction.getPeer(botId), let inputUser = apiInputUser(bot) else { guard let bot = transaction.getPeer(botId), let inputUser = apiInputUser(bot) else {
return .fail(.generic) return .fail(.generic)
} }
@ -46,17 +42,21 @@ func _internal_requestSimpleWebView(postbox: Postbox, network: Network, botId: P
flags |= (1 << 3) flags |= (1 << 3)
} }
return network.request(Api.functions.messages.requestSimpleWebView(flags: flags, bot: inputUser, url: url, startParam: nil, themeParams: serializedThemeParams, platform: botWebViewPlatform)) return network.request(Api.functions.messages.requestSimpleWebView(flags: flags, bot: inputUser, url: url, startParam: nil, themeParams: serializedThemeParams, platform: botWebViewPlatform))
|> mapError { _ -> RequestSimpleWebViewError in |> mapError { _ -> RequestWebViewError in
return .generic return .generic
} }
|> mapToSignal { result -> Signal<String, RequestSimpleWebViewError> in |> mapToSignal { result -> Signal<RequestWebViewResult, RequestWebViewError> in
switch result { switch result {
case let .simpleWebViewResultUrl(url): case let .webViewResultUrl(flags, queryId, url):
return .single(url) var resultFlags: RequestWebViewResult.Flags = []
if (flags & (1 << 1)) != 0 {
resultFlags.insert(.fullSize)
}
return .single(RequestWebViewResult(flags: resultFlags, queryId: queryId, url: url, keepAliveSignal: nil))
} }
} }
} }
|> castError(RequestSimpleWebViewError.self) |> castError(RequestWebViewError.self)
|> switchToLatest |> switchToLatest
} }
@ -65,9 +65,24 @@ public enum KeepWebViewError {
} }
public struct RequestWebViewResult { public struct RequestWebViewResult {
public let queryId: Int64 public struct Flags: OptionSet {
public var rawValue: Int32
public init(rawValue: Int32) {
self.rawValue = rawValue
}
public init() {
self.rawValue = 0
}
public static let fullSize = Flags(rawValue: 1 << 0)
}
public let flags: Flags
public let queryId: Int64?
public let url: String public let url: String
public let keepAliveSignal: Signal<Never, KeepWebViewError> public let keepAliveSignal: Signal<Never, KeepWebViewError>?
} }
public enum RequestWebViewError { public enum RequestWebViewError {
@ -166,8 +181,19 @@ func _internal_requestWebView(postbox: Postbox, network: Network, stateManager:
} }
|> mapToSignal { result -> Signal<RequestWebViewResult, RequestWebViewError> in |> mapToSignal { result -> Signal<RequestWebViewResult, RequestWebViewError> in
switch result { switch result {
case let .webViewResultUrl(queryId, url): case let .webViewResultUrl(webViewFlags, queryId, url):
return .single(RequestWebViewResult(queryId: queryId, url: url, keepAliveSignal: keepWebViewSignal(network: network, stateManager: stateManager, flags: flags, peer: inputPeer, bot: inputBot, queryId: queryId, replyToMessageId: replyToMessageId, threadId: threadId, sendAs: nil))) var resultFlags: RequestWebViewResult.Flags = []
if (webViewFlags & (1 << 1)) != 0 {
resultFlags.insert(.fullSize)
}
let keepAlive: Signal<Never, KeepWebViewError>?
if let queryId {
keepAlive = keepWebViewSignal(network: network, stateManager: stateManager, flags: flags, peer: inputPeer, bot: inputBot, queryId: queryId, replyToMessageId: replyToMessageId, threadId: threadId, sendAs: nil)
} else {
keepAlive = nil
}
return .single(RequestWebViewResult(flags: resultFlags, queryId: queryId, url: url, keepAliveSignal: keepAlive))
} }
} }
} }
@ -199,17 +225,13 @@ func _internal_sendWebViewData(postbox: Postbox, network: Network, stateManager:
|> switchToLatest |> switchToLatest
} }
public enum RequestAppWebViewError { func _internal_requestAppWebView(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, appReference: BotAppReference, payload: String?, themeParams: [String: Any]?, compact: Bool, allowWrite: Bool) -> Signal<RequestWebViewResult, RequestWebViewError> {
case generic
}
func _internal_requestAppWebView(postbox: Postbox, network: Network, stateManager: AccountStateManager, peerId: PeerId, appReference: BotAppReference, payload: String?, themeParams: [String: Any]?, allowWrite: Bool) -> Signal<String, RequestAppWebViewError> {
var serializedThemeParams: Api.DataJSON? var serializedThemeParams: Api.DataJSON?
if let themeParams = themeParams, let data = try? JSONSerialization.data(withJSONObject: themeParams, options: []), let dataString = String(data: data, encoding: .utf8) { if let themeParams = themeParams, let data = try? JSONSerialization.data(withJSONObject: themeParams, options: []), let dataString = String(data: data, encoding: .utf8) {
serializedThemeParams = .dataJSON(data: dataString) serializedThemeParams = .dataJSON(data: dataString)
} }
return postbox.transaction { transaction -> Signal<String, RequestAppWebViewError> in return postbox.transaction { transaction -> Signal<RequestWebViewResult, RequestWebViewError> in
guard let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) else { guard let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) else {
return .fail(.generic) return .fail(.generic)
} }
@ -235,19 +257,26 @@ func _internal_requestAppWebView(postbox: Postbox, network: Network, stateManage
if allowWrite { if allowWrite {
flags |= (1 << 0) flags |= (1 << 0)
} }
if compact {
flags |= (1 << 7)
}
return network.request(Api.functions.messages.requestAppWebView(flags: flags, peer: inputPeer, app: app, startParam: payload, themeParams: serializedThemeParams, platform: botWebViewPlatform)) return network.request(Api.functions.messages.requestAppWebView(flags: flags, peer: inputPeer, app: app, startParam: payload, themeParams: serializedThemeParams, platform: botWebViewPlatform))
|> mapError { _ -> RequestAppWebViewError in |> mapError { _ -> RequestWebViewError in
return .generic return .generic
} }
|> mapToSignal { result -> Signal<String, RequestAppWebViewError> in |> mapToSignal { result -> Signal<RequestWebViewResult, RequestWebViewError> in
switch result { switch result {
case let .appWebViewResultUrl(url): case let .webViewResultUrl(flags, queryId, url):
return .single(url) var resultFlags: RequestWebViewResult.Flags = []
if (flags & (1 << 1)) != 0 {
resultFlags.insert(.fullSize)
}
return .single(RequestWebViewResult(flags: resultFlags, queryId: queryId, url: url, keepAliveSignal: nil))
} }
} }
} }
|> castError(RequestAppWebViewError.self) |> castError(RequestWebViewError.self)
|> switchToLatest |> switchToLatest
} }

View File

@ -566,12 +566,12 @@ public extension TelegramEngine {
return _internal_requestWebView(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: peerId, botId: botId, url: url, payload: payload, themeParams: themeParams, fromMenu: fromMenu, replyToMessageId: replyToMessageId, threadId: threadId) return _internal_requestWebView(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: peerId, botId: botId, url: url, payload: payload, themeParams: themeParams, fromMenu: fromMenu, replyToMessageId: replyToMessageId, threadId: threadId)
} }
public func requestSimpleWebView(botId: PeerId, url: String?, source: RequestSimpleWebViewSource, themeParams: [String: Any]?) -> Signal<String, RequestSimpleWebViewError> { public func requestSimpleWebView(botId: PeerId, url: String?, source: RequestSimpleWebViewSource, themeParams: [String: Any]?) -> Signal<RequestWebViewResult, RequestWebViewError> {
return _internal_requestSimpleWebView(postbox: self.account.postbox, network: self.account.network, botId: botId, url: url, source: source, themeParams: themeParams) return _internal_requestSimpleWebView(postbox: self.account.postbox, network: self.account.network, botId: botId, url: url, source: source, themeParams: themeParams)
} }
public func requestAppWebView(peerId: PeerId, appReference: BotAppReference, payload: String?, themeParams: [String: Any]?, allowWrite: Bool) -> Signal<String, RequestAppWebViewError> { public func requestAppWebView(peerId: PeerId, appReference: BotAppReference, payload: String?, themeParams: [String: Any]?, compact: Bool, allowWrite: Bool) -> Signal<RequestWebViewResult, RequestWebViewError> {
return _internal_requestAppWebView(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: peerId, appReference: appReference, payload: payload, themeParams: themeParams, allowWrite: allowWrite) return _internal_requestAppWebView(postbox: self.account.postbox, network: self.account.network, stateManager: self.account.stateManager, peerId: peerId, appReference: appReference, payload: payload, themeParams: themeParams, compact: compact, allowWrite: allowWrite)
} }
public func sendWebViewData(botId: PeerId, buttonText: String, data: String) -> Signal<Never, SendWebViewDataError> { public func sendWebViewData(botId: PeerId, buttonText: String, data: String) -> Signal<Never, SendWebViewDataError> {

View File

@ -17,6 +17,7 @@ swift_library(
"//submodules/TelegramPresentationData", "//submodules/TelegramPresentationData",
"//submodules/AccountContext", "//submodules/AccountContext",
"//submodules/WallpaperBackgroundNode", "//submodules/WallpaperBackgroundNode",
"//submodules/UrlHandling",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -7,6 +7,7 @@ import Display
import TelegramPresentationData import TelegramPresentationData
import AccountContext import AccountContext
import WallpaperBackgroundNode import WallpaperBackgroundNode
import UrlHandling
private let titleFont = Font.medium(16.0) private let titleFont = Font.medium(16.0)
@ -178,7 +179,9 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
case .text: case .text:
iconImage = incoming ? graphics.chatBubbleActionButtonIncomingMessageIconImage : graphics.chatBubbleActionButtonOutgoingMessageIconImage iconImage = incoming ? graphics.chatBubbleActionButtonIncomingMessageIconImage : graphics.chatBubbleActionButtonOutgoingMessageIconImage
case let .url(value): case let .url(value):
if value.lowercased().contains("?startgroup=") { if isTelegramMeLink(value), let internalUrl = parseFullInternalUrl(sharedContext: context.sharedContext, url: value), case .peer(_, .appStart) = internalUrl {
iconImage = incoming ? graphics.chatBubbleActionButtonIncomingWebAppIconImage : graphics.chatBubbleActionButtonOutgoingWebAppIconImage
} else if value.lowercased().contains("?startgroup=") {
iconImage = incoming ? graphics.chatBubbleActionButtonIncomingAddToChatIconImage : graphics.chatBubbleActionButtonOutgoingAddToChatIconImage iconImage = incoming ? graphics.chatBubbleActionButtonIncomingAddToChatIconImage : graphics.chatBubbleActionButtonOutgoingAddToChatIconImage
} else { } else {
iconImage = incoming ? graphics.chatBubbleActionButtonIncomingLinkIconImage : graphics.chatBubbleActionButtonOutgoingLinkIconImage iconImage = incoming ? graphics.chatBubbleActionButtonIncomingLinkIconImage : graphics.chatBubbleActionButtonOutgoingLinkIconImage

View File

@ -27,10 +27,12 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
final class Item { final class Item {
let id: AnyHashable let id: AnyHashable
let controller: ViewController let controller: ViewController
let beforeMaximize: (NavigationController, @escaping () -> Void) -> Void
init(id: AnyHashable, controller: ViewController) { init(id: AnyHashable, controller: ViewController, beforeMaximize: @escaping (NavigationController, @escaping () -> Void) -> Void) {
self.id = id self.id = id
self.controller = controller self.controller = controller
self.beforeMaximize = beforeMaximize
} }
} }
@ -302,6 +304,8 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
private var dismissingItemId: AnyHashable? private var dismissingItemId: AnyHashable?
private var dismissingItemOffset: CGFloat? private var dismissingItemOffset: CGFloat?
private var expandedTapGestureRecoginzer: UITapGestureRecognizer?
private var currentTransition: Transition? private var currentTransition: Transition?
private var isApplyingTransition = false private var isApplyingTransition = false
private var validLayout: ContainerViewLayout? private var validLayout: ContainerViewLayout?
@ -370,6 +374,11 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
panGestureRecognizer.delegate = self.wrappedGestureRecognizerDelegate panGestureRecognizer.delegate = self.wrappedGestureRecognizerDelegate
panGestureRecognizer.delaysTouchesBegan = true panGestureRecognizer.delaysTouchesBegan = true
self.scrollView.addGestureRecognizer(panGestureRecognizer) self.scrollView.addGestureRecognizer(panGestureRecognizer)
let expandedTapGestureRecoginzer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
expandedTapGestureRecoginzer.isEnabled = false
self.expandedTapGestureRecoginzer = expandedTapGestureRecoginzer
self.scrollView.addGestureRecognizer(expandedTapGestureRecoginzer)
} }
func item(at y: CGFloat) -> Int? { func item(at y: CGFloat) -> Int? {
@ -401,6 +410,15 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
return false return false
} }
@objc func tapGesture(_ gestureRecognizer: UITapGestureRecognizer) {
guard self.isExpanded else {
return
}
if let result = self.scrollView.hitTest(gestureRecognizer.location(in: self.scrollView), with: nil), result === self.scrollView {
self.collapse()
}
}
@objc func panGesture(_ gestureRecognizer: UIPanGestureRecognizer) { @objc func panGesture(_ gestureRecognizer: UIPanGestureRecognizer) {
if self.isExpanded { if self.isExpanded {
self.dismissPanGesture(gestureRecognizer) self.dismissPanGesture(gestureRecognizer)
@ -483,10 +501,11 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
return result return result
} }
public func addController(_ viewController: ViewController, transition: ContainedViewLayoutTransition) { public func addController(_ viewController: ViewController, beforeMaximize: @escaping (NavigationController, @escaping () -> Void) -> Void, transition: ContainedViewLayoutTransition) {
let item = Item( let item = Item(
id: AnyHashable(Int64.random(in: Int64.min ... Int64.max)), id: AnyHashable(Int64.random(in: Int64.min ... Int64.max)),
controller: viewController controller: viewController,
beforeMaximize: beforeMaximize
) )
self.items.append(item) self.items.append(item)
@ -552,7 +571,11 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
return return
} }
if self.items.count == 1, let item = self.items.first { if self.items.count == 1, let item = self.items.first {
self.navigationController?.maximizeViewController(item.controller, animated: true) if let navigationController = self.navigationController {
item.beforeMaximize(navigationController, { [weak self] in
self?.navigationController?.maximizeViewController(item.controller, animated: true)
})
}
} else { } else {
self.isExpanded = true self.isExpanded = true
self.requestUpdate(transition: .animated(duration: 0.4, curve: .spring)) self.requestUpdate(transition: .animated(duration: 0.4, curve: .spring))
@ -697,7 +720,11 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
return return
} }
if self.isExpanded { if self.isExpanded {
self.navigationController?.maximizeViewController(item.controller, animated: true) if let navigationController = self.navigationController {
itemNode.item.beforeMaximize(navigationController, { [weak self] in
self?.navigationController?.maximizeViewController(item.controller, animated: true)
})
}
} else { } else {
self.expand() self.expand()
} }
@ -816,6 +843,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
} }
self.scrollView.passthrough = !self.isExpanded self.scrollView.passthrough = !self.isExpanded
self.scrollView.isScrollEnabled = self.isExpanded self.scrollView.isScrollEnabled = self.isExpanded
self.expandedTapGestureRecoginzer?.isEnabled = self.isExpanded
if let currentTransition = self.currentTransition { if let currentTransition = self.currentTransition {
self.isApplyingTransition = true self.isApplyingTransition = true
@ -904,28 +932,33 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
return return
} }
if self.items.count == 1 { if self.items.count == 1 {
if let itemNode = self.itemNodes.first(where: { $0.0 != itemId })?.value { if let itemNode = self.itemNodes.first(where: { $0.0 != itemId })?.value, let navigationController = self.navigationController {
let dimView = UIView() itemNode.item.beforeMaximize(navigationController, { [weak self] in
dimView.frame = CGRect(origin: .zero, size: layout.size) guard let self else {
dimView.backgroundColor = UIColor(white: 0.0, alpha: 0.25) return
self.view.insertSubview(dimView, aboveSubview: self.blurView)
dimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
itemNode.animateOut()
transition.updateTransform(node: itemNode, transform: CATransform3DIdentity)
transition.updatePosition(node: itemNode, position: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0 + topInset + self.scrollView.contentOffset.y), completion: { _ in
self.isApplyingTransition = false
if self.currentTransition == currentTransition {
self.currentTransition = nil
} }
completion(currentTransition) let dimView = UIView()
self.itemNodes[itemId] = nil dimView.frame = CGRect(origin: .zero, size: layout.size)
itemNode.removeFromSupernode() dimView.backgroundColor = UIColor(white: 0.0, alpha: 0.25)
dimView.removeFromSuperview() self.view.insertSubview(dimView, aboveSubview: self.blurView)
dimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
self.navigationController?.maximizeViewController(itemNode.item.controller, animated: false) itemNode.animateOut()
transition.updateTransform(node: itemNode, transform: CATransform3DIdentity)
self.requestUpdate(transition: .immediate) transition.updatePosition(node: itemNode, position: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0 + topInset + self.scrollView.contentOffset.y), completion: { _ in
self.isApplyingTransition = false
if self.currentTransition == currentTransition {
self.currentTransition = nil
}
completion(currentTransition)
self.itemNodes[itemId] = nil
itemNode.removeFromSupernode()
dimView.removeFromSuperview()
self.navigationController?.maximizeViewController(itemNode.item.controller, animated: false)
self.requestUpdate(transition: .immediate)
})
}) })
} }
transition.updatePosition(node: dismissedItemNode, position: CGPoint(x: -layout.size.width, y: dismissedItemNode.position.y)) transition.updatePosition(node: dismissedItemNode, position: CGPoint(x: -layout.size.width, y: dismissedItemNode.position.y))

View File

@ -5312,7 +5312,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
return return
} }
let params = WebAppParameters(source: .settings, peerId: self.context.account.peerId, botId: bot.peer.id, botName: bot.peer.compactDisplayTitle, url: nil, queryId: nil, payload: nil, buttonText: nil, keepAliveSignal: nil, forceHasSettings: bot.flags.contains(.hasSettings)) let params = WebAppParameters(source: .settings, peerId: self.context.account.peerId, botId: bot.peer.id, botName: bot.peer.compactDisplayTitle, url: nil, queryId: nil, payload: nil, buttonText: nil, keepAliveSignal: nil, forceHasSettings: bot.flags.contains(.hasSettings), fullSize: true)
let controller = standaloneWebAppController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, params: params, threadId: nil, openUrl: { [weak self] url, concealed, commit in let controller = standaloneWebAppController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, params: params, threadId: nil, openUrl: { [weak self] url, concealed, commit in
self?.openUrl(url: url, concealed: concealed, external: false, forceExternal: true, commit: commit) self?.openUrl(url: url, concealed: concealed, external: false, forceExternal: true, commit: commit)
}, requestSwitchInline: { _, _, _ in }, requestSwitchInline: { _, _, _ in

View File

@ -1808,7 +1808,7 @@ final class StoryItemSetContainerSendMessage {
//TODO:gift controller //TODO:gift controller
break break
case let .app(bot): case let .app(bot):
let params = WebAppParameters(source: .attachMenu, peerId: peer.id, botId: bot.peer.id, botName: bot.shortName, url: nil, queryId: nil, payload: nil, buttonText: nil, keepAliveSignal: nil, forceHasSettings: false) let params = WebAppParameters(source: .attachMenu, peerId: peer.id, botId: bot.peer.id, botName: bot.shortName, url: nil, queryId: nil, payload: nil, buttonText: nil, keepAliveSignal: nil, forceHasSettings: false, fullSize: true)
let theme = component.theme let theme = component.theme
let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>) = (component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), component.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) }) let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>) = (component.context.sharedContext.currentPresentationData.with({ $0 }).withUpdated(theme: theme), component.context.sharedContext.presentationData |> map { $0.withUpdated(theme: theme) })
let controller = WebAppController(context: component.context, updatedPresentationData: updatedPresentationData, params: params, replyToMessageId: nil, threadId: nil) let controller = WebAppController(context: component.context, updatedPresentationData: updatedPresentationData, params: params, replyToMessageId: nil, threadId: nil)

View File

@ -10,8 +10,9 @@ import AttachmentUI
import AccountContext import AccountContext
import TelegramNotices import TelegramNotices
import PresentationDataUtils import PresentationDataUtils
import UndoUI
extension ChatControllerImpl { public extension ChatControllerImpl {
func openWebApp(buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource) { func openWebApp(buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource) {
guard let peerId = self.chatLocation.peerId, let peer = self.presentationInterfaceState.renderedPeer?.peer else { guard let peerId = self.chatLocation.peerId, let peer = self.presentationInterfaceState.renderedPeer?.peer else {
return return
@ -76,14 +77,7 @@ extension ChatControllerImpl {
if source == .menu { if source == .menu {
self.updateChatPresentationInterfaceState(interactive: false) { state in self.updateChatPresentationInterfaceState(interactive: false) { state in
return state.updatedForceInputCommandsHidden(true) return state.updatedForceInputCommandsHidden(true)
// return state.updatedShowWebView(true).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 { if let navigationController = self.navigationController as? NavigationController, let minimizedContainer = navigationController.minimizedContainer {
@ -96,7 +90,7 @@ extension ChatControllerImpl {
} }
let context = self.context 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 params = WebAppParameters(source: .menu, peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false, fullSize: false)
let controller = standaloneWebAppController(context: self.context, updatedPresentationData: self.updatedPresentationData, params: params, threadId: self.chatLocation.threadId, openUrl: { [weak self] url, concealed, commit in 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) self?.openUrl(url, concealed: concealed, forceExternal: true, commit: commit)
}, requestSwitchInline: { [weak self] query, chatTypes, completion in }, requestSwitchInline: { [weak self] query, chatTypes, completion in
@ -146,7 +140,6 @@ extension ChatControllerImpl {
}) })
controller.navigationPresentation = .flatModal controller.navigationPresentation = .flatModal
self.push(controller) self.push(controller)
self.currentMenuWebAppController = controller
} else if simple { } else if simple {
var isInline = false var isInline = false
var botId = peerId var botId = peerId
@ -163,12 +156,12 @@ extension ChatControllerImpl {
|> afterDisposed { |> afterDisposed {
updateProgress() updateProgress()
}) })
|> deliverOnMainQueue).startStrict(next: { [weak self] url in |> deliverOnMainQueue).startStrict(next: { [weak self] result in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
let context = strongSelf.context 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 params = WebAppParameters(source: isInline ? .inline : .simple, peerId: peerId, botId: botId, botName: botName, url: result.url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false, fullSize: result.flags.contains(.fullSize))
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url, concealed, commit in 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) self?.openUrl(url, concealed: concealed, forceExternal: true, commit: commit)
}, requestSwitchInline: { [weak self] query, chatTypes, completion in }, requestSwitchInline: { [weak self] query, chatTypes, completion in
@ -209,7 +202,7 @@ extension ChatControllerImpl {
return return
} }
let context = strongSelf.context 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 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, fullSize: result.flags.contains(.fullSize))
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, threadId: strongSelf.chatLocation.threadId, openUrl: { [weak self] url, concealed, commit in 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) self?.openUrl(url, concealed: concealed, forceExternal: true, commit: commit)
}, completion: { [weak self] in }, completion: { [weak self] in
@ -250,4 +243,164 @@ extension ChatControllerImpl {
} }
}) })
} }
func presentBotApp(botApp: BotApp, botPeer: EnginePeer, payload: String?, compact: Bool, concealed: Bool = false, commit: @escaping () -> Void = {}) {
guard let peerId = self.chatLocation.peerId else {
return
}
self.attachmentController?.dismiss(animated: true, completion: nil)
let openBotApp: (Bool, Bool) -> Void = { [weak self] allowWrite, justInstalled in
guard let strongSelf = self else {
return
}
commit()
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 botAddress = botPeer.addressName ?? ""
strongSelf.messageActionCallbackDisposable.set(((strongSelf.context.engine.messages.requestAppWebView(peerId: peerId, appReference: .id(id: botApp.id, accessHash: botApp.accessHash), payload: payload, themeParams: generateWebAppThemeParams(strongSelf.presentationData.theme), compact: compact, allowWrite: allowWrite)
|> 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: botPeer.id, botName: botApp.title, url: result.url, queryId: 0, payload: payload, buttonText: "", keepAliveSignal: nil, forceHasSettings: botApp.flags.contains(.hasSettings), fullSize: result.flags.contains(.fullSize))
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)
}
}
}, 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)
if justInstalled {
let content: UndoOverlayContent = .succeed(text: strongSelf.presentationData.strings.WebApp_ShortcutsSettingsAdded(botPeer.compactDisplayTitle).string, timeout: 5.0, customUndoText: nil)
controller.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: content, elevatedLayout: false, position: .top, action: { _ in return false }), in: .current)
}
}, 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))
}
}))
}
let _ = combineLatest(
queue: Queue.mainQueue(),
ApplicationSpecificNotice.getBotGameNotice(accountManager: self.context.sharedContext.accountManager, peerId: botPeer.id),
self.context.engine.messages.attachMenuBots(),
self.context.engine.messages.getAttachMenuBot(botId: botPeer.id, cached: true)
|> map(Optional.init)
|> `catch` { _ -> Signal<AttachMenuBot?, NoError> in
return .single(nil)
}
).startStandalone(next: { [weak self] noticed, attachMenuBots, attachMenuBot in
guard let self else {
return
}
var isAttachMenuBotInstalled: Bool?
if let _ = attachMenuBot {
if let _ = attachMenuBots.first(where: { $0.peer.id == botPeer.id && !$0.flags.contains(.notActivated) }) {
isAttachMenuBotInstalled = true
} else {
isAttachMenuBotInstalled = false
}
}
let context = self.context
if !noticed || botApp.flags.contains(.notActivated) || isAttachMenuBotInstalled == false {
if let isAttachMenuBotInstalled, let attachMenuBot {
if !isAttachMenuBotInstalled {
let controller = webAppTermsAlertController(context: context, updatedPresentationData: self.updatedPresentationData, bot: attachMenuBot, completion: { allowWrite in
let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id).startStandalone()
let _ = (context.engine.messages.addBotToAttachMenu(botId: botPeer.id, allowWrite: allowWrite)
|> deliverOnMainQueue).startStandalone(error: { _ in
}, completed: {
openBotApp(allowWrite, true)
})
})
self.present(controller, in: .window(.root))
} else {
openBotApp(false, false)
}
} else {
let controller = webAppLaunchConfirmationController(context: context, updatedPresentationData: self.updatedPresentationData, peer: botPeer, requestWriteAccess: botApp.flags.contains(.notActivated) && botApp.flags.contains(.requiresWriteAccess), completion: { allowWrite in
let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id).startStandalone()
openBotApp(allowWrite, false)
}, showMore: { [weak self] in
if let self {
self.openResolved(result: .peer(botPeer._asPeer(), .info(nil)), sourceMessageId: nil)
}
})
self.present(controller, in: .window(.root))
}
} else {
openBotApp(false, false)
}
})
}
} }

View File

@ -525,7 +525,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let chatLocationContextHolder: Atomic<ChatLocationContextHolder?> let chatLocationContextHolder: Atomic<ChatLocationContextHolder?>
weak var attachmentController: AttachmentController? weak var attachmentController: AttachmentController?
weak var currentMenuWebAppController: ViewController?
weak var currentWebAppController: ViewController? weak var currentWebAppController: ViewController?
weak var currentImportMessageTooltip: UndoOverlayController? weak var currentImportMessageTooltip: UndoOverlayController?
@ -7917,166 +7916,6 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
self.presentAttachmentMenu(subject: .bot(id: botId, payload: payload, justInstalled: justInstalled)) self.presentAttachmentMenu(subject: .bot(id: botId, payload: payload, justInstalled: justInstalled))
} }
public func presentBotApp(botApp: BotApp, botPeer: EnginePeer, payload: String?, concealed: Bool = false, commit: @escaping () -> Void = {}) {
guard let peerId = self.chatLocation.peerId else {
return
}
self.attachmentController?.dismiss(animated: true, completion: nil)
let openBotApp: (Bool, Bool) -> Void = { [weak self] allowWrite, justInstalled in
guard let strongSelf = self else {
return
}
commit()
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 botAddress = botPeer.addressName ?? ""
strongSelf.messageActionCallbackDisposable.set(((strongSelf.context.engine.messages.requestAppWebView(peerId: peerId, appReference: .id(id: botApp.id, accessHash: botApp.accessHash), payload: payload, themeParams: generateWebAppThemeParams(strongSelf.presentationData.theme), allowWrite: allowWrite)
|> afterDisposed {
updateProgress()
})
|> deliverOnMainQueue).startStrict(next: { [weak self] url in
guard let strongSelf = self else {
return
}
let context = strongSelf.context
let params = WebAppParameters(source: .generic, peerId: peerId, botId: botPeer.id, botName: botApp.title, url: url, queryId: 0, payload: payload, buttonText: "", keepAliveSignal: nil, forceHasSettings: botApp.flags.contains(.hasSettings))
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)
}
}
}, 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)
if justInstalled {
let content: UndoOverlayContent = .succeed(text: strongSelf.presentationData.strings.WebApp_ShortcutsSettingsAdded(botPeer.compactDisplayTitle).string, timeout: 5.0, customUndoText: nil)
controller.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: content, elevatedLayout: false, position: .top, action: { _ in return false }), in: .current)
}
}, 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))
}
}))
}
let _ = combineLatest(
queue: Queue.mainQueue(),
ApplicationSpecificNotice.getBotGameNotice(accountManager: self.context.sharedContext.accountManager, peerId: botPeer.id),
self.context.engine.messages.attachMenuBots(),
self.context.engine.messages.getAttachMenuBot(botId: botPeer.id, cached: true)
|> map(Optional.init)
|> `catch` { _ -> Signal<AttachMenuBot?, NoError> in
return .single(nil)
}
).startStandalone(next: { [weak self] value, attachMenuBots, attachMenuBot in
guard let self else {
return
}
var isAttachMenuBotInstalled: Bool?
if let _ = attachMenuBot {
if let _ = attachMenuBots.first(where: { $0.peer.id == botPeer.id && !$0.flags.contains(.notActivated) }) {
isAttachMenuBotInstalled = true
} else {
isAttachMenuBotInstalled = false
}
}
let context = self.context
if !value || concealed || botApp.flags.contains(.notActivated) || isAttachMenuBotInstalled == false {
if let isAttachMenuBotInstalled, let attachMenuBot {
if !isAttachMenuBotInstalled {
let controller = webAppTermsAlertController(context: context, updatedPresentationData: self.updatedPresentationData, bot: attachMenuBot, completion: { allowWrite in
let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id).startStandalone()
let _ = (context.engine.messages.addBotToAttachMenu(botId: botPeer.id, allowWrite: allowWrite)
|> deliverOnMainQueue).startStandalone(error: { _ in
}, completed: {
openBotApp(allowWrite, true)
})
})
self.present(controller, in: .window(.root))
} else {
openBotApp(false, false)
}
} else {
let controller = webAppLaunchConfirmationController(context: context, updatedPresentationData: self.updatedPresentationData, peer: botPeer, requestWriteAccess: botApp.flags.contains(.notActivated) && botApp.flags.contains(.requiresWriteAccess), completion: { allowWrite in
let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: context.sharedContext.accountManager, peerId: botPeer.id).startStandalone()
openBotApp(allowWrite, false)
}, showMore: { [weak self] in
if let self {
self.openResolved(result: .peer(botPeer._asPeer(), .info(nil)), sourceMessageId: nil)
}
})
self.present(controller, in: .window(.root))
}
} else {
openBotApp(false, false)
}
})
}
func displayPollSolution(solution: TelegramMediaPollResults.Solution, sourceNode: ASDisplayNode, isAutomatic: Bool) { func displayPollSolution(solution: TelegramMediaPollResults.Solution, sourceNode: ASDisplayNode, isAutomatic: Bool) {
var maybeFoundItemNode: ChatMessageItemView? var maybeFoundItemNode: ChatMessageItemView?
self.chatDisplayNode.historyNode.forEachItemNode { itemNode in self.chatDisplayNode.historyNode.forEachItemNode { itemNode in
@ -9305,7 +9144,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId.id)) let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId.id))
|> deliverOnMainQueue).startStandalone(next: { [weak self] peer in |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in
if let strongSelf = self, let peer { if let strongSelf = self, let peer {
strongSelf.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload, concealed: concealed, commit: { strongSelf.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload, compact: botAppStart.compact, concealed: concealed, commit: {
dismissWebAppControllers() dismissWebAppControllers()
commit() commit()
}) })

View File

@ -615,7 +615,7 @@ extension ChatControllerImpl {
payload = botPayload payload = botPayload
fromAttachMenu = false fromAttachMenu = false
} }
let params = WebAppParameters(source: fromAttachMenu ? .attachMenu : .generic, peerId: peer.id, botId: bot.peer.id, botName: bot.shortName, url: nil, queryId: nil, payload: payload, buttonText: nil, keepAliveSignal: nil, forceHasSettings: false) let params = WebAppParameters(source: fromAttachMenu ? .attachMenu : .generic, peerId: peer.id, botId: bot.peer.id, botName: bot.shortName, url: nil, queryId: nil, payload: payload, buttonText: nil, keepAliveSignal: nil, forceHasSettings: false, fullSize: false)
let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject let replyMessageSubject = strongSelf.presentationInterfaceState.interfaceState.replyMessageSubject
let controller = WebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, replyToMessageId: replyMessageSubject?.messageId, threadId: strongSelf.chatLocation.threadId) let controller = WebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, params: params, replyToMessageId: replyMessageSubject?.messageId, threadId: strongSelf.chatLocation.threadId)
controller.openUrl = { [weak self] url, concealed, commit in controller.openUrl = { [weak self] url, concealed, commit in

View File

@ -165,7 +165,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
controller.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload, justInstalled: attachBotStart.justInstalled) controller.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload, justInstalled: attachBotStart.justInstalled)
} }
if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation { if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation {
controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload) controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload, compact: botAppStart.compact)
} }
params.setupController(controller) params.setupController(controller)
found = true found = true
@ -188,7 +188,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
} }
if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation { if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation {
Queue.mainQueue().after(0.1) { Queue.mainQueue().after(0.1) {
controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload) controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload, compact: botAppStart.compact)
} }
} }
} else { } else {
@ -196,7 +196,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation { if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation {
Queue.mainQueue().after(0.1) { Queue.mainQueue().after(0.1) {
controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload) controller.presentBotApp(botApp: botAppStart.botApp, botPeer: peer, payload: botAppStart.payload, compact: botAppStart.compact)
} }
} }
} }

View File

@ -71,7 +71,7 @@ public enum ParsedInternalPeerUrlParameter {
case channelMessage(Int32, Double?) case channelMessage(Int32, Double?)
case replyThread(Int32, Int32) case replyThread(Int32, Int32)
case voiceChat(String?) case voiceChat(String?)
case appStart(String, String?) case appStart(String, String?, Bool)
case story(Int32) case story(Int32)
case boost case boost
case text(String) case text(String)
@ -588,16 +588,19 @@ public func parseInternalUrl(sharedContext: SharedAccountContext, query: String)
} else if pathComponents.count == 2 { } else if pathComponents.count == 2 {
let appName = pathComponents[1] let appName = pathComponents[1]
var startApp: String? var startApp: String?
var compact = false
if let queryItems = components.queryItems { if let queryItems = components.queryItems {
for queryItem in queryItems { for queryItem in queryItems {
if let value = queryItem.value { if let value = queryItem.value {
if queryItem.name == "startapp" { if queryItem.name == "startapp" {
startApp = value startApp = value
} else if queryItem.name == "mode", value == "compact" {
compact = true
} }
} }
} }
} }
return .peer(.name(peerName), .appStart(appName, startApp)) return .peer(.name(peerName), .appStart(appName, startApp, compact))
} else { } else {
return nil return nil
} }
@ -713,7 +716,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
} }
} }
} }
case let .appStart(name, payload): case let .appStart(name, payload, compact):
return .single(.progress) |> then(context.engine.messages.getBotApp(botId: peer.id, shortName: name, cached: false) return .single(.progress) |> then(context.engine.messages.getBotApp(botId: peer.id, shortName: name, cached: false)
|> map(Optional.init) |> map(Optional.init)
|> `catch` { _ -> Signal<BotApp?, NoError> in |> `catch` { _ -> Signal<BotApp?, NoError> in
@ -721,7 +724,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
} }
|> mapToSignal { botApp -> Signal<ResolveInternalUrlResult, NoError> in |> mapToSignal { botApp -> Signal<ResolveInternalUrlResult, NoError> in
if let botApp { if let botApp {
return .single(.result(.peer(peer._asPeer(), .withBotApp(ChatControllerInitialBotAppStart(botApp: botApp, payload: payload, justInstalled: false))))) return .single(.result(.peer(peer._asPeer(), .withBotApp(ChatControllerInitialBotAppStart(botApp: botApp, payload: payload, justInstalled: false, compact: compact)))))
} else { } else {
return .single(.result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil)))) return .single(.result(.peer(peer._asPeer(), .chat(textInputState: nil, subject: nil, peekData: nil))))
} }
@ -1138,6 +1141,21 @@ public func parseAdUrl(sharedContext: SharedAccountContext, url: String) -> Pars
return nil return nil
} }
public func parseFullInternalUrl(sharedContext: SharedAccountContext, url: String) -> ParsedInternalUrl? {
let schemes = ["http://", "https://", ""]
for basePath in baseTelegramMePaths {
for scheme in schemes {
let basePrefix = scheme + basePath + "/"
if url.lowercased().hasPrefix(basePrefix) {
if let internalUrl = parseInternalUrl(sharedContext: sharedContext, query: String(url[basePrefix.endIndex...])) {
return internalUrl
}
}
}
}
return nil
}
private struct UrlHandlingConfiguration { private struct UrlHandlingConfiguration {
static var defaultValue: UrlHandlingConfiguration { static var defaultValue: UrlHandlingConfiguration {
return UrlHandlingConfiguration(domains: [], urlAuthDomains: []) return UrlHandlingConfiguration(domains: [], urlAuthDomains: [])

View File

@ -208,6 +208,7 @@ public struct WebAppParameters {
let buttonText: String? let buttonText: String?
let keepAliveSignal: Signal<Never, KeepWebViewError>? let keepAliveSignal: Signal<Never, KeepWebViewError>?
let forceHasSettings: Bool let forceHasSettings: Bool
let fullSize: Bool
public init( public init(
source: Source, source: Source,
@ -219,7 +220,8 @@ public struct WebAppParameters {
payload: String?, payload: String?,
buttonText: String?, buttonText: String?,
keepAliveSignal: Signal<Never, KeepWebViewError>?, keepAliveSignal: Signal<Never, KeepWebViewError>?,
forceHasSettings: Bool forceHasSettings: Bool,
fullSize: Bool
) { ) {
self.source = source self.source = source
self.peerId = peerId self.peerId = peerId
@ -231,6 +233,7 @@ public struct WebAppParameters {
self.buttonText = buttonText self.buttonText = buttonText
self.keepAliveSignal = keepAliveSignal self.keepAliveSignal = keepAliveSignal
self.forceHasSettings = forceHasSettings self.forceHasSettings = forceHasSettings
self.fullSize = fullSize
} }
} }
@ -288,6 +291,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
private let context: AccountContext private let context: AccountContext
var presentationData: PresentationData var presentationData: PresentationData
private var queryId: Int64? private var queryId: Int64?
fileprivate let canMinimize = true
private var placeholderDisposable: Disposable? private var placeholderDisposable: Disposable?
private var iconDisposable: Disposable? private var iconDisposable: Disposable?
@ -306,7 +310,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
self.context = context self.context = context
self.controller = controller self.controller = controller
self.presentationData = controller.presentationData self.presentationData = controller.presentationData
self.backgroundNode = ASDisplayNode() self.backgroundNode = ASDisplayNode()
self.headerBackgroundNode = ASDisplayNode() self.headerBackgroundNode = ASDisplayNode()
self.topOverscrollNode = ASDisplayNode() self.topOverscrollNode = ASDisplayNode()
@ -477,7 +481,8 @@ public final class WebAppController: ViewController, AttachmentContainable {
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
if let parsedUrl = URL(string: result) { if let parsedUrl = URL(string: result.url) {
strongSelf.queryId = result.queryId
strongSelf.webView?.load(URLRequest(url: parsedUrl)) strongSelf.webView?.load(URLRequest(url: parsedUrl))
} }
}) })
@ -491,17 +496,19 @@ public final class WebAppController: ViewController, AttachmentContainable {
strongSelf.queryId = result.queryId strongSelf.queryId = result.queryId
strongSelf.webView?.load(URLRequest(url: parsedUrl)) strongSelf.webView?.load(URLRequest(url: parsedUrl))
strongSelf.keepAliveDisposable = (result.keepAliveSignal if let keepAliveSignal = result.keepAliveSignal {
|> deliverOnMainQueue).start(error: { [weak self] _ in strongSelf.keepAliveDisposable = (keepAliveSignal
if let strongSelf = self { |> deliverOnMainQueue).start(error: { [weak self] _ in
strongSelf.controller?.dismiss() if let strongSelf = self {
} strongSelf.controller?.dismiss()
}, completed: { [weak self] in }
if let strongSelf = self { }, completed: { [weak self] in
strongSelf.controller?.completion() if let strongSelf = self {
strongSelf.controller?.dismiss() strongSelf.controller?.completion()
} strongSelf.controller?.dismiss()
}) }
})
}
} }
}) })
} }
@ -909,6 +916,11 @@ public final class WebAppController: ViewController, AttachmentContainable {
} }
case "web_app_open_link": case "web_app_open_link":
if let json = json, let url = json["url"] as? String { if let json = json, let url = json["url"] as? String {
let webAppConfiguration = WebAppConfiguration.with(appConfiguration: self.context.currentAppConfiguration.with { $0 })
if let escapedUrl = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let url = URL(string: escapedUrl), let scheme = url.scheme?.lowercased(), !["http", "https"].contains(scheme) && !webAppConfiguration.allowedProtocols.contains(scheme) {
return
}
let tryInstantView = json["try_instant_view"] as? Bool ?? false let tryInstantView = json["try_instant_view"] as? Bool ?? false
if let lastTouchTimestamp = self.webView?.lastTouchTimestamp, currentTimestamp < lastTouchTimestamp + 10.0 { if let lastTouchTimestamp = self.webView?.lastTouchTimestamp, currentTimestamp < lastTouchTimestamp + 10.0 {
self.webView?.lastTouchTimestamp = nil self.webView?.lastTouchTimestamp = nil
@ -1877,6 +1889,25 @@ public final class WebAppController: ViewController, AttachmentContainable {
self.presentationDataDisposable?.dispose() self.presentationDataDisposable?.dispose()
} }
public func beforeMaximize(navigationController: NavigationController, completion: @escaping () -> Void) {
switch self.source {
case .generic, .settings:
completion()
case .inline, .attachMenu, .menu, .simple:
let _ = (self.context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.Peer(id: self.peerId)
)
|> deliverOnMainQueue).start(next: { [weak self] chatPeer in
guard let self, let chatPeer else {
return
}
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(chatPeer), completion: { _ in
completion()
}))
})
}
}
fileprivate func updateNavigationBarTheme(transition: ContainedViewLayoutTransition) { fileprivate func updateNavigationBarTheme(transition: ContainedViewLayoutTransition) {
let navigationBarPresentationData: NavigationBarPresentationData let navigationBarPresentationData: NavigationBarPresentationData
if let backgroundColor = self.controllerNode.headerColor, let textColor = self.controllerNode.headerPrimaryTextColor { if let backgroundColor = self.controllerNode.headerColor, let textColor = self.controllerNode.headerPrimaryTextColor {
@ -2095,6 +2126,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
public func shouldDismissImmediately() -> Bool { public func shouldDismissImmediately() -> Bool {
return true return true
} }
fileprivate var canMinimize: Bool {
return self.controllerNode.canMinimize
}
} }
final class WebAppPickerContext: AttachmentMediaPickerContext { final class WebAppPickerContext: AttachmentMediaPickerContext {
@ -2172,8 +2207,9 @@ public func standaloneWebAppController(
willDismiss: @escaping () -> Void = {}, willDismiss: @escaping () -> Void = {},
didDismiss: @escaping () -> Void = {}, didDismiss: @escaping () -> Void = {},
getNavigationController: @escaping () -> NavigationController? = { return nil }, getNavigationController: @escaping () -> NavigationController? = { return nil },
getSourceRect: (() -> CGRect?)? = nil) -> ViewController { getSourceRect: (() -> CGRect?)? = nil
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: params.peerId), buttons: [.standalone], initialButton: .standalone, fromMenu: params.source == .menu, hasTextInput: false, makeEntityInputView: { ) -> ViewController {
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: params.peerId), buttons: [.standalone], initialButton: .standalone, fromMenu: params.source == .menu, hasTextInput: false, isFullSize: params.fullSize, makeEntityInputView: {
return nil return nil
}) })
controller.requestController = { _, present in controller.requestController = { _, present in
@ -2188,8 +2224,35 @@ public func standaloneWebAppController(
controller.didDismiss = didDismiss controller.didDismiss = didDismiss
controller.getSourceRect = getSourceRect controller.getSourceRect = getSourceRect
controller.title = params.botName controller.title = params.botName
controller.shouldMinimizeOnSwipe = { _ in controller.shouldMinimizeOnSwipe = { [weak controller] _ in
return true if let controller, let mainController = controller.mainController as? WebAppController {
return mainController.canMinimize
}
return false
} }
return controller return controller
} }
private struct WebAppConfiguration {
static var defaultValue: WebAppConfiguration {
return WebAppConfiguration(allowedProtocols: [])
}
let allowedProtocols: [String]
fileprivate init(allowedProtocols: [String]) {
self.allowedProtocols = allowedProtocols
}
static func with(appConfiguration: AppConfiguration) -> WebAppConfiguration {
if let data = appConfiguration.data {
var allowedProtocols: [String] = []
if let value = data["web_app_allowed_protocols"] as? [String] {
allowedProtocols = value
}
return WebAppConfiguration(allowedProtocols: allowedProtocols)
} else {
return .defaultValue
}
}
}