diff --git a/submodules/AttachmentUI/Sources/AttachmentContainer.swift b/submodules/AttachmentUI/Sources/AttachmentContainer.swift index 3b7faef988..6aa2fdec49 100644 --- a/submodules/AttachmentUI/Sources/AttachmentContainer.swift +++ b/submodules/AttachmentUI/Sources/AttachmentContainer.swift @@ -64,6 +64,8 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate { private var panGestureRecognizer: UIPanGestureRecognizer? var isPanningUpdated: (Bool) -> Void = { _ in } + var isExpandedUpdated: (Bool) -> Void = { _ in } + var onExpandAnimationCompleted: () -> Void = {} override init() { self.wrappingNode = ASDisplayNode() @@ -150,6 +152,11 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate { return self.panGestureArguments != nil } + private var isAnimating = false + var isPanning: Bool { + return self.panGestureArguments != nil || self.isAnimating + } + private var panGestureArguments: (topInset: CGFloat, offset: CGFloat, scrollView: UIScrollView?, listNode: ListView?)? @objc func panGesture(_ recognizer: UIPanGestureRecognizer) { guard let (layout, controllers, coveredByModalTransition) = self.validLayout else { @@ -159,6 +166,14 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate { let isLandscape = layout.orientation == .landscape let edgeTopInset = isLandscape ? 0.0 : attachmentDefaultTopInset(layout: layout) + let completion = { + self.isAnimating = false + guard self.panGestureArguments == nil else { + return + } + self.isPanningUpdated(false) + } + switch recognizer.state { case .began: let point = recognizer.location(in: self.view) @@ -261,13 +276,6 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate { let offset = currentTopInset + panOffset let topInset: CGFloat = edgeTopInset - - let completion = { - guard self.panGestureArguments == nil else { - return - } - self.isPanningUpdated(false) - } var dismissing = false if bounds.minY < -60 || (bounds.minY < 0.0 && velocity.y > 300.0) || (self.isExpanded && bounds.minY.isZero && velocity.y > 1800.0) { @@ -285,15 +293,14 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate { let distance = topInset - offset let initialVelocity: CGFloat = distance.isZero ? 0.0 : abs(velocity.y / distance) let transition = ContainedViewLayoutTransition.animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity)) - self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: transition) - Queue.mainQueue().after(0.5, completion) + self.isAnimating = true + self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: transition, completion: completion) } else { self.isExpanded = true - self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: .animated(duration: 0.3, curve: .easeInOut)) - - Queue.mainQueue().after(0.35, completion) + self.isAnimating = true + self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: .animated(duration: 0.3, curve: .easeInOut), completion: completion) } } else if (velocity.y < -300.0 || offset < topInset / 2.0) { if velocity.y > -2200.0 && velocity.y < -300.0, let listNode = listNode { @@ -302,13 +309,11 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate { } } - let initialVelocity: CGFloat = offset.isZero ? 0.0 : abs(velocity.y / offset) - let transition = ContainedViewLayoutTransition.animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity)) self.isExpanded = true - self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: transition) - - Queue.mainQueue().after(0.5, completion) + let initialVelocity: CGFloat = offset.isZero ? 0.0 : abs(velocity.y / offset) + self.isAnimating = true + self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: .animated(duration: 0.45, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity)), completion: completion) } else { if let listNode = listNode { listNode.scroller.setContentOffset(CGPoint(), animated: false) @@ -319,9 +324,8 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate { }) } - self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: .animated(duration: 0.3, curve: .easeInOut)) - - Queue.mainQueue().after(0.35, completion) + self.isAnimating = true + self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: .animated(duration: 0.3, curve: .easeInOut), completion: completion) } if !dismissing { @@ -334,7 +338,8 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate { case .cancelled: self.panGestureArguments = nil - self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: .animated(duration: 0.3, curve: .easeInOut)) + self.isAnimating = true + self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: .animated(duration: 0.3, curve: .easeInOut), completion: completion) default: break } @@ -362,7 +367,7 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate { self.update(layout: layout, controllers: controllers, coveredByModalTransition: coveredByModalTransition, transition: transition) } - func update(layout: ContainerViewLayout, controllers: [AttachmentContainable], coveredByModalTransition: CGFloat, transition: ContainedViewLayoutTransition) { + func update(layout: ContainerViewLayout, controllers: [AttachmentContainable], coveredByModalTransition: CGFloat, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void = {}) { if self.isDismissed { return } @@ -391,7 +396,9 @@ final class AttachmentContainer: ASDisplayNode, UIGestureRecognizerDelegate { } else { topInset = effectiveExpanded ? 0.0 : edgeTopInset } - transition.updateFrame(node: self.wrappingNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: layout.size)) + transition.updateFrame(node: self.wrappingNode, frame: CGRect(origin: CGPoint(x: 0.0, y: topInset), size: layout.size), completion: { _ in + completion() + }) let modalProgress = isLandscape ? 0.0 : (1.0 - topInset / defaultTopInset) self.updateModalProgress?(modalProgress, transition) diff --git a/submodules/AttachmentUI/Sources/AttachmentController.swift b/submodules/AttachmentUI/Sources/AttachmentController.swift index b48dbcd3ab..5864f4c561 100644 --- a/submodules/AttachmentUI/Sources/AttachmentController.swift +++ b/submodules/AttachmentUI/Sources/AttachmentController.swift @@ -75,6 +75,7 @@ public protocol AttachmentContainable: ViewController { var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void { get set } var cancelPanGesture: () -> Void { get set } var isContainerPanning: () -> Bool { get set } + var isContainerExpanded: () -> Bool { get set } func isContainerPanningUpdated(_ panning: Bool) @@ -472,7 +473,15 @@ public class AttachmentController: ViewController { controller.isContainerPanning = { [weak self] in if let strongSelf = self { - return strongSelf.container.isTracking + return strongSelf.container.isPanning + } else { + return false + } + } + + controller.isContainerExpanded = { [weak self] in + if let strongSelf = self { + return strongSelf.container.isExpanded } else { return false } diff --git a/submodules/ComposePollUI/Sources/CreatePollController.swift b/submodules/ComposePollUI/Sources/CreatePollController.swift index b48388fb84..f3fa5ebba8 100644 --- a/submodules/ComposePollUI/Sources/CreatePollController.swift +++ b/submodules/ComposePollUI/Sources/CreatePollController.swift @@ -522,6 +522,7 @@ private class CreatePollControllerImpl: ItemListController, AttachmentContainabl public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in } public var cancelPanGesture: () -> Void = { } public var isContainerPanning: () -> Bool = { return false } + public var isContainerExpanded: () -> Bool = { return false } } public func createPollController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer, isQuiz: Bool? = nil, completion: @escaping (ComposedPoll) -> Void) -> AttachmentContainable { diff --git a/submodules/LegacyUI/Sources/LegacyController.swift b/submodules/LegacyUI/Sources/LegacyController.swift index 135d855187..a822040022 100644 --- a/submodules/LegacyUI/Sources/LegacyController.swift +++ b/submodules/LegacyUI/Sources/LegacyController.swift @@ -410,6 +410,7 @@ open class LegacyController: ViewController, PresentableController, AttachmentCo open var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in } open var cancelPanGesture: () -> Void = { } open var isContainerPanning: () -> Bool = { return false } + open var isContainerExpanded: () -> Bool = { return false } public init(presentation: LegacyControllerPresentation, theme: PresentationTheme? = nil, strings: PresentationStrings? = nil, initialLayout: ContainerViewLayout? = nil) { self.sizeClass.set(SSignal.single(UIUserInterfaceSizeClass.compact.rawValue as NSNumber)) diff --git a/submodules/LocationUI/Sources/LocationPickerController.swift b/submodules/LocationUI/Sources/LocationPickerController.swift index 69acc6363f..912d1962b1 100644 --- a/submodules/LocationUI/Sources/LocationPickerController.swift +++ b/submodules/LocationUI/Sources/LocationPickerController.swift @@ -76,6 +76,7 @@ public final class LocationPickerController: ViewController, AttachmentContainab public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in } public var cancelPanGesture: () -> Void = { } public var isContainerPanning: () -> Bool = { return false } + public var isContainerExpanded: () -> Bool = { return false } public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, mode: LocationPickerMode, completion: @escaping (TelegramMediaMap, String?) -> Void) { self.context = context diff --git a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift index e5905fad80..61ed98cb27 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerScreen.swift @@ -134,6 +134,7 @@ public final class MediaPickerScreen: ViewController, AttachmentContainable { public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in } public var cancelPanGesture: () -> Void = { } public var isContainerPanning: () -> Bool = { return false } + public var isContainerExpanded: () -> Bool = { return false } var dismissAll: () -> Void = { } diff --git a/submodules/TelegramUI/Sources/AttachmentFileController.swift b/submodules/TelegramUI/Sources/AttachmentFileController.swift index d428f8f4f0..ef8dba066a 100644 --- a/submodules/TelegramUI/Sources/AttachmentFileController.swift +++ b/submodules/TelegramUI/Sources/AttachmentFileController.swift @@ -169,6 +169,7 @@ private class AttachmentFileControllerImpl: ItemListController, AttachmentContai public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in } public var cancelPanGesture: () -> Void = { } public var isContainerPanning: () -> Bool = { return false } + public var isContainerExpanded: () -> Bool = { return false } var delayDisappear = false diff --git a/submodules/TelegramUI/Sources/ContactSelectionController.swift b/submodules/TelegramUI/Sources/ContactSelectionController.swift index 069849b9ac..bf62a19908 100644 --- a/submodules/TelegramUI/Sources/ContactSelectionController.swift +++ b/submodules/TelegramUI/Sources/ContactSelectionController.swift @@ -80,6 +80,7 @@ class ContactSelectionControllerImpl: ViewController, ContactSelectionController var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in } var cancelPanGesture: () -> Void = { } var isContainerPanning: () -> Bool = { return false } + var isContainerExpanded: () -> Bool = { return false } init(_ params: ContactSelectionControllerParams) { self.context = params.context diff --git a/submodules/WebUI/Sources/WebAppController.swift b/submodules/WebUI/Sources/WebAppController.swift index 36fd7c33be..0aeff803f0 100644 --- a/submodules/WebUI/Sources/WebAppController.swift +++ b/submodules/WebUI/Sources/WebAppController.swift @@ -75,7 +75,8 @@ public final class WebAppController: ViewController, AttachmentContainable { public var updateTabBarAlpha: (CGFloat, ContainedViewLayoutTransition) -> Void = { _, _ in } public var cancelPanGesture: () -> Void = { } public var isContainerPanning: () -> Bool = { return false } - + public var isContainerExpanded: () -> Bool = { return false } + fileprivate class Node: ViewControllerTracingNode, WKNavigationDelegate, WKUIDelegate, UIScrollViewDelegate { private weak var controller: WebAppController? @@ -348,6 +349,12 @@ public final class WebAppController: ViewController, AttachmentContainable { } } + fileprivate func isContainerPanningUpdated(_ isPanning: Bool) { + if let (layout, navigationBarHeight) = self.validLayout { + self.containerLayoutUpdated(layout, navigationBarHeight: navigationBarHeight, transition: .immediate) + } + } + private var validLayout: (ContainerViewLayout, CGFloat)? func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { let previousLayout = self.validLayout?.0 @@ -373,7 +380,9 @@ public final class WebAppController: ViewController, AttachmentContainable { transition.updateFrame(view: webView, frame: frame) } - webView.updateFrame(frame: viewportFrame, transition: transition) + if let controller = self.controller { + webView.updateMetrics(height: viewportFrame.height, isExpanded: controller.isContainerExpanded(), isStable: !controller.isContainerPanning(), transition: transition) + } } if let placeholderNode = self.placeholderNode { @@ -664,6 +673,10 @@ public final class WebAppController: ViewController, AttachmentContainable { self.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate) self.updateTabBarAlpha(1.0, .immediate) } + + public func isContainerPanningUpdated(_ isPanning: Bool) { + self.controllerNode.isContainerPanningUpdated(isPanning) + } override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { super.containerLayoutUpdated(layout, transition: transition) diff --git a/submodules/WebUI/Sources/WebAppWebView.swift b/submodules/WebUI/Sources/WebAppWebView.swift index 4b699b6c3d..fc2572b409 100644 --- a/submodules/WebUI/Sources/WebAppWebView.swift +++ b/submodules/WebUI/Sources/WebAppWebView.swift @@ -126,8 +126,9 @@ final class WebAppWebView: WKWebView { }) } - func updateFrame(frame: CGRect, transition: ContainedViewLayoutTransition) { - self.sendEvent(name: "viewport_changed", data: "{height:\(frame.height)}") + func updateMetrics(height: CGFloat, isExpanded: Bool, isStable: Bool, transition: ContainedViewLayoutTransition) { + let data = "{height:\(height), is_expanded:\(isExpanded ? "true" : "false"), is_stable_state:\(isStable ? "true" : "false")}" + self.sendEvent(name: "viewport_changed", data: data) } private(set) var didTouchOnce = false