Context menu improvements

This commit is contained in:
Peter
2019-09-06 23:24:37 +04:00
1146 changed files with 156026 additions and 8021 deletions

View File

@@ -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)
}
}