From 0659e69918eedd43fd258ca40b3cf67bc5a035ff Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 8 Apr 2022 15:36:59 +0400 Subject: [PATCH 1/4] Web app improvements --- .../WebUI/Sources/WebAppController.swift | 14 ++++++++- submodules/WebUI/Sources/WebAppWebView.swift | 31 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index b1fc7dab20..e8e14d9434 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -262,7 +262,7 @@ public final class WebAppController: ViewController, AttachmentContainable { } func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) { - Queue.mainQueue().after(0.65, { + Queue.mainQueue().after(1.0, { let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .linear) transition.updateAlpha(layer: webView.layer, alpha: 1.0) if let placeholderNode = self.placeholderNode { @@ -283,9 +283,14 @@ public final class WebAppController: ViewController, AttachmentContainable { decisionHandler(.prompt) } + private var targetContentOffset: CGPoint? func scrollViewDidScroll(_ scrollView: UIScrollView) { let contentOffset = scrollView.contentOffset.y self.controller?.navigationBar?.updateBackgroundAlpha(min(30.0, contentOffset) / 30.0, transition: .immediate) + + if let targetContentOffset = self.targetContentOffset, scrollView.contentOffset != targetContentOffset { + scrollView.contentOffset = targetContentOffset + } } private var validLayout: (ContainerViewLayout, CGFloat)? @@ -298,8 +303,15 @@ public final class WebAppController: ViewController, AttachmentContainable { let viewportFrame = 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))) if previousLayout != nil && (previousLayout?.inputHeight ?? 0.0).isZero, let inputHeight = layout.inputHeight, inputHeight > 44.0, transition.isAnimated { + webView.scrollToActiveElement(layout: layout, transition: transition) Queue.mainQueue().after(0.4, { + let contentOffset = webView.scrollView.contentOffset transition.updateFrame(view: webView, frame: frame) + webView.scrollView.contentOffset = contentOffset + self.targetContentOffset = contentOffset + Queue.mainQueue().after(0.1) { + self.targetContentOffset = nil + } }) } else { transition.updateFrame(view: webView, frame: frame) diff --git a/submodules/WebUI/Sources/WebAppWebView.swift b/submodules/WebUI/Sources/WebAppWebView.swift index 51192c310a..57c4e7772c 100644 --- a/submodules/WebUI/Sources/WebAppWebView.swift +++ b/submodules/WebUI/Sources/WebAppWebView.swift @@ -4,6 +4,10 @@ import Display import WebKit import SwiftSignalKit +private let findActiveElementY = """ +document.activeElement.getBoundingClientRect().y +""" + private class WeakGameScriptMessageHandler: NSObject, WKScriptMessageHandler { private let f: (WKScriptMessage) -> () @@ -104,6 +108,10 @@ final class WebAppWebView: WKWebView { } contentView?.removeInteraction(dragInteraction) }) + + NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil) + NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil) + NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil) } } @@ -122,6 +130,29 @@ final class WebAppWebView: WKWebView { self.didTouchOnce = true } + func scrollToActiveElement(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + self.evaluateJavaScript(findActiveElementY, completionHandler: { result, _ in + if let result = result as? CGFloat { + Queue.mainQueue().async { + let convertedY = result - self.scrollView.contentOffset.y + let viewportHeight = self.frame.height - (layout.inputHeight ?? 0.0) + if convertedY < 0.0 || convertedY > viewportHeight { + let targetOffset: CGFloat + if convertedY < 0.0 { + targetOffset = max(0.0, result - 36.0) + } else { + targetOffset = max(0.0, result + 60.0 - viewportHeight) + } + transition.animateView({ + self.scrollView.contentOffset = CGPoint(x: 0.0, y: targetOffset) + }) +// transition.updateBounds(layer: self.scrollView.layer, bounds: CGRect(x: 0.0, y: targetOffset, width: self.scrollView.layer.bounds.width, height: self.scrollView.layer.bounds.height)) + } + } + } + }) + } + override var inputAccessoryView: UIView? { return nil } From dc9677fafd2f104e6734f46d4aa9a54d7e8c57f7 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 8 Apr 2022 16:08:33 +0400 Subject: [PATCH 2/4] Web app improvements --- submodules/WebUI/Sources/WebAppController.swift | 15 ++++++++------- submodules/WebUI/Sources/WebAppWebView.swift | 13 ++++++++----- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index e8e14d9434..63896e6ac6 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -303,14 +303,15 @@ public final class WebAppController: ViewController, AttachmentContainable { let viewportFrame = 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))) if previousLayout != nil && (previousLayout?.inputHeight ?? 0.0).isZero, let inputHeight = layout.inputHeight, inputHeight > 44.0, transition.isAnimated { - webView.scrollToActiveElement(layout: layout, transition: transition) + webView.scrollToActiveElement(layout: layout, completion: { [weak self] contentOffset in + self?.targetContentOffset = contentOffset + }, transition: transition) Queue.mainQueue().after(0.4, { - let contentOffset = webView.scrollView.contentOffset - transition.updateFrame(view: webView, frame: frame) - webView.scrollView.contentOffset = contentOffset - self.targetContentOffset = contentOffset - Queue.mainQueue().after(0.1) { - self.targetContentOffset = nil + if let inputHeight = self.validLayout?.0.inputHeight, inputHeight > 44.0 { + transition.updateFrame(view: webView, frame: frame) + Queue.mainQueue().after(0.1) { + self.targetContentOffset = nil + } } }) } else { diff --git a/submodules/WebUI/Sources/WebAppWebView.swift b/submodules/WebUI/Sources/WebAppWebView.swift index 57c4e7772c..9d18b7d23d 100644 --- a/submodules/WebUI/Sources/WebAppWebView.swift +++ b/submodules/WebUI/Sources/WebAppWebView.swift @@ -68,6 +68,8 @@ final class WebAppWebView: WKWebView { super.init(frame: CGRect(), configuration: configuration) + self.disablesInteractiveKeyboardGestureRecognizer = true + self.isOpaque = false self.backgroundColor = .clear if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { @@ -130,23 +132,24 @@ final class WebAppWebView: WKWebView { self.didTouchOnce = true } - func scrollToActiveElement(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + func scrollToActiveElement(layout: ContainerViewLayout, completion: @escaping (CGPoint) -> Void, transition: ContainedViewLayoutTransition) { self.evaluateJavaScript(findActiveElementY, completionHandler: { result, _ in if let result = result as? CGFloat { Queue.mainQueue().async { let convertedY = result - self.scrollView.contentOffset.y - let viewportHeight = self.frame.height - (layout.inputHeight ?? 0.0) - if convertedY < 0.0 || convertedY > viewportHeight { + let viewportHeight = self.frame.height - (layout.inputHeight ?? 0.0) + 26.0 + if convertedY < 0.0 || (convertedY + 44.0) > viewportHeight { let targetOffset: CGFloat if convertedY < 0.0 { targetOffset = max(0.0, result - 36.0) } else { targetOffset = max(0.0, result + 60.0 - viewportHeight) } + let contentOffset = CGPoint(x: 0.0, y: targetOffset) + completion(contentOffset) transition.animateView({ - self.scrollView.contentOffset = CGPoint(x: 0.0, y: targetOffset) + self.scrollView.contentOffset = contentOffset }) -// transition.updateBounds(layer: self.scrollView.layer, bounds: CGRect(x: 0.0, y: targetOffset, width: self.scrollView.layer.bounds.width, height: self.scrollView.layer.bounds.height)) } } } From 4a544882f2d5490df53c70c4797afb62d345522a Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 8 Apr 2022 16:37:40 +0400 Subject: [PATCH 3/4] Web app improvements --- submodules/WebUI/Sources/WebAppWebView.swift | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/submodules/WebUI/Sources/WebAppWebView.swift b/submodules/WebUI/Sources/WebAppWebView.swift index 9d18b7d23d..51998d4b00 100644 --- a/submodules/WebUI/Sources/WebAppWebView.swift +++ b/submodules/WebUI/Sources/WebAppWebView.swift @@ -5,7 +5,14 @@ import WebKit import SwiftSignalKit private let findActiveElementY = """ -document.activeElement.getBoundingClientRect().y +function getOffset(el) { + const rect = el.getBoundingClientRect(); + return { + left: rect.left + window.scrollX, + top: rect.top + window.scrollY + }; +} +getOffset(document.activeElement).top; """ private class WeakGameScriptMessageHandler: NSObject, WKScriptMessageHandler { @@ -88,10 +95,6 @@ final class WebAppWebView: WKWebView { strongSelf.handleScriptMessage(message) } } - -// let tapGestureRecognizer = WebViewTouchGestureRecognizer(target: self, action: #selector(self.handleTap)) -// tapGestureRecognizer.delegate = self -// self.addGestureRecognizer(tapGestureRecognizer) } required init?(coder: NSCoder) { From 9ff797d9a42b6bb169ae647ab8d64d08c15dc70f Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 8 Apr 2022 17:35:50 +0400 Subject: [PATCH 4/4] Web app improvements --- .../Sources/PhotoResources.swift | 19 +++- .../Resources/durgerking.placeholder | Bin 0 -> 2672 bytes .../WebUI/Sources/WebAppController.swift | 102 ++++++++++++------ 3 files changed, 84 insertions(+), 37 deletions(-) create mode 100644 submodules/TelegramUI/Resources/durgerking.placeholder diff --git a/submodules/PhotoResources/Sources/PhotoResources.swift b/submodules/PhotoResources/Sources/PhotoResources.swift index b4438bcc84..4da31297b2 100644 --- a/submodules/PhotoResources/Sources/PhotoResources.swift +++ b/submodules/PhotoResources/Sources/PhotoResources.swift @@ -2283,9 +2283,20 @@ public func instantPageImageFile(account: Account, fileReference: FileMediaRefer } } -public func svgIconImageFile(account: Account, fileReference: FileMediaReference, stickToTop: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { - let data = account.postbox.mediaBox.cachedResourceRepresentation(fileReference.media.resource, representation: CachedPreparedSvgRepresentation(), complete: false, fetch: true) - +public func svgIconImageFile(account: Account, fileReference: FileMediaReference?, stickToTop: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { + let data: Signal + if let fileReference = fileReference { + data = account.postbox.mediaBox.cachedResourceRepresentation(fileReference.media.resource, representation: CachedPreparedSvgRepresentation(), complete: false, fetch: true) + } else { + data = Signal { subscriber in + if let url = getAppBundle().url(forResource: "durgerking", withExtension: "placeholder"), let data = try? Data(contentsOf: url, options: .mappedRead) { + subscriber.putNext(MediaResourceData(path: url.path, offset: 0, size: data.count, complete: true)) + subscriber.putCompletion() + } + return EmptyDisposable + } + } + return data |> map { value in let fullSizePath = value.path @@ -2301,8 +2312,6 @@ public func svgIconImageFile(account: Account, fileReference: FileMediaReference if fullSizeComplete, let data = try? Data(contentsOf: URL(fileURLWithPath: fullSizePath)) { fullSizeImage = renderPreparedImage(data, CGSize.zero, .clear, UIScreenScale) - -// fullSizeImage = drawSvgImage(data, stickToTop ? CGSize.zero : CGSize(width: 90.0, height: 90.0), .clear, .black, false) if let image = fullSizeImage { fittedSize = image.size.aspectFitted(arguments.boundingSize) } diff --git a/submodules/TelegramUI/Resources/durgerking.placeholder b/submodules/TelegramUI/Resources/durgerking.placeholder new file mode 100644 index 0000000000000000000000000000000000000000..473f118134dc228df648d730668f6a47d6ba0ec3 GIT binary patch literal 2672 zcmYk7F{@m45XJv7aW_~5B7*4c(gYSQtZZ}Ne=CcC#oE}~2q9e3SXk_;pCC3OTN%{C zG>^x2Y4Q;)Y*M5VlxHJX`~b=Moioe6YUa+|IcLt^!|YerTKoEq)~>y}extSXv9)Pj ze75=YjAXO<#j&+N9T#6b`=<)?*`v1~>+M<3=i?&DP7f6RsQ6BIPv85O;OVpi(xm`Qd9N+Nwxw>XXG4*IO@xbR@~yYYBXMYYb9Gj|oVn8PiZfTjz?o{q zUliuE`*%KcZ0(~Mu(i)q*nL~^hcsS1{IQ(5R|Ba1(V6>lCJo5dh$kpaBa=4Z&QMz1 z*a(1&X!2=fn@qADP^(_fT=gr?l!&-x)#Dys$4+X*UlgVub(8WCC^4fx59-vYl{!0S zlAVE)h+yx_nKW>b8u1r}r6+B`K*@>5#*uIlO}_LtnPfYltS-qFXG%oe!neacdfj$X zBmQFkQ0g$C2}od6^ae!#;0N!RN$qSXe><@E$ICui=SlrxpsnToa(4Lj3?`RwlPpVi<*{(5xgvnu>v@vm~`zkkP>yUT*Q zEN80Vo9Tw^q!?$qAZKba1~e2-1e*vK(d5&}Hg=}B1L{dRQ_0DhdWbWX*gcB26Ak;z zg{em!&J2MPGb(xm#jMoXF_Wm1GZ7K&%Q(|wMxovFmmL=1MKVSiDO0izB>nt%jGA{!9BlANg>Gbwe3^0$*SB_dqNmWxnl ze_5TJX>}OTP$Xaz;Ub!R>uoa0c0gHOl9Myre7u=+oT)+%of^5B6y(gu|2ekZ|K-ek zH`cc5^IM7c-dp?M{K3cXuj}7j4Q}SJKK`4p>)%`r*@;Fj-=M-YGHC+_N=`JkqPY}9 zE@x_+OtKwN^YLcRSDcBu+|0RdCa-ho)X3$dAZJ?Lq+iMEl!UR@270!-sk37y*%>H_ z$juzq^>3~Q_f3slKHgk<(gqBaoM?55=28r~oT+Ux$#y_lU6PYC5s{n8x8ttqbssu4 za`~8-Gp$Yr41p3eDtZH+t?u9*GpU^o Void private var queryId: Int64? + private var placeholderDisposable: Disposable? private var iconDisposable: Disposable? private var keepAliveDisposable: Disposable? @@ -121,45 +124,79 @@ public final class WebAppController: ViewController, AttachmentContainable { let placeholderNode = ShimmerEffectNode() self.addSubnode(placeholderNode) self.placeholderNode = placeholderNode - - let _ = (self.context.engine.messages.getAttachMenuBot(botId: controller.botId, cached: true) - |> deliverOnMainQueue).start(next: { [weak self] bot in + + let placeholder: Signal<(FileMediaReference, Bool)?, NoError> + if durgerKingBotIds.contains(controller.botId.id._internalGetInt64Value()) { + placeholder = .single(nil) + |> delay(0.05, queue: Queue.mainQueue()) + } else { + placeholder = self.context.engine.messages.getAttachMenuBot(botId: controller.botId, cached: true) + |> map(Optional.init) + |> `catch` { error -> Signal in + return .complete() + } + |> mapToSignal { bot -> Signal<(FileMediaReference, Bool)?, NoError> in + if let bot = bot, let peerReference = PeerReference(bot.peer) { + var imageFile: TelegramMediaFile? + var isPlaceholder = false + if let file = bot.icons[.placeholder] { + imageFile = file + isPlaceholder = true + } else if let file = bot.icons[.iOSStatic] { + imageFile = file + } else if let file = bot.icons[.default] { + imageFile = file + } + if let imageFile = imageFile { + return .single((.attachBot(peer: peerReference, media: imageFile), isPlaceholder)) + } else { + return .complete() + } + } else { + return .complete() + } + } + } + + self.placeholderDisposable = (placeholder + |> deliverOnMainQueue).start(next: { [weak self] fileReferenceAndIsPlaceholder in guard let strongSelf = self else { return } - var imageFile: TelegramMediaFile? - var isPlaceholder = false - if let file = bot.icons[.placeholder] { - imageFile = file + let fileReference: FileMediaReference? + let isPlaceholder: Bool + if let (maybeFileReference, maybeIsPlaceholder) = fileReferenceAndIsPlaceholder { + fileReference = maybeFileReference + isPlaceholder = maybeIsPlaceholder + } else { + fileReference = nil isPlaceholder = true - } else if let file = bot.icons[.iOSStatic] { - imageFile = file - } else if let file = bot.icons[.default] { - imageFile = file } - if let imageFile = imageFile, let peer = PeerReference(bot.peer) { - let _ = freeMediaFileInteractiveFetched(account: strongSelf.context.account, fileReference: .attachBot(peer: peer, media: imageFile)).start() - strongSelf.iconDisposable = (svgIconImageFile(account: strongSelf.context.account, fileReference: .attachBot(peer: peer, media: imageFile), stickToTop: isPlaceholder) - |> deliverOnMainQueue).start(next: { [weak self] transform in - if let strongSelf = self { - let imageSize: CGSize - if isPlaceholder, let (layout, _) = strongSelf.validLayout { - let minSize = min(layout.size.width, layout.size.height) - imageSize = CGSize(width: minSize, height: minSize * 3.0) - } else { - imageSize = CGSize(width: 75.0, height: 75.0) - } - let arguments = TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()) - let drawingContext = transform(arguments) - if let image = drawingContext?.generateImage()?.withRenderingMode(.alwaysTemplate) { - strongSelf.placeholderIcon = (image, isPlaceholder) - if let (layout, navigationBarHeight) = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) - } + + if let fileReference = fileReference { + let _ = freeMediaFileInteractiveFetched(account: strongSelf.context.account, fileReference: fileReference).start() + } + strongSelf.iconDisposable = (svgIconImageFile(account: strongSelf.context.account, fileReference: fileReference, stickToTop: isPlaceholder) + |> deliverOnMainQueue).start(next: { [weak self] transform in + if let strongSelf = self { + let imageSize: CGSize + if isPlaceholder, let (layout, _) = strongSelf.validLayout { + let minSize = min(layout.size.width, layout.size.height) + imageSize = CGSize(width: minSize, height: minSize * 2.0) + } else { + imageSize = CGSize(width: 75.0, height: 75.0) + } + let arguments = TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()) + let drawingContext = transform(arguments) + if let image = drawingContext?.generateImage()?.withRenderingMode(.alwaysTemplate) { + strongSelf.placeholderIcon = (image, isPlaceholder) + if let (layout, navigationBarHeight) = strongSelf.validLayout { + strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) } } - }) - } + strongSelf.placeholderNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + }) }) if let url = controller.url, !controller.fromMenu { @@ -207,6 +244,7 @@ public final class WebAppController: ViewController, AttachmentContainable { } deinit { + self.placeholderDisposable?.dispose() self.iconDisposable?.dispose() self.keepAliveDisposable?.dispose()