Files
Telegram-iOS/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessageQuizAnswerBubbleContentNode.swift
T
isaacisaacClaude Opus 4.7
e918b353ec Postbox refactor waves 278-356: squash
Squashes 79 sequential refactor waves into a single commit.

Highlights:
- Drop `import Postbox` (and matching `//submodules/Postbox` BUILD deps) from a wide swath of consumer files: ChatList, ChatMessage*BubbleContentNode subclasses, ListMessageNode/ListMessageItem, GalleryData, ChatHistorySearchContainerNode, ChatPanelInterfaceInteraction, ChatControllerInteraction, ChatMessageStickerItemNode, ChatMessageReplyInfoNode, ChatMessageInstantVideoItemNode, ChatPresentationInterfaceState, BrowserMarkdown/Readability, MediaResources, LocalMediaResources, ICloudResources, FetchManager, ShareController, OpenChatMessage, GalleryController, GroupStickerSearchContainerNode, GroupStickerPackCurrentItem, ChatPinnedMessageTitlePanelNode, OverlayAudioPlayerController, PresentationThemeSettings, StatisticsUI/StoryIconNode, TextFormat/StringWithAppliedEntities, GalleryUI/VideoAdComponent, StickerPackPreviewUI, WallpaperPreviewMedia, WallpaperResources, YoutubeEmbedImplementation, InstantPageExternalMediaResource, PlatformRestrictionMatching, TelegramUIDeclareEncodables, ChatListNode/ChatListSearchContainerNode.
- Add `TelegramEngine` facades: `Themes.wallpapers`, `Themes.themes`, `AccountData.addAppLogEvent`.
- Add `EngineMessageHistoryEntryLocation` wrapper.
- Add `EngineRaw*` escape-hatch typealiases (`EngineRawMessage`, `EngineRawPeer`, `EngineRawMedia`, `EngineRawMediaResource`, `EngineRawMediaResourceData`, `EngineRawItemCollectionItem`, `EngineRawItemCollectionInfo`) and `engineDeclareEncodable` forwarder.
- Drop unused `account:`/`postbox:`/`network:` parameters from several public functions and delete the dead overloads/types/functions left over: `automaticThemeShouldSwitchNow`, `cancelFreeMediaFileInteractiveFetch`, `legacyEnqueueVideoMessage`, `TelegramMediaFileReference`, plus assorted dead public TelegramCore/AccountContext SecureId entry points.
- Delete entire dead modules: `LegacyDataImport`, `TonBinding`, `SpotlightSupport`, `SvgRendering`, third-party `AppCenter`/`VectorPlus`/`SwiftColor`/`SwiftSVG`.
- Drop orphan `//submodules/Postbox` BUILD deps across 3 cleanup rounds.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 14:11:06 +02:00

138 lines
6.9 KiB
Swift

import Foundation
import UIKit
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: ([EngineRawMedia], 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: [EngineRawMedia]?) -> Bool {
return self.contentNode.updateHiddenMedia(media)
}
override public func transitionNode(messageId: EngineMessage.Id, media: EngineRawMedia, adjustRect: Bool) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
if self.item?.message.id != messageId {
return nil
}
return self.contentNode.transitionNode(media: media)
}
}