[WIP] Message context menu

This commit is contained in:
Isaac
2024-04-30 23:40:55 +04:00
parent 24834e428c
commit e0c2800e8f
7 changed files with 423 additions and 77 deletions

View File

@@ -2966,13 +2966,13 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
self.isUserInteractionEnabled = false
}
public func animateReactionSelection(context: AccountContext, theme: PresentationTheme, animationCache: AnimationCache, reaction: ReactionItem, avatarPeers: [EnginePeer], playHaptic: Bool, isLarge: Bool, forceSmallEffectAnimation: Bool = false, hideCenterAnimation: Bool = false, targetView: UIView, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, completion: @escaping () -> Void) {
self.animateReactionSelection(context: context, theme: theme, animationCache: animationCache, reaction: reaction, avatarPeers: avatarPeers, playHaptic: playHaptic, isLarge: isLarge, forceSmallEffectAnimation: forceSmallEffectAnimation, hideCenterAnimation: hideCenterAnimation, targetView: targetView, addStandaloneReactionAnimation: addStandaloneReactionAnimation, currentItemNode: nil, completion: completion)
public func animateReactionSelection(context: AccountContext, theme: PresentationTheme, animationCache: AnimationCache, reaction: ReactionItem, avatarPeers: [EnginePeer], playHaptic: Bool, isLarge: Bool, playCenterReaction: Bool = true, forceSmallEffectAnimation: Bool = false, hideCenterAnimation: Bool = false, targetView: UIView, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, completion: @escaping () -> Void) {
self.animateReactionSelection(context: context, theme: theme, animationCache: animationCache, reaction: reaction, avatarPeers: avatarPeers, playHaptic: playHaptic, isLarge: isLarge, playCenterReaction: playCenterReaction, forceSmallEffectAnimation: forceSmallEffectAnimation, hideCenterAnimation: hideCenterAnimation, targetView: targetView, addStandaloneReactionAnimation: addStandaloneReactionAnimation, currentItemNode: nil, completion: completion)
}
public var currentDismissAnimation: (() -> Void)?
public func animateReactionSelection(context: AccountContext, theme: PresentationTheme, animationCache: AnimationCache, reaction: ReactionItem, avatarPeers: [EnginePeer], playHaptic: Bool, isLarge: Bool, forceSmallEffectAnimation: Bool = false, hideCenterAnimation: Bool = false, targetView: UIView, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, currentItemNode: ReactionNode?, completion: @escaping () -> Void) {
public func animateReactionSelection(context: AccountContext, theme: PresentationTheme, animationCache: AnimationCache, reaction: ReactionItem, avatarPeers: [EnginePeer], playHaptic: Bool, isLarge: Bool, playCenterReaction: Bool = true, forceSmallEffectAnimation: Bool = false, hideCenterAnimation: Bool = false, targetView: UIView, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, currentItemNode: ReactionNode?, completion: @escaping () -> Void) {
guard let sourceSnapshotView = targetView.snapshotContentTree() else {
completion()
return
@@ -2984,28 +2984,36 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
self.targetView = targetView
let itemNode: ReactionNode
if let currentItemNode = currentItemNode {
itemNode = currentItemNode
let itemNode: ReactionNode?
if playCenterReaction {
if let currentItemNode = currentItemNode {
itemNode = currentItemNode
} else {
let animationRenderer = MultiAnimationRendererImpl()
itemNode = ReactionNode(context: context, theme: theme, item: reaction, animationCache: animationCache, animationRenderer: animationRenderer, loopIdle: false, isLocked: false)
}
self.itemNode = itemNode
} else {
let animationRenderer = MultiAnimationRendererImpl()
itemNode = ReactionNode(context: context, theme: theme, item: reaction, animationCache: animationCache, animationRenderer: animationRenderer, loopIdle: false, isLocked: false)
itemNode = nil
}
self.itemNode = itemNode
let switchToInlineImmediately: Bool
if itemNode.item.listAnimation.isVideoEmoji || itemNode.item.listAnimation.isVideoSticker || itemNode.item.listAnimation.isAnimatedSticker || itemNode.item.listAnimation.isStaticEmoji {
switch itemNode.item.reaction.rawValue {
case .builtin:
if let itemNode {
if itemNode.item.listAnimation.isVideoEmoji || itemNode.item.listAnimation.isVideoSticker || itemNode.item.listAnimation.isAnimatedSticker || itemNode.item.listAnimation.isStaticEmoji {
switch itemNode.item.reaction.rawValue {
case .builtin:
switchToInlineImmediately = false
case .custom:
switchToInlineImmediately = true
}
} else {
switchToInlineImmediately = false
case .custom:
switchToInlineImmediately = true
}
} else {
switchToInlineImmediately = false
}
if !forceSmallEffectAnimation && !switchToInlineImmediately && !hideCenterAnimation {
if let itemNode, !forceSmallEffectAnimation, !switchToInlineImmediately, !hideCenterAnimation {
if let targetView = targetView as? ReactionIconView, !isLarge {
self.itemNodeIsEmbedded = true
targetView.addSubnode(itemNode)
@@ -3014,28 +3022,27 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
}
}
itemNode.expandedAnimationDidBegin = { [weak self, weak targetView] in
guard let strongSelf = self, let targetView = targetView else {
return
}
if let targetView = targetView as? ReactionIconView, !isLarge {
strongSelf.itemNodeIsEmbedded = true
targetView.updateIsAnimationHidden(isAnimationHidden: true, transition: .immediate)
} else {
targetView.isHidden = true
if let itemNode {
itemNode.expandedAnimationDidBegin = { [weak self, weak targetView] in
guard let strongSelf = self, let targetView = targetView else {
return
}
if let targetView = targetView as? ReactionIconView, !isLarge {
strongSelf.itemNodeIsEmbedded = true
targetView.updateIsAnimationHidden(isAnimationHidden: true, transition: .immediate)
} else {
targetView.isHidden = true
}
}
itemNode.isExtracted = true
}
itemNode.isExtracted = true
var selfTargetBounds = targetView.bounds
if let targetView = targetView as? ReactionIconView, let iconFrame = targetView.iconFrame {
selfTargetBounds = iconFrame
}
/*if case .builtin = itemNode.item.reaction.rawValue {
selfTargetBounds = selfTargetBounds.insetBy(dx: -selfTargetBounds.width * 0.5, dy: -selfTargetBounds.height * 0.5)
}*/
let selfTargetRect = self.view.convert(selfTargetBounds, from: targetView)
@@ -3064,21 +3071,23 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
})
}
if self.itemNodeIsEmbedded {
itemNode.frame = selfTargetBounds
} else {
itemNode.frame = expandedFrame
if let itemNode {
if self.itemNodeIsEmbedded {
itemNode.frame = selfTargetBounds
} else {
itemNode.frame = expandedFrame
itemNode.layer.animateSpring(from: (selfTargetRect.width / expandedFrame.width) as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.7)
}
itemNode.layer.animateSpring(from: (selfTargetRect.width / expandedFrame.width) as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: 0.7)
itemNode.updateLayout(size: expandedFrame.size, isExpanded: true, largeExpanded: isLarge, isPreviewing: false, transition: .immediate)
}
itemNode.updateLayout(size: expandedFrame.size, isExpanded: true, largeExpanded: isLarge, isPreviewing: false, transition: .immediate)
let additionalAnimation: TelegramMediaFile?
if isLarge && !forceSmallEffectAnimation {
additionalAnimation = itemNode.item.largeApplicationAnimation
additionalAnimation = reaction.largeApplicationAnimation
} else {
additionalAnimation = itemNode.item.applicationAnimation
additionalAnimation = reaction.applicationAnimation
}
let additionalAnimationNode: AnimatedStickerNode?
@@ -3099,15 +3108,13 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
}
}
var additionalCachePathPrefix: String?
additionalCachePathPrefix = itemNode.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(additionalAnimation.resource.id)
additionalCachePathPrefix = nil
let additionalCachePathPrefix: String? = nil
additionalAnimationNodeValue.setup(source: AnimatedStickerResourceSource(account: itemNode.context.account, resource: additionalAnimation.resource), width: Int(effectFrame.width * 1.33), height: Int(effectFrame.height * 1.33), playbackMode: .once, mode: .direct(cachePathPrefix: additionalCachePathPrefix))
additionalAnimationNodeValue.setup(source: AnimatedStickerResourceSource(account: context.account, resource: additionalAnimation.resource), width: Int(effectFrame.width * 1.33), height: Int(effectFrame.height * 1.33), playbackMode: .once, mode: .direct(cachePathPrefix: additionalCachePathPrefix))
additionalAnimationNodeValue.frame = effectFrame
additionalAnimationNodeValue.updateLayout(size: effectFrame.size)
self.addSubnode(additionalAnimationNodeValue)
} else if itemNode.item.isCustom {
} else if reaction.isCustom {
var effectURL: URL?
if let genericReactionEffect = self.genericReactionEffect {
effectURL = URL(fileURLWithPath: genericReactionEffect)
@@ -3157,18 +3164,18 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
genericAnimationView = view
let animationCache = itemNode.context.animationCache
let animationRenderer = itemNode.context.animationRenderer
let animationCache = context.animationCache
let animationRenderer = context.animationRenderer
for i in 1 ... 7 {
let allLayers = view.allLayers(forKeypath: AnimationKeypath(keypath: "placeholder_\(i)"))
for animationLayer in allLayers {
let baseItemLayer = InlineStickerItemLayer(
context: itemNode.context,
context: context,
userLocation: .other,
attemptSynchronousLoad: false,
emoji: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: itemNode.item.listAnimation.fileId.id, file: itemNode.item.listAnimation),
file: itemNode.item.listAnimation,
emoji: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: reaction.listAnimation.fileId.id, file: reaction.listAnimation),
file: reaction.listAnimation,
cache: animationCache,
renderer: animationRenderer,
placeholderColor: UIColor(white: 0.0, alpha: 0.0),
@@ -3256,15 +3263,6 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
return
}
/*if switchToInlineImmediately {
targetView.updateIsAnimationHidden(isAnimationHidden: false, transition: .immediate)
itemNode.isHidden = true
} else {
targetView.updateIsAnimationHidden(isAnimationHidden: true, transition: .immediate)
targetView.addSubnode(itemNode)
itemNode.frame = selfTargetBounds
}*/
if forceSmallEffectAnimation {
if let additionalAnimationNode = additionalAnimationNode {
additionalAnimationNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak additionalAnimationNode] _ in
@@ -3275,7 +3273,7 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
mainAnimationCompleted = true
intermediateCompletion()
} else {
if isLarge {
if isLarge, let itemNode {
let genericReactionEffect = strongSelf.genericReactionEffect
strongSelf.animateFromItemNodeToReaction(itemNode: itemNode, targetView: targetView, hideNode: true, completion: {
if let addStandaloneReactionAnimation = addStandaloneReactionAnimation {
@@ -3284,10 +3282,10 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
addStandaloneReactionAnimation(standaloneReactionAnimation)
standaloneReactionAnimation.animateReactionSelection(
context: itemNode.context,
theme: itemNode.context.sharedContext.currentPresentationData.with({ $0 }).theme,
context: context,
theme: context.sharedContext.currentPresentationData.with({ $0 }).theme,
animationCache: animationCache,
reaction: itemNode.item,
reaction: reaction,
avatarPeers: avatarPeers,
playHaptic: false,
isLarge: false,
@@ -3331,10 +3329,8 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
}
if forceSmallEffectAnimation {
//itemNode.mainAnimationCompletion = {
mainAnimationCompleted = true
maybeBeginDismissAnimation()
//}
mainAnimationCompleted = true
maybeBeginDismissAnimation()
}
if let additionalAnimationNode = additionalAnimationNode {