Message effects improvements

This commit is contained in:
Isaac 2024-05-14 15:48:07 +04:00
parent 250b5ff2ad
commit 608893e317
16 changed files with 116 additions and 7 deletions

View File

@ -1309,6 +1309,12 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
}
controllerInteraction.activateMessagePinch(sourceNode)
}
contentMedia.playMessageEffect = { [weak controllerInteraction] message in
guard let controllerInteraction else {
return
}
controllerInteraction.playMessageEffect(message)
}
contentMedia.activateLocalContent = { [weak self] mode in
guard let self else {
return

View File

@ -3980,6 +3980,17 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
}
let absoluteOrigin = mosaicStatusOrigin.offsetBy(dx: contentOrigin.x, dy: contentOrigin.y)
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)
if item.message.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects) != nil {
mosaicStatusNode.pressed = { [weak strongSelf] in
guard let strongSelf, let item = strongSelf.item else {
return
}
item.controllerInteraction.playMessageEffect(item.message)
}
} else {
mosaicStatusNode.pressed = nil
}
} else if let mosaicStatusNode = strongSelf.mosaicStatusNode {
strongSelf.mosaicStatusNode = nil
mosaicStatusNode.removeFromSupernode()
@ -4886,6 +4897,12 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
}
}
if let mosaicStatusNode = self.mosaicStatusNode {
if let result = mosaicStatusNode.hitTest(self.view.convert(point, to: mosaicStatusNode.view), with: event) {
return result
}
}
for contentNode in self.contentNodes {
if let result = contentNode.hitTest(self.view.convert(point, to: contentNode.view), with: event) {
return result
@ -5929,6 +5946,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
}
}
override public func playMessageEffect() {
self.playMessageEffect(force: true)
}
private func updateVisibility() {
guard let item = self.item else {
return

View File

@ -564,6 +564,8 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
isReplyThread = true
}
let messageEffect = item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects)
let statusSuggestedWidthAndContinue = makeDateAndStatusLayout(ChatMessageDateAndStatusNode.Arguments(
context: item.context,
presentationData: item.presentationData,
@ -579,7 +581,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
reactionPeers: dateReactionsAndPeers.peers,
displayAllReactionPeers: item.message.id.peerId.namespace == Namespaces.Peer.CloudUser,
areReactionsTags: item.topMessage.areReactionsTags(accountPeerId: item.context.account.peerId),
messageEffect: item.topMessage.messageEffect(availableMessageEffects: item.associatedData.availableMessageEffects),
messageEffect: messageEffect,
replyCount: dateReplies,
isPinned: item.message.tags.contains(.pinned) && !item.associatedData.isInPinnedListMode && !isReplyThread,
hasAutoremove: item.message.isSelfExpiring,
@ -997,6 +999,13 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
}
item.controllerInteraction.displayImportedMessageTooltip(strongSelf.dateAndStatusNode)
}
} else if messageEffect != nil {
strongSelf.dateAndStatusNode.pressed = { [weak strongSelf] in
guard let strongSelf, let item = strongSelf.item else {
return
}
item.controllerInteraction.playMessageEffect(item.message)
}
} else {
strongSelf.dateAndStatusNode.pressed = nil
}
@ -1630,6 +1639,11 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
if let statusNode = self.statusNode, statusNode.supernode != nil, !statusNode.isHidden, statusNode.frame.contains(point) {
return self.view
}
if self.dateAndStatusNode.supernode != nil, !self.dateAndStatusNode.isHidden {
if let result = self.dateAndStatusNode.hitTest(self.view.convert(point, to: self.dateAndStatusNode.view), with: event) {
return result
}
}
if let videoNode = self.videoNode, videoNode.view.frame.contains(point) {
return self.view

View File

@ -456,6 +456,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
public var activateLocalContent: (InteractiveMediaNodeActivateContent) -> Void = { _ in }
public var activatePinch: ((PinchSourceContainerNode) -> Void)?
public var updateMessageReaction: ((Message, ChatControllerInteractionReaction, Bool, ContextExtractedContentContainingView?) -> Void)?
public var playMessageEffect: ((Message) -> Void)?
override public init() {
self.pinchContainerNode = PinchSourceContainerNode()
@ -637,6 +638,11 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
}
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let result = super.hitTest(point, with: event)
return result
}
@objc private func imageTap(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
let point = recognizer.location(in: self.imageNode.view)
@ -857,6 +863,8 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
var statusSize = CGSize()
var statusApply: ((ListViewItemUpdateAnimation) -> Void)?
let messageEffect = message.messageEffect(availableMessageEffects: associatedData.availableMessageEffects)
if let dateAndStatus = dateAndStatus {
let statusSuggestedWidthAndContinue = statusLayout(ChatMessageDateAndStatusNode.Arguments(
@ -874,7 +882,7 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
reactionPeers: dateAndStatus.dateReactionPeers,
displayAllReactionPeers: message.id.peerId.namespace == Namespaces.Peer.CloudUser,
areReactionsTags: message.areReactionsTags(accountPeerId: context.account.peerId),
messageEffect: message.messageEffect(availableMessageEffects: associatedData.availableMessageEffects),
messageEffect: messageEffect,
replyCount: dateAndStatus.dateReplies,
isPinned: dateAndStatus.isPinned,
hasAutoremove: message.isSelfExpiring,
@ -1465,8 +1473,20 @@ public final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTr
transition.animator.updateFrame(layer: strongSelf.dateAndStatusNode.layer, frame: dateAndStatusFrame, completion: nil)
statusApply(transition)
}
if messageEffect != nil {
strongSelf.dateAndStatusNode.pressed = { [weak strongSelf] in
guard let strongSelf, let message = strongSelf.message else {
return
}
strongSelf.playMessageEffect?(message)
}
} else {
strongSelf.dateAndStatusNode.pressed = nil
}
} else if strongSelf.dateAndStatusNode.supernode != nil {
strongSelf.dateAndStatusNode.removeFromSupernode()
strongSelf.dateAndStatusNode.pressed = nil
}
if let statusNode = strongSelf.statusNode {

View File

@ -887,4 +887,7 @@ open class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol {
open func contentFrame() -> CGRect {
return self.bounds
}
open func playMessageEffect() {
}
}

View File

@ -71,6 +71,12 @@ public class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
}
strongSelf.item?.controllerInteraction.activateMessagePinch(sourceNode)
}
self.interactiveImageNode.playMessageEffect = { [weak self] message in
guard let strongSelf = self, let _ = strongSelf.item else {
return
}
strongSelf.item?.controllerInteraction.playMessageEffect(message)
}
}
required public init?(coder aDecoder: NSCoder) {
@ -403,8 +409,6 @@ public class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
}
item.controllerInteraction.displayImportedMessageTooltip(strongSelf.interactiveImageNode.dateAndStatusNode)
}
} else {
strongSelf.interactiveImageNode.dateAndStatusNode.pressed = nil
}
}
})

View File

@ -614,6 +614,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
}, openRecommendedChannelContextMenu: { _, _, _ in
}, openGroupBoostInfo: { _, _ in
}, openStickerEditor: {
}, playMessageEffect: { _ in
}, requestMessageUpdate: { _, _ in
}, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: {

View File

@ -242,6 +242,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
public let openRecommendedChannelContextMenu: (EnginePeer, UIView, ContextGesture?) -> Void
public let openGroupBoostInfo: (EnginePeer.Id?, Int) -> Void
public let openStickerEditor: () -> Void
public let playMessageEffect: (Message) -> Void
public let requestMessageUpdate: (MessageId, Bool) -> Void
public let cancelInteractiveKeyboardGestures: () -> Void
@ -367,6 +368,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
openRecommendedChannelContextMenu: @escaping (EnginePeer, UIView, ContextGesture?) -> Void,
openGroupBoostInfo: @escaping (EnginePeer.Id?, Int) -> Void,
openStickerEditor: @escaping () -> Void,
playMessageEffect: @escaping (Message) -> Void,
requestMessageUpdate: @escaping (MessageId, Bool) -> Void,
cancelInteractiveKeyboardGestures: @escaping () -> Void,
dismissTextInput: @escaping () -> Void,
@ -472,6 +474,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol
self.openRecommendedChannelContextMenu = openRecommendedChannelContextMenu
self.openGroupBoostInfo = openGroupBoostInfo
self.openStickerEditor = openStickerEditor
self.playMessageEffect = playMessageEffect
self.requestMessageUpdate = requestMessageUpdate
self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures

View File

@ -3326,6 +3326,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
}, openRecommendedChannelContextMenu: { _, _, _ in
}, openGroupBoostInfo: { _, _ in
}, openStickerEditor: {
}, playMessageEffect: { _ in
}, requestMessageUpdate: { _, _ in
}, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: {

View File

@ -40,6 +40,7 @@ swift_library(
"//submodules/LegacyMediaPickerUI",
"//submodules/TelegramAudio",
"//submodules/ChatSendMessageActionUI",
"//submodules/TelegramUI/Components/ChatControllerInteraction",
],
visibility = [
"//visibility:public",

View File

@ -28,6 +28,7 @@ import ImageCompression
import LegacyMediaPickerUI
import TelegramAudio
import ChatSendMessageActionUI
import ChatControllerInteraction
struct CameraState: Equatable {
enum Recording: Equatable {
@ -1467,7 +1468,7 @@ public class VideoMessageCameraScreen: ViewController {
fileprivate var didSend = false
fileprivate var lastActionTimestamp: Double?
fileprivate var isSendingImmediately = false
public func sendVideoRecording(silentPosting: Bool? = nil, scheduleTime: Int32? = nil) {
public func sendVideoRecording(silentPosting: Bool? = nil, scheduleTime: Int32? = nil, messageEffect: ChatSendMessageEffect? = nil) {
guard !self.didSend else {
return
}
@ -1613,11 +1614,13 @@ public class VideoMessageCameraScreen: ViewController {
let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.FileName(fileName: "video.mp4"), .Video(duration: finalDuration, size: video.dimensions, flags: [.instantRoundVideo], preloadSize: nil)])
var attributes: [MessageAttribute] = []
if self.cameraState.isViewOnceEnabled {
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: viewOnceTimeout, countdownBeginTime: nil))
}
if let messageEffect {
attributes.append(EffectMessageAttribute(id: messageEffect.id))
}
self.completion(.message(
text: "",

View File

@ -517,6 +517,9 @@ extension ChatControllerImpl {
if viewOnce {
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: viewOnceTimeout, countdownBeginTime: nil))
}
if let messageEffect {
attributes.append(EffectMessageAttribute(id: messageEffect.id))
}
let messages: [EnqueueMessage] = [.message(text: "", attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: audio.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int64(audio.fileSize), attributes: [.Audio(isVoice: true, duration: Int(audio.duration), title: nil, performer: nil, waveform: waveformBuffer)])), threadId: self.chatLocation.threadId, replyToMessageId: self.presentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]
@ -542,7 +545,7 @@ extension ChatControllerImpl {
donateSendMessageIntent(account: self.context.account, sharedContext: self.context.sharedContext, intentContext: .chat, peerIds: [peerId])
case .video:
self.videoRecorderValue?.sendVideoRecording(silentPosting: silentPosting, scheduleTime: scheduleTime)
self.videoRecorderValue?.sendVideoRecording(silentPosting: silentPosting, scheduleTime: scheduleTime, messageEffect: messageEffect)
}
}
}

View File

@ -0,0 +1,22 @@
import Foundation
import UIKit
import Display
import SwiftSignalKit
import Postbox
import TelegramCore
import TelegramUIPreferences
import AccountContext
import ChatMessageItemView
extension ChatControllerImpl {
func playMessageEffect(message: Message) {
var messageItemNode: ChatMessageItemView?
self.chatDisplayNode.historyNode.forEachVisibleMessageItemNode { itemNode in
if let item = itemNode.item, item.message.id == message.id {
messageItemNode = itemNode
}
}
messageItemNode?.playMessageEffect()
}
}

View File

@ -4629,6 +4629,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
return
}
self.openStickerEditor()
}, playMessageEffect: { [weak self] message in
guard let self else {
return
}
self.playMessageEffect(message: message)
}, requestMessageUpdate: { [weak self] id, scroll in
if let self {
self.chatDisplayNode.historyNode.requestMessageUpdate(id, andScrollToItem: scroll)

View File

@ -175,6 +175,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
}, openRecommendedChannelContextMenu: { _, _, _ in
}, openGroupBoostInfo: { _, _ in
}, openStickerEditor: {
}, playMessageEffect: { _ in
}, requestMessageUpdate: { _, _ in
}, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: {

View File

@ -1769,6 +1769,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}, openRecommendedChannelContextMenu: { _, _, _ in
}, openGroupBoostInfo: { _, _ in
}, openStickerEditor: {
}, playMessageEffect: { _ in
}, requestMessageUpdate: { _, _ in
}, cancelInteractiveKeyboardGestures: {
}, dismissTextInput: {