diff --git a/submodules/Display/Source/ImmediateTextNode.swift b/submodules/Display/Source/ImmediateTextNode.swift index af2ffc8f57..e8cf468842 100644 --- a/submodules/Display/Source/ImmediateTextNode.swift +++ b/submodules/Display/Source/ImmediateTextNode.swift @@ -17,6 +17,7 @@ public class ImmediateTextNode: TextNode { public var textShadowColor: UIColor? public var textStroke: (UIColor, CGFloat)? public var cutout: TextNodeCutout? + public var displaySpoilers = false public var truncationMode: NSLineBreakMode { get { @@ -89,7 +90,7 @@ public class ImmediateTextNode: TextNode { self.constrainedSize = constrainedSize let makeLayout = TextNode.asyncLayout(self) - let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, textShadowColor: self.textShadowColor, textStroke: self.textStroke)) + let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, textShadowColor: self.textShadowColor, textStroke: self.textStroke, displaySpoilers: self.displaySpoilers)) let _ = apply() if layout.numberOfLines > 1 { self.trailingLineWidth = layout.trailingLineWidth @@ -103,7 +104,7 @@ public class ImmediateTextNode: TextNode { self.constrainedSize = constrainedSize let makeLayout = TextNode.asyncLayout(self) - let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets)) + let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers)) let _ = apply() return ImmediateTextNodeLayoutInfo(size: layout.size, truncated: layout.truncated) } @@ -112,7 +113,7 @@ public class ImmediateTextNode: TextNode { self.constrainedSize = constrainedSize let makeLayout = TextNode.asyncLayout(self) - let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets)) + let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers)) let _ = apply() return layout } diff --git a/submodules/Display/Source/TextNode.swift b/submodules/Display/Source/TextNode.swift index c8d8e8d1a0..33ca210f87 100644 --- a/submodules/Display/Source/TextNode.swift +++ b/submodules/Display/Source/TextNode.swift @@ -973,13 +973,30 @@ public class TextNode: ASDisplayNode { let lineCharacterCount = CTTypesetterSuggestLineBreak(typesetter, lastLineCharacterIndex, Double(lineConstrainedWidth)) + func addSpoiler(line: CTLine, ascent: CGFloat, descent: CGFloat, startIndex: Int, endIndex: Int) { + var secondaryLeftOffset: CGFloat = 0.0 + let rawLeftOffset = CTLineGetOffsetForStringIndex(line, startIndex, &secondaryLeftOffset) + var leftOffset = floor(rawLeftOffset) + if !rawLeftOffset.isEqual(to: secondaryLeftOffset) { + leftOffset = floor(secondaryLeftOffset) + } + + var secondaryRightOffset: CGFloat = 0.0 + let rawRightOffset = CTLineGetOffsetForStringIndex(line, endIndex, &secondaryRightOffset) + var rightOffset = ceil(rawRightOffset) + if !rawRightOffset.isEqual(to: secondaryRightOffset) { + rightOffset = ceil(secondaryRightOffset) + } + + spoilers.append(TextNodeSpoiler(range: NSMakeRange(startIndex, endIndex - startIndex + 1), frame: CGRect(x: min(leftOffset, rightOffset), y: descent - (ascent + descent), width: abs(rightOffset - leftOffset), height: ascent + descent))) + } + var isLastLine = false if maximumNumberOfLines != 0 && lines.count == maximumNumberOfLines - 1 && lineCharacterCount > 0 { isLastLine = true } else if layoutSize.height + (fontLineSpacing + fontLineHeight) * 2.0 > constrainedSize.height { isLastLine = true } - if isLastLine { if first { first = false @@ -1022,25 +1039,27 @@ public class TextNode: ASDisplayNode { var descent: CGFloat = 0.0 CTLineGetTypographicBounds(coreTextLine, &ascent, &descent, nil) - (attributedString.string as NSString).enumerateSubstrings(in: range, options: .byWords) { _, range, _, _ in - let startIndex = range.location - let endIndex = range.location + range.length - - var secondaryLeftOffset: CGFloat = 0.0 - let rawLeftOffset = CTLineGetOffsetForStringIndex(coreTextLine, startIndex, &secondaryLeftOffset) - var leftOffset = ceil(rawLeftOffset) - if !rawLeftOffset.isEqual(to: secondaryLeftOffset) { - leftOffset = ceil(secondaryLeftOffset) + var startIndex: Int? + var currentIndex: Int? + + let nsString = (attributedString.string as NSString) + nsString.enumerateSubstrings(in: range, options: .byComposedCharacterSequences) { substring, range, _, _ in + if let substring = substring, substring.rangeOfCharacter(from: .whitespacesAndNewlines) != nil { + if let currentStartIndex = startIndex { + startIndex = nil + let endIndex = range.location + addSpoiler(line: coreTextLine, ascent: ascent, descent: descent, startIndex: currentStartIndex, endIndex: endIndex) + } + } else if startIndex == nil { + startIndex = range.location } - - var secondaryRightOffset: CGFloat = 0.0 - let rawRighOffset = CTLineGetOffsetForStringIndex(coreTextLine, endIndex, &secondaryRightOffset) - var rightOffset = ceil(rawRighOffset) - if !rawRighOffset.isEqual(to: secondaryRightOffset) { - rightOffset = ceil(secondaryRightOffset) - } - - spoilers.append(TextNodeSpoiler(range: range, frame: CGRect(x: min(leftOffset, rightOffset), y: descent - (ascent + descent), width: abs(rightOffset - leftOffset), height: ascent + descent))) + currentIndex = range.location + range.length + } + + if let currentStartIndex = startIndex, let currentIndex = currentIndex { + startIndex = nil + let endIndex = currentIndex + addSpoiler(line: coreTextLine, ascent: ascent, descent: descent, startIndex: currentStartIndex, endIndex: endIndex) } } else if let _ = attributes[NSAttributedString.Key.strikethroughStyle] { let lowerX = floor(CTLineGetOffsetForStringIndex(coreTextLine, range.location, nil)) @@ -1098,26 +1117,28 @@ public class TextNode: ASDisplayNode { var ascent: CGFloat = 0.0 var descent: CGFloat = 0.0 CTLineGetTypographicBounds(coreTextLine, &ascent, &descent, nil) + + var startIndex: Int? + var currentIndex: Int? - (attributedString.string as NSString).enumerateSubstrings(in: range, options: .byWords) { _, range, _, _ in - let startIndex = range.location - let endIndex = range.location + range.length - - var secondaryLeftOffset: CGFloat = 0.0 - let rawLeftOffset = CTLineGetOffsetForStringIndex(coreTextLine, startIndex, &secondaryLeftOffset) - var leftOffset = ceil(rawLeftOffset) - if !rawLeftOffset.isEqual(to: secondaryLeftOffset) { - leftOffset = ceil(secondaryLeftOffset) + let nsString = (attributedString.string as NSString) + nsString.enumerateSubstrings(in: range, options: .byComposedCharacterSequences) { substring, range, _, _ in + if let substring = substring, substring.rangeOfCharacter(from: .whitespacesAndNewlines) != nil { + if let currentStartIndex = startIndex { + startIndex = nil + let endIndex = range.location + addSpoiler(line: coreTextLine, ascent: ascent, descent: descent, startIndex: currentStartIndex, endIndex: endIndex) + } + } else if startIndex == nil { + startIndex = range.location } - - var secondaryRightOffset: CGFloat = 0.0 - let rawRighOffset = CTLineGetOffsetForStringIndex(coreTextLine, endIndex, &secondaryRightOffset) - var rightOffset = ceil(rawRighOffset) - if !rawRighOffset.isEqual(to: secondaryRightOffset) { - rightOffset = ceil(secondaryRightOffset) - } - - spoilers.append(TextNodeSpoiler(range: range, frame: CGRect(x: min(leftOffset, rightOffset), y: descent - (ascent + descent), width: abs(rightOffset - leftOffset), height: ascent + descent))) + currentIndex = range.location + range.length + } + + if let currentStartIndex = startIndex, let currentIndex = currentIndex { + startIndex = nil + let endIndex = currentIndex + addSpoiler(line: coreTextLine, ascent: ascent, descent: descent, startIndex: currentStartIndex, endIndex: endIndex) } } else if let _ = attributes[NSAttributedString.Key.strikethroughStyle] { let lowerX = floor(CTLineGetOffsetForStringIndex(coreTextLine, range.location, nil)) diff --git a/submodules/GalleryUI/BUILD b/submodules/GalleryUI/BUILD index d98f10b4c9..87f0008595 100644 --- a/submodules/GalleryUI/BUILD +++ b/submodules/GalleryUI/BUILD @@ -38,6 +38,7 @@ swift_library( "//submodules/TextSelectionNode:TextSelectionNode", "//submodules/Speak:Speak", "//submodules/UndoUI:UndoUI", + "//submodules/InvisibleInkDustNode:InvisibleInkDustNode", ], visibility = [ "//visibility:public", diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index eee044e16d..c3cb3842e9 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -21,6 +21,7 @@ import UrlEscaping import UndoUI import ManagedAnimationNode import TelegramUniversalVideoContent +import InvisibleInkDustNode private let deleteImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: .white) private let actionImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: .white) @@ -130,6 +131,9 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll private let scrollNode: ASScrollNode private let textNode: ImmediateTextNode + private var spoilerTextNode: ImmediateTextNode? + private var dustNode: InvisibleInkDustNode? + private let authorNameNode: ASTextNode private let dateNode: ASTextNode private let backwardButton: PlaybackButtonNode @@ -713,6 +717,41 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll } } + private func updateSpoilers(textFrame: CGRect) { + if let textLayout = self.textNode.cachedLayout, !textLayout.spoilers.isEmpty { + if self.spoilerTextNode == nil { + let spoilerTextNode = ImmediateTextNode() + spoilerTextNode.attributedText = textNode.attributedText + spoilerTextNode.linkHighlightColor = UIColor(rgb: 0x5ac8fa, alpha: 0.2) + spoilerTextNode.displaySpoilers = true + spoilerTextNode.isHidden = false + spoilerTextNode.alpha = 0.0 + spoilerTextNode.isUserInteractionEnabled = false + + self.spoilerTextNode = spoilerTextNode + self.textNode.supernode?.insertSubnode(spoilerTextNode, aboveSubnode: self.textNode) + + let dustNode = InvisibleInkDustNode(textNode: spoilerTextNode) + self.dustNode = dustNode + spoilerTextNode.supernode?.insertSubnode(dustNode, aboveSubnode: spoilerTextNode) + + } + if let dustNode = self.dustNode { + dustNode.update(size: textFrame.size, color: .white, rects: textLayout.spoilers.map { $0.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) }) + dustNode.frame = textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0) + } + } else { + if let spoilerTextNode = self.spoilerTextNode { + self.spoilerTextNode = nil + spoilerTextNode.removeFromSupernode() + } + if let dustNode = self.dustNode { + self.dustNode = nil + dustNode.removeFromSupernode() + } + } + } + func setWebPage(_ webPage: TelegramMediaWebpage, media: Media) { self.currentWebPageAndMedia = (webPage, media) } @@ -763,7 +802,9 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll let sideInset: CGFloat = 8.0 + leftInset let topInset: CGFloat = 8.0 let textBottomInset: CGFloat = 8.0 - let textSize = self.textNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude)) + + let constrainSize = CGSize(width: width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude) + let textSize = self.textNode.updateLayout(constrainSize) var textOffset: CGFloat = 0.0 if displayCaption { @@ -809,8 +850,14 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll } } textFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset + textOffset), size: textSize) + + self.updateSpoilers(textFrame: textFrame) + + let _ = self.spoilerTextNode?.updateLayout(constrainSize) + if self.textNode.frame != textFrame { self.textNode.frame = textFrame + self.spoilerTextNode?.frame = textFrame } } diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index ac3c045fa6..824f19c07b 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -764,6 +764,12 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { } } } + + override func adjustForPreviewing() { + super.adjustForPreviewing() + + self.recognitionOverlayContentNode.isHidden = true + } } /*private func tileRectForImage(_ mappedImage: CGImage, rect: CGRect) -> CGRect { diff --git a/submodules/ImageContentAnalysis/Sources/ImageContentAnalysis.swift b/submodules/ImageContentAnalysis/Sources/ImageContentAnalysis.swift index eb0bf757a8..8ebeb58f25 100644 --- a/submodules/ImageContentAnalysis/Sources/ImageContentAnalysis.swift +++ b/submodules/ImageContentAnalysis/Sources/ImageContentAnalysis.swift @@ -276,9 +276,9 @@ public struct RecognizedContent: Codable { } } -private func recognizeContent(in image: UIImage) -> Signal<[RecognizedContent], NoError> { +private func recognizeContent(in image: UIImage?) -> Signal<[RecognizedContent], NoError> { if #available(iOS 11.0, *) { - guard let cgImage = image.cgImage else { + guard let cgImage = image?.cgImage else { return .complete() } return Signal { subscriber in @@ -339,13 +339,15 @@ public func recognizedContent(postbox: Postbox, image: @escaping () -> UIImage?, |> mapToSignal { cachedContent -> Signal<[RecognizedContent], NoError> in if let cachedContent = cachedContent { return .single(cachedContent.results) - } else if let image = image() { - return recognizeContent(in: image) - |> beforeNext { results in - let _ = updateCachedImageRecognizedContent(postbox: postbox, messageId: messageId, content: CachedImageRecognizedContent(results: results)).start() - } } else { - return .single([]) + return (.complete() + |> delay(0.3, queue: Queue.concurrentDefaultQueue())) + |> then( + recognizeContent(in: image()) + |> beforeNext { results in + let _ = updateCachedImageRecognizedContent(postbox: postbox, messageId: messageId, content: CachedImageRecognizedContent(results: results)).start() + } + ) } } } diff --git a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift index 91a408c9f2..cdded32c83 100644 --- a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift +++ b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift @@ -60,6 +60,8 @@ public class InvisibleInkDustNode: ASDisplayNode { public var isRevealedUpdated: (Bool) -> Void = { _ in } + public var isRevealed = false + public init(textNode: TextNode) { self.textNode = textNode @@ -141,13 +143,12 @@ public class InvisibleInkDustNode: ASDisplayNode { self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tap(_:)))) } - private var revealed = false @objc private func tap(_ gestureRecognizer: UITapGestureRecognizer) { - guard let (size, _, _) = self.currentParams, !self.revealed else { + guard let (size, _, _) = self.currentParams, !self.isRevealed else { return } - self.revealed = true + self.isRevealed = true let position = gestureRecognizer.location(in: self.view) self.emitterLayer?.setValue(true, forKeyPath: "emitterBehaviors.fingerAttractor.enabled") @@ -185,8 +186,11 @@ public class InvisibleInkDustNode: ASDisplayNode { self.emitterMaskFillNode.layer.removeAllAnimations() } - Queue.mainQueue().after(4.0 * UIView.animationDurationFactor()) { - self.revealed = false + + let textLength = CGFloat((self.textNode?.cachedLayout?.attributedString?.string ?? "").count) + let timeToRead = min(45.0, ceil(max(4.0, textLength * 0.04))) + Queue.mainQueue().after(timeToRead * UIView.animationDurationFactor()) { + self.isRevealed = false self.isRevealedUpdated(false) let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .linear) diff --git a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift index 011d653d9d..6f0b95f0f4 100644 --- a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift +++ b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift @@ -69,7 +69,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode { public var icon: UIImage? { didSet { - self.iconNode.image = icon + self.iconNode.image = generateTintedImage(image: self.iconNode.image, color: self.theme.foregroundColor) } } @@ -111,7 +111,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode { self.iconNode = ASImageNode() self.iconNode.displaysAsynchronously = false - self.iconNode.image = icon + self.iconNode.image = generateTintedImage(image: icon, color: self.theme.foregroundColor) super.init() @@ -215,6 +215,8 @@ public final class SolidRoundedButtonNode: ASDisplayNode { self.titleNode.attributedText = NSAttributedString(string: self.title ?? "", font: self.font == .bold ? Font.semibold(self.fontSize) : Font.regular(self.fontSize), textColor: theme.foregroundColor) self.subtitleNode.attributedText = NSAttributedString(string: self.subtitle ?? "", font: Font.regular(14.0), textColor: theme.foregroundColor) + self.iconNode.image = generateTintedImage(image: self.iconNode.image, color: theme.foregroundColor) + if let width = self.validLayout { _ = self.updateLayout(width: width, transition: .immediate) } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index f78c5927af..0ce59576db 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -10244,6 +10244,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return nil } } + controller.getCaptionPanelView = { [weak self] in + return self?.getCaptionPanelView() + } strongSelf.effectiveNavigationController?.pushViewController(controller) } }, presentSelectionLimitExceeded: { @@ -10350,6 +10353,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return nil } } + controller.getCaptionPanelView = { [weak self] in + return self?.getCaptionPanelView() + } strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) } }) diff --git a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift index afcbf68a04..167cdfa360 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift @@ -460,7 +460,9 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { override func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { let textNodeFrame = self.textNode.frame if let (index, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) { - if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { + if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler)], !(self.dustNode?.isRevealed ?? true) { + return .none + } else if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { var concealed = true if let (attributeText, fullText) = self.textNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) { concealed = !doesUrlMatchText(url: url, text: attributeText, fullText: fullText) @@ -517,6 +519,7 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { override func updateTouchesAtPoint(_ point: CGPoint?) { if let item = self.item { var rects: [CGRect]? + var spoilerRects: [CGRect]? if let point = point { let textNodeFrame = self.textNode.frame if let (index, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) { @@ -535,10 +538,15 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { break } } + if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler)] { + spoilerRects = self.textNode.attributeRects(name: TelegramTextAttributes.Spoiler, at: index) + } } } - if let rects = rects { + if let spoilerRects = spoilerRects, !spoilerRects.isEmpty, !(self.dustNode?.isRevealed ?? true) { + + } else if let rects = rects { let linkHighlightingNode: LinkHighlightingNode if let current = self.linkHighlightingNode { linkHighlightingNode = current diff --git a/submodules/TelegramUI/Sources/ChatTextInputMenu.swift b/submodules/TelegramUI/Sources/ChatTextInputMenu.swift index 4929e88372..630f06b617 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputMenu.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputMenu.swift @@ -31,9 +31,9 @@ final class ChatTextInputMenu { UIMenuItem(title: self.stringItalic, action: Selector(("formatAttributesItalic:"))), UIMenuItem(title: self.stringMonospace, action: Selector(("formatAttributesMonospace:"))), UIMenuItem(title: self.stringLink, action: Selector(("formatAttributesLink:"))), + UIMenuItem(title: self.stringSpoiler, action: Selector(("formatAttributesSpoiler:"))), UIMenuItem(title: self.stringStrikethrough, action: Selector(("formatAttributesStrikethrough:"))), - UIMenuItem(title: self.stringUnderline, action: Selector(("formatAttributesUnderline:"))), - UIMenuItem(title: self.stringSpoiler, action: Selector(("formatAttributesSpoiler:"))) + UIMenuItem(title: self.stringUnderline, action: Selector(("formatAttributesUnderline:"))) ] } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 5ccb99dd7d..cac743bd4c 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -984,7 +984,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese } if let aboutText = aboutText { - items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: presentationData.strings.Channel_AboutItem, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPublicBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: { + items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: ItemAbout, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPublicBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: { interaction.requestLayout() })) } @@ -1034,7 +1034,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese } if let aboutText = aboutText { - items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.PeerInfo_GroupAboutItem, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPublicBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: { + items[.peerInfo]!.append(PeerInfoScreenLabeledValueItem(id: 0, label: presentationData.strings.Channel_Info_Description, text: aboutText, textColor: .primary, textBehavior: .multiLine(maxLines: 100, enabledEntities: enabledPublicBioEntities), action: nil, longTapAction: bioContextAction, linkItemAction: bioLinkAction, requestLayout: { interaction.requestLayout() })) } diff --git a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift index 6cb4c144ce..1a3d5022b4 100644 --- a/submodules/TextFormat/Sources/ChatTextInputAttributes.swift +++ b/submodules/TextFormat/Sources/ChatTextInputAttributes.swift @@ -86,7 +86,7 @@ public func textAttributedStringForStateText(_ stateText: NSAttributedString, fo result.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: range) } else if key == ChatTextInputAttributes.spoiler { result.addAttribute(key, value: value, range: range) - result.addAttribute(NSAttributedString.Key.backgroundColor, value: textColor.withAlphaComponent(0.15), range: fullRange) + result.addAttribute(NSAttributedString.Key.backgroundColor, value: textColor.withAlphaComponent(0.15), range: range) } } @@ -472,7 +472,7 @@ public func refreshChatTextInputAttributes(_ textNode: ASEditableTextNode, theme textNode.textView.textStorage.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: range) } else if key == ChatTextInputAttributes.spoiler { textNode.textView.textStorage.addAttribute(key, value: value, range: range) - textNode.textView.textStorage.addAttribute(NSAttributedString.Key.backgroundColor, value: theme.chat.inputPanel.primaryTextColor.withAlphaComponent(0.15), range: fullRange) + textNode.textView.textStorage.addAttribute(NSAttributedString.Key.backgroundColor, value: theme.chat.inputPanel.primaryTextColor.withAlphaComponent(0.15), range: range) } } @@ -564,7 +564,7 @@ public func refreshGenericTextInputAttributes(_ textNode: ASEditableTextNode, th textNode.textView.textStorage.addAttribute(NSAttributedString.Key.underlineStyle, value: NSUnderlineStyle.single.rawValue as NSNumber, range: range) } else if key == ChatTextInputAttributes.spoiler { textNode.textView.textStorage.addAttribute(key, value: value, range: range) - textNode.textView.textStorage.addAttribute(NSAttributedString.Key.backgroundColor, value: theme.chat.inputPanel.primaryTextColor.withAlphaComponent(0.15), range: fullRange) + textNode.textView.textStorage.addAttribute(NSAttributedString.Key.backgroundColor, value: UIColor.clear, range: range) } } diff --git a/submodules/WebSearchUI/Sources/LegacyWebSearchGallery.swift b/submodules/WebSearchUI/Sources/LegacyWebSearchGallery.swift index ba3e9c36dc..419a98b01e 100644 --- a/submodules/WebSearchUI/Sources/LegacyWebSearchGallery.swift +++ b/submodules/WebSearchUI/Sources/LegacyWebSearchGallery.swift @@ -312,11 +312,14 @@ private func galleryItems(account: Account, results: [ChatContextResult], curren return (galleryItems, focusItem) } -func presentLegacyWebSearchGallery(context: AccountContext, peer: EnginePeer?, chatLocation: ChatLocation?, presentationData: PresentationData, results: [ChatContextResult], current: ChatContextResult, selectionContext: TGMediaSelectionContext?, editingContext: TGMediaEditingContext, updateHiddenMedia: @escaping (String?) -> Void, initialLayout: ContainerViewLayout?, transitionHostView: @escaping () -> UIView?, transitionView: @escaping (ChatContextResult) -> UIView?, completed: @escaping (ChatContextResult) -> Void, presentStickers: ((@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?)?, present: (ViewController, Any?) -> Void) { +func presentLegacyWebSearchGallery(context: AccountContext, peer: EnginePeer?, chatLocation: ChatLocation?, presentationData: PresentationData, results: [ChatContextResult], current: ChatContextResult, selectionContext: TGMediaSelectionContext?, editingContext: TGMediaEditingContext, updateHiddenMedia: @escaping (String?) -> Void, initialLayout: ContainerViewLayout?, transitionHostView: @escaping () -> UIView?, transitionView: @escaping (ChatContextResult) -> UIView?, completed: @escaping (ChatContextResult) -> Void, presentStickers: ((@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?)?, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, present: (ViewController, Any?) -> Void) { let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil) legacyController.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style let paintStickersContext = LegacyPaintStickersContext(context: context) + paintStickersContext.captionPanelView = { + return getCaptionPanelView() + } paintStickersContext.presentStickersController = { completion in if let presentStickers = presentStickers { return presentStickers({ file, animated, view, rect in diff --git a/submodules/WebSearchUI/Sources/WebSearchController.swift b/submodules/WebSearchUI/Sources/WebSearchController.swift index 776dd46a4a..ec493852d1 100644 --- a/submodules/WebSearchUI/Sources/WebSearchController.swift +++ b/submodules/WebSearchUI/Sources/WebSearchController.swift @@ -155,6 +155,12 @@ public final class WebSearchController: ViewController { } } + public var getCaptionPanelView: () -> TGCaptionPanelView? = { return nil } { + didSet { + self.controllerNode.getCaptionPanelView = self.getCaptionPanelView + } + } + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, peer: EnginePeer?, chatLocation: ChatLocation?, configuration: SearchBotsConfiguration, mode: WebSearchControllerMode) { self.context = context self.mode = mode diff --git a/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift b/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift index 176e6c63fe..313e263d48 100644 --- a/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift +++ b/submodules/WebSearchUI/Sources/WebSearchControllerNode.swift @@ -170,6 +170,7 @@ class WebSearchControllerNode: ASDisplayNode { var dismissInput: (() -> Void)? var presentStickers: ((@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?)? + var getCaptionPanelView: () -> TGCaptionPanelView? = { return nil } init(context: AccountContext, presentationData: PresentationData, controllerInteraction: WebSearchControllerInteraction, peer: EnginePeer?, chatLocation: ChatLocation?, mode: WebSearchMode) { self.context = context @@ -700,7 +701,7 @@ class WebSearchControllerNode: ASDisplayNode { strongSelf.controllerInteraction.sendSelected(results, result) strongSelf.cancel?() } - }, presentStickers: self.presentStickers, present: present) + }, presentStickers: self.presentStickers, getCaptionPanelView: self.getCaptionPanelView, present: present) } } else { if let results = self.currentProcessedResults?.results {