diff --git a/submodules/Display/Source/ContextControllerSourceNode.swift b/submodules/Display/Source/ContextControllerSourceNode.swift index 0bfd0ce673..b01479bfe5 100644 --- a/submodules/Display/Source/ContextControllerSourceNode.swift +++ b/submodules/Display/Source/ContextControllerSourceNode.swift @@ -2,6 +2,12 @@ import Foundation import AsyncDisplayKit open class ContextControllerSourceNode: ContextReferenceContentNode { + public enum ShouldBegin { + case none + case `default` + case customActivationProcess + } + public private(set) var contextGesture: ContextGesture? public var isGestureEnabled: Bool = true { @@ -18,15 +24,19 @@ open class ContextControllerSourceNode: ContextReferenceContentNode { public var activated: ((ContextGesture, CGPoint) -> Void)? public var shouldBegin: ((CGPoint) -> Bool)? + public var shouldBeginWithCustomActivationProcess: ((CGPoint) -> ShouldBegin)? public var customActivationProgress: ((CGFloat, ContextGestureTransition) -> Void)? public weak var additionalActivationProgressLayer: CALayer? public var targetNodeForActivationProgress: ASDisplayNode? public var targetNodeForActivationProgressContentRect: CGRect? + private var ignoreCurrentActivationProcess: Bool = false + public func cancelGesture() { self.contextGesture?.cancel() self.contextGesture?.isEnabled = false self.contextGesture?.isEnabled = self.isGestureEnabled + self.ignoreCurrentActivationProcess = false } override open func didLoad() { @@ -40,10 +50,26 @@ open class ContextControllerSourceNode: ContextReferenceContentNode { contextGesture.isEnabled = self.isGestureEnabled contextGesture.shouldBegin = { [weak self] point in - guard let strongSelf = self, !strongSelf.bounds.width.isZero else { + guard let self, !self.bounds.width.isZero else { return false } - return strongSelf.shouldBegin?(point) ?? true + if let shouldBeginWithCustomActivationProcess = self.shouldBeginWithCustomActivationProcess { + let result = shouldBeginWithCustomActivationProcess(point) + switch result { + case .none: + self.ignoreCurrentActivationProcess = false + return false + case .default: + self.ignoreCurrentActivationProcess = false + return true + case .customActivationProcess: + self.ignoreCurrentActivationProcess = true + return true + } + } else { + self.ignoreCurrentActivationProcess = false + return self.shouldBegin?(point) ?? true + } } contextGesture.activationProgress = { [weak self] progress, update in @@ -52,7 +78,7 @@ open class ContextControllerSourceNode: ContextReferenceContentNode { } if let customActivationProgress = strongSelf.customActivationProgress { customActivationProgress(progress, update) - } else if strongSelf.animateScale { + } else if strongSelf.animateScale && !strongSelf.ignoreCurrentActivationProcess { let targetNode: ASDisplayNode let targetContentRect: CGRect if let targetNodeForActivationProgress = strongSelf.targetNodeForActivationProgress { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift index cfda050e1b..902c24c9ad 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -190,8 +190,8 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { switch action { case .action, .optionalAction: break - case let .openContextMenu(tapMessage, selectAll, subFrame): - item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture, nil) + case let .openContextMenu(openContextMenu): + item.controllerInteraction.openMessageContextMenu(openContextMenu.tapMessage, openContextMenu.selectAll, strongSelf, openContextMenu.subFrame, gesture, nil) } } } @@ -289,7 +289,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if let action = strongSelf.gestureRecognized(gesture: .longTap, location: point, recognizer: recognizer) { switch action { case let .action(f): - f() + f.action() recognizer.cancel() case let .optionalAction(f): f() @@ -1697,14 +1697,14 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } switch action { case let .action(f): - f() + f.action() case let .optionalAction(f): f() - case let .openContextMenu(tapMessage, selectAll, subFrame): + case let .openContextMenu(openContextMenu): if canAddMessageReactions(message: item.message) { item.controllerInteraction.updateMessageReaction(item.message, .default) } else { - item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, self, subFrame, nil, nil) + item.controllerInteraction.openMessageContextMenu(openContextMenu.tapMessage, openContextMenu.selectAll, self, openContextMenu.subFrame, nil, nil) } } } else if case .tap = gesture { @@ -1976,7 +1976,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { item.controllerInteraction.navigateToStory(item.message, attribute.storyId) }) } else if let attribute = attribute as? QuotedReplyMessageAttribute { - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.attemptedNavigationToPrivateQuote(attribute.peerId.flatMap { item.message.peers[$0] }) }) } @@ -2005,7 +2005,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } if forwardInfoNode.hasAction(at: self.view.convert(location, to: forwardInfoNode.view)) { - return .action({}) + return .action(InternalBubbleTapAction.Action {}) } else { return .optionalAction(performAction) } @@ -2246,7 +2246,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { return nil case .longTap, .doubleTap, .secondaryTap: if let item = self.item, self.imageNode.frame.contains(location) { - return .openContextMenu(tapMessage: item.message, selectAll: false, subFrame: self.imageNode.frame) + return .openContextMenu(InternalBubbleTapAction.OpenContextMenu(tapMessage: item.message, selectAll: false, subFrame: self.imageNode.frame)) } case .hold: break diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift index 5492c87f21..59a6d7a543 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift @@ -161,10 +161,12 @@ public struct ChatMessageBubbleContentTapAction { } public var content: Content + public var hasLongTapAction: Bool public var activate: (() -> Promise?)? - public init(content: Content, activate: (() -> Promise?)? = nil) { + public init(content: Content, hasLongTapAction: Bool = true, activate: (() -> Promise?)? = nil) { self.content = content + self.hasLongTapAction = hasLongTapAction self.activate = activate } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 100d9c87d7..3871e5ae29 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -641,30 +641,40 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI //self.addSubnode(self.debugNode) - self.mainContainerNode.shouldBegin = { [weak self] location in + self.mainContainerNode.shouldBeginWithCustomActivationProcess = { [weak self] location in guard let strongSelf = self else { - return false + return .none } if !strongSelf.backgroundNode.frame.contains(location) { - return false + return .none } if strongSelf.selectionNode != nil { - return false + return .none } if let action = strongSelf.gestureRecognized(gesture: .tap, location: location, recognizer: nil) { - if case .action = action { - return false + if case let .action(action) = action, !action.contextMenuOnLongPress { + return .none } } if let action = strongSelf.gestureRecognized(gesture: .longTap, location: location, recognizer: nil) { switch action { - case .action, .optionalAction: - return false - case let .openContextMenu(_, selectAll, _): - return selectAll || strongSelf.contentContainers.count < 2 + case .action: + return .none + case .optionalAction: + return .none + case let .openContextMenu(openContextMenu): + if openContextMenu.selectAll || strongSelf.contentContainers.count < 2 { + if openContextMenu.disableDefaultPressAnimation { + return .customActivationProcess + } else { + return .default + } + } else { + return .none + } } } - return true + return .default } self.mainContainerNode.activated = { [weak self] gesture, location in @@ -676,9 +686,9 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI switch action { case .action, .optionalAction: break - case let .openContextMenu(tapMessage, selectAll, subFrame): - var tapMessage = tapMessage - if selectAll, case let .group(messages) = item.content, tapMessage.text.isEmpty { + case let .openContextMenu(openContextMenu): + var tapMessage = openContextMenu.tapMessage + if openContextMenu.selectAll, case let .group(messages) = item.content, tapMessage.text.isEmpty { for message in messages { if !message.0.text.isEmpty { tapMessage = message.0 @@ -686,7 +696,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } } } - item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture, nil) + item.controllerInteraction.openMessageContextMenu(tapMessage, openContextMenu.selectAll, strongSelf, openContextMenu.subFrame, gesture, nil) } } } @@ -1109,7 +1119,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if let action = strongSelf.gestureRecognized(gesture: .longTap, location: point, recognizer: recognizer) { switch action { case let .action(f): - f() + f.action() recognizer.cancel() case let .optionalAction(f): f() @@ -1128,8 +1138,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI switch action { case .action, .optionalAction: break - case let .openContextMenu(tapMessage, selectAll, subFrame): - item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, nil, point) + case let .openContextMenu(openContextMenu): + item.controllerInteraction.openMessageContextMenu(openContextMenu.tapMessage, openContextMenu.selectAll, strongSelf, openContextMenu.subFrame, nil, point) } } } @@ -3229,8 +3239,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI switch action { case .action, .optionalAction: return false - case let .openContextMenu(_, selectAll, _): - return !selectAll + case let .openContextMenu(openContextMenu): + return !openContextMenu.selectAll } } return true @@ -3893,14 +3903,14 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } switch action { case let .action(f): - f() + f.action() case let .optionalAction(f): f() - case let .openContextMenu(tapMessage, selectAll, subFrame): - if canAddMessageReactions(message: tapMessage) { - item.controllerInteraction.updateMessageReaction(tapMessage, .default) + case let .openContextMenu(openContextMenu): + if canAddMessageReactions(message: openContextMenu.tapMessage) { + item.controllerInteraction.updateMessageReaction(openContextMenu.tapMessage, .default) } else { - item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, self, subFrame, nil, nil) + item.controllerInteraction.openMessageContextMenu(openContextMenu.tapMessage, openContextMenu.selectAll, self, openContextMenu.subFrame, nil, nil) } } } else if case .tap = gesture { @@ -4014,7 +4024,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if let item = self.item { for attribute in item.message.attributes { if let attribute = attribute as? ReplyMessageAttribute { - return .action({ [weak self] in + return .action(InternalBubbleTapAction.Action { [weak self] in guard let self else { return } @@ -4025,11 +4035,11 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.quote?.text, progress: progress)) }) } else if let attribute = attribute as? ReplyStoryAttribute { - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.navigateToStory(item.message, attribute.storyId) }) } else if let attribute = attribute as? QuotedReplyMessageAttribute { - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.attemptedNavigationToPrivateQuote(attribute.peerId.flatMap { item.message.peers[$0] }) }) } @@ -4067,19 +4077,19 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } if forwardInfoNode.hasAction(at: self.view.convert(location, to: forwardInfoNode.view)) { - return .action({}) + return .action(InternalBubbleTapAction.Action {}) } else { return .optionalAction(performAction) } } else if let item = self.item, let story = item.message.media.first(where: { $0 is TelegramMediaStory }) as? TelegramMediaStory { if let storyItem = item.message.associatedStories[story.storyId] { if storyItem.data.isEmpty { - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.navigateToStory(item.message, story.storyId) }) } else { if let peer = item.message.peers[story.storyId.peerId] { - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) }) } @@ -4094,28 +4104,42 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI switch tapAction.content { case .none: if let item = self.item, self.backgroundNode.frame.contains(CGPoint(x: self.frame.width - location.x, y: location.y)), let tapMessage = self.item?.controllerInteraction.tapMessage { - return .action({ + return .action(InternalBubbleTapAction.Action { tapMessage(item.message) }) } case .ignore: if let item = self.item, self.backgroundNode.frame.contains(CGPoint(x: self.frame.width - location.x, y: location.y)), let tapMessage = self.item?.controllerInteraction.tapMessage { - return .action({ + return .action(InternalBubbleTapAction.Action { tapMessage(item.message) }) } else { - return .action({ + return .action(InternalBubbleTapAction.Action { }) } case let .url(url): - return .action({ [weak self] in - guard let self, let item = self.item else { - return + if case .longTap = gesture, !tapAction.hasLongTapAction, let item = self.item { + let tapMessage = item.content.firstMessage + var subFrame = self.backgroundNode.frame + if case .group = item.content { + for contentNode in self.contentNodes { + if contentNode.item?.message.stableId == tapMessage.stableId { + subFrame = contentNode.frame.insetBy(dx: 0.0, dy: -4.0) + break + } + } } - item.controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: url.url, concealed: url.concealed, message: item.content.firstMessage, allowInlineWebpageResolution: url.allowInlineWebpageResolution, progress: tapAction.activate?())) - }) + return .openContextMenu(InternalBubbleTapAction.OpenContextMenu(tapMessage: tapMessage, selectAll: false, subFrame: subFrame, disableDefaultPressAnimation: true)) + } else { + return .action(InternalBubbleTapAction.Action({ [weak self] in + guard let self, let item = self.item else { + return + } + item.controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: url.url, concealed: url.concealed, message: item.content.firstMessage, allowInlineWebpageResolution: url.allowInlineWebpageResolution, progress: tapAction.activate?())) + }, contextMenuOnLongPress: !tapAction.hasLongTapAction)) + } case let .peerMention(peerId, _, openProfile): - return .action({ [weak self] in + return .action(InternalBubbleTapAction.Action { [weak self] in if let item = self?.item { let _ = (item.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> deliverOnMainQueue).startStandalone(next: { peer in @@ -4126,17 +4150,17 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } }) case let .textMention(name): - return .action({ + return .action(InternalBubbleTapAction.Action { self.item?.controllerInteraction.openPeerMention(name) }) case let .botCommand(command): if let item = self.item { - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.sendBotCommand(item.message.id, command) }) } case let .hashtag(peerName, hashtag): - return .action({ + return .action(InternalBubbleTapAction.Action { self.item?.controllerInteraction.openHashtag(peerName, hashtag) }) case .instantPage: @@ -4147,13 +4171,13 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } case .wallpaper: if let item = self.item { - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.openWallpaper(item.message) }) } case .theme: if let item = self.item { - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.openTheme(item.message) }) } @@ -4168,20 +4192,20 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI let _ = item.controllerInteraction.openMessage(item.message, .default) }) } else { - return .action({ + return .action(InternalBubbleTapAction.Action { let _ = item.controllerInteraction.openMessage(item.message, .default) }) } } case let .timecode(timecode, _): if let item = self.item, let mediaMessage = mediaMessage { - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.seekToTimecode(mediaMessage, timecode, forceOpen) }) } case let .bankCard(number): if let item = self.item { - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.longTap(.bankCard(number), item.message) }) } @@ -4223,21 +4247,21 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI let message = item.message if let threadInfoNode = self.threadInfoNode, self.item?.controllerInteraction.tapMessage == nil, threadInfoNode.frame.contains(location) { - return .action({}) + return .action(InternalBubbleTapAction.Action {}) } if let replyInfoNode = self.replyInfoNode, self.item?.controllerInteraction.tapMessage == nil, replyInfoNode.frame.contains(location) { if let item = self.item { for attribute in item.message.attributes { if let attribute = attribute as? ReplyMessageAttribute { - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.quote?.text)) }) } else if let attribute = attribute as? ReplyStoryAttribute { - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.navigateToStory(item.message, attribute.storyId) }) } else if let attribute = attribute as? QuotedReplyMessageAttribute { - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.attemptedNavigationToPrivateQuote(attribute.peerId.flatMap { item.message.peers[$0] }) }) } @@ -4248,6 +4272,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI var tapMessage: Message? = item.content.firstMessage var selectAll = true var hasFiles = false + var disableDefaultPressAnimation = false loop: for contentNode in self.contentNodes { let convertedLocation = self.view.convert(location, to: contentNode.view) @@ -4274,23 +4299,27 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI case .none, .ignore: break case let .url(url): - return .action({ - item.controllerInteraction.longTap(.url(url.url), message) - }) + if tapAction.hasLongTapAction { + return .action(InternalBubbleTapAction.Action({ + item.controllerInteraction.longTap(.url(url.url), message) + }, contextMenuOnLongPress: false)) + } else { + disableDefaultPressAnimation = true + } case let .peerMention(peerId, mention, _): - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.longTap(.peerMention(peerId, mention), message) }) case let .textMention(name): - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.longTap(.mention(name), message) }) case let .botCommand(command): - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.longTap(.command(command), message) }) case let .hashtag(_, hashtag): - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.longTap(.hashtag(hashtag), message) }) case .instantPage: @@ -4305,12 +4334,12 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI break case let .timecode(timecode, text): if let mediaMessage = mediaMessage { - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.longTap(.timecode(timecode, text), mediaMessage) }) } case let .bankCard(number): - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.longTap(.bankCard(number), message) }) case .tooltip: @@ -4335,7 +4364,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } } } - return .openContextMenu(tapMessage: tapMessage, selectAll: selectAll, subFrame: subFrame) + return .openContextMenu(InternalBubbleTapAction.OpenContextMenu(tapMessage: tapMessage, selectAll: selectAll, subFrame: subFrame, disableDefaultPressAnimation: disableDefaultPressAnimation)) } } default: diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift index ec6167071a..3d66ffe92c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift @@ -142,9 +142,9 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureReco switch action { case .action, .optionalAction: break - case let .openContextMenu(tapMessage, selectAll, subFrame): + case let .openContextMenu(openContextMenu): strongSelf.recognizer?.cancel() - item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture, nil) + item.controllerInteraction.openMessageContextMenu(openContextMenu.tapMessage, openContextMenu.selectAll, strongSelf, openContextMenu.subFrame, gesture, nil) if (strongSelf.appliedCurrentlyPlaying ?? false) && strongSelf.interactiveVideoNode.isPlaying { strongSelf.wasPlaying = true strongSelf.interactiveVideoNode.pause() @@ -940,7 +940,7 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureReco } switch action { case let .action(f): - f() + f.action() case let .optionalAction(f): f() case .openContextMenu: @@ -966,7 +966,7 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureReco item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.quote?.text)) }) } else if let attribute = attribute as? QuotedReplyMessageAttribute { - return .action({ + return .action(InternalBubbleTapAction.Action { item.controllerInteraction.attemptedNavigationToPrivateQuote(attribute.peerId.flatMap { item.message.peers[$0] }) }) } @@ -995,7 +995,7 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureReco } if forwardInfoNode.hasAction(at: self.view.convert(location, to: forwardInfoNode.view)) { - return .action({}) + return .action(InternalBubbleTapAction.Action {}) } else { return .optionalAction(performAction) } @@ -1004,7 +1004,7 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureReco return nil case .longTap, .doubleTap, .secondaryTap: if let item = self.item, self.interactiveVideoNode.frame.contains(location) { - return .openContextMenu(tapMessage: item.message, selectAll: false, subFrame: self.interactiveVideoNode.frame) + return .openContextMenu(InternalBubbleTapAction.OpenContextMenu(tapMessage: item.message, selectAll: false, subFrame: self.interactiveVideoNode.frame)) } case .hold: break diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemView/Sources/ChatMessageItemView.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemView/Sources/ChatMessageItemView.swift index ee89750b66..b17022d555 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemView/Sources/ChatMessageItemView.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemView/Sources/ChatMessageItemView.swift @@ -615,9 +615,33 @@ public final class ChatMessageAccessibilityData { } public enum InternalBubbleTapAction { - case action(() -> Void) + public struct Action { + public var action: () -> Void + public var contextMenuOnLongPress: Bool + + public init(_ action: @escaping () -> Void, contextMenuOnLongPress: Bool = false) { + self.action = action + self.contextMenuOnLongPress = contextMenuOnLongPress + } + } + + public struct OpenContextMenu { + public var tapMessage: Message + public var selectAll: Bool + public var subFrame: CGRect + public var disableDefaultPressAnimation: Bool + + public init(tapMessage: Message, selectAll: Bool, subFrame: CGRect, disableDefaultPressAnimation: Bool = false) { + self.tapMessage = tapMessage + self.selectAll = selectAll + self.subFrame = subFrame + self.disableDefaultPressAnimation = disableDefaultPressAnimation + } + } + + case action(Action) case optionalAction(() -> Void) - case openContextMenu(tapMessage: Message, selectAll: Bool, subFrame: CGRect) + case openContextMenu(OpenContextMenu) } open class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift index f31a4623bc..069fc50e52 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift @@ -159,8 +159,8 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { switch action { case .action, .optionalAction: break - case let .openContextMenu(tapMessage, selectAll, subFrame): - item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture, nil) + case let .openContextMenu(openContextMenu): + item.controllerInteraction.openMessageContextMenu(openContextMenu.tapMessage, openContextMenu.selectAll, strongSelf, openContextMenu.subFrame, gesture, nil) } } } @@ -234,7 +234,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { if let action = strongSelf.gestureRecognized(gesture: .longTap, location: point, recognizer: recognizer) { switch action { case let .action(f): - f() + f.action() recognizer.cancel() case let .optionalAction(f): f() @@ -1283,14 +1283,14 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { } switch action { case let .action(f): - f() + f.action() case let .optionalAction(f): f() - case let .openContextMenu(tapMessage, selectAll, subFrame): + case let .openContextMenu(openContextMenu): if canAddMessageReactions(message: item.message) { - item.controllerInteraction.updateMessageReaction(tapMessage, .default) + item.controllerInteraction.updateMessageReaction(openContextMenu.tapMessage, .default) } else { - item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, self, subFrame, nil, nil) + item.controllerInteraction.openMessageContextMenu(openContextMenu.tapMessage, openContextMenu.selectAll, self, openContextMenu.subFrame, nil, nil) } } } else if case .tap = gesture { @@ -1372,7 +1372,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { } if forwardInfoNode.hasAction(at: self.view.convert(location, to: forwardInfoNode.view)) { - return .action({}) + return .action(InternalBubbleTapAction.Action {}) } else { return .optionalAction(performAction) } @@ -1388,7 +1388,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { return nil case .longTap, .doubleTap, .secondaryTap: if let item = self.item, self.imageNode.frame.contains(location) { - return .openContextMenu(tapMessage: item.message, selectAll: false, subFrame: self.imageNode.frame) + return .openContextMenu(InternalBubbleTapAction.OpenContextMenu(tapMessage: item.message, selectAll: false, subFrame: self.imageNode.frame)) } case .hold: break diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift index 27f5d03d33..e241f9d3bd 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift @@ -152,7 +152,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent isConcealed = false } } - return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: content.url, concealed: isConcealed, allowInlineWebpageResolution: true)), activate: { [weak self] in + return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: content.url, concealed: isConcealed, allowInlineWebpageResolution: true)), hasLongTapAction: false, activate: { [weak self] in guard let self else { return nil }