mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge commit '43405ce029021c2ca4d40bc6241c5625504c7912'
This commit is contained in:
commit
b3678917c8
@ -7418,6 +7418,9 @@ Sorry for the inconvenience.";
|
|||||||
|
|
||||||
"WebApp.AddToAttachmentAlreadyAddedError" = "This bot is already added in the attachment menu.";
|
"WebApp.AddToAttachmentAlreadyAddedError" = "This bot is already added in the attachment menu.";
|
||||||
|
|
||||||
|
"WebApp.OpenWebViewAlertTitle" = "Open Web App";
|
||||||
|
"WebApp.OpenWebViewAlertText" = "**%@** would like to open its web app to proceed.\n\nIt will be able to access your **IP address** and basic device info.";
|
||||||
|
|
||||||
"WebApp.MessagePreview" = "Message Preview";
|
"WebApp.MessagePreview" = "Message Preview";
|
||||||
"WebApp.Send" = "Send";
|
"WebApp.Send" = "Send";
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ public final class AlertControllerContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func textAlertController(alertContext: AlertControllerContext, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissOnOutsideTap: Bool = true) -> AlertController {
|
public func textAlertController(alertContext: AlertControllerContext, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true) -> AlertController {
|
||||||
let controller = standardTextAlertController(theme: alertContext.theme, title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, dismissOnOutsideTap: dismissOnOutsideTap)
|
let controller = standardTextAlertController(theme: alertContext.theme, title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, parseMarkdown: parseMarkdown, dismissOnOutsideTap: dismissOnOutsideTap)
|
||||||
let presentationDataDisposable = alertContext.themeSignal.start(next: { [weak controller] theme in
|
let presentationDataDisposable = alertContext.themeSignal.start(next: { [weak controller] theme in
|
||||||
controller?.theme = theme
|
controller?.theme = theme
|
||||||
})
|
})
|
||||||
|
@ -144,8 +144,11 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private var panGestureArguments: (topInset: CGFloat, offset: CGFloat, scrollView: UIScrollView?, listNode: ListView?)?
|
var isTracking: Bool {
|
||||||
|
return self.panGestureArguments != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private var panGestureArguments: (topInset: CGFloat, offset: CGFloat, scrollView: UIScrollView?, listNode: ListView?)?
|
||||||
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
@objc func panGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||||
guard let (layout, controllers, coveredByModalTransition) = self.validLayout else {
|
guard let (layout, controllers, coveredByModalTransition) = self.validLayout else {
|
||||||
return
|
return
|
||||||
|
@ -18,6 +18,7 @@ public enum AttachmentButtonType: Equatable {
|
|||||||
case contact
|
case contact
|
||||||
case poll
|
case poll
|
||||||
case app(PeerId, String, TelegramMediaFile)
|
case app(PeerId, String, TelegramMediaFile)
|
||||||
|
case standalone
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol AttachmentContainable: ViewController {
|
public protocol AttachmentContainable: ViewController {
|
||||||
@ -25,6 +26,7 @@ public protocol AttachmentContainable: ViewController {
|
|||||||
var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void { get set }
|
var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void { get set }
|
||||||
var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void { get set }
|
var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void { get set }
|
||||||
var cancelPanGesture: () -> Void { get set }
|
var cancelPanGesture: () -> Void { get set }
|
||||||
|
var isContainerPanning: () -> Bool { get set }
|
||||||
|
|
||||||
func resetForReuse()
|
func resetForReuse()
|
||||||
func prepareForReuse()
|
func prepareForReuse()
|
||||||
@ -349,6 +351,15 @@ public class AttachmentController: ViewController {
|
|||||||
strongSelf.container.cancelPanGesture()
|
strongSelf.container.cancelPanGesture()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
controller.isContainerPanning = { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
return strongSelf.container.isTracking
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let previousController = strongSelf.currentControllers.last
|
let previousController = strongSelf.currentControllers.last
|
||||||
strongSelf.currentControllers = [controller]
|
strongSelf.currentControllers = [controller]
|
||||||
|
|
||||||
@ -557,14 +568,21 @@ public class AttachmentController: ViewController {
|
|||||||
containerTransition.updateFrame(node: self.container, frame: CGRect(origin: CGPoint(), size: containerRect.size))
|
containerTransition.updateFrame(node: self.container, frame: CGRect(origin: CGPoint(), size: containerRect.size))
|
||||||
|
|
||||||
var containerInsets = containerLayout.intrinsicInsets
|
var containerInsets = containerLayout.intrinsicInsets
|
||||||
containerInsets.bottom = panelHeight
|
var hasPanel = false
|
||||||
|
if let controller = self.controller, controller.buttons.count > 1 {
|
||||||
|
hasPanel = true
|
||||||
|
containerInsets.bottom = panelHeight
|
||||||
|
}
|
||||||
|
|
||||||
let containerLayout = containerLayout.withUpdatedIntrinsicInsets(containerInsets)
|
let containerLayout = containerLayout.withUpdatedIntrinsicInsets(containerInsets)
|
||||||
|
|
||||||
self.container.update(layout: containerLayout, controllers: controllers, coveredByModalTransition: 0.0, transition: self.switchingController ? .immediate : transition)
|
self.container.update(layout: containerLayout, controllers: controllers, coveredByModalTransition: 0.0, transition: self.switchingController ? .immediate : transition)
|
||||||
|
|
||||||
if self.container.supernode == nil, !controllers.isEmpty && self.container.isReady {
|
if self.container.supernode == nil, !controllers.isEmpty && self.container.isReady {
|
||||||
self.wrapperNode.addSubnode(self.container)
|
self.wrapperNode.addSubnode(self.container)
|
||||||
self.container.addSubnode(self.panel)
|
if hasPanel {
|
||||||
|
self.container.addSubnode(self.panel)
|
||||||
|
}
|
||||||
|
|
||||||
self.animateIn()
|
self.animateIn()
|
||||||
}
|
}
|
||||||
|
@ -184,6 +184,10 @@ private final class AttachButtonComponent: CombinedComponent {
|
|||||||
name = appName
|
name = appName
|
||||||
imageName = ""
|
imageName = ""
|
||||||
imageFile = appIcon
|
imageFile = appIcon
|
||||||
|
case .standalone:
|
||||||
|
name = ""
|
||||||
|
imageName = ""
|
||||||
|
imageFile = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let tintColor = component.isSelected ? component.theme.rootController.tabBar.selectedIconColor : component.theme.rootController.tabBar.iconColor
|
let tintColor = component.isSelected ? component.theme.rootController.tabBar.selectedIconColor : component.theme.rootController.tabBar.iconColor
|
||||||
|
@ -521,6 +521,7 @@ private class CreatePollControllerImpl: ItemListController, AttachmentContainabl
|
|||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
|
public var isContainerPanning: () -> Bool = { return false }
|
||||||
}
|
}
|
||||||
|
|
||||||
public func createPollController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer, isQuiz: Bool? = nil, completion: @escaping (ComposedPoll) -> Void) -> AttachmentContainable {
|
public func createPollController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer, isQuiz: Bool? = nil, completion: @escaping (ComposedPoll) -> Void) -> AttachmentContainable {
|
||||||
|
@ -409,6 +409,7 @@ open class LegacyController: ViewController, PresentableController, AttachmentCo
|
|||||||
open var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
open var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
open var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
open var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
open var cancelPanGesture: () -> Void = { }
|
open var cancelPanGesture: () -> Void = { }
|
||||||
|
open var isContainerPanning: () -> Bool = { return false }
|
||||||
|
|
||||||
public init(presentation: LegacyControllerPresentation, theme: PresentationTheme? = nil, strings: PresentationStrings? = nil, initialLayout: ContainerViewLayout? = nil) {
|
public init(presentation: LegacyControllerPresentation, theme: PresentationTheme? = nil, strings: PresentationStrings? = nil, initialLayout: ContainerViewLayout? = nil) {
|
||||||
self.sizeClass.set(SSignal.single(UIUserInterfaceSizeClass.compact.rawValue as NSNumber))
|
self.sizeClass.set(SSignal.single(UIUserInterfaceSizeClass.compact.rawValue as NSNumber))
|
||||||
|
@ -75,6 +75,7 @@ public final class LocationPickerController: ViewController, AttachmentContainab
|
|||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
|
public var isContainerPanning: () -> Bool = { return false }
|
||||||
|
|
||||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: LocationPickerMode, completion: @escaping (TelegramMediaMap, String?) -> Void) {
|
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: LocationPickerMode, completion: @escaping (TelegramMediaMap, String?) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
|
@ -133,6 +133,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable {
|
|||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
|
public var isContainerPanning: () -> Bool = { return false }
|
||||||
|
|
||||||
var dismissAll: () -> Void = { }
|
var dismissAll: () -> Void = { }
|
||||||
|
|
||||||
|
@ -5,11 +5,11 @@ import AccountContext
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
|
||||||
public func textAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, forceTheme: PresentationTheme? = nil, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissOnOutsideTap: Bool = true) -> AlertController {
|
public func textAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, forceTheme: PresentationTheme? = nil, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true) -> AlertController {
|
||||||
return textAlertController(sharedContext: context.sharedContext, updatedPresentationData: updatedPresentationData, forceTheme: forceTheme, title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, dismissOnOutsideTap: dismissOnOutsideTap)
|
return textAlertController(sharedContext: context.sharedContext, updatedPresentationData: updatedPresentationData, forceTheme: forceTheme, title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, parseMarkdown: parseMarkdown, dismissOnOutsideTap: dismissOnOutsideTap)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func textAlertController(sharedContext: SharedAccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, forceTheme: PresentationTheme? = nil, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissOnOutsideTap: Bool = true) -> AlertController {
|
public func textAlertController(sharedContext: SharedAccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, forceTheme: PresentationTheme? = nil, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, parseMarkdown: Bool = false, dismissOnOutsideTap: Bool = true) -> AlertController {
|
||||||
var presentationData = updatedPresentationData?.initial ?? sharedContext.currentPresentationData.with { $0 }
|
var presentationData = updatedPresentationData?.initial ?? sharedContext.currentPresentationData.with { $0 }
|
||||||
if let forceTheme = forceTheme {
|
if let forceTheme = forceTheme {
|
||||||
presentationData = presentationData.withUpdated(theme: forceTheme)
|
presentationData = presentationData.withUpdated(theme: forceTheme)
|
||||||
@ -21,7 +21,7 @@ public func textAlertController(sharedContext: SharedAccountContext, updatedPres
|
|||||||
presentationData = presentationData.withUpdated(theme: forceTheme)
|
presentationData = presentationData.withUpdated(theme: forceTheme)
|
||||||
}
|
}
|
||||||
return AlertControllerTheme(presentationData: presentationData)
|
return AlertControllerTheme(presentationData: presentationData)
|
||||||
}), title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, dismissOnOutsideTap: dismissOnOutsideTap)
|
}), title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, parseMarkdown: parseMarkdown, dismissOnOutsideTap: dismissOnOutsideTap)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func textAlertController(sharedContext: SharedAccountContext, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissOnOutsideTap: Bool = true) -> AlertController {
|
public func textAlertController(sharedContext: SharedAccountContext, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissOnOutsideTap: Bool = true) -> AlertController {
|
||||||
|
@ -35,7 +35,7 @@ public func flatMap<T, E, R>(_ f: @escaping (T) -> R) -> (Signal<T?, E>) -> Sign
|
|||||||
return Signal<R?, E> { subscriber in
|
return Signal<R?, E> { subscriber in
|
||||||
return signal.start(next: { next in
|
return signal.start(next: { next in
|
||||||
if let next = next {
|
if let next = next {
|
||||||
subscriber.putNext(f(next))
|
subscriber.putNext(f(next))
|
||||||
} else {
|
} else {
|
||||||
subscriber.putNext(nil)
|
subscriber.putNext(nil)
|
||||||
}
|
}
|
||||||
|
@ -804,7 +804,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[-440534818] = { return Api.Update.parse_updateUserStatus($0) }
|
dict[-440534818] = { return Api.Update.parse_updateUserStatus($0) }
|
||||||
dict[-1071741569] = { return Api.Update.parse_updateUserTyping($0) }
|
dict[-1071741569] = { return Api.Update.parse_updateUserTyping($0) }
|
||||||
dict[2139689491] = { return Api.Update.parse_updateWebPage($0) }
|
dict[2139689491] = { return Api.Update.parse_updateWebPage($0) }
|
||||||
dict[-118080598] = { return Api.Update.parse_updateWebViewResultSent($0) }
|
dict[361936797] = { return Api.Update.parse_updateWebViewResultSent($0) }
|
||||||
dict[2027216577] = { return Api.Updates.parse_updateShort($0) }
|
dict[2027216577] = { return Api.Updates.parse_updateShort($0) }
|
||||||
dict[1299050149] = { return Api.Updates.parse_updateShortChatMessage($0) }
|
dict[1299050149] = { return Api.Updates.parse_updateShortChatMessage($0) }
|
||||||
dict[826001400] = { return Api.Updates.parse_updateShortMessage($0) }
|
dict[826001400] = { return Api.Updates.parse_updateShortMessage($0) }
|
||||||
|
@ -605,7 +605,7 @@ public extension Api {
|
|||||||
case updateUserStatus(userId: Int64, status: Api.UserStatus)
|
case updateUserStatus(userId: Int64, status: Api.UserStatus)
|
||||||
case updateUserTyping(userId: Int64, action: Api.SendMessageAction)
|
case updateUserTyping(userId: Int64, action: Api.SendMessageAction)
|
||||||
case updateWebPage(webpage: Api.WebPage, pts: Int32, ptsCount: Int32)
|
case updateWebPage(webpage: Api.WebPage, pts: Int32, ptsCount: Int32)
|
||||||
case updateWebViewResultSent(peer: Api.Peer, botId: Int64, queryId: Int64)
|
case updateWebViewResultSent(queryId: Int64)
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
@ -1473,12 +1473,10 @@ public extension Api {
|
|||||||
serializeInt32(pts, buffer: buffer, boxed: false)
|
serializeInt32(pts, buffer: buffer, boxed: false)
|
||||||
serializeInt32(ptsCount, buffer: buffer, boxed: false)
|
serializeInt32(ptsCount, buffer: buffer, boxed: false)
|
||||||
break
|
break
|
||||||
case .updateWebViewResultSent(let peer, let botId, let queryId):
|
case .updateWebViewResultSent(let queryId):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(-118080598)
|
buffer.appendInt32(361936797)
|
||||||
}
|
}
|
||||||
peer.serialize(buffer, true)
|
|
||||||
serializeInt64(botId, buffer: buffer, boxed: false)
|
|
||||||
serializeInt64(queryId, buffer: buffer, boxed: false)
|
serializeInt64(queryId, buffer: buffer, boxed: false)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1684,8 +1682,8 @@ public extension Api {
|
|||||||
return ("updateUserTyping", [("userId", String(describing: userId)), ("action", String(describing: action))])
|
return ("updateUserTyping", [("userId", String(describing: userId)), ("action", String(describing: action))])
|
||||||
case .updateWebPage(let webpage, let pts, let ptsCount):
|
case .updateWebPage(let webpage, let pts, let ptsCount):
|
||||||
return ("updateWebPage", [("webpage", String(describing: webpage)), ("pts", String(describing: pts)), ("ptsCount", String(describing: ptsCount))])
|
return ("updateWebPage", [("webpage", String(describing: webpage)), ("pts", String(describing: pts)), ("ptsCount", String(describing: ptsCount))])
|
||||||
case .updateWebViewResultSent(let peer, let botId, let queryId):
|
case .updateWebViewResultSent(let queryId):
|
||||||
return ("updateWebViewResultSent", [("peer", String(describing: peer)), ("botId", String(describing: botId)), ("queryId", String(describing: queryId))])
|
return ("updateWebViewResultSent", [("queryId", String(describing: queryId))])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3449,19 +3447,11 @@ public extension Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static func parse_updateWebViewResultSent(_ reader: BufferReader) -> Update? {
|
public static func parse_updateWebViewResultSent(_ reader: BufferReader) -> Update? {
|
||||||
var _1: Api.Peer?
|
var _1: Int64?
|
||||||
if let signature = reader.readInt32() {
|
_1 = reader.readInt64()
|
||||||
_1 = Api.parse(reader, signature: signature) as? Api.Peer
|
|
||||||
}
|
|
||||||
var _2: Int64?
|
|
||||||
_2 = reader.readInt64()
|
|
||||||
var _3: Int64?
|
|
||||||
_3 = reader.readInt64()
|
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
if _c1 {
|
||||||
let _c3 = _3 != nil
|
return Api.Update.updateWebViewResultSent(queryId: _1!)
|
||||||
if _c1 && _c2 && _c3 {
|
|
||||||
return Api.Update.updateWebViewResultSent(peer: _1!, botId: _2!, queryId: _3!)
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1514,7 +1514,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
|
|||||||
updatedState.updateMessageReactions(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: msgId), reactions: reactions, eventTimestamp: updatesDate)
|
updatedState.updateMessageReactions(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: msgId), reactions: reactions, eventTimestamp: updatesDate)
|
||||||
case .updateAttachMenuBots:
|
case .updateAttachMenuBots:
|
||||||
updatedState.addUpdateAttachMenuBots()
|
updatedState.addUpdateAttachMenuBots()
|
||||||
case let .updateWebViewResultSent(_, _, queryId):
|
case let .updateWebViewResultSent(queryId):
|
||||||
updatedState.addDismissWebView(queryId)
|
updatedState.addDismissWebView(queryId)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
|
@ -188,20 +188,20 @@ private func setCachedAttachMenuBots(transaction: Transaction, attachMenuBots: A
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func managedSynchronizeAttachMenuBots(postbox: Postbox, network: Network) -> Signal<Never, NoError> {
|
func managedSynchronizeAttachMenuBots(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
|
||||||
let poll = Signal<Never, NoError> { subscriber in
|
let poll = Signal<Void, NoError> { subscriber in
|
||||||
let signal: Signal<Never, NoError> = cachedAttachMenuBots(postbox: postbox)
|
let signal: Signal<Void, NoError> = cachedAttachMenuBots(postbox: postbox)
|
||||||
|> mapToSignal { current in
|
|> mapToSignal { current in
|
||||||
return (network.request(Api.functions.messages.getAttachMenuBots(hash: current?.hash ?? 0))
|
return (network.request(Api.functions.messages.getAttachMenuBots(hash: current?.hash ?? 0))
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
|> `catch` { _ -> Signal<Api.AttachMenuBots?, NoError> in
|
|> `catch` { _ -> Signal<Api.AttachMenuBots?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
|> mapToSignal { result -> Signal<Never, NoError> in
|
|> mapToSignal { result -> Signal<Void, NoError> in
|
||||||
guard let result = result else {
|
guard let result = result else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
return postbox.transaction { transaction in
|
return postbox.transaction { transaction -> Void in
|
||||||
switch result {
|
switch result {
|
||||||
case let .attachMenuBots(hash, bots, users):
|
case let .attachMenuBots(hash, bots, users):
|
||||||
var peers: [Peer] = []
|
var peers: [Peer] = []
|
||||||
@ -237,11 +237,14 @@ func managedSynchronizeAttachMenuBots(postbox: Postbox, network: Network) -> Sig
|
|||||||
case .attachMenuBotsNotModified:
|
case .attachMenuBotsNotModified:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} |> ignoreValues
|
return Void()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return signal.start(completed: {
|
return signal.start(next: { value in
|
||||||
|
subscriber.putNext(value)
|
||||||
|
}, completed: {
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -273,9 +276,16 @@ func _internal_addBotToAttachMenu(postbox: Postbox, network: Network, botId: Pee
|
|||||||
|> `catch` { error -> Signal<Bool, NoError> in
|
|> `catch` { error -> Signal<Bool, NoError> in
|
||||||
return .single(false)
|
return .single(false)
|
||||||
}
|
}
|
||||||
|> afterCompleted {
|
|> mapToSignal { value -> Signal<Bool, NoError> in
|
||||||
let _ = (managedSynchronizeAttachMenuBots(postbox: postbox, network: network)
|
if value {
|
||||||
|> take(1)).start()
|
return managedSynchronizeAttachMenuBots(postbox: postbox, network: network)
|
||||||
|
|> take(1)
|
||||||
|
|> map { _ -> Bool in
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .single(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> switchToLatest
|
|> switchToLatest
|
||||||
|
@ -168,6 +168,7 @@ private class AttachmentFileControllerImpl: ItemListController, AttachmentContai
|
|||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
|
public var isContainerPanning: () -> Bool = { return false }
|
||||||
|
|
||||||
var delayDisappear = false
|
var delayDisappear = false
|
||||||
|
|
||||||
|
@ -3302,7 +3302,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
strongSelf.openResolved(result: .join(joinHash), sourceMessageId: nil)
|
strongSelf.openResolved(result: .join(joinHash), sourceMessageId: nil)
|
||||||
}, openWebView: { [weak self] buttonText, url, simple in
|
}, openWebView: { [weak self] buttonText, url, simple in
|
||||||
guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer else {
|
guard let strongSelf = self, let peerId = strongSelf.chatLocation.peerId, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer as? TelegramUser else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3350,48 +3350,76 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if simple {
|
let openWebView = {
|
||||||
strongSelf.messageActionCallbackDisposable.set(((strongSelf.context.engine.messages.requestSimpleWebView(botId: peerId, url: url, themeParams: generateWebAppThemeParams(strongSelf.presentationData.theme))
|
if simple {
|
||||||
|> afterDisposed {
|
strongSelf.messageActionCallbackDisposable.set(((strongSelf.context.engine.messages.requestSimpleWebView(botId: peerId, url: url, themeParams: generateWebAppThemeParams(strongSelf.presentationData.theme))
|
||||||
updateProgress()
|
|> afterDisposed {
|
||||||
})
|
updateProgress()
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] url in
|
})
|
||||||
guard let strongSelf = self else {
|
|> deliverOnMainQueue).start(next: { [weak self] url in
|
||||||
return
|
guard let strongSelf = self else {
|
||||||
}
|
return
|
||||||
let controller = WebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, buttonText: buttonText, keepAliveSignal: nil, replyToMessageId: nil, iconFile: nil)
|
}
|
||||||
controller.getNavigationController = { [weak self] in
|
|
||||||
return self?.effectiveNavigationController
|
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peerId, botId: peerId, botName: botName, url: url, queryId: nil, buttonText: buttonText, keepAliveSignal: nil)
|
||||||
}
|
strongSelf.present(controller, in: .window(.root))
|
||||||
controller.navigationPresentation = .modal
|
// controller.getNavigationController = { [weak self] in
|
||||||
strongSelf.push(controller)
|
// return self?.effectiveNavigationController
|
||||||
}, error: { [weak self] error in
|
// }
|
||||||
if let strongSelf = self {
|
// controller.navigationPresentation = .modal
|
||||||
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: {
|
// strongSelf.push(controller)
|
||||||
})]), in: .window(.root))
|
}, error: { [weak self] error in
|
||||||
}
|
if let strongSelf = self {
|
||||||
}))
|
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||||
|
})]), in: .window(.root))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
strongSelf.messageActionCallbackDisposable.set(((strongSelf.context.engine.messages.requestWebView(peerId: peerId, botId: peerId, url: url, payload: nil, themeParams: generateWebAppThemeParams(strongSelf.presentationData.theme), replyToMessageId: nil)
|
||||||
|
|> afterDisposed {
|
||||||
|
updateProgress()
|
||||||
|
})
|
||||||
|
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let controller = standaloneWebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peerId, botId: peerId, botName: botName, url: result.url, queryId: result.queryId, buttonText: buttonText, keepAliveSignal: result.keepAliveSignal)
|
||||||
|
strongSelf.present(controller, in: .window(.root))
|
||||||
|
// let controller = WebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peerId, botId: peerId, botName: botName, url: result.url, queryId: result.queryId, buttonText: buttonText, keepAliveSignal: result.keepAliveSignal, replyToMessageId: nil, iconFile: nil)
|
||||||
|
// controller.getNavigationController = { [weak self] in
|
||||||
|
// return self?.effectiveNavigationController
|
||||||
|
// }
|
||||||
|
// controller.navigationPresentation = .modal
|
||||||
|
// strongSelf.push(controller)
|
||||||
|
}, error: { [weak self] error in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||||
|
})]), in: .window(.root))
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if peer.flags.contains(.isVerified) {
|
||||||
|
openWebView()
|
||||||
} else {
|
} else {
|
||||||
strongSelf.messageActionCallbackDisposable.set(((strongSelf.context.engine.messages.requestWebView(peerId: peerId, botId: peerId, url: url, payload: nil, themeParams: generateWebAppThemeParams(strongSelf.presentationData.theme), replyToMessageId: nil)
|
let _ = (ApplicationSpecificNotice.getBotGameNotice(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peer.id)
|
||||||
|> afterDisposed {
|
|> deliverOnMainQueue).start(next: { value in
|
||||||
updateProgress()
|
|
||||||
})
|
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let controller = WebAppController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peerId, botId: peerId, botName: botName, url: result.url, queryId: result.queryId, buttonText: buttonText, keepAliveSignal: result.keepAliveSignal, replyToMessageId: nil, iconFile: nil)
|
|
||||||
controller.getNavigationController = { [weak self] in
|
if value {
|
||||||
return self?.effectiveNavigationController
|
openWebView()
|
||||||
|
} else {
|
||||||
|
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: strongSelf.presentationData.strings.WebApp_OpenWebViewAlertTitle, text: strongSelf.presentationData.strings.WebApp_OpenWebViewAlertText(botName).string, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: { }), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||||
|
if let strongSelf = self {
|
||||||
|
let _ = ApplicationSpecificNotice.setBotGameNotice(accountManager: strongSelf.context.sharedContext.accountManager, peerId: peer.id).start()
|
||||||
|
openWebView()
|
||||||
|
}
|
||||||
|
})], parseMarkdown: true), in: .window(.root), with: nil)
|
||||||
}
|
}
|
||||||
controller.navigationPresentation = .modal
|
})
|
||||||
strongSelf.push(controller)
|
|
||||||
}, error: { [weak self] error in
|
|
||||||
if let strongSelf = self {
|
|
||||||
strongSelf.present(textAlertController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
|
||||||
})]), in: .window(.root))
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}, requestMessageUpdate: { [weak self] id in
|
}, requestMessageUpdate: { [weak self] id in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
@ -10878,6 +10906,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
completion(controller, nil)
|
completion(controller, nil)
|
||||||
strongSelf.controllerNavigationDisposable.set(nil)
|
strongSelf.controllerNavigationDisposable.set(nil)
|
||||||
|
default:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let present = {
|
let present = {
|
||||||
|
@ -79,6 +79,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController
|
|||||||
var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
var cancelPanGesture: () -> Void = { }
|
var cancelPanGesture: () -> Void = { }
|
||||||
|
var isContainerPanning: () -> Bool = { return false }
|
||||||
|
|
||||||
init(_ params: ContactSelectionControllerParams) {
|
init(_ params: ContactSelectionControllerParams) {
|
||||||
self.context = params.context
|
self.context = params.context
|
||||||
|
@ -567,9 +567,8 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
let controller = addWebAppToAttachmentController(context: context, peerName: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), peerIcon: icon, completion: {
|
let controller = addWebAppToAttachmentController(context: context, peerName: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), peerIcon: icon, completion: {
|
||||||
let _ = context.engine.messages.addBotToAttachMenu(botId: peerId).start()
|
let _ = (context.engine.messages.addBotToAttachMenu(botId: peerId)
|
||||||
|
|> deliverOnMainQueue).start(completed: {
|
||||||
Queue.mainQueue().after(1.0, {
|
|
||||||
if let navigationController = navigationController, case let .chat(chatPeerId, _) = urlContext {
|
if let navigationController = navigationController, case let .chat(chatPeerId, _) = urlContext {
|
||||||
let _ = context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: chatPeerId), attachBotStart: ChatControllerInitialAttachBotStart(botId: peer.id, payload: payload), useExisting: true))
|
let _ = context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: chatPeerId), attachBotStart: ChatControllerInitialAttachBotStart(botId: peer.id, payload: payload), useExisting: true))
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ public enum ParsedInternalUrl {
|
|||||||
case share(url: String?, text: String?, to: String?)
|
case share(url: String?, text: String?, to: String?)
|
||||||
case wallpaper(WallpaperUrlParameter)
|
case wallpaper(WallpaperUrlParameter)
|
||||||
case theme(String)
|
case theme(String)
|
||||||
case phone(String)
|
case phone(String, String?, String?)
|
||||||
case startAttach(String, String?)
|
case startAttach(String, String?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,7 +223,21 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
|||||||
} else if pathComponents[0].hasPrefix("+") || pathComponents[0].hasPrefix("%20") {
|
} else if pathComponents[0].hasPrefix("+") || pathComponents[0].hasPrefix("%20") {
|
||||||
let component = pathComponents[0].replacingOccurrences(of: "%20", with: "+")
|
let component = pathComponents[0].replacingOccurrences(of: "%20", with: "+")
|
||||||
if component.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789+").inverted) == nil {
|
if component.rangeOfCharacter(from: CharacterSet(charactersIn: "0123456789+").inverted) == nil {
|
||||||
return .phone(component.replacingOccurrences(of: "+", with: ""))
|
var attach: String?
|
||||||
|
var startAttach: String?
|
||||||
|
if let queryItems = components.queryItems {
|
||||||
|
for queryItem in queryItems {
|
||||||
|
if let value = queryItem.value {
|
||||||
|
if queryItem.name == "attach" {
|
||||||
|
attach = value
|
||||||
|
} else if queryItem.name == "startattach" {
|
||||||
|
startAttach = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return .phone(component.replacingOccurrences(of: "+", with: ""), attach, startAttach)
|
||||||
} else {
|
} else {
|
||||||
return .join(String(component.dropFirst()))
|
return .join(String(component.dropFirst()))
|
||||||
}
|
}
|
||||||
@ -422,14 +436,26 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? {
|
|||||||
|
|
||||||
private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) -> Signal<ResolvedUrl?, NoError> {
|
private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) -> Signal<ResolvedUrl?, NoError> {
|
||||||
switch url {
|
switch url {
|
||||||
case let .phone(phone):
|
case let .phone(phone, attach, startAttach):
|
||||||
return context.engine.peers.resolvePeerByPhone(phone: phone)
|
return context.engine.peers.resolvePeerByPhone(phone: phone)
|
||||||
|> take(1)
|
|> take(1)
|
||||||
|> map { peer -> ResolvedUrl? in
|
|> mapToSignal { peer -> Signal<ResolvedUrl?, NoError> in
|
||||||
if let peer = peer?._asPeer() {
|
if let peer = peer?._asPeer() {
|
||||||
return .peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil))
|
if let attach = attach {
|
||||||
|
return context.engine.peers.resolvePeerByName(name: attach)
|
||||||
|
|> take(1)
|
||||||
|
|> map { botPeer -> ResolvedUrl? in
|
||||||
|
if let botPeer = botPeer?._asPeer() {
|
||||||
|
return .peer(peer.id, .withAttachBot(ChatControllerInitialAttachBotStart(botId: botPeer.id, payload: startAttach)))
|
||||||
|
} else {
|
||||||
|
return .peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .single(.peer(peer.id, .chat(textInputState: nil, subject: nil, peekData: nil)))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return .peer(nil, .info)
|
return .single(.peer(nil, .info))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .peerName(name, parameter):
|
case let .peerName(name, parameter):
|
||||||
|
@ -113,11 +113,12 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||||
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in }
|
||||||
public var cancelPanGesture: () -> Void = { }
|
public var cancelPanGesture: () -> Void = { }
|
||||||
|
public var isContainerPanning: () -> Bool = { return false }
|
||||||
|
|
||||||
private class Node: ViewControllerTracingNode, WKNavigationDelegate, UIScrollViewDelegate {
|
private class Node: ViewControllerTracingNode, WKNavigationDelegate, UIScrollViewDelegate {
|
||||||
private weak var controller: WebAppController?
|
private weak var controller: WebAppController?
|
||||||
|
|
||||||
fileprivate var webView: WKWebView?
|
fileprivate var webView: WebAppWebView?
|
||||||
|
|
||||||
private var placeholderIcon: UIImage?
|
private var placeholderIcon: UIImage?
|
||||||
private var placeholderNode: ShimmerEffectNode?
|
private var placeholderNode: ShimmerEffectNode?
|
||||||
@ -183,7 +184,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
configuration.mediaPlaybackRequiresUserAction = false
|
configuration.mediaPlaybackRequiresUserAction = false
|
||||||
}
|
}
|
||||||
|
|
||||||
let webView = WKWebView(frame: CGRect(), configuration: configuration)
|
let webView = WebAppWebView(frame: CGRect(), configuration: configuration)
|
||||||
webView.alpha = 0.0
|
webView.alpha = 0.0
|
||||||
webView.isOpaque = false
|
webView.isOpaque = false
|
||||||
webView.backgroundColor = .clear
|
webView.backgroundColor = .clear
|
||||||
@ -200,6 +201,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
}
|
}
|
||||||
webView.allowsBackForwardNavigationGestures = false
|
webView.allowsBackForwardNavigationGestures = false
|
||||||
webView.scrollView.delegate = self
|
webView.scrollView.delegate = self
|
||||||
|
webView.scrollView.contentInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 1.0, right: 0.0)
|
||||||
webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: [], context: nil)
|
webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: [], context: nil)
|
||||||
webView.tintColor = self.presentationData.theme.rootController.tabBar.iconColor
|
webView.tintColor = self.presentationData.theme.rootController.tabBar.iconColor
|
||||||
self.webView = webView
|
self.webView = webView
|
||||||
@ -334,40 +336,15 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
self.controller?.navigationBar?.updateBackgroundAlpha(min(30.0, contentOffset) / 30.0, transition: .immediate)
|
self.controller?.navigationBar?.updateBackgroundAlpha(min(30.0, contentOffset) / 30.0, transition: .immediate)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var animationProgress: CGFloat = 0.0
|
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||||
if let webView = self.webView {
|
let previous = self.validLayout?.0
|
||||||
let frame = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: navigationBarHeight), size: CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right, height: max(1.0, layout.size.height - navigationBarHeight - layout.intrinsicInsets.bottom - layout.additionalInsets.bottom)))
|
self.validLayout = (layout, navigationBarHeight)
|
||||||
if case let .animated(duration, curve) = transition, let springAnimation = transition.animation(), webView.frame != frame {
|
|
||||||
let initial = webView.frame
|
|
||||||
|
|
||||||
let animation = POPBasicAnimation()
|
if let webView = self.webView, let controller = self.controller {
|
||||||
animation.property = (POPAnimatableProperty.property(withName: "frame", initializer: { property in
|
let frame = CGRect(origin: CGPoint(x: layout.safeInsets.left, y: navigationBarHeight), size: CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right, height: max(1.0, layout.size.height - navigationBarHeight - layout.intrinsicInsets.bottom - layout.additionalInsets.bottom)))
|
||||||
property?.readBlock = { node, values in
|
|
||||||
values?.pointee = (node as! Node).animationProgress
|
webView.updateFrame(frame: frame, panning: controller.isContainerPanning(), transition: transition)
|
||||||
}
|
|
||||||
property?.writeBlock = { node, values in
|
|
||||||
(node as! Node).animationProgress = values!.pointee
|
|
||||||
var mappedValue = values!.pointee
|
|
||||||
switch curve {
|
|
||||||
case .spring:
|
|
||||||
mappedValue = springAnimationValueAt(springAnimation, mappedValue)
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
let currentFrame = CGRect.interpolator()(initial, frame, mappedValue) as! CGRect
|
|
||||||
(node as! Node).webView?.frame = currentFrame
|
|
||||||
}
|
|
||||||
property?.threshold = 0.01
|
|
||||||
}) as! POPAnimatableProperty)
|
|
||||||
animation.fromValue = 0.0 as NSNumber
|
|
||||||
animation.toValue = 1.0 as NSNumber
|
|
||||||
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
|
||||||
animation.duration = duration * Double(springAnimation.speed)
|
|
||||||
self.pop_add(animation, forKey: "frame")
|
|
||||||
} else {
|
|
||||||
webView.frame = frame
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let placeholderNode = self.placeholderNode {
|
if let placeholderNode = self.placeholderNode {
|
||||||
@ -387,6 +364,10 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
|||||||
let loadingProgressHeight: CGFloat = 2.0
|
let loadingProgressHeight: CGFloat = 2.0
|
||||||
transition.updateFrame(node: self.loadingProgressNode, frame: CGRect(origin: CGPoint(x: 0.0, y: height - loadingProgressHeight), size: CGSize(width: layout.size.width, height: loadingProgressHeight)))
|
transition.updateFrame(node: self.loadingProgressNode, frame: CGRect(origin: CGPoint(x: 0.0, y: height - loadingProgressHeight), size: CGSize(width: layout.size.width, height: loadingProgressHeight)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let previous = previous, (previous.inputHeight ?? 0.0).isZero, let inputHeight = layout.inputHeight, inputHeight > 44.0 {
|
||||||
|
self.controller?.requestAttachmentMenuExpansion()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||||
@ -671,3 +652,11 @@ private final class WebAppContextReferenceContentSource: ContextReferenceContent
|
|||||||
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
|
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func standaloneWebAppController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, botId: PeerId, botName: String, url: String, queryId: Int64?, buttonText: String?, keepAliveSignal: Signal<Never, KeepWebViewError>?) -> ViewController {
|
||||||
|
let controller = AttachmentController(context: context, updatedPresentationData: updatedPresentationData, chatLocation: .peer(id: peerId), buttons: [.standalone], initialButton: .standalone)
|
||||||
|
controller.requestController = { _, completion in
|
||||||
|
completion(WebAppController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, botId: botId, botName: botName, url: url, queryId: queryId, buttonText: buttonText, keepAliveSignal: keepAliveSignal, replyToMessageId: nil, iconFile: nil), nil)
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
148
submodules/WebUI/Sources/WebAppWebView.swift
Normal file
148
submodules/WebUI/Sources/WebAppWebView.swift
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import Display
|
||||||
|
import WebKit
|
||||||
|
import SwiftSignalKit
|
||||||
|
|
||||||
|
private let findFixedPositionClasses = """
|
||||||
|
function findFixedPositionClasses() {
|
||||||
|
var elems = document.body.getElementsByTagName("*");
|
||||||
|
var len = elems.length
|
||||||
|
|
||||||
|
var result = []
|
||||||
|
var j = 0;
|
||||||
|
for (var i = 0; i < len; i++) {
|
||||||
|
if ((window.getComputedStyle(elems[i],null).getPropertyValue('position') == 'fixed') && (window.getComputedStyle(elems[i],null).getPropertyValue('bottom') == '0px')) {
|
||||||
|
result[j] = elems[i].className;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
findFixedPositionClasses();
|
||||||
|
"""
|
||||||
|
|
||||||
|
private func findFixedPositionViews(webView: WKWebView, classes: [String]) -> [(String, UIView)] {
|
||||||
|
if let contentView = webView.scrollView.subviews.first {
|
||||||
|
func recursiveSearch(_ view: UIView) -> [(String, UIView)] {
|
||||||
|
var result: [(String, UIView)] = []
|
||||||
|
|
||||||
|
let description = view.description
|
||||||
|
if description.contains("class='") {
|
||||||
|
for className in classes {
|
||||||
|
if description.contains(className) {
|
||||||
|
result.append((className, view))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for subview in view.subviews {
|
||||||
|
result.append(contentsOf: recursiveSearch(subview))
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
return recursiveSearch(contentView)
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class WebAppWebView: WKWebView {
|
||||||
|
private var fixedPositionClasses: [String] = []
|
||||||
|
private var currentFixedViews: [(String, UIView, UIView)] = []
|
||||||
|
|
||||||
|
private var timer: SwiftSignalKit.Timer?
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
self.timer?.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func didMoveToSuperview() {
|
||||||
|
super.didMoveToSuperview()
|
||||||
|
|
||||||
|
if self.timer == nil {
|
||||||
|
let timer = SwiftSignalKit.Timer(timeout: 1.0, repeat: true, completion: { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSelf.evaluateJavaScript(findFixedPositionClasses, completionHandler: { [weak self] result, _ in
|
||||||
|
if let result = result {
|
||||||
|
Queue.mainQueue().async {
|
||||||
|
self?.fixedPositionClasses = (result as? [String]) ?? []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, queue: Queue.mainQueue())
|
||||||
|
timer.start()
|
||||||
|
self.timer = timer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func updateFrame(frame: CGRect, panning: Bool, transition: ContainedViewLayoutTransition) {
|
||||||
|
let reset = { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (_, view, snapshotView) in strongSelf.currentFixedViews {
|
||||||
|
view.isHidden = false
|
||||||
|
snapshotView.removeFromSuperview()
|
||||||
|
}
|
||||||
|
strongSelf.currentFixedViews = []
|
||||||
|
}
|
||||||
|
|
||||||
|
let update = { [weak self] in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (_, view, snapshotView) in strongSelf.currentFixedViews {
|
||||||
|
view.isHidden = true
|
||||||
|
|
||||||
|
var snapshotFrame = view.frame
|
||||||
|
snapshotFrame.origin.y = frame.height - snapshotFrame.height
|
||||||
|
transition.updateFrame(view: snapshotView, frame: snapshotFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if panning {
|
||||||
|
let fixedPositionViews = findFixedPositionViews(webView: self, classes: self.fixedPositionClasses)
|
||||||
|
if fixedPositionViews.count != self.currentFixedViews.count {
|
||||||
|
var existing: [String: (UIView, UIView)] = [:]
|
||||||
|
for (className, originalView, snapshotView) in self.currentFixedViews {
|
||||||
|
existing[className] = (originalView, snapshotView)
|
||||||
|
}
|
||||||
|
|
||||||
|
var updatedFixedViews: [(String, UIView, UIView)] = []
|
||||||
|
for (className, view) in fixedPositionViews {
|
||||||
|
if let (_, existingSnapshotView) = existing[className] {
|
||||||
|
updatedFixedViews.append((className, view, existingSnapshotView))
|
||||||
|
existing[className] = nil
|
||||||
|
} else if let snapshotView = view.snapshotView(afterScreenUpdates: false) {
|
||||||
|
updatedFixedViews.append((className, view, snapshotView))
|
||||||
|
self.addSubview(snapshotView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (_, originalAndSnapshotView) in existing {
|
||||||
|
originalAndSnapshotView.0.isHidden = false
|
||||||
|
originalAndSnapshotView.1.removeFromSuperview()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.currentFixedViews = updatedFixedViews
|
||||||
|
}
|
||||||
|
transition.updateFrame(view: self, frame: frame)
|
||||||
|
update()
|
||||||
|
} else {
|
||||||
|
update()
|
||||||
|
transition.updateFrame(view: self, frame: frame, completion: { _ in
|
||||||
|
reset()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user