From f8e994c074e29852a576889c56a646f92fd9eecb Mon Sep 17 00:00:00 2001
From: "B. Petersen" <r10s@b44t.com>
Date: Tue, 17 Dec 2024 23:53:38 +0100
Subject: [PATCH] get link text to copy

---
 deltachat-ios/Chat/ChatViewController.swift | 26 +++++----
 deltachat-ios/Chat/Views/MessageLabel.swift | 64 ++++++++++++---------
 2 files changed, 54 insertions(+), 36 deletions(-)

diff --git a/deltachat-ios/Chat/ChatViewController.swift b/deltachat-ios/Chat/ChatViewController.swift
index ecf2d6706..dc160c170 100644
--- a/deltachat-ios/Chat/ChatViewController.swift
+++ b/deltachat-ios/Chat/ChatViewController.swift
@@ -2021,6 +2021,15 @@ extension ChatViewController {
         menuElements.append(action)
     }
 
+    private func isLinkTapped(indexPath: IndexPath, point: CGPoint) -> String? {
+        if let cell = tableView.cellForRow(at: indexPath) as? BaseMessageCell {
+            let label = cell.messageLabel.label
+            let localTouchLocation = tableView.convert(point, to: label)
+            return label.getCopyableLinkText(localTouchLocation)
+        }
+        return nil
+    }
+
     // context menu for iOS 13+
     override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
         let messageId = messageIds[indexPath.row]
@@ -2028,15 +2037,6 @@ extension ChatViewController {
             return nil
         }
 
-        // Check if the long tap is on a link (or other message text element with custom long tap behavior)
-        if let msgcell = tableView.cellForRow(at: indexPath) as? BaseMessageCell {
-            let label = msgcell.messageLabel.label
-            let localTouchLocation = tableView.convert(point, to: label)
-            if let (detectorType, value) = label.detectGesture(localTouchLocation) {
-                print("url: \(detectorType) -- \(value) -- ")
-            }
-        }
-
         return UIContextMenuConfiguration(
             identifier: NSString(string: "\(messageId)"),
             previewProvider: nil,
@@ -2076,7 +2076,13 @@ extension ChatViewController {
                     UIAction.menuAction(localizationKey: "forward", image: image, indexPath: indexPath, action: { self.forward(at: $0 ) })
                 )
 
-                if let text = message.text, !text.isEmpty {
+                if let link = isLinkTapped(indexPath: indexPath, point: point) {
+                    children.append(
+                        UIAction.menuAction(localizationKey: "menu_copy_link_to_clipboard", systemImageName: "link", indexPath: indexPath, action: { _ in
+                            UIPasteboard.general.string = link
+                        })
+                    )
+                } else if let text = message.text, !text.isEmpty {
                     let copyTitle = message.file == nil ? "global_menu_edit_copy_desktop" : "menu_copy_text_to_clipboard"
                     children.append(
                         UIAction.menuAction(localizationKey: copyTitle, systemImageName: "doc.on.doc", indexPath: indexPath, action: { self.copyToClipboard(at: $0 ) })
diff --git a/deltachat-ios/Chat/Views/MessageLabel.swift b/deltachat-ios/Chat/Views/MessageLabel.swift
index 2f3617354..6ef0f9168 100644
--- a/deltachat-ios/Chat/Views/MessageLabel.swift
+++ b/deltachat-ios/Chat/Views/MessageLabel.swift
@@ -452,55 +452,65 @@ open class MessageLabel: UILabel {
 
     }
 
-    internal func detectGesture(_ touchLocation: CGPoint) -> (DetectorType, NewMessageTextCheckingType)? {
-          guard let index = stringIndex(at: touchLocation) else { return nil }
-
-          for (detectorType, ranges) in rangesForDetectors {
-              for (range, value) in ranges {
-                  if range.contains(index) {
-                      return (detectorType, value)
-                  }
-              }
-          }
-          return nil
-    }
-
-    open func handleGesture(_ touchLocation: CGPoint) -> Bool {
-        if let (detectorType, value) = detectGesture(touchLocation) {
-            handleGesture(for: detectorType, value: value)
-            return true
+    internal func detectLink(_ touchLocation: CGPoint) -> (DetectorType, NewMessageTextCheckingType)? {
+        guard let index = stringIndex(at: touchLocation) else { return nil }
+
+        for (detectorType, ranges) in rangesForDetectors {
+            for (range, value) in ranges {
+                if range.contains(index) {
+                    return (detectorType, value)
+                }
+            }
+        }
+
+        return nil
+    }
+
+    internal func getCopyableLinkText(_ touchLocation: CGPoint) -> String? {
+        guard let (detectorType, value) = detectLink(touchLocation) else { return nil }
+
+        switch value {
+        case let .link(url):
+            guard let url = url else { return nil }
+            return url.absoluteString
+        case let .phoneNumber(phoneNumber):
+            return phoneNumber
+        case let .custom(pattern, match):
+            return match
+        case .addressComponents, .date, .transitInfoComponents:
+            return nil
         }
-        return false
     }
 
-    /// swiftlint:disable cyclomatic_complexity
-    private func handleGesture(for detectorType: DetectorType, value: NewMessageTextCheckingType) {
+    internal func handleGesture(_ touchLocation: CGPoint) -> Bool {
+        guard let (detectorType, value) = detectLink(touchLocation) else { return false }
+
         switch value {
         case let .addressComponents(addressComponents):
             var transformedAddressComponents = [String: String]()
-            guard let addressComponents = addressComponents else { return }
+            guard let addressComponents = addressComponents else { return false }
             addressComponents.forEach { (key, value) in
                 transformedAddressComponents[key.rawValue] = value
             }
             handleAddress(transformedAddressComponents)
         case let .phoneNumber(phoneNumber):
-            guard let phoneNumber = phoneNumber else { return }
+            guard let phoneNumber = phoneNumber else { return false }
             handlePhoneNumber(phoneNumber)
         case let .date(date):
-            guard let date = date else { return }
+            guard let date = date else { return false }
             handleDate(date)
         case let .link(url):
-            guard let url = url else { return }
+            guard let url = url else { return false }
             handleURL(url)
         case let .transitInfoComponents(transitInformation):
             var transformedTransitInformation = [String: String]()
-            guard let transitInformation = transitInformation else { return }
+            guard let transitInformation = transitInformation else { return false }
             transitInformation.forEach { (key, value) in
                 transformedTransitInformation[key.rawValue] = value
             }
             handleTransitInformation(transformedTransitInformation)
         case let .custom(pattern, match):
-            guard let match = match else { return }
+            guard let match = match else { return false }
             switch detectorType {
             case .hashtag:
                 handleHashtag(match)
@@ -512,6 +522,8 @@ open class MessageLabel: UILabel {
                 handleCustom(pattern, match: match)
             }
         }
+
+        return true
     }
 
     private func handleAddress(_ addressComponents: [String: String]) {