mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 11:23:48 +00:00
Implement reaction preview
This commit is contained in:
parent
7021252dc6
commit
ee7873b8f2
@ -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?
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user