diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index f68f8e0038..5a7081071c 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -12223,11 +12223,15 @@ Sorry for the inconvenience."; "Chat.Context.Username.Copy" = "Copy Username"; "Chat.Context.Username.NotOnTelegram" = "This user doesn't exist on Telegram."; -"Chat.Context.Hashtag.Search" = "View Profile"; +"Chat.Context.Hashtag.Search" = "Search"; "Chat.Context.Hashtag.Copy" = "Copy Hashtag"; "Chat.Context.Card.Copy" = "Copy Card Number"; +"Chat.Context.Command.Copy" = "Copy Command"; + +"Chat.Context.Timecode.Copy" = "Copy Timecode"; + "Message.FactCheck" = "Fact Check"; "Message.FactCheck.WhatIsThis" = "what's this?"; diff --git a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift index d26c351aa8..7eb2349e23 100644 --- a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift @@ -1066,6 +1066,9 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo if case let .extracted(extracted) = self.source { if extracted.adjustContentHorizontally { contentFrame.origin.x = combinedActionsFrame.minX + if contentFrame.maxX > layout.size.width { + contentFrame.origin.x = layout.size.width - contentFrame.width - actionsEdgeInset + } } if extracted.centerVertically { if combinedActionsFrame.height.isZero { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageActionButtonsNode/Sources/ChatMessageActionButtonsNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageActionButtonsNode/Sources/ChatMessageActionButtonsNode.swift index 49cb6a9f55..66ca0c9d8b 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageActionButtonsNode/Sources/ChatMessageActionButtonsNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageActionButtonsNode/Sources/ChatMessageActionButtonsNode.swift @@ -225,7 +225,7 @@ private final class ChatMessageActionButtonNode: ASDisplayNode { let titleColor = bubbleVariableColor(variableColor: messageTheme.actionButtonsTextColor, wallpaper: theme.wallpaper) let attributedTitle: NSAttributedString if isStarsPayment { - let updatedTitle = title.replacingOccurrences(of: "⭐️", with: " # ") + let updatedTitle = title.replacingOccurrences(of: "⭐️", with: " # ") let buttonAttributedString = NSMutableAttributedString(string: updatedTitle, font: titleFont, textColor: titleColor, paragraphAlignment: .center) if let range = buttonAttributedString.string.range(of: "#"), let starImage = UIImage(bundleImageName: "Item List/PremiumIcon") { buttonAttributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: buttonAttributedString.string)) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift index 68b9fecff9..3a4c42bf42 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleContentNode/Sources/ChatMessageBubbleContentNode.swift @@ -163,11 +163,13 @@ public struct ChatMessageBubbleContentTapAction { } public var content: Content + public var rects: [CGRect]? public var hasLongTapAction: Bool public var activate: (() -> Promise?)? - public init(content: Content, hasLongTapAction: Bool = true, activate: (() -> Promise?)? = nil) { + public init(content: Content, rects: [CGRect]? = nil, hasLongTapAction: Bool = true, activate: (() -> Promise?)? = nil) { self.content = content + self.rects = rects self.hasLongTapAction = hasLongTapAction self.activate = activate } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 13cce07f84..cb0561b055 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -4649,6 +4649,13 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI let convertedLocation = self.view.convert(location, to: contentNode.view) let tapAction = contentNode.tapActionAtPoint(convertedLocation, gesture: gesture, isEstimating: false) + var rects: [CGRect] = [] + if let actionRects = tapAction.rects { + for rect in actionRects { + rects.append(rect.offsetBy(dx: contentNode.frame.minX, dy: contentNode.frame.minY)) + } + } + 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 { @@ -4692,13 +4699,11 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } case let .phone(number): return .action(InternalBubbleTapAction.Action({ [weak self] in - guard let self, let item = self.item, let contentNode = self.contextContentNodeForLink(number) else { + guard let self, let item = self.item, let contentNode = self.contextContentNodeForLink(number, rects: rects) else { return } item.controllerInteraction.longTap(.phone(number), ChatControllerInteraction.LongTapParams(message: item.content.firstMessage, contentNode: contentNode, messageNode: self, progress: tapAction.activate?())) - -// item.controllerInteraction.openPhoneContextMenu(ChatControllerInteraction.OpenPhone(number: number, message: item.content.firstMessage, contentNode: contentNode, messageNode: self, progress: tapAction.activate?())) }, contextMenuOnLongPress: !tapAction.hasLongTapAction)) case let .peerMention(peerId, _, openProfile): return .action(InternalBubbleTapAction.Action { [weak self] in @@ -4768,7 +4773,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI case let .bankCard(number): if let item = self.item { return .action(InternalBubbleTapAction.Action { [weak self] in - guard let self, let contentNode = self.contextContentNodeForLink(number) else { + guard let self, let contentNode = self.contextContentNodeForLink(number, rects: rects) else { return } item.controllerInteraction.longTap(.bankCard(number), ChatControllerInteraction.LongTapParams(message: item.message, contentNode: contentNode, messageNode: self, progress: tapAction.activate?())) @@ -4854,6 +4859,13 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI tapMessage = contentNode.item?.message } let tapAction = contentNode.tapActionAtPoint(convertedLocation, gesture: gesture, isEstimating: false) + var rects: [CGRect] = [] + if let actionRects = tapAction.rects { + for rect in actionRects { + rects.append(rect.offsetBy(dx: contentNode.frame.minX, dy: contentNode.frame.minY)) + } + } + switch tapAction.content { case .none, .ignore: break @@ -4861,7 +4873,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if tapAction.hasLongTapAction { return .action(InternalBubbleTapAction.Action({ [weak self] in let cleanUrl = url.url.replacingOccurrences(of: "mailto:", with: "") - guard let self, let contentNode = self.contextContentNodeForLink(cleanUrl) else { + guard let self, let contentNode = self.contextContentNodeForLink(cleanUrl, rects: rects) else { return } item.controllerInteraction.longTap(.url(url.url), ChatControllerInteraction.LongTapParams(message: item.content.firstMessage, contentNode: contentNode, messageNode: self, progress: tapAction.activate?())) @@ -4871,35 +4883,35 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } case let .phone(number): return .action(InternalBubbleTapAction.Action({ [weak self] in - guard let self, let contentNode = self.contextContentNodeForLink(number) else { + guard let self, let contentNode = self.contextContentNodeForLink(number, rects: rects) else { return } item.controllerInteraction.longTap(.phone(number), ChatControllerInteraction.LongTapParams(message: item.content.firstMessage, contentNode: contentNode, messageNode: self, progress: tapAction.activate?())) }, contextMenuOnLongPress: !tapAction.hasLongTapAction)) case let .peerMention(peerId, mention, _): return .action(InternalBubbleTapAction.Action { [weak self] in - guard let self, let contentNode = self.contextContentNodeForLink(mention) else { + guard let self, let contentNode = self.contextContentNodeForLink(mention, rects: rects) else { return } item.controllerInteraction.longTap(.peerMention(peerId, mention), ChatControllerInteraction.LongTapParams(message: item.content.firstMessage, contentNode: contentNode, messageNode: self, progress: tapAction.activate?())) }) case let .textMention(name): return .action(InternalBubbleTapAction.Action { [weak self] in - guard let self, let contentNode = self.contextContentNodeForLink(name) else { + guard let self, let contentNode = self.contextContentNodeForLink(name, rects: rects) else { return } item.controllerInteraction.longTap(.mention(name), ChatControllerInteraction.LongTapParams(message: item.content.firstMessage, contentNode: contentNode, messageNode: self, progress: tapAction.activate?())) }) case let .botCommand(command): return .action(InternalBubbleTapAction.Action { [weak self] in - guard let self, let contentNode = self.contextContentNodeForLink(command) else { + guard let self, let contentNode = self.contextContentNodeForLink(command, rects: rects) else { return } item.controllerInteraction.longTap(.command(command), ChatControllerInteraction.LongTapParams(message: item.content.firstMessage, contentNode: contentNode, messageNode: self, progress: tapAction.activate?())) }) case let .hashtag(_, hashtag): return .action(InternalBubbleTapAction.Action { [weak self] in - guard let self, let contentNode = self.contextContentNodeForLink(hashtag) else { + guard let self, let contentNode = self.contextContentNodeForLink(hashtag, rects: rects) else { return } item.controllerInteraction.longTap(.hashtag(hashtag), ChatControllerInteraction.LongTapParams(message: item.content.firstMessage, contentNode: contentNode, messageNode: self, progress: tapAction.activate?())) @@ -4917,7 +4929,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI case let .timecode(timecode, text): if let mediaMessage = mediaMessage { return .action(InternalBubbleTapAction.Action { [weak self] in - guard let self, let contentNode = self.contextContentNodeForLink(text) else { + guard let self, let contentNode = self.contextContentNodeForLink(text, rects: rects) else { return } item.controllerInteraction.longTap(.timecode(timecode, text), ChatControllerInteraction.LongTapParams(message: mediaMessage, contentNode: contentNode, messageNode: self, progress: tapAction.activate?())) @@ -4925,7 +4937,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } case let .bankCard(number): return .action(InternalBubbleTapAction.Action { [weak self] in - guard let self, let contentNode = self.contextContentNodeForLink(number) else { + guard let self, let contentNode = self.contextContentNodeForLink(number, rects: rects) else { return } item.controllerInteraction.longTap(.bankCard(number), ChatControllerInteraction.LongTapParams(message: item.content.firstMessage, contentNode: contentNode, messageNode: self, progress: tapAction.activate?())) @@ -4963,7 +4975,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI return nil } - private func contextContentNodeForLink(_ link: String) -> ContextExtractedContentContainingNode? { + private func contextContentNodeForLink(_ link: String, rects: [CGRect]?) -> ContextExtractedContentContainingNode? { guard let item = self.item else { return nil } @@ -4972,8 +4984,9 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI let incoming = item.content.effectivelyIncoming(item.context.account.peerId, associatedData: item.associatedData) let textNode = ImmediateTextNode() + textNode.maximumNumberOfLines = 2 textNode.attributedText = NSAttributedString(string: link, font: Font.regular(item.presentationData.fontSize.baseDisplaySize), textColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.linkTextColor : item.presentationData.theme.theme.chat.message.outgoing.linkTextColor) - let textSize = textNode.updateLayout(CGSize(width: 1000.0, height: 100.0)) + let textSize = textNode.updateLayout(CGSize(width: self.bounds.width - 32.0, height: 100.0)) let backgroundNode = ASDisplayNode() backgroundNode.backgroundColor = (incoming ? item.presentationData.theme.theme.chat.message.incoming.bubble.withoutWallpaper.fill : item.presentationData.theme.theme.chat.message.outgoing.bubble.withoutWallpaper.fill).first ?? .black @@ -4986,7 +4999,12 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI textNode.frame = CGRect(origin: CGPoint(x: insets.left, y: insets.top), size: textSize) backgroundNode.addSubnode(textNode) - containingNode.frame = CGRect(origin: CGPoint(x: self.backgroundNode.frame.minX + 3.0, y: 1.0), size: CGSize(width: backgroundSize.width, height: backgroundSize.height + 20.0)) + var origin = CGPoint(x: self.backgroundNode.frame.minX + 3.0, y: 1.0) + if let rect = rects?.first { + origin = rect.origin + } + + containingNode.frame = CGRect(origin: origin, size: CGSize(width: backgroundSize.width, height: backgroundSize.height + 20.0)) containingNode.contentNode.frame = CGRect(origin: .zero, size: backgroundSize) containingNode.contentRect = CGRect(origin: .zero, size: backgroundSize) containingNode.contentNode.addSubnode(backgroundNode) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift index 15ae42f2a9..ae805ac9af 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift @@ -929,6 +929,24 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { let textNodeFrame = self.textNode.textNode.frame let textLocalPoint = CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY) if let (index, attributes) = self.textNode.textNode.attributesAtPoint(textLocalPoint) { + var rects: [CGRect]? + let possibleNames: [String] = [ + TelegramTextAttributes.URL, + TelegramTextAttributes.PeerMention, + TelegramTextAttributes.PeerTextMention, + TelegramTextAttributes.BotCommand, + TelegramTextAttributes.Hashtag, + TelegramTextAttributes.Timecode, + TelegramTextAttributes.BankCard + ] + for name in possibleNames { + if let _ = attributes[NSAttributedString.Key(rawValue: name)] { + rects = self.textNode.textNode.attributeRects(name: name, at: index) + break + } + } + + if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler)], !self.displayContentsUnderSpoilers.value { return ChatMessageBubbleContentTapAction(content: .none) } else if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { @@ -946,28 +964,28 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { content = .url(ChatMessageBubbleContentTapAction.Url(url: url, concealed: concealed)) } - return ChatMessageBubbleContentTapAction(content: content, activate: makeActivate(urlRange)) + return ChatMessageBubbleContentTapAction(content: content, rects: rects, activate: makeActivate(urlRange)) } else if let peerMention = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerMention)] as? TelegramPeerMention { - return ChatMessageBubbleContentTapAction(content: .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: false)) + return ChatMessageBubbleContentTapAction(content: .peerMention(peerId: peerMention.peerId, mention: peerMention.mention, openProfile: false), rects: rects) } else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String { var urlRange: NSRange? if let (_, _, urlRangeValue) = self.textNode.textNode.attributeSubstringWithRange(name: TelegramTextAttributes.PeerTextMention, index: index) { urlRange = urlRangeValue } - return ChatMessageBubbleContentTapAction(content: .textMention(peerName), activate: makeActivate(urlRange)) + return ChatMessageBubbleContentTapAction(content: .textMention(peerName), rects: rects, activate: makeActivate(urlRange)) } else if let botCommand = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.BotCommand)] as? String { - return ChatMessageBubbleContentTapAction(content: .botCommand(botCommand)) + return ChatMessageBubbleContentTapAction(content: .botCommand(botCommand), rects: rects) } else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag { - return ChatMessageBubbleContentTapAction(content: .hashtag(hashtag.peerName, hashtag.hashtag)) + return ChatMessageBubbleContentTapAction(content: .hashtag(hashtag.peerName, hashtag.hashtag), rects: rects) } else if let timecode = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Timecode)] as? TelegramTimecode { - return ChatMessageBubbleContentTapAction(content: .timecode(timecode.time, timecode.text)) + return ChatMessageBubbleContentTapAction(content: .timecode(timecode.time, timecode.text), rects: rects) } else if let bankCard = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.BankCard)] as? String { var urlRange: NSRange? if let (_, _, urlRangeValue) = self.textNode.textNode.attributeSubstringWithRange(name: TelegramTextAttributes.BankCard, index: index) { urlRange = urlRangeValue } - return ChatMessageBubbleContentTapAction(content: .bankCard(bankCard), activate: makeActivate(urlRange)) + return ChatMessageBubbleContentTapAction(content: .bankCard(bankCard), rects: rects, activate: makeActivate(urlRange)) } else if let pre = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Pre)] as? String { return ChatMessageBubbleContentTapAction(content: .copy(pre)) } else if let code = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Code)] as? String { diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/CodableDrawingEntity.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/CodableDrawingEntity.swift index 8951b64618..ad6000d2cc 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/CodableDrawingEntity.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/Drawing/CodableDrawingEntity.swift @@ -69,6 +69,7 @@ public enum CodableDrawingEntity: Equatable { var size: CGSize? var rotation: CGFloat? var scale: CGFloat? + var cornerRadius: Double? switch self { case let .location(entity): @@ -76,6 +77,9 @@ public enum CodableDrawingEntity: Equatable { size = entity.renderImage?.size rotation = entity.rotation scale = entity.scale + if let size { + cornerRadius = 10.0 / (size.width * entity.scale) + } case let .sticker(entity): var entityPosition = entity.position var entitySize = entity.baseSize @@ -83,7 +87,7 @@ public enum CodableDrawingEntity: Equatable { let entityScale = entity.scale if case .message = entity.content { - let offset: CGFloat = 16.18 * entityScale //54.0 * entityScale / 3.337 + let offset: CGFloat = 16.18 * entityScale entitySize = CGSize(width: entitySize.width - 38.0, height: entitySize.height - 4.0) entityPosition = CGPoint(x: entityPosition.x + offset * cos(entityRotation), y: entityPosition.y + offset * sin(entityRotation)) } @@ -94,9 +98,17 @@ public enum CodableDrawingEntity: Equatable { scale = entityScale case let .link(entity): position = entity.position - size = entity.renderImage?.size rotation = entity.rotation scale = entity.scale + if let entitySize = entity.renderImage?.size { + if entity.whiteImage != nil { + cornerRadius = 38.0 / (entitySize.width * entity.scale) + size = CGSize(width: entitySize.width - 28.0, height: entitySize.height - 26.0) + } else { + cornerRadius = 10.0 / (entitySize.width * entity.scale) + size = entitySize + } + } default: return nil } @@ -105,13 +117,16 @@ public enum CodableDrawingEntity: Equatable { return nil } + let width = size.width * scale / 1080.0 * 100.0 + let height = size.height * scale / 1920.0 * 100.0 + return MediaArea.Coordinates( x: position.x / 1080.0 * 100.0, y: position.y / 1920.0 * 100.0, - width: size.width * scale / 1080.0 * 100.0, - height: size.height * scale / 1920.0 * 100.0, + width: width, + height: height, rotation: rotation / .pi * 180.0, - cornerRadius: nil + cornerRadius: cornerRadius.flatMap { $0 * 100.0 } ) } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenCommandContextMenu.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenCommandContextMenu.swift new file mode 100644 index 0000000000..ce1746705c --- /dev/null +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenCommandContextMenu.swift @@ -0,0 +1,69 @@ +import Foundation +import UIKit +import SwiftSignalKit +import Postbox +import TelegramCore +import AsyncDisplayKit +import Display +import ContextUI +import UndoUI +import AccountContext +import ChatMessageItemView +import ChatMessageItemCommon +import ChatControllerInteraction + +extension ChatControllerImpl { + func openCommandContextMenu(command: String, params: ChatControllerInteraction.LongTapParams) -> Void { + guard let message = params.message, let contentNode = params.contentNode else { + return + } + + guard let messages = self.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(message.id) else { + return + } + + var updatedMessages = messages + for i in 0 ..< updatedMessages.count { + if updatedMessages[i].id == message.id { + let message = updatedMessages.remove(at: i) + updatedMessages.insert(message, at: 0) + break + } + } + + let recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil// anyRecognizer as? TapLongTapOrDoubleTapGestureRecognizer + let gesture: ContextGesture? = nil // anyRecognizer as? ContextGesture + + let source: ContextContentSource +// if let location = location { +// source = .location(ChatMessageContextLocationContentSource(controller: self, location: messageNode.view.convert(messageNode.bounds, to: nil).origin.offsetBy(dx: location.x, dy: location.y))) +// } else { + source = .extracted(ChatMessageLinkContextExtractedContentSource(chatNode: self.chatDisplayNode, contentNode: contentNode)) +// } + + var items: [ContextMenuItem] = [] + + items.append( + .action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Command_Copy, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in + f(.default) + + guard let self else { + return + } + + UIPasteboard.general.string = command + + self.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + })) + ) + + self.canReadHistory.set(false) + + let controller = ContextController(presentationData: self.presentationData, source: source, items: .single(ContextController.Items(content: .list(items))), recognizer: recognizer, gesture: gesture, disableScreenshots: false) + controller.dismissed = { [weak self] in + self?.canReadHistory.set(true) + } + + self.window?.presentInGlobalOverlay(controller) + } +} diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenLinkLongTap.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenLinkLongTap.swift index dd8a2b1d18..1340fdac41 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenLinkLongTap.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenLinkLongTap.swift @@ -26,16 +26,11 @@ extension ChatControllerImpl { case let .peerMention(peerId, mention): self.openMentionContextMenu(username: mention, peerId: peerId, params: params) case let .command(command): - let _ = command - break -// self.openBotCommandContextMenu(command: command, params: params) + self.openCommandContextMenu(command: command, params: params) case let .hashtag(hashtag): self.openHashtagContextMenu(hashtag: hashtag, params: params) case let .timecode(value, timecode): - let _ = value - let _ = timecode - break -// self.openTimecodeContextMenu(timecode: timecode, params: params) + self.openTimecodeContextMenu(timecode: timecode, value: value, params: params) case let .bankCard(number): self.openBankCardContextMenu(number: number, params: params) case let .phone(number): diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenTimecodeContextMenu.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenTimecodeContextMenu.swift new file mode 100644 index 0000000000..0bc97815ad --- /dev/null +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenTimecodeContextMenu.swift @@ -0,0 +1,69 @@ +import Foundation +import UIKit +import SwiftSignalKit +import Postbox +import TelegramCore +import AsyncDisplayKit +import Display +import ContextUI +import UndoUI +import AccountContext +import ChatMessageItemView +import ChatMessageItemCommon +import ChatControllerInteraction + +extension ChatControllerImpl { + func openTimecodeContextMenu(timecode: String, value: Double, params: ChatControllerInteraction.LongTapParams) -> Void { + guard let message = params.message, let contentNode = params.contentNode else { + return + } + + guard let messages = self.chatDisplayNode.historyNode.messageGroupInCurrentHistoryView(message.id) else { + return + } + + var updatedMessages = messages + for i in 0 ..< updatedMessages.count { + if updatedMessages[i].id == message.id { + let message = updatedMessages.remove(at: i) + updatedMessages.insert(message, at: 0) + break + } + } + + let recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil// anyRecognizer as? TapLongTapOrDoubleTapGestureRecognizer + let gesture: ContextGesture? = nil // anyRecognizer as? ContextGesture + + let source: ContextContentSource +// if let location = location { +// source = .location(ChatMessageContextLocationContentSource(controller: self, location: messageNode.view.convert(messageNode.bounds, to: nil).origin.offsetBy(dx: location.x, dy: location.y))) +// } else { + source = .extracted(ChatMessageLinkContextExtractedContentSource(chatNode: self.chatDisplayNode, contentNode: contentNode)) +// } + + var items: [ContextMenuItem] = [] + + items.append( + .action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Context_Timecode_Copy, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in + f(.default) + + guard let self else { + return + } + + UIPasteboard.general.string = timecode + + self.present(UndoOverlayController(presentationData: self.presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current) + })) + ) + + self.canReadHistory.set(false) + + let controller = ContextController(presentationData: self.presentationData, source: source, items: .single(ContextController.Items(content: .list(items))), recognizer: recognizer, gesture: gesture, disableScreenshots: false) + controller.dismissed = { [weak self] in + self?.canReadHistory.set(true) + } + + self.window?.presentInGlobalOverlay(controller) + } +} diff --git a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift index 9844a5678f..b47e365257 100644 --- a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift @@ -322,10 +322,15 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { messageUpdated = true } + var isStarsPayment = false if let message = interfaceState.pinnedMessage, !message.message.isRestricted(platform: "ios", contentSettings: self.context.currentContentSettings.with { $0 }) { for attribute in message.message.attributes { if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), attribute.rows.count == 1, attribute.rows[0].buttons.count == 1 { - actionTitle = attribute.rows[0].buttons[0].title + let title = attribute.rows[0].buttons[0].title + actionTitle = title + if case .payment = attribute.rows[0].buttons[0].action, title.contains("⭐️") { + isStarsPayment = true + } } } } else { @@ -430,7 +435,20 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { self.actionButtonBackgroundNode.isHidden = false self.actionButtonTitleNode.isHidden = false - self.actionButtonTitleNode.attributedText = NSAttributedString(string: actionTitle, font: Font.with(size: 15.0, design: .round, weight: .semibold, traits: [.monospacedNumbers]), textColor: interfaceState.theme.list.itemCheckColors.foregroundColor) + let attributedTitle: NSAttributedString + if isStarsPayment { + let updatedTitle = actionTitle.replacingOccurrences(of: "⭐️", with: " # ") + let buttonAttributedString = NSMutableAttributedString(string: updatedTitle, font: Font.with(size: 15.0, design: .round, weight: .semibold, traits: [.monospacedNumbers]), textColor: interfaceState.theme.list.itemCheckColors.foregroundColor) + if let range = buttonAttributedString.string.range(of: "#"), let starImage = UIImage(bundleImageName: "Item List/PremiumIcon") { + buttonAttributedString.addAttribute(.attachment, value: starImage, range: NSRange(range, in: buttonAttributedString.string)) + buttonAttributedString.addAttribute(.foregroundColor, value: interfaceState.theme.list.itemCheckColors.foregroundColor, range: NSRange(range, in: buttonAttributedString.string)) + buttonAttributedString.addAttribute(.baselineOffset, value: 1.0, range: NSRange(range, in: buttonAttributedString.string)) + } + attributedTitle = buttonAttributedString + } else { + attributedTitle = NSAttributedString(string: actionTitle, font: Font.with(size: 15.0, design: .round, weight: .semibold, traits: [.monospacedNumbers]), textColor: interfaceState.theme.list.itemCheckColors.foregroundColor) + } + self.actionButtonTitleNode.attributedText = attributedTitle let actionButtonTitleSize = self.actionButtonTitleNode.updateLayout(CGSize(width: 150.0, height: .greatestFiniteMagnitude)) let actionButtonSize = CGSize(width: max(actionButtonTitleSize.width + 20.0, 40.0), height: 28.0)