Merge branch 'eb/contextMenuAnimations'
This commit is contained in:
commit
dc391eb398
@ -10,6 +10,11 @@ public class ContextMenuActionsAccessory: ContextMenuTargetedPreviewAccessory, C
|
||||
|
||||
private let menuView: ContextMenuActionsView
|
||||
|
||||
private let minimumScale: CGFloat = 0.2
|
||||
private let minimumOpacity: CGFloat = 0.2
|
||||
private let springDamping: CGFloat = 0.8
|
||||
private let springInitialVelocity: CGFloat = 1
|
||||
|
||||
public init(
|
||||
menu: ContextMenu,
|
||||
accessoryAlignment: AccessoryAlignment
|
||||
@ -19,19 +24,104 @@ public class ContextMenuActionsAccessory: ContextMenuTargetedPreviewAccessory, C
|
||||
menuView = ContextMenuActionsView(menu: menu)
|
||||
super.init(accessoryView: menuView, accessoryAlignment: accessoryAlignment)
|
||||
menuView.delegate = self
|
||||
animateAccessoryPresentationAlongsidePreview = true
|
||||
}
|
||||
|
||||
override func touchLocationInViewDidChange(locationInView: CGPoint) {
|
||||
override func animateIn(
|
||||
duration: TimeInterval,
|
||||
previewWillShift: Bool,
|
||||
completion: @escaping () -> Void
|
||||
) {
|
||||
|
||||
setMenuLayerAnchorPoint()
|
||||
|
||||
menuView.transform = CGAffineTransform.scale(minimumScale)
|
||||
UIView.animate(
|
||||
withDuration: duration,
|
||||
delay: 0,
|
||||
usingSpringWithDamping: springDamping,
|
||||
initialSpringVelocity: springInitialVelocity,
|
||||
options: [.curveEaseInOut, .beginFromCurrentState],
|
||||
animations: {
|
||||
self.menuView.transform = CGAffineTransform.identity
|
||||
},
|
||||
completion: nil
|
||||
)
|
||||
|
||||
let opacityAnimation = CABasicAnimation(keyPath: "opacity")
|
||||
opacityAnimation.fromValue = minimumOpacity
|
||||
opacityAnimation.toValue = 1
|
||||
opacityAnimation.duration = duration
|
||||
opacityAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
||||
menuView.layer.add(opacityAnimation, forKey: "insertOpacity")
|
||||
}
|
||||
|
||||
override func animateOut(
|
||||
duration: TimeInterval,
|
||||
previewWillShift: Bool,
|
||||
completion: @escaping () -> Void
|
||||
) {
|
||||
|
||||
setMenuLayerAnchorPoint()
|
||||
UIView.animate(
|
||||
withDuration: duration,
|
||||
delay: 0,
|
||||
usingSpringWithDamping: springDamping,
|
||||
initialSpringVelocity: springInitialVelocity,
|
||||
options: [.curveEaseInOut, .beginFromCurrentState],
|
||||
animations: {
|
||||
self.menuView.transform = CGAffineTransform.scale(self.minimumScale)
|
||||
},
|
||||
completion: { _ in
|
||||
completion()
|
||||
}
|
||||
)
|
||||
|
||||
let opacityAnimation = CABasicAnimation(keyPath: "opacity")
|
||||
opacityAnimation.fromValue = 1
|
||||
opacityAnimation.toValue = 0
|
||||
opacityAnimation.duration = duration - 0.1
|
||||
opacityAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
|
||||
opacityAnimation.isRemovedOnCompletion = false
|
||||
opacityAnimation.fillMode = .forwards
|
||||
menuView.layer.add(opacityAnimation, forKey: "removeOpacity")
|
||||
}
|
||||
|
||||
private func setMenuLayerAnchorPoint() {
|
||||
let alignment = delegate?.contextMenuTargetedPreviewAccessoryPreviewAlignment(self)
|
||||
let xAnchor: CGFloat
|
||||
switch alignment {
|
||||
case .center:
|
||||
xAnchor = 0.5
|
||||
case .left:
|
||||
xAnchor = 0
|
||||
case .right:
|
||||
xAnchor = 1
|
||||
case .none:
|
||||
xAnchor = 0
|
||||
}
|
||||
|
||||
let frame = menuView.frame
|
||||
menuView.layer.anchorPoint = CGPoint(x: xAnchor, y: 0)
|
||||
menuView.frame = frame
|
||||
}
|
||||
|
||||
override func touchLocationInViewDidChange(
|
||||
locationInView: CGPoint
|
||||
) {
|
||||
menuView.handleGestureChanged(locationInView: locationInView)
|
||||
}
|
||||
|
||||
override func touchLocationInViewDidEnd(locationInView: CGPoint) {
|
||||
override func touchLocationInViewDidEnd(
|
||||
locationInView: CGPoint
|
||||
) {
|
||||
menuView.handleGestureEnded(locationInView: locationInView)
|
||||
}
|
||||
|
||||
func contextMenuActionViewDidSelectAction(contextMenuAction: ContextMenuAction) {
|
||||
delegate?.contextMenuTargetedPreviewAccessoryRequestsDismissal(self)
|
||||
contextMenuAction.handler(contextMenuAction)
|
||||
delegate?.contextMenuTargetedPreviewAccessoryRequestsDismissal(self, completion: {
|
||||
contextMenuAction.handler(contextMenuAction)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -52,7 +52,8 @@ public class ContextMenu {
|
||||
}
|
||||
|
||||
protocol ContextMenuTargetedPreviewAccessoryInteractionDelegate: AnyObject {
|
||||
func contextMenuTargetedPreviewAccessoryRequestsDismissal(_ accessory: ContextMenuTargetedPreviewAccessory)
|
||||
func contextMenuTargetedPreviewAccessoryRequestsDismissal(_ accessory: ContextMenuTargetedPreviewAccessory, completion: @escaping () -> Void)
|
||||
func contextMenuTargetedPreviewAccessoryPreviewAlignment(_ accessory: ContextMenuTargetedPreviewAccessory) -> ContextMenuTargetedPreview.Alignment
|
||||
func contextMenuTargetedPreviewAccessoryRequestsEmojiPicker(_ accessory: ContextMenuTargetedPreviewAccessory, completion: @escaping (String) -> Void)
|
||||
}
|
||||
|
||||
@ -85,6 +86,8 @@ public class ContextMenuTargetedPreviewAccessory {
|
||||
var accessoryAlignment: AccessoryAlignment
|
||||
var landscapeAccessoryAlignment: AccessoryAlignment?
|
||||
|
||||
var animateAccessoryPresentationAlongsidePreview: Bool = false
|
||||
|
||||
weak var delegate: ContextMenuTargetedPreviewAccessoryInteractionDelegate?
|
||||
|
||||
init(
|
||||
@ -97,6 +100,7 @@ public class ContextMenuTargetedPreviewAccessory {
|
||||
|
||||
func animateIn(
|
||||
duration: TimeInterval,
|
||||
previewWillShift: Bool,
|
||||
completion: @escaping () -> Void
|
||||
) {
|
||||
completion()
|
||||
@ -104,6 +108,7 @@ public class ContextMenuTargetedPreviewAccessory {
|
||||
|
||||
func animateOut(
|
||||
duration: TimeInterval,
|
||||
previewWillShift: Bool,
|
||||
completion: @escaping () -> Void
|
||||
) {
|
||||
completion()
|
||||
@ -130,7 +135,6 @@ public class ContextMenuTargetedPreview {
|
||||
case left
|
||||
case center
|
||||
case right
|
||||
|
||||
}
|
||||
|
||||
public let view: UIView?
|
||||
|
||||
@ -8,11 +8,19 @@ protocol ContextMenuControllerDelegate: AnyObject {
|
||||
func contextMenuControllerRequestsDismissal(_ contextMenuController: ContextMenuController)
|
||||
}
|
||||
|
||||
protocol ContextMenuViewDelegate: AnyObject {
|
||||
private protocol ContextMenuViewDelegate: AnyObject {
|
||||
func contextMenuViewPreviewSourceFrame(_ contextMenuView: ContextMenuHostView) -> CGRect
|
||||
func contextMenuViewAnimationState(_ contextMenuView: ContextMenuHostView) -> ContextMenuAnimationState
|
||||
func contextMenuViewPreviewFrameForAccessoryLayout(_ contextMenuView: ContextMenuHostView) -> CGRect
|
||||
}
|
||||
|
||||
class ContextMenuHostView: UIView {
|
||||
private enum ContextMenuAnimationState {
|
||||
case none
|
||||
case animateIn
|
||||
case animateOut
|
||||
}
|
||||
|
||||
private class ContextMenuHostView: UIView {
|
||||
|
||||
weak var delegate: ContextMenuViewDelegate?
|
||||
var previewViewAlignment: ContextMenuTargetedPreview.Alignment = .center
|
||||
@ -73,8 +81,14 @@ class ContextMenuHostView: UIView {
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
blurView?.frame = bounds
|
||||
|
||||
let animationState = delegate?.contextMenuViewAnimationState(self) ?? .none
|
||||
|
||||
if let previewView = self.previewView {
|
||||
previewView.frame = targetPreviewFrame()
|
||||
// Let the controller manage the preview's frame if animating
|
||||
if animationState != .animateOut {
|
||||
previewView.frame = targetPreviewFrame()
|
||||
}
|
||||
}
|
||||
|
||||
if let accessories = accessoryViews {
|
||||
@ -199,8 +213,10 @@ class ContextMenuHostView: UIView {
|
||||
}
|
||||
|
||||
private func layoutAccessoryView(_ accessory: ContextMenuTargetedPreviewAccessory) {
|
||||
guard let previewFrame = previewView?.frame else {
|
||||
owsFailDebug("Cannot layout accessory views without a preview view")
|
||||
let previewFrame = delegate?.contextMenuViewPreviewFrameForAccessoryLayout(self) ?? CGRect.zero
|
||||
|
||||
let animationState = delegate?.contextMenuViewAnimationState(self) ?? .none
|
||||
guard !(accessory.animateAccessoryPresentationAlongsidePreview && animationState == .animateOut) else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -215,6 +231,14 @@ class ContextMenuController: UIViewController, ContextMenuViewDelegate, UIGestur
|
||||
let contextMenuConfiguration: ContextMenuConfiguration
|
||||
let menuAccessory: ContextMenuActionsAccessory?
|
||||
|
||||
var previewView: UIView? {
|
||||
if let hostView = view as? ContextMenuHostView {
|
||||
return hostView.previewView
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var gestureRecognizer: UIGestureRecognizer?
|
||||
var localPanGestureRecoginzer: UIPanGestureRecognizer?
|
||||
|
||||
@ -222,6 +246,16 @@ class ContextMenuController: UIViewController, ContextMenuViewDelegate, UIGestur
|
||||
private let deadZoneRadius: CGFloat = 30
|
||||
private var initialTouchLocation: CGPoint?
|
||||
|
||||
private var animationState: ContextMenuAnimationState = .none
|
||||
private var animateOutPreviewFrame = CGRect.zero
|
||||
private let animationDuration = 0.4
|
||||
|
||||
private var previewShadowVisible = false {
|
||||
didSet {
|
||||
self.previewView?.layer.shadowOpacity = previewShadowVisible ? 0.3 : 0
|
||||
}
|
||||
}
|
||||
|
||||
var accessoryViews: [ContextMenuTargetedPreviewAccessory] {
|
||||
var accessories = contextMenuPreview.accessoryViews
|
||||
if let menuAccessory = self.menuAccessory {
|
||||
@ -231,8 +265,7 @@ class ContextMenuController: UIViewController, ContextMenuViewDelegate, UIGestur
|
||||
}
|
||||
|
||||
lazy var blurView: UIVisualEffectView = {
|
||||
let effect = UIBlurEffect(style: UIBlurEffect.Style.regular)
|
||||
return UIVisualEffectView(effect: effect)
|
||||
return UIVisualEffectView(effect: nil)
|
||||
}()
|
||||
|
||||
private var emojiPickerSheet: EmojiPickerSheet?
|
||||
@ -268,6 +301,11 @@ class ContextMenuController: UIViewController, ContextMenuViewDelegate, UIGestur
|
||||
contextMenuView.previewView = contextMenuPreview.snapshot
|
||||
contextMenuView.accessoryViews = accessoryViews
|
||||
|
||||
self.previewView?.isHidden = true
|
||||
self.previewView?.layer.shadowRadius = 12
|
||||
self.previewView?.layer.shadowOffset = CGSize(width: 0, height: 4)
|
||||
self.previewView?.layer.shadowColor = UIColor.ows_black.cgColor
|
||||
|
||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapGestureRecognized(sender:)))
|
||||
view.addGestureRecognizer(tapGesture)
|
||||
}
|
||||
@ -280,13 +318,119 @@ class ContextMenuController: UIViewController, ContextMenuViewDelegate, UIGestur
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
guard let previewView = previewView else {
|
||||
owsFailDebug("Cannot animate without preview view!")
|
||||
return
|
||||
}
|
||||
|
||||
animationState = .animateIn
|
||||
|
||||
UIView.animate(withDuration: animationDuration / 2.0) {
|
||||
self.blurView.effect = UIBlurEffect(style: UIBlurEffect.Style.regular)
|
||||
self.previewShadowVisible = true
|
||||
}
|
||||
|
||||
previewView.isHidden = false
|
||||
let finalFrame = previewView.frame
|
||||
let initialFrame = previewSourceFrame()
|
||||
let shiftPreview = finalFrame != initialFrame
|
||||
if shiftPreview {
|
||||
previewView.frame = initialFrame
|
||||
|
||||
let yDelta = finalFrame.y - initialFrame.y
|
||||
for accessory in accessoryViews {
|
||||
if accessory.animateAccessoryPresentationAlongsidePreview {
|
||||
accessory.accessoryView.frame.y -= yDelta
|
||||
}
|
||||
}
|
||||
|
||||
UIView.animate(
|
||||
withDuration: animationDuration,
|
||||
delay: 0,
|
||||
usingSpringWithDamping: 0.8,
|
||||
initialSpringVelocity: 1,
|
||||
options: [.curveEaseInOut, .beginFromCurrentState],
|
||||
animations: {
|
||||
for accessory in self.accessoryViews {
|
||||
if accessory.animateAccessoryPresentationAlongsidePreview {
|
||||
accessory.accessoryView.frame.y += yDelta
|
||||
}
|
||||
}
|
||||
self.previewView?.frame = finalFrame
|
||||
}) { _ in
|
||||
self.animationState = .none
|
||||
}
|
||||
}
|
||||
|
||||
// Animate in accessories
|
||||
for accessory in accessoryViews {
|
||||
accessory.animateIn(duration: 0.2) { }
|
||||
accessory.animateIn(duration: animationDuration, previewWillShift: shiftPreview) { }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public func animateOut(_ completion: @escaping () -> Void) {
|
||||
|
||||
guard let previewView = previewView else {
|
||||
owsFailDebug("Cannot animate without preview view!")
|
||||
completion()
|
||||
return
|
||||
}
|
||||
|
||||
let dispatchGroup = DispatchGroup()
|
||||
animationState = .animateOut
|
||||
dispatchGroup.enter()
|
||||
UIView.animate(withDuration: animationDuration / 2.0) {
|
||||
self.blurView.effect = nil
|
||||
self.previewShadowVisible = false
|
||||
} completion: { _ in
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
|
||||
let finalFrame = previewSourceFrame()
|
||||
let initialFrame = previewView.frame
|
||||
animateOutPreviewFrame = initialFrame
|
||||
let shiftPreview = finalFrame != initialFrame
|
||||
if shiftPreview {
|
||||
|
||||
let yDelta = finalFrame.y - initialFrame.y
|
||||
|
||||
dispatchGroup.enter()
|
||||
UIView.animate(
|
||||
withDuration: animationDuration,
|
||||
delay: 0,
|
||||
usingSpringWithDamping: 0.8,
|
||||
initialSpringVelocity: 1,
|
||||
options: [.curveEaseInOut, .beginFromCurrentState],
|
||||
animations: {
|
||||
for accessory in self.accessoryViews {
|
||||
if accessory.animateAccessoryPresentationAlongsidePreview {
|
||||
accessory.accessoryView.frame.y += yDelta
|
||||
}
|
||||
}
|
||||
self.previewView?.frame = finalFrame
|
||||
},
|
||||
completion: { _ in
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Animate in accessories
|
||||
for accessory in accessoryViews {
|
||||
dispatchGroup.enter()
|
||||
accessory.animateOut(duration: animationDuration, previewWillShift: shiftPreview) {
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
}
|
||||
|
||||
dispatchGroup.notify(queue: .main) {
|
||||
self.animationState = .none
|
||||
completion()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Gesture Recognizer Support
|
||||
public func gestureDidChange() {
|
||||
if let locationInView = gestureRecognizer?.location(in: view) {
|
||||
@ -367,7 +511,25 @@ class ContextMenuController: UIViewController, ContextMenuViewDelegate, UIGestur
|
||||
|
||||
// MARK: ContextMenuViewDelegate
|
||||
|
||||
func contextMenuViewPreviewSourceFrame(_ contextMenuView: ContextMenuHostView) -> CGRect {
|
||||
fileprivate func contextMenuViewPreviewSourceFrame(_ contextMenuView: ContextMenuHostView) -> CGRect {
|
||||
return previewSourceFrame()
|
||||
}
|
||||
|
||||
fileprivate func contextMenuViewAnimationState(_ contextMenuView: ContextMenuHostView) -> ContextMenuAnimationState {
|
||||
return animationState
|
||||
}
|
||||
|
||||
fileprivate func contextMenuViewPreviewFrameForAccessoryLayout(_ contextMenuView: ContextMenuHostView) -> CGRect {
|
||||
if animationState == .animateOut {
|
||||
return animateOutPreviewFrame
|
||||
}
|
||||
|
||||
return previewView?.frame ?? CGRect.zero
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private func previewSourceFrame() -> CGRect {
|
||||
guard let sourceView = contextMenuPreview.view else {
|
||||
owsFailDebug("Expected source view")
|
||||
return CGRect.zero
|
||||
@ -375,8 +537,6 @@ class ContextMenuController: UIViewController, ContextMenuViewDelegate, UIGestur
|
||||
return view.convert(sourceView.frame, from: sourceView)
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@objc
|
||||
private func tapGestureRecognized(sender: UIGestureRecognizer) {
|
||||
delegate?.contextMenuControllerRequestsDismissal(self)
|
||||
|
||||
@ -109,13 +109,16 @@ public class ContextMenuInteraction: NSObject, UIInteraction {
|
||||
return accessory
|
||||
}
|
||||
|
||||
public func dismissMenu() {
|
||||
public func dismissMenu(completion: @escaping() -> Void ) {
|
||||
if let configuarion = self.configuration {
|
||||
delegate?.contextMenuInteraction(self, willEndForConfiguration: configuarion)
|
||||
}
|
||||
|
||||
contextMenuController?.view.removeFromSuperview()
|
||||
contextMenuController = nil
|
||||
contextMenuController?.animateOut({
|
||||
completion()
|
||||
self.contextMenuController?.view.removeFromSuperview()
|
||||
self.contextMenuController = nil
|
||||
})
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
@ -129,12 +132,16 @@ public class ContextMenuInteraction: NSObject, UIInteraction {
|
||||
|
||||
extension ContextMenuInteraction: ContextMenuControllerDelegate, ContextMenuTargetedPreviewAccessoryInteractionDelegate {
|
||||
|
||||
func contextMenuTargetedPreviewAccessoryRequestsDismissal(_ accessory: ContextMenuTargetedPreviewAccessory) {
|
||||
dismissMenu()
|
||||
func contextMenuTargetedPreviewAccessoryRequestsDismissal(_ accessory: ContextMenuTargetedPreviewAccessory, completion: @escaping () -> Void) {
|
||||
dismissMenu(completion: completion)
|
||||
}
|
||||
|
||||
func contextMenuTargetedPreviewAccessoryPreviewAlignment(_ accessory: ContextMenuTargetedPreviewAccessory) -> ContextMenuTargetedPreview.Alignment {
|
||||
return contextMenuController?.contextMenuPreview.alignment ?? .center
|
||||
}
|
||||
|
||||
func contextMenuControllerRequestsDismissal(_ contextMenuController: ContextMenuController) {
|
||||
dismissMenu()
|
||||
dismissMenu(completion: { })
|
||||
}
|
||||
|
||||
func contextMenuTargetedPreviewAccessoryRequestsEmojiPicker(
|
||||
|
||||
@ -24,18 +24,31 @@ public class ContextMenuRectionBarAccessory: ContextMenuTargetedPreviewAccessory
|
||||
let alignment = ContextMenuTargetedPreviewAccessory.AccessoryAlignment(alignments: [(.top, .exterior), (isIncomingMessage ? .leading : .trailing, .interior)], alignmentOffset: CGPoint(x: alignmnetOffset, y: -12))
|
||||
super.init(accessoryView: reactionPicker, accessoryAlignment: alignment)
|
||||
reactionPicker.delegate = self
|
||||
reactionPicker.isHidden = true
|
||||
}
|
||||
|
||||
override func animateIn(
|
||||
duration: TimeInterval,
|
||||
previewWillShift: Bool,
|
||||
completion: @escaping () -> Void
|
||||
) {
|
||||
reactionPicker.playPresentationAnimation(duration: duration)
|
||||
completion()
|
||||
let animateIn = {
|
||||
self.reactionPicker.isHidden = false
|
||||
self.reactionPicker.playPresentationAnimation(duration: 0.2)
|
||||
completion()
|
||||
|
||||
}
|
||||
if previewWillShift {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { animateIn() }
|
||||
} else {
|
||||
animateIn()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override func animateOut(
|
||||
duration: TimeInterval,
|
||||
previewWillShift: Bool,
|
||||
completion: @escaping () -> Void
|
||||
) {
|
||||
reactionPicker.playDismissalAnimation(duration: duration, completion: completion)
|
||||
@ -69,7 +82,7 @@ public class ContextMenuRectionBarAccessory: ContextMenuTargetedPreviewAccessory
|
||||
|
||||
reactionPicker.playDismissalAnimation(duration: 0.2) {
|
||||
self.didSelectReactionHandler?(message, reaction, isRemoving)
|
||||
self.delegate?.contextMenuTargetedPreviewAccessoryRequestsDismissal(self)
|
||||
self.delegate?.contextMenuTargetedPreviewAccessoryRequestsDismissal(self, completion: { })
|
||||
|
||||
}
|
||||
}
|
||||
@ -85,7 +98,7 @@ public class ContextMenuRectionBarAccessory: ContextMenuTargetedPreviewAccessory
|
||||
self.delegate?.contextMenuTargetedPreviewAccessoryRequestsEmojiPicker(self) { emojiString in
|
||||
let isRemoving = emojiString == self.itemViewModel?.reactionState?.localUserEmoji
|
||||
self.didSelectReactionHandler?(message, emojiString, isRemoving)
|
||||
self.delegate?.contextMenuTargetedPreviewAccessoryRequestsDismissal(self)
|
||||
self.delegate?.contextMenuTargetedPreviewAccessoryRequestsDismissal(self, completion: { })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1658,6 +1658,7 @@ public class CVComponentMessage: CVComponentBase, CVRootComponent {
|
||||
avatarView.frame = componentView.avatarView.bounds
|
||||
let alignment = ContextMenuTargetedPreviewAccessory.AccessoryAlignment(alignments: [(.leading, .exterior), (.bottom, .interior)], alignmentOffset: CGPoint(x: -8, y: 0))
|
||||
let avatarViewAccessory = ContextMenuTargetedPreviewAccessory(accessoryView: avatarView, accessoryAlignment: alignment)
|
||||
avatarViewAccessory.animateAccessoryPresentationAlongsidePreview = true
|
||||
return [avatarViewAccessory]
|
||||
} else {
|
||||
return nil
|
||||
|
||||
@ -199,7 +199,7 @@ extension ConversationViewController: ContextMenuInteractionDelegate {
|
||||
let actionWithType = actions.first { $0.actionType == type }
|
||||
if let messageAction = actionWithType {
|
||||
let contextMenuAction = ContextMenuAction(title: messageAction.contextMenuTitle, image: messageAction.image, attributes: messageAction.contextMenuAttributes, handler: { _ in
|
||||
messageAction.block(self)
|
||||
messageAction.block(nil)
|
||||
})
|
||||
|
||||
contextMenuActions.append(contextMenuAction)
|
||||
|
||||
@ -358,7 +358,7 @@ public class DebugFlags: BaseFlags {
|
||||
public static let permissiveGroupUpdateInfoMessages = build.includes(.dev)
|
||||
|
||||
@objc
|
||||
public static let showContextMenuDebugRects = build.includes(.dev)
|
||||
public static let showContextMenuDebugRects = false
|
||||
|
||||
@objc
|
||||
public static let verboseNotificationLogging = build.includes(.qa)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user