diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index 7dada255cb..5f04cd4839 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -761,7 +761,7 @@ final class AttachmentPanel: ASDisplayNode, UIScrollViewDelegate { self.mainButtonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside) - self.interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _ in + self.interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _ in }, setupEditMessage: { _, _ in }, beginMessageSelection: { _, _ in }, deleteSelectedMessages: { diff --git a/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift b/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift index 689e5d8c06..a9952ef608 100644 --- a/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift +++ b/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift @@ -67,7 +67,7 @@ public enum ChatOpenWebViewSource: Equatable { } public final class ChatPanelInterfaceInteraction { - public let setupReplyMessage: (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void + public let setupReplyMessage: (MessageId?, @escaping (ContainedViewLayoutTransition, @escaping () -> Void) -> Void) -> Void public let setupEditMessage: (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void public let beginMessageSelection: ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void public let deleteSelectedMessages: () -> Void @@ -173,7 +173,7 @@ public final class ChatPanelInterfaceInteraction { public let statuses: ChatPanelInterfaceInteractionStatuses? public init( - setupReplyMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, + setupReplyMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition, @escaping () -> Void) -> Void) -> Void, setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void, deleteSelectedMessages: @escaping () -> Void, diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index 13a1d9b2d7..9a5558b948 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -22,6 +22,7 @@ public protocol ContextControllerProtocol: ViewController { var getOverlayViews: (() -> [UIView])? { get set } func dismiss(completion: (() -> Void)?) + func dismiss(result: ContextMenuActionResult, completion: (() -> Void)?) func getActionsMinHeight() -> ContextController.ActionsHeight? func setItems(_ items: Signal, minHeight: ContextController.ActionsHeight?, animated: Bool) @@ -2601,7 +2602,7 @@ public final class ContextController: ViewController, StandalonePresentableContr } } - private func dismiss(result: ContextMenuActionResult, completion: (() -> Void)?) { + public func dismiss(result: ContextMenuActionResult, completion: (() -> Void)?) { if viewTreeContainsFirstResponder(view: self.view) { self.dismissOnInputClose = (result, completion) self.view.endEditing(true) diff --git a/submodules/ContextUI/Sources/PeekController.swift b/submodules/ContextUI/Sources/PeekController.swift index a561c2278c..3d9c459a33 100644 --- a/submodules/ContextUI/Sources/PeekController.swift +++ b/submodules/ContextUI/Sources/PeekController.swift @@ -126,4 +126,8 @@ public final class PeekController: ViewController, ContextControllerProtocol { self?.presentingViewController?.dismiss(animated: false, completion: nil) }) } + + public func dismiss(result: ContextMenuActionResult, completion: (() -> Void)?) { + self.dismiss(completion: completion) + } } diff --git a/submodules/Display/Source/ContainedViewLayoutTransition.swift b/submodules/Display/Source/ContainedViewLayoutTransition.swift index 14efefd113..320d7dc448 100644 --- a/submodules/Display/Source/ContainedViewLayoutTransition.swift +++ b/submodules/Display/Source/ContainedViewLayoutTransition.swift @@ -1133,7 +1133,7 @@ public extension ContainedViewLayoutTransition { previousTransform = layer.transform } layer.transform = transform - layer.animate(from: NSValue(caTransform3D: previousTransform), to: NSValue(caTransform3D: transform), keyPath: "transform", timingFunction: curve.timingFunction, duration: duration, mediaTimingFunction: curve.mediaTimingFunction, completion: { value in + layer.animate(from: NSValue(caTransform3D: previousTransform), to: NSValue(caTransform3D: transform), keyPath: "transform", timingFunction: curve.timingFunction, duration: duration, delay: delay, mediaTimingFunction: curve.mediaTimingFunction, completion: { value in completion?(value) }) } @@ -1198,37 +1198,13 @@ public extension ContainedViewLayoutTransition { } } - func updateSublayerTransformScale(node: ASDisplayNode, scale: CGFloat, delay: Double = 0.0, completion: ((Bool) -> Void)? = nil) { + func updateSublayerTransformScale(node: ASDisplayNode, scale: CGFloat, delay: Double = 0.0, beginWithCurrentState: Bool = false, completion: ((Bool) -> Void)? = nil) { if !node.isNodeLoaded { node.subnodeTransform = CATransform3DMakeScale(scale, scale, 1.0) completion?(true) return } - let t = node.layer.sublayerTransform - let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13)) - if currentScale.isEqual(to: scale) { - if let completion = completion { - completion(true) - } - return - } - - switch self { - case .immediate: - node.layer.removeAnimation(forKey: "sublayerTransform") - node.layer.sublayerTransform = CATransform3DMakeScale(scale, scale, 1.0) - if let completion = completion { - completion(true) - } - case let .animated(duration, curve): - node.layer.sublayerTransform = CATransform3DMakeScale(scale, scale, 1.0) - node.layer.animate(from: NSValue(caTransform3D: t), to: NSValue(caTransform3D: node.layer.sublayerTransform), keyPath: "sublayerTransform", timingFunction: curve.timingFunction, duration: duration, delay: delay, mediaTimingFunction: curve.mediaTimingFunction, removeOnCompletion: true, additive: false, completion: { - result in - if let completion = completion { - completion(result) - } - }) - } + self.updateSublayerTransformScale(layer: node.layer, scale: CGPoint(x: scale, y: scale), beginWithCurrentState: beginWithCurrentState, completion: completion) } func updateSublayerTransformScaleAdditive(node: ASDisplayNode, scale: CGFloat, completion: ((Bool) -> Void)? = nil) { diff --git a/submodules/Display/Source/TextNode.swift b/submodules/Display/Source/TextNode.swift index e96863e83b..72fddae5d6 100644 --- a/submodules/Display/Source/TextNode.swift +++ b/submodules/Display/Source/TextNode.swift @@ -2236,7 +2236,9 @@ open class TextNode: ASDisplayNode { } let glyphRuns = CTLineGetGlyphRuns(line.line) as NSArray + if glyphRuns.count != 0 { + let hasAttachments = !line.attachments.isEmpty for run in glyphRuns { let run = run as! CTRun let glyphCount = CTRunGetGlyphCount(run) @@ -2269,13 +2271,45 @@ open class TextNode: ASDisplayNode { if fixDoubleEmoji { context.setBlendMode(.normal) } - CTRunDraw(run, context, CFRangeMake(0, glyphCount)) + + if hasAttachments { + let stringRange = CTRunGetStringRange(run) + if line.attachments.contains(where: { $0.range.contains(stringRange.location) }) { + } else { + CTRunDraw(run, context, CFRangeMake(0, glyphCount)) + } + } else { + CTRunDraw(run, context, CFRangeMake(0, glyphCount)) + } + if fixDoubleEmoji { context.setBlendMode(blendMode) } } } + for attachment in line.attachments { + let image = attachment.attachment + var textColor: UIColor? + layout.attributedString?.enumerateAttributes(in: attachment.range, options: []) { attributes, range, _ in + if let color = attributes[NSAttributedString.Key.foregroundColor] as? UIColor { + textColor = color + } + } + if let textColor { + if let tintedImage = generateTintedImage(image: image, color: textColor) { + let imageRect = CGRect(origin: CGPoint(x: attachment.frame.midX - tintedImage.size.width * 0.5, y: attachment.frame.midY - tintedImage.size.height * 0.5 + 1.0), size: tintedImage.size).offsetBy(dx: lineFrame.minX, dy: lineFrame.minY) + context.translateBy(x: imageRect.midX, y: imageRect.midY) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -imageRect.midX, y: -imageRect.midY) + context.draw(tintedImage.cgImage!, in: imageRect) + context.translateBy(x: imageRect.midX, y: imageRect.midY) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -imageRect.midX, y: -imageRect.midY) + } + } + } + if !line.strikethroughs.isEmpty { for strikethrough in line.strikethroughs { guard let lineRange = line.range else { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift index 215588d8e9..4987c2524b 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TextEntitiesMessageAttribute.swift @@ -331,7 +331,7 @@ public func messageTextEntitiesInRange(entities: [MessageTextEntity], range: NSR } if entity.range.overlaps(range) { var mappedRange = entity.range.clamped(to: range) - mappedRange = (entity.range.lowerBound - range.lowerBound) ..< (entity.range.upperBound - range.lowerBound) + mappedRange = (mappedRange.lowerBound - range.lowerBound) ..< (mappedRange.upperBound - range.lowerBound) result.append(MessageTextEntity(range: mappedRange, type: entity.type)) } } diff --git a/submodules/TelegramUI/Components/Chat/ChatBotInfoItem/Sources/ChatBotInfoItem.swift b/submodules/TelegramUI/Components/Chat/ChatBotInfoItem/Sources/ChatBotInfoItem.swift index 7103bda2d1..9b64930637 100644 --- a/submodules/TelegramUI/Components/Chat/ChatBotInfoItem/Sources/ChatBotInfoItem.swift +++ b/submodules/TelegramUI/Components/Chat/ChatBotInfoItem/Sources/ChatBotInfoItem.swift @@ -170,13 +170,13 @@ public final class ChatBotInfoItemNode: ListViewItemNode { recognizer.tapActionAtPoint = { [weak self] point in if let strongSelf = self { let tapAction = strongSelf.tapActionAtPoint(point, gesture: .tap, isEstimating: true) - switch tapAction { - case .none: - break - case .ignore: - return .fail - case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji, .customEmoji: - return .waitForSingleTap + switch tapAction.content { + case .none: + break + case .ignore: + return .fail + case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults, .copy, .largeEmoji, .customEmoji: + return .waitForSingleTap } } @@ -427,20 +427,20 @@ public final class ChatBotInfoItemNode: ListViewItemNode { if let (attributeText, fullText) = self.textNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) { concealed = !doesUrlMatchText(url: url, text: attributeText, fullText: fullText) } - return .url(url: url, concealed: concealed, activate: nil) + return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: url, concealed: concealed))) } else if let peerMention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerMention)] as? TelegramPeerMention { - return .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: false) + return ChatMessageBubbleContentTapAction(content: .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: false)) } else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String { - return .textMention(peerName) + return ChatMessageBubbleContentTapAction(content: .textMention(peerName)) } else if let botCommand = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.BotCommand)] as? String { - return .botCommand(botCommand) + return ChatMessageBubbleContentTapAction(content: .botCommand(botCommand)) } else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag { - return .hashtag(hashtag.peerName, hashtag.hashtag) + return ChatMessageBubbleContentTapAction(content: .hashtag(hashtag.peerName, hashtag.hashtag)) } else { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } } else { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } } @@ -449,49 +449,49 @@ public final class ChatBotInfoItemNode: ListViewItemNode { case .ended: if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation { switch gesture { - case .tap: - let tapAction = self.tapActionAtPoint(location, gesture: gesture, isEstimating: false) - switch tapAction { - case .none, .ignore: - break - case let .url(url, concealed, activate): - self.item?.controllerInteraction.openUrl(url, concealed, nil, nil, activate?()) - case let .peerMention(peerId, _, _): - if let item = self.item { - let _ = (item.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) - |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in - if let peer = peer { - self?.item?.controllerInteraction.openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) - } - }) + case .tap: + let tapAction = self.tapActionAtPoint(location, gesture: gesture, isEstimating: false) + switch tapAction.content { + case .none, .ignore: + break + case let .url(url): + self.item?.controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: url.url, concealed: url.concealed, progress: tapAction.activate?())) + case let .peerMention(peerId, _, _): + if let item = self.item { + let _ = (item.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) + |> deliverOnMainQueue).startStandalone(next: { [weak self] peer in + if let peer = peer { + self?.item?.controllerInteraction.openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default) } - case let .textMention(name): - self.item?.controllerInteraction.openPeerMention(name) - case let .botCommand(command): - self.item?.controllerInteraction.sendBotCommand(nil, command) - case let .hashtag(peerName, hashtag): - self.item?.controllerInteraction.openHashtag(peerName, hashtag) - default: - break + }) + } + case let .textMention(name): + self.item?.controllerInteraction.openPeerMention(name) + case let .botCommand(command): + self.item?.controllerInteraction.sendBotCommand(nil, command) + case let .hashtag(peerName, hashtag): + self.item?.controllerInteraction.openHashtag(peerName, hashtag) + default: + break } case .longTap, .doubleTap: if let item = self.item, self.backgroundNode.frame.contains(location) { let tapAction = self.tapActionAtPoint(location, gesture: gesture, isEstimating: false) - switch tapAction { - case .none, .ignore: - break - case let .url(url, _, _): - item.controllerInteraction.longTap(.url(url), nil) - case let .peerMention(peerId, mention, _): - item.controllerInteraction.longTap(.peerMention(peerId, mention), nil) - case let .textMention(name): - item.controllerInteraction.longTap(.mention(name), nil) - case let .botCommand(command): - item.controllerInteraction.longTap(.command(command), nil) - case let .hashtag(_, hashtag): - item.controllerInteraction.longTap(.hashtag(hashtag), nil) - default: - break + switch tapAction.content { + case .none, .ignore: + break + case let .url(url): + item.controllerInteraction.longTap(.url(url.url), nil) + case let .peerMention(peerId, mention, _): + item.controllerInteraction.longTap(.peerMention(peerId, mention), nil) + case let .textMention(name): + item.controllerInteraction.longTap(.mention(name), nil) + case let .botCommand(command): + item.controllerInteraction.longTap(.command(command), nil) + case let .hashtag(_, hashtag): + item.controllerInteraction.longTap(.hashtag(hashtag), nil) + default: + break } } default: diff --git a/submodules/TelegramUI/Components/Chat/ChatButtonKeyboardInputNode/Sources/ChatButtonKeyboardInputNode.swift b/submodules/TelegramUI/Components/Chat/ChatButtonKeyboardInputNode/Sources/ChatButtonKeyboardInputNode.swift index 467803d942..3c7dfab0a4 100644 --- a/submodules/TelegramUI/Components/Chat/ChatButtonKeyboardInputNode/Sources/ChatButtonKeyboardInputNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatButtonKeyboardInputNode/Sources/ChatButtonKeyboardInputNode.swift @@ -378,7 +378,7 @@ public final class ChatButtonKeyboardInputNode: ChatInputNode { self.controllerInteraction.sendMessage(markupButton.title) dismissIfOnce = true case let .url(url): - self.controllerInteraction.openUrl(url, true, nil, nil, nil) + self.controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: url, concealed: true)) case .requestMap: self.controllerInteraction.shareCurrentLocation() case .requestPhone: diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageActionBubbleContentNode/Sources/ChatMessageActionBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageActionBubbleContentNode/Sources/ChatMessageActionBubbleContentNode.swift index 93874e28b5..3d49bde8e3 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageActionBubbleContentNode/Sources/ChatMessageActionBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageActionBubbleContentNode/Sources/ChatMessageActionBubbleContentNode.swift @@ -517,29 +517,29 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { if let (attributeText, fullText) = self.labelNode.textNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) { concealed = !doesUrlMatchText(url: url, text: attributeText, fullText: fullText) } - return .url(url: url, concealed: concealed, activate: nil) + return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: url, concealed: concealed))) } else if let peerMention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerMention)] as? TelegramPeerMention { - return .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: true) + return ChatMessageBubbleContentTapAction(content: .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: true)) } else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String { - return .textMention(peerName) + return ChatMessageBubbleContentTapAction(content: .textMention(peerName)) } else if let botCommand = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.BotCommand)] as? String { - return .botCommand(botCommand) + return ChatMessageBubbleContentTapAction(content: .botCommand(botCommand)) } else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag { - return .hashtag(hashtag.peerName, hashtag.hashtag) + return ChatMessageBubbleContentTapAction(content: .hashtag(hashtag.peerName, hashtag.hashtag)) } } if let imageNode = imageNode, imageNode.frame.contains(point) { - return .openMessage + return ChatMessageBubbleContentTapAction(content: .openMessage) } if let backgroundNode = self.backgroundNode, backgroundNode.frame.contains(point) { if let item = self.item, item.message.media.contains(where: { $0 is TelegramMediaStory }) { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } else { - return .openMessage + return ChatMessageBubbleContentTapAction(content: .openMessage) } } else { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift index 491f0cefd4..fd0b70494c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift @@ -83,7 +83,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { public var activateAction: (() -> Void)? public var requestUpdateLayout: (() -> Void)? - public var defaultContentAction: () -> ChatMessageBubbleContentTapAction = { return .none } + public var defaultContentAction: () -> ChatMessageBubbleContentTapAction = { return ChatMessageBubbleContentTapAction(content: .none) } public var visibility: ListViewItemNodeVisibility = .none { didSet { @@ -729,6 +729,12 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { case .actionButton: actualSize.height += buttonBottomSpacing } + } else { + if let (_, inlineMediaSize) = inlineMediaAndSize { + if actualSize.height < insets.top + inlineMediaEdgeInset + inlineMediaSize.height + inlineMediaEdgeInset { + actualSize.height = insets.top + inlineMediaEdgeInset + inlineMediaSize.height + inlineMediaEdgeInset + } + } } if case let .linear(_, bottom) = position { @@ -796,7 +802,10 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { } if let (inlineMediaValue, inlineMediaSize) = inlineMediaAndSize { - let inlineMediaFrame = CGRect(origin: CGPoint(x: actualSize.width - insets.right - inlineMediaSize.width, y: backgroundInsets.top + inlineMediaEdgeInset), size: inlineMediaSize) + var inlineMediaFrame = CGRect(origin: CGPoint(x: actualSize.width - insets.right - inlineMediaSize.width, y: backgroundInsets.top + inlineMediaEdgeInset), size: inlineMediaSize) + if contentLayoutOrder.isEmpty { + inlineMediaFrame.origin.x = insets.left + } let inlineMedia: TransformImageNode var updateMedia = false @@ -2002,21 +2011,21 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { if let (attributeText, fullText) = text.textNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) { concealed = !doesUrlMatchText(url: url, text: attributeText, fullText: fullText) } - return .url(url: url, concealed: concealed, activate: nil) + return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: url, concealed: concealed))) } else if let peerMention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerMention)] as? TelegramPeerMention { - return .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: false) + return ChatMessageBubbleContentTapAction(content: .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: false)) } else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String { - return .textMention(peerName) + return ChatMessageBubbleContentTapAction(content: .textMention(peerName)) } else if let botCommand = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.BotCommand)] as? String { - return .botCommand(botCommand) + return ChatMessageBubbleContentTapAction(content: .botCommand(botCommand)) } else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag { - return .hashtag(hashtag.peerName, hashtag.hashtag) + return ChatMessageBubbleContentTapAction(content: .hashtag(hashtag.peerName, hashtag.hashtag)) } } } if let actionButton = self.actionButton, actionButton.frame.contains(point) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } return self.defaultContentAction() @@ -2101,9 +2110,9 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { highlightTimer.invalidate() } - let transition: ContainedViewLayoutTransition = .animated(duration: self.isHighlighted ? 0.2 : 0.2, curve: .easeInOut) + let transition: ContainedViewLayoutTransition = .animated(duration: self.isHighlighted ? 0.3 : 0.2, curve: .easeInOut) let scale: CGFloat = self.isHighlighted ? ((self.bounds.width - 5.0) / self.bounds.width) : 1.0 - transition.updateSublayerTransformScale(node: self, scale: scale) + transition.updateSublayerTransformScale(node: self, scale: scale, beginWithCurrentState: true) } public func reactionTargetView(value: MessageReaction.Reaction) -> UIView? { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift index d26365ea08..5492c87f21 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift @@ -121,26 +121,52 @@ public enum ChatMessageBubblePreparePosition { case mosaic(top: ChatMessageBubbleRelativePosition, bottom: ChatMessageBubbleRelativePosition) } -public enum ChatMessageBubbleContentTapAction { - case none - case url(url: String, concealed: Bool, activate: (() -> Promise?)?) - case textMention(String) - case peerMention(peerId: PeerId, mention: String, openProfile: Bool) - case botCommand(String) - case hashtag(String?, String) - case instantPage - case wallpaper - case theme - case call(peerId: PeerId, isVideo: Bool) - case openMessage - case timecode(Double, String) - case tooltip(String, ASDisplayNode?, CGRect?) - case bankCard(String) - case ignore - case openPollResults(Data) - case copy(String) - case largeEmoji(String, String?, TelegramMediaFile) - case customEmoji(TelegramMediaFile) +public struct ChatMessageBubbleContentTapAction { + public struct Url { + public var url: String + public var concealed: Bool + public var allowInlineWebpageResolution: Bool + + public init( + url: String, + concealed: Bool, + allowInlineWebpageResolution: Bool = false + ) { + self.url = url + self.concealed = concealed + self.allowInlineWebpageResolution = allowInlineWebpageResolution + } + } + + public enum Content { + case none + case url(Url) + case textMention(String) + case peerMention(peerId: PeerId, mention: String, openProfile: Bool) + case botCommand(String) + case hashtag(String?, String) + case instantPage + case wallpaper + case theme + case call(peerId: PeerId, isVideo: Bool) + case openMessage + case timecode(Double, String) + case tooltip(String, ASDisplayNode?, CGRect?) + case bankCard(String) + case ignore + case openPollResults(Data) + case copy(String) + case largeEmoji(String, String?, TelegramMediaFile) + case customEmoji(TelegramMediaFile) + } + + public var content: Content + public var activate: (() -> Promise?)? + + public init(content: Content, activate: (() -> Promise?)? = nil) { + self.content = content + self.activate = activate + } } public final class ChatMessageBubbleContentItem { @@ -235,7 +261,7 @@ open class ChatMessageBubbleContentNode: ASDisplayNode { } open func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } open func updateTouchesAtPoint(_ point: CGPoint?) { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 75a816cee3..2ea84593d7 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -521,6 +521,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI private let mainContainerNode: ContextControllerSourceNode private let backgroundWallpaperNode: ChatMessageBubbleBackdrop private let backgroundNode: ChatMessageBackground + private var backgroundHighlightNode: ChatMessageBackground? private let shadowNode: ChatMessageShadowNode private var clippingNode: ChatMessageBubbleClippingNode @@ -557,7 +558,11 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI private let messageAccessibilityArea: AccessibilityAreaNode private var backgroundType: ChatMessageBackgroundType? - private var highlightedState: Bool = false + + private struct HighlightedState: Equatable { + var quote: String? + } + private var highlightedState: HighlightedState? private var backgroundFrameTransition: (CGRect, CGRect)? @@ -703,7 +708,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } if let singleUrl = accessibilityData.singleUrl { - strongSelf.item?.controllerInteraction.openUrl(singleUrl, false, false, strongSelf.item?.content.firstMessage, nil) + strongSelf.item?.controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: singleUrl, concealed: false, external: false, message: strongSelf.item?.content.firstMessage)) return true } @@ -1007,7 +1012,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI for contentNode in strongSelf.contentNodes { let contentNodePoint = strongSelf.view.convert(point, to: contentNode.view) let tapAction = contentNode.tapActionAtPoint(contentNodePoint, gesture: .tap, isEstimating: true) - switch tapAction { + switch tapAction.content { case .none: break case .ignore: @@ -1066,7 +1071,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI for contentNode in strongSelf.contentNodes { let contentNodePoint = strongSelf.view.convert(point, to: contentNode.view) let tapAction = contentNode.tapActionAtPoint(contentNodePoint, gesture: .tap, isEstimating: true) - switch tapAction { + switch tapAction.content { case .none: if let _ = strongSelf.item?.controllerInteraction.tapMessage { return .waitForSingleTap @@ -2818,7 +2823,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if item.presentationData.theme.theme.forceSync { legacyTransition = .immediate } - strongSelf.backgroundNode.setType(type: backgroundType, highlighted: strongSelf.highlightedState, graphics: graphics, maskMode: strongSelf.backgroundMaskMode, hasWallpaper: hasWallpaper, transition: legacyTransition, backgroundNode: presentationContext.backgroundNode) + strongSelf.backgroundNode.setType(type: backgroundType, highlighted: false, graphics: graphics, maskMode: strongSelf.backgroundMaskMode, hasWallpaper: hasWallpaper, transition: legacyTransition, backgroundNode: presentationContext.backgroundNode) strongSelf.backgroundWallpaperNode.setType(type: backgroundType, theme: item.presentationData.theme, essentialGraphics: graphics, maskMode: strongSelf.backgroundMaskMode, backgroundNode: presentationContext.backgroundNode) strongSelf.shadowNode.setType(type: backgroundType, hasWallpaper: hasWallpaper, graphics: graphics) @@ -3535,6 +3540,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI let backgroundAnimation = ListViewAnimation(from: strongSelf.backgroundNode.frame, to: backgroundFrame, duration: duration * UIView.animationDurationFactor(), curve: strongSelf.preferredAnimationCurve, beginAt: beginAt, update: { [weak strongSelf] _, frame in if let strongSelf = strongSelf { strongSelf.backgroundNode.frame = frame + if let backgroundHighlightNode = strongSelf.backgroundHighlightNode { + backgroundHighlightNode.frame = frame + backgroundHighlightNode.updateLayout(size: frame.size, transition: .immediate) + } strongSelf.clippingNode.position = CGPoint(x: frame.midX, y: frame.midY) strongSelf.clippingNode.bounds = CGRect(origin: CGPoint(x: frame.minX, y: frame.minY), size: frame.size) @@ -3546,6 +3555,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI strongSelf.setAnimationForKey("backgroundNodeFrame", animation: backgroundAnimation) } else { animation.animator.updateFrame(layer: strongSelf.backgroundNode.layer, frame: backgroundFrame, completion: nil) + if let backgroundHighlightNode = strongSelf.backgroundHighlightNode { + animation.animator.updateFrame(layer: backgroundHighlightNode.layer, frame: backgroundFrame, completion: nil) + backgroundHighlightNode.updateLayout(size: backgroundFrame.size, transition: animation) + } animation.animator.updatePosition(layer: strongSelf.clippingNode.layer, position: backgroundFrame.center, completion: nil) strongSelf.clippingNode.clipsToBounds = shouldClipOnTransitions animation.animator.updateBounds(layer: strongSelf.clippingNode.layer, bounds: CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY), size: backgroundFrame.size), completion: { [weak strongSelf] _ in @@ -3608,6 +3621,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if case .System = animation, strongSelf.mainContextSourceNode.isExtractedToContextPreview { legacyTransition.updateFrame(node: strongSelf.backgroundNode, frame: backgroundFrame) + if let backgroundHighlightNode = strongSelf.backgroundHighlightNode { + legacyTransition.updateFrame(node: backgroundHighlightNode, frame: backgroundFrame, completion: nil) + backgroundHighlightNode.updateLayout(size: backgroundFrame.size, transition: legacyTransition) + } legacyTransition.updateFrame(node: strongSelf.clippingNode, frame: backgroundFrame) legacyTransition.updateBounds(node: strongSelf.clippingNode, bounds: CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY), size: backgroundFrame.size)) @@ -3617,6 +3634,11 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI strongSelf.shadowNode.updateLayout(backgroundFrame: backgroundFrame, transition: legacyTransition) } else { strongSelf.backgroundNode.frame = backgroundFrame + if let backgroundHighlightNode = strongSelf.backgroundHighlightNode { + backgroundHighlightNode.frame = backgroundFrame + backgroundHighlightNode.updateLayout(size: backgroundFrame.size, transition: .immediate) + } + strongSelf.clippingNode.frame = backgroundFrame strongSelf.clippingNode.bounds = CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY), size: backgroundFrame.size) strongSelf.backgroundNode.updateLayout(size: backgroundFrame.size, transition: .immediate) @@ -3699,56 +3721,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI override public func shouldAnimateHorizontalFrameTransition() -> Bool { return false - /*if let _ = self.backgroundFrameTransition { - return true - } else { - return false - }*/ } override public func animateFrameTransition(_ progress: CGFloat, _ currentValue: CGFloat) { super.animateFrameTransition(progress, currentValue) - - /*if let backgroundFrameTransition = self.backgroundFrameTransition { - let backgroundFrame = CGRect.interpolator()(backgroundFrameTransition.0, backgroundFrameTransition.1, progress) as! CGRect - self.backgroundNode.frame = backgroundFrame - - self.clippingNode.frame = backgroundFrame - self.clippingNode.bounds = CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY), size: backgroundFrame.size) - - self.backgroundNode.updateLayout(size: backgroundFrame.size, transition: .immediate) - self.backgroundWallpaperNode.frame = backgroundFrame - self.shadowNode.updateLayout(backgroundFrame: backgroundFrame, transition: .immediate) - - if let type = self.backgroundNode.type { - var incomingOffset: CGFloat = 0.0 - switch type { - case .incoming: - incomingOffset = 5.0 - default: - break - } - self.mainContextSourceNode.contentRect = backgroundFrame.offsetBy(dx: incomingOffset, dy: 0.0) - self.mainContainerNode.targetNodeForActivationProgressContentRect = self.mainContextSourceNode.contentRect - if !self.mainContextSourceNode.isExtractedToContextPreview { - if let (rect, size) = self.absoluteRect { - self.updateAbsoluteRect(rect, within: size) - } - } - } - self.messageAccessibilityArea.frame = backgroundFrame - - if let item = self.item, let shareButtonNode = self.shareButtonNode { - let buttonSize = shareButtonNode.update(presentationData: item.presentationData, chatLocation: item.chatLocation, subject: item.associatedData.subject, message: item.message, account: item.context.account, disableComments: true) - shareButtonNode.frame = CGRect(origin: CGPoint(x: backgroundFrame.maxX + 8.0, y: backgroundFrame.maxY - buttonSize.width - 1.0), size: buttonSize) - } - - if CGFloat(1.0).isLessThanOrEqualTo(progress) { - self.backgroundFrameTransition = nil - - self.clippingNode.clipsToBounds = false - } - }*/ } @objc private func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) { @@ -3948,7 +3924,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI let convertedLocation = self.view.convert(location, to: contentNode.view) let tapAction = contentNode.tapActionAtPoint(convertedLocation, gesture: gesture, isEstimating: false) - switch tapAction { + switch tapAction.content { case .none: if let item = self.item, self.backgroundNode.frame.contains(CGPoint(x: self.frame.width - location.x, y: location.y)), let tapMessage = self.item?.controllerInteraction.tapMessage { return .action({ @@ -3964,12 +3940,12 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI return .action({ }) } - case let .url(url, concealed, activate): + case let .url(url): return .action({ [weak self] in guard let self, let item = self.item else { return } - item.controllerInteraction.openUrl(url, concealed, nil, item.content.firstMessage, activate?()) + item.controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: url.url, concealed: url.concealed, message: item.content.firstMessage, allowInlineWebpageResolution: url.allowInlineWebpageResolution, progress: tapAction.activate?())) }) case let .peerMention(peerId, _, openProfile): return .action({ [weak self] in @@ -4123,12 +4099,12 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI tapMessage = contentNode.item?.message } let tapAction = contentNode.tapActionAtPoint(convertedLocation, gesture: gesture, isEstimating: false) - switch tapAction { + switch tapAction.content { case .none, .ignore: break - case let .url(url, _, _): + case let .url(url): return .action({ - item.controllerInteraction.longTap(.url(url), message) + item.controllerInteraction.longTap(.url(url.url), message) }) case let .peerMention(peerId, mention, _): return .action({ @@ -4480,43 +4456,114 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI return } - var highlighted = false - var highlightedQuote: String? + var highlightedState: HighlightedState? for contentNode in self.contentNodes { let _ = contentNode.updateHighlightedState(animated: animated) } - if let highlightedState = item.controllerInteraction.highlightedState { + if let highlightedStateValue = item.controllerInteraction.highlightedState { for (message, _) in item.content { - if highlightedState.messageStableId == message.stableId { - highlighted = true - highlightedQuote = highlightedState.quote + if highlightedStateValue.messageStableId == message.stableId { + highlightedState = HighlightedState(quote: highlightedStateValue.quote) break } } } - if self.highlightedState != highlighted { - self.highlightedState = highlighted + if self.highlightedState != highlightedState { + self.highlightedState = highlightedState + + for contentNode in self.contentNodes { + if let contentNode = contentNode as? ChatMessageTextBubbleContentNode { + contentNode.updateQuoteTextHighlightState(text: nil, color: .clear, animated: true) + } + } + if let backgroundType = self.backgroundType { let graphics = PresentationResourcesChat.principalGraphics(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper, bubbleCorners: item.presentationData.chatBubbleCorners) - let hasWallpaper = item.presentationData.theme.wallpaper.hasWallpaper - self.backgroundNode.setType(type: backgroundType, highlighted: highlighted, graphics: graphics, maskMode: self.backgroundMaskMode, hasWallpaper: hasWallpaper, transition: animated ? .animated(duration: 0.3, curve: .easeInOut) : .immediate, backgroundNode: item.controllerInteraction.presentationContext.backgroundNode) - } - } - - if let highlightedQuote { - for contentNode in self.contentNodes { - if let contentNode = contentNode as? ChatMessageTextBubbleContentNode { - contentNode.updateQuoteTextHighlightState(text: highlightedQuote, animated: animated) - } - } - } else { - for contentNode in self.contentNodes { - if let contentNode = contentNode as? ChatMessageTextBubbleContentNode { - contentNode.updateQuoteTextHighlightState(text: nil, animated: animated) + if self.highlightedState != nil { + let backgroundHighlightNode: ChatMessageBackground + if let current = self.backgroundHighlightNode { + backgroundHighlightNode = current + } else { + backgroundHighlightNode = ChatMessageBackground() + self.mainContextSourceNode.contentNode.insertSubnode(backgroundHighlightNode, aboveSubnode: self.backgroundNode) + self.backgroundHighlightNode = backgroundHighlightNode + + backgroundHighlightNode.setType(type: backgroundType, highlighted: true, graphics: graphics, maskMode: true, hasWallpaper: false, transition: .immediate, backgroundNode: nil) + + backgroundHighlightNode.frame = self.backgroundNode.frame + backgroundHighlightNode.updateLayout(size: backgroundHighlightNode.frame.size, transition: .immediate) + + if highlightedState?.quote != nil { + Queue.mainQueue().after(0.3, { [weak self] in + guard let self, let item = self.item, let backgroundHighlightNode = self.backgroundHighlightNode else { + return + } + + if let highlightedState = self.highlightedState, let quote = highlightedState.quote { + let hasWallpaper = item.presentationData.theme.wallpaper.hasWallpaper + let incoming: PresentationThemeBubbleColorComponents = !hasWallpaper ? item.presentationData.theme.theme.chat.message.incoming.bubble.withoutWallpaper : item.presentationData.theme.theme.chat.message.incoming.bubble.withWallpaper + let outgoing: PresentationThemeBubbleColorComponents = !hasWallpaper ? item.presentationData.theme.theme.chat.message.outgoing.bubble.withoutWallpaper : item.presentationData.theme.theme.chat.message.outgoing.bubble.withWallpaper + + let highlightColor: UIColor + if item.message.effectivelyIncoming(item.context.account.peerId) { + highlightColor = incoming.highlightedFill + } else { + highlightColor = outgoing.highlightedFill + } + + let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring) + + var quoteFrame: CGRect? + for contentNode in self.contentNodes { + if let contentNode = contentNode as? ChatMessageTextBubbleContentNode { + contentNode.updateQuoteTextHighlightState(text: quote, color: highlightColor, animated: false) + var sourceFrame = backgroundHighlightNode.view.convert(backgroundHighlightNode.bounds, to: contentNode.view) + if item.message.effectivelyIncoming(item.context.account.peerId) { + sourceFrame.origin.x += 6.0 + sourceFrame.size.width -= 6.0 + } else { + sourceFrame.size.width -= 6.0 + } + + if let localFrame = contentNode.animateQuoteTextHighlightIn(sourceFrame: sourceFrame, transition: transition) { + if self.contentNodes[0] !== contentNode && self.contentNodes[0].supernode === contentNode.supernode { + contentNode.supernode?.insertSubnode(contentNode, belowSubnode: self.contentNodes[0]) + } + + quoteFrame = contentNode.view.convert(localFrame, to: backgroundHighlightNode.view.superview) + } + break + } + } + + if let quoteFrame { + self.backgroundHighlightNode = nil + + backgroundHighlightNode.updateLayout(size: quoteFrame.size, transition: transition) + transition.updateFrame(node: backgroundHighlightNode, frame: quoteFrame) + backgroundHighlightNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, delay: 0.1, removeOnCompletion: false, completion: { [weak backgroundHighlightNode] _ in + backgroundHighlightNode?.removeFromSupernode() + }) + } + } + }) + } + } + } else { + if let backgroundHighlightNode = self.backgroundHighlightNode { + self.backgroundHighlightNode = nil + if animated { + backgroundHighlightNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak backgroundHighlightNode] _ in + backgroundHighlightNode?.removeFromSupernode() + }) + } else { + backgroundHighlightNode.removeFromSupernode() + } + } } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageCallBubbleContentNode/Sources/ChatMessageCallBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageCallBubbleContentNode/Sources/ChatMessageCallBubbleContentNode.swift index 0c8a27b15f..714b4e7f7f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageCallBubbleContentNode/Sources/ChatMessageCallBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageCallBubbleContentNode/Sources/ChatMessageCallBubbleContentNode.swift @@ -262,7 +262,7 @@ public class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode { override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.buttonNode.frame.contains(point) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } else if self.bounds.contains(point), let item = self.item { var isVideo = false for media in item.message.media { @@ -270,9 +270,9 @@ public class ChatMessageCallBubbleContentNode: ChatMessageBubbleContentNode { isVideo = isVideoValue } } - return .call(peerId: item.message.id.peerId, isVideo: isVideo) + return ChatMessageBubbleContentTapAction(content: .call(peerId: item.message.id.peerId, isVideo: isVideo)) } else { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageCommentFooterContentNode/Sources/ChatMessageCommentFooterContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageCommentFooterContentNode/Sources/ChatMessageCommentFooterContentNode.swift index a1622f69cf..926429e4a9 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageCommentFooterContentNode/Sources/ChatMessageCommentFooterContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageCommentFooterContentNode/Sources/ChatMessageCommentFooterContentNode.swift @@ -410,9 +410,9 @@ public final class ChatMessageCommentFooterContentNode: ChatMessageBubbleContent override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.buttonNode.frame.contains(point) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } - return .none + return ChatMessageBubbleContentTapAction(content: .none) } override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift index da895d97a4..1e3c3f46d4 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageContactBubbleContentNode/Sources/ChatMessageContactBubbleContentNode.swift @@ -379,12 +379,12 @@ public class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.buttonNode.frame.contains(point) { - return .openMessage + return ChatMessageBubbleContentTapAction(content: .openMessage) } if self.dateAndStatusNode.supernode != nil, let _ = self.dateAndStatusNode.hitTest(self.view.convert(point, to: self.dateAndStatusNode.view), with: nil) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } - return .none + return ChatMessageBubbleContentTapAction(content: .none) } @objc private func contactTap(_ recognizer: UITapGestureRecognizer) { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousDescriptionContentNode/Sources/ChatMessageEventLogPreviousDescriptionContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousDescriptionContentNode/Sources/ChatMessageEventLogPreviousDescriptionContentNode.swift index 8421fef52c..7f70a401ef 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousDescriptionContentNode/Sources/ChatMessageEventLogPreviousDescriptionContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousDescriptionContentNode/Sources/ChatMessageEventLogPreviousDescriptionContentNode.swift @@ -100,7 +100,7 @@ public final class ChatMessageEventLogPreviousDescriptionContentNode: ChatMessag } }*/ } - return .none + return ChatMessageBubbleContentTapAction(content: .none) } override public func updateHiddenMedia(_ media: [Media]?) -> Bool { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousLinkContentNode/Sources/ChatMessageEventLogPreviousLinkContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousLinkContentNode/Sources/ChatMessageEventLogPreviousLinkContentNode.swift index a6d21075d6..37502efe72 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousLinkContentNode/Sources/ChatMessageEventLogPreviousLinkContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousLinkContentNode/Sources/ChatMessageEventLogPreviousLinkContentNode.swift @@ -95,7 +95,7 @@ public final class ChatMessageEventLogPreviousLinkContentNode: ChatMessageBubble } }*/ } - return .none + return ChatMessageBubbleContentTapAction(content: .none) } override public func updateHiddenMedia(_ media: [Media]?) -> Bool { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousMessageContentNode/Sources/ChatMessageEventLogPreviousMessageContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousMessageContentNode/Sources/ChatMessageEventLogPreviousMessageContentNode.swift index ef73309cae..73176c153f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousMessageContentNode/Sources/ChatMessageEventLogPreviousMessageContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageEventLogPreviousMessageContentNode/Sources/ChatMessageEventLogPreviousMessageContentNode.swift @@ -97,7 +97,7 @@ public final class ChatMessageEventLogPreviousMessageContentNode: ChatMessageBub let contentNodeFrame = self.contentNode.frame return self.contentNode.tapActionAtPoint(point.offsetBy(dx: -contentNodeFrame.minX, dy: -contentNodeFrame.minY), gesture: gesture, isEstimating: isEstimating) } - return .none + return ChatMessageBubbleContentTapAction(content: .none) } override public func updateTouchesAtPoint(_ point: CGPoint?) { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageFileBubbleContentNode/Sources/ChatMessageFileBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageFileBubbleContentNode/Sources/ChatMessageFileBubbleContentNode.swift index 029ddcf1ec..0d8d6a3f84 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageFileBubbleContentNode/Sources/ChatMessageFileBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageFileBubbleContentNode/Sources/ChatMessageFileBubbleContentNode.swift @@ -221,10 +221,10 @@ public class ChatMessageFileBubbleContentNode: ChatMessageBubbleContentNode { override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.interactiveFileNode.dateAndStatusNode.supernode != nil, let _ = self.interactiveFileNode.dateAndStatusNode.hitTest(self.view.convert(point, to: self.interactiveFileNode.dateAndStatusNode.view), with: nil) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } if self.interactiveFileNode.hasTapAction(at: self.view.convert(point, to: self.interactiveFileNode.view)) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } return super.tapActionAtPoint(point, gesture: gesture, isEstimating: isEstimating) } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGameBubbleContentNode/Sources/ChatMessageGameBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGameBubbleContentNode/Sources/ChatMessageGameBubbleContentNode.swift index fdd14b3f7a..b763bd5e92 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGameBubbleContentNode/Sources/ChatMessageGameBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGameBubbleContentNode/Sources/ChatMessageGameBubbleContentNode.swift @@ -129,7 +129,7 @@ public final class ChatMessageGameBubbleContentNode: ChatMessageBubbleContentNod } }*/ } - return .none + return ChatMessageBubbleContentTapAction(content: .none) } override public func updateHiddenMedia(_ media: [Media]?) -> Bool { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift index 62618eb648..0e266f0e6a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiftBubbleContentNode/Sources/ChatMessageGiftBubbleContentNode.swift @@ -513,26 +513,26 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode { if let (attributeText, fullText) = self.labelNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) { concealed = !doesUrlMatchText(url: url, text: attributeText, fullText: fullText) } - return .url(url: url, concealed: concealed, activate: nil) + return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: url, concealed: concealed))) } else if let peerMention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerMention)] as? TelegramPeerMention { - return .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: false) + return ChatMessageBubbleContentTapAction(content: .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: false)) } else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String { - return .textMention(peerName) + return ChatMessageBubbleContentTapAction(content: .textMention(peerName)) } else if let botCommand = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.BotCommand)] as? String { - return .botCommand(botCommand) + return ChatMessageBubbleContentTapAction(content: .botCommand(botCommand)) } else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag { - return .hashtag(hashtag.peerName, hashtag.hashtag) + return ChatMessageBubbleContentTapAction(content: .hashtag(hashtag.peerName, hashtag.hashtag)) } } if self.buttonNode.frame.contains(point) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } else if let backgroundNode = self.backgroundNode, backgroundNode.frame.contains(point) { - return .openMessage + return ChatMessageBubbleContentTapAction(content: .openMessage) } else if self.mediaBackgroundNode.frame.contains(point) { - return .openMessage + return ChatMessageBubbleContentTapAction(content: .openMessage) } else { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift index 15e1f2514b..7db6f0918b 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageGiveawayBubbleContentNode/Sources/ChatMessageGiveawayBubbleContentNode.swift @@ -567,12 +567,12 @@ public class ChatMessageGiveawayBubbleContentNode: ChatMessageBubbleContentNode override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.buttonNode.frame.contains(point) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } if self.dateAndStatusNode.supernode != nil, let _ = self.dateAndStatusNode.hitTest(self.view.convert(point, to: self.dateAndStatusNode.view), with: nil) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } - return .none + return ChatMessageBubbleContentTapAction(content: .none) } @objc private func buttonPressed() { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoBubbleContentNode/Sources/ChatMessageInstantVideoBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoBubbleContentNode/Sources/ChatMessageInstantVideoBubbleContentNode.swift index 6f6dd71eb5..2f6096b29a 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoBubbleContentNode/Sources/ChatMessageInstantVideoBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoBubbleContentNode/Sources/ChatMessageInstantVideoBubbleContentNode.swift @@ -414,18 +414,18 @@ public class ChatMessageInstantVideoBubbleContentNode: ChatMessageBubbleContentN override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if !self.interactiveFileNode.isHidden { if self.interactiveFileNode.dateAndStatusNode.supernode != nil, let _ = self.interactiveFileNode.dateAndStatusNode.hitTest(self.view.convert(point, to: self.interactiveFileNode.dateAndStatusNode.view), with: nil) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } if self.interactiveFileNode.hasTapAction(at: self.view.convert(point, to: self.interactiveFileNode.view)) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } } if !self.interactiveVideoNode.isHidden { if self.interactiveVideoNode.dateAndStatusNode.supernode != nil, let _ = self.interactiveVideoNode.dateAndStatusNode.hitTest(self.view.convert(point, to: self.interactiveVideoNode.dateAndStatusNode.view), with: nil) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } if let audioTranscriptionButton = self.interactiveVideoNode.audioTranscriptionButton, let _ = audioTranscriptionButton.hitTest(self.view.convert(point, to: audioTranscriptionButton), with: nil) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } } return super.tapActionAtPoint(point, gesture: gesture, isEstimating: isEstimating) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInvoiceBubbleContentNode/Sources/ChatMessageInvoiceBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInvoiceBubbleContentNode/Sources/ChatMessageInvoiceBubbleContentNode.swift index 57e4c691ab..d2d3f5bb71 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInvoiceBubbleContentNode/Sources/ChatMessageInvoiceBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInvoiceBubbleContentNode/Sources/ChatMessageInvoiceBubbleContentNode.swift @@ -125,7 +125,7 @@ public final class ChatMessageInvoiceBubbleContentNode: ChatMessageBubbleContent } }*/ } - return .none + return ChatMessageBubbleContentTapAction(content: .none) } override public func updateHiddenMedia(_ media: [Media]?) -> Bool { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageItemView/Sources/ChatMessageItemView.swift b/submodules/TelegramUI/Components/Chat/ChatMessageItemView/Sources/ChatMessageItemView.swift index 90b0d1e71c..ee89750b66 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageItemView/Sources/ChatMessageItemView.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageItemView/Sources/ChatMessageItemView.swift @@ -753,7 +753,7 @@ open class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol { if url.hasPrefix("tg://") { concealed = false } - item.controllerInteraction.openUrl(url, concealed, nil, nil, nil) + item.controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: url, concealed: concealed)) case .requestMap: item.controllerInteraction.shareCurrentLocation() case .requestPhone: diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift index f008917a84..7a945e542f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageMapBubbleContentNode/Sources/ChatMessageMapBubbleContentNode.swift @@ -505,7 +505,7 @@ public class ChatMessageMapBubbleContentNode: ChatMessageBubbleContentNode { } override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } @objc private func imageTap(_ recognizer: UITapGestureRecognizer) { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift index b692daefc8..f6fd96bae0 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageMediaBubbleContentNode/Sources/ChatMessageMediaBubbleContentNode.swift @@ -455,7 +455,7 @@ public class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode { } override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } override public func animateInsertion(_ currentTimestamp: Double, duration: Double) { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift index ece615c599..da7d147bd5 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessagePollBubbleContentNode/Sources/ChatMessagePollBubbleContentNode.swift @@ -1608,17 +1608,17 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { if let (attributeText, fullText) = self.textNode.attributeSubstring(name: TelegramTextAttributes.URL, index: index) { concealed = !doesUrlMatchText(url: url, text: attributeText, fullText: fullText) } - return .url(url: url, concealed: concealed, activate: nil) + return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: url, concealed: concealed))) } else if let peerMention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerMention)] as? TelegramPeerMention { - return .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: false) + return ChatMessageBubbleContentTapAction(content: .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: false)) } else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String { - return .textMention(peerName) + return ChatMessageBubbleContentTapAction(content: .textMention(peerName)) } else if let botCommand = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.BotCommand)] as? String { - return .botCommand(botCommand) + return ChatMessageBubbleContentTapAction(content: .botCommand(botCommand)) } else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag { - return .hashtag(hashtag.peerName, hashtag.hashtag) + return ChatMessageBubbleContentTapAction(content: .hashtag(hashtag.peerName, hashtag.hashtag)) } else { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } } else { var isBotChat: Bool = false @@ -1629,7 +1629,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { for optionNode in self.optionNodes { if optionNode.frame.contains(point), case .tap = gesture { if optionNode.isUserInteractionEnabled { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } else if let result = optionNode.currentResult, let item = self.item, !Namespaces.Message.allScheduled.contains(item.message.id.namespace), let poll = self.poll, let option = optionNode.option, !isBotChat { switch poll.publicity { case .anonymous: @@ -1648,7 +1648,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { string = item.presentationData.strings.MessagePoll_QuizCount(result.count) } } - return .tooltip(string, optionNode, optionNode.bounds.offsetBy(dx: 0.0, dy: 10.0)) + return ChatMessageBubbleContentTapAction(content: .tooltip(string, optionNode, optionNode.bounds.offsetBy(dx: 0.0, dy: 10.0))) case .public: var hasNonZeroVoters = false if let voters = poll.results.voters { @@ -1661,24 +1661,24 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode { } if hasNonZeroVoters { if !isEstimating { - return .openPollResults(option.opaqueIdentifier) + return ChatMessageBubbleContentTapAction(content: .openPollResults(option.opaqueIdentifier)) } - return .openMessage + return ChatMessageBubbleContentTapAction(content: .openMessage) } } } } } if self.buttonNode.isUserInteractionEnabled, !self.buttonNode.isHidden, self.buttonNode.frame.contains(point) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } if self.avatarsNode.isUserInteractionEnabled, !self.avatarsNode.isHidden, self.avatarsNode.frame.contains(point) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } if self.solutionButtonNode.isUserInteractionEnabled, !self.solutionButtonNode.isHidden, !self.solutionButtonNode.alpha.isZero, self.solutionButtonNode.frame.contains(point) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } - return .none + return ChatMessageBubbleContentTapAction(content: .none) } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageProfilePhotoSuggestionContentNode/Sources/ChatMessageProfilePhotoSuggestionContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageProfilePhotoSuggestionContentNode/Sources/ChatMessageProfilePhotoSuggestionContentNode.swift index 36d8ba5556..d46289cd4f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageProfilePhotoSuggestionContentNode/Sources/ChatMessageProfilePhotoSuggestionContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageProfilePhotoSuggestionContentNode/Sources/ChatMessageProfilePhotoSuggestionContentNode.swift @@ -314,9 +314,9 @@ public class ChatMessageProfilePhotoSuggestionContentNode: ChatMessageBubbleCont override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.mediaBackgroundNode.frame.contains(point) { - return .openMessage + return ChatMessageBubbleContentTapAction(content: .openMessage) } else { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift index b27dcf2704..86b938b6b2 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift @@ -557,9 +557,9 @@ public final class ChatMessageReactionsFooterContentNode: ChatMessageBubbleConte override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if let result = self.buttonsNode.hitTest(self.view.convert(point, to: self.buttonsNode.view), with: nil), result !== self.buttonsNode.view { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } - return .none + return ChatMessageBubbleContentTapAction(content: .none) } override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift index d501556b44..38a6c7ea0c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift @@ -26,6 +26,26 @@ private let quoteIcon: UIImage = { return UIImage(bundleImageName: "Chat/Message/ReplyQuoteIcon")!.precomposed().withRenderingMode(.alwaysTemplate) }() +private let channelIcon: UIImage = { + let sourceImage = UIImage(bundleImageName: "Chat/Input/Accessory Panels/PanelTextChannelIcon")! + return generateImage(CGSize(width: sourceImage.size.width + 4.0, height: sourceImage.size.height + 4.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + UIGraphicsPushContext(context) + sourceImage.draw(at: CGPoint(x: 2.0, y: 2.0)) + UIGraphicsPopContext() + })!.precomposed().withRenderingMode(.alwaysTemplate) +}() + +private let groupIcon: UIImage = { + let sourceImage = UIImage(bundleImageName: "Chat/Input/Accessory Panels/PanelTextGroupIcon")! + return generateImage(CGSize(width: sourceImage.size.width + 3.0, height: sourceImage.size.height + 4.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + UIGraphicsPushContext(context) + sourceImage.draw(at: CGPoint(x: 3.0, y: 1.0)) + UIGraphicsPopContext() + })!.precomposed().withRenderingMode(.alwaysTemplate) +}() + public class ChatMessageReplyInfoNode: ASDisplayNode { public final class TransitionReplyPanel { public let titleNode: ASDisplayNode @@ -135,29 +155,121 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { let titleFont = Font.semibold(fontSize) let textFont = Font.regular(fontSize) - var titleString: String + var titleString: NSAttributedString var textString: NSAttributedString let isMedia: Bool let isText: Bool var isExpiredStory: Bool = false var isStory: Bool = false + let titleColor: UIColor + let mainColor: UIColor + let dustColor: UIColor + var secondaryColor: UIColor? + + var authorNameColor: UIColor? + var dashSecondaryColor: UIColor? + + let author = arguments.message?.effectiveAuthor + + if author?.hasCustomNameColor == true || ([Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(arguments.parentMessage.id.peerId.namespace) && author?.id.namespace == Namespaces.Peer.CloudUser) { + authorNameColor = author?.nameColor?.color + dashSecondaryColor = author?.nameColor?.dashColors.1 + } + + switch arguments.type { + case let .bubble(incoming): + titleColor = incoming ? (authorNameColor ?? arguments.presentationData.theme.theme.chat.message.incoming.accentTextColor) : arguments.presentationData.theme.theme.chat.message.outgoing.accentTextColor + if incoming { + if let authorNameColor { + mainColor = authorNameColor + secondaryColor = dashSecondaryColor + } else { + mainColor = arguments.presentationData.theme.theme.chat.message.incoming.accentTextColor + } + } else { + mainColor = arguments.presentationData.theme.theme.chat.message.outgoing.accentTextColor + } + dustColor = incoming ? arguments.presentationData.theme.theme.chat.message.incoming.secondaryTextColor : arguments.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor + case .standalone: + let serviceColor = serviceMessageColorComponents(theme: arguments.presentationData.theme.theme, wallpaper: arguments.presentationData.theme.wallpaper) + titleColor = serviceColor.primaryText + + mainColor = serviceMessageColorComponents(chatTheme: arguments.presentationData.theme.theme.chat, wallpaper: arguments.presentationData.theme.wallpaper).primaryText + dustColor = titleColor + } + if let message = arguments.message { let author = message.effectiveAuthor - titleString = author.flatMap(EnginePeer.init)?.displayTitle(strings: arguments.strings, displayOrder: arguments.presentationData.nameDisplayOrder) ?? arguments.strings.User_DeletedAccount + let rawTitleString = author.flatMap(EnginePeer.init)?.displayTitle(strings: arguments.strings, displayOrder: arguments.presentationData.nameDisplayOrder) ?? arguments.strings.User_DeletedAccount + titleString = NSAttributedString(string: rawTitleString, font: titleFont, textColor: titleColor) if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported) || arguments.parentMessage.forwardInfo != nil { if let author = forwardInfo.author { - titleString = EnginePeer(author).displayTitle(strings: arguments.strings, displayOrder: arguments.presentationData.nameDisplayOrder) + let rawTitleString = EnginePeer(author).displayTitle(strings: arguments.strings, displayOrder: arguments.presentationData.nameDisplayOrder) + titleString = NSAttributedString(string: rawTitleString, font: titleFont, textColor: titleColor) } else if let authorSignature = forwardInfo.authorSignature { - titleString = authorSignature + let rawTitleString = authorSignature + titleString = NSAttributedString(string: rawTitleString, font: titleFont, textColor: titleColor) } } - if message.id.peerId != arguments.parentMessage.id.peerId { - //TODO:localize - if let peer = message.peers[message.id.peerId], (peer is TelegramChannel || peer is TelegramGroup) { - titleString += " in \(peer.debugDisplayTitle)" + if message.id.peerId != arguments.parentMessage.id.peerId, let peer = message.peers[message.id.peerId], (peer is TelegramChannel || peer is TelegramGroup) { + final class RunDelegateData { + let ascent: CGFloat + let descent: CGFloat + let width: CGFloat + + init(ascent: CGFloat, descent: CGFloat, width: CGFloat) { + self.ascent = ascent + self.descent = descent + self.width = width + } + } + let font = titleFont + let runDelegateData = RunDelegateData( + ascent: font.ascender, + descent: font.descender, + width: channelIcon.size.width + ) + var callbacks = CTRunDelegateCallbacks( + version: kCTRunDelegateCurrentVersion, + dealloc: { dataRef in + Unmanaged.fromOpaque(dataRef).release() + }, + getAscent: { dataRef in + let data = Unmanaged.fromOpaque(dataRef) + return data.takeUnretainedValue().ascent + }, + getDescent: { dataRef in + let data = Unmanaged.fromOpaque(dataRef) + return data.takeUnretainedValue().descent + }, + getWidth: { dataRef in + let data = Unmanaged.fromOpaque(dataRef) + return data.takeUnretainedValue().width + } + ) + + if let runDelegate = CTRunDelegateCreate(&callbacks, Unmanaged.passRetained(runDelegateData).toOpaque()) { + if let peer = peer as? TelegramChannel, case .broadcast = peer.info { + let rawTitleString = NSMutableAttributedString(attributedString: titleString) + rawTitleString.insert(NSAttributedString(string: ">", attributes: [ + .attachment: channelIcon, + .foregroundColor: titleColor, + NSAttributedString.Key(rawValue: kCTRunDelegateAttributeName as String): runDelegate + ]), at: 0) + titleString = rawTitleString + } else { + let rawTitleString = NSMutableAttributedString(attributedString: titleString) + rawTitleString.append(NSAttributedString(string: ">", attributes: [ + .attachment: groupIcon, + .foregroundColor: titleColor, + NSAttributedString.Key(rawValue: kCTRunDelegateAttributeName as String): runDelegate + ])) + rawTitleString.append(NSAttributedString(string: peer.debugDisplayTitle, font: titleFont, textColor: titleColor)) + titleString = rawTitleString + } } } @@ -167,9 +279,11 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { isText = isTextValue } else if let replyForward = arguments.replyForward { if let replyAuthorId = replyForward.peerId, let replyAuthor = arguments.parentMessage.peers[replyAuthorId] { - titleString = EnginePeer(replyAuthor).displayTitle(strings: arguments.strings, displayOrder: arguments.presentationData.nameDisplayOrder) + let rawTitleString = EnginePeer(replyAuthor).displayTitle(strings: arguments.strings, displayOrder: arguments.presentationData.nameDisplayOrder) + titleString = NSAttributedString(string: rawTitleString, font: titleFont, textColor: titleColor) } else { - titleString = replyForward.authorName ?? " " + let rawTitleString = replyForward.authorName ?? " " + titleString = NSAttributedString(string: rawTitleString, font: titleFont, textColor: titleColor) } //TODO:localize @@ -191,9 +305,11 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { isText = replyForward.quote?.text != nil && replyForward.quote?.text != "" } else if let story = arguments.story { if let authorPeer = arguments.parentMessage.peers[story.peerId] { - titleString = EnginePeer(authorPeer).displayTitle(strings: arguments.strings, displayOrder: arguments.presentationData.nameDisplayOrder) + let rawTitleString = EnginePeer(authorPeer).displayTitle(strings: arguments.strings, displayOrder: arguments.presentationData.nameDisplayOrder) + titleString = NSAttributedString(string: rawTitleString, font: titleFont, textColor: titleColor) } else { - titleString = arguments.strings.User_DeletedAccount + let rawTitleString = arguments.strings.User_DeletedAccount + titleString = NSAttributedString(string: rawTitleString, font: titleFont, textColor: titleColor) } isText = false @@ -221,7 +337,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { isMedia = true } } else { - titleString = " " + titleString = NSAttributedString(string: " ", font: titleFont, textColor: titleColor) textString = NSAttributedString(string: " ") isMedia = true isText = false @@ -229,70 +345,20 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { let placeholderColor: UIColor = arguments.parentMessage.effectivelyIncoming(arguments.context.account.peerId) ? arguments.presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : arguments.presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor - let titleColor: UIColor let textColor: UIColor - let dustColor: UIColor - - var authorNameColor: UIColor? - var dashSecondaryColor: UIColor? - - let author = arguments.message?.effectiveAuthor - - if author?.hasCustomNameColor == true || ([Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(arguments.parentMessage.id.peerId.namespace) && author?.id.namespace == Namespaces.Peer.CloudUser) { - authorNameColor = author?.nameColor?.color - dashSecondaryColor = author?.nameColor?.dashColors.1 -// if let rawAuthorNameColor = authorNameColor { -// var dimColors = false -// switch arguments.presentationData.theme.theme.name { -// case .builtin(.nightAccent), .builtin(.night): -// dimColors = true -// default: -// break -// } -// if dimColors { -// var hue: CGFloat = 0.0 -// var saturation: CGFloat = 0.0 -// var brightness: CGFloat = 0.0 -// rawAuthorNameColor.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: nil) -// authorNameColor = UIColor(hue: hue, saturation: saturation * 0.7, brightness: min(1.0, brightness * 1.2), alpha: 1.0) -// } -// } - } - - let mainColor: UIColor - var secondaryColor: UIColor? - switch arguments.type { - case let .bubble(incoming): - titleColor = incoming ? (authorNameColor ?? arguments.presentationData.theme.theme.chat.message.incoming.accentTextColor) : arguments.presentationData.theme.theme.chat.message.outgoing.accentTextColor - if incoming { - if let authorNameColor { - mainColor = authorNameColor - secondaryColor = dashSecondaryColor - } else { - mainColor = arguments.presentationData.theme.theme.chat.message.incoming.accentTextColor - } - } else { - mainColor = arguments.presentationData.theme.theme.chat.message.outgoing.accentTextColor - } - if isExpiredStory || isStory { - textColor = incoming ? arguments.presentationData.theme.theme.chat.message.incoming.accentTextColor : arguments.presentationData.theme.theme.chat.message.outgoing.accentTextColor - } else if isMedia { - textColor = incoming ? arguments.presentationData.theme.theme.chat.message.incoming.secondaryTextColor : arguments.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor - } else { - textColor = incoming ? arguments.presentationData.theme.theme.chat.message.incoming.primaryTextColor : arguments.presentationData.theme.theme.chat.message.outgoing.primaryTextColor - } - dustColor = incoming ? arguments.presentationData.theme.theme.chat.message.incoming.secondaryTextColor : arguments.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor - case .standalone: - let serviceColor = serviceMessageColorComponents(theme: arguments.presentationData.theme.theme, wallpaper: arguments.presentationData.theme.wallpaper) - titleColor = serviceColor.primaryText - - mainColor = serviceMessageColorComponents(chatTheme: arguments.presentationData.theme.theme.chat, wallpaper: arguments.presentationData.theme.wallpaper).primaryText - textColor = titleColor - dustColor = titleColor + case let .bubble(incoming): + if isExpiredStory || isStory { + textColor = incoming ? arguments.presentationData.theme.theme.chat.message.incoming.accentTextColor : arguments.presentationData.theme.theme.chat.message.outgoing.accentTextColor + } else if isMedia { + textColor = incoming ? arguments.presentationData.theme.theme.chat.message.incoming.secondaryTextColor : arguments.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor + } else { + textColor = incoming ? arguments.presentationData.theme.theme.chat.message.incoming.primaryTextColor : arguments.presentationData.theme.theme.chat.message.outgoing.primaryTextColor + } + case .standalone: + textColor = titleColor } - let messageText: NSAttributedString if isText, let message = arguments.message { var text: String @@ -448,7 +514,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { } } - let (titleLayout, titleApply) = titleNodeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: titleString, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: maxTitleNumberOfLines, truncationType: .end, constrainedSize: CGSize(width: contrainedTextSize.width - additionalTitleWidth, height: contrainedTextSize.height), alignment: .natural, cutout: nil, insets: textInsets)) + let (titleLayout, titleApply) = titleNodeLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: maxTitleNumberOfLines, truncationType: .end, constrainedSize: CGSize(width: contrainedTextSize.width - additionalTitleWidth, height: contrainedTextSize.height), alignment: .natural, cutout: nil, insets: textInsets)) if isExpiredStory || isStory { contrainedTextSize.width -= 26.0 } @@ -699,9 +765,9 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { isHighlighted = true } - let transition: ContainedViewLayoutTransition = .animated(duration: isHighlighted ? 0.1 : 0.2, curve: .easeInOut) + let transition: ContainedViewLayoutTransition = .animated(duration: isHighlighted ? 0.3 : 0.2, curve: .easeInOut) let scale: CGFloat = isHighlighted ? ((self.bounds.width - 5.0) / self.bounds.width) : 1.0 - transition.updateSublayerTransformScale(node: self, scale: scale) + transition.updateSublayerTransformScale(node: self, scale: scale, beginWithCurrentState: true) } public func animateFromInputPanel(sourceReplyPanel: TransitionReplyPanel, unclippedTransitionNode: ASDisplayNode? = nil, localRect: CGRect, transition: CombinedTransition) -> CGPoint { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageStoryMentionContentNode/Sources/ChatMessageStoryMentionContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageStoryMentionContentNode/Sources/ChatMessageStoryMentionContentNode.swift index 17ff7c398d..da5c821ddd 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStoryMentionContentNode/Sources/ChatMessageStoryMentionContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStoryMentionContentNode/Sources/ChatMessageStoryMentionContentNode.swift @@ -361,9 +361,9 @@ public class ChatMessageStoryMentionContentNode: ChatMessageBubbleContentNode { override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.mediaBackgroundNode.frame.contains(point) { - return .openMessage + return ChatMessageBubbleContentTapAction(content: .openMessage) } else { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/BUILD index e6baf14a9b..748b191a91 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/BUILD @@ -36,6 +36,7 @@ swift_library( "//submodules/TelegramUI/Components/Chat/ChatMessageItemCommon", "//submodules/TelegramUI/Components/Chat/MessageQuoteComponent", "//submodules/TelegramUI/Components/TextLoadingEffect", + "//submodules/TelegramUI/Components/ChatControllerInteraction", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift index 17760e251b..1dbbf804cc 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift @@ -25,6 +25,7 @@ import ChatMessageBubbleContentNode import ShimmeringLinkNode import ChatMessageItemCommon import TextLoadingEffect +import ChatControllerInteraction private final class CachedChatMessageText { let text: String @@ -72,7 +73,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { private var linkPreviewOptionsDisposable: Disposable? private var linkPreviewHighlightingNodes: [LinkHighlightingNode] = [] - private var quoteHighlightingNodes: [LinkHighlightingNode] = [] + private var quoteHighlightingNode: LinkHighlightingNode? private var linkProgressRange: NSRange? private var linkProgressView: TextLoadingEffectView? @@ -113,7 +114,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { self.addSubnode(self.textAccessibilityOverlayNode) self.textAccessibilityOverlayNode.openUrl = { [weak self] url in - self?.item?.controllerInteraction.openUrl(url, false, false, nil, nil) + self?.item?.controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: url, concealed: false, external: false)) } self.statusNode.reactionSelected = { [weak self] value in @@ -678,14 +679,14 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { if case .tap = gesture { } else { if let item = self.item, let subject = item.associatedData.subject, case .messageOptions = subject { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } } let textNodeFrame = self.textNode.textNode.frame if let (index, attributes) = self.textNode.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) { if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler)], !(self.dustNode?.isRevealed ?? true) { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } else if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { var concealed = true var urlRange: NSRange? @@ -693,7 +694,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { urlRange = urlRangeValue concealed = !doesUrlMatchText(url: url, text: attributeText, fullText: fullText) } - return .url(url: url, concealed: concealed, activate: { [weak self] in + return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: url, concealed: concealed)), activate: { [weak self] in guard let self else { return nil } @@ -721,23 +722,23 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { return promise }) } else if let peerMention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerMention)] as? TelegramPeerMention { - return .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: false) + return ChatMessageBubbleContentTapAction(content: .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: false)) } else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String { - return .textMention(peerName) + return ChatMessageBubbleContentTapAction(content: .textMention(peerName)) } else if let botCommand = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.BotCommand)] as? String { - return .botCommand(botCommand) + return ChatMessageBubbleContentTapAction(content: .botCommand(botCommand)) } else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag { - return .hashtag(hashtag.peerName, hashtag.hashtag) + return ChatMessageBubbleContentTapAction(content: .hashtag(hashtag.peerName, hashtag.hashtag)) } else if let timecode = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Timecode)] as? TelegramTimecode { - return .timecode(timecode.time, timecode.text) + return ChatMessageBubbleContentTapAction(content: .timecode(timecode.time, timecode.text)) } else if let bankCard = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.BankCard)] as? String { - return .bankCard(bankCard) + return ChatMessageBubbleContentTapAction(content: .bankCard(bankCard)) } else if let pre = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Pre)] as? String { - return .copy(pre) + return ChatMessageBubbleContentTapAction(content: .copy(pre)) } else if let code = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Code)] as? String { - return .copy(code) + return ChatMessageBubbleContentTapAction(content: .copy(code)) } else if let emoji = attributes[NSAttributedString.Key(rawValue: ChatTextInputAttributes.customEmoji.rawValue)] as? ChatTextInputTextCustomEmojiAttribute, let file = emoji.file { - return .customEmoji(file) + return ChatMessageBubbleContentTapAction(content: .customEmoji(file)) } else { if let item = self.item, item.message.text.count == 1, !item.presentationData.largeEmoji { let (emoji, fitz) = item.message.text.basicEmoji @@ -749,19 +750,19 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } if let emojiFile = emojiFile { - return .largeEmoji(emoji, fitz, emojiFile) + return ChatMessageBubbleContentTapAction(content: .largeEmoji(emoji, fitz, emojiFile)) } else { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } } else { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } } } else { if let _ = self.statusNode.hitTest(self.view.convert(point, to: self.statusNode.view), with: nil) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } - return .none + return ChatMessageBubbleContentTapAction(content: .none) } } @@ -941,43 +942,75 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } } - public func updateQuoteTextHighlightState(text: String?, animated: Bool) { - guard let item = self.item else { - return + public func animateQuoteTextHighlightIn(sourceFrame: CGRect, transition: ContainedViewLayoutTransition) -> CGRect? { + if let quoteHighlightingNode = self.quoteHighlightingNode { + var currentRect = CGRect() + for rect in quoteHighlightingNode.rects { + if currentRect.isEmpty { + currentRect = rect + } else { + currentRect = currentRect.union(rect) + } + } + if !currentRect.isEmpty { + currentRect = currentRect.insetBy(dx: -quoteHighlightingNode.inset, dy: -quoteHighlightingNode.inset) + let innerRect = currentRect.offsetBy(dx: quoteHighlightingNode.frame.minX, dy: quoteHighlightingNode.frame.minY) + + quoteHighlightingNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.08, delay: 0.1) + + let fromScale = CGPoint(x: sourceFrame.width / innerRect.width, y: sourceFrame.height / innerRect.height) + + var fromTransform = CATransform3DIdentity + let fromOffset = CGPoint(x: sourceFrame.midX - innerRect.midX, y: sourceFrame.midY - innerRect.midY) + + fromTransform = CATransform3DTranslate(fromTransform, fromOffset.x, fromOffset.y, 0.0) + + fromTransform = CATransform3DTranslate(fromTransform, -quoteHighlightingNode.bounds.width * 0.5 + currentRect.midX, -quoteHighlightingNode.bounds.height * 0.5 + currentRect.midY, 0.0) + fromTransform = CATransform3DScale(fromTransform, fromScale.x, fromScale.y, 1.0) + fromTransform = CATransform3DTranslate(fromTransform, quoteHighlightingNode.bounds.width * 0.5 - currentRect.midX, quoteHighlightingNode.bounds.height * 0.5 - currentRect.midY, 0.0) + + quoteHighlightingNode.transform = fromTransform + transition.updateTransform(node: quoteHighlightingNode, transform: CGAffineTransformIdentity) + + return currentRect.offsetBy(dx: quoteHighlightingNode.frame.minX, dy: quoteHighlightingNode.frame.minY) + } } - var rectsSet: [[CGRect]] = [] + return nil + } + + public func updateQuoteTextHighlightState(text: String?, color: UIColor, animated: Bool) { + var rectsSet: [CGRect] = [] if let text = text, !text.isEmpty, let cachedLayout = self.textNode.textNode.cachedLayout, let string = cachedLayout.attributedString?.string { let nsString = string as NSString let range = nsString.range(of: text) if range.location != NSNotFound { if let rects = cachedLayout.rangeRects(in: range)?.rects, !rects.isEmpty { - rectsSet = [rects] + rectsSet = rects } } } - for i in 0 ..< rectsSet.count { - let rects = rectsSet[i] + if !rectsSet.isEmpty { + let rects = rectsSet let textHighlightNode: LinkHighlightingNode - if i < self.quoteHighlightingNodes.count { - textHighlightNode = self.quoteHighlightingNodes[i] + if let current = self.quoteHighlightingNode { + textHighlightNode = current } else { - textHighlightNode = LinkHighlightingNode(color: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.linkHighlightColor : item.presentationData.theme.theme.chat.message.outgoing.linkHighlightColor) - self.quoteHighlightingNodes.append(textHighlightNode) + textHighlightNode = LinkHighlightingNode(color: color) + self.quoteHighlightingNode = textHighlightNode self.insertSubnode(textHighlightNode, belowSubnode: self.textNode.textNode) } textHighlightNode.frame = self.textNode.textNode.frame textHighlightNode.updateRects(rects) - } - for i in (rectsSet.count ..< self.quoteHighlightingNodes.count).reversed() { - let node = self.quoteHighlightingNodes[i] - self.quoteHighlightingNodes.remove(at: i) - - if animated { - node.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak node] _ in - node?.removeFromSupernode() - }) - } else { - node.removeFromSupernode() + } else { + if let quoteHighlightingNode = self.quoteHighlightingNode { + self.quoteHighlightingNode = nil + if animated { + quoteHighlightingNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak quoteHighlightingNode] _ in + quoteHighlightingNode?.removeFromSupernode() + }) + } else { + quoteHighlightingNode.removeFromSupernode() + } } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageUnsupportedBubbleContentNode/Sources/ChatMessageUnsupportedBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageUnsupportedBubbleContentNode/Sources/ChatMessageUnsupportedBubbleContentNode.swift index 56fb848572..b154236175 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageUnsupportedBubbleContentNode/Sources/ChatMessageUnsupportedBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageUnsupportedBubbleContentNode/Sources/ChatMessageUnsupportedBubbleContentNode.swift @@ -94,11 +94,11 @@ public final class ChatMessageUnsupportedBubbleContentNode: ChatMessageBubbleCon override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.bounds.contains(point) { if self.buttonNode.frame.contains(point) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } else { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } } - return .none + return ChatMessageBubbleContentTapAction(content: .none) } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode/Sources/ChatMessageWallpaperBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode/Sources/ChatMessageWallpaperBubbleContentNode.swift index f41cee9a22..58ce6de289 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode/Sources/ChatMessageWallpaperBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageWallpaperBubbleContentNode/Sources/ChatMessageWallpaperBubbleContentNode.swift @@ -461,11 +461,11 @@ public class ChatMessageWallpaperBubbleContentNode: ChatMessageBubbleContentNode override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { if self.statusOverlayNode.alpha > 0.0 { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } else if self.mediaBackgroundNode.frame.contains(point) { - return .openMessage + return ChatMessageBubbleContentTapAction(content: .openMessage) } else { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/BUILD index c9dc5f9a4c..61ab438674 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/BUILD @@ -29,6 +29,7 @@ swift_library( "//submodules/TelegramUI/Components/Chat/ChatMessageInteractiveMediaNode", "//submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode", "//submodules/TelegramUI/Components/Chat/ChatHistoryEntry", + "//submodules/TelegramUI/Components/ChatControllerInteraction", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift index 268cd3f5bb..0dd3756dc8 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageWebpageBubbleContentNode/Sources/ChatMessageWebpageBubbleContentNode.swift @@ -18,6 +18,7 @@ import ChatMessageItemCommon import WallpaperPreviewMedia import ChatMessageInteractiveMediaNode import ChatMessageAttachedContentNode +import ChatControllerInteraction private let titleFont: UIFont = Font.semibold(15.0) @@ -52,6 +53,15 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent } else if content.type == "telegram_theme" { item.controllerInteraction.openTheme(item.message) return + } else { + if content.title != nil || content.text != nil { + var isConcealed = true + if item.message.text.contains(content.url) { + isConcealed = false + } + item.controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: content.url, concealed: isConcealed)) + return + } } } let openChatMessageMode: ChatControllerInteractionOpenMessageMode @@ -90,7 +100,7 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent if item.message.text.contains(webpage.url) { isConcealed = false } - item.controllerInteraction.openUrl(webpage.url, isConcealed, nil, nil, nil) + item.controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: webpage.url, concealed: isConcealed)) } } } @@ -103,13 +113,13 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent } self.contentNode.defaultContentAction = { [weak self] in guard let self, let item = self.item, let webPage = self.webPage, case let .Loaded(content) = webPage.content else { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } var isConcealed = true if item.message.text.contains(content.url) { isConcealed = false } - return .url(url: content.url, concealed: isConcealed, activate: nil) + return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: content.url, concealed: isConcealed, allowInlineWebpageResolution: true))) } } @@ -501,22 +511,22 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent override public func tapActionAtPoint(_ point: CGPoint, gesture: TapLongTapOrDoubleTapGesture, isEstimating: Bool) -> ChatMessageBubbleContentTapAction { guard let item = self.item else { - return .none + return ChatMessageBubbleContentTapAction(content: .none) } if self.bounds.contains(point) { let contentNodeFrame = self.contentNode.frame let result = self.contentNode.tapActionAtPoint(point.offsetBy(dx: -contentNodeFrame.minX, dy: -contentNodeFrame.minY), gesture: gesture, isEstimating: isEstimating) if item.message.adAttribute != nil { - if case .none = result { + if case .none = result.content { if self.contentNode.hasActionAtPoint(point.offsetBy(dx: -contentNodeFrame.minX, dy: -contentNodeFrame.minY)) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } } return result } - switch result { + switch result.content { case .none: break case let .textMention(value): @@ -527,9 +537,9 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent } switch websiteType(of: content.websiteName) { case .twitter: - return .url(url: "https://twitter.com/\(mention)", concealed: false, activate: nil) + return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: "https://twitter.com/\(mention)", concealed: false))) case .instagram: - return .url(url: "https://instagram.com/\(mention)", concealed: false, activate: nil) + return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: "https://instagram.com/\(mention)", concealed: false))) default: break } @@ -542,9 +552,9 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent } switch websiteType(of: content.websiteName) { case .twitter: - return .url(url: "https://twitter.com/hashtag/\(hashtag)", concealed: false, activate: nil) + return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: "https://twitter.com/hashtag/\(hashtag)", concealed: false))) case .instagram: - return .url(url: "https://instagram.com/explore/tags/\(hashtag)", concealed: false, activate: nil) + return ChatMessageBubbleContentTapAction(content: .url(ChatMessageBubbleContentTapAction.Url(url: "https://instagram.com/explore/tags/\(hashtag)", concealed: false))) default: break } @@ -557,22 +567,22 @@ public final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContent if content.instantPage != nil { switch websiteType(of: content.websiteName) { case .instagram, .twitter: - return .none + return ChatMessageBubbleContentTapAction(content: .none) default: - return .instantPage + return ChatMessageBubbleContentTapAction(content: .instantPage) } } else if content.type == "telegram_background" { - return .wallpaper + return ChatMessageBubbleContentTapAction(content: .wallpaper) } else if content.type == "telegram_theme" { - return .theme + return ChatMessageBubbleContentTapAction(content: .theme) } } if self.contentNode.hasActionAtPoint(point.offsetBy(dx: -contentNodeFrame.minX, dy: -contentNodeFrame.minY)) { - return .ignore + return ChatMessageBubbleContentTapAction(content: .ignore) } - return .none + return ChatMessageBubbleContentTapAction(content: .none) } - return .none + return ChatMessageBubbleContentTapAction(content: .none) } override public func updateHiddenMedia(_ media: [Media]?) -> Bool { diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift index 59792cf92f..fd8177dcd1 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift @@ -276,8 +276,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _, _ in }, navigateToMessageStandalone: { _ in }, navigateToThreadMessage: { _, _, _ in - }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false }, sendEmoji: { _, _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _, _ in }, openUrl: { [weak self] url, _, _, _, _ in - self?.openUrl(url) + }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false }, sendEmoji: { _, _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _, _ in }, openUrl: { [weak self] url in + self?.openUrl(url.url) }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { [weak self] message, associatedData in if let strongSelf = self, let navigationController = strongSelf.getNavigationController() { strongSelf.context.sharedContext.openChatInstantPage(context: strongSelf.context, message: message, sourcePeerType: associatedData?.automaticDownloadPeerType, navigationController: navigationController) diff --git a/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/Sources/ForwardAccessoryPanelNode.swift b/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/Sources/ForwardAccessoryPanelNode.swift index f04add6746..fb206e91ae 100644 --- a/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/Sources/ForwardAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ForwardAccessoryPanelNode/Sources/ForwardAccessoryPanelNode.swift @@ -19,6 +19,7 @@ import TextNodeWithEntities import AnimationCache import MultiAnimationRenderer import AccessoryPanelNode +import AppBundle func textStringForForwardedMessage(_ message: Message, strings: PresentationStrings) -> (text: String, entities: [MessageTextEntity], isMedia: Bool) { for media in message.media { @@ -91,7 +92,7 @@ public final class ForwardAccessoryPanelNode: AccessoryPanelNode { let closeButton: HighlightableButtonNode let lineNode: ASImageNode - let iconNode: ASImageNode + let iconView: UIImageView let titleNode: ImmediateTextNode let textNode: ImmediateTextNodeWithEntities private var originalText: NSAttributedString? @@ -127,10 +128,9 @@ public final class ForwardAccessoryPanelNode: AccessoryPanelNode { self.lineNode.displaysAsynchronously = false self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(theme) - self.iconNode = ASImageNode() - self.iconNode.displayWithoutProcessing = false - self.iconNode.displaysAsynchronously = false - self.iconNode.image = PresentationResourcesChat.chatInputPanelForwardIconImage(theme) + self.iconView = UIImageView() + self.iconView.image = UIImage(bundleImageName: "Chat/Input/Accessory Panels/ForwardSettingsIcon")?.withRenderingMode(.alwaysTemplate) + self.iconView.tintColor = theme.chat.inputPanel.panelControlAccentColor self.titleNode = ImmediateTextNode() self.titleNode.maximumNumberOfLines = 1 @@ -148,7 +148,7 @@ public final class ForwardAccessoryPanelNode: AccessoryPanelNode { self.addSubnode(self.closeButton) self.addSubnode(self.lineNode) - self.addSubnode(self.iconNode) + self.view.addSubview(self.iconView) self.addSubnode(self.titleNode) self.addSubnode(self.textNode) self.addSubnode(self.actionArea) @@ -283,11 +283,11 @@ public final class ForwardAccessoryPanelNode: AccessoryPanelNode { } override public func animateIn() { - self.iconNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2) + self.iconView.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2) } override public func animateOut() { - self.iconNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false) + self.iconView.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false) } override public func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { @@ -302,7 +302,7 @@ public final class ForwardAccessoryPanelNode: AccessoryPanelNode { self.closeButton.setImage(PresentationResourcesChat.chatInputPanelCloseIconImage(theme), for: []) self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(theme) - self.iconNode.image = PresentationResourcesChat.chatInputPanelForwardIconImage(theme) + self.iconView.tintColor = theme.chat.inputPanel.panelControlAccentColor let filteredMessages = self.messages @@ -350,8 +350,8 @@ public final class ForwardAccessoryPanelNode: AccessoryPanelNode { self.lineNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 8.0), size: CGSize(width: 2.0, height: bounds.size.height - 10.0)) - if let icon = self.iconNode.image { - self.iconNode.frame = CGRect(origin: CGPoint(x: 7.0 + inset, y: 10.0), size: icon.size) + if let icon = self.iconView.image { + self.iconView.frame = CGRect(origin: CGPoint(x: 7.0 + inset, y: 10.0), size: icon.size) } let titleSize = self.titleNode.updateLayout(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset, height: bounds.size.height)) diff --git a/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/BUILD b/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/BUILD index c31c9ccbc1..d027a20de9 100644 --- a/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/BUILD @@ -27,6 +27,7 @@ swift_library( "//submodules/TelegramUI/Components/AnimationCache", "//submodules/TelegramUI/Components/MultiAnimationRenderer", "//submodules/TelegramUI/Components/Chat/AccessoryPanelNode", + "//submodules/TelegramUI/Components/CompositeTextNode", "//submodules/TelegramNotices", ], visibility = [ diff --git a/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/Sources/ReplyAccessoryPanelNode.swift b/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/Sources/ReplyAccessoryPanelNode.swift index e3692fa6e0..296edfd64d 100644 --- a/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/Sources/ReplyAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/Sources/ReplyAccessoryPanelNode.swift @@ -18,6 +18,8 @@ import AnimationCache import MultiAnimationRenderer import AccessoryPanelNode import TelegramNotices +import AppBundle +import CompositeTextNode public final class ReplyAccessoryPanelNode: AccessoryPanelNode { private let messageDisposable = MetaDisposable() @@ -29,8 +31,8 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode { public let closeButton: HighlightableButtonNode public let lineNode: ASImageNode - public let iconNode: ASImageNode - public let titleNode: ImmediateTextNode + public let iconView: UIImageView + public let titleNode: CompositeTextNode public let textNode: ImmediateTextNodeWithEntities public let imageNode: TransformImageNode @@ -64,15 +66,16 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode { self.lineNode.displaysAsynchronously = false self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(theme) - self.iconNode = ASImageNode() - self.iconNode.displayWithoutProcessing = false - self.iconNode.displaysAsynchronously = false - self.iconNode.image = PresentationResourcesChat.chatInputPanelReplyIconImage(theme) + self.iconView = UIImageView() + if quote != nil { + self.iconView.image = UIImage(bundleImageName: "Chat/Input/Accessory Panels/ReplyQuoteIcon")?.withRenderingMode(.alwaysTemplate) + } else { + self.iconView.image = UIImage(bundleImageName: "Chat/Input/Accessory Panels/ReplySettingsIcon")?.withRenderingMode(.alwaysTemplate) + } + self.iconView.tintColor = theme.chat.inputPanel.panelControlAccentColor - self.titleNode = ImmediateTextNode() - self.titleNode.maximumNumberOfLines = 1 - self.titleNode.displaysAsynchronously = false - self.titleNode.insets = UIEdgeInsets(top: 3.0, left: 0.0, bottom: 3.0, right: 0.0) + self.titleNode = CompositeTextNode() + self.titleNode.imageTintColor = theme.chat.inputPanel.panelControlAccentColor self.textNode = ImmediateTextNodeWithEntities() self.textNode.maximumNumberOfLines = 1 @@ -103,7 +106,7 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode { self.addSubnode(self.closeButton) self.addSubnode(self.lineNode) - self.addSubnode(self.iconNode) + self.view.addSubview(self.iconView) self.addSubnode(self.titleNode) self.addSubnode(self.textNode) self.addSubnode(self.imageNode) @@ -114,7 +117,7 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode { if let strongSelf = self { if messageView.message == nil { Queue.mainQueue().justDispatch { - strongSelf.interfaceInteraction?.setupReplyMessage(nil, { _ in }) + strongSelf.interfaceInteraction?.setupReplyMessage(nil, { _, _ in }) } return } @@ -235,27 +238,61 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode { } } - var titleText: String - if let quote = strongSelf.quote { - //TODO:localize - titleText = "Reply to quote by \(authorName)" + var titleText: [CompositeTextNode.Component] = [] + if let peer = message?.peers[strongSelf.messageId.peerId] as? TelegramChannel, case .broadcast = peer.info { + let icon: UIImage? + icon = UIImage(bundleImageName: "Chat/Input/Accessory Panels/PanelTextChannelIcon")?.withRenderingMode(.alwaysTemplate) + if let _ = strongSelf.quote { + if let icon { + let string = "Reply to Quote by " + titleText = [.text(NSAttributedString(string: string, font: Font.medium(15.0), textColor: .white))] + titleText.append(.icon(icon)) + titleText.append(.text(NSAttributedString(string: peer.debugDisplayTitle, font: Font.medium(15.0), textColor: .white))) + } + } else { + if let icon { + let string = "Reply to " + titleText = [.text(NSAttributedString(string: string, font: Font.medium(15.0), textColor: .white))] + titleText.append(.icon(icon)) + titleText.append(.text(NSAttributedString(string: peer.debugDisplayTitle, font: Font.medium(15.0), textColor: .white))) + } + } + } else { + if let _ = strongSelf.quote { + //TODO:localize + let string = "Reply to Quote by \(authorName)" + titleText = [.text(NSAttributedString(string: string, font: Font.medium(15.0), textColor: .white))] + } else { + let string = strongSelf.strings.Conversation_ReplyMessagePanelTitle(authorName).string + titleText = [.text(NSAttributedString(string: string, font: Font.medium(15.0), textColor: .white))] + strongSelf.textNode.attributedText = messageText + } + + if strongSelf.messageId.peerId != strongSelf.chatPeerId { + if let peer = message?.peers[strongSelf.messageId.peerId], (peer is TelegramChannel || peer is TelegramGroup) { + let icon: UIImage? + if let channel = peer as? TelegramChannel, case .broadcast = channel.info { + icon = UIImage(bundleImageName: "Chat/Input/Accessory Panels/PanelTextChannelIcon")?.withRenderingMode(.alwaysTemplate) + } else { + icon = UIImage(bundleImageName: "Chat/Input/Accessory Panels/PanelTextGroupIcon")?.withRenderingMode(.alwaysTemplate) + } + if let icon { + titleText.append(.icon(icon)) + titleText.append(.text(NSAttributedString(string: peer.debugDisplayTitle, font: Font.medium(15.0), textColor: .white))) + } + } + } + } + + if let quote = strongSelf.quote { let textColor = strongSelf.theme.chat.inputPanel.primaryTextColor let quoteText = stringWithAppliedEntities(trimToLineCount(quote.text, lineCount: 1), entities: quote.entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message) strongSelf.textNode.attributedText = quoteText - } else { - titleText = strongSelf.strings.Conversation_ReplyMessagePanelTitle(authorName).string - strongSelf.textNode.attributedText = messageText } - if strongSelf.messageId.peerId != strongSelf.chatPeerId { - if let peer = message?.peers[strongSelf.messageId.peerId], (peer is TelegramChannel || peer is TelegramGroup) { - titleText += " in \(peer.debugDisplayTitle)" - } - } - - strongSelf.titleNode.attributedText = NSAttributedString(string: titleText, font: Font.medium(15.0), textColor: strongSelf.theme.chat.inputPanel.panelControlAccentColor) + strongSelf.titleNode.components = titleText let headerString: String if let message = message, message.flags.contains(.Incoming), let author = message.author { @@ -329,11 +366,11 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode { } override public func animateIn() { - self.iconNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2) + self.iconView.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2) } override public func animateOut() { - self.iconNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false) + self.iconView.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false) } override public func updateThemeAndStrings(theme: PresentationTheme, strings: PresentationStrings) { @@ -347,11 +384,9 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode { self.closeButton.setImage(PresentationResourcesChat.chatInputPanelCloseIconImage(theme), for: []) self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(theme) - self.iconNode.image = PresentationResourcesChat.chatInputPanelReplyIconImage(theme) + self.iconView.tintColor = theme.chat.inputPanel.panelControlAccentColor - if let text = self.titleNode.attributedText?.string { - self.titleNode.attributedText = NSAttributedString(string: text, font: Font.medium(15.0), textColor: self.theme.chat.inputPanel.panelControlAccentColor) - } + self.titleNode.imageTintColor = theme.chat.inputPanel.panelControlAccentColor if let text = self.textNode.attributedText?.string { self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(15.0), textColor: self.textIsOptions ? self.theme.chat.inputPanel.secondaryTextColor : self.theme.chat.inputPanel.primaryTextColor) @@ -387,8 +422,8 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode { self.lineNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 8.0), size: CGSize(width: 2.0, height: bounds.size.height - 10.0)) } - if let icon = self.iconNode.image { - self.iconNode.frame = CGRect(origin: CGPoint(x: 7.0 + inset, y: 10.0), size: icon.size) + if let icon = self.iconView.image { + self.iconView.frame = CGRect(origin: CGPoint(x: 7.0 + inset, y: 10.0), size: icon.size) } var imageTextInset: CGFloat = 0.0 @@ -399,9 +434,9 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode { self.imageNode.frame = CGRect(origin: CGPoint(x: leftInset + 9.0, y: 8.0), size: CGSize(width: 35.0, height: 35.0)) } - let titleSize = self.titleNode.updateLayout(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height)) + let titleSize = self.titleNode.update(constrainedSize: CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height)) if self.titleNode.supernode == self { - self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset - self.titleNode.insets.left, y: 7.0 - self.titleNode.insets.top), size: titleSize) + self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset, y: 7.0), size: titleSize) } let textSize = self.textNode.updateLayout(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height)) diff --git a/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift index 6da499e5f9..0eedb7d605 100644 --- a/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift @@ -90,6 +90,24 @@ public final class ChatControllerInteraction { case groupParticipant(storyStats: PeerStoryStats?, avatarHeaderNode: ASDisplayNode?) } + public struct OpenUrl { + public var url: String + public var concealed: Bool + public var external: Bool? + public var message: Message? + public var allowInlineWebpageResolution: Bool + public var progress: Promise? + + public init(url: String, concealed: Bool, external: Bool? = nil, message: Message? = nil, allowInlineWebpageResolution: Bool = false, progress: Promise? = nil) { + self.url = url + self.concealed = concealed + self.external = external + self.message = message + self.allowInlineWebpageResolution = allowInlineWebpageResolution + self.progress = progress + } + } + public let openMessage: (Message, ChatControllerInteractionOpenMessageMode) -> Bool public let openPeer: (EnginePeer, ChatControllerInteractionNavigateToPeer, MessageReference?, OpenPeerSource) -> Void public let openPeerMention: (String) -> Void @@ -113,7 +131,7 @@ public final class ChatControllerInteraction { public let requestMessageActionCallback: (MessageId, MemoryBuffer?, Bool, Bool) -> Void public let requestMessageActionUrlAuth: (String, MessageActionUrlSubject) -> Void public let activateSwitchInline: (PeerId?, String, ReplyMarkupButtonAction.PeerTypes?) -> Void - public let openUrl: (String, Bool, Bool?, Message?, Promise?) -> Void + public let openUrl: (OpenUrl) -> Void public let shareCurrentLocation: () -> Void public let shareAccountContact: () -> Void public let sendBotCommand: (MessageId?, String) -> Void @@ -227,7 +245,7 @@ public final class ChatControllerInteraction { requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageActionUrlSubject) -> Void, activateSwitchInline: @escaping (PeerId?, String, ReplyMarkupButtonAction.PeerTypes?) -> Void, - openUrl: @escaping (String, Bool, Bool?, Message?, Promise?) -> Void, + openUrl: @escaping (OpenUrl) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, diff --git a/submodules/TelegramUI/Components/CompositeTextNode/BUILD b/submodules/TelegramUI/Components/CompositeTextNode/BUILD new file mode 100644 index 0000000000..cdba78a4df --- /dev/null +++ b/submodules/TelegramUI/Components/CompositeTextNode/BUILD @@ -0,0 +1,19 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "CompositeTextNode", + module_name = "CompositeTextNode", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display", + "//submodules/AsyncDisplayKit", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Components/CompositeTextNode/Sources/CompositeTextNode.swift b/submodules/TelegramUI/Components/CompositeTextNode/Sources/CompositeTextNode.swift new file mode 100644 index 0000000000..0ab1648802 --- /dev/null +++ b/submodules/TelegramUI/Components/CompositeTextNode/Sources/CompositeTextNode.swift @@ -0,0 +1,117 @@ +import Foundation +import UIKit +import Display +import AsyncDisplayKit + +public class CompositeTextNode: ASDisplayNode { + public enum Component: Equatable { + case text(NSAttributedString) + case icon(UIImage) + } + + public var components: [Component] = [] + + private var textNodes: [Int: ImmediateTextNode] = [:] + private var iconViews: [Int: UIImageView] = [:] + + public var imageTintColor: UIColor? { + didSet { + for (_, textNode) in self.textNodes { + textNode.layer.layerTintColor = self.imageTintColor?.cgColor + } + for (_, iconView) in self.iconViews { + iconView.tintColor = self.imageTintColor + } + } + } + + public func update(constrainedSize: CGSize) -> CGSize { + var validTextIds: [Int] = [] + var validIconIds: [Int] = [] + + var size = CGSize() + + var nextTextId = 0 + var nextIconId = 0 + for component in self.components { + switch component { + case let .text(text): + let id = nextTextId + nextTextId += 1 + validTextIds.append(id) + + let textNode: ImmediateTextNode + if let current = self.textNodes[id] { + textNode = current + } else { + textNode = ImmediateTextNode() + textNode.maximumNumberOfLines = 1 + textNode.insets = UIEdgeInsets(top: 3.0, left: 0.0, bottom: 3.0, right: 0.0) + textNode.layer.layerTintColor = self.imageTintColor?.cgColor + self.textNodes[id] = textNode + self.addSubnode(textNode) + } + textNode.attributedText = text + + let textSize = textNode.updateLayout(CGSize(width: max(1.0, constrainedSize.width - size.width), height: constrainedSize.height)) + + textNode.frame = CGRect(origin: CGPoint(x: size.width - textNode.insets.left, y: -textNode.insets.top), size: textSize) + size.width += textSize.width - textNode.insets.left - textNode.insets.right + size.height = max(size.height, textSize.height - textNode.insets.top - textNode.insets.bottom) + case let .icon(icon): + let id = nextIconId + nextIconId += 1 + validIconIds.append(id) + + let iconView: UIImageView + if let current = self.iconViews[id] { + iconView = current + } else { + iconView = UIImageView() + self.iconViews[id] = iconView + self.view.addSubview(iconView) + } + iconView.image = icon + iconView.tintColor = self.imageTintColor + + let iconSize = icon.size + if size.width != 0.0 { + size.width += 3.0 + } + iconView.frame = CGRect(origin: CGPoint(x: size.width, y: 3.0 + UIScreenPixel), size: iconSize) + size.width += iconSize.width + size.width += 3.0 + size.height = max(size.height, iconSize.height) + } + + if size.width >= constrainedSize.width { + size.width = constrainedSize.width + break + } + } + + var removeTextIds: [Int] = [] + for (id, textNode) in self.textNodes { + if !validTextIds.contains(id) { + textNode.removeFromSupernode() + removeTextIds.append(id) + } + } + for id in removeTextIds { + self.textNodes.removeValue(forKey: id) + } + + var removeIconIds: [Int] = [] + for (id, iconView) in self.iconViews { + if !validIconIds.contains(id) { + iconView.removeFromSuperview() + removeIconIds.append(id) + } + } + for id in removeIconIds { + self.iconViews.removeValue(forKey: id) + } + + return size + } +} diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift index 9b416b15f7..1e0b8d09b9 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoVisualMediaPaneNode/Sources/PeerInfoVisualMediaPaneNode.swift @@ -1392,8 +1392,8 @@ public final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, toggleMessagesSelection: { messageId, selected in chatControllerInteraction.toggleMessagesSelection(messageId, selected) }, - openUrl: { url, param1, param2, message in - chatControllerInteraction.openUrl(url, param1, param2, message, nil) + openUrl: { url, concealed, external, message in + chatControllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: url, concealed: concealed, external: external, message: message)) }, openInstantPage: { message, data in chatControllerInteraction.openInstantPage(message, data) diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ForwardSettingsIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ForwardSettingsIcon.imageset/Contents.json new file mode 100644 index 0000000000..71efa15f5f --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ForwardSettingsIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "forwardtools_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ForwardSettingsIcon.imageset/forwardtools_30.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ForwardSettingsIcon.imageset/forwardtools_30.pdf new file mode 100644 index 0000000000..26877de63a --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ForwardSettingsIcon.imageset/forwardtools_30.pdf @@ -0,0 +1,206 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 6.026367 4.379944 cm +0.000000 0.000000 0.000000 scn +10.631250 13.370056 m +10.631250 12.540056 l +11.089646 12.540056 11.461250 12.911659 11.461250 13.370056 c +10.631250 13.370056 l +h +10.631250 5.870056 m +11.461250 5.870056 l +11.461250 6.090186 11.373804 6.301300 11.218149 6.456955 c +11.062494 6.612610 10.851380 6.700056 10.631250 6.700056 c +10.631250 5.870056 l +h +1.188047 1.981661 m +0.561160 2.525639 l +1.188047 1.981661 l +h +12.315193 1.823753 m +12.882865 1.218237 l +12.315193 1.823753 l +h +19.853075 10.349594 m +19.285404 9.744079 l +19.853075 10.349594 l +h +19.853075 8.890519 m +19.285404 9.496035 l +19.853075 8.890519 l +h +12.315191 17.416361 m +12.882863 18.021877 l +12.315191 17.416361 l +h +11.461250 13.370056 m +11.461250 16.686825 l +9.801250 16.686825 l +9.801250 13.370056 l +11.461250 13.370056 l +h +11.747520 16.810844 m +19.285404 9.744079 l +20.420746 10.955110 l +12.882863 18.021877 l +11.747520 16.810844 l +h +19.285404 9.496035 m +11.747522 2.429268 l +12.882865 1.218237 l +20.420748 8.285003 l +19.285404 9.496035 l +h +11.461250 2.553288 m +11.461250 5.870056 l +9.801250 5.870056 l +9.801250 2.553288 l +11.461250 2.553288 l +h +10.631250 6.700056 m +5.229464 6.700056 2.125496 4.328404 0.561160 2.525639 c +1.814935 1.437683 l +3.118428 2.939848 5.787818 5.040056 10.631250 5.040056 c +10.631250 6.700056 l +h +0.915461 2.267101 m +1.091532 4.065034 1.607588 6.660966 3.036170 8.786676 c +4.431379 10.862727 6.724575 12.540056 10.631250 12.540056 c +10.631250 14.200056 l +6.139981 14.200056 3.343834 12.220509 1.658399 9.712606 c +0.006337 7.254363 -0.549554 4.339265 -0.736636 2.428890 c +0.915461 2.267101 l +h +0.561160 2.525639 m +0.611023 2.583103 0.667563 2.604307 0.696526 2.610254 c +0.721157 2.615311 0.734035 2.612202 0.741838 2.609715 c +0.747947 2.607769 0.788049 2.594332 0.833411 2.538847 c +0.888431 2.471550 0.925537 2.369989 0.915461 2.267101 c +-0.736636 2.428890 l +-0.813821 1.640717 -0.224644 1.175383 0.238040 1.028011 c +0.696962 0.881838 1.366337 0.920712 1.814935 1.437683 c +0.561160 2.525639 l +h +11.747522 2.429268 m +11.712430 2.396370 11.683138 2.386383 11.659966 2.382921 c +11.632549 2.378824 11.598459 2.382227 11.563670 2.397299 c +11.528881 2.412371 11.503082 2.434914 11.487321 2.457716 c +11.473999 2.476988 11.461250 2.505186 11.461250 2.553288 c +9.801250 2.553288 l +9.801250 0.951229 11.714108 0.122528 12.882865 1.218237 c +11.747522 2.429268 l +h +19.285404 9.744079 m +19.357044 9.676915 19.357044 9.563196 19.285404 9.496035 c +20.420748 8.285003 l +21.191931 9.007989 21.191929 10.232126 20.420746 10.955110 c +19.285404 9.744079 l +h +11.461250 16.686825 m +11.461250 16.734926 11.473999 16.763124 11.487321 16.782398 c +11.503083 16.805201 11.528882 16.827742 11.563670 16.842813 c +11.598459 16.857885 11.632549 16.861290 11.659966 16.857193 c +11.683137 16.853729 11.712428 16.843744 11.747520 16.810844 c +12.882863 18.021877 l +11.714105 19.117588 9.801250 18.288879 9.801250 16.686825 c +11.461250 16.686825 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 1.334961 19.835052 cm +0.000000 0.000000 0.000000 scn +2.165001 7.999987 m +1.703843 7.999987 1.330001 7.626144 1.330001 7.164987 c +1.330001 6.703829 1.703843 6.329987 2.165001 6.329987 c +2.626159 6.329987 3.000001 6.703829 3.000001 7.164987 c +3.000001 7.626144 2.626159 7.999987 2.165001 7.999987 c +h +0.000000 7.164987 m +0.000000 8.360683 0.969304 9.329987 2.165001 9.329987 c +3.128699 9.329987 3.945334 8.700336 4.225954 7.829987 c +9.665001 7.829987 l +10.032270 7.829987 10.330001 7.532256 10.330001 7.164987 c +10.330001 6.797717 10.032270 6.499987 9.665001 6.499987 c +4.225954 6.499987 l +3.945334 5.629638 3.128699 4.999987 2.165001 4.999987 c +0.969304 4.999987 0.000000 5.969290 0.000000 7.164987 c +h +1.165001 2.829987 m +0.797732 2.829987 0.500001 2.532256 0.500001 2.164987 c +0.500001 1.797717 0.797732 1.499987 1.165001 1.499987 c +6.104048 1.499987 l +6.384667 0.629638 7.201303 -0.000013 8.165001 -0.000013 c +9.360698 -0.000013 10.330001 0.969290 10.330001 2.164987 c +10.330001 3.360683 9.360698 4.329987 8.165001 4.329987 c +7.201303 4.329987 6.384668 3.700336 6.104048 2.829987 c +1.165001 2.829987 l +h +7.330001 2.164987 m +7.330001 2.626144 7.703843 2.999987 8.165001 2.999987 c +8.626159 2.999987 9.000001 2.626144 9.000001 2.164987 c +9.000001 1.703829 8.626159 1.329987 8.165001 1.329987 c +7.703843 1.329987 7.330001 1.703829 7.330001 2.164987 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 4389 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000004479 00000 n +0000004502 00000 n +0000004675 00000 n +0000004749 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +4808 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/LinkSettingsIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/LinkSettingsIcon.imageset/Contents.json new file mode 100644 index 0000000000..720b4d0321 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/LinkSettingsIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "linktools_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/LinkSettingsIcon.imageset/linktools_30.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/LinkSettingsIcon.imageset/linktools_30.pdf new file mode 100644 index 0000000000..03fd17e3ba --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/LinkSettingsIcon.imageset/linktools_30.pdf @@ -0,0 +1,139 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 1.334961 19.835052 cm +0.000000 0.000000 0.000000 scn +2.165001 7.999987 m +1.703843 7.999987 1.330001 7.626144 1.330001 7.164987 c +1.330001 6.703829 1.703843 6.329987 2.165001 6.329987 c +2.626159 6.329987 3.000001 6.703829 3.000001 7.164987 c +3.000001 7.626144 2.626159 7.999987 2.165001 7.999987 c +h +0.000000 7.164987 m +0.000000 8.360683 0.969304 9.329987 2.165001 9.329987 c +3.128699 9.329987 3.945334 8.700336 4.225954 7.829987 c +9.665001 7.829987 l +10.032270 7.829987 10.330001 7.532256 10.330001 7.164987 c +10.330001 6.797717 10.032270 6.499987 9.665001 6.499987 c +4.225954 6.499987 l +3.945334 5.629638 3.128699 4.999987 2.165001 4.999987 c +0.969304 4.999987 0.000000 5.969290 0.000000 7.164987 c +h +1.165001 2.829987 m +0.797732 2.829987 0.500001 2.532256 0.500001 2.164987 c +0.500001 1.797717 0.797732 1.499987 1.165001 1.499987 c +6.104048 1.499987 l +6.384667 0.629638 7.201303 -0.000013 8.165001 -0.000013 c +9.360698 -0.000013 10.330001 0.969290 10.330001 2.164987 c +10.330001 3.360683 9.360698 4.329987 8.165001 4.329987 c +7.201303 4.329987 6.384668 3.700336 6.104048 2.829987 c +1.165001 2.829987 l +h +7.330001 2.164987 m +7.330001 2.626144 7.703843 2.999987 8.165001 2.999987 c +8.626159 2.999987 9.000001 2.626144 9.000001 2.164987 c +9.000001 1.703829 8.626159 1.329987 8.165001 1.329987 c +7.703843 1.329987 7.330001 1.703829 7.330001 2.164987 c +h +f* +n +Q +q +1.000000 0.000000 -0.000000 1.000000 6.120117 1.915771 cm +0.000000 0.000000 0.000000 scn +12.468664 19.994034 m +14.215597 21.740967 17.047935 21.740967 18.794867 19.994034 c +20.541800 18.247101 20.541800 15.414764 18.794867 13.667831 c +16.294867 11.167831 l +14.547935 9.420898 11.715597 9.420898 9.968664 11.167831 c +9.736349 11.400146 9.535478 11.650909 9.365597 11.915075 c +9.117653 12.300628 8.604102 12.412183 8.218549 12.164239 c +7.832996 11.916295 7.721442 11.402744 7.969386 11.017192 c +8.202941 10.654012 8.478252 10.310648 8.794867 9.994034 c +11.190069 7.598831 15.073462 7.598831 17.468664 9.994034 c +19.968664 12.494034 l +22.363867 14.889237 22.363867 18.772629 19.968664 21.167831 c +17.573462 23.563034 13.690069 23.563034 11.294867 21.167831 c +8.794867 18.667831 l +8.470732 18.343697 8.470732 17.818169 8.794867 17.494034 c +9.119002 17.169899 9.644529 17.169899 9.968664 17.494034 c +12.468664 19.994034 l +h +9.296402 4.167830 m +7.549469 2.420897 4.717132 2.420897 2.970200 4.167830 c +1.223267 5.914762 1.223267 8.747099 2.970200 10.494032 c +5.470199 12.994032 l +7.217132 14.740965 10.049469 14.740965 11.796402 12.994032 c +12.028717 12.761717 12.229589 12.510954 12.399469 12.246788 c +12.647413 11.861236 13.160964 11.749681 13.546517 11.997624 c +13.932070 12.245568 14.043624 12.759119 13.795681 13.144671 c +13.562125 13.507852 13.286814 13.851215 12.970200 14.167830 c +10.574997 16.563032 6.691605 16.563032 4.296402 14.167830 c +1.796402 11.667830 l +-0.598801 9.272626 -0.598801 5.389235 1.796402 2.994032 c +4.191605 0.598829 8.074997 0.598829 10.470200 2.994032 c +12.970200 5.494032 l +13.294334 5.818167 13.294334 6.343695 12.970200 6.667830 c +12.646064 6.991964 12.120537 6.991964 11.796402 6.667830 c +9.296402 4.167830 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 3174 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000003264 00000 n +0000003287 00000 n +0000003460 00000 n +0000003534 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +3593 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/PanelTextChannelIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/PanelTextChannelIcon.imageset/Contents.json new file mode 100644 index 0000000000..4fb60ebb2c --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/PanelTextChannelIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "channel.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/PanelTextChannelIcon.imageset/channel.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/PanelTextChannelIcon.imageset/channel.pdf new file mode 100644 index 0000000000..898047f96e --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/PanelTextChannelIcon.imageset/channel.pdf @@ -0,0 +1,102 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 1.000000 0.572754 cm +0.000000 0.000000 0.000000 scn +2.171946 3.261456 m +3.602304 3.261456 l +3.736763 3.261456 3.803992 3.261456 3.869396 3.256588 c +4.171662 3.234089 4.463039 3.134116 4.715446 2.966301 c +4.770054 2.929994 4.823115 2.888726 4.929220 2.806200 c +4.929265 2.806165 l +6.192761 1.823446 l +7.167630 1.065215 7.655064 0.686099 8.064308 0.689581 c +8.420319 0.692611 8.755808 0.856692 8.976768 1.135851 c +9.230768 1.456751 9.230768 2.074262 9.230768 3.309284 c +9.230768 7.557522 l +9.230768 8.792542 9.230768 9.410053 8.976768 9.730952 c +8.755808 10.010111 8.420319 10.174193 8.064308 10.177222 c +7.655064 10.180704 7.167630 9.801589 6.192762 9.043358 c +4.929265 8.060638 l +4.929246 8.060623 l +4.929197 8.060584 l +4.823106 7.978071 4.770050 7.936806 4.715446 7.900502 c +4.463039 7.732687 4.171662 7.632714 3.869396 7.610216 c +3.803992 7.605347 3.736763 7.605347 3.602304 7.605347 c +2.171948 7.605347 l +0.972415 7.605347 0.000000 6.632934 0.000000 5.433402 c +0.000000 4.233869 0.972414 3.261456 2.171946 3.261456 c +h +2.206053 2.149403 m +2.171725 2.053373 2.171725 1.933668 2.171725 1.694258 c +2.171725 1.089490 l +2.171725 0.489724 2.657931 0.003517 3.257698 0.003517 c +3.857464 0.003517 4.343671 0.489724 4.343671 1.089491 c +4.343671 1.694259 l +4.343671 1.933668 4.343671 2.053373 4.309342 2.149403 c +4.250016 2.315361 4.119403 2.445974 3.953444 2.505301 c +3.857416 2.539629 3.737711 2.539629 3.498301 2.539629 c +3.017095 2.539629 l +2.777685 2.539629 2.657980 2.539629 2.561951 2.505301 c +2.395993 2.445974 2.265379 2.315361 2.206053 2.149403 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1626 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 12.000000 11.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000001716 00000 n +0000001739 00000 n +0000001912 00000 n +0000001986 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +2045 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/PanelTextGroupIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/PanelTextGroupIcon.imageset/Contents.json new file mode 100644 index 0000000000..02af66c5a5 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/PanelTextGroupIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "group.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/PanelTextGroupIcon.imageset/group.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/PanelTextGroupIcon.imageset/group.pdf new file mode 100644 index 0000000000..001e5de824 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/PanelTextGroupIcon.imageset/group.pdf @@ -0,0 +1,162 @@ +%PDF-1.7 + +1 0 obj + << /Type /XObject + /Length 2 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 12.000000 11.000000 ] + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.549805 cm +0.000000 0.000000 0.000000 scn +4.210876 5.670196 m +5.254694 5.670196 6.100876 6.516377 6.100876 7.560195 c +6.100876 8.604013 5.254694 9.450195 4.210876 9.450195 c +3.167058 9.450195 2.320876 8.604013 2.320876 7.560195 c +2.320876 6.516377 3.167058 5.670196 4.210876 5.670196 c +h +8.530897 5.670156 m +9.425598 5.670156 10.150897 6.395455 10.150897 7.290156 c +10.150897 8.184857 9.425598 8.910156 8.530897 8.910156 c +7.636196 8.910156 6.910897 8.184857 6.910897 7.290156 c +6.910897 6.395455 7.636196 5.670156 8.530897 5.670156 c +h +4.211036 4.319980 m +6.565200 4.319980 7.711937 3.183578 8.270525 2.076473 c +8.807893 1.011423 7.833971 -0.000021 6.641036 -0.000021 c +1.781036 -0.000021 l +0.588101 -0.000021 -0.385821 1.011424 0.151547 2.076473 c +0.710135 3.183578 1.856872 4.319980 4.211036 4.319980 c +h +7.304841 4.215691 m +8.083344 3.710282 8.587383 3.042050 8.911377 2.399903 c +9.138542 1.949668 9.186308 1.495334 9.099791 1.080082 c +10.421161 1.080082 l +11.315862 1.080082 12.045328 1.837108 11.649654 2.639562 c +11.227840 3.495031 10.351560 4.383612 8.531160 4.383612 c +8.059293 4.383612 7.650861 4.323907 7.297337 4.220552 c +7.304841 4.215691 l +h +f* +n +Q + +endstream +endobj + +2 0 obj + 1240 +endobj + +3 0 obj + << /Type /XObject + /Length 4 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 12.000000 11.000000 ] + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.000000 0.000000 0.000000 scn +0.000000 11.000000 m +12.000000 11.000000 l +12.000000 0.000000 l +0.000000 0.000000 l +0.000000 11.000000 l +h +f +n +Q + +endstream +endobj + +4 0 obj + 232 +endobj + +5 0 obj + << /XObject << /X1 1 0 R >> + /ExtGState << /E1 << /SMask << /Type /Mask + /G 3 0 R + /S /Alpha + >> + /Type /ExtGState + >> >> + >> +endobj + +6 0 obj + << /Length 7 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +/E1 gs +/X1 Do +Q + +endstream +endobj + +7 0 obj + 46 +endobj + +8 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 12.000000 11.000000 ] + /Resources 5 0 R + /Contents 6 0 R + /Parent 9 0 R + >> +endobj + +9 0 obj + << /Kids [ 8 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +10 0 obj + << /Pages 9 0 R + /Type /Catalog + >> +endobj + +xref +0 11 +0000000000 65535 f +0000000010 00000 n +0000001498 00000 n +0000001521 00000 n +0000002001 00000 n +0000002023 00000 n +0000002321 00000 n +0000002423 00000 n +0000002444 00000 n +0000002617 00000 n +0000002691 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 10 0 R + /Size 11 +>> +startxref +2751 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ReplyQuoteIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ReplyQuoteIcon.imageset/Contents.json new file mode 100644 index 0000000000..4acb3df7ba --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ReplyQuoteIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "replyquote_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ReplyQuoteIcon.imageset/replyquote_30.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ReplyQuoteIcon.imageset/replyquote_30.pdf new file mode 100644 index 0000000000..3f0bc2aad2 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ReplyQuoteIcon.imageset/replyquote_30.pdf @@ -0,0 +1,198 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +-1.000000 -0.000000 -0.000000 1.000000 23.973633 4.379944 cm +0.000000 0.000000 0.000000 scn +10.631250 13.370056 m +10.631250 12.540056 l +11.089646 12.540056 11.461250 12.911659 11.461250 13.370056 c +10.631250 13.370056 l +h +10.631250 5.870056 m +11.461250 5.870056 l +11.461250 6.090186 11.373804 6.301300 11.218149 6.456955 c +11.062494 6.612610 10.851380 6.700056 10.631250 6.700056 c +10.631250 5.870056 l +h +1.188047 1.981661 m +0.561160 2.525639 l +1.188047 1.981661 l +h +12.315193 1.823753 m +12.882865 1.218237 l +12.315193 1.823753 l +h +19.853075 10.349594 m +19.285404 9.744079 l +19.853075 10.349594 l +h +19.853075 8.890519 m +19.285404 9.496035 l +19.853075 8.890519 l +h +12.315191 17.416361 m +12.882863 18.021877 l +12.315191 17.416361 l +h +11.461250 13.370056 m +11.461250 16.686825 l +9.801250 16.686825 l +9.801250 13.370056 l +11.461250 13.370056 l +h +11.747520 16.810844 m +19.285404 9.744079 l +20.420746 10.955110 l +12.882863 18.021877 l +11.747520 16.810844 l +h +19.285404 9.496035 m +11.747522 2.429268 l +12.882865 1.218237 l +20.420748 8.285003 l +19.285404 9.496035 l +h +11.461250 2.553288 m +11.461250 5.870056 l +9.801250 5.870056 l +9.801250 2.553288 l +11.461250 2.553288 l +h +10.631250 6.700056 m +5.229464 6.700056 2.125496 4.328404 0.561160 2.525639 c +1.814935 1.437683 l +3.118428 2.939848 5.787818 5.040056 10.631250 5.040056 c +10.631250 6.700056 l +h +0.915461 2.267101 m +1.091532 4.065034 1.607588 6.660966 3.036170 8.786676 c +4.431379 10.862727 6.724575 12.540056 10.631250 12.540056 c +10.631250 14.200056 l +6.139981 14.200056 3.343834 12.220509 1.658399 9.712606 c +0.006337 7.254363 -0.549554 4.339265 -0.736636 2.428890 c +0.915461 2.267101 l +h +0.561160 2.525639 m +0.611023 2.583103 0.667563 2.604307 0.696526 2.610254 c +0.721157 2.615311 0.734035 2.612202 0.741838 2.609715 c +0.747947 2.607769 0.788049 2.594332 0.833411 2.538847 c +0.888431 2.471550 0.925537 2.369989 0.915461 2.267101 c +-0.736636 2.428890 l +-0.813821 1.640717 -0.224644 1.175383 0.238040 1.028011 c +0.696962 0.881838 1.366337 0.920712 1.814935 1.437683 c +0.561160 2.525639 l +h +11.747522 2.429268 m +11.712430 2.396370 11.683138 2.386383 11.659966 2.382921 c +11.632549 2.378824 11.598459 2.382227 11.563670 2.397299 c +11.528881 2.412371 11.503082 2.434914 11.487321 2.457716 c +11.473999 2.476988 11.461250 2.505186 11.461250 2.553288 c +9.801250 2.553288 l +9.801250 0.951229 11.714108 0.122528 12.882865 1.218237 c +11.747522 2.429268 l +h +19.285404 9.744079 m +19.357044 9.676915 19.357044 9.563196 19.285404 9.496035 c +20.420748 8.285003 l +21.191931 9.007989 21.191929 10.232126 20.420746 10.955110 c +19.285404 9.744079 l +h +11.461250 16.686825 m +11.461250 16.734926 11.473999 16.763124 11.487321 16.782398 c +11.503083 16.805201 11.528882 16.827742 11.563670 16.842813 c +11.598459 16.857885 11.632549 16.861290 11.659966 16.857193 c +11.683137 16.853729 11.712428 16.843744 11.747520 16.810844 c +12.882863 18.021877 l +11.714105 19.117588 9.801250 18.288879 9.801250 16.686825 c +11.461250 16.686825 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 19.000000 20.858582 cm +0.000000 0.000000 0.000000 scn +0.000000 5.141418 m +0.000000 6.245988 0.895431 7.141418 2.000000 7.141418 c +3.104569 7.141418 4.000000 6.245988 4.000000 5.141418 c +4.000000 4.557826 l +4.000000 3.471111 3.746984 2.399319 3.260991 1.427331 c +2.894427 0.694205 l +2.647438 0.200226 2.046765 0.000002 1.552786 0.246991 c +1.058808 0.493980 0.858584 1.094654 1.105573 1.588632 c +1.472136 2.321758 l +1.605720 2.588927 1.714662 2.866652 1.798144 3.151479 c +0.788369 3.252682 0.000000 4.104984 0.000000 5.141418 c +h +5.000000 5.141418 m +5.000000 6.245988 5.895431 7.141418 7.000000 7.141418 c +8.104569 7.141418 9.000000 6.245988 9.000000 5.141418 c +9.000000 4.557826 l +9.000000 3.471111 8.746984 2.399319 8.260990 1.427331 c +7.894427 0.694205 l +7.647438 0.200226 7.046765 0.000002 6.552786 0.246991 c +6.058808 0.493980 5.858583 1.094654 6.105573 1.588632 c +6.472136 2.321758 l +6.605721 2.588927 6.714662 2.866652 6.798144 3.151479 c +5.788369 3.252682 5.000000 4.104984 5.000000 5.141418 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 4042 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000004132 00000 n +0000004155 00000 n +0000004328 00000 n +0000004402 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +4461 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ReplySettingsIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ReplySettingsIcon.imageset/Contents.json new file mode 100644 index 0000000000..c4123cb2b2 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ReplySettingsIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "replytools_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ReplySettingsIcon.imageset/replytools_30.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ReplySettingsIcon.imageset/replytools_30.pdf new file mode 100644 index 0000000000..8225c44f71 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Input/Accessory Panels/ReplySettingsIcon.imageset/replytools_30.pdf @@ -0,0 +1,206 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +-1.000000 -0.000000 -0.000000 1.000000 23.973633 4.379944 cm +0.000000 0.000000 0.000000 scn +10.631250 13.370056 m +10.631250 12.540056 l +11.089646 12.540056 11.461250 12.911659 11.461250 13.370056 c +10.631250 13.370056 l +h +10.631250 5.870056 m +11.461250 5.870056 l +11.461250 6.090186 11.373804 6.301300 11.218149 6.456955 c +11.062494 6.612610 10.851380 6.700056 10.631250 6.700056 c +10.631250 5.870056 l +h +1.188047 1.981661 m +0.561160 2.525639 l +1.188047 1.981661 l +h +12.315193 1.823753 m +12.882865 1.218237 l +12.315193 1.823753 l +h +19.853075 10.349594 m +19.285404 9.744079 l +19.853075 10.349594 l +h +19.853075 8.890519 m +19.285404 9.496035 l +19.853075 8.890519 l +h +12.315191 17.416361 m +12.882863 18.021877 l +12.315191 17.416361 l +h +11.461250 13.370056 m +11.461250 16.686825 l +9.801250 16.686825 l +9.801250 13.370056 l +11.461250 13.370056 l +h +11.747520 16.810844 m +19.285404 9.744079 l +20.420746 10.955110 l +12.882863 18.021877 l +11.747520 16.810844 l +h +19.285404 9.496035 m +11.747522 2.429268 l +12.882865 1.218237 l +20.420748 8.285003 l +19.285404 9.496035 l +h +11.461250 2.553288 m +11.461250 5.870056 l +9.801250 5.870056 l +9.801250 2.553288 l +11.461250 2.553288 l +h +10.631250 6.700056 m +5.229464 6.700056 2.125496 4.328404 0.561160 2.525639 c +1.814935 1.437683 l +3.118428 2.939848 5.787818 5.040056 10.631250 5.040056 c +10.631250 6.700056 l +h +0.915461 2.267101 m +1.091532 4.065034 1.607588 6.660966 3.036170 8.786676 c +4.431379 10.862727 6.724575 12.540056 10.631250 12.540056 c +10.631250 14.200056 l +6.139981 14.200056 3.343834 12.220509 1.658399 9.712606 c +0.006337 7.254363 -0.549554 4.339265 -0.736636 2.428890 c +0.915461 2.267101 l +h +0.561160 2.525639 m +0.611023 2.583103 0.667563 2.604307 0.696526 2.610254 c +0.721157 2.615311 0.734035 2.612202 0.741838 2.609715 c +0.747947 2.607769 0.788049 2.594332 0.833411 2.538847 c +0.888431 2.471550 0.925537 2.369989 0.915461 2.267101 c +-0.736636 2.428890 l +-0.813821 1.640717 -0.224644 1.175383 0.238040 1.028011 c +0.696962 0.881838 1.366337 0.920712 1.814935 1.437683 c +0.561160 2.525639 l +h +11.747522 2.429268 m +11.712430 2.396370 11.683138 2.386383 11.659966 2.382921 c +11.632549 2.378824 11.598459 2.382227 11.563670 2.397299 c +11.528881 2.412371 11.503082 2.434914 11.487321 2.457716 c +11.473999 2.476988 11.461250 2.505186 11.461250 2.553288 c +9.801250 2.553288 l +9.801250 0.951229 11.714108 0.122528 12.882865 1.218237 c +11.747522 2.429268 l +h +19.285404 9.744079 m +19.357044 9.676915 19.357044 9.563196 19.285404 9.496035 c +20.420748 8.285003 l +21.191931 9.007989 21.191929 10.232126 20.420746 10.955110 c +19.285404 9.744079 l +h +11.461250 16.686825 m +11.461250 16.734926 11.473999 16.763124 11.487321 16.782398 c +11.503083 16.805201 11.528882 16.827742 11.563670 16.842813 c +11.598459 16.857885 11.632549 16.861290 11.659966 16.857193 c +11.683137 16.853729 11.712428 16.843744 11.747520 16.810844 c +12.882863 18.021877 l +11.714105 19.117588 9.801250 18.288879 9.801250 16.686825 c +11.461250 16.686825 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 18.334961 19.840027 cm +0.000000 0.000000 0.000000 scn +2.165001 7.999987 m +1.703843 7.999987 1.330001 7.626144 1.330001 7.164987 c +1.330001 6.703829 1.703843 6.329987 2.165001 6.329987 c +2.626159 6.329987 3.000001 6.703829 3.000001 7.164987 c +3.000001 7.626144 2.626159 7.999987 2.165001 7.999987 c +h +2.165001 9.329987 m +3.128699 9.329987 3.945334 8.700336 4.225954 7.829987 c +9.165001 7.829987 l +9.532270 7.829987 9.830001 7.532256 9.830001 7.164987 c +9.830001 6.797717 9.532270 6.499987 9.165001 6.499987 c +4.225954 6.499987 l +3.945334 5.629638 3.128699 4.999987 2.165001 4.999987 c +0.969304 4.999987 0.000000 5.969290 0.000000 7.164987 c +0.000000 8.360683 0.969304 9.329987 2.165001 9.329987 c +h +1.165001 2.829987 m +0.797732 2.829987 0.500001 2.532256 0.500001 2.164987 c +0.500001 1.797717 0.797732 1.499987 1.165001 1.499987 c +6.104048 1.499987 l +6.384667 0.629638 7.201303 -0.000013 8.165001 -0.000013 c +9.360698 -0.000013 10.330001 0.969290 10.330001 2.164987 c +10.330001 3.360683 9.360698 4.329987 8.165001 4.329987 c +7.201303 4.329987 6.384668 3.700336 6.104048 2.829987 c +1.165001 2.829987 l +h +7.330001 2.164987 m +7.330001 1.703829 7.703843 1.329987 8.165001 1.329987 c +8.626159 1.329987 9.000001 1.703829 9.000001 2.164987 c +9.000001 2.626144 8.626159 2.999987 8.165001 2.999987 c +7.703843 2.999987 7.330001 2.626144 7.330001 2.164987 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 4388 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000004478 00000 n +0000004501 00000 n +0000004674 00000 n +0000004748 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +4807 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 332837661e..a4e50d50ff 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -2743,8 +2743,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.openPeer(peer: nil, navigation: .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: inputString)), subject: nil, peekData: nil), fromMessage: nil, peerTypes: peerTypes) } } - }, openUrl: { [weak self] url, concealed, _, message, progress in + }, openUrl: { [weak self] urlData in if let strongSelf = self { + let url = urlData.url + let concealed = urlData.concealed + let message = urlData.message + let progress = urlData.progress + var skipConcealedAlert = false if let author = message?.author, author.isVerified { skipConcealedAlert = true @@ -2758,7 +2763,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G performOpenURL(message, url, progress) } else { progress?.set(.single(false)) - strongSelf.openUrl(url, concealed: concealed, skipConcealedAlert: skipConcealedAlert, message: message) + strongSelf.openUrl(url, concealed: concealed, skipConcealedAlert: skipConcealedAlert, message: message, allowInlineWebpageResolution: urlData.allowInlineWebpageResolution) } } }, shareCurrentLocation: { [weak self] in @@ -3365,7 +3370,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G items.append(ActionSheetButtonItem(title: url.title, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self { - strongSelf.controllerInteraction?.openUrl(url.url, false, false, message, nil) + strongSelf.controllerInteraction?.openUrl(ChatControllerInteraction.OpenUrl(url: url.url, concealed: false, external: false, message: message)) } })) } @@ -3444,7 +3449,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) }, openSearch: { }, setupReply: { [weak self] messageId in - self?.interfaceInteraction?.setupReplyMessage(messageId, { _ in }) + self?.interfaceInteraction?.setupReplyMessage(messageId, { _, _ in }) }, canSetupReply: { [weak self] message in if !message.flags.contains(.Incoming) { if !message.flags.intersection([.Failed, .Sending, .Unsent]).isEmpty { @@ -4609,7 +4614,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case let .join(_, joinHash): self.controllerInteraction?.openJoinLink(joinHash) case let .webPage(_, url): - self.controllerInteraction?.openUrl(url, false, false, nil, nil) + self.controllerInteraction?.openUrl(ChatControllerInteraction.OpenUrl(url: url, concealed: false, external: false)) } }, openRequestedPeerSelection: { [weak self] messageId, peerType, buttonId in guard let self else { @@ -8645,20 +8650,33 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G messageId: message.id, quote: nil )) - }).updatedSearch(nil).updatedShowCommands(false) }, completion: completion) + }).updatedSearch(nil).updatedShowCommands(false) }, completion: { t in + completion(t, {}) + }) strongSelf.updateItemNodesSearchTextHighlightStates() strongSelf.chatDisplayNode.ensureInputViewFocused() } else { - completion(.immediate) + completion(.immediate, {}) } }, alertAction: { - completion(.immediate) + completion(.immediate, {}) }, delay: true) } else { - completion(.immediate) + let replySubject = ChatInterfaceState.ReplyMessageSubject( + messageId: messageId, + quote: nil + ) + completion(.immediate, { + guard let self else { + return + } + moveReplyMessageToAnotherChat(selfController: self, replySubject: replySubject) + }) } } else { - strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(nil) }) }, completion: completion) + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(nil) }) }, completion: { t in + completion(t, {}) + }) } }, setupEditMessage: { [weak self] messageId, completion in if let strongSelf = self, strongSelf.isNodeLoaded { @@ -17970,10 +17988,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }, contentContext: nil) } - func openUrl(_ url: String, concealed: Bool, forceExternal: Bool = false, skipUrlAuth: Bool = false, skipConcealedAlert: Bool = false, message: Message? = nil, commit: @escaping () -> Void = {}) { + func openUrl(_ url: String, concealed: Bool, forceExternal: Bool = false, skipUrlAuth: Bool = false, skipConcealedAlert: Bool = false, message: Message? = nil, allowInlineWebpageResolution: Bool = false, commit: @escaping () -> Void = {}) { self.commitPurposefulAction() - if let message, let webpage = message.media.first(where: { $0 is TelegramMediaWebpage }) as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, content.url == url { + if allowInlineWebpageResolution, let message, let webpage = message.media.first(where: { $0 is TelegramMediaWebpage }) as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, content.url == url { if content.instantPage != nil { if let navigationController = self.navigationController as? NavigationController { switch instantPageType(of: content) { diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index abd5a86e99..d0e5b7bba2 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -206,7 +206,7 @@ extension ListMessageItemInteraction { }, toggleMessagesSelection: { messageId, selected in controllerInteraction.toggleMessagesSelection(messageId, selected) }, openUrl: { url, param1, param2, message in - controllerInteraction.openUrl(url, param1, param2, message, nil) + controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: url, concealed: param1, external: param2, message: message)) }, openInstantPage: { message, data in controllerInteraction.openInstantPage(message, data) }, longTap: { action, message in diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 9df01a5677..21b559877b 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -294,6 +294,9 @@ func canReplyInChat(_ chatPresentationInterfaceState: ChatPresentationInterfaceS if case .member = channel.participationStatus { canReply = channel.hasPermission(.sendSomething) } + if case .broadcast = channel.info { + canReply = true + } } else if let group = peer as? TelegramGroup { if case .Member = group.membership { canReply = true @@ -958,9 +961,11 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState if !isPinnedMessages, !isReplyThreadHead, data.canReply { actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuReply, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reply"), color: theme.actionSheet.primaryTextColor) - }, action: { _, f in - interfaceInteraction.setupReplyMessage(messages[0].id, { transition in - f(.custom(transition)) + }, action: { c, _ in + interfaceInteraction.setupReplyMessage(messages[0].id, { transition, completed in + c.dismiss(result: .custom(transition), completion: { + completed() + }) }) }))) } diff --git a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift index 449012ea15..04eda7684b 100644 --- a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift @@ -22,6 +22,7 @@ import TextNodeWithEntities import AnimationCache import MultiAnimationRenderer import TranslateUI +import ChatControllerInteraction private enum PinnedMessageAnimation { case slideToTop @@ -858,7 +859,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { if url.hasPrefix("tg://") { isConcealed = false } - controllerInteraction.openUrl(url, isConcealed, nil, nil, nil) + controllerInteraction.openUrl(ChatControllerInteraction.OpenUrl(url: url, concealed: isConcealed)) case .requestMap: controllerInteraction.shareCurrentLocation() case .requestPhone: diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index c0e9e488e2..9c393ae5ef 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -98,7 +98,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _, _ in - }, openUrl: { _, _, _, _, _ in + }, openUrl: { _ in }, shareCurrentLocation: { }, shareAccountContact: { }, sendBotCommand: { _, _ in diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index 063e239a3a..b1deb2b1b5 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -2779,11 +2779,11 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _, _ in - }, openUrl: { [weak self] url, concealed, external, _, _ in + }, openUrl: { [weak self] url in guard let strongSelf = self else { return } - strongSelf.openUrl(url: url, concealed: concealed, external: external ?? false) + strongSelf.openUrl(url: url.url, concealed: url.concealed, external: url.external ?? false) }, shareCurrentLocation: { }, shareAccountContact: { }, sendBotCommand: { _, _ in diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index ce2d8cdf5c..56e8e59804 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1490,7 +1490,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { clickThroughMessage?() }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _, _ in return false }, sendEmoji: { _, _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _, _ in return false - }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _, _ in }, openUrl: { _, _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in + }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _, _ in }, openUrl: { _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in }, presentController: { _, _ in }, presentControllerInCurrent: { _, _ in }, navigationController: { diff --git a/submodules/TelegramUI/Sources/WebpagePreviewAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/WebpagePreviewAccessoryPanelNode.swift index 87270c2649..630b6b118a 100644 --- a/submodules/TelegramUI/Sources/WebpagePreviewAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/WebpagePreviewAccessoryPanelNode.swift @@ -9,6 +9,7 @@ import AccountContext import TelegramStringFormatting import ChatPresentationInterfaceState import AccessoryPanelNode +import AppBundle final class WebpagePreviewAccessoryPanelNode: AccessoryPanelNode { private let webpageDisposable = MetaDisposable() @@ -18,7 +19,7 @@ final class WebpagePreviewAccessoryPanelNode: AccessoryPanelNode { let closeButton: HighlightableButtonNode let lineNode: ASImageNode - let iconNode: ASImageNode + let iconView: UIImageView let titleNode: TextNode private var titleString: NSAttributedString? @@ -46,10 +47,9 @@ final class WebpagePreviewAccessoryPanelNode: AccessoryPanelNode { self.lineNode.displaysAsynchronously = false self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(theme) - self.iconNode = ASImageNode() - self.iconNode.displayWithoutProcessing = false - self.iconNode.displaysAsynchronously = false - self.iconNode.image = PresentationResourcesChat.chatInputPanelWebpageIconImage(theme) + self.iconView = UIImageView() + self.iconView.image = UIImage(bundleImageName: "Chat/Input/Accessory Panels/LinkSettingsIcon")?.withRenderingMode(.alwaysTemplate) + self.iconView.tintColor = theme.chat.inputPanel.panelControlAccentColor self.titleNode = TextNode() self.titleNode.displaysAsynchronously = false @@ -63,7 +63,7 @@ final class WebpagePreviewAccessoryPanelNode: AccessoryPanelNode { self.addSubnode(self.closeButton) self.addSubnode(self.lineNode) - self.addSubnode(self.iconNode) + self.view.addSubview(self.iconView) self.addSubnode(self.titleNode) self.addSubnode(self.textNode) @@ -75,11 +75,11 @@ final class WebpagePreviewAccessoryPanelNode: AccessoryPanelNode { } override func animateIn() { - self.iconNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2) + self.iconView.layer.animateScale(from: 0.001, to: 1.0, duration: 0.2) } override func animateOut() { - self.iconNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false) + self.iconView.layer.animateScale(from: 1.0, to: 0.001, duration: 0.2, removeOnCompletion: false) } override public func didLoad() { @@ -101,7 +101,7 @@ final class WebpagePreviewAccessoryPanelNode: AccessoryPanelNode { self.closeButton.setImage(PresentationResourcesChat.chatInputPanelCloseIconImage(theme), for: []) self.lineNode.image = PresentationResourcesChat.chatInputPanelVerticalSeparatorLineImage(theme) - self.iconNode.image = PresentationResourcesChat.chatInputPanelWebpageIconImage(theme) + self.iconView.tintColor = theme.chat.inputPanel.panelControlAccentColor } if let text = self.titleString?.string { @@ -194,8 +194,8 @@ final class WebpagePreviewAccessoryPanelNode: AccessoryPanelNode { self.lineNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 8.0), size: CGSize(width: 2.0, height: bounds.size.height - 10.0)) - if let icon = self.iconNode.image { - self.iconNode.frame = CGRect(origin: CGPoint(x: 7.0 + inset, y: 10.0), size: icon.size) + if let icon = self.iconView.image { + self.iconView.frame = CGRect(origin: CGPoint(x: 7.0 + inset, y: 10.0), size: icon.size) } let makeTitleLayout = TextNode.asyncLayout(self.titleNode)