From 245582961bd2e17f41f9df7dd4a828557e290cb7 Mon Sep 17 00:00:00 2001 From: Nathan Mattes Date: Fri, 17 Mar 2023 10:47:19 +0100 Subject: [PATCH] IOS-123: Open profiles and statuses in app (#981) --- Mastodon/Mastodon.entitlements | 5 ++ Mastodon/Supporting Files/SceneDelegate.swift | 82 ++++++++++++++----- 2 files changed, 68 insertions(+), 19 deletions(-) diff --git a/Mastodon/Mastodon.entitlements b/Mastodon/Mastodon.entitlements index 153b958a3..f2ab4df87 100644 --- a/Mastodon/Mastodon.entitlements +++ b/Mastodon/Mastodon.entitlements @@ -4,6 +4,11 @@ aps-environment development + com.apple.developer.associated-domains + + applinks:mastodon.social + applinks:mastodon.online + com.apple.developer.siri com.apple.security.application-groups diff --git a/Mastodon/Supporting Files/SceneDelegate.swift b/Mastodon/Supporting Files/SceneDelegate.swift index 44501a1dd..a5c3a9caa 100644 --- a/Mastodon/Supporting Files/SceneDelegate.swift +++ b/Mastodon/Supporting Files/SceneDelegate.swift @@ -71,6 +71,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { if let urlContext = connectionOptions.urlContexts.first { handleUrl(context: urlContext) } + + if let userActivity = connectionOptions.userActivities.first { + handleUniversalLink(userActivity: userActivity) + } #if SNAPSHOT // speedup animation @@ -104,13 +108,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { #endif } - func sceneDidDisconnect(_ scene: UIScene) { - // Called as the scene is being released by the system. - // This occurs shortly after the scene enters the background, or when its session is discarded. - // Release any resources associated with this scene that can be re-created the next time the scene connects. - // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). - } - func sceneDidBecomeActive(_ scene: UIScene) { // Called when the scene has moved from an inactive state to an active state. // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. @@ -135,22 +132,68 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } } - func sceneWillResignActive(_ scene: UIScene) { - // Called when the scene will move from an active state to an inactive state. - // This may occur due to temporary interruptions (ex. an incoming phone call). + func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { + handleUniversalLink(userActivity: userActivity) } - func sceneWillEnterForeground(_ scene: UIScene) { - // Called as the scene transitions from the background to the foreground. - // Use this method to undo the changes made on entering the background. - } + private func handleUniversalLink(userActivity: NSUserActivity) { + guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, + let incomingURL = userActivity.webpageURL, + let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true) else { + return + } - func sceneDidEnterBackground(_ scene: UIScene) { - // Called as the scene transitions from the foreground to the background. - // Use this method to save data, release shared resources, and store enough scene-specific state information - // to restore the scene back to its current state. - } + guard let path = components.path, let authContext = coordinator?.authContext else { + return + } + let pathElements = path.split(separator: "/") + + let profile: String? + if let profileInPath = pathElements[safe: 0] { + profile = String(profileInPath) + } else { + profile = nil + } + + let statusID: String? + if let statusIDInPath = pathElements[safe: 1] { + statusID = String(statusIDInPath) + } else { + statusID = nil + } + + switch (profile, statusID) { + case (profile, nil): + let profileViewModel = RemoteProfileViewModel( + context: AppContext.shared, + authContext: authContext, + acct: incomingURL.absoluteString + ) + self.coordinator?.present( + scene: .profile(viewModel: profileViewModel), + from: nil, + transition: .show + ) + + case (profile, statusID): + Task { + guard let statusOnMyInstance = try await AppContext.shared.apiService.search(query: .init(q: incomingURL.absoluteString, resolve: true), authenticationBox: authContext.mastodonAuthenticationBox).value.statuses.first else { return } + + let threadViewModel = RemoteThreadViewModel( + context: AppContext.shared, + authContext: authContext, + statusID: statusOnMyInstance.id + ) + coordinator?.present(scene: .thread(viewModel: threadViewModel), from: nil, transition: .show) + } + + case (_, _): + break + // do nothing + } + + } } extension SceneDelegate { @@ -298,3 +341,4 @@ extension SceneDelegate { } } } +