import Foundation import UIKit import AsyncDisplayKit import Display import Postbox import TelegramCore import TelegramUIPreferences import TelegramPresentationData import AccountContext import ChatMessageBackground import ChatControllerInteraction import ChatHistoryEntry import ChatMessageItemCommon public enum ChatMessageBubbleContentBackgroundHiding { case never case emptyWallpaper case always } public enum ChatMessageBubbleContentAlignment { case none case center } public struct ChatMessageBubbleContentProperties { public let hidesSimpleAuthorHeader: Bool public let headerSpacing: CGFloat public let hidesBackground: ChatMessageBubbleContentBackgroundHiding public let forceFullCorners: Bool public let forceAlignment: ChatMessageBubbleContentAlignment public let shareButtonOffset: CGPoint? public let hidesHeaders: Bool public let avatarOffset: CGFloat? public init( hidesSimpleAuthorHeader: Bool, headerSpacing: CGFloat, hidesBackground: ChatMessageBubbleContentBackgroundHiding, forceFullCorners: Bool, forceAlignment: ChatMessageBubbleContentAlignment, shareButtonOffset: CGPoint? = nil, hidesHeaders: Bool = false, avatarOffset: CGFloat? = nil ) { self.hidesSimpleAuthorHeader = hidesSimpleAuthorHeader self.headerSpacing = headerSpacing self.hidesBackground = hidesBackground self.forceFullCorners = forceFullCorners self.forceAlignment = forceAlignment self.shareButtonOffset = shareButtonOffset self.hidesHeaders = hidesHeaders self.avatarOffset = avatarOffset } } public enum ChatMessageBubbleNoneMergeStatus { case Incoming case Outgoing case None } public enum ChatMessageBubbleMergeStatus { case None(ChatMessageBubbleNoneMergeStatus) case Left case Right case Both } public enum ChatMessageBubbleRelativePosition { public enum NeighbourType { case media case freeform } public enum NeighbourSpacing { case `default` case condensed case overlap(CGFloat) } case None(ChatMessageBubbleMergeStatus) case BubbleNeighbour case Neighbour(Bool, NeighbourType, NeighbourSpacing) } public enum ChatMessageBubbleContentMosaicNeighbor { case merged case mergedBubble case none(tail: Bool) } public struct ChatMessageBubbleContentMosaicPosition { public let topLeft: ChatMessageBubbleContentMosaicNeighbor public let topRight: ChatMessageBubbleContentMosaicNeighbor public let bottomLeft: ChatMessageBubbleContentMosaicNeighbor public let bottomRight: ChatMessageBubbleContentMosaicNeighbor public init(topLeft: ChatMessageBubbleContentMosaicNeighbor, topRight: ChatMessageBubbleContentMosaicNeighbor, bottomLeft: ChatMessageBubbleContentMosaicNeighbor, bottomRight: ChatMessageBubbleContentMosaicNeighbor) { self.topLeft = topLeft self.topRight = topRight self.bottomLeft = bottomLeft self.bottomRight = bottomRight } } public enum ChatMessageBubbleContentPosition { case linear(top: ChatMessageBubbleRelativePosition, bottom: ChatMessageBubbleRelativePosition) case mosaic(position: ChatMessageBubbleContentMosaicPosition, wide: Bool) } public enum ChatMessageBubblePreparePosition { case linear(top: ChatMessageBubbleRelativePosition, bottom: ChatMessageBubbleRelativePosition) case mosaic(top: ChatMessageBubbleRelativePosition, bottom: ChatMessageBubbleRelativePosition) } public enum ChatMessageBubbleContentTapAction { case none case url(url: String, concealed: Bool) case textMention(String) case peerMention(peerId: PeerId, mention: String, openProfile: Bool) case botCommand(String) case hashtag(String?, String) case instantPage case wallpaper case theme case call(peerId: PeerId, isVideo: Bool) case openMessage case timecode(Double, String) case tooltip(String, ASDisplayNode?, CGRect?) case bankCard(String) case ignore case openPollResults(Data) case copy(String) case largeEmoji(String, String?, TelegramMediaFile) case customEmoji(TelegramMediaFile) } public final class ChatMessageBubbleContentItem { public let context: AccountContext public let controllerInteraction: ChatControllerInteraction public let message: Message public let topMessage: Message public let read: Bool public let chatLocation: ChatLocation public let presentationData: ChatPresentationData public let associatedData: ChatMessageItemAssociatedData public let attributes: ChatMessageEntryAttributes public let isItemPinned: Bool public let isItemEdited: Bool public init(context: AccountContext, controllerInteraction: ChatControllerInteraction, message: Message, topMessage: Message, read: Bool, chatLocation: ChatLocation, presentationData: ChatPresentationData, associatedData: ChatMessageItemAssociatedData, attributes: ChatMessageEntryAttributes, isItemPinned: Bool, isItemEdited: Bool) { self.context = context self.controllerInteraction = controllerInteraction self.message = message self.topMessage = topMessage self.read = read self.chatLocation = chatLocation self.presentationData = presentationData self.associatedData = associatedData self.attributes = attributes self.isItemPinned = isItemPinned self.isItemEdited = isItemEdited } } open class ChatMessageBubbleContentNode: ASDisplayNode { open var supportsMosaic: Bool { return false } public weak var bubbleBackgroundNode: ChatMessageBackground? public weak var bubbleBackdropNode: ChatMessageBubbleBackdrop? open var visibility: ListViewItemNodeVisibility = .none public var item: ChatMessageBubbleContentItem? public var updateIsTextSelectionActive: ((Bool) -> Void)? open var disablesClipping: Bool { return false } required public override init() { super.init() } open func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, unboundSize: CGSize?, maxWidth: CGFloat, layout: (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) { preconditionFailure() } open func animateInsertion(_ currentTimestamp: Double, duration: Double) { } open func animateAdded(_ currentTimestamp: Double, duration: Double) { } open func animateRemoved(_ currentTimestamp: Double, duration: Double) { } open func animateInsertionIntoBubble(_ duration: Double) { } open func animateRemovalFromBubble(_ duration: Double, completion: @escaping () -> Void) { self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in completion() }) } open func transitionNode(messageId: MessageId, media: Media, adjustRect: Bool) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? { return nil } open func updateHiddenMedia(_ media: [Media]?) -> Bool { return false } open func updateSearchTextHighlightState(text: String?, messages: [MessageIndex]?) { } open func updateAutomaticMediaDownloadSettings(_ settings: MediaAutoDownloadSettings) { } open func playMediaWithSound() -> ((Double?) -> Void, Bool, Bool, Bool, ASDisplayNode?)? { return nil } open func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { return .none } open func updateTouchesAtPoint(_ point: CGPoint?) { } open func updateHighlightedState(animated: Bool) -> Bool { return false } open func willUpdateIsExtractedToContextPreview(_ value: Bool) { } open func updateIsExtractedToContextPreview(_ value: Bool) { } open func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) { } open func applyAbsoluteOffset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) { } open func applyAbsoluteOffsetSpring(value: CGFloat, duration: Double, damping: CGFloat) { } open func unreadMessageRangeUpdated() { } open func reactionTargetView(value: MessageReaction.Reaction) -> UIView? { return nil } open func targetForStoryTransition(id: StoryId) -> UIView? { return nil } open func getStatusNode() -> ASDisplayNode? { return nil } }