From 42773dbe01ac0630e9e7f6216a83f8ef68a0c8fa Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 19 Dec 2021 19:12:19 +0400 Subject: [PATCH] Reaction improvements --- .../ReactionListContextMenuContent.swift | 18 ++++++++++++- .../Sources/ReactionContextNode.swift | 25 ++++++++++++++----- .../TelegramUI/Sources/ChatController.swift | 18 ++++++++++--- .../ChatMessageAnimatedStickerItemNode.swift | 4 +-- .../ChatMessageAttachedContentNode.swift | 11 ++++++-- .../Sources/ChatMessageBubbleItemNode.swift | 14 ++++++++--- .../ChatMessageContactBubbleContentNode.swift | 2 +- .../ChatMessageDateAndStatusNode.swift | 2 +- .../ChatMessageFileBubbleContentNode.swift | 5 +++- .../ChatMessageInteractiveFileNode.swift | 7 ++++++ ...hatMessageReactionsFooterContentNode.swift | 4 +-- .../Sources/ChatMessageStickerItemNode.swift | 4 +-- .../ChatMessageTextBubbleContentNode.swift | 4 +-- 13 files changed, 91 insertions(+), 27 deletions(-) diff --git a/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift b/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift index c4bf2af919..e6e5740c51 100644 --- a/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift +++ b/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift @@ -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 diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index 1bcd8f363c..7686ab1d2d 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -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() diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index bfa1038a69..6428918210 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index d734e96750..f7beb9ea94 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -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 { diff --git a/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift index 27785eca94..a2e3bf89ae 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift @@ -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() } diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index de9e6b6496..c6eb92d433 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -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 } diff --git a/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift index 1869f576fb..e89c1e0dca 100644 --- a/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift @@ -55,7 +55,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { return } - item.controllerInteraction.openMessageReactionContextMenu(item.message, sourceNode, gesture, value) + item.controllerInteraction.openMessageReactionContextMenu(item.topMessage, sourceNode, gesture, value) } } diff --git a/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift index 016f17676e..f1a4707ab8 100644 --- a/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageDateAndStatusNode.swift @@ -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 } diff --git a/submodules/TelegramUI/Sources/ChatMessageFileBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageFileBubbleContentNode.swift index 827b30dc56..f09a642e57 100644 --- a/submodules/TelegramUI/Sources/ChatMessageFileBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageFileBubbleContentNode.swift @@ -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) } diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift index 260c025ce0..393f87cc06 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift @@ -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 + } } diff --git a/submodules/TelegramUI/Sources/ChatMessageReactionsFooterContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageReactionsFooterContentNode.swift index 41b56929a6..db8d90b2d0 100644 --- a/submodules/TelegramUI/Sources/ChatMessageReactionsFooterContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageReactionsFooterContentNode.swift @@ -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) } } diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index 19a03cd5ed..619222d1f8 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -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 { diff --git a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift index 5cb4c3be32..b9f7a5caef 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift @@ -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 {