Implement reaction preview

This commit is contained in:
Ali 2021-12-21 04:29:31 +04:00
parent 7021252dc6
commit ee7873b8f2
5 changed files with 96 additions and 2 deletions

View File

@ -551,3 +551,7 @@ public enum FileMediaResourceMediaStatus: Equatable {
case fetchStatus(MediaResourceStatus) case fetchStatus(MediaResourceStatus)
case playbackStatus(FileMediaResourcePlaybackStatus) case playbackStatus(FileMediaResourcePlaybackStatus)
} }
public protocol ChatMessageItemNodeProtocol: ListViewItemNode {
func targetReactionView(value: String) -> UIView?
}

View File

@ -206,6 +206,14 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable
} }
} }
public var didScrollWithOffset: ((CGFloat, ContainedViewLayoutTransition, ListViewItemNode?) -> Void)? {
didSet {
if self.isNodeLoaded {
(self.displayNode as! ItemListControllerNode).listNode.didScrollWithOffset = self.didScrollWithOffset
}
}
}
public var willScrollToTop: (() -> Void)? public var willScrollToTop: (() -> Void)?
public func setReorderEntry<T: ItemListNodeEntry>(_ f: @escaping (Int, Int, [T]) -> Signal<Bool, NoError>) { public func setReorderEntry<T: ItemListNodeEntry>(_ f: @escaping (Int, Int, [T]) -> Signal<Bool, NoError>) {
@ -471,6 +479,7 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable
displayNode.reorderEntry = self.reorderEntry displayNode.reorderEntry = self.reorderEntry
displayNode.reorderCompleted = self.reorderCompleted displayNode.reorderCompleted = self.reorderCompleted
displayNode.listNode.experimentalSnapScrollToItem = self.experimentalSnapScrollToItem displayNode.listNode.experimentalSnapScrollToItem = self.experimentalSnapScrollToItem
displayNode.listNode.didScrollWithOffset = self.didScrollWithOffset
displayNode.requestLayout = { [weak self] transition in displayNode.requestLayout = { [weak self] transition in
self?.requestLayout(transition: transition) self?.requestLayout(transition: transition)
} }

View File

@ -326,6 +326,18 @@ public func quickReactionSetupController(
} }
let controller = ItemListController(context: context, state: signal) let controller = ItemListController(context: context, state: signal)
controller.didScrollWithOffset = { [weak controller] offset, transition, _ in
guard let controller = controller else {
return
}
controller.forEachItemNode { itemNode in
if let itemNode = itemNode as? ReactionChatPreviewItemNode {
itemNode.standaloneReactionAnimation?.addRelativeContentOffset(CGPoint(x: 0.0, y: offset), transition: transition)
}
}
}
dismissImpl = { [weak controller] in dismissImpl = { [weak controller] in
guard let controller = controller else { guard let controller = controller else {
return return

View File

@ -12,6 +12,7 @@ import PresentationDataUtils
import AccountContext import AccountContext
import WallpaperBackgroundNode import WallpaperBackgroundNode
import AvatarNode import AvatarNode
import ReactionSelectionNode
class ReactionChatPreviewItem: ListViewItem, ItemListItem { class ReactionChatPreviewItem: ListViewItem, ItemListItem {
let context: AccountContext let context: AccountContext
@ -86,6 +87,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
private var messageNode: ListViewItemNode? private var messageNode: ListViewItemNode?
private var item: ReactionChatPreviewItem? private var item: ReactionChatPreviewItem?
private(set) weak var standaloneReactionAnimation: StandaloneReactionAnimation?
init() { init() {
self.topStripeNode = ASDisplayNode() self.topStripeNode = ASDisplayNode()
@ -109,6 +111,63 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
self.addSubnode(self.containerNode) self.addSubnode(self.containerNode)
} }
override func didLoad() {
super.didLoad()
let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapGesture(_:)))
recognizer.tapActionAtPoint = { _ in
return .waitForDoubleTap
}
self.view.addGestureRecognizer(recognizer)
}
@objc private func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
switch recognizer.state {
case .ended:
if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation {
switch gesture {
case .doubleTap:
if let item = self.item, let updatedReaction = item.reaction, let availableReactions = item.availableReactions, let messageNode = self.messageNode as? ChatMessageItemNodeProtocol {
if let targetView = messageNode.targetReactionView(value: updatedReaction) {
for reaction in availableReactions.reactions {
if reaction.value == updatedReaction {
if let standaloneReactionAnimation = self.standaloneReactionAnimation {
standaloneReactionAnimation.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak standaloneReactionAnimation] _ in
standaloneReactionAnimation?.removeFromSupernode()
})
self.standaloneReactionAnimation = nil
}
if let supernode = self.supernode {
let standaloneReactionAnimation = StandaloneReactionAnimation(context: item.context, theme: item.theme, reaction: ReactionContextItem(
reaction: ReactionContextItem.Reaction(rawValue: reaction.value),
stillAnimation: reaction.selectAnimation,
listAnimation: reaction.activateAnimation,
applicationAnimation: reaction.effectAnimation
))
self.standaloneReactionAnimation = standaloneReactionAnimation
supernode.addSubnode(standaloneReactionAnimation)
standaloneReactionAnimation.frame = supernode.bounds
standaloneReactionAnimation.animateReactionSelection(targetView: targetView, hideNode: true, completion: { [weak standaloneReactionAnimation] in
standaloneReactionAnimation?.removeFromSupernode()
})
}
break
}
}
}
}
default:
break
}
}
default:
break
}
}
func asyncLayout() -> (_ item: ReactionChatPreviewItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) { func asyncLayout() -> (_ item: ReactionChatPreviewItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
let currentNode = self.messageNode let currentNode = self.messageNode
@ -161,6 +220,7 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
node = messageNode node = messageNode
apply().1(ListViewItemApply(isOnScreen: true)) apply().1(ListViewItemApply(isOnScreen: true))
}) })
node?.isUserInteractionEnabled = false
} }
var contentSize = CGSize(width: params.width, height: 8.0 + 8.0) var contentSize = CGSize(width: params.width, height: 8.0 + 8.0)
@ -174,6 +234,15 @@ class ReactionChatPreviewItemNode: ListViewItemNode {
return (layout, { [weak self] in return (layout, { [weak self] in
if let strongSelf = self { if let strongSelf = self {
if let previousItem = strongSelf.item, previousItem.reaction != item.reaction {
if let standaloneReactionAnimation = strongSelf.standaloneReactionAnimation {
standaloneReactionAnimation.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak standaloneReactionAnimation] _ in
standaloneReactionAnimation?.removeFromSupernode()
})
strongSelf.standaloneReactionAnimation = nil
}
}
strongSelf.item = item strongSelf.item = item
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize) strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: contentSize)

View File

@ -675,7 +675,7 @@ final class ChatMessageAccessibilityData {
} }
} }
public class ChatMessageItemView: ListViewItemNode { public class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol {
let layoutConstants = (ChatMessageItemLayoutConstants.compact, ChatMessageItemLayoutConstants.regular) let layoutConstants = (ChatMessageItemLayoutConstants.compact, ChatMessageItemLayoutConstants.regular)
var item: ChatMessageItem? var item: ChatMessageItem?
@ -872,7 +872,7 @@ public class ChatMessageItemView: ListViewItemNode {
func openMessageContextMenu() { func openMessageContextMenu() {
} }
func targetReactionView(value: String) -> UIView? { public func targetReactionView(value: String) -> UIView? {
return nil return nil
} }