mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Context menu improvements
This commit is contained in:
@@ -6,6 +6,7 @@ import TelegramPresentationData
|
||||
import TextSelectionNode
|
||||
import ReactionSelectionNode
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
|
||||
private let animationDurationFactor: Double = 1.0
|
||||
|
||||
@@ -61,9 +62,17 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
private var theme: PresentationTheme
|
||||
private var strings: PresentationStrings
|
||||
private let source: ContextContentSource
|
||||
private var items: [ContextMenuItem]
|
||||
private var items: Signal<[ContextMenuItem], NoError>
|
||||
private let beginDismiss: (ContextMenuActionResult) -> Void
|
||||
private let reactionSelected: (String) -> Void
|
||||
private let getController: () -> ContextController?
|
||||
private weak var gesture: ContextGesture?
|
||||
|
||||
private var didSetItemsReady = false
|
||||
let itemsReady = Promise<Bool>()
|
||||
let contentReady = Promise<Bool>()
|
||||
|
||||
private var currentItems: [ContextMenuItem]?
|
||||
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
@@ -93,13 +102,20 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
|
||||
private var isAnimatingOut = false
|
||||
|
||||
init(account: Account, controller: ContextController, theme: PresentationTheme, strings: PresentationStrings, source: ContextContentSource, items: [ContextMenuItem], reactionItems: [ReactionContextItem], beginDismiss: @escaping (ContextMenuActionResult) -> Void, recognizer: TapLongTapOrDoubleTapGestureRecognizer?, reactionSelected: @escaping (String) -> Void) {
|
||||
private let itemsDisposable = MetaDisposable()
|
||||
|
||||
init(account: Account, controller: ContextController, theme: PresentationTheme, strings: PresentationStrings, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], beginDismiss: @escaping (ContextMenuActionResult) -> Void, recognizer: TapLongTapOrDoubleTapGestureRecognizer?, gesture: ContextGesture?, reactionSelected: @escaping (String) -> Void) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.source = source
|
||||
self.items = items
|
||||
self.beginDismiss = beginDismiss
|
||||
self.reactionSelected = reactionSelected
|
||||
self.gesture = gesture
|
||||
|
||||
self.getController = { [weak controller] in
|
||||
return controller
|
||||
}
|
||||
|
||||
self.effectView = UIVisualEffectView()
|
||||
if #available(iOS 9.0, *) {
|
||||
@@ -131,9 +147,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
|
||||
self.contentContainerNode = ContextContentContainerNode()
|
||||
|
||||
var getController: (() -> ContextController?)?
|
||||
self.actionsContainerNode = ContextActionsContainerNode(theme: theme, items: items, getController: {
|
||||
return getController?()
|
||||
self.actionsContainerNode = ContextActionsContainerNode(theme: theme, items: [], getController: { [weak controller] in
|
||||
return controller
|
||||
}, actionSelected: { result in
|
||||
beginDismiss(result)
|
||||
})
|
||||
@@ -161,10 +176,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self.scrollNode.addSubnode(self.contentContainerNode)
|
||||
self.reactionContextNode.flatMap(self.addSubnode)
|
||||
|
||||
getController = { [weak controller] in
|
||||
return controller
|
||||
}
|
||||
|
||||
if let recognizer = recognizer {
|
||||
recognizer.externalUpdated = { [weak self, weak recognizer] view, point in
|
||||
guard let strongSelf = self, let _ = recognizer else {
|
||||
@@ -245,6 +256,86 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let gesture = gesture {
|
||||
gesture.externalUpdated = { [weak self, weak gesture] view, point in
|
||||
guard let strongSelf = self, let _ = gesture else {
|
||||
return
|
||||
}
|
||||
let localPoint = strongSelf.view.convert(point, from: view)
|
||||
let initialPoint: CGPoint
|
||||
if let current = strongSelf.initialContinueGesturePoint {
|
||||
initialPoint = current
|
||||
} else {
|
||||
initialPoint = localPoint
|
||||
strongSelf.initialContinueGesturePoint = localPoint
|
||||
}
|
||||
if strongSelf.didCompleteAnimationIn {
|
||||
if !strongSelf.didMoveFromInitialGesturePoint {
|
||||
let distance = abs(localPoint.y - initialPoint.y)
|
||||
if distance > 4.0 {
|
||||
strongSelf.didMoveFromInitialGesturePoint = true
|
||||
}
|
||||
}
|
||||
if strongSelf.didMoveFromInitialGesturePoint {
|
||||
let actionPoint = strongSelf.view.convert(localPoint, to: strongSelf.actionsContainerNode.view)
|
||||
let actionNode = strongSelf.actionsContainerNode.actionNode(at: actionPoint)
|
||||
if strongSelf.highlightedActionNode !== actionNode {
|
||||
strongSelf.highlightedActionNode?.setIsHighlighted(false)
|
||||
strongSelf.highlightedActionNode = actionNode
|
||||
if let actionNode = actionNode {
|
||||
actionNode.setIsHighlighted(true)
|
||||
strongSelf.hapticFeedback.tap()
|
||||
}
|
||||
}
|
||||
|
||||
if let reactionContextNode = strongSelf.reactionContextNode {
|
||||
let highlightedReaction = reactionContextNode.reaction(at: strongSelf.view.convert(localPoint, to: reactionContextNode.view)).flatMap { value -> String? in
|
||||
switch value {
|
||||
case let .reaction(reaction, _, _):
|
||||
return reaction
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if strongSelf.highlightedReaction != highlightedReaction {
|
||||
strongSelf.highlightedReaction = highlightedReaction
|
||||
reactionContextNode.setHighlightedReaction(highlightedReaction)
|
||||
if let _ = highlightedReaction {
|
||||
strongSelf.hapticFeedback.tap()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
gesture.externalEnded = { [weak self, weak recognizer] viewAndPoint in
|
||||
guard let strongSelf = self, let recognizer = recognizer else {
|
||||
return
|
||||
}
|
||||
recognizer.externalUpdated = nil
|
||||
if strongSelf.didMoveFromInitialGesturePoint {
|
||||
if let (_, _) = viewAndPoint {
|
||||
if let highlightedActionNode = strongSelf.highlightedActionNode {
|
||||
strongSelf.highlightedActionNode = nil
|
||||
highlightedActionNode.performAction()
|
||||
}
|
||||
if let _ = strongSelf.reactionContextNode {
|
||||
if let reaction = strongSelf.highlightedReaction {
|
||||
strongSelf.reactionSelected(reaction)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let highlightedActionNode = strongSelf.highlightedActionNode {
|
||||
strongSelf.highlightedActionNode = nil
|
||||
highlightedActionNode.setIsHighlighted(false)
|
||||
}
|
||||
if let reactionContextNode = strongSelf.reactionContextNode, let _ = strongSelf.highlightedReaction {
|
||||
strongSelf.highlightedReaction = nil
|
||||
reactionContextNode.setHighlightedReaction(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let reactionContextNode = self.reactionContextNode {
|
||||
@@ -260,6 +351,20 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.itemsDisposable.set((items
|
||||
|> deliverOnMainQueue).start(next: { [weak self] items in
|
||||
self?.setItems(items: items)
|
||||
}))
|
||||
|
||||
switch source {
|
||||
case .extracted:
|
||||
self.contentReady.set(.single(true))
|
||||
case let .controller(source):
|
||||
self.contentReady.set(source.controller.ready.get())
|
||||
}
|
||||
|
||||
self.initializeContent()
|
||||
}
|
||||
|
||||
deinit {
|
||||
@@ -269,6 +374,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
propertyAnimator?.stopAnimation(true)
|
||||
}
|
||||
}
|
||||
|
||||
self.itemsDisposable.dispose()
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
@@ -281,9 +388,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self.beginDismiss(.default)
|
||||
}
|
||||
|
||||
func animateIn() {
|
||||
self.hapticFeedback.impact()
|
||||
|
||||
private func initializeContent() {
|
||||
switch self.source {
|
||||
case let .extracted(source):
|
||||
let takenViewInfo = source.takeView()
|
||||
@@ -330,13 +435,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
takenViewInfo.contentContainingNode.isExtractedToContextPreviewUpdated?(true)
|
||||
|
||||
self.originalProjectedContentViewFrame = (convertFrame(takenViewInfo.contentContainingNode.frame, from: parentSupernode.view, to: self.view), convertFrame(takenViewInfo.contentContainingNode.contentRect, from: takenViewInfo.contentContainingNode.view, to: self.view))
|
||||
|
||||
var updatedContentAreaInScreenSpace = takenViewInfo.contentAreaInScreenSpace
|
||||
updatedContentAreaInScreenSpace.origin.x = 0.0
|
||||
updatedContentAreaInScreenSpace.size.width = self.bounds.width
|
||||
|
||||
self.clippingNode.layer.animateFrame(from: updatedContentAreaInScreenSpace, to: self.clippingNode.frame, duration: 0.18 * animationDurationFactor, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
self.clippingNode.layer.animateBoundsOriginYAdditive(from: updatedContentAreaInScreenSpace.minY, to: 0.0, duration: 0.18 * animationDurationFactor, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
}
|
||||
case let .controller(source):
|
||||
let contentParentNode = ContextControllerContentNode(controller: source.controller)
|
||||
@@ -345,6 +443,30 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self.contentContainerNode.cornerRadius = 14.0
|
||||
self.contentContainerNode.addSubnode(contentParentNode)
|
||||
|
||||
let transitionInfo = source.transitionInfo()
|
||||
if let transitionInfo = transitionInfo, let (sourceNode, sourceNodeRect) = transitionInfo.sourceNode() {
|
||||
let projectedFrame = convertFrame(sourceNodeRect, from: sourceNode.view, to: self.view)
|
||||
self.originalProjectedContentViewFrame = (projectedFrame, projectedFrame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func animateIn() {
|
||||
self.gesture?.endPressedAppearance()
|
||||
|
||||
self.hapticFeedback.impact()
|
||||
|
||||
switch self.source {
|
||||
case let .extracted(source):
|
||||
if let contentAreaInScreenSpace = contentAreaInScreenSpace {
|
||||
var updatedContentAreaInScreenSpace = contentAreaInScreenSpace
|
||||
updatedContentAreaInScreenSpace.origin.x = 0.0
|
||||
updatedContentAreaInScreenSpace.size.width = self.bounds.width
|
||||
|
||||
self.clippingNode.layer.animateFrame(from: updatedContentAreaInScreenSpace, to: self.clippingNode.frame, duration: 0.18 * animationDurationFactor, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
self.clippingNode.layer.animateBoundsOriginYAdditive(from: updatedContentAreaInScreenSpace.minY, to: 0.0, duration: 0.18 * animationDurationFactor, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
}
|
||||
case let .controller(source):
|
||||
let transitionInfo = source.transitionInfo()
|
||||
if let transitionInfo = transitionInfo, let (sourceNode, sourceNodeRect) = transitionInfo.sourceNode() {
|
||||
let projectedFrame = convertFrame(sourceNodeRect, from: sourceNode.view, to: self.view)
|
||||
@@ -354,9 +476,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
updatedContentAreaInScreenSpace.origin.x = 0.0
|
||||
updatedContentAreaInScreenSpace.size.width = self.bounds.width
|
||||
self.contentAreaInScreenSpace = updatedContentAreaInScreenSpace
|
||||
|
||||
//self.clippingNode.layer.animateFrame(from: updatedContentAreaInScreenSpace, to: self.clippingNode.frame, duration: 0.18 * animationDurationFactor, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
//self.clippingNode.layer.animateBoundsOriginYAdditive(from: updatedContentAreaInScreenSpace.minY, to: 0.0, duration: 0.18 * animationDurationFactor, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -372,14 +491,14 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator
|
||||
propertyAnimator?.stopAnimation(true)
|
||||
}
|
||||
self.propertyAnimator = UIViewPropertyAnimator(duration: 0.2 * animationDurationFactor, curve: .easeInOut, animations: { [weak self] in
|
||||
self.propertyAnimator = UIViewPropertyAnimator(duration: 0.2 * animationDurationFactor * UIView.animationDurationFactor(), curve: .easeInOut, animations: { [weak self] in
|
||||
self?.effectView.effect = makeCustomZoomBlurEffect()
|
||||
})
|
||||
}
|
||||
|
||||
if let _ = self.propertyAnimator {
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.2 * animationDurationFactor, from: 0.0, to: 1.0, update: { [weak self] value in
|
||||
self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.2 * animationDurationFactor * UIView.animationDurationFactor(), from: 0.0, to: 1.0, update: { [weak self] value in
|
||||
(self?.propertyAnimator as? UIViewPropertyAnimator)?.fractionComplete = value
|
||||
}, completion: { [weak self] in
|
||||
self?.didCompleteAnimationIn = true
|
||||
@@ -394,11 +513,12 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
})
|
||||
}
|
||||
|
||||
let springDuration: Double = 0.42 * animationDurationFactor
|
||||
let springDamping: CGFloat = 104.0
|
||||
if let contentNode = self.contentContainerNode.contentNode {
|
||||
switch contentNode {
|
||||
case let .extracted(extracted):
|
||||
let springDuration: Double = 0.42 * animationDurationFactor
|
||||
let springDamping: CGFloat = 104.0
|
||||
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor)
|
||||
self.actionsContainerNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
|
||||
|
||||
@@ -416,6 +536,9 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
contentParentNode.applyAbsoluteOffsetSpring?(-contentContainerOffset.y, springDuration, springDamping)
|
||||
}
|
||||
case .controller:
|
||||
let springDuration: Double = 0.52 * animationDurationFactor
|
||||
let springDamping: CGFloat = 110.0
|
||||
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor)
|
||||
self.actionsContainerNode.layer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
|
||||
self.contentContainerNode.allowsGroupOpacity = true
|
||||
@@ -427,7 +550,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
if let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
|
||||
let actionsSideInset: CGFloat = 11.0
|
||||
|
||||
let localSourceFrame = self.view.convert(CGRect(origin: CGPoint(x: actionsSideInset, y: originalProjectedContentViewFrame.1.minY), size: CGSize(width: actionsSideInset, height: originalProjectedContentViewFrame.1.height)), to: self.scrollNode.view)
|
||||
let localSourceFrame = self.view.convert(CGRect(origin: CGPoint(x: originalProjectedContentViewFrame.1.minX, y: originalProjectedContentViewFrame.1.minY), size: CGSize(width: originalProjectedContentViewFrame.1.width, height: originalProjectedContentViewFrame.1.height)), to: self.scrollNode.view)
|
||||
self.actionsContainerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
let contentContainerOffset = CGPoint(x: localSourceFrame.center.x - self.contentContainerNode.frame.center.x, y: localSourceFrame.center.y - self.contentContainerNode.frame.center.y)
|
||||
self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: contentContainerOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
|
||||
@@ -511,14 +634,14 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator
|
||||
propertyAnimator?.stopAnimation(true)
|
||||
}
|
||||
self.propertyAnimator = UIViewPropertyAnimator(duration: transitionDuration, curve: .easeInOut, animations: { [weak self] in
|
||||
self.propertyAnimator = UIViewPropertyAnimator(duration: transitionDuration * UIView.animationDurationFactor(), curve: .easeInOut, animations: { [weak self] in
|
||||
self?.effectView.effect = nil
|
||||
})
|
||||
}
|
||||
|
||||
if let _ = self.propertyAnimator {
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.2 * animationDurationFactor, from: 0.0, to: 0.999, update: { [weak self] value in
|
||||
self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.2 * animationDurationFactor * UIView.animationDurationFactor(), from: 0.0, to: 0.999, update: { [weak self] value in
|
||||
(self?.propertyAnimator as? UIViewPropertyAnimator)?.fractionComplete = value
|
||||
}, completion: {
|
||||
completedEffect = true
|
||||
@@ -648,14 +771,14 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
let propertyAnimator = propertyAnimator as? UIViewPropertyAnimator
|
||||
propertyAnimator?.stopAnimation(true)
|
||||
}
|
||||
self.propertyAnimator = UIViewPropertyAnimator(duration: transitionDuration, curve: .easeInOut, animations: { [weak self] in
|
||||
self.propertyAnimator = UIViewPropertyAnimator(duration: transitionDuration * UIView.animationDurationFactor(), curve: .easeInOut, animations: { [weak self] in
|
||||
self?.effectView.effect = nil
|
||||
})
|
||||
}
|
||||
|
||||
if let _ = self.propertyAnimator {
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.2 * animationDurationFactor, from: 0.0, to: 0.999, update: { [weak self] value in
|
||||
self.displayLinkAnimator = DisplayLinkAnimator(duration: 0.2 * animationDurationFactor * UIView.animationDurationFactor(), from: 0.0, to: 0.999, update: { [weak self] value in
|
||||
(self?.propertyAnimator as? UIViewPropertyAnimator)?.fractionComplete = value
|
||||
}, completion: {
|
||||
completedEffect = true
|
||||
@@ -697,7 +820,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
if animateOutToItem, let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
|
||||
let actionsSideInset: CGFloat = 11.0
|
||||
|
||||
let localSourceFrame = self.view.convert(CGRect(origin: CGPoint(x: actionsSideInset, y: originalProjectedContentViewFrame.1.minY), size: CGSize(width: actionsSideInset, height: originalProjectedContentViewFrame.1.height)), to: self.scrollNode.view)
|
||||
let localSourceFrame = self.view.convert(CGRect(origin: CGPoint(x: originalProjectedContentViewFrame.1.minX, y: originalProjectedContentViewFrame.1.minY), size: CGSize(width: originalProjectedContentViewFrame.1.width, height: originalProjectedContentViewFrame.1.height)), to: self.scrollNode.view)
|
||||
|
||||
self.actionsContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y), duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false, additive: true)
|
||||
let contentContainerOffset = CGPoint(x: localSourceFrame.center.x - self.contentContainerNode.frame.center.x, y: localSourceFrame.center.y - self.contentContainerNode.frame.center.y)
|
||||
@@ -756,12 +879,23 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
})
|
||||
}
|
||||
|
||||
func setItems(controller: ContextController, items: [ContextMenuItem]) {
|
||||
func setItemsSignal(items: Signal<[ContextMenuItem], NoError>) {
|
||||
self.items = items
|
||||
self.itemsDisposable.set((items
|
||||
|> deliverOnMainQueue).start(next: { [weak self] items in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.setItems(items: items)
|
||||
}))
|
||||
}
|
||||
|
||||
private func setItems(items: [ContextMenuItem]) {
|
||||
self.currentItems = items
|
||||
|
||||
let previousActionsContainerNode = self.actionsContainerNode
|
||||
self.actionsContainerNode = ContextActionsContainerNode(theme: self.theme, items: items, getController: { [weak controller] in
|
||||
return controller
|
||||
self.actionsContainerNode = ContextActionsContainerNode(theme: self.theme, items: items, getController: { [weak self] in
|
||||
return self?.getController()
|
||||
}, actionSelected: { [weak self] result in
|
||||
self?.beginDismiss(result)
|
||||
})
|
||||
@@ -773,6 +907,11 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
} else {
|
||||
previousActionsContainerNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if !self.didSetItemsReady {
|
||||
self.didSetItemsReady = true
|
||||
self.itemsReady.set(.single(true))
|
||||
}
|
||||
}
|
||||
|
||||
func updateTheme(theme: PresentationTheme) {
|
||||
@@ -804,7 +943,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
|
||||
transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
|
||||
let contentActionsSpacing: CGFloat = 7.0
|
||||
let actionsSideInset: CGFloat = 11.0
|
||||
var contentTopInset: CGFloat = max(11.0, layout.statusBarHeight ?? 0.0)
|
||||
if let _ = self.reactionContextNode {
|
||||
@@ -815,6 +953,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
if let contentNode = self.contentContainerNode.contentNode {
|
||||
switch contentNode {
|
||||
case let .extracted(contentParentNode):
|
||||
let contentActionsSpacing: CGFloat = 8.0
|
||||
if let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
|
||||
let isInitialLayout = self.actionsContainerNode.frame.size.width.isZero
|
||||
let previousContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
|
||||
@@ -867,23 +1006,29 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
}
|
||||
case let .controller(contentParentNode):
|
||||
let contentActionsSpacing: CGFloat = actionsSideInset
|
||||
let topEdge = max(contentTopInset, self.contentAreaInScreenSpace?.minY ?? 0.0)
|
||||
|
||||
contentParentNode.updateLayout(size: layout.size, transition: transition)
|
||||
//contentParentNode.updateLayout(size: layout.size, transition: transition)
|
||||
|
||||
let isInitialLayout = self.actionsContainerNode.frame.size.width.isZero
|
||||
let previousContainerFrame = self.view.convert(self.contentContainerNode.frame, from: self.scrollNode.view)
|
||||
|
||||
let actionsSize = self.actionsContainerNode.updateLayout(constrainedWidth: layout.size.width - actionsSideInset * 2.0, transition: actionsContainerTransition)
|
||||
let contentScale = (layout.size.width - actionsSideInset * 2.0) / layout.size.width
|
||||
let proposedContentHeight = layout.size.height - topEdge - contentActionsSpacing - actionsSize.height - layout.intrinsicInsets.bottom - actionsBottomInset
|
||||
let contentUnscaledSize = CGSize(width: layout.size.width, height: max(400.0, proposedContentHeight))
|
||||
let contentUnscaledSize: CGSize
|
||||
if !contentParentNode.controller.preferredContentSize.width.isZero {
|
||||
contentUnscaledSize = contentParentNode.controller.preferredContentSize
|
||||
} else {
|
||||
let proposedContentHeight = layout.size.height - topEdge - contentActionsSpacing - actionsSize.height - layout.intrinsicInsets.bottom - actionsBottomInset
|
||||
contentUnscaledSize = CGSize(width: layout.size.width, height: max(400.0, proposedContentHeight))
|
||||
}
|
||||
let contentSize = CGSize(width: floor(contentUnscaledSize.width * contentScale), height: floor(contentUnscaledSize.height * contentScale))
|
||||
|
||||
self.contentContainerNode.updateLayout(size: contentUnscaledSize, scaledSize: contentSize, transition: transition)
|
||||
|
||||
let maximumActionsFrameOrigin = max(60.0, layout.size.height - layout.intrinsicInsets.bottom - actionsBottomInset - actionsSize.height)
|
||||
var originalActionsFrame = CGRect(origin: CGPoint(x: actionsSideInset, y: maximumActionsFrameOrigin), size: actionsSize)
|
||||
var originalActionsFrame = CGRect(origin: CGPoint(x: actionsSideInset, y: min(maximumActionsFrameOrigin, floor((layout.size.height - contentActionsSpacing - contentSize.height) / 2.0) + contentSize.height + contentActionsSpacing)), size: actionsSize)
|
||||
var originalContentFrame = CGRect(origin: CGPoint(x: actionsSideInset, y: originalActionsFrame.minY - contentActionsSpacing - contentSize.height), size: contentSize)
|
||||
if originalContentFrame.minY < topEdge {
|
||||
let requiredOffset = topEdge - originalContentFrame.minY
|
||||
@@ -1035,10 +1180,16 @@ public final class ContextController: ViewController {
|
||||
private var theme: PresentationTheme
|
||||
private var strings: PresentationStrings
|
||||
private let source: ContextContentSource
|
||||
private var items: [ContextMenuItem]
|
||||
private var items: Signal<[ContextMenuItem], NoError>
|
||||
private var reactionItems: [ReactionContextItem]
|
||||
|
||||
private let _ready = Promise<Bool>()
|
||||
override public var ready: Promise<Bool> {
|
||||
return self._ready
|
||||
}
|
||||
|
||||
private weak var recognizer: TapLongTapOrDoubleTapGestureRecognizer?
|
||||
private weak var gesture: ContextGesture?
|
||||
|
||||
private var animatedDidAppear = false
|
||||
private var wasDismissed = false
|
||||
@@ -1049,7 +1200,7 @@ public final class ContextController: ViewController {
|
||||
|
||||
public var reactionSelected: ((String) -> Void)?
|
||||
|
||||
public init(account: Account, theme: PresentationTheme, strings: PresentationStrings, source: ContextContentSource, items: [ContextMenuItem], reactionItems: [ReactionContextItem], recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil) {
|
||||
public init(account: Account, theme: PresentationTheme, strings: PresentationStrings, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil) {
|
||||
self.account = account
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
@@ -1057,6 +1208,7 @@ public final class ContextController: ViewController {
|
||||
self.items = items
|
||||
self.reactionItems = reactionItems
|
||||
self.recognizer = recognizer
|
||||
self.gesture = gesture
|
||||
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
@@ -1070,7 +1222,7 @@ public final class ContextController: ViewController {
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = ContextControllerNode(account: self.account, controller: self, theme: self.theme, strings: self.strings, source: self.source, items: self.items, reactionItems: self.reactionItems, beginDismiss: { [weak self] result in
|
||||
self?.dismiss(result: result, completion: nil)
|
||||
}, recognizer: self.recognizer, reactionSelected: { [weak self] value in
|
||||
}, recognizer: self.recognizer, gesture: self.gesture, reactionSelected: { [weak self] value in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
@@ -1078,6 +1230,11 @@ public final class ContextController: ViewController {
|
||||
})
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
|
||||
self._ready.set(combineLatest(queue: .mainQueue(), self.controllerNode.itemsReady.get(), self.controllerNode.contentReady.get())
|
||||
|> map { values in
|
||||
return values.0 && values.1
|
||||
})
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
@@ -1098,10 +1255,10 @@ public final class ContextController: ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
public func setItems(_ items: [ContextMenuItem]) {
|
||||
public func setItems(_ items: Signal<[ContextMenuItem], NoError>) {
|
||||
self.items = items
|
||||
if self.isNodeLoaded {
|
||||
self.controllerNode.setItems(controller: self, items: items)
|
||||
self.controllerNode.setItemsSignal(items: items)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user