2024-05-03 22:56:50 +04:00

310 lines
10 KiB
Swift

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
import SwiftSignalKit
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 let isDetached: Bool
public init(
hidesSimpleAuthorHeader: Bool,
headerSpacing: CGFloat,
hidesBackground: ChatMessageBubbleContentBackgroundHiding,
forceFullCorners: Bool,
forceAlignment: ChatMessageBubbleContentAlignment,
shareButtonOffset: CGPoint? = nil,
hidesHeaders: Bool = false,
avatarOffset: CGFloat? = nil,
isDetached: Bool = false
) {
self.hidesSimpleAuthorHeader = hidesSimpleAuthorHeader
self.headerSpacing = headerSpacing
self.hidesBackground = hidesBackground
self.forceFullCorners = forceFullCorners
self.forceAlignment = forceAlignment
self.shareButtonOffset = shareButtonOffset
self.hidesHeaders = hidesHeaders
self.avatarOffset = avatarOffset
self.isDetached = isDetached
}
}
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 header
case footer
case text
case reactions
}
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 struct ChatMessageBubbleContentTapAction {
public struct Url {
public var url: String
public var concealed: Bool
public var allowInlineWebpageResolution: Bool
public init(
url: String,
concealed: Bool,
allowInlineWebpageResolution: Bool = false
) {
self.url = url
self.concealed = concealed
self.allowInlineWebpageResolution = allowInlineWebpageResolution
}
}
public enum Content {
case none
case url(Url)
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 var content: Content
public var hasLongTapAction: Bool
public var activate: (() -> Promise<Bool>?)?
public init(content: Content, hasLongTapAction: Bool = true, activate: (() -> Promise<Bool>?)? = nil) {
self.content = content
self.hasLongTapAction = hasLongTapAction
self.activate = activate
}
}
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 itemNode: ChatMessageItemNodeProtocol?
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 ChatMessageBubbleContentTapAction(content: .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 messageEffectTargetView() -> UIView? {
return nil
}
open func targetForStoryTransition(id: StoryId) -> UIView? {
return nil
}
open func getStatusNode() -> ASDisplayNode? {
return nil
}
}