mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Reaction improvements
This commit is contained in:
parent
884a31987c
commit
42773dbe01
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
return
|
||||
}
|
||||
|
||||
item.controllerInteraction.openMessageReactionContextMenu(item.message, sourceNode, gesture, value)
|
||||
item.controllerInteraction.openMessageReactionContextMenu(item.topMessage, sourceNode, gesture, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user