WinterGram/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageActionSheetController.swift
isaac 1d8931b334 Render markdown rich-text preview in send-message context screen
When the user long-presses Send in the main chat and the composed text passes the
same rich-markdown gate used at send time (richMarkdownAttributeIfNeeded), the
send-options preview bubble renders the message as rich InstantPage content,
crossfading from the raw text as the menu animates in. Plain text and custom chat
contents / edit previews are unchanged.

ChatSendMessageActionUI cannot depend on InstantPageUI (it closes a dependency
cycle via LocationUI -> AttachmentUI -> ... -> ChatTextInputActionButtonsNode), so
the rich view is built in TelegramUI (ChatSendMessageRichTextPreview, wrapping
InstantPageV2View + the outgoing message theme) and injected into the screen via a
new ChatSendMessageContextScreenRichTextPreview protocol, mirroring the existing
media-preview pattern. The main-chat send call site classifies the compose text
and injects the preview; MessageItemView lays it out within the send-button-bounded
bubble envelope and crossfades between the raw text and the rich layout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 15:46:16 +02:00

118 lines
4.9 KiB
Swift

import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import TelegramPresentationData
import AccountContext
import ContextUI
import TelegramCore
import TextFormat
import ReactionSelectionNode
import WallpaperBackgroundNode
public enum SendMessageActionSheetControllerParams {
public final class SendMessage {
public let isScheduledMessages: Bool
public let mediaPreview: ChatSendMessageContextScreenMediaPreview?
public let mediaCaptionIsAbove: (Bool, (Bool) -> Void)?
public let messageEffect: (ChatSendMessageActionSheetControllerSendParameters.Effect?, (ChatSendMessageActionSheetControllerSendParameters.Effect?) -> Void)?
public let attachment: Bool
public let canSendWhenOnline: Bool
public let forwardMessageIds: [EngineMessage.Id]
public let canMakePaidContent: Bool
public let currentPrice: Int64?
public let hasTimers: Bool
public let sendPaidMessageStars: StarsAmount?
public let isMonoforum: Bool
public init(
isScheduledMessages: Bool,
mediaPreview: ChatSendMessageContextScreenMediaPreview?,
mediaCaptionIsAbove: (Bool, (Bool) -> Void)?,
messageEffect: (ChatSendMessageActionSheetControllerSendParameters.Effect?, (ChatSendMessageActionSheetControllerSendParameters.Effect?) -> Void)?,
attachment: Bool,
canSendWhenOnline: Bool,
forwardMessageIds: [EngineMessage.Id],
canMakePaidContent: Bool,
currentPrice: Int64?,
hasTimers: Bool,
sendPaidMessageStars: StarsAmount?,
isMonoforum: Bool
) {
self.isScheduledMessages = isScheduledMessages
self.mediaPreview = mediaPreview
self.mediaCaptionIsAbove = mediaCaptionIsAbove
self.messageEffect = messageEffect
self.attachment = attachment
self.canSendWhenOnline = canSendWhenOnline
self.forwardMessageIds = forwardMessageIds
self.canMakePaidContent = canMakePaidContent
self.currentPrice = currentPrice
self.hasTimers = hasTimers
self.sendPaidMessageStars = sendPaidMessageStars
self.isMonoforum = isMonoforum
}
}
public final class EditMessage {
public let messages: [EngineMessage]
public let mediaPreview: ChatSendMessageContextScreenMediaPreview?
public let mediaCaptionIsAbove: (Bool, (Bool) -> Void)?
public init(messages: [EngineMessage], mediaPreview: ChatSendMessageContextScreenMediaPreview?, mediaCaptionIsAbove: (Bool, (Bool) -> Void)?) {
self.messages = messages
self.mediaPreview = mediaPreview
self.mediaCaptionIsAbove = mediaCaptionIsAbove
}
}
case sendMessage(SendMessage)
case editMessage(EditMessage)
}
public func makeChatSendMessageActionSheetController(
initialData: ChatSendMessageContextScreen.InitialData,
context: AccountContext,
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
peerId: EnginePeer.Id?,
params: SendMessageActionSheetControllerParams,
hasEntityKeyboard: Bool,
gesture: ContextGesture?,
sourceSendButton: UIView,
textInputView: UITextView?,
emojiViewProvider: ((ChatTextInputTextCustomEmojiAttribute) -> UIView)?,
wallpaperBackgroundNode: WallpaperBackgroundNode? = nil,
completion: @escaping () -> Void,
sendMessage: @escaping (ChatSendMessageActionSheetController.SendMode, ChatSendMessageActionSheetController.SendParameters?) -> Void,
schedule: @escaping (ChatSendMessageActionSheetController.SendParameters?) -> Void,
editPrice: @escaping (Int64) -> Void,
openPremiumPaywall: @escaping (ViewController) -> Void,
reactionItems: [ReactionItem]? = nil,
availableMessageEffects: AvailableMessageEffects? = nil,
isPremium: Bool = false,
richTextPreview: ChatSendMessageContextScreenRichTextPreview? = nil
) -> ChatSendMessageActionSheetController {
return ChatSendMessageContextScreen(
initialData: initialData,
context: context,
updatedPresentationData: updatedPresentationData,
peerId: peerId,
params: params,
hasEntityKeyboard: hasEntityKeyboard,
gesture: gesture,
sourceSendButton: sourceSendButton,
textInputView: textInputView,
emojiViewProvider: emojiViewProvider,
wallpaperBackgroundNode: wallpaperBackgroundNode,
completion: completion,
sendMessage: sendMessage,
schedule: schedule,
editPrice: editPrice,
openPremiumPaywall: openPremiumPaywall,
reactionItems: reactionItems,
availableMessageEffects: availableMessageEffects,
isPremium: isPremium,
richTextPreview: richTextPreview
)
}