From 126e8c88582694425d52095bdc37f6b16d1e60e7 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Mon, 15 May 2023 22:01:44 -0400 Subject: [PATCH] Resolve Mastodon remote status links Closes #384 --- .../ConversationViewController.swift | 26 ++++++++++++++++--- Tusker/TuskerNavigationDelegate.swift | 1 + 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Tusker/Screens/Conversation/ConversationViewController.swift b/Tusker/Screens/Conversation/ConversationViewController.swift index 3ccc51a7..3e3a5ddc 100644 --- a/Tusker/Screens/Conversation/ConversationViewController.swift +++ b/Tusker/Screens/Conversation/ConversationViewController.swift @@ -11,6 +11,13 @@ import Pachyderm import WebURL import WebURLFoundationExtras +private let mastodonRemoteStatusRegex = try! NSRegularExpression(pattern: "^/@.+@.+/\\d{18}") +private func isLikelyMastodonRemoteStatus(url: URL) -> Bool { + let path = url.path + let range = NSRange(location: 0, length: path.utf16.count) + return mastodonRemoteStatusRegex.numberOfMatches(in: path, range: range) == 1 +} + class ConversationViewController: UIViewController { weak var mastodonController: MastodonController! @@ -210,11 +217,24 @@ class ConversationViewController: UIViewController { indicator.startAnimating() state = .loading(indicator) - let url = WebURL(url)!.serialized(excludingFragment: true) - let request = Client.search(query: url, types: [.statuses], resolve: true) + let effectiveURL: String + class RedirectBlocker: NSObject, URLSessionTaskDelegate { + func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) { + completionHandler(nil) + } + } + if isLikelyMastodonRemoteStatus(url: url), + let (_, response) = try? await URLSession.shared.data(from: url, delegate: RedirectBlocker()), + let location = (response as? HTTPURLResponse)?.value(forHTTPHeaderField: "location") { + effectiveURL = location + } else { + effectiveURL = WebURL(url)!.serialized(excludingFragment: true) + } + + let request = Client.search(query: effectiveURL, types: [.statuses], resolve: true) do { let (results, _) = try await mastodonController.run(request) - guard let status = results.statuses.first(where: { $0.url?.serialized() == url }) else { + guard let status = results.statuses.first(where: { $0.url?.serialized() == effectiveURL }) else { throw UnableToResolveError() } _ = mastodonController.persistentContainer.addOrUpdateOnViewContext(status: status) diff --git a/Tusker/TuskerNavigationDelegate.swift b/Tusker/TuskerNavigationDelegate.swift index fe1eabbc..c4989a25 100644 --- a/Tusker/TuskerNavigationDelegate.swift +++ b/Tusker/TuskerNavigationDelegate.swift @@ -205,6 +205,7 @@ enum PopoverSource { private let statusPathRegex = try! NSRegularExpression( pattern: "(^/@[a-z0-9_]+/\\d{18})" // mastodon + + "|(^/@.+@.+/\\d{18})" // mastodon remote + "|(^/notice/[a-z0-9]{18})" // pleroma + "|(^/notes/[a-z0-9]{10})" // misskey + "|(^/p/[a-z0-9_]+/\\d{18})" // pixelfed