diff --git a/submodules/Display/Source/ContainedViewLayoutTransition.swift b/submodules/Display/Source/ContainedViewLayoutTransition.swift index c46b10a17a..c74db47d29 100644 --- a/submodules/Display/Source/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Source/ContainedViewLayoutTransition.swift @@ -1672,6 +1672,27 @@ public extension ContainedViewLayoutTransition { ) } } + + func animateContents(layer: CALayer, from fromContents: Any) { + guard case let .animated(duration, curve) = self else { + return + } + guard let contents = layer.contents, CFGetTypeID(contents as CFTypeRef) == CGImage.typeID else { + return + } + guard CFGetTypeID(fromContents as CFTypeRef) == CGImage.typeID else { + return + } + + let contentsImage = contents as! CGImage + let fromContentsImage = fromContents as! CGImage + + if contentsImage === fromContentsImage { + return + } + + layer.animate(from: fromContentsImage, to: contentsImage, keyPath: "contents", timingFunction: curve.timingFunction, duration: duration, delay: 0.0, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: false) + } } public struct CombinedTransition { diff --git a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift index f98a704e0d..cab565a18b 100644 --- a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift @@ -1482,7 +1482,7 @@ private final class QuoteBackgroundView: UIView { } if let image = self.collapseButtonIconView.image { let iconSize = image.size.aspectFitted(collapseButtonSize) - self.collapseButtonIconView.frame = CGRect(origin: CGPoint(x: self.collapseButton.bounds.width - 2.0 - collapseButtonSize.width + floorToScreenPixels((collapseButtonSize.width - iconSize.width) * 0.5), y: 2.0 + floorToScreenPixels((collapseButtonSize.height - iconSize.height) * 0.5)), size: iconSize) + self.collapseButtonIconView.frame = CGRect(origin: CGPoint(x: self.collapseButton.bounds.width - 4.0 - collapseButtonSize.width + floorToScreenPixels((collapseButtonSize.width - iconSize.width) * 0.5), y: 4.0 + floorToScreenPixels((collapseButtonSize.height - iconSize.height) * 0.5)), size: iconSize) } var primaryColor: UIColor diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift index 42dc5e13c8..6be46c2567 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift @@ -8,7 +8,6 @@ import TextFormat import UrlEscaping import TelegramUniversalVideoContent import TextSelectionNode -import InvisibleInkDustNode import Emoji import AnimatedStickerNode import TelegramAnimatedStickerNode @@ -85,7 +84,6 @@ private func findQuoteRange(string: String, quoteText: String, offset: Int?) -> public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { private let containerNode: ASDisplayNode private let textNode: InteractiveTextNodeWithEntities - private var dustNode: InvisibleInkDustNode? private let textAccessibilityOverlayNode: TextAccessibilityOverlayNode public var statusNode: ChatMessageDateAndStatusNode? @@ -112,6 +110,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { private var codeHighlightState: (id: EngineMessage.Id, specs: [CachedMessageSyntaxHighlight.Spec], disposable: Disposable)? private var expandedBlockIds: Set = Set() + private var displayContentsUnderSpoilers: Bool = false override public var visibility: ListViewItemNodeVisibility { didSet { @@ -162,6 +161,12 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } item.controllerInteraction.requestMessageUpdate(item.message.id, false) } + self.textNode.textNode.requestDisplayContentsUnderSpoilers = { [weak self] in + guard let self else { + return + } + self.updateDisplayContentsUnderSpoilers(value: true) + } self.textNode.textNode.canHandleTapAtPoint = { [weak self] point in guard let self else { return false @@ -192,6 +197,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { let currentCachedChatMessageText = self.cachedChatMessageText let expandedBlockIds = self.expandedBlockIds + let displayContentsUnderSpoilers = self.displayContentsUnderSpoilers return { item, layoutConstants, _, _, _, _ in let contentProperties = ChatMessageBubbleContentProperties(hidesSimpleAuthorHeader: false, headerSpacing: 0.0, hidesBackground: .never, forceFullCorners: false, forceAlignment: .none) @@ -579,7 +585,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { cutout: nil, insets: textInsets, lineColor: messageTheme.accentControlColor, - displayContentsUnderSpoilers: false, + displayContentsUnderSpoilers: displayContentsUnderSpoilers, customTruncationToken: customTruncationToken, expandedBlocks: expandedBlockIds )) @@ -594,6 +600,8 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { let trailingWidthToMeasure: CGFloat if let lastSegment = textLayout.segments.last, lastSegment.hasRTL { trailingWidthToMeasure = 10000.0 + } else if let lastSegment = textLayout.segments.last, lastSegment.hasBlockQuote { + trailingWidthToMeasure = textLayout.size.width } else { trailingWidthToMeasure = textLayout.trailingLineWidth } @@ -675,31 +683,6 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { )) animation.animator.updateFrame(layer: strongSelf.textNode.textNode.layer, frame: textFrame, completion: nil) - /*if let (_, spoilerTextApply) = spoilerTextLayoutAndApply { - let spoilerTextNode = spoilerTextApply(InteractiveTextNodeWithEntities.Arguments(context: item.context, cache: item.controllerInteraction.presentationContext.animationCache, renderer: item.controllerInteraction.presentationContext.animationRenderer, placeholderColor: messageTheme.mediaPlaceholderColor, attemptSynchronous: synchronousLoads, animation: animation)) - - strongSelf.spoilerTextNode?.textNode.frame = textFrame - - let dustNode: InvisibleInkDustNode - if let current = strongSelf.dustNode { - dustNode = current - } else { - dustNode = InvisibleInkDustNode(textNode: spoilerTextNode.textNode, enableAnimations: item.context.sharedContext.energyUsageSettings.fullTranslucency && !item.presentationData.isPreview) - strongSelf.dustNode = dustNode - strongSelf.containerNode.insertSubnode(dustNode, aboveSubnode: spoilerTextNode.textNode) - } - dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0) - dustNode.update(size: dustNode.frame.size, color: messageTheme.secondaryTextColor, textColor: messageTheme.primaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }) - } else if let spoilerTextNode = strongSelf.spoilerTextNode { - strongSelf.spoilerTextNode = nil - spoilerTextNode.textNode.removeFromSupernode() - - if let dustNode = strongSelf.dustNode { - strongSelf.dustNode = nil - dustNode.removeFromSupernode() - } - }*/ - switch strongSelf.visibility { case .none: strongSelf.textNode.visibilityRect = nil @@ -864,7 +847,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { let textNodeFrame = self.textNode.textNode.frame let textLocalPoint = CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY) if let (index, attributes) = self.textNode.textNode.attributesAtPoint(textLocalPoint) { - if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler)], !(self.dustNode?.isRevealed ?? true) { + if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler)], !self.displayContentsUnderSpoilers { return ChatMessageBubbleContentTapAction(content: .none) } else if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { var concealed = true @@ -1056,8 +1039,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } } - if let spoilerRects = spoilerRects, !spoilerRects.isEmpty, let dustNode = self.dustNode, !dustNode.isRevealed { - + if let spoilerRects = spoilerRects, !spoilerRects.isEmpty, !self.displayContentsUnderSpoilers { } else if let rects = rects { let linkHighlightingNode: LinkHighlightingNode if let current = self.linkHighlightingNode { @@ -1327,11 +1309,11 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { guard let strongSelf = self else { return } - if let dustNode = strongSelf.dustNode, !dustNode.isRevealed, let textLayout = strongSelf.textNode.textNode.cachedLayout, textLayout.segments.contains(where: { !$0.spoilers.isEmpty }), let selectionRange { + if !strongSelf.displayContentsUnderSpoilers, let textLayout = strongSelf.textNode.textNode.cachedLayout, textLayout.segments.contains(where: { !$0.spoilers.isEmpty }), let selectionRange { for segment in textLayout.segments { for (spoilerRange, _) in segment.spoilers { if let intersection = selectionRange.intersection(spoilerRange), intersection.length > 0 { - dustNode.update(revealed: true) + strongSelf.updateDisplayContentsUnderSpoilers(value: true) return } } @@ -1387,12 +1369,22 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { }) } - if let dustNode = self.dustNode, dustNode.isRevealed { - dustNode.update(revealed: false) + if self.displayContentsUnderSpoilers { + self.updateDisplayContentsUnderSpoilers(value: false) } } } + private func updateDisplayContentsUnderSpoilers(value: Bool) { + if self.displayContentsUnderSpoilers == value { + return + } + self.displayContentsUnderSpoilers = value + if let item = self.item { + item.controllerInteraction.requestMessageUpdate(item.message.id, false) + } + } + override public func reactionTargetView(value: MessageReaction.Reaction) -> UIView? { if let statusNode = self.statusNode, !statusNode.isHidden { return statusNode.reactionView(value: value) diff --git a/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift b/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift index ffc6c7d3a7..6d81eb0f69 100644 --- a/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift +++ b/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextComponent.swift @@ -33,19 +33,12 @@ private func generateBlockMaskImage() -> UIImage { let colorSpace = CGColorSpaceCreateDeviceRGB() var locations: [CGFloat] = [0.0, 0.5, 1.0] - var colors: [CGColor] = [UIColor.black.withAlphaComponent(0.0).cgColor, UIColor.black.withAlphaComponent(0.0).cgColor, UIColor.black.cgColor] + let colors: [CGColor] = [UIColor.black.withAlphaComponent(0.0).cgColor, UIColor.black.withAlphaComponent(0.0).cgColor, UIColor.black.cgColor] - var gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! + let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! context.setBlendMode(.copy) context.drawRadialGradient(gradient, startCenter: CGPoint(x: size.width - 20.0, y: size.height), startRadius: 0.0, endCenter: CGPoint(x: size.width - 20.0, y: size.height), endRadius: 34.0, options: CGGradientDrawingOptions()) - - locations = [0.0, 0.5, 1.0] - colors = [UIColor.black.withAlphaComponent(0.0).cgColor, UIColor.black.withAlphaComponent(0.4).cgColor, UIColor.black.cgColor] - gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! - - context.setBlendMode(.destinationIn) - context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: size.height), end: CGPoint(x: 0.0, y: size.height - 18.0), options: CGGradientDrawingOptions()) })!.resizableImage(withCapInsets: UIEdgeInsets(top: 0.0, left: 0.0, bottom: size.height - 1.0, right: size.width - 1.0), resizingMode: .stretch) } @@ -190,6 +183,10 @@ public final class InteractiveTextNodeSegment { public let spoilerWords: [(NSRange, CGRect)] public let embeddedItems: [InteractiveTextNodeLayout.EmbeddedItem] + public var hasBlockQuote: Bool { + return self.blockQuote != nil + } + fileprivate init( lines: [InteractiveTextNodeLine], visibleLineCount: Int, @@ -393,7 +390,7 @@ public final class InteractiveTextNodeLayout: NSObject { fileprivate let textShadowColor: UIColor? fileprivate let textShadowBlur: CGFloat? fileprivate let textStroke: (UIColor, CGFloat)? - fileprivate let displayContentsUnderSpoilers: Bool + public let displayContentsUnderSpoilers: Bool fileprivate let expandedBlocks: Set fileprivate init( @@ -444,6 +441,33 @@ public final class InteractiveTextNodeLayout: NSObject { self.expandedBlocks = expandedBlocks } + func withUpdatedDisplayContentsUnderSpoilers(_ displayContentsUnderSpoilers: Bool) -> InteractiveTextNodeLayout { + return InteractiveTextNodeLayout( + attributedString: self.attributedString, + maximumNumberOfLines: self.maximumNumberOfLines, + truncationType: self.truncationType, + constrainedSize: self.constrainedSize, + explicitAlignment: self.explicitAlignment, + resolvedAlignment: self.resolvedAlignment, + verticalAlignment: self.verticalAlignment, + lineSpacing: self.lineSpacing, + cutout: self.cutout, + insets: self.insets, + size: self.size, + rawTextSize: self.rawTextSize, + truncated: self.truncated, + firstLineOffset: self.firstLineOffset, + segments: self.segments, + backgroundColor: self.backgroundColor, + lineColor: self.lineColor, + textShadowColor: self.textShadowColor, + textShadowBlur: self.textShadowBlur, + textStroke: self.textStroke, + displayContentsUnderSpoilers: displayContentsUnderSpoilers, + expandedBlocks: self.expandedBlocks + ) + } + public var numberOfLines: Int { var result = 0 for segment in self.segments { @@ -1078,8 +1102,11 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn public var renderContentTypes: RenderContentTypes = .all private var contentItemLayers: [Int: TextContentItemLayer] = [:] + private var isDisplayingContentsUnderSpoilers: Bool? + public var canHandleTapAtPoint: ((CGPoint) -> Bool)? public var requestToggleBlockCollapsed: ((Int) -> Void)? + public var requestDisplayContentsUnderSpoilers: (() -> Void)? private var tapRecognizer: UITapGestureRecognizer? public var currentText: NSAttributedString? { @@ -1538,7 +1565,10 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn var descent: CGFloat = 0.0 CTLineGetTypographicBounds(line.line, &ascent, &descent, nil) - let isHiddenBySpoiler = attributes[NSAttributedString.Key(rawValue: "Attribute__Spoiler")] != nil || attributes[NSAttributedString.Key(rawValue: "TelegramSpoiler")] != nil + var isHiddenBySpoiler = attributes[NSAttributedString.Key(rawValue: "Attribute__Spoiler")] != nil || attributes[NSAttributedString.Key(rawValue: "TelegramSpoiler")] != nil + if displayContentsUnderSpoilers { + isHiddenBySpoiler = false + } addEmbeddedItem(item: embeddedItem, isHiddenBySpoiler: isHiddenBySpoiler, line: line, ascent: ascent, descent: descent, startIndex: range.location, endIndex: range.location + range.length) } @@ -1589,7 +1619,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn var segmentBlockQuote: InteractiveTextNodeBlockQuote? if let blockQuote = segment.blockQuote, let tintColor = segment.tintColor, let blockIndex { - segmentBlockQuote = InteractiveTextNodeBlockQuote(id: blockIndex, frame: CGRect(origin: CGPoint(x: 0.0, y: blockMinY - 2.0), size: CGSize(width: blockWidth, height: blockMaxY - (blockMinY - 2.0) + 4.0)), data: blockQuote, tintColor: tintColor, secondaryTintColor: segment.secondaryTintColor, tertiaryTintColor: segment.tertiaryTintColor, backgroundColor: blockQuote.backgroundColor, isCollapsed: (blockQuote.isCollapsible && segmentLines.count > 3) ? isCollapsed : nil) + segmentBlockQuote = InteractiveTextNodeBlockQuote(id: blockIndex, frame: CGRect(origin: CGPoint(x: 0.0, y: blockMinY - 2.0), size: CGSize(width: blockWidth, height: blockMaxY - (blockMinY - 2.0) + 3.0)), data: blockQuote, tintColor: tintColor, secondaryTintColor: segment.secondaryTintColor, tertiaryTintColor: segment.tertiaryTintColor, backgroundColor: blockQuote.backgroundColor, isCollapsed: (blockQuote.isCollapsible && segmentLines.count > 3) ? isCollapsed : nil) } segments.append(InteractiveTextNodeSegment( @@ -1651,6 +1681,10 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn return } + let animateContents = self.isDisplayingContentsUnderSpoilers != nil && self.isDisplayingContentsUnderSpoilers != cachedLayout.displayContentsUnderSpoilers && animation.isAnimated + let synchronous = animateContents + self.isDisplayingContentsUnderSpoilers = cachedLayout.displayContentsUnderSpoilers + let topLeftOffset = CGPoint(x: cachedLayout.insets.left, y: cachedLayout.insets.top) var validIds: [Int] = [] @@ -1704,7 +1738,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn self.layer.addSublayer(contentItemLayer) } - contentItemLayer.update(item: contentItem, animation: contentItemAnimation) + contentItemLayer.update(item: contentItem, animation: contentItemAnimation, synchronously: synchronous, animateContents: animateContents && contentItemAnimation.isAnimated) contentItemAnimation.animator.updateFrame(layer: contentItemLayer, frame: contentItemFrame, completion: nil) } @@ -1739,6 +1773,12 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { let point = recognizer.location(in: self.view) + if let cachedLayout = self.cachedLayout, !cachedLayout.displayContentsUnderSpoilers, let (_, attributes) = self.attributesAtPoint(point) { + if attributes[NSAttributedString.Key(rawValue: "Attribute__Spoiler")] != nil || attributes[NSAttributedString.Key(rawValue: "TelegramSpoiler")] != nil { + self.requestDisplayContentsUnderSpoilers?() + return + } + } if let blockId = self.collapsibleBlockAtPoint(point) { self.requestToggleBlockCollapsed?(blockId) } @@ -1749,7 +1789,7 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn let existingLayout: InteractiveTextNodeLayout? = maybeNode?.cachedLayout return { arguments in - let layout: InteractiveTextNodeLayout + var layout: InteractiveTextNodeLayout if let existingLayout = existingLayout, existingLayout.constrainedSize == arguments.constrainedSize && existingLayout.maximumNumberOfLines == arguments.maximumNumberOfLines && existingLayout.truncationType == arguments.truncationType && existingLayout.cutout == arguments.cutout && existingLayout.explicitAlignment == arguments.alignment && existingLayout.lineSpacing.isEqual(to: arguments.lineSpacing) && existingLayout.expandedBlocks == arguments.expandedBlocks { let stringMatch: Bool @@ -1775,6 +1815,9 @@ open class InteractiveTextNode: ASDisplayNode, TextNodeProtocol, UIGestureRecogn if stringMatch { layout = existingLayout + if layout.displayContentsUnderSpoilers != arguments.displayContentsUnderSpoilers { + layout = layout.withUpdatedDisplayContentsUnderSpoilers(arguments.displayContentsUnderSpoilers) + } } else { layout = InteractiveTextNode.calculateLayout(attributedString: arguments.attributedString, minimumNumberOfLines: arguments.minimumNumberOfLines, maximumNumberOfLines: arguments.maximumNumberOfLines, truncationType: arguments.truncationType, backgroundColor: arguments.backgroundColor, constrainedSize: arguments.constrainedSize, alignment: arguments.alignment, verticalAlignment: arguments.verticalAlignment, lineSpacingFactor: arguments.lineSpacing, cutout: arguments.cutout, insets: arguments.insets, lineColor: arguments.lineColor, textShadowColor: arguments.textShadowColor, textShadowBlur: arguments.textShadowBlur, textStroke: arguments.textStroke, displayContentsUnderSpoilers: arguments.displayContentsUnderSpoilers, customTruncationToken: arguments.customTruncationToken, expandedBlocks: arguments.expandedBlocks) } @@ -1964,7 +2007,7 @@ final class TextContentItemLayer: SimpleLayer { if attributes["Attribute__EmbeddedItem"] != nil { continue } - if hasHiddenSpoilers && attributes["Attribute__Spoiler"] != nil || attributes["TelegramSpoiler"] != nil { + if hasHiddenSpoilers && (attributes["Attribute__Spoiler"] != nil || attributes["TelegramSpoiler"] != nil) { continue } @@ -2155,9 +2198,8 @@ final class TextContentItemLayer: SimpleLayer { fatalError("init(coder:) has not been implemented") } - func update(item: TextContentItem, animation: ListViewItemUpdateAnimation, synchronously: Bool = false) { + func update(item: TextContentItem, animation: ListViewItemUpdateAnimation, synchronously: Bool = false, animateContents: Bool = false) { self.item = item - self.setNeedsDisplay() let contentFrame = CGRect(origin: CGPoint(), size: item.size) var effectiveContentFrame = contentFrame @@ -2316,7 +2358,11 @@ final class TextContentItemLayer: SimpleLayer { self.renderNode.params = RenderParams(size: contentFrame.size, item: item, mask: staticContentMask) if synchronously { + let previousContents = self.renderNode.layer.contents self.renderNode.displayImmediately() + if animateContents, let previousContents { + animation.transition.animateContents(layer: self.renderNode.layer, from: previousContents) + } } else { self.renderNode.setNeedsDisplay() } diff --git a/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextNodeWithEntities.swift b/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextNodeWithEntities.swift index e4d8de4142..77fd404cb1 100644 --- a/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextNodeWithEntities.swift +++ b/submodules/TelegramUI/Components/InteractiveTextComponent/Sources/InteractiveTextNodeWithEntities.swift @@ -257,6 +257,11 @@ public final class InteractiveTextNodeWithEntities { ) { self.enableLooping = context.sharedContext.energyUsageSettings.loopEmoji + var displayContentsUnderSpoilers = false + if let textLayout { + displayContentsUnderSpoilers = textLayout.displayContentsUnderSpoilers + } + var nextIndexById: [Int64: Int] = [:] var validIds: [InlineStickerItemLayer.Key] = [] @@ -291,6 +296,7 @@ public final class InteractiveTextNodeWithEntities { itemFrame.origin.y += segmentItem.contentOffset.y let itemLayerData: InlineStickerItemLayerData + var itemLayerTransition = animation.transition if let current = self.inlineStickerItemLayers[id] { itemLayerData = current itemLayerData.itemLayer.dynamicColor = item.textColor @@ -299,6 +305,7 @@ public final class InteractiveTextNodeWithEntities { segmentLayer.addSublayer(itemLayerData.itemLayer) } } else { + itemLayerTransition = .immediate let pointSize = floor(itemSize * 1.3) itemLayerData = InlineStickerItemLayerData(itemLayer: InlineStickerItemLayer(context: context, userLocation: .other, attemptSynchronousLoad: attemptSynchronousLoad, emoji: stickerItem.emoji, file: stickerItem.file, cache: cache, renderer: renderer, placeholderColor: placeholderColor, pointSize: CGSize(width: pointSize, height: pointSize), dynamicColor: item.textColor)) self.inlineStickerItemLayers[id] = itemLayerData @@ -307,7 +314,7 @@ public final class InteractiveTextNodeWithEntities { itemLayerData.itemLayer.isVisibleForAnimations = self.enableLooping && self.isItemVisible(itemRect: itemFrame.offsetBy(dx: -segmentItem.contentOffset.x, dy: -segmentItem.contentOffset.x)) } - itemLayerData.itemLayer.opacity = item.isHiddenBySpoiler ? 0.0 : 1.0 + itemLayerTransition.updateAlpha(layer: itemLayerData.itemLayer, alpha: item.isHiddenBySpoiler ? 0.0 : 1.0) itemLayerData.itemLayer.frame = itemFrame itemLayerData.rect = itemFrame.offsetBy(dx: -segmentItem.contentOffset.x, dy: -segmentItem.contentOffset.y) @@ -337,6 +344,8 @@ public final class InteractiveTextNodeWithEntities { rects: segment.spoilers.map { $0.1.offsetBy(dx: 3.0 + segmentItem.contentOffset.x, dy: segmentItem.contentOffset.y + 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: segment.spoilerWords.map { $0.1.offsetBy(dx: segmentItem.contentOffset.x + 3.0, dy: segmentItem.contentOffset.y + 3.0).insetBy(dx: 1.0, dy: 1.0) } ) + + animation.transition.updateAlpha(node: dustEffectNode, alpha: displayContentsUnderSpoilers ? 0.0 : 1.0) } } }