mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Web app improvements
This commit is contained in:
parent
90943b78b6
commit
67d34e1ee9
@ -97,7 +97,8 @@ public class AttachmentController: ViewController {
|
||||
private let context: AccountContext
|
||||
private let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
|
||||
private let chatLocation: ChatLocation
|
||||
private var buttons: [AttachmentButtonType]
|
||||
private let buttons: [AttachmentButtonType]
|
||||
private let initialButton: AttachmentButtonType
|
||||
|
||||
public var mediaPickerContext: AttachmentMediaPickerContext? {
|
||||
get {
|
||||
@ -268,7 +269,20 @@ public class AttachmentController: ViewController {
|
||||
|
||||
self.dim.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
|
||||
|
||||
let _ = self.switchToController(.gallery)
|
||||
if let controller = self.controller {
|
||||
let _ = self.switchToController(controller.initialButton)
|
||||
if case let .app(botId, _, _) = controller.initialButton {
|
||||
if let index = controller.buttons.firstIndex(where: {
|
||||
if case let .app(otherBotId, _, _) = $0, otherBotId == botId {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}) {
|
||||
self.panel.updateSelectedIndex(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateSelectionCount(_ count: Int) {
|
||||
@ -293,25 +307,6 @@ public class AttachmentController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
func switchTo(_ type: AttachmentButtonType) {
|
||||
guard let buttons = self.controller?.buttons else {
|
||||
return
|
||||
}
|
||||
if case let .app(botId, _, _) = type {
|
||||
let index = buttons.firstIndex(where: {
|
||||
if case let .app(otherBotId, _, _) = $0, otherBotId == botId {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
if let index = index {
|
||||
self.panel.updateSelectedIndex(index)
|
||||
let _ = self.switchToController(buttons[index], animated: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func switchToController(_ type: AttachmentButtonType, animated: Bool = true) -> Bool {
|
||||
guard self.currentType != type else {
|
||||
if self.animating {
|
||||
@ -583,13 +578,12 @@ public class AttachmentController: ViewController {
|
||||
completion(nil, nil)
|
||||
}
|
||||
|
||||
private var buttonsDisposable: Disposable?
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, chatLocation: ChatLocation, buttons: Signal<[AttachmentButtonType], NoError>) {
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, chatLocation: ChatLocation, buttons: [AttachmentButtonType], initialButton: AttachmentButtonType = .gallery) {
|
||||
self.context = context
|
||||
self.updatedPresentationData = updatedPresentationData
|
||||
self.chatLocation = chatLocation
|
||||
self.buttons = []
|
||||
self.buttons = buttons
|
||||
self.initialButton = initialButton
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
@ -602,21 +596,6 @@ public class AttachmentController: ViewController {
|
||||
strongSelf.node.scrollToTop()
|
||||
}
|
||||
}
|
||||
|
||||
self.buttonsDisposable = (buttons
|
||||
|> deliverOnMainQueue).start(next: { [weak self] buttons in
|
||||
if let strongSelf = self {
|
||||
let previousButtons = strongSelf.buttons
|
||||
strongSelf.buttons = buttons
|
||||
if let layout = strongSelf.validLayout {
|
||||
strongSelf.containerLayoutUpdated(layout, transition: !previousButtons.isEmpty ? .animated(duration: 0.2, curve: .easeInOut) : .immediate)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.buttonsDisposable?.dispose()
|
||||
}
|
||||
|
||||
public required init(coder aDecoder: NSCoder) {
|
||||
@ -632,10 +611,6 @@ public class AttachmentController: ViewController {
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
public func switchTo(_ type: AttachmentButtonType) {
|
||||
(self.displayNode as! Node).switchTo(type)
|
||||
}
|
||||
|
||||
public func _dismiss() {
|
||||
super.dismiss(animated: false, completion: {})
|
||||
}
|
||||
|
@ -10497,6 +10497,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
return
|
||||
}
|
||||
|
||||
let context = self.context
|
||||
|
||||
let inputIsActive = self.presentationInterfaceState.inputMode == .text
|
||||
|
||||
self.chatDisplayNode.dismissInput()
|
||||
@ -10535,49 +10537,74 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
isScheduledMessages = true
|
||||
}
|
||||
|
||||
var switchToBotImpl: ((AttachmentButtonType) -> Void)?
|
||||
var switchToBotId = botId
|
||||
let buttons: Signal<[AttachmentButtonType], NoError>
|
||||
let buttons: Signal<([AttachmentButtonType], AttachmentButtonType?), NoError>
|
||||
if let _ = peer as? TelegramUser, !isScheduledMessages {
|
||||
buttons = .single(availableButtons)
|
||||
|> then(
|
||||
self.context.engine.messages.attachMenuBots()
|
||||
buttons = self.context.engine.messages.attachMenuBots()
|
||||
|> map { attachMenuBots in
|
||||
var buttons = availableButtons
|
||||
var initialButton: AttachmentButtonType?
|
||||
if botId == nil {
|
||||
initialButton = .gallery
|
||||
}
|
||||
for bot in attachMenuBots.reversed() {
|
||||
let peerTitle = EnginePeer(bot.peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)
|
||||
buttons.insert(.app(bot.peer.id, peerTitle, bot.icon), at: 1)
|
||||
let button: AttachmentButtonType = .app(bot.peer.id, peerTitle, bot.icon)
|
||||
buttons.insert(button, at: 1)
|
||||
|
||||
if initialButton == nil && bot.peer.id == botId {
|
||||
initialButton = button
|
||||
}
|
||||
return buttons
|
||||
}
|
||||
) |> afterNext { buttons in
|
||||
if let botId = switchToBotId, let button = buttons.first(where: {
|
||||
if case let .app(otherBotId, _,_) = $0, botId == otherBotId {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}) {
|
||||
Queue.mainQueue().justDispatch {
|
||||
switchToBotImpl?(button)
|
||||
}
|
||||
switchToBotId = nil
|
||||
}
|
||||
return (buttons, initialButton)
|
||||
}
|
||||
} else {
|
||||
buttons = .single(availableButtons)
|
||||
buttons = .single((availableButtons, .gallery))
|
||||
}
|
||||
|
||||
let inputText = self.presentationInterfaceState.interfaceState.effectiveInputState.inputText
|
||||
let _ = (buttons
|
||||
|> deliverOnMainQueue).start(next: { [weak self] buttons, initialButton in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let initialButton = initialButton else {
|
||||
if let botId = botId {
|
||||
let _ = (strongSelf.context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: botId)
|
||||
)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let strongSelf = self, let peer = peer else {
|
||||
return
|
||||
}
|
||||
let _ = (strongSelf.context.engine.messages.requestWebView(peerId: peer.id, botId: botId, url: nil, themeParams: nil, replyToMessageId: nil)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
if let strongSelf = self, case let .requestConfirmation(botIcon) = result {
|
||||
if case let .user(user) = peer, let botInfo = user.botInfo, botInfo.flags.contains(.canBeAddedToAttachMenu) {
|
||||
let controller = addWebAppToAttachmentController(context: context, peerName: peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), peerIcon: botIcon, completion: {
|
||||
let _ = context.engine.messages.addBotToAttachMenu(peerId: botId).start()
|
||||
|
||||
Queue.mainQueue().after(1.0, {
|
||||
strongSelf.presentAttachmentBot(botId: botId)
|
||||
})
|
||||
})
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
} else {
|
||||
strongSelf.present(textAlertController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
let inputText = strongSelf.presentationInterfaceState.interfaceState.effectiveInputState.inputText
|
||||
|
||||
let currentMediaController = Atomic<MediaPickerScreen?>(value: nil)
|
||||
let currentFilesController = Atomic<AttachmentContainable?>(value: nil)
|
||||
let currentLocationController = Atomic<AttachmentContainable?>(value: nil)
|
||||
|
||||
let attachmentController = AttachmentController(context: self.context, updatedPresentationData: self.updatedPresentationData, chatLocation: self.chatLocation, buttons: buttons)
|
||||
switchToBotImpl = { [weak attachmentController] button in
|
||||
attachmentController?.switchTo(button)
|
||||
}
|
||||
let attachmentController = AttachmentController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, chatLocation: strongSelf.chatLocation, buttons: buttons, initialButton: initialButton)
|
||||
attachmentController.requestController = { [weak self, weak attachmentController] type, completion in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -10845,8 +10872,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
let present = {
|
||||
self.present(attachmentController, in: .window(.root))
|
||||
self.attachmentController = attachmentController
|
||||
strongSelf.present(attachmentController, in: .window(.root))
|
||||
strongSelf.attachmentController = attachmentController
|
||||
}
|
||||
|
||||
if inputIsActive {
|
||||
@ -10856,6 +10883,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
} else {
|
||||
present()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private func oldPresentAttachmentMenu(editMediaOptions: MessageMediaEditingOptions?, editMediaReference: AnyMediaReference?) {
|
||||
|
@ -1269,7 +1269,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
}
|
||||
badgeContent = .text(inset: 0.0, backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, text: string)
|
||||
}
|
||||
var animated: Bool = animated
|
||||
var animated = animated
|
||||
if let updatingMedia = attributes.updatingMedia, case .update = updatingMedia.media {
|
||||
state = .progress(color: messageTheme.mediaOverlayControlColors.foregroundColor, lineWidth: nil, value: CGFloat(updatingMedia.progress), cancelEnabled: true, animateRotation: true)
|
||||
} else if var fetchStatus = self.fetchStatus {
|
||||
@ -1497,6 +1497,14 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
self.statusNode = nil
|
||||
removeStatusNode = true
|
||||
}
|
||||
|
||||
var animated = animated
|
||||
if case .download = statusNode.state, case .progress = state {
|
||||
animated = true
|
||||
} else if case .progress = statusNode.state, case .download = state {
|
||||
animated = true
|
||||
}
|
||||
|
||||
statusNode.transitionToState(state, animated: animated, completion: { [weak statusNode] in
|
||||
if removeStatusNode {
|
||||
statusNode?.removeFromSupernode()
|
||||
|
@ -554,7 +554,11 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
let _ = (context.engine.messages.attachMenuBots()
|
||||
|> deliverOnMainQueue).start(next: { attachMenuBots in
|
||||
if let _ = attachMenuBots.firstIndex(where: { $0.peer.id == peerId }) {
|
||||
if let navigationController = navigationController, case let .chat(chatPeerId, _) = urlContext {
|
||||
let _ = context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(id: chatPeerId), attachBotId: peerId, useExisting: true))
|
||||
} else {
|
||||
presentError(presentationData.strings.WebApp_AddToAttachmentAlreadyAddedError)
|
||||
}
|
||||
} else {
|
||||
let _ = (context.engine.data.get(
|
||||
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)
|
||||
@ -581,6 +585,8 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur
|
||||
presentError(presentationData.strings.Login_UnknownError)
|
||||
}
|
||||
}
|
||||
}, error: { _ in
|
||||
presentError(presentationData.strings.Login_UnknownError)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -710,7 +710,7 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur
|
||||
if let path = parsedUrl.pathComponents.last {
|
||||
var section: ResolvedUrlSettingsSection?
|
||||
switch path {
|
||||
case "theme":
|
||||
case "themes":
|
||||
section = .theme
|
||||
case "devices":
|
||||
section = .devices
|
||||
|
@ -45,6 +45,68 @@ public func generateWebAppThemeParams(_ presentationTheme: PresentationTheme) ->
|
||||
]
|
||||
}
|
||||
|
||||
private final class LoadingProgressNode: ASDisplayNode {
|
||||
var color: UIColor {
|
||||
didSet {
|
||||
self.foregroundNode.backgroundColor = self.color
|
||||
}
|
||||
}
|
||||
|
||||
private let foregroundNode: ASDisplayNode
|
||||
|
||||
init(color: UIColor) {
|
||||
self.color = color
|
||||
|
||||
self.foregroundNode = ASDisplayNode()
|
||||
self.foregroundNode.backgroundColor = color
|
||||
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.foregroundNode)
|
||||
}
|
||||
|
||||
private var _progress: CGFloat = 0.0
|
||||
func updateProgress(_ progress: CGFloat, animated: Bool = false) {
|
||||
if self._progress == progress && animated {
|
||||
return
|
||||
}
|
||||
|
||||
var animated = animated
|
||||
if (progress < self._progress && animated) {
|
||||
animated = false
|
||||
}
|
||||
|
||||
let size = self.bounds.size
|
||||
|
||||
self._progress = progress
|
||||
|
||||
let transition: ContainedViewLayoutTransition
|
||||
if animated && progress > 0.0 {
|
||||
transition = .animated(duration: 0.7, curve: .spring)
|
||||
} else {
|
||||
transition = .immediate
|
||||
}
|
||||
|
||||
let alpaTransition: ContainedViewLayoutTransition
|
||||
if animated {
|
||||
alpaTransition = .animated(duration: 0.3, curve: .easeInOut)
|
||||
} else {
|
||||
alpaTransition = .immediate
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.foregroundNode, frame: CGRect(x: -2.0, y: 0.0, width: (size.width + 4.0) * progress, height: size.height))
|
||||
|
||||
let alpha: CGFloat = progress < 0.001 || progress > 0.999 ? 0.0 : 1.0
|
||||
alpaTransition.updateAlpha(node: self.foregroundNode, alpha: alpha)
|
||||
}
|
||||
|
||||
override func layout() {
|
||||
super.layout()
|
||||
|
||||
self.foregroundNode.cornerRadius = self.frame.height / 2.0
|
||||
}
|
||||
}
|
||||
|
||||
public final class WebAppController: ViewController, AttachmentContainable {
|
||||
public var requestAttachmentMenuExpansion: () -> Void = { }
|
||||
public var updateNavigationStack: (@escaping ([AttachmentContainable]) -> ([AttachmentContainable], AttachmentMediaPickerContext?)) -> Void = { _ in }
|
||||
@ -59,6 +121,8 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
private var placeholderIcon: UIImage?
|
||||
private var placeholderNode: ShimmerEffectNode?
|
||||
|
||||
private let loadingProgressNode: LoadingProgressNode
|
||||
|
||||
private let context: AccountContext
|
||||
var presentationData: PresentationData
|
||||
private let present: (ViewController, Any?) -> Void
|
||||
@ -73,6 +137,8 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
self.presentationData = controller.presentationData
|
||||
self.present = present
|
||||
|
||||
self.loadingProgressNode = LoadingProgressNode(color: presentationData.theme.rootController.tabBar.selectedIconColor)
|
||||
|
||||
super.init()
|
||||
|
||||
if self.presentationData.theme.list.plainBackgroundColor.rgb == 0x000000 {
|
||||
@ -133,12 +199,17 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
}
|
||||
webView.allowsBackForwardNavigationGestures = false
|
||||
webView.scrollView.delegate = self
|
||||
webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: [], context: nil)
|
||||
self.webView = webView
|
||||
|
||||
let placeholderNode = ShimmerEffectNode()
|
||||
self.addSubnode(placeholderNode)
|
||||
self.placeholderNode = placeholderNode
|
||||
|
||||
if controller.buttonText == nil {
|
||||
self.addSubnode(self.loadingProgressNode)
|
||||
}
|
||||
|
||||
if let iconFile = controller.iconFile {
|
||||
let _ = freeMediaFileInteractiveFetched(account: self.context.account, fileReference: .standalone(media: iconFile)).start()
|
||||
self.iconDisposable = (svgIconImageFile(account: self.context.account, fileReference: .standalone(media: iconFile))
|
||||
@ -156,12 +227,13 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
})
|
||||
}
|
||||
|
||||
if let url = controller.url, let queryId = controller.queryId, let keepAliveSignal = controller.keepAliveSignal {
|
||||
self.queryId = queryId
|
||||
if let url = controller.url {
|
||||
self.queryId = controller.queryId
|
||||
if let parsedUrl = URL(string: url) {
|
||||
self.webView?.load(URLRequest(url: parsedUrl))
|
||||
}
|
||||
|
||||
if let keepAliveSignal = controller.keepAliveSignal {
|
||||
self.keepAliveDisposable = (keepAliveSignal
|
||||
|> deliverOnMainQueue).start(error: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
@ -172,6 +244,7 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
strongSelf.controller?.dismiss()
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
let _ = (context.engine.messages.requestWebView(peerId: controller.peerId, botId: controller.botId, url: controller.url, themeParams: generateWebAppThemeParams(presentationData.theme), replyToMessageId: controller.replyToMessageId)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] result in
|
||||
@ -205,6 +278,8 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
deinit {
|
||||
self.iconDisposable?.dispose()
|
||||
self.keepAliveDisposable?.dispose()
|
||||
|
||||
self.webView?.removeObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress))
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
@ -272,14 +347,23 @@ public final class WebAppController: ViewController, AttachmentContainable {
|
||||
|
||||
let height: CGFloat
|
||||
if case .compact = layout.metrics.widthClass {
|
||||
height = layout.size.height - attachmentDefaultTopInset(layout: layout) - 56.0
|
||||
height = layout.size.height - attachmentDefaultTopInset(layout: layout) - layout.intrinsicInsets.bottom - 14.0
|
||||
} else {
|
||||
height = layout.size.height - 56.0
|
||||
height = layout.size.height - layout.intrinsicInsets.bottom
|
||||
}
|
||||
|
||||
let placeholderFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - iconSize.width) / 2.0), y: floorToScreenPixels((height - iconSize.height) / 2.0)), size: iconSize)
|
||||
transition.updateFrame(node: placeholderNode, frame: placeholderFrame)
|
||||
placeholderNode.updateAbsoluteRect(placeholderFrame, within: layout.size)
|
||||
|
||||
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)))
|
||||
}
|
||||
}
|
||||
|
||||
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||
if keyPath == "estimatedProgress", let webView = self.webView {
|
||||
self.loadingProgressNode.updateProgress(webView.estimatedProgress, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user