From 34bd339d8cdae61a9668ae0fd6499853f9c4194f Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 29 Oct 2023 20:30:26 +0400 Subject: [PATCH 01/12] Various improvements --- .../AttachmentTextInputPanelNode.swift | 14 +++++++++- .../Sources/CreatePollTextInputItem.swift | 7 +++++ submodules/Display/Source/TextNode.swift | 6 ++++- .../ChatInputTextViewImpl.h | 1 + .../Sources/ChatInputTextViewImpl.m | 14 ++++++++++ .../Sources/ChatInputTextNode.swift | 21 +++++++++++++++ .../Sources/ShareExtensionContext.swift | 26 ++++++++++++++++++- .../Sources/ChatTextInputPanelNode.swift | 21 ++++++++------- .../TextInputMenu/Sources/TextInputMenu.swift | 9 ++++++- .../Sources/TextSelectionNode.swift | 6 ++--- 10 files changed, 108 insertions(+), 17 deletions(-) diff --git a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift index 5af2ccc8a7..d4e5df3f7b 100644 --- a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift +++ b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift @@ -1473,7 +1473,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS return ASEditableTextNodeTargetForAction(target: nil) } } - } else if action == #selector(self.formatAttributesBold(_:)) || action == #selector(self.formatAttributesItalic(_:)) || action == #selector(self.formatAttributesMonospace(_:)) || action == #selector(self.formatAttributesLink(_:)) || action == #selector(self.formatAttributesStrikethrough(_:)) || action == #selector(self.formatAttributesUnderline(_:)) || action == #selector(self.formatAttributesSpoiler(_:)) { + } else if action == #selector(self.formatAttributesBold(_:)) || action == #selector(self.formatAttributesItalic(_:)) || action == #selector(self.formatAttributesMonospace(_:)) || action == #selector(self.formatAttributesLink(_:)) || action == #selector(self.formatAttributesStrikethrough(_:)) || action == #selector(self.formatAttributesUnderline(_:)) || action == #selector(self.formatAttributesSpoiler(_:)) || action == #selector(self.formatAttributesQuote(_:)) { if case .format = self.inputMenu.state { return ASEditableTextNodeTargetForAction(target: self) } else { @@ -1646,6 +1646,14 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS self.updateSpoilersRevealed(animated: animated) } + @objc func formatAttributesQuote(_ sender: Any) { + self.inputMenu.back() + + self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in + return (chatTextInputAddFormattingAttribute(current, attribute: ChatTextInputAttributes.quote), inputMode) + } + } + public func chatInputTextNode(shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { guard let editableTextNode = self.textInputNode else { return true @@ -1728,6 +1736,10 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS return true } + public func chatInputTextNodeTargetForAction(action: Selector) -> ChatInputTextNode.TargetForAction? { + return nil + } + @objc public func editableTextNodeShouldPaste(_ editableTextNode: ASEditableTextNode) -> Bool { return self.chatInputTextNodeShouldPaste() } diff --git a/submodules/ComposePollUI/Sources/CreatePollTextInputItem.swift b/submodules/ComposePollUI/Sources/CreatePollTextInputItem.swift index 91d354d021..74c6f78c9f 100644 --- a/submodules/ComposePollUI/Sources/CreatePollTextInputItem.swift +++ b/submodules/ComposePollUI/Sources/CreatePollTextInputItem.swift @@ -554,6 +554,13 @@ public class CreatePollTextInputItemNode: ListViewItemNode, ASEditableTextNodeDe } } + @objc func formatAttributesQuote(_ sender: Any) { + self.inputMenu.back() + if let item = self.item { + chatTextInputAddFormattingAttribute(item: item, textNode: self.textNode, theme: item.presentationData.theme, attribute: ChatTextInputAttributes.quote) + } + } + public func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { if let item = self.item { if text.count > 1, let processPaste = item.processPaste { diff --git a/submodules/Display/Source/TextNode.swift b/submodules/Display/Source/TextNode.swift index 6d3c386c9f..cfc904d4a3 100644 --- a/submodules/Display/Source/TextNode.swift +++ b/submodules/Display/Source/TextNode.swift @@ -947,7 +947,11 @@ public final class TextNodeLayout: NSObject { rects.append((lineFrame, CGRect(origin: CGPoint(x: lineFrame.minX + min(leftOffset, rightOffset) + self.insets.left, y: lineFrame.minY + self.insets.top), size: CGSize(width: width, height: lineFrame.size.height)))) } } - if !rects.isEmpty, let startEdge = startEdge, let endEdge = endEdge { + if !rects.isEmpty, var startEdge = startEdge, var endEdge = endEdge { + startEdge.x += self.insets.left + startEdge.y += self.insets.top + endEdge.x += self.insets.left + endEdge.y += self.insets.top return (rects.map { $1 }, startEdge, endEdge) } return nil diff --git a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl/PublicHeaders/ChatInputTextViewImpl/ChatInputTextViewImpl.h b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl/PublicHeaders/ChatInputTextViewImpl/ChatInputTextViewImpl.h index 528c45c33f..f4a0508002 100755 --- a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl/PublicHeaders/ChatInputTextViewImpl/ChatInputTextViewImpl.h +++ b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl/PublicHeaders/ChatInputTextViewImpl/ChatInputTextViewImpl.h @@ -17,6 +17,7 @@ @property (nonatomic, copy) bool (^ _Nullable shouldCopy)(); @property (nonatomic, copy) bool (^ _Nullable shouldPaste)(); @property (nonatomic, copy) bool (^ _Nullable shouldRespondToAction)(SEL _Nullable); +@property (nonatomic, copy) ChatInputTextViewImplTargetForAction * _Nullable (^ _Nullable targetForAction)(SEL _Nullable); @property (nonatomic, copy) bool (^ _Nullable shouldReturn)(); @property (nonatomic, copy) void (^ _Nullable backspaceWhileEmpty)(); @property (nonatomic, copy) void (^ _Nullable dropAutocorrectioniOS16)(); diff --git a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl/Sources/ChatInputTextViewImpl.m b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl/Sources/ChatInputTextViewImpl.m index 8e96504a12..eb3e9c739b 100755 --- a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl/Sources/ChatInputTextViewImpl.m +++ b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/ChatInputTextViewImpl/Sources/ChatInputTextViewImpl.m @@ -68,6 +68,13 @@ - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { + if (_targetForAction) { + ChatInputTextViewImplTargetForAction *result = _targetForAction(action); + if (result) { + return result.target != nil; + } + } + if (_shouldRespondToAction) { if (!_shouldRespondToAction(action)) { return false; @@ -102,6 +109,13 @@ } - (id)targetForAction:(SEL)action withSender:(id)__unused sender { + if (_targetForAction) { + ChatInputTextViewImplTargetForAction *result = _targetForAction(action); + if (result) { + return result.target; + } + } + return [super targetForAction:action withSender:sender]; } diff --git a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift index c293c74e2f..cebdc34511 100644 --- a/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatInputTextNode/Sources/ChatInputTextNode.swift @@ -22,9 +22,18 @@ public protocol ChatInputTextNodeDelegate: AnyObject { func chatInputTextNodeShouldPaste() -> Bool func chatInputTextNodeShouldRespondToAction(action: Selector) -> Bool + func chatInputTextNodeTargetForAction(action: Selector) -> ChatInputTextNode.TargetForAction? } open class ChatInputTextNode: ASDisplayNode, UITextViewDelegate { + public final class TargetForAction { + public let target: Any? + + public init(target: Any?) { + self.target = target + } + } + public weak var delegate: ChatInputTextNodeDelegate? { didSet { self.textView.customDelegate = self.delegate @@ -124,6 +133,18 @@ open class ChatInputTextNode: ASDisplayNode, UITextViewDelegate { return true } } + self.textView.targetForAction = { [weak self] action in + guard let self, let action else { + return nil + } + if let delegate = self.delegate { + return delegate.chatInputTextNodeTargetForAction(action: action).flatMap { value in + return ChatInputTextViewImplTargetForAction(target: value.target) + } + } else { + return nil + } + } } public func resetInitialPrimaryLanguage() { diff --git a/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift b/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift index 7066c6aff7..2d2654d7a6 100644 --- a/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift +++ b/submodules/TelegramUI/Components/ShareExtensionContext/Sources/ShareExtensionContext.swift @@ -267,6 +267,31 @@ public class ShareRootControllerImpl { let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata", isTemporary: true, isReadOnly: false, useCaches: false, removeDatabaseOnError: false) initializeAccountManagement() + do { + let semaphore = DispatchSemaphore(value: 0) + var loggingSettings = LoggingSettings.defaultSettings + if self.initializationData.appBuildType == .internal { + loggingSettings = LoggingSettings(logToFile: true, logToConsole: false, redactSensitiveData: true) + } + let _ = (accountManager.transaction { transaction -> LoggingSettings? in + if let value = transaction.getSharedData(SharedDataKeys.loggingSettings)?.get(LoggingSettings.self) { + return value + } else { + return nil + } + }).start(next: { value in + if let value { + loggingSettings = value + } + semaphore.signal() + }) + semaphore.wait() + + Logger.shared.logToFile = loggingSettings.logToFile + Logger.shared.logToConsole = loggingSettings.logToConsole + Logger.shared.redactSensitiveData = loggingSettings.redactSensitiveData + } + var initialPresentationDataAndSettings: InitialPresentationDataAndSettings? let semaphore = DispatchSemaphore(value: 0) let systemUserInterfaceStyle: WindowUserInterfaceStyle @@ -306,7 +331,6 @@ public class ShareRootControllerImpl { |> mapToSignal { sharedContext, loggingSettings -> Signal<(SharedAccountContextImpl, Account, [AccountWithInfo]), ShareAuthorizationError> in Logger.shared.logToFile = loggingSettings.logToFile Logger.shared.logToConsole = loggingSettings.logToConsole - Logger.shared.redactSensitiveData = loggingSettings.redactSensitiveData return combineLatest(sharedContext.activeAccountsWithInfo, accountManager.transaction { transaction -> (Set, PeerId?) in diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 82aa0d7393..d522560103 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -772,10 +772,12 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch self.textInputViewInternalInsets = UIEdgeInsets(top: 1.0, left: 13.0, bottom: 1.0, right: 13.0) var hasSpoilers = true + var hasQuotes = true if presentationInterfaceState.chatLocation.peerId?.namespace == Namespaces.Peer.SecretChat { hasSpoilers = false + hasQuotes = false } - self.inputMenu = TextInputMenu(hasSpoilers: hasSpoilers) + self.inputMenu = TextInputMenu(hasSpoilers: hasSpoilers, hasQuotes: hasQuotes) self.clippingNode = ASDisplayNode() self.clippingNode.clipsToBounds = true @@ -3954,18 +3956,17 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch } public func chatInputTextNodeShouldRespondToAction(action: Selector) -> Bool { - guard let textInputNode = self.textInputNode else { - return true - } - let _ = textInputNode - - /*if textInputNode.attributedText == nil || textInputNode.attributedText!.length == 0 || textInputNode.selectedRange.length == 0 { - print("action: \(action)") - }*/ - return true } + public func chatInputTextNodeTargetForAction(action: Selector) -> ChatInputTextNode.TargetForAction? { + if let target = self.editableTextNodeTarget(forAction: action) { + return ChatInputTextNode.TargetForAction(target: target.target) + } else { + return nil + } + } + func chatInputTextNodeShouldPaste() -> Bool { let pasteboard = UIPasteboard.general diff --git a/submodules/TextInputMenu/Sources/TextInputMenu.swift b/submodules/TextInputMenu/Sources/TextInputMenu.swift index 9e13cccfe9..6046e69d6d 100644 --- a/submodules/TextInputMenu/Sources/TextInputMenu.swift +++ b/submodules/TextInputMenu/Sources/TextInputMenu.swift @@ -16,8 +16,10 @@ public final class TextInputMenu { private var stringStrikethrough: String = "Strikethrough" private var stringUnderline: String = "Underline" private var stringSpoiler: String = "Spoiler" + private var stringQuote: String = "Quote" private let hasSpoilers: Bool + private let hasQuotes: Bool public private(set) var state: State = .inactive { didSet { @@ -39,6 +41,9 @@ public final class TextInputMenu { if self.hasSpoilers { menuItems.insert(UIMenuItem(title: self.stringSpoiler, action: Selector(("formatAttributesSpoiler:"))), at: 0) } + if self.hasQuotes { + menuItems.insert(UIMenuItem(title: self.stringQuote, action: Selector(("formatAttributesQuote:"))), at: 0) + } UIMenuController.shared.menuItems = menuItems } @@ -48,8 +53,9 @@ public final class TextInputMenu { private var observer: NSObjectProtocol? - public init(hasSpoilers: Bool = false) { + public init(hasSpoilers: Bool = false, hasQuotes: Bool = false) { self.hasSpoilers = hasSpoilers + self.hasQuotes = hasQuotes self.observer = NotificationCenter.default.addObserver(forName: UIMenuController.didHideMenuNotification, object: nil, queue: nil, using: { [weak self] _ in self?.back() }) @@ -69,6 +75,7 @@ public final class TextInputMenu { self.stringStrikethrough = strings.TextFormat_Strikethrough self.stringUnderline = strings.TextFormat_Underline self.stringSpoiler = strings.TextFormat_Spoiler + self.stringQuote = strings.TextFormat_Quote } public func activate() { diff --git a/submodules/TextSelectionNode/Sources/TextSelectionNode.swift b/submodules/TextSelectionNode/Sources/TextSelectionNode.swift index fd6cf39c1a..b65d15b18f 100644 --- a/submodules/TextSelectionNode/Sources/TextSelectionNode.swift +++ b/submodules/TextSelectionNode/Sources/TextSelectionNode.swift @@ -589,7 +589,7 @@ public final class TextSelectionNode: ASDisplayNode { highlightOverlay.innerRadius = 2.0 highlightOverlay.outerRadius = 2.0 highlightOverlay.inset = 1.0 - highlightOverlay.useModernPathCalculation = true + highlightOverlay.useModernPathCalculation = false self.highlightOverlay = highlightOverlay self.highlightAreaNode.addSubnode(highlightOverlay) @@ -597,8 +597,8 @@ public final class TextSelectionNode: ASDisplayNode { highlightOverlay.frame = self.bounds highlightOverlay.updateRects(rects) if let image = self.leftKnob.image { - self.leftKnob.frame = CGRect(origin: CGPoint(x: floor(startEdge.x - image.size.width / 2.0), y: startEdge.y + 1.0 - 12.0), size: CGSize(width: image.size.width, height: self.theme.knobDiameter + startEdge.height + 2.0)) - self.rightKnob.frame = CGRect(origin: CGPoint(x: floor(endEdge.x + 1.0 - image.size.width / 2.0), y: endEdge.y + endEdge.height + 3.0 - (endEdge.height + 2.0)), size: CGSize(width: image.size.width, height: self.theme.knobDiameter + endEdge.height + 2.0)) + self.leftKnob.frame = CGRect(origin: CGPoint(x: floor(startEdge.x - image.size.width / 2.0), y: startEdge.y - self.theme.knobDiameter), size: CGSize(width: image.size.width, height: self.theme.knobDiameter + startEdge.height)) + self.rightKnob.frame = CGRect(origin: CGPoint(x: floor(endEdge.x - image.size.width / 2.0), y: endEdge.y), size: CGSize(width: image.size.width, height: self.theme.knobDiameter + endEdge.height)) } if self.leftKnob.alpha.isZero { highlightOverlay.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue) From a4bf4b7a3b71269c9a321aa1d0cc5f9c1502d110 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sun, 29 Oct 2023 22:55:27 +0400 Subject: [PATCH 02/12] Bump version --- versions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.json b/versions.json index 4442aa64f2..171484ddee 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "10.2", + "app": "10.2.1", "bazel": "6.4.0", "xcode": "15.0" } From 736e0705039373a300cada8442dfcd2c4524f318 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 29 Oct 2023 23:05:26 +0400 Subject: [PATCH 03/12] Bump version --- versions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.json b/versions.json index 4442aa64f2..171484ddee 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "app": "10.2", + "app": "10.2.1", "bazel": "6.4.0", "xcode": "15.0" } From c29cd1bbba8de6a64a862e596116e3c62d11ef59 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 29 Oct 2023 23:05:51 +0400 Subject: [PATCH 04/12] Fix small sticker alpha blur (iOS 17) --- submodules/FastBlur/Sources/FastBlur.m | 7 ++++++- submodules/StickerResources/Sources/StickerResources.swift | 3 +++ submodules/WebPBinding/Sources/UIImage+WebP.m | 5 +++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/submodules/FastBlur/Sources/FastBlur.m b/submodules/FastBlur/Sources/FastBlur.m index 2c7849384f..4c9b887860 100644 --- a/submodules/FastBlur/Sources/FastBlur.m +++ b/submodules/FastBlur/Sources/FastBlur.m @@ -207,18 +207,23 @@ void stickerThumbnailAlphaBlur(int imageWidth, int imageHeight, int imageStride, srcBuffer.rowBytes = imageStride; srcBuffer.data = pixels; + void *tempBytes = malloc(imageHeight * imageStride); + { vImage_Buffer dstBuffer; dstBuffer.width = imageWidth; dstBuffer.height = imageHeight; dstBuffer.rowBytes = imageStride; - dstBuffer.data = pixels; + dstBuffer.data = tempBytes; int boxSize = 2; boxSize = boxSize - (boxSize % 2) + 1; vImageBoxConvolve_ARGB8888(&srcBuffer, &dstBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend); } + + memcpy(pixels, tempBytes, imageHeight * imageStride); + free(tempBytes); } static void modifyImage(void *pixels, unsigned int width, unsigned int height, unsigned int stride, int16_t * _Nonnull matrix) diff --git a/submodules/StickerResources/Sources/StickerResources.swift b/submodules/StickerResources/Sources/StickerResources.swift index 3063bc9cad..b5561992b3 100644 --- a/submodules/StickerResources/Sources/StickerResources.swift +++ b/submodules/StickerResources/Sources/StickerResources.swift @@ -387,6 +387,9 @@ public func chatMessageStickerPackThumbnail(postbox: Postbox, resource: MediaRes public func chatMessageSticker(postbox: Postbox, userLocation: MediaResourceUserLocation, file: TelegramMediaFile, small: Bool, fetched: Bool = false, onlyFullSize: Bool = false, thumbnail: Bool = false, synchronousLoad: Bool = false, colorSpace: CGColorSpace? = nil) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { let signal: Signal, NoError> + + let thumbnail = "".isEmpty + if thumbnail { signal = chatMessageStickerThumbnailData(postbox: postbox, userLocation: userLocation, file: file, synchronousLoad: synchronousLoad) |> map { data -> Tuple3in diff --git a/submodules/WebPBinding/Sources/UIImage+WebP.m b/submodules/WebPBinding/Sources/UIImage+WebP.m index c4a07eed72..f209b7e3b2 100644 --- a/submodules/WebPBinding/Sources/UIImage+WebP.m +++ b/submodules/WebPBinding/Sources/UIImage+WebP.m @@ -10,6 +10,11 @@ return nil; } + UIImage *osImage = [[UIImage alloc] initWithData:imgData scale:1.0]; + if (osImage != nil) { + return osImage; + } + int width = 0, height = 0; if (!WebPGetInfo([imgData bytes], [imgData length], &width, &height)) { NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary]; From 75359a283d58ffe128e006b6887c7521681b0d49 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 29 Oct 2023 23:06:11 +0400 Subject: [PATCH 05/12] Don't use bubble highlight on video messages --- .../Sources/ChatMessageBubbleItemNode.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 38ab36d432..b08fc69702 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -4699,7 +4699,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if let backgroundType = self.backgroundType { let graphics = PresentationResourcesChat.principalGraphics(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) - if self.highlightedState != nil { + if self.highlightedState != nil, !(self.backgroundNode.layer.mask is SimpleLayer) { let backgroundHighlightNode: ChatMessageBackground if let current = self.backgroundHighlightNode { backgroundHighlightNode = current From 53d320d1bfe877d70173fe4d82ebe5b7aaac93f5 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 29 Oct 2023 23:06:30 +0400 Subject: [PATCH 06/12] Fix video message reply and forward info background --- ...atMessageInteractiveInstantVideoNode.swift | 163 ++++++++++++------ 1 file changed, 111 insertions(+), 52 deletions(-) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift index d8dfd8c06a..82efafc83a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -27,6 +27,7 @@ import ChatMessageReplyInfoNode import InstantVideoRadialStatusNode import ChatInstantVideoMessageDurationNode import ChatControllerInteraction +import WallpaperBackgroundNode public struct ChatMessageInstantVideoItemLayoutResult { public let contentSize: CGSize @@ -109,8 +110,9 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { public var viaBotNode: TextNode? public var replyInfoNode: ChatMessageReplyInfoNode? - public var replyBackgroundNode: NavigationBackgroundNode? + public var replyBackgroundContent: WallpaperBubbleBackgroundNode? public var forwardInfoNode: ChatMessageForwardInfoNode? + public var forwardBackgroundContent: WallpaperBubbleBackgroundNode? private var status: FileMediaResourceStatus? private var playerStatus: MediaPlayerStatus? { @@ -240,7 +242,6 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { let viaBotLayout = TextNode.asyncLayout(self.viaBotNode) let makeReplyInfoLayout = ChatMessageReplyInfoNode.asyncLayout(self.replyInfoNode) let makeForwardInfoLayout = ChatMessageForwardInfoNode.asyncLayout(self.forwardInfoNode) - let currentReplyBackgroundNode = self.replyBackgroundNode return { item, width, displaySize, maximumDisplaySize, scaleProgress, statusDisplayType, automaticDownload, avatarInset in var secretVideoPlaceholderBackgroundImage: UIImage? @@ -598,15 +599,14 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { strongSelf.automaticDownload = automaticDownload - var updatedReplyBackgroundNode: NavigationBackgroundNode? - if replyInfoApply != nil || viaBotApply != nil || forwardInfoSizeApply != nil { - if let currentReplyBackgroundNode = currentReplyBackgroundNode { - updatedReplyBackgroundNode = currentReplyBackgroundNode - } else { - updatedReplyBackgroundNode = NavigationBackgroundNode(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: item.controllerInteraction.enableFullTranslucency && dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper)) - } - - updatedReplyBackgroundNode?.updateColor(color: selectDateFillStaticColor(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), enableBlur: item.controllerInteraction.enableFullTranslucency && dateFillNeedsBlur(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper), transition: .immediate) + var needsReplyBackground = false + if replyInfoApply != nil { + needsReplyBackground = true + } + + var needsForwardBackground = false + if viaBotApply != nil || forwardInfoSizeApply != nil { + needsForwardBackground = true } if let updatedAudioTranscriptionState = updatedAudioTranscriptionState { @@ -918,7 +918,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { if let telegramFile = updatedFile, previousAutomaticDownload != automaticDownload, automaticDownload { strongSelf.fetchDisposable.set(messageMediaFileInteractiveFetched(context: item.context, message: item.message, file: telegramFile, userInitiated: false).startStrict()) } - + if let forwardInfo = item.message.forwardInfo, forwardInfo.flags.contains(.isImported) { strongSelf.dateAndStatusNode.pressed = { guard let strongSelf = self else { @@ -930,42 +930,75 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { strongSelf.dateAndStatusNode.pressed = nil } - if let updatedReplyBackgroundNode = updatedReplyBackgroundNode { - if strongSelf.replyBackgroundNode == nil { - strongSelf.replyBackgroundNode = updatedReplyBackgroundNode - strongSelf.addSubnode(updatedReplyBackgroundNode) - } - } else if let replyBackgroundNode = strongSelf.replyBackgroundNode { - replyBackgroundNode.removeFromSupernode() - strongSelf.replyBackgroundNode = nil - } - - var messageInfoSize = CGSize() - if let (viaBotLayout, _) = viaBotApply, forwardInfoSizeApply == nil { - messageInfoSize = CGSize(width: viaBotLayout.size.width + 1.0, height: 0.0) - } - if let (forwardInfoSize, _) = forwardInfoSizeApply { - messageInfoSize = CGSize(width: max(messageInfoSize.width, forwardInfoSize.width + 2.0), height: 0.0) - } - if let (replyInfoSize, _) = replyInfoApply { - messageInfoSize = CGSize(width: max(messageInfoSize.width, replyInfoSize.width), height: 0.0) - } - var width = width if !scaleProgress.isZero { width += avatarInset } + + if needsReplyBackground { + if strongSelf.replyBackgroundContent == nil, let backgroundContent = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) { + backgroundContent.clipsToBounds = true + strongSelf.replyBackgroundContent = backgroundContent + strongSelf.insertSubnode(backgroundContent, at: 0) + } + } else { + if let replyBackgroundContent = strongSelf.replyBackgroundContent { + replyBackgroundContent.removeFromSupernode() + strongSelf.replyBackgroundContent = nil + } + } + + if needsForwardBackground { + if strongSelf.forwardBackgroundContent == nil, let backgroundContent = item.controllerInteraction.presentationContext.backgroundNode?.makeBubbleBackground(for: .free) { + backgroundContent.clipsToBounds = true + strongSelf.forwardBackgroundContent = backgroundContent + strongSelf.insertSubnode(backgroundContent, at: 0) + } + } else { + if let forwardBackgroundContent = strongSelf.forwardBackgroundContent { + forwardBackgroundContent.removeFromSupernode() + strongSelf.forwardBackgroundContent = nil + } + } + + var headersOffset: CGFloat = 0.0 + + var forwardAreaSize = CGSize() + if let (viaBotLayout, _) = viaBotApply, forwardInfoSizeApply == nil { + forwardAreaSize = CGSize(width: viaBotLayout.size.width + 1.0, height: 0.0) + } + if let (forwardInfoSize, _) = forwardInfoSizeApply { + forwardAreaSize = CGSize(width: max(forwardAreaSize.width, forwardInfoSize.width + 2.0), height: 0.0) + } + + var replyAreaSize = CGSize() + if let (replyInfoSize, _) = replyInfoApply { + replyAreaSize = CGSize(width: max(replyAreaSize.width, replyInfoSize.width), height: 0.0) + } + + let edgeInset: CGFloat = 4.0 + let leftInset: CGFloat = 0.0 + let rightInset: CGFloat = 0.0 + + var forwardAreaFrame: CGRect? + var messageInfoSize = CGSize() if let (viaBotLayout, viaBotApply) = viaBotApply, forwardInfoSizeApply == nil { let viaBotNode = viaBotApply() if strongSelf.viaBotNode == nil { strongSelf.viaBotNode = viaBotNode strongSelf.addSubnode(viaBotNode) } + let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (leftInset + edgeInset) : (width - rightInset - forwardAreaSize.width - edgeInset)), y: headersOffset + 8.0), size: viaBotLayout.size) - let viaBotFrame = CGRect(origin: CGPoint(x: (!incoming ? (displayVideoFrame.maxX - width + 6.0) : (width - messageInfoSize.width - bubbleEdgeInset - 9.0 + 10.0)), y: 8.0), size: viaBotLayout.size) - animation.animator.updateFrame(layer: viaBotNode.layer, frame: viaBotFrame, completion: nil) + viaBotNode.frame = viaBotFrame messageInfoSize = CGSize(width: messageInfoSize.width, height: viaBotLayout.size.height) + + if let forwardAreaFrameValue = forwardAreaFrame { + forwardAreaFrame = forwardAreaFrameValue.union(viaBotFrame) + } else { + forwardAreaFrame = viaBotFrame + } } else if let viaBotNode = strongSelf.viaBotNode { viaBotNode.removeFromSupernode() strongSelf.viaBotNode = nil @@ -981,10 +1014,16 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { forwardInfoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } } - let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (displayVideoFrame.maxX - width + 6.0) : (width - messageInfoSize.width - bubbleEdgeInset - 8.0 + 10.0)), y: 8.0 + messageInfoSize.height), size: forwardInfoSize) - animation.animator.updateFrame(layer: forwardInfoNode.layer, frame: forwardInfoFrame, completion: nil) + let forwardInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (displayVideoFrame.minX - forwardAreaSize.width - 4.0) : (displayVideoFrame.maxX + 6.0)), y: headersOffset + 8.0 + messageInfoSize.height), size: forwardInfoSize) + forwardInfoNode.frame = forwardInfoFrame - messageInfoSize = CGSize(width: messageInfoSize.width, height: messageInfoSize.height + forwardInfoSize.height - 1.0) + messageInfoSize = CGSize(width: messageInfoSize.width, height: messageInfoSize.height + forwardInfoSize.height + 8.0) + + if let forwardAreaFrameValue = forwardAreaFrame { + forwardAreaFrame = forwardAreaFrameValue.union(forwardInfoFrame) + } else { + forwardAreaFrame = forwardInfoFrame + } } else if let forwardInfoNode = strongSelf.forwardInfoNode { if animation.isAnimated { if let forwardInfoNode = strongSelf.forwardInfoNode { @@ -999,9 +1038,18 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } } + var forwardBackgroundFrame: CGRect? + if let forwardAreaFrame { + forwardBackgroundFrame = forwardAreaFrame.insetBy(dx: -6.0, dy: -3.0) + } + var replyBackgroundFrame: CGRect? if let (replyInfoSize, replyInfoApply) = replyInfoApply { - let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (displayVideoFrame.maxX - width + 5.0) : (width - messageInfoSize.width - bubbleEdgeInset - 9.0 + 10.0)), y: 8.0 + messageInfoSize.height), size: replyInfoSize) + if headersOffset != 0.0 { + headersOffset += 6.0 + } + + let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (displayVideoFrame.minX - replyInfoSize.width) : (displayVideoFrame.maxX)), y: headersOffset + 8.0 + messageInfoSize.height), size: replyInfoSize) replyBackgroundFrame = replyInfoFrame let replyInfoNode = replyInfoApply(replyInfoFrame.size, false, animation) @@ -1009,7 +1057,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { strongSelf.replyInfoNode = replyInfoNode strongSelf.addSubnode(replyInfoNode) } - animation.animator.updateFrame(layer: replyInfoNode.layer, frame: replyInfoFrame, completion: nil) + replyInfoNode.frame = replyInfoFrame messageInfoSize = CGSize(width: max(messageInfoSize.width, replyInfoSize.width), height: messageInfoSize.height + replyInfoSize.height) } else if let replyInfoNode = strongSelf.replyInfoNode { @@ -1017,20 +1065,25 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { strongSelf.replyInfoNode = nil } - if let replyBackgroundNode = strongSelf.replyBackgroundNode, let replyBackgroundFrame { - let replyBackgroundFrame = replyBackgroundFrame - animation.animator.updateFrame(layer: replyBackgroundNode.layer, frame: replyBackgroundFrame, completion: nil) - - let cornerRadius = replyBackgroundNode.frame.height <= 22.0 ? replyBackgroundNode.frame.height / 2.0 : 8.0 - replyBackgroundNode.update(size: replyBackgroundNode.bounds.size, cornerRadius: cornerRadius, transition: .immediate) + if let backgroundContent = strongSelf.replyBackgroundContent, let replyBackgroundFrame { + backgroundContent.cornerRadius = 4.0 + backgroundContent.frame = replyBackgroundFrame + } + + if let backgroundContent = strongSelf.forwardBackgroundContent, let forwardBackgroundFrame { + backgroundContent.cornerRadius = 4.0 + backgroundContent.frame = forwardBackgroundFrame } let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut) if let viaBotNode = strongSelf.viaBotNode { transition.updateAlpha(node: viaBotNode, alpha: strongSelf.isPlaying ? 0.0 : 1.0) } - if let replyBackgroundNode = strongSelf.replyBackgroundNode { - transition.updateAlpha(node: replyBackgroundNode, alpha: strongSelf.isPlaying ? 0.0 : 1.0) + if let replyBackgroundContent = strongSelf.replyBackgroundContent { + transition.updateAlpha(node: replyBackgroundContent, alpha: strongSelf.isPlaying ? 0.0 : 1.0) + } + if let forwardBackgroundContent = strongSelf.forwardBackgroundContent { + transition.updateAlpha(node: forwardBackgroundContent, alpha: strongSelf.isPlaying ? 0.0 : 1.0) } if let forwardInfoNode = strongSelf.forwardInfoNode { transition.updateAlpha(node: forwardInfoNode, alpha: strongSelf.isPlaying ? 0.0 : 1.0) @@ -1761,8 +1814,11 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { if let viaBotNode = self.viaBotNode { viaBotNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration) } - if let replyBackgroundNode = self.replyBackgroundNode { - replyBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration) + if let replyBackgroundContent = self.replyBackgroundContent { + replyBackgroundContent.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration) + } + if let forwardBackgroundContent = self.replyBackgroundContent { + forwardBackgroundContent.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration) } if let forwardInfoNode = self.forwardInfoNode { forwardInfoNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration) @@ -1866,8 +1922,11 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { if let viaBotNode = self.viaBotNode { viaBotNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration) } - if let replyBackgroundNode = self.replyBackgroundNode { - replyBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration) + if let replyBackgroundContent = self.replyBackgroundContent { + replyBackgroundContent.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration) + } + if let forwardBackgroundContent = self.forwardBackgroundContent { + forwardBackgroundContent.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration) } if let forwardInfoNode = self.forwardInfoNode { forwardInfoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration) From 31e5158b6159b6c4b42e1a63d9e408680cf6f45f Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 29 Oct 2023 23:06:47 +0400 Subject: [PATCH 07/12] Fix quote block pattern alpha gradient --- .../Sources/MessageInlineBlockBackgroundView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView/Sources/MessageInlineBlockBackgroundView.swift b/submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView/Sources/MessageInlineBlockBackgroundView.swift index 8b0ed49bf0..dafe76a82b 100644 --- a/submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView/Sources/MessageInlineBlockBackgroundView.swift +++ b/submodules/TelegramUI/Components/Chat/MessageInlineBlockBackgroundView/Sources/MessageInlineBlockBackgroundView.swift @@ -735,7 +735,7 @@ public final class MessageInlineBlockBackgroundView: UIView { let itemSize = CGSize(width: placement.size / 3.0, height: placement.size / 3.0) patternContentLayer.frame = CGRect(origin: CGPoint(x: size.width - placement.position.x / 3.0 - itemSize.width * 0.5, y: placement.position.y / 3.0 - itemSize.height * 0.5), size: itemSize) - var alphaFraction = abs(placement.position.x) / min(500.0, size.width) + var alphaFraction = abs(placement.position.x / 3.0) / min(500.0, size.width) alphaFraction = min(1.0, max(0.0, alphaFraction)) patternContentLayer.opacity = 0.3 * Float(1.0 - alphaFraction) From 33c655c6d248970c06c538179f65fb46dbaa7618 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 29 Oct 2023 23:20:34 +0400 Subject: [PATCH 08/12] Disable reply on ad messages --- submodules/TelegramUI/Sources/ChatController.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 2915acc7c3..cc4e08bd62 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -3338,6 +3338,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }, setupReply: { [weak self] messageId in self?.interfaceInteraction?.setupReplyMessage(messageId, { _, f in f() }) }, canSetupReply: { [weak self] message in + if message.adAttribute != nil { + return .none + } if !message.flags.contains(.Incoming) { if !message.flags.intersection([.Failed, .Sending, .Unsent]).isEmpty { return .none From 6781334fe101474cb87b42fdc8bc701573fb334f Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 29 Oct 2023 23:26:10 +0400 Subject: [PATCH 09/12] Remove debugging --- submodules/StickerResources/Sources/StickerResources.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/submodules/StickerResources/Sources/StickerResources.swift b/submodules/StickerResources/Sources/StickerResources.swift index b5561992b3..04745ac252 100644 --- a/submodules/StickerResources/Sources/StickerResources.swift +++ b/submodules/StickerResources/Sources/StickerResources.swift @@ -388,8 +388,6 @@ public func chatMessageStickerPackThumbnail(postbox: Postbox, resource: MediaRes public func chatMessageSticker(postbox: Postbox, userLocation: MediaResourceUserLocation, file: TelegramMediaFile, small: Bool, fetched: Bool = false, onlyFullSize: Bool = false, thumbnail: Bool = false, synchronousLoad: Bool = false, colorSpace: CGColorSpace? = nil) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> { let signal: Signal, NoError> - let thumbnail = "".isEmpty - if thumbnail { signal = chatMessageStickerThumbnailData(postbox: postbox, userLocation: userLocation, file: file, synchronousLoad: synchronousLoad) |> map { data -> Tuple3in From 73ce7faf6665da5076a16ac0c65a4572c39940b2 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 29 Oct 2023 23:29:36 +0400 Subject: [PATCH 10/12] Enable text sharing only when copy is available --- .../Sources/ChatMessageTextBubbleContentNode.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift index 05d675a0ae..838dc25275 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift @@ -1201,7 +1201,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { textSelectionNode.enableQuote = enableQuote textSelectionNode.enableTranslate = enableOtherActions - textSelectionNode.enableShare = enableOtherActions + textSelectionNode.enableShare = enableOtherActions && enableCopy textSelectionNode.menuSkipCoordnateConversion = !enableOtherActions self.textSelectionNode = textSelectionNode self.containerNode.addSubnode(textSelectionNode) From 37e861817b0ae6e262fd604a74030f716997b88d Mon Sep 17 00:00:00 2001 From: Ali <> Date: Sun, 29 Oct 2023 23:40:32 +0400 Subject: [PATCH 11/12] Fix raster template emoji --- .../Sources/LottieAnimationCache.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/submodules/TelegramUI/Components/LottieAnimationCache/Sources/LottieAnimationCache.swift b/submodules/TelegramUI/Components/LottieAnimationCache/Sources/LottieAnimationCache.swift index 21265ad2fa..f6980ebb25 100644 --- a/submodules/TelegramUI/Components/LottieAnimationCache/Sources/LottieAnimationCache.swift +++ b/submodules/TelegramUI/Components/LottieAnimationCache/Sources/LottieAnimationCache.swift @@ -70,11 +70,12 @@ public func cacheStillSticker(path: String, width: Int, height: Int, writer: Ani UIGraphicsPushContext(c) if let customColor = customColor { + c.clip(to: CGRect(origin: CGPoint(), size: context.size), mask: image.cgImage!) c.setFillColor(customColor.cgColor) - c.setBlendMode(.sourceIn) + c.fill(CGRect(origin: CGPoint(), size: context.size)) + } else { + c.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: context.size)) } - - c.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: context.size)) UIGraphicsPopContext() } memcpy(surface.argb, context.bytes, surface.height * surface.bytesPerRow) From 333011a6a649c7f6afea162d5eb1bef8bc178310 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Mon, 30 Oct 2023 00:07:24 +0400 Subject: [PATCH 12/12] Merge quotes before sending --- ...yncCore_TextEntitiesMessageAttribute.swift | 4 +-- .../Sources/GenerateTextEntities.swift | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift index b6b81056e7..eddb827c8e 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift @@ -27,8 +27,8 @@ public enum MessageTextEntityType: Equatable { } public struct MessageTextEntity: PostboxCoding, Codable, Equatable { - public let range: Range - public let type: MessageTextEntityType + public var range: Range + public var type: MessageTextEntityType public init(range: Range, type: MessageTextEntityType) { self.range = range diff --git a/submodules/TextFormat/Sources/GenerateTextEntities.swift b/submodules/TextFormat/Sources/GenerateTextEntities.swift index feacfe990d..4e7fd0356f 100644 --- a/submodules/TextFormat/Sources/GenerateTextEntities.swift +++ b/submodules/TextFormat/Sources/GenerateTextEntities.swift @@ -181,6 +181,35 @@ public func generateChatInputTextEntities(_ text: NSAttributedString, maxAnimate } } + while true { + var hadReductions = false + + scan: for i in 0 ..< entities.count { + if case .BlockQuote = entities[i].type { + inner: for j in 0 ..< entities.count { + if j == i { + continue inner + } + if case .BlockQuote = entities[j].type { + if entities[i].range.upperBound == entities[j].range.lowerBound || entities[i].range.lowerBound == entities[j].range.upperBound { + entities[i].range = min(entities[i].range.lowerBound, entities[j].range.lowerBound) ..< max(entities[i].range.upperBound, entities[j].range.upperBound) + entities.remove(at: j) + + hadReductions = true + break scan + } + } + } + + break scan + } + } + + if !hadReductions { + break + } + } + return entities }