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_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_NOTEXT" = "%1$@|posted a message";
@ -138,6 +139,7 @@
"PUSH_CHANNEL_ALBUM" = "%1$@|posted an album";
"PUSH_CHANNEL_MESSAGE_DOCS_TEXT_1" = "posted a file";
"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_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_NOTHEME" = "%1$@|disabled theme in 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_NOTEXT" = "%1$@|pinned a message";
@ -200,6 +203,7 @@
"PUSH_PINNED_GAME" = "%1$@|pinned a game";
"PUSH_PINNED_INVOICE" = "%1$@|pinned an invoice";
"PUSH_PINNED_GIF" = "%1$@|pinned a GIF";
"PUSH_PINNED_PAID_MEDIA" = "%1$@|pinned a paid post for %2$@";
"PUSH_CONTACT_JOINED" = "%1$@|joined Telegram!";
@ -252,6 +256,7 @@
"PUSH_CHAT_REACT_GAME" = "%2$@|%1$@ %3$@ to your game";
"PUSH_CHAT_REACT_INVOICE" = "%2$@|%1$@ %3$@ to your invoice";
"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";
@ -273,6 +278,7 @@
"PUSH_REACT_STORY" = "%1$@|%2$@ to your story";
"PUSH_REACT_STORY_HIDDEN" = "New reaction to your story";
"LOCAL_MESSAGE_FWDS" = "%1$@ forwarded you %2$d messages";
"LOCAL_CHANNEL_MESSAGE_FWDS" = "%1$@ posted %2$d forwarded 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 payload: String?
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.payload = payload
self.justInstalled = justInstalled
self.compact = compact
}
}

View File

@ -47,6 +47,7 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
private var isDismissed = false
private var isInteractiveDimissEnabled = true
private let isFullSize: Bool
public private(set) var isExpanded = false
private var validLayout: (layout: ContainerViewLayout, controllers: [AttachmentContainable], coveredByModalTransition: CGFloat)?
@ -72,7 +73,12 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
var isPanGestureEnabled: (() -> Bool)?
var onExpandAnimationCompleted: () -> Void = {}
override init() {
init(isFullSize: Bool) {
self.isFullSize = isFullSize
if isFullSize {
self.isExpanded = true
}
self.wrappingNode = 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.requestDismiss?()
return
}
var bounds = self.bounds
if self.isExpanded {
if self.isExpanded && !self.isFullSize {
bounds.origin.y = -max(0.0, translation - edgeTopInset)
} else {
bounds.origin.y = -translation
@ -307,7 +313,7 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
}
var bounds = self.bounds
if self.isExpanded {
if self.isExpanded && !self.isFullSize {
bounds.origin.y = -max(0.0, translation - edgeTopInset)
} else {
bounds.origin.y = -translation
@ -326,21 +332,29 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
var minimizing = 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 {
dismissing = true
} else {
minimizing = true
}
} 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
if let listNode = listNode {
listNode.scroller.setContentOffset(CGPoint(), animated: false)
} else if let scrollView = scrollView {
scrollView.setContentOffset(CGPoint(x: scrollView.contentOffset.x, y: -scrollView.contentInset.top), animated: false)
}
let distance = topInset - offset
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))
@ -432,6 +446,7 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
}
self.isUpdatingState = true
let isFirstTime = self.validLayout == nil
self.validLayout = (layout, controllers, coveredByModalTransition)
self.panGestureRecognizer?.isEnabled = (layout.inputHeight == nil || layout.inputHeight == 0.0)
@ -446,7 +461,7 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
}
let topInset: CGFloat
if let (panInitialTopInset, panOffset, _, _) = self.panGestureArguments {
if !self.isFullSize, let (panInitialTopInset, panOffset, _, _) = self.panGestureArguments {
if effectiveExpanded {
topInset = min(edgeTopInset, panInitialTopInset + max(0.0, panOffset))
} else {
@ -459,9 +474,29 @@ final class AttachmentContainer: ASDisplayNode, ASGestureRecognizerDelegate {
completion()
})
let modalProgress = isLandscape ? 0.0 : (1.0 - topInset / defaultTopInset)
self.updateModalProgress?(modalProgress, topInset, self.bounds, transition)
let modalProgress: CGFloat
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 containerFrame: CGRect
let clipFrame: CGRect

View File

@ -131,6 +131,8 @@ public protocol AttachmentContainable: ViewController {
func requestDismiss(completion: @escaping () -> Void)
func shouldDismissImmediately() -> Bool
func beforeMaximize(navigationController: NavigationController, completion: @escaping () -> Void)
}
public extension AttachmentContainable {
@ -154,6 +156,10 @@ public extension AttachmentContainable {
return true
}
func beforeMaximize(navigationController: NavigationController, completion: @escaping () -> Void) {
completion()
}
var isPanGestureEnabled: (() -> Bool)? {
return nil
}
@ -234,6 +240,7 @@ public class AttachmentController: ViewController {
private let initialButton: AttachmentButtonType
private let fromMenu: Bool
private var hasTextInput: Bool
private let isFullSize: Bool
private let makeEntityInputView: () -> AttachmentTextInputPanelInputView?
public var animateAppearance: Bool = false
@ -345,7 +352,7 @@ public class AttachmentController: ViewController {
self.wrapperNode = ASDisplayNode()
self.wrapperNode.clipsToBounds = true
self.container = AttachmentContainer()
self.container = AttachmentContainer(isFullSize: controller.isFullSize)
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.fromMenu = controller.fromMenu
@ -574,7 +581,13 @@ public class AttachmentController: ViewController {
guard let controller = self.controller, let navigationController = controller.navigationController as? NavigationController else {
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?
if let current = current as? MinimizedContainerImpl {
minimizedContainer = current
@ -1062,7 +1075,7 @@ public class AttachmentController: ViewController {
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.updatedPresentationData = updatedPresentationData
self.chatLocation = chatLocation
@ -1071,6 +1084,7 @@ public class AttachmentController: ViewController {
self.initialButton = initialButton
self.fromMenu = fromMenu
self.hasTextInput = hasTextInput
self.isFullSize = isFullSize
self.makeEntityInputView = makeEntityInputView
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 {
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?
if let current = current as? MinimizedContainerImpl {
minimizedContainer = current

View File

@ -8,7 +8,7 @@ public protocol MinimizedContainer: ASDisplayNode {
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 collapse()
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 minimizedContainer.supernode !== self.displayNode {
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 {
self.displayNode.insertSubnode(minimizedContainer, at: 0)
}
@ -1572,7 +1576,7 @@ open class NavigationController: UINavigationController, ContainableController,
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 minimizedContainer = setupContainer(self.minimizedContainer)
@ -1592,7 +1596,7 @@ open class NavigationController: UINavigationController, ContainableController,
}
viewController.isMinimized = true
self.filterController(viewController, animated: true)
minimizedContainer?.addController(viewController, transition: transition)
minimizedContainer?.addController(viewController, beforeMaximize: beforeMaximize, transition: transition)
}
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 positionTransition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .easeInOut)
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
guard let strongSelf = self else {
return

View File

@ -54,7 +54,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[571523412] = { return $0.readDouble() }
dict[-1255641564] = { return parseString($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[-1297663893] = { return Api.AttachMenuBotIcon.parse_attachMenuBotIcon($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[-618540889] = { return Api.InputSecureValue.parse_inputSecureValue($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[215889721] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmojiAnimations($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[-651419003] = { return Api.SendMessageAction.parse_speakingInGroupCallAction($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[-1108478618] = { return Api.SponsoredMessage.parse_sponsoredMessage($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[1421174295] = { return Api.WebPageAttribute.parse_webPageAttributeTheme($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[1275039392] = { return Api.account.Authorizations.parse_authorizations($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[1398007207] = { return Api.auth.SentCodeType.parse_sentCodeTypeCall($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[-648651719] = { return Api.auth.SentCodeType.parse_sentCodeTypeFragmentSms($0) }
dict[-2113903484] = { return Api.auth.SentCodeType.parse_sentCodeTypeMissedCall($0) }
@ -1437,8 +1436,6 @@ public extension Api {
switch object {
case let _1 as Api.AccountDaysTTL:
_1.serialize(buffer, boxed)
case let _1 as Api.AppWebViewResult:
_1.serialize(buffer, boxed)
case let _1 as Api.AttachMenuBot:
_1.serialize(buffer, boxed)
case let _1 as Api.AttachMenuBotIcon:
@ -1749,6 +1746,8 @@ public extension Api {
_1.serialize(buffer, boxed)
case let _1 as Api.InputSingleMedia:
_1.serialize(buffer, boxed)
case let _1 as Api.InputStarsTransaction:
_1.serialize(buffer, boxed)
case let _1 as Api.InputStickerSet:
_1.serialize(buffer, boxed)
case let _1 as Api.InputStickerSetItem:
@ -1979,8 +1978,6 @@ public extension Api {
_1.serialize(buffer, boxed)
case let _1 as Api.ShippingOption:
_1.serialize(buffer, boxed)
case let _1 as Api.SimpleWebViewResult:
_1.serialize(buffer, boxed)
case let _1 as Api.SmsJob:
_1.serialize(buffer, boxed)
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 {
enum AttachMenuBot: TypeConstructorDescription {
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 {
enum InputStickerSet: TypeConstructorDescription {
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 {
enum Invoice: TypeConstructorDescription {
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 {
enum MaskCoords: TypeConstructorDescription {
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 {
indirect enum BotCommandScope: TypeConstructorDescription {
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 {
enum SmsJob: TypeConstructorDescription {
case smsJob(jobId: String, phoneNumber: String, text: String)

View File

@ -160,15 +160,16 @@ public extension Api {
}
public extension Api {
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) {
switch self {
case .webViewResultUrl(let queryId, let url):
case .webViewResultUrl(let flags, let queryId, let url):
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)
break
}
@ -176,20 +177,23 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .webViewResultUrl(let queryId, let url):
return ("webViewResultUrl", [("queryId", queryId as Any), ("url", url as Any)])
case .webViewResultUrl(let flags, let queryId, let url):
return ("webViewResultUrl", [("flags", flags as Any), ("queryId", queryId as Any), ("url", url as Any)])
}
}
public static func parse_webViewResultUrl(_ reader: BufferReader) -> WebViewResult? {
var _1: Int64?
_1 = reader.readInt64()
var _2: String?
_2 = parseString(reader)
var _1: Int32?
_1 = reader.readInt32()
var _2: Int64?
if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt64() }
var _3: String?
_3 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.WebViewResult.webViewResultUrl(queryId: _1!, url: _2!)
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.WebViewResult.webViewResultUrl(flags: _1!, queryId: _2, url: _3!)
}
else {
return nil

View File

@ -589,7 +589,7 @@ public extension Api.auth {
case sentCodeTypeApp(length: Int32)
case sentCodeTypeCall(length: 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 sentCodeTypeFragmentSms(url: 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 << 4) != 0 {serializeInt32(resetPendingDate!, buffer: buffer, boxed: false)}
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 {
buffer.appendInt32(331943703)
buffer.appendInt32(10475318)
}
serializeInt32(flags, 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 << 1) != 0 {serializeString(receipt!, 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)])
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)])
case .sentCodeTypeFirebaseSms(let flags, let nonce, 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)])
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), ("playIntegrityProjectId", playIntegrityProjectId as Any), ("playIntegrityNonce", playIntegrityNonce as Any), ("receipt", receipt as Any), ("pushTimeout", pushTimeout as Any), ("length", length as Any)])
case .sentCodeTypeFlashCall(let pattern):
return ("sentCodeTypeFlashCall", [("pattern", pattern as Any)])
case .sentCodeTypeFragmentSms(let url, let length):
@ -759,22 +760,25 @@ public extension Api.auth {
_1 = reader.readInt32()
var _2: Buffer?
if Int(_1!) & Int(1 << 0) != 0 {_2 = parseBytes(reader) }
var _3: Buffer?
if Int(_1!) & Int(1 << 2) != 0 {_3 = parseBytes(reader) }
var _4: String?
if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(reader) }
var _5: Int32?
if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() }
var _3: Int64?
if Int(_1!) & Int(1 << 2) != 0 {_3 = reader.readInt64() }
var _4: Buffer?
if Int(_1!) & Int(1 << 2) != 0 {_4 = parseBytes(reader) }
var _5: String?
if Int(_1!) & Int(1 << 1) != 0 {_5 = parseString(reader) }
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 _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != 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 _c6 = _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.auth.SentCodeType.sentCodeTypeFirebaseSms(flags: _1!, nonce: _2, playIntegrityNonce: _3, receipt: _4, pushTimeout: _5, length: _6!)
let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil
let _c7 = _7 != nil
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 {
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 {
enum BusinessLocation: TypeConstructorDescription {
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 {
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()
buffer.appendInt32(-1940243652)
buffer.appendInt32(1398901710)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.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 << 2) != 0 {themeParams!.serialize(buffer, true)}
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)
var result: Api.AppWebViewResult?
var result: Api.WebViewResult?
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
})
@ -7345,20 +7345,20 @@ 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()
buffer.appendInt32(440815626)
buffer.appendInt32(1094336115)
serializeInt32(flags, buffer: buffer, boxed: false)
bot.serialize(buffer, true)
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 << 0) != 0 {themeParams!.serialize(buffer, true)}
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)
var result: Api.SimpleWebViewResult?
var result: Api.WebViewResult?
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
})
@ -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 {
static func launchPrepaidGiveaway(peer: Api.InputPeer, giveawayId: Int64, purpose: Api.InputStorePaymentPurpose) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()

View File

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

View File

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

View File

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

View File

@ -16,16 +16,12 @@ public enum RequestSimpleWebViewSource {
case settings
}
public enum RequestSimpleWebViewError {
case generic
}
func _internal_requestSimpleWebView(postbox: Postbox, network: Network, botId: PeerId, url: String?, source: RequestSimpleWebViewSource, themeParams: [String: Any]?) -> Signal<String, RequestSimpleWebViewError> {
func _internal_requestSimpleWebView(postbox: Postbox, network: Network, botId: PeerId, url: String?, source: RequestSimpleWebViewSource, themeParams: [String: Any]?) -> Signal<RequestWebViewResult, RequestWebViewError> {
var serializedThemeParams: Api.DataJSON?
if let themeParams = themeParams, let data = try? JSONSerialization.data(withJSONObject: themeParams, options: []), let dataString = String(data: data, encoding: .utf8) {
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 {
return .fail(.generic)
}
@ -46,17 +42,21 @@ func _internal_requestSimpleWebView(postbox: Postbox, network: Network, botId: P
flags |= (1 << 3)
}
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
}
|> mapToSignal { result -> Signal<String, RequestSimpleWebViewError> in
|> mapToSignal { result -> Signal<RequestWebViewResult, RequestWebViewError> in
switch result {
case let .simpleWebViewResultUrl(url):
return .single(url)
case let .webViewResultUrl(flags, queryId, 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
}
@ -65,9 +65,24 @@ public enum KeepWebViewError {
}
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 keepAliveSignal: Signal<Never, KeepWebViewError>
public let keepAliveSignal: Signal<Never, KeepWebViewError>?
}
public enum RequestWebViewError {
@ -166,8 +181,19 @@ func _internal_requestWebView(postbox: Postbox, network: Network, stateManager:
}
|> mapToSignal { result -> Signal<RequestWebViewResult, RequestWebViewError> in
switch result {
case let .webViewResultUrl(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)))
case let .webViewResultUrl(webViewFlags, queryId, url):
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
}
public enum RequestAppWebViewError {
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> {
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> {
var serializedThemeParams: Api.DataJSON?
if let themeParams = themeParams, let data = try? JSONSerialization.data(withJSONObject: themeParams, options: []), let dataString = String(data: data, encoding: .utf8) {
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 {
return .fail(.generic)
}
@ -235,19 +257,26 @@ func _internal_requestAppWebView(postbox: Postbox, network: Network, stateManage
if allowWrite {
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))
|> mapError { _ -> RequestAppWebViewError in
|> mapError { _ -> RequestWebViewError in
return .generic
}
|> mapToSignal { result -> Signal<String, RequestAppWebViewError> in
|> mapToSignal { result -> Signal<RequestWebViewResult, RequestWebViewError> in
switch result {
case let .appWebViewResultUrl(url):
return .single(url)
case let .webViewResultUrl(flags, queryId, 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
}

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)
}
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)
}
public func requestAppWebView(peerId: PeerId, appReference: BotAppReference, payload: String?, themeParams: [String: Any]?, allowWrite: Bool) -> Signal<String, RequestAppWebViewError> {
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)
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, compact: compact, allowWrite: allowWrite)
}
public func sendWebViewData(botId: PeerId, buttonText: String, data: String) -> Signal<Never, SendWebViewDataError> {

View File

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

View File

@ -7,6 +7,7 @@ import Display
import TelegramPresentationData
import AccountContext
import WallpaperBackgroundNode
import UrlHandling
private let titleFont = Font.medium(16.0)
@ -178,7 +179,9 @@ private final class ChatMessageActionButtonNode: ASDisplayNode {
case .text:
iconImage = incoming ? graphics.chatBubbleActionButtonIncomingMessageIconImage : graphics.chatBubbleActionButtonOutgoingMessageIconImage
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
} else {
iconImage = incoming ? graphics.chatBubbleActionButtonIncomingLinkIconImage : graphics.chatBubbleActionButtonOutgoingLinkIconImage

View File

@ -27,10 +27,12 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
final class Item {
let id: AnyHashable
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.controller = controller
self.beforeMaximize = beforeMaximize
}
}
@ -302,6 +304,8 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
private var dismissingItemId: AnyHashable?
private var dismissingItemOffset: CGFloat?
private var expandedTapGestureRecoginzer: UITapGestureRecognizer?
private var currentTransition: Transition?
private var isApplyingTransition = false
private var validLayout: ContainerViewLayout?
@ -370,6 +374,11 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
panGestureRecognizer.delegate = self.wrappedGestureRecognizerDelegate
panGestureRecognizer.delaysTouchesBegan = true
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? {
@ -401,6 +410,15 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
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) {
if self.isExpanded {
self.dismissPanGesture(gestureRecognizer)
@ -483,10 +501,11 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
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(
id: AnyHashable(Int64.random(in: Int64.min ... Int64.max)),
controller: viewController
controller: viewController,
beforeMaximize: beforeMaximize
)
self.items.append(item)
@ -552,7 +571,11 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
return
}
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 {
self.isExpanded = true
self.requestUpdate(transition: .animated(duration: 0.4, curve: .spring))
@ -697,7 +720,11 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
return
}
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 {
self.expand()
}
@ -816,6 +843,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
}
self.scrollView.passthrough = !self.isExpanded
self.scrollView.isScrollEnabled = self.isExpanded
self.expandedTapGestureRecoginzer?.isEnabled = self.isExpanded
if let currentTransition = self.currentTransition {
self.isApplyingTransition = true
@ -904,28 +932,33 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
return
}
if self.items.count == 1 {
if let itemNode = self.itemNodes.first(where: { $0.0 != itemId })?.value {
let dimView = UIView()
dimView.frame = CGRect(origin: .zero, size: layout.size)
dimView.backgroundColor = UIColor(white: 0.0, alpha: 0.25)
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
if let itemNode = self.itemNodes.first(where: { $0.0 != itemId })?.value, let navigationController = self.navigationController {
itemNode.item.beforeMaximize(navigationController, { [weak self] in
guard let self else {
return
}
completion(currentTransition)
self.itemNodes[itemId] = nil
itemNode.removeFromSupernode()
dimView.removeFromSuperview()
let dimView = UIView()
dimView.frame = CGRect(origin: .zero, size: layout.size)
dimView.backgroundColor = UIColor(white: 0.0, alpha: 0.25)
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)
self.requestUpdate(transition: .immediate)
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)
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))

View File

@ -5312,7 +5312,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
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
self?.openUrl(url: url, concealed: concealed, external: false, forceExternal: true, commit: commit)
}, requestSwitchInline: { _, _, _ in

View File

@ -1808,7 +1808,7 @@ final class StoryItemSetContainerSendMessage {
//TODO:gift controller
break
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 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)

View File

@ -10,8 +10,9 @@ import AttachmentUI
import AccountContext
import TelegramNotices
import PresentationDataUtils
import UndoUI
extension ChatControllerImpl {
public extension ChatControllerImpl {
func openWebApp(buttonText: String, url: String, simple: Bool, source: ChatOpenWebViewSource) {
guard let peerId = self.chatLocation.peerId, let peer = self.presentationInterfaceState.renderedPeer?.peer else {
return
@ -76,14 +77,7 @@ extension ChatControllerImpl {
if source == .menu {
self.updateChatPresentationInterfaceState(interactive: false) { state in
return state.updatedForceInputCommandsHidden(true)
// return state.updatedShowWebView(true).updatedForceInputCommandsHidden(true)
}
if let currentMenuWebAppController = self.currentMenuWebAppController {
if currentMenuWebAppController.isMinimized {
(currentMenuWebAppController.navigationController as? NavigationController)?.maximizeViewController(currentMenuWebAppController, animated: true)
return
}
// return state.updatedShowWebView(true).updatedForceInputCommandsHidden(true)
}
if let navigationController = self.navigationController as? NavigationController, let minimizedContainer = navigationController.minimizedContainer {
@ -96,7 +90,7 @@ extension ChatControllerImpl {
}
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
self?.openUrl(url, concealed: concealed, forceExternal: true, commit: commit)
}, requestSwitchInline: { [weak self] query, chatTypes, completion in
@ -146,7 +140,6 @@ extension ChatControllerImpl {
})
controller.navigationPresentation = .flatModal
self.push(controller)
self.currentMenuWebAppController = controller
} else if simple {
var isInline = false
var botId = peerId
@ -163,12 +156,12 @@ extension ChatControllerImpl {
|> afterDisposed {
updateProgress()
})
|> deliverOnMainQueue).startStrict(next: { [weak self] url in
|> deliverOnMainQueue).startStrict(next: { [weak self] result in
guard let strongSelf = self else {
return
}
let context = strongSelf.context
let params = WebAppParameters(source: isInline ? .inline : .simple, peerId: peerId, botId: botId, botName: botName, url: url, queryId: nil, payload: nil, buttonText: buttonText, keepAliveSignal: nil, forceHasSettings: false)
let 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
self?.openUrl(url, concealed: concealed, forceExternal: true, commit: commit)
}, requestSwitchInline: { [weak self] query, chatTypes, completion in
@ -209,7 +202,7 @@ extension ChatControllerImpl {
return
}
let context = strongSelf.context
let params = WebAppParameters(source: .generic, peerId: peerId, botId: peerId, botName: botName, url: result.url, queryId: result.queryId, payload: nil, buttonText: buttonText, keepAliveSignal: result.keepAliveSignal, forceHasSettings: false)
let 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
self?.openUrl(url, concealed: concealed, forceExternal: true, commit: commit)
}, 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?>
weak var attachmentController: AttachmentController?
weak var currentMenuWebAppController: ViewController?
weak var currentWebAppController: ViewController?
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))
}
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) {
var maybeFoundItemNode: ChatMessageItemView?
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))
|> deliverOnMainQueue).startStandalone(next: { [weak self] peer in
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()
commit()
})

View File

@ -615,7 +615,7 @@ extension ChatControllerImpl {
payload = botPayload
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 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

View File

@ -165,7 +165,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
controller.presentAttachmentBot(botId: attachBotStart.botId, payload: attachBotStart.payload, justInstalled: attachBotStart.justInstalled)
}
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)
found = true
@ -188,7 +188,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
}
if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation {
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 {
@ -196,7 +196,7 @@ public func navigateToChatControllerImpl(_ params: NavigateToChatControllerParam
if let botAppStart = params.botAppStart, case let .peer(peer) = params.chatLocation {
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 replyThread(Int32, Int32)
case voiceChat(String?)
case appStart(String, String?)
case appStart(String, String?, Bool)
case story(Int32)
case boost
case text(String)
@ -588,16 +588,19 @@ public func parseInternalUrl(sharedContext: SharedAccountContext, query: String)
} else if pathComponents.count == 2 {
let appName = pathComponents[1]
var startApp: String?
var compact = false
if let queryItems = components.queryItems {
for queryItem in queryItems {
if let value = queryItem.value {
if queryItem.name == "startapp" {
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 {
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)
|> map(Optional.init)
|> `catch` { _ -> Signal<BotApp?, NoError> in
@ -721,7 +724,7 @@ private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl)
}
|> mapToSignal { botApp -> Signal<ResolveInternalUrlResult, NoError> in
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 {
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
}
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 {
static var defaultValue: UrlHandlingConfiguration {
return UrlHandlingConfiguration(domains: [], urlAuthDomains: [])

View File

@ -208,6 +208,7 @@ public struct WebAppParameters {
let buttonText: String?
let keepAliveSignal: Signal<Never, KeepWebViewError>?
let forceHasSettings: Bool
let fullSize: Bool
public init(
source: Source,
@ -219,7 +220,8 @@ public struct WebAppParameters {
payload: String?,
buttonText: String?,
keepAliveSignal: Signal<Never, KeepWebViewError>?,
forceHasSettings: Bool
forceHasSettings: Bool,
fullSize: Bool
) {
self.source = source
self.peerId = peerId
@ -231,6 +233,7 @@ public struct WebAppParameters {
self.buttonText = buttonText
self.keepAliveSignal = keepAliveSignal
self.forceHasSettings = forceHasSettings
self.fullSize = fullSize
}
}
@ -288,6 +291,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
private let context: AccountContext
var presentationData: PresentationData
private var queryId: Int64?
fileprivate let canMinimize = true
private var placeholderDisposable: Disposable?
private var iconDisposable: Disposable?
@ -306,7 +310,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
self.context = context
self.controller = controller
self.presentationData = controller.presentationData
self.backgroundNode = ASDisplayNode()
self.headerBackgroundNode = ASDisplayNode()
self.topOverscrollNode = ASDisplayNode()
@ -477,7 +481,8 @@ public final class WebAppController: ViewController, AttachmentContainable {
guard let strongSelf = self else {
return
}
if let parsedUrl = URL(string: result) {
if let parsedUrl = URL(string: result.url) {
strongSelf.queryId = result.queryId
strongSelf.webView?.load(URLRequest(url: parsedUrl))
}
})
@ -491,17 +496,19 @@ public final class WebAppController: ViewController, AttachmentContainable {
strongSelf.queryId = result.queryId
strongSelf.webView?.load(URLRequest(url: parsedUrl))
strongSelf.keepAliveDisposable = (result.keepAliveSignal
|> deliverOnMainQueue).start(error: { [weak self] _ in
if let strongSelf = self {
strongSelf.controller?.dismiss()
}
}, completed: { [weak self] in
if let strongSelf = self {
strongSelf.controller?.completion()
strongSelf.controller?.dismiss()
}
})
if let keepAliveSignal = result.keepAliveSignal {
strongSelf.keepAliveDisposable = (keepAliveSignal
|> deliverOnMainQueue).start(error: { [weak self] _ in
if let strongSelf = self {
strongSelf.controller?.dismiss()
}
}, completed: { [weak self] in
if let strongSelf = self {
strongSelf.controller?.completion()
strongSelf.controller?.dismiss()
}
})
}
}
})
}
@ -909,6 +916,11 @@ public final class WebAppController: ViewController, AttachmentContainable {
}
case "web_app_open_link":
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
if let lastTouchTimestamp = self.webView?.lastTouchTimestamp, currentTimestamp < lastTouchTimestamp + 10.0 {
self.webView?.lastTouchTimestamp = nil
@ -1877,6 +1889,25 @@ public final class WebAppController: ViewController, AttachmentContainable {
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) {
let navigationBarPresentationData: NavigationBarPresentationData
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 {
return true
}
fileprivate var canMinimize: Bool {
return self.controllerNode.canMinimize
}
}
final class WebAppPickerContext: AttachmentMediaPickerContext {
@ -2172,8 +2207,9 @@ public func standaloneWebAppController(
willDismiss: @escaping () -> Void = {},
didDismiss: @escaping () -> Void = {},
getNavigationController: @escaping () -> NavigationController? = { return nil },
getSourceRect: (() -> CGRect?)? = nil) -> ViewController {
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: params.peerId), buttons: [.standalone], initialButton: .standalone, fromMenu: params.source == .menu, hasTextInput: false, makeEntityInputView: {
getSourceRect: (() -> CGRect?)? = nil
) -> 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
})
controller.requestController = { _, present in
@ -2188,8 +2224,35 @@ public func standaloneWebAppController(
controller.didDismiss = didDismiss
controller.getSourceRect = getSourceRect
controller.title = params.botName
controller.shouldMinimizeOnSwipe = { _ in
return true
controller.shouldMinimizeOnSwipe = { [weak controller] _ in
if let controller, let mainController = controller.mainController as? WebAppController {
return mainController.canMinimize
}
return false
}
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
}
}
}