From fe791128d6e0206787801e962b437132e75943c9 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 27 Mar 2026 00:46:53 +0800 Subject: [PATCH] Various improvements --- .../Display/Source/SparseContainerView.swift | 19 ++ .../Sources/InteractiveTextComponent.swift | 60 ++++- .../InteractiveTextNodeWithEntities.swift | 254 ++++++++++++++++++ .../Components/TextProcessingScreen/BUILD | 1 + .../TextProcessingTextAreaComponent.swift | 143 ++++++++-- ...tProcessingTranslateContentComponent.swift | 21 +- 6 files changed, 452 insertions(+), 46 deletions(-) diff --git a/submodules/Display/Source/SparseContainerView.swift b/submodules/Display/Source/SparseContainerView.swift index e9f2997735..949deee949 100644 --- a/submodules/Display/Source/SparseContainerView.swift +++ b/submodules/Display/Source/SparseContainerView.swift @@ -45,3 +45,22 @@ open class SparseContainerView: UIView { } } } + +open class TransparentHitView: UIView { + public var onTap: (() -> Void)? + + override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + if let event { + var eventIsPresses = false + if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { + eventIsPresses = event.type == .presses + } + if event.type == .touches || eventIsPresses { + self.onTap?() + return nil + } + } + + return super.hitTest(point, with: event) + } +} diff --git a/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift b/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift index 9cfa0551db..48c25ec2d9 100644 --- a/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift +++ b/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift @@ -56,12 +56,21 @@ private let expandableBlockMaskImage: UIImage = { }() private final class InteractiveTextNodeStrikethrough { + enum Style { + case single + case wavy + } + let range: NSRange let frame: CGRect - - init(range: NSRange, frame: CGRect) { + let color: UIColor? + let style: Style + + init(range: NSRange, frame: CGRect, color: UIColor? = nil, style: Style = .single) { self.range = range self.frame = frame + self.color = color + self.style = style } } @@ -1814,11 +1823,11 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn let upperX = ceil(CTLineGetOffsetForStringIndex(line.line, range.location + range.length, nil)) let x = lowerX < upperX ? lowerX : upperX line.strikethroughs.append(InteractiveTextNodeStrikethrough(range: range, frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: line.frame.height))) - } else if let _ = attributes[NSAttributedString.Key.underlineStyle] { + } else if let underlineStyle = attributes[NSAttributedString.Key.underlineStyle] as? Int { let lowerX = floor(CTLineGetOffsetForStringIndex(line.line, range.location, nil)) let upperX = ceil(CTLineGetOffsetForStringIndex(line.line, range.location + range.length, nil)) let x = lowerX < upperX ? lowerX : upperX - line.underlines.append(InteractiveTextNodeStrikethrough(range: range, frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: line.frame.height))) + line.underlines.append(InteractiveTextNodeStrikethrough(range: range, frame: CGRect(x: x, y: 0.0, width: abs(upperX - lowerX), height: line.frame.height), color: attributes[NSAttributedString.Key.underlineColor] as? UIColor, style: underlineStyle == NSUnderlineStyle.patternDot.rawValue ? .wavy : .single)) } if let embeddedItem = (attributes[NSAttributedString.Key(rawValue: "TelegramEmbeddedItem")] as? AnyHashable ?? attributes[NSAttributedString.Key(rawValue: "Attribute__EmbeddedItem")] as? AnyHashable) { @@ -2518,11 +2527,46 @@ final class TextContentItemLayer: SimpleLayer { textColor = color } } - if let textColor = textColor { - context.setFillColor(textColor.cgColor) + switch strikethrough.style { + case .single: + if let color = strikethrough.color { + context.setFillColor(color.cgColor) + } else if let textColor { + context.setFillColor(textColor.cgColor) + } + let frame = strikethrough.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY) + context.fill(CGRect(x: frame.minX, y: frame.maxY - 2.0, width: frame.width, height: 1.0)) + case .wavy: + if let color = strikethrough.color { + context.setStrokeColor(color.cgColor) + } else if let textColor { + context.setStrokeColor(textColor.cgColor) + } + context.setLineWidth(1.33) + context.setLineCap(.round) + context.setLineJoin(.round) + let frame = strikethrough.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.maxY + 12.0) + + let amplitude: CGFloat = 1.2 + let period: CGFloat = 8.0 + let phase: CGFloat = -0.5 + let midY = frame.midY + let step: CGFloat = 1.0 + + context.saveGState() + context.clip(to: frame) + + var x = frame.minX + context.move(to: CGPoint(x: x, y: midY + amplitude * sin(phase))) + x += step + while x <= frame.maxX + step { + let y = midY + amplitude * sin((x - frame.minX) * 2.0 * .pi / period + phase) + context.addLine(to: CGPoint(x: x, y: y)) + x += step + } + context.strokePath() + context.restoreGState() } - let frame = strikethrough.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY) - context.fill(CGRect(x: frame.minX, y: frame.maxY - 2.0, width: frame.width, height: 1.0)) } } } diff --git a/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextNodeWithEntities.swift b/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextNodeWithEntities.swift index 4d4c9e26c8..caadbfbed8 100644 --- a/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextNodeWithEntities.swift +++ b/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextNodeWithEntities.swift @@ -368,3 +368,257 @@ public final class InteractiveTextNodeWithEntities { } } } + +public final class InteractiveTextComponent: Component { + public final class External { + public fileprivate(set) var layout: InteractiveTextNodeLayout? + + public init() { + } + } + + public let external: External? + public let attributedString: NSAttributedString? + public let backgroundColor: UIColor? + public let minimumNumberOfLines: Int + public let maximumNumberOfLines: Int + public let truncationType: CTLineTruncationType + public let alignment: NSTextAlignment + public let verticalAlignment: TextVerticalAlignment + public let lineSpacing: CGFloat + public let cutout: TextNodeCutout? + public let insets: UIEdgeInsets + public let lineColor: UIColor? + public let textShadowColor: UIColor? + public let textShadowBlur: CGFloat? + public let textStroke: (UIColor, CGFloat)? + public let displayContentsUnderSpoilers: Bool + public let customTruncationToken: ((UIFont, Bool) -> NSAttributedString?)? + public let expandedBlocks: Set + public let context: AccountContext + public let cache: AnimationCache + public let renderer: MultiAnimationRenderer + public let placeholderColor: UIColor + public let attemptSynchronous: Bool + public let textColor: UIColor + public let spoilerEffectColor: UIColor + public let spoilerTextColor: UIColor + public let areContentAnimationsEnabled: Bool + public let spoilerExpandPoint: CGPoint? + public let crossfadeContents: ((UIView) -> Void)? + public let canHandleTapAtPoint: ((CGPoint) -> Bool)? + public let requestToggleBlockCollapsed: ((Int) -> Void)? + public let requestDisplayContentsUnderSpoilers: ((CGPoint?) -> Void)? + + public init( + external: External? = nil, + attributedString: NSAttributedString?, + backgroundColor: UIColor?, + minimumNumberOfLines: Int, + maximumNumberOfLines: Int, + truncationType: CTLineTruncationType, + alignment: NSTextAlignment, + verticalAlignment: TextVerticalAlignment, + lineSpacing: CGFloat, + cutout: TextNodeCutout?, + insets: UIEdgeInsets, + lineColor: UIColor?, + textShadowColor: UIColor?, + textShadowBlur: CGFloat?, + textStroke: (UIColor, CGFloat)?, + displayContentsUnderSpoilers: Bool, + customTruncationToken: ((UIFont, Bool) -> NSAttributedString?)?, + expandedBlocks: Set, + context: AccountContext, + cache: AnimationCache, + renderer: MultiAnimationRenderer, + placeholderColor: UIColor, + attemptSynchronous: Bool, + textColor: UIColor, + spoilerEffectColor: UIColor, + spoilerTextColor: UIColor, + areContentAnimationsEnabled: Bool, + spoilerExpandPoint: CGPoint?, + crossfadeContents: ((UIView) -> Void)? = nil, + canHandleTapAtPoint: ((CGPoint) -> Bool)? = nil, + requestToggleBlockCollapsed: ((Int) -> Void)? = nil, + requestDisplayContentsUnderSpoilers: ((CGPoint?) -> Void)? = nil + ) { + self.external = external + self.attributedString = attributedString + self.backgroundColor = backgroundColor + self.minimumNumberOfLines = minimumNumberOfLines + self.maximumNumberOfLines = maximumNumberOfLines + self.truncationType = truncationType + self.alignment = alignment + self.verticalAlignment = verticalAlignment + self.lineSpacing = lineSpacing + self.cutout = cutout + self.insets = insets + self.lineColor = lineColor + self.textShadowColor = textShadowColor + self.textShadowBlur = textShadowBlur + self.textStroke = textStroke + self.displayContentsUnderSpoilers = displayContentsUnderSpoilers + self.customTruncationToken = customTruncationToken + self.expandedBlocks = expandedBlocks + self.context = context + self.cache = cache + self.renderer = renderer + self.placeholderColor = placeholderColor + self.attemptSynchronous = attemptSynchronous + self.textColor = textColor + self.spoilerTextColor = spoilerTextColor + self.areContentAnimationsEnabled = areContentAnimationsEnabled + self.spoilerExpandPoint = spoilerExpandPoint + self.crossfadeContents = crossfadeContents + self.spoilerEffectColor = spoilerEffectColor + self.canHandleTapAtPoint = canHandleTapAtPoint + self.requestToggleBlockCollapsed = requestToggleBlockCollapsed + self.requestDisplayContentsUnderSpoilers = requestDisplayContentsUnderSpoilers + } + + public static func ==(lhs: InteractiveTextComponent, rhs: InteractiveTextComponent) -> Bool { + if lhs.external !== rhs.external { + return false + } + if lhs.attributedString != rhs.attributedString { + return false + } + if lhs.backgroundColor != rhs.backgroundColor { + return false + } + if lhs.minimumNumberOfLines != rhs.minimumNumberOfLines { + return false + } + if lhs.maximumNumberOfLines != rhs.maximumNumberOfLines { + return false + } + if lhs.truncationType != rhs.truncationType { + return false + } + if lhs.alignment != rhs.alignment { + return false + } + if lhs.verticalAlignment != rhs.verticalAlignment { + return false + } + if lhs.lineSpacing != rhs.lineSpacing { + return false + } + if lhs.cutout != rhs.cutout { + return false + } + if lhs.insets != rhs.insets { + return false + } + if lhs.lineColor != rhs.lineColor { + return false + } + if lhs.textShadowColor != rhs.textShadowColor { + return false + } + if lhs.textShadowBlur != rhs.textShadowBlur { + return false + } + if lhs.textStroke?.0 != rhs.textStroke?.0 || lhs.textStroke?.1 != rhs.textStroke?.1 { + return false + } + if lhs.displayContentsUnderSpoilers != rhs.displayContentsUnderSpoilers { + return false + } + if (lhs.customTruncationToken == nil) != (rhs.customTruncationToken == nil) { + return false + } + if lhs.expandedBlocks != rhs.expandedBlocks { + return false + } + if lhs.spoilerExpandPoint != rhs.spoilerExpandPoint { + return false + } + return true + } + + public final class View: UIView { + private let textNodeWithEntities: InteractiveTextNodeWithEntities + + public var textNode: TextNodeProtocol { + return self.textNodeWithEntities.textNode + } + + override public init(frame: CGRect) { + self.textNodeWithEntities = InteractiveTextNodeWithEntities() + + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(component: InteractiveTextComponent, availableSize: CGSize, state: State, environment: Environment, transition: ComponentTransition) -> CGSize { + let makeLayout = InteractiveTextNodeWithEntities.asyncLayout(self.textNodeWithEntities) + let (layout, apply) = makeLayout(InteractiveTextNodeLayoutArguments(attributedString: component.attributedString, backgroundColor: component.backgroundColor, minimumNumberOfLines: component.minimumNumberOfLines, maximumNumberOfLines: component.maximumNumberOfLines, truncationType: component.truncationType, constrainedSize: availableSize, alignment: component.alignment, verticalAlignment: component.verticalAlignment, lineSpacing: component.lineSpacing, cutout: component.cutout, insets: component.insets, lineColor: component.lineColor, textShadowColor: component.textShadowColor, textShadowBlur: component.textShadowBlur, textStroke: component.textStroke, displayContentsUnderSpoilers: component.displayContentsUnderSpoilers, customTruncationToken: component.customTruncationToken, expandedBlocks: component.expandedBlocks)) + let textFrame = CGRect(origin: CGPoint(), size: layout.size) + + var spoilerExpandRect: CGRect? + if let mappedLocation = component.spoilerExpandPoint { + let getDistance: (CGPoint, CGPoint) -> CGFloat = { a, b in + let v = CGPoint(x: a.x - b.x, y: a.y - b.y) + return sqrt(v.x * v.x + v.y * v.y) + } + + var maxDistance: CGFloat = getDistance(mappedLocation, CGPoint(x: 0.0, y: 0.0)) + maxDistance = max(maxDistance, getDistance(mappedLocation, CGPoint(x: textFrame.width, y: 0.0))) + maxDistance = max(maxDistance, getDistance(mappedLocation, CGPoint(x: textFrame.width, y: textFrame.height))) + maxDistance = max(maxDistance, getDistance(mappedLocation, CGPoint(x: 0.0, y: textFrame.height))) + + let mappedSize = CGSize(width: maxDistance * 2.0, height: maxDistance * 2.0) + spoilerExpandRect = mappedSize.centered(around: mappedLocation) + } + + let applyArguments = InteractiveTextNode.ApplyArguments( + animation: transition.animation.isImmediate ? .None : .System(duration: 0.4, transition: .init(duration: 0.4, curve: .spring, interactive: false)), + spoilerTextColor: component.spoilerTextColor, + spoilerEffectColor: component.spoilerEffectColor, + areContentAnimationsEnabled: component.areContentAnimationsEnabled, + spoilerExpandRect: spoilerExpandRect, + crossfadeContents: component.crossfadeContents + ) + + let resultNode = apply(InteractiveTextNodeWithEntities.Arguments( + context: component.context, + cache: component.cache, + renderer: component.renderer, + placeholderColor: component.placeholderColor, + attemptSynchronous: component.attemptSynchronous, + textColor: component.textColor, + spoilerEffectColor: component.spoilerEffectColor, + applyArguments: applyArguments + )) + + if self.textNodeWithEntities.textNode.view.superview == nil { + self.addSubview(self.textNodeWithEntities.textNode.view) + } + + resultNode.visibilityRect = CGRect(origin: CGPoint(), size: layout.size) + resultNode.textNode.view.frame = textFrame + + resultNode.textNode.canHandleTapAtPoint = component.canHandleTapAtPoint + resultNode.textNode.requestToggleBlockCollapsed = component.requestToggleBlockCollapsed + resultNode.textNode.requestDisplayContentsUnderSpoilers = component.requestDisplayContentsUnderSpoilers + + component.external?.layout = resultNode.textNode.cachedLayout + + return layout.size + } + } + + public func makeView() -> View { + return View(frame: CGRect()) + } + + public func update(view: View, availableSize: CGSize, state: State, environment: Environment, transition: ComponentTransition) -> CGSize { + return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition) + } +} diff --git a/submodules/TelegramUI/Components/TextProcessingScreen/BUILD b/submodules/TelegramUI/Components/TextProcessingScreen/BUILD index f9f67f5d42..a9b6476a76 100644 --- a/submodules/TelegramUI/Components/TextProcessingScreen/BUILD +++ b/submodules/TelegramUI/Components/TextProcessingScreen/BUILD @@ -37,6 +37,7 @@ swift_library( "//submodules/TelegramUI/Components/ListActionItemComponent", "//submodules/TelegramUI/Components/TooltipComponent", "//submodules/TelegramUI/Components/ToastComponent", + "//submodules/TelegramUI/Components/InteractiveTextComponent", "//submodules/TelegramNotices", "//submodules/Markdown", "//submodules/TelegramUIPreferences", diff --git a/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingTextAreaComponent.swift b/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingTextAreaComponent.swift index 2ca48b76a2..fb22faf9df 100644 --- a/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingTextAreaComponent.swift +++ b/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingTextAreaComponent.swift @@ -8,7 +8,6 @@ import AccountContext import MultilineTextComponent import BundleIconComponent import TelegramCore -import MultilineTextWithEntitiesComponent import TextFormat import PlainButtonComponent import CheckComponent @@ -16,6 +15,7 @@ import ShimmerEffect import TextSelectionNode import Pasteboard import Speak +import InteractiveTextComponent final class TextProcessingTextAreaComponent: Component { let context: AccountContext @@ -116,7 +116,7 @@ final class TextProcessingTextAreaComponent: Component { private var emojify: ComponentView? private let titleButton: HighlightTrackingButton - private let textState = MultilineTextWithEntitiesComponent.External() + private let textState = InteractiveTextComponent.External() private let textContainer: UIView private let text = ComponentView() private var expandShadow: UIImageView? @@ -126,7 +126,7 @@ final class TextProcessingTextAreaComponent: Component { private var previousText: TextWithEntities? private var previousTextLineCount: Int? - private let measureLoadingTextState = MultilineTextWithEntitiesComponent.External() + private let measureLoadingTextState = InteractiveTextComponent.External() private let measureLoadingText = ComponentView() private var shimmerEffectNode: ShimmerEffectNode? @@ -135,6 +135,10 @@ final class TextProcessingTextAreaComponent: Component { private let textSelectionKnobContainer: UIView private let textSelectionKnobSurface: UIView + private var expandedBlockIds: Set = Set() + private var appliedExpandedBlockIds: Set? + private var displayContentsUnderSpoilers: (value: Bool, location: CGPoint?) = (false, nil) + private var currentSpeechHolder: SpeechSynthesizerHolder? override init(frame: CGRect) { @@ -181,6 +185,25 @@ final class TextProcessingTextAreaComponent: Component { } component.titleAction?(titleView) } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + guard let result = super.hitTest(point, with: event) else { + return nil + } + if result == self.textSelectionNode?.view && !self.displayContentsUnderSpoilers.value { + if let textView = self.text.view as? InteractiveTextComponent.View { + let textPoint = self.convert(point, to: textView.textNode.view) + if let attributes = textView.textNode.attributesAtPoint(textPoint, orNearest: false)?.1 { + if attributes[NSAttributedString.Key(rawValue: "TelegramSpoiler")] != nil || attributes[NSAttributedString.Key(rawValue: "Attribute__Spoiler")] != nil { + if let value = textView.textNode.view.hitTest(textPoint, with: event) { + return value + } + } + } + } + } + return result + } func update(component: TextProcessingTextAreaComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { self.isUpdating = true @@ -362,27 +385,80 @@ final class TextProcessingTextAreaComponent: Component { ], range: NSRange(location: range.lowerBound, length: range.upperBound - range.lowerBound)) } } + + let textOrigin = CGPoint(x: sideInset, y: contentHeight) + + var spoilerExpandPoint: CGPoint? + if let location = self.displayContentsUnderSpoilers.location { + self.displayContentsUnderSpoilers.location = nil + + spoilerExpandPoint = CGPoint(x: location.x - textOrigin.x, y: location.y - textOrigin.y) + } + let textSize = self.text.update( - transition: .immediate, - component: AnyComponent(MultilineTextWithEntitiesComponent( + transition: transition, + component: AnyComponent(InteractiveTextComponent( external: self.textState, - context: component.context, - animationCache: component.context.animationCache, - animationRenderer: component.context.animationRenderer, - placeholderColor: component.theme.list.mediaPlaceholderColor, - text: .plain(textValue), + attributedString: textValue, + backgroundColor: nil, + minimumNumberOfLines: 1, maximumNumberOfLines: 0, + truncationType: .end, + alignment: .left, + verticalAlignment: .top, lineSpacing: 0.12, cutout: nil, insets: UIEdgeInsets(), - spoilerColor: component.theme.list.itemPrimaryTextColor, - enableLooping: true, - displaysAsynchronously: false, + lineColor: nil, + textShadowColor: nil, + textShadowBlur: nil, + textStroke: nil, + displayContentsUnderSpoilers: self.displayContentsUnderSpoilers.value, + customTruncationToken: nil, + expandedBlocks: Set(), + context: component.context, + cache: component.context.animationCache, + renderer: component.context.animationRenderer, + placeholderColor: component.theme.list.mediaPlaceholderColor, + attemptSynchronous: true, + textColor: component.theme.list.itemPrimaryTextColor, + spoilerEffectColor: component.theme.list.itemPrimaryTextColor, + spoilerTextColor: component.theme.list.itemPrimaryTextColor, + areContentAnimationsEnabled: true, + spoilerExpandPoint: spoilerExpandPoint, + canHandleTapAtPoint: { _ in + return true + }, + requestToggleBlockCollapsed: { [weak self] blockId in + guard let self else { + return + } + if self.expandedBlockIds.contains(blockId) { + self.expandedBlockIds.remove(blockId) + } else { + self.expandedBlockIds.insert(blockId) + } + self.state?.updated(transition: .spring(duration: 0.4)) + }, + requestDisplayContentsUnderSpoilers: { [weak self] location in + guard let self, let textView = self.text.view as? InteractiveTextComponent.View else { + return + } + + cancelParentGestures(view: self) + + var mappedLocation: CGPoint? + if let location { + mappedLocation = textView.textNode.layer.convert(location, to: self.layer) + } + self.displayContentsUnderSpoilers = (true, mappedLocation) + self.state?.updated(transition: .spring(duration: 0.4)) + } )), environment: {}, containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: availableSize.height) ) - let textFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: textSize) + let textFrame = CGRect(origin: textOrigin, size: textSize) if let textView = self.text.view { if textView.superview == nil { textView.layer.anchorPoint = CGPoint() @@ -400,7 +476,7 @@ final class TextProcessingTextAreaComponent: Component { var textContainerFrame = textFrame if let isExpanded = component.isExpanded, let textLayout = self.textState.layout, textLayout.numberOfLines > 1 { if !isExpanded.value, let firstLineRect = textLayout.linesRects().first { - textContainerFrame.size.height = firstLineRect.maxY - 14.0 + textContainerFrame.size.height = ceil(firstLineRect.maxY) } let expandButton: ComponentView @@ -482,13 +558,13 @@ final class TextProcessingTextAreaComponent: Component { } if component.copyAction != nil, let textLayout = self.textState.layout { - if textLayout.trailingLineWidth >= availableSize.width - sideInset - 32.0 { + if textLayout.trailingLineWidth >= availableSize.width - sideInset - 32.0 || textLayout.trailingLineIsRTL { textContainerFrame.size.height += 28.0 } } } - if component.text != nil, component.isExpanded?.value ?? true, let textView = self.text.view as? MultilineTextWithEntitiesComponent.View { + if component.text != nil, component.isExpanded?.value ?? true, let textView = self.text.view as? InteractiveTextComponent.View { let textSelectionNode: TextSelectionNode if let current = self.textSelectionNode { textSelectionNode = current @@ -611,20 +687,35 @@ final class TextProcessingTextAreaComponent: Component { let measureLoadingTextSize = self.measureLoadingText.update( transition: .immediate, - component: AnyComponent(MultilineTextWithEntitiesComponent( + component: AnyComponent(InteractiveTextComponent( external: self.measureLoadingTextState, - context: component.context, - animationCache: component.context.animationCache, - animationRenderer: component.context.animationRenderer, - placeholderColor: component.theme.list.mediaPlaceholderColor, - text: .plain(NSAttributedString(string: fakeLines, font: Font.regular(fontSize), textColor: .black)), + attributedString: NSAttributedString(string: fakeLines, font: Font.regular(fontSize), textColor: .black), + backgroundColor: nil, + minimumNumberOfLines: 1, maximumNumberOfLines: 0, + truncationType: .end, + alignment: .left, + verticalAlignment: .top, lineSpacing: 0.12, cutout: nil, insets: UIEdgeInsets(), - spoilerColor: component.theme.list.itemPrimaryTextColor, - enableLooping: true, - displaysAsynchronously: false, + lineColor: nil, + textShadowColor: nil, + textShadowBlur: nil, + textStroke: nil, + displayContentsUnderSpoilers: true, + customTruncationToken: nil, + expandedBlocks: Set(), + context: component.context, + cache: component.context.animationCache, + renderer: component.context.animationRenderer, + placeholderColor: component.theme.list.mediaPlaceholderColor, + attemptSynchronous: true, + textColor: component.theme.list.itemPrimaryTextColor, + spoilerEffectColor: component.theme.list.itemPrimaryTextColor, + spoilerTextColor: component.theme.list.itemPrimaryTextColor, + areContentAnimationsEnabled: true, + spoilerExpandPoint: nil )), environment: {}, containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: availableSize.height) diff --git a/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingTranslateContentComponent.swift b/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingTranslateContentComponent.swift index f93b3af842..ca8fb65863 100644 --- a/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingTranslateContentComponent.swift +++ b/submodules/TelegramUI/Components/TextProcessingScreen/Sources/TextProcessingTranslateContentComponent.swift @@ -229,16 +229,6 @@ final class TextProcessingTranslateContentComponent: Component { } } } - - @objc private func onTooltipTapGesture(_ recognizer: UITapGestureRecognizer) { - guard let component = self.component else { - return - } - if case .ended = recognizer.state { - component.externalState.displayStyleTooltip = false - self.state?.updated(transition: .easeInOut(duration: 0.2)) - } - } func update(component: TextProcessingTranslateContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { self.isUpdating = true @@ -488,8 +478,15 @@ final class TextProcessingTranslateContentComponent: Component { } else { tooltipTransition = tooltipTransition.withAnimation(.none) tooltip = ComponentView() - dimView = UIView() - dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.onTooltipTapGesture(_:)))) + let dimViewValue = TransparentHitView() + dimViewValue.onTap = { [weak self] in + guard let self, let component = self.component else { + return + } + component.externalState.displayStyleTooltip = false + self.state?.updated(transition: .easeInOut(duration: 0.2)) + } + dimView = dimViewValue self.styleTooltip = (dimView, tooltip) } let tooltipSize = tooltip.update(