Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add option to copy links instead opening #2445

Merged
merged 11 commits into from
Dec 18, 2024
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## Unreleased

- Don't show message-input when forwarding (#2435)
- Long-tap links for copying to clipboard (#2445)


## v1.50.3

Expand Down
17 changes: 16 additions & 1 deletion deltachat-ios/Chat/ChatViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -2067,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 ) })
Expand Down
46 changes: 32 additions & 14 deletions deltachat-ios/Chat/Views/MessageLabel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -452,49 +452,65 @@ open class MessageLabel: UILabel {

}

open func handleGesture(_ touchLocation: CGPoint) -> Bool {

guard let index = stringIndex(at: touchLocation) else { return false }
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) {
handleGesture(for: detectorType, value: value)
return true
return (detectorType, value)
}
}
}
return false

return nil
}

/// swiftlint:disable cyclomatic_complexity
private func handleGesture(for detectorType: DetectorType, value: NewMessageTextCheckingType) {
internal func getCopyableLinkText(_ touchLocation: CGPoint) -> String? {
guard let (_, value) = detectLink(touchLocation) else { return nil }

switch value {
case let .link(url):
guard let url else { return nil }
return url.absoluteString
case let .phoneNumber(phoneNumber):
return phoneNumber
case let .custom(_, match):
return match
case .addressComponents, .date, .transitInfoComponents:
return nil
}
}

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 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 else { return false }
handlePhoneNumber(phoneNumber)
case let .date(date):
guard let date = date else { return }
guard let date else { return false }
handleDate(date)
case let .link(url):
guard let url = url else { return }
guard let url else { return false }
handleURL(url)
case let .transitInfoComponents(transitInformation):
var transformedTransitInformation = [String: String]()
guard let transitInformation = transitInformation else { return }
guard let 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 else { return false }
switch detectorType {
case .hashtag:
handleHashtag(match)
Expand All @@ -506,6 +522,8 @@ open class MessageLabel: UILabel {
handleCustom(pattern, match: match)
}
}

return true
}

private func handleAddress(_ addressComponents: [String: String]) {
Expand Down
Loading