Reaction improvements

This commit is contained in:
Ali 2021-12-19 19:12:19 +04:00
parent 884a31987c
commit 42773dbe01
13 changed files with 91 additions and 27 deletions

View File

@ -187,6 +187,11 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
private let selectionHighlightNode: ASDisplayNode
private let itemNodes: [ItemNode]
private struct ScrollToTabReaction {
var value: String?
}
private var scrollToTabReaction: ScrollToTabReaction?
var action: ((String?) -> Void)?
init(context: AccountContext, availableReactions: AvailableReactions?, reactions: [(String?, Int)], message: EngineMessage) {
@ -217,6 +222,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
guard let strongSelf = self else {
return
}
strongSelf.scrollToTabReaction = ScrollToTabReaction(value: reaction)
strongSelf.action?(reaction)
}
}
@ -256,6 +262,16 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
if self.scrollNode.view.contentSize != contentSize {
self.scrollNode.view.contentSize = contentSize
}
if let scrollToTabReaction = self.scrollToTabReaction {
self.scrollToTabReaction = nil
for itemNode in self.itemNodes {
if itemNode.reaction == scrollToTabReaction.value {
self.scrollNode.view.scrollRectToVisible(itemNode.frame.insetBy(dx: -sideInset, dy: 0.0), animated: transition.isAnimated)
break
}
}
}
}
}
@ -494,7 +510,7 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent
itemNode.update(size: itemFrame.size, presentationData: presentationData, item: self.state.items[index], isLast: index == self.state.items.count - 1, syncronousLoad: syncronousLoad)
itemNode.frame = itemFrame
} else {
} else if index < self.state.totalCount {
validPlaceholderIds.insert(index)
let placeholderLayer: SimpleLayer

View File

@ -254,6 +254,12 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
rect.origin.x = max(sideInset, rect.origin.x)
rect.origin.y = max(insets.top + sideInset, rect.origin.y)
rect.origin.x = min(containerSize.width - contentSize.width - sideInset, rect.origin.x)
if rect.maxX > containerSize.width - sideInset {
rect.origin.x = containerSize.width - sideInset - rect.width
}
if rect.minX < sideInset {
rect.origin.x = sideInset
}
let cloudSourcePoint: CGFloat
if isLeftAligned {
@ -309,11 +315,18 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
let completeContentWidth = CGFloat(self.items.count) * itemSize + (CGFloat(self.items.count) - 1.0) * itemSpacing + sideInset * 2.0
let minVisibleItemCount: CGFloat = min(CGFloat(self.items.count), 6.5)
let visibleContentWidth = floor(minVisibleItemCount * itemSize + (minVisibleItemCount - 1.0) * itemSpacing + sideInset * 2.0)
var visibleContentWidth = floor(minVisibleItemCount * itemSize + (minVisibleItemCount - 1.0) * itemSpacing + sideInset * 2.0)
if visibleContentWidth > size.width - sideInset * 2.0 {
visibleContentWidth = size.width - sideInset * 2.0
}
let contentHeight = verticalInset * 2.0 + rowHeight
let (backgroundFrame, isLeftAligned, cloudSourcePoint) = self.calculateBackgroundFrame(containerSize: size, insets: insets, anchorRect: anchorRect, contentSize: CGSize(width: visibleContentWidth, height: contentHeight))
var backgroundInsets = insets
backgroundInsets.left += sideInset
backgroundInsets.right += sideInset
let (backgroundFrame, isLeftAligned, cloudSourcePoint) = self.calculateBackgroundFrame(containerSize: CGSize(width: size.width - sideInset * 2.0, height: size.height), insets: backgroundInsets, anchorRect: anchorRect, contentSize: CGSize(width: visibleContentWidth, height: contentHeight))
self.isLeftAligned = isLeftAligned
transition.updateFrame(node: self.contentContainer, frame: backgroundFrame)
@ -369,7 +382,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
let springDamping: CGFloat = 104.0
let springDelay: Double = 0.22
let sourceBackgroundFrame = self.calculateBackgroundFrame(containerSize: size, insets: insets, anchorRect: animateInFromAnchorRect, contentSize: CGSize(width: backgroundFrame.height, height: contentHeight)).0
let sourceBackgroundFrame = self.calculateBackgroundFrame(containerSize: size, insets: backgroundInsets, anchorRect: animateInFromAnchorRect, contentSize: CGSize(width: backgroundFrame.height, height: contentHeight)).0
self.backgroundNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceBackgroundFrame.midX - backgroundFrame.midX, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true)
self.backgroundNode.layer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: sourceBackgroundFrame.size).insetBy(dx: -shadowBlur, dy: -shadowBlur)), to: NSValue(cgRect: CGRect(origin: CGPoint(), size: backgroundFrame.size).insetBy(dx: -shadowBlur, dy: -shadowBlur)), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping)
@ -380,7 +393,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
//self.contentContainerMask.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceBackgroundFrame.midX - backgroundFrame.midX, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true)
//self.contentContainerMask.layer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: sourceBackgroundFrame.size)), to: NSValue(cgRect: CGRect(origin: CGPoint(), size: backgroundFrame.size)), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping)
} else if let animateOutToAnchorRect = animateOutToAnchorRect {
let targetBackgroundFrame = self.calculateBackgroundFrame(containerSize: size, insets: insets, anchorRect: animateOutToAnchorRect, contentSize: CGSize(width: visibleContentWidth, height: contentHeight)).0
let targetBackgroundFrame = self.calculateBackgroundFrame(containerSize: size, insets: backgroundInsets, anchorRect: animateOutToAnchorRect, contentSize: CGSize(width: visibleContentWidth, height: contentHeight)).0
let offset = CGPoint(x: -(targetBackgroundFrame.minX - backgroundFrame.minX), y: -(targetBackgroundFrame.minY - backgroundFrame.minY))
self.position = CGPoint(x: self.position.x - offset.x, y: self.position.y - offset.y)
@ -472,8 +485,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
itemNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.9, removeOnCompletion: false)
targetSnapshotView.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration * 0.8)
targetSnapshotView.layer.animateScale(from: itemNode.bounds.width / targetSnapshotView.bounds.width, to: 1.0, duration: duration, removeOnCompletion: false, completion: { [weak self, weak targetSnapshotView] _ in
if let strongSelf = self {
strongSelf.hapticFeedback.tap()
if let _ = self {
//strongSelf.hapticFeedback.tap()
}
completedTarget = true
intermediateCompletion()

View File

@ -1173,6 +1173,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
var removedReaction: String?
var messageAlreadyHasThisReaction = false
for attribute in message.attributes {
if let attribute = attribute as? ReactionsMessageAttribute {
@ -1182,11 +1183,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if listReaction.isSelected {
updatedReaction = nil
removedReaction = listReaction.value
} else if listReaction.value == updatedReaction {
messageAlreadyHasThisReaction = true
}
case let .reaction(value):
if listReaction.value == value && listReaction.isSelected {
updatedReaction = nil
removedReaction = value
if listReaction.value == value {
messageAlreadyHasThisReaction = true
if listReaction.isSelected {
updatedReaction = nil
removedReaction = value
}
}
}
}
@ -1213,7 +1220,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
switch allowedReactions {
case let .set(set):
if !set.contains(updatedReaction) {
if !messageAlreadyHasThisReaction && !set.contains(updatedReaction) {
itemNode.openMessageContextMenu()
return
}
@ -14450,6 +14457,9 @@ extension Peer {
}
func canAddMessageReactions(message: Message) -> Bool {
if message.id.namespace != Namespaces.Message.Cloud {
return false
}
if let peer = message.peers[message.id.peerId] {
if let _ = peer as? TelegramSecretChat {
return false

View File

@ -945,8 +945,8 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
var dateReplies = 0
let dateReactionsAndPeers = mergedMessageReactionsAndPeers(message: item.message)
for attribute in item.message.attributes {
if let _ = attribute as? EditedMessageAttribute, isEmoji {
edited = true
if let attribute = attribute as? EditedMessageAttribute, isEmoji {
edited = !attribute.isHidden
} else if let attribute = attribute as? ViewCountMessageAttribute {
viewCount = attribute.count
} else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = item.chatLocation {

View File

@ -939,11 +939,18 @@ final class ChatMessageAttachedContentNode: ASDisplayNode {
strongSelf.textNode.frame = textFrame.offsetBy(dx: 0.0, dy: textVerticalOffset)
if let statusSizeAndApply = statusSizeAndApply {
var statusFrame = CGRect(origin: CGPoint(x: strongSelf.textNode.frame.minX, y: strongSelf.textNode.frame.maxY), size: statusSizeAndApply.0)
if let imageFrame = imageFrame {
statusFrame.origin.y = max(statusFrame.minY, imageFrame.maxY + 2.0)
}
if strongSelf.statusNode.supernode == nil {
strongSelf.addSubnode(strongSelf.statusNode)
strongSelf.statusNode.frame = statusFrame
statusSizeAndApply.1(.None)
} else {
animation.animator.updateFrame(layer: strongSelf.statusNode.layer, frame: statusFrame, completion: nil)
statusSizeAndApply.1(animation)
}
strongSelf.statusNode.frame = CGRect(origin: CGPoint(x: strongSelf.textNode.frame.minX, y: strongSelf.textNode.frame.maxY), size: statusSizeAndApply.0)
statusSizeAndApply.1(animation)
} else if strongSelf.statusNode.supernode != nil {
strongSelf.statusNode.removeFromSupernode()
}

View File

@ -835,7 +835,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
}
}
for contentNode in strongSelf.contentNodes {
let tapAction = contentNode.tapActionAtPoint(CGPoint(x: point.x - contentNode.frame.minX, y: point.y - contentNode.frame.minY), gesture: .tap, isEstimating: true)
let contentNodePoint = strongSelf.view.convert(point, to: contentNode.view)
let tapAction = contentNode.tapActionAtPoint(contentNodePoint, gesture: .tap, isEstimating: true)
switch tapAction {
case .none:
if let _ = strongSelf.item?.controllerInteraction.tapMessage {
@ -2696,14 +2697,18 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
}
if let mosaicStatusOrigin = mosaicStatusOrigin, let (size, apply) = mosaicStatusSizeAndApply {
let mosaicStatusNode = apply(animation)
var statusNodeAnimation = animation
if strongSelf.mosaicStatusNode == nil {
statusNodeAnimation = .None
}
let mosaicStatusNode = apply(statusNodeAnimation)
if mosaicStatusNode !== strongSelf.mosaicStatusNode {
strongSelf.mosaicStatusNode?.removeFromSupernode()
strongSelf.mosaicStatusNode = mosaicStatusNode
strongSelf.clippingNode.addSubnode(mosaicStatusNode)
}
let absoluteOrigin = mosaicStatusOrigin.offsetBy(dx: contentOrigin.x, dy: contentOrigin.y)
mosaicStatusNode.frame = CGRect(origin: CGPoint(x: absoluteOrigin.x - layoutConstants.image.statusInsets.right - size.width, y: absoluteOrigin.y - layoutConstants.image.statusInsets.bottom - size.height), size: size)
statusNodeAnimation.animator.updateFrame(layer: mosaicStatusNode.layer, frame: CGRect(origin: CGPoint(x: absoluteOrigin.x - layoutConstants.image.statusInsets.right - size.width, y: absoluteOrigin.y - layoutConstants.image.statusInsets.bottom - size.height), size: size), completion: nil)
} else if let mosaicStatusNode = strongSelf.mosaicStatusNode {
strongSelf.mosaicStatusNode = nil
mosaicStatusNode.removeFromSupernode()
@ -3880,6 +3885,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
return result
}
}
if let mosaicStatusNode = self.mosaicStatusNode, let result = mosaicStatusNode.reactionView(value: value) {
return result
}
return nil
}

View File

@ -55,7 +55,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
return
}
item.controllerInteraction.openMessageReactionContextMenu(item.message, sourceNode, gesture, value)
item.controllerInteraction.openMessageReactionContextMenu(item.topMessage, sourceNode, gesture, value)
}
}

View File

@ -791,7 +791,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
var reactionButtonPosition = CGPoint(x: -1.0, y: verticalReactionsInset)
for item in reactionButtons.items {
if reactionButtonPosition.x + item.size.width > boundingWidth {
reactionButtonPosition.x = 0.0
reactionButtonPosition.x = -1.0
reactionButtonPosition.y += item.size.height + 6.0
}

View File

@ -70,7 +70,7 @@ class ChatMessageFileBubbleContentNode: ChatMessageBubbleContentNode {
return
}
item.controllerInteraction.openMessageReactionContextMenu(item.message, sourceNode, gesture, value)
item.controllerInteraction.openMessageReactionContextMenu(item.topMessage, sourceNode, gesture, value)
}
}
@ -181,6 +181,9 @@ class ChatMessageFileBubbleContentNode: ChatMessageBubbleContentNode {
if self.interactiveFileNode.dateAndStatusNode.supernode != nil, let _ = self.interactiveFileNode.dateAndStatusNode.hitTest(self.view.convert(point, to: self.interactiveFileNode.dateAndStatusNode.view), with: nil) {
return .ignore
}
if self.interactiveFileNode.hasTapAction(at: self.view.convert(point, to: self.interactiveFileNode.view)) {
return .ignore
}
return super.tapActionAtPoint(point, gesture: gesture, isEstimating: isEstimating)
}

View File

@ -1146,6 +1146,13 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
}
return super.hitTest(point, with: event)
}
func hasTapAction(at point: CGPoint) -> Bool {
if let _ = self.dateAndStatusNode.hitTest(self.view.convert(point, to: self.dateAndStatusNode.view), with: nil) {
return true
}
return false
}
}

View File

@ -275,7 +275,7 @@ final class MessageReactionButtonsNode: ASDisplayNode {
switch alignment {
case .left:
if reactionButtonPosition.x + item.size.width > boundingWidth {
reactionButtonPosition.x = 0.0
reactionButtonPosition.x = -1.0
reactionButtonPosition.y += item.size.height + 6.0
}
case .right:
@ -462,7 +462,7 @@ final class ChatMessageReactionsFooterContentNode: ChatMessageBubbleContentNode
return
}
item.controllerInteraction.openMessageReactionContextMenu(item.message, sourceNode, gesture, value)
item.controllerInteraction.openMessageReactionContextMenu(item.topMessage, sourceNode, gesture, value)
}
}

View File

@ -485,8 +485,8 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
var dateReplies = 0
let dateReactionsAndPeers = mergedMessageReactionsAndPeers(message: item.message)
for attribute in item.message.attributes {
if let _ = attribute as? EditedMessageAttribute, isEmoji {
edited = true
if let attribute = attribute as? EditedMessageAttribute, isEmoji {
edited = !attribute.isHidden
} else if let attribute = attribute as? ViewCountMessageAttribute {
viewCount = attribute.count
} else if let attribute = attribute as? ReplyThreadMessageAttribute, case .peer = item.chatLocation {

View File

@ -83,7 +83,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
return
}
item.controllerInteraction.openMessageReactionContextMenu(item.message, sourceNode, gesture, value)
item.controllerInteraction.openMessageReactionContextMenu(item.topMessage, sourceNode, gesture, value)
}
}
@ -128,7 +128,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
}
var viewCount: Int?
var dateReplies = 0
let dateReactionsAndPeers = mergedMessageReactionsAndPeers(message: item.message)
let dateReactionsAndPeers = mergedMessageReactionsAndPeers(message: item.topMessage)
for attribute in item.message.attributes {
if let attribute = attribute as? EditedMessageAttribute {