diff --git a/submodules/Display/Source/ContainedViewLayoutTransition.swift b/submodules/Display/Source/ContainedViewLayoutTransition.swift index b3f5189cbd..6313fb29c3 100644 --- a/submodules/Display/Source/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Source/ContainedViewLayoutTransition.swift @@ -1051,14 +1051,15 @@ public extension ContainedViewLayoutTransition { #if os(iOS) public extension ContainedViewLayoutTransition { - func animateView(_ f: @escaping () -> Void) { + func animateView(_ f: @escaping () -> Void, completion: ((Bool) -> Void)? = nil) { switch self { case .immediate: f() + completion?(true) case let .animated(duration, curve): UIView.animate(withDuration: duration, delay: 0.0, options: curve.viewAnimationOptions, animations: { f() - }, completion: nil) + }, completion: completion) } } } diff --git a/submodules/Display/Source/Navigation/NavigationController.swift b/submodules/Display/Source/Navigation/NavigationController.swift index c7316c1035..fcbaee731f 100644 --- a/submodules/Display/Source/Navigation/NavigationController.swift +++ b/submodules/Display/Source/Navigation/NavigationController.swift @@ -500,6 +500,8 @@ open class NavigationController: UINavigationController, ContainableController, var notifyGlobalOverlayControllersUpdated = false + var additionalSideInsets = UIEdgeInsets() + var modalStyleOverlayTransitionFactor: CGFloat = 0.0 var previousGlobalOverlayContainer: NavigationOverlayContainer? for i in (0 ..< self.globalOverlayContainers.count).reversed() { @@ -527,6 +529,9 @@ open class NavigationController: UINavigationController, ContainableController, notifyGlobalOverlayControllersUpdated = true } + let controllerAdditionalSideInsets = overlayContainer.controller.additionalSideInsets + additionalSideInsets = UIEdgeInsets(top: 0.0, left: max(additionalSideInsets.left, controllerAdditionalSideInsets.left), bottom: 0.0, right: max(additionalSideInsets.right, controllerAdditionalSideInsets.right)) + if overlayContainer.supernode != nil { previousGlobalOverlayContainer = overlayContainer let controllerStatusBarStyle = overlayContainer.controller.statusBar.statusBarStyle @@ -546,7 +551,6 @@ open class NavigationController: UINavigationController, ContainableController, } } - var additionalSideInsets = UIEdgeInsets() var previousOverlayContainer: NavigationOverlayContainer? for i in (0 ..< self.overlayContainers.count).reversed() { let overlayContainer = self.overlayContainers[i] diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatActionButton.swift b/submodules/TelegramCallsUI/Sources/VoiceChatActionButton.swift index 795aebe214..fe32961abd 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatActionButton.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatActionButton.swift @@ -47,15 +47,13 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode { var pressing: Bool = false { didSet { - var pressing = self.pressing var snap = false - if let (_, _, _, _, _, _, _, snapValue) = self.currentParams, snapValue { - pressing = false - snap = true + if let (_, _, _, _, _, _, _, snapValue) = self.currentParams { + snap = snapValue } - if pressing { + if self.pressing { let transition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .spring) - transition.updateTransformScale(node: self.iconNode, scale: 0.9) + transition.updateTransformScale(node: self.iconNode, scale: snap ? 0.5 : 0.9) } else { let transition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .spring) transition.updateTransformScale(node: self.iconNode, scale: snap ? 0.5 : 1.0) @@ -80,18 +78,15 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode { self.containerNode.addSubnode(self.backgroundNode) self.containerNode.addSubnode(self.iconNode) - self.highligthedChanged = { [weak self] highlighted in + self.highligthedChanged = { [weak self] pressing in if let strongSelf = self { - var highlighted = highlighted var snap = false - if let (_, _, _, _, _, _, _, snapValue) = strongSelf.currentParams, snapValue { - highlighted = false - snap = true + if let (_, _, _, _, _, _, _, snapValue) = strongSelf.currentParams { + snap = snapValue } - - if highlighted { + if pressing { let transition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .spring) - transition.updateTransformScale(node: strongSelf.iconNode, scale: 0.9) + transition.updateTransformScale(node: strongSelf.iconNode, scale: snap ? 0.5 : 0.9) } else if !strongSelf.pressing { let transition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .spring) transition.updateTransformScale(node: strongSelf.iconNode, scale: snap ? 0.5 : 1.0) @@ -173,7 +168,7 @@ final class VoiceChatActionButton: HighlightTrackingButtonNode { let transition: ContainedViewLayoutTransition = animated ? .animated(duration: 0.2, curve: .easeInOut) : .immediate if snap { - transition.updateTransformScale(node: self.backgroundNode, scale: active ? 0.75 : 0.5) + transition.updateTransformScale(node: self.backgroundNode, scale: self.pressing ? 0.75 : 0.5) transition.updateTransformScale(node: self.iconNode, scale: 0.5) transition.updateAlpha(node: self.titleLabel, alpha: 0.0) transition.updateAlpha(node: self.subtitleLabel, alpha: 0.0) @@ -909,8 +904,8 @@ private final class VoiceBlobView: UIView { pointsCount: 8, minRandomness: 1, maxRandomness: 1, - minSpeed: 0.85, - maxSpeed: 7, + minSpeed: 0.9, + maxSpeed: 4.0, minScale: mediumBlobRange.min, maxScale: mediumBlobRange.max ) @@ -918,8 +913,8 @@ private final class VoiceBlobView: UIView { pointsCount: 8, minRandomness: 1, maxRandomness: 1, - minSpeed: 0.85, - maxSpeed: 7, + minSpeed: 1.0, + maxSpeed: 4.4, minScale: bigBlobRange.min, maxScale: bigBlobRange.max ) diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index 90cc296dde..42e3aa23b9 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -460,6 +460,7 @@ public final class VoiceChatController: ViewController { self.dimNode.backgroundColor = dimColor self.contentContainer = ASDisplayNode() + self.contentContainer.isHidden = true self.backgroundNode = ASDisplayNode() self.backgroundNode.backgroundColor = secondaryPanelBackgroundColor @@ -1596,7 +1597,7 @@ public final class VoiceChatController: ViewController { } func animateIn() { - guard let (layout, _) = self.validLayout else { + guard let (layout, navigationHeight) = self.validLayout else { return } let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring) @@ -1605,13 +1606,23 @@ public final class VoiceChatController: ViewController { let initialBounds = self.contentContainer.bounds self.contentContainer.bounds = initialBounds.offsetBy(dx: 0.0, dy: -(layout.size.height - topPanelFrame.minY)) - transition.animateView { + self.contentContainer.isHidden = false + transition.animateView({ self.contentContainer.view.bounds = initialBounds - } + }, completion: { _ in + self.bottomPanelNode.addSubnode(self.actionButton) + self.containerLayoutUpdated(layout, navigationHeight:navigationHeight, transition: .immediate) + + self.controller?.currentOverlayController?.dismiss() + self.controller?.currentOverlayController = nil + }) self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) } func animateOut(completion: (() -> Void)?) { + guard let (layout, _) = self.validLayout else { + return + } var offsetCompleted = false let internalCompletion: () -> Void = { [weak self] in if offsetCompleted { @@ -1627,7 +1638,9 @@ public final class VoiceChatController: ViewController { } } - self.contentContainer.layer.animateBoundsOriginYAdditive(from: self.contentContainer.bounds.origin.y, to: -self.contentContainer.bounds.size.height, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in + let topPanelFrame = self.topPanelNode.view.convert(self.topPanelNode.bounds, to: self.view) + + self.contentContainer.layer.animateBoundsOriginYAdditive(from: self.contentContainer.bounds.origin.y, to: -(layout.size.height - topPanelFrame.minY), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in offsetCompleted = true internalCompletion() }) @@ -1857,6 +1870,7 @@ public final class VoiceChatController: ViewController { private var didAppearOnce: Bool = false private var isDismissed: Bool = false + private var isDisconnected: Bool = false private var controllerNode: Node { return self.displayNode as! Node @@ -1864,7 +1878,7 @@ public final class VoiceChatController: ViewController { private let idleTimerExtensionDisposable = MetaDisposable() - private var currentOverlayController: VoiceChatOverlayController? + private weak var currentOverlayController: VoiceChatOverlayController? private var validLayout: ContainerViewLayout? @@ -1937,39 +1951,41 @@ public final class VoiceChatController: ViewController { self.idleTimerExtensionDisposable.set(nil) DispatchQueue.main.async { + self.didAppearOnce = false + self.isDismissed = true + self.detachActionButton() self.onViewDidDisappear?() } } public func dismiss(closing: Bool) { - if closing { - self.dismiss() + if !closing { + self.detachActionButton() } else { - let overlayController = VoiceChatOverlayController(actionButton: self.controllerNode.actionButton) - if let navigationController = self.navigationController as? NavigationController { - navigationController.presentOverlay(controller: overlayController, inGlobal: true, blockInteraction: false) + self.isDisconnected = true + } + + self.dismiss() + } + + private func detachActionButton() { + guard self.currentOverlayController == nil && !self.isDisconnected else { + return + } + + let overlayController = VoiceChatOverlayController(actionButton: self.controllerNode.actionButton) + if let navigationController = self.navigationController as? NavigationController { + navigationController.presentOverlay(controller: overlayController, inGlobal: true, blockInteraction: false) + } + + self.currentOverlayController = overlayController + + self.reclaimActionButton = { [weak self, weak overlayController] in + if let strongSelf = self { + let actionButton = strongSelf.controllerNode.actionButton + overlayController?.animateOut(reclaim: true, completion: {}) + strongSelf.reclaimActionButton = nil } - self.sharedContext.presentGlobalController(overlayController, nil) - - self.currentOverlayController = overlayController - - self.reclaimActionButton = { [weak self, weak overlayController] in - if let strongSelf = self { - let actionButton = strongSelf.controllerNode.actionButton - overlayController?.animateOut(reclaim: true, completion: { [weak self] in - if let strongSelf = self { - strongSelf.controllerNode.bottomPanelNode.addSubnode(actionButton) - if let validLayout = strongSelf.validLayout { - strongSelf.containerLayoutUpdated(validLayout, transition: .immediate) - } - } - }) - strongSelf.currentOverlayController = nil - strongSelf.reclaimActionButton = nil - } - } - - self.dismiss() } } diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatOverlayController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatOverlayController.swift index 6288859203..a4a18f93bd 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatOverlayController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatOverlayController.swift @@ -15,7 +15,7 @@ import AppBundle import ContextUI import PresentationDataUtils -final class VoiceChatOverlayController: ViewController { +public final class VoiceChatOverlayController: ViewController { private final class Node: ViewControllerTracingNode, UIGestureRecognizerDelegate { private weak var controller: VoiceChatOverlayController? @@ -24,6 +24,41 @@ final class VoiceChatOverlayController: ViewController { init(controller: VoiceChatOverlayController) { self.controller = controller } + + private var isButtonHidden = false + func update(hidden: Bool, slide: Bool, animated: Bool) { + guard let actionButton = self.controller?.actionButton, actionButton.supernode === self else { + return + } + + if self.isButtonHidden == hidden { + return + } + self.isButtonHidden = hidden + + if animated { + if hidden { + if slide { + + } else { + actionButton.layer.removeAllAnimations() + actionButton.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, completion: { [weak actionButton] _ in + actionButton?.isHidden = true + }) + } + } else { + if slide { + + } else { + actionButton.layer.removeAllAnimations() + actionButton.isHidden = false + actionButton.layer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.4) + } + } + } else { + + } + } func animateIn(from: CGRect) { guard let actionButton = self.controller?.actionButton else { @@ -53,7 +88,7 @@ final class VoiceChatOverlayController: ViewController { keyframes.append(NSValue(cgPoint: CGPoint(x: x, y: y))) } - actionButton.layer.animateKeyframes(values: keyframes, duration: 0.3, keyPath: "position", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, completion: { [weak self] _ in + actionButton.layer.animateKeyframes(values: keyframes, duration: 0.2, keyPath: "position", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, completion: { _ in }) } @@ -88,9 +123,8 @@ final class VoiceChatOverlayController: ViewController { actionButton.update(snap: false) actionButton.position = targetPosition - actionButton.layer.animateKeyframes(values: keyframes, duration: 0.4, keyPath: "position", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, completion: { [weak self] _ in + actionButton.layer.animateKeyframes(values: keyframes, duration: 0.4, keyPath: "position", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, completion: { _ in completion() - self?.controller?.dismiss() }) } else { actionButton.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, completion: { [weak self, weak actionButton] _ in @@ -101,11 +135,14 @@ final class VoiceChatOverlayController: ViewController { } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - if let actionButton = self.controller?.actionButton, actionButton.supernode === self, actionButton.frame.contains(point) { - return actionButton.hitTest(self.view.convert(point, to: actionButton.view), with: event) - } else { - return nil + if let actionButton = self.controller?.actionButton, actionButton.supernode === self { + let actionButtonSize = CGSize(width: 84.0, height: 84.0) + let actionButtonFrame = CGRect(origin: CGPoint(x: actionButton.position.x - actionButtonSize.width / 2.0, y: actionButton.position.y - actionButtonSize.height / 2.0), size: actionButtonSize) + if actionButtonFrame.contains(point) { + return actionButton.hitTest(self.view.convert(point, to: actionButton.view), with: event) + } } + return nil } func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { @@ -141,6 +178,10 @@ final class VoiceChatOverlayController: ViewController { self.additionalSideInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: 75.0) } + deinit { + print("") + } + required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -150,7 +191,7 @@ final class VoiceChatOverlayController: ViewController { self.displayNodeDidLoad() } - override func dismiss(completion: (() -> Void)? = nil) { + public override func dismiss(completion: (() -> Void)? = nil) { super.dismiss(completion: completion) self.presentingViewController?.dismiss(animated: false, completion: nil) completion?() @@ -160,6 +201,10 @@ final class VoiceChatOverlayController: ViewController { self.controllerNode.animateOut(reclaim: reclaim, completion: completion) } + public func update(hidden: Bool, slide: Bool, animated: Bool) { + self.controllerNode.update(hidden: hidden, slide: slide, animated: animated) + } + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { super.containerLayoutUpdated(layout, transition: transition) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index f74aac4b43..1e59e3bef1 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -666,8 +666,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }, sendMessagesWithSignals: { [weak self] signals, _, _ in if let strongSelf = self { - strongSelf.interfaceInteraction?.setupEditMessage(messageId, { _ in }) - strongSelf.editMessageMediaWithLegacySignals(signals!) + if canEditMessage(context: strongSelf.context, limitsConfiguration: strongSelf.context.currentLimitsConfiguration.with { $0 }, message: message) { + strongSelf.interfaceInteraction?.setupEditMessage(messageId, { _ in }) + strongSelf.editMessageMediaWithLegacySignals(signals!) + } else { + strongSelf.enqueueMediaMessages(signals: signals, silentPosting: false) + } } }, present: { [weak self] c, a in self?.present(c, in: .window(.root), with: a) @@ -7052,6 +7056,24 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if saveInterfaceState { self.saveInterfaceState(includeScrollState: false) } + + if let navigationController = self.navigationController as? NavigationController { + var voiceChatOverlayController: VoiceChatOverlayController? + for controller in navigationController.globalOverlayControllers { + if let controller = controller as? VoiceChatOverlayController { + voiceChatOverlayController = controller + break + } + } + + if let controller = voiceChatOverlayController { + if self.presentationInterfaceState.inputMode == .text && self.presentationInterfaceState.interfaceState.composeInputState.inputText.string.count > 0 { + controller.update(hidden: true, slide: false, animated: true) + } else { + controller.update(hidden: false, slide: false, animated: true) + } + } + } } private func updateItemNodesSelectionStates(animated: Bool) { diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index e4368cc84f..7135453d4a 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -634,7 +634,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { |> deliverOnMainQueue).start(next: { [weak self] call in if let strongSelf = self { if call !== strongSelf.groupCallController?.call { - strongSelf.groupCallController?.dismiss() + strongSelf.groupCallController?.dismiss(closing: true) strongSelf.groupCallController = nil strongSelf.hasOngoingCall.set(false)