mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-08 19:10:53 +00:00
Message effects improvements
This commit is contained in:
parent
250b5ff2ad
commit
608893e317
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -887,4 +887,7 @@ open class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol {
|
||||
open func contentFrame() -> CGRect {
|
||||
return self.bounds
|
||||
}
|
||||
|
||||
open func playMessageEffect() {
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -614,6 +614,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}, openRecommendedChannelContextMenu: { _, _, _ in
|
||||
}, openGroupBoostInfo: { _, _ in
|
||||
}, openStickerEditor: {
|
||||
}, playMessageEffect: { _ in
|
||||
}, requestMessageUpdate: { _, _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, dismissTextInput: {
|
||||
|
@ -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
|
||||
|
@ -3326,6 +3326,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
}, openRecommendedChannelContextMenu: { _, _, _ in
|
||||
}, openGroupBoostInfo: { _, _ in
|
||||
}, openStickerEditor: {
|
||||
}, playMessageEffect: { _ in
|
||||
}, requestMessageUpdate: { _, _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, dismissTextInput: {
|
||||
|
@ -40,6 +40,7 @@ swift_library(
|
||||
"//submodules/LegacyMediaPickerUI",
|
||||
"//submodules/TelegramAudio",
|
||||
"//submodules/ChatSendMessageActionUI",
|
||||
"//submodules/TelegramUI/Components/ChatControllerInteraction",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -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: "",
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -175,6 +175,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu
|
||||
}, openRecommendedChannelContextMenu: { _, _, _ in
|
||||
}, openGroupBoostInfo: { _, _ in
|
||||
}, openStickerEditor: {
|
||||
}, playMessageEffect: { _ in
|
||||
}, requestMessageUpdate: { _, _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, dismissTextInput: {
|
||||
|
@ -1769,6 +1769,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}, openRecommendedChannelContextMenu: { _, _, _ in
|
||||
}, openGroupBoostInfo: { _, _ in
|
||||
}, openStickerEditor: {
|
||||
}, playMessageEffect: { _ in
|
||||
}, requestMessageUpdate: { _, _ in
|
||||
}, cancelInteractiveKeyboardGestures: {
|
||||
}, dismissTextInput: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user