Files
Swiftgram/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessageQuizAnswerBubbleContentNode.swift
2026-03-27 01:13:38 +01:00

139 lines
6.9 KiB
Swift

import Foundation
import UIKit
import Postbox
import Display
import AsyncDisplayKit
import SwiftSignalKit
import TelegramCore
import ChatMessageBubbleContentNode
import ChatMessageItemCommon
import ChatMessageAttachedContentNode
import ChatControllerInteraction
import ComposePollScreen
import AccountContext
public final class ChatMessageQuizAnswerBubbleContentNode: ChatMessageBubbleContentNode {
private let contentNode: ChatMessageAttachedContentNode
private let temporaryHiddenMediaDisposable = MetaDisposable()
override public var visibility: ListViewItemNodeVisibility {
didSet {
self.contentNode.visibility = visibility
}
}
required public init() {
self.contentNode = ChatMessageAttachedContentNode()
super.init()
self.clipsToBounds = true
self.addSubnode(self.contentNode)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.temporaryHiddenMediaDisposable.dispose()
}
override public func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, CGSize?, CGFloat, (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) {
let contentNodeLayout = self.contentNode.asyncLayout()
return { item, layoutConstants, preparePosition, _, constrainedSize, _ in
let title: String = item.presentationData.strings.MessagePoll_Explanation
var text: String = ""
var entities: [MessageTextEntity] = []
var mediaAndFlags: ([Media], ChatMessageAttachedContentNodeMediaFlags)? = nil
var solution: TelegramMediaPollResults.Solution?
if let poll = item.message.media.first(where: { $0 is TelegramMediaPoll }) as? TelegramMediaPoll, let solutionValue = poll.results.solution {
text = solutionValue.text
entities = solutionValue.entities
mediaAndFlags = solutionValue.media.flatMap { ([$0], []) }
solution = solutionValue
}
let (initialWidth, continueLayout) = contentNodeLayout(item.presentationData, item.controllerInteraction.automaticMediaDownloadSettings, item.associatedData, item.attributes, item.context, item.controllerInteraction, item.message, true, .peer(id: item.message.id.peerId), title, nil, nil, text, entities, mediaAndFlags, nil, nil, nil, true, layoutConstants, preparePosition, constrainedSize, item.controllerInteraction.presentationContext.animationCache, item.controllerInteraction.presentationContext.animationRenderer)
let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 8.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none)
return (contentProperties, nil, initialWidth, { constrainedSize, position in
let (refinedWidth, finalizeLayout) = continueLayout(constrainedSize, position)
return (refinedWidth, { boundingWidth in
let (size, apply) = finalizeLayout(boundingWidth)
return (size, { [weak self] animation, synchronousLoads, applyInfo in
if let strongSelf = self {
strongSelf.item = item
apply(animation, synchronousLoads, applyInfo)
strongSelf.contentNode.frame = CGRect(origin: CGPoint(), size: size)
strongSelf.contentNode.openMedia = { [weak self] _ in
if let item = self?.item, let solution {
item.controllerInteraction.openPollMedia(item.message, .solution(solution))
}
}
}
})
})
})
}
}
override public func animateInsertion(_ currentTimestamp: Double, duration: Double) {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
}
override public func animateAdded(_ currentTimestamp: Double, duration: Double) {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
}
override public func animateRemoved(_ currentTimestamp: Double, duration: Double) {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
}
override public func animateRemovalFromBubble(_ duration: Double, completion: @escaping () -> Void) {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
completion()
})
self.layer.animateBounds(from: self.bounds, to: CGRect(origin: .zero, size: CGSize(width: self.bounds.width, height: 0.0)), duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
}
override public func animateInsertionIntoBubble(_ duration: Double) {
self.layer.animateBounds(from: CGRect(origin: .zero, size: CGSize(width: self.bounds.width, height: 0.0)), to: self.bounds, duration: 0.25, timingFunction: kCAMediaTimingFunctionSpring)
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
}
override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction {
if self.bounds.contains(point) {
let contentNodeFrame = self.contentNode.frame
return self.contentNode.tapActionAtPoint(point.offsetBy(dx: -contentNodeFrame.minX, dy: -contentNodeFrame.minY), gesture: gesture, isEstimating: isEstimating)
}
return ChatMessageBubbleContentTapAction(content: .none)
}
override public func updateTouchesAtPoint(_ point: CGPoint?) {
let contentNodeFrame = self.contentNode.frame
self.contentNode.updateTouchesAtPoint(point.flatMap { $0.offsetBy(dx: -contentNodeFrame.minX, dy: -contentNodeFrame.minY) })
}
override public func updateHiddenMedia(_ media: [Media]?) -> Bool {
return self.contentNode.updateHiddenMedia(media)
}
override public func transitionNode(messageId: MessageId, media: Media, adjustRect: Bool) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
if self.item?.message.id != messageId {
return nil
}
return self.contentNode.transitionNode(media: media)
}
}