Implement URL scheme
This commit is contained in:
parent
f71c4964f5
commit
de962a0c09
|
@ -12,6 +12,7 @@ import CoreDataStack
|
|||
import MastodonCore
|
||||
import MastodonExtension
|
||||
import MastodonUI
|
||||
import MastodonSDK
|
||||
|
||||
#if PROFILE
|
||||
import FPSIndicator
|
||||
|
@ -67,6 +68,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
sceneCoordinator.setup()
|
||||
window.makeKeyAndVisible()
|
||||
|
||||
if let urlContext = connectionOptions.urlContexts.first {
|
||||
handleUrl(context: urlContext)
|
||||
}
|
||||
|
||||
#if SNAPSHOT
|
||||
// speedup animation
|
||||
// window.layer.speed = 999
|
||||
|
@ -187,21 +192,7 @@ extension SceneDelegate {
|
|||
coordinator.switchToTabBar(tab: .notifications)
|
||||
|
||||
case "org.joinmastodon.app.new-post":
|
||||
if coordinator?.tabBarController.topMost is ComposeViewController {
|
||||
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): composing…")
|
||||
} else {
|
||||
if let authContext = coordinator?.authContext {
|
||||
let composeViewModel = ComposeViewModel(
|
||||
context: AppContext.shared,
|
||||
authContext: authContext,
|
||||
destination: .topLevel
|
||||
)
|
||||
_ = coordinator?.present(scene: .compose(viewModel: composeViewModel), from: nil, transition: .modal(animated: true, completion: nil))
|
||||
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): present compose scene")
|
||||
} else {
|
||||
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): not authenticated")
|
||||
}
|
||||
}
|
||||
showComposeViewController()
|
||||
|
||||
case "org.joinmastodon.app.search":
|
||||
coordinator?.switchToTabBar(tab: .search)
|
||||
|
@ -219,4 +210,89 @@ extension SceneDelegate {
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
|
||||
// Determine who sent the URL.
|
||||
if let urlContext = URLContexts.first {
|
||||
handleUrl(context: urlContext)
|
||||
}
|
||||
}
|
||||
|
||||
private func showComposeViewController() {
|
||||
if coordinator?.tabBarController.topMost is ComposeViewController {
|
||||
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): composing…")
|
||||
} else {
|
||||
if let authContext = coordinator?.authContext {
|
||||
let composeViewModel = ComposeViewModel(
|
||||
context: AppContext.shared,
|
||||
authContext: authContext,
|
||||
destination: .topLevel
|
||||
)
|
||||
_ = coordinator?.present(scene: .compose(viewModel: composeViewModel), from: nil, transition: .modal(animated: true, completion: nil))
|
||||
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): present compose scene")
|
||||
} else {
|
||||
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): not authenticated")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func handleUrl(context: UIOpenURLContext) {
|
||||
let sendingAppID = context.options.sourceApplication
|
||||
let url = context.url
|
||||
|
||||
if !UIApplication.shared.canOpenURL(url) { return }
|
||||
|
||||
print("source application = \(sendingAppID ?? "Unknown")")
|
||||
print("url = \(url)")
|
||||
|
||||
if let username = url.user {
|
||||
guard let host = url.host else { return }
|
||||
let components = url.pathComponents
|
||||
if components.count == 3 && components[1] == "status" {
|
||||
let statusId = components[2]
|
||||
// View post from user
|
||||
print("view status \(statusId)")
|
||||
if let authContext = coordinator?.authContext {
|
||||
Task {
|
||||
guard let thread = try await AppContext.shared.apiService.fetchThread(
|
||||
statusID: statusId,
|
||||
domain: host,
|
||||
authenticationBox: authContext.mastodonAuthenticationBox
|
||||
) else { return }
|
||||
|
||||
let threadViewModel = CachedThreadViewModel(context: AppContext.shared,
|
||||
authContext: authContext,
|
||||
status: thread)
|
||||
coordinator?.present(scene: .thread(viewModel: threadViewModel), from: nil, transition: .show)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print("view profile \(username)@\(host)")
|
||||
if let authContext = coordinator?.authContext {
|
||||
Task { @MainActor in
|
||||
guard let user = try await AppContext.shared.apiService.fetchUser(
|
||||
username: username,
|
||||
domain: host,
|
||||
authenticationBox: authContext.mastodonAuthenticationBox
|
||||
) else { return }
|
||||
|
||||
let profileViewModel = RemoteProfileViewModel(context: AppContext.shared,
|
||||
authContext: authContext,
|
||||
userID: user.id)
|
||||
self.coordinator?.present(
|
||||
scene: .profile(viewModel: profileViewModel),
|
||||
from: nil,
|
||||
transition: .show
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
guard let action = url.host else { return }
|
||||
if action == "post" {
|
||||
print("make post")
|
||||
showComposeViewController()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
//
|
||||
|
||||
import os.log
|
||||
import CoreDataStack
|
||||
import Foundation
|
||||
import Combine
|
||||
import CommonOSLog
|
||||
|
@ -199,3 +200,43 @@ extension APIService {
|
|||
return response
|
||||
} // end func
|
||||
}
|
||||
|
||||
extension APIService {
|
||||
public func fetchUser(username: String, domain: String, authenticationBox: MastodonAuthenticationBox)
|
||||
async throws -> MastodonUser? {
|
||||
let query = Mastodon.API.Account.AccountLookupQuery(acct: "\(username)@\(domain)")
|
||||
let authorization = authenticationBox.userAuthorization
|
||||
|
||||
let response = try await Mastodon.API.Account.lookupAccount(
|
||||
session: session,
|
||||
domain: authenticationBox.domain,
|
||||
query: query,
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
// user
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
try await managedObjectContext.performChanges {
|
||||
_ = Persistence.MastodonUser.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.MastodonUser.PersistContext(
|
||||
domain: domain,
|
||||
entity: response.value,
|
||||
cache: nil,
|
||||
networkDate: response.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
var result: MastodonUser?
|
||||
try await managedObjectContext.perform {
|
||||
result = Persistence.MastodonUser.fetch(in: managedObjectContext,
|
||||
context: Persistence.MastodonUser.PersistContext(
|
||||
domain: domain,
|
||||
entity: response.value,
|
||||
cache: nil,
|
||||
networkDate: response.networkDate
|
||||
))
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,10 @@ extension APIService {
|
|||
|
||||
public func statusContext(
|
||||
statusID: Mastodon.Entity.Status.ID,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
authenticationBox: MastodonAuthenticationBox,
|
||||
domain: String? = nil
|
||||
) async throws -> Mastodon.Response.Content<Mastodon.Entity.Context> {
|
||||
let domain = authenticationBox.domain
|
||||
let domain = domain ?? authenticationBox.domain
|
||||
let authorization = authenticationBox.userAuthorization
|
||||
|
||||
let response = try await Mastodon.API.Statuses.statusContext(
|
||||
|
@ -51,4 +52,79 @@ extension APIService {
|
|||
return response
|
||||
} // end func
|
||||
|
||||
public func fetchThread(
|
||||
statusID: Mastodon.Entity.Status.ID,
|
||||
domain: String,
|
||||
authenticationBox: MastodonAuthenticationBox
|
||||
) async throws -> Status? {
|
||||
let authorization = authenticationBox.userAuthorization
|
||||
let managedObjectContext = self.backgroundManagedObjectContext
|
||||
|
||||
let responseOne = try await Mastodon.API.Statuses.status(
|
||||
session: session,
|
||||
domain: domain,
|
||||
statusID: statusID,
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
try await managedObjectContext.performChanges {
|
||||
let me = authenticationBox.authenticationRecord.object(in: managedObjectContext)?.user
|
||||
_ = Persistence.Status.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: domain,
|
||||
entity: responseOne.value,
|
||||
me: me,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: responseOne.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
let responseTwo = try await Mastodon.API.Statuses.statusContext(
|
||||
session: session,
|
||||
domain: domain,
|
||||
statusID: statusID,
|
||||
authorization: authorization
|
||||
).singleOutput()
|
||||
|
||||
try await managedObjectContext.performChanges {
|
||||
let me = authenticationBox.authenticationRecord.object(in: managedObjectContext)?.user
|
||||
let value = responseTwo.value.ancestors + responseTwo.value.descendants
|
||||
|
||||
for entity in value {
|
||||
_ = Persistence.Status.createOrMerge(
|
||||
in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: domain,
|
||||
entity: entity,
|
||||
me: me,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: responseTwo.networkDate
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
var result: Status?
|
||||
try await managedObjectContext.perform {
|
||||
let me = authenticationBox.authenticationRecord.object(in: managedObjectContext)?.user
|
||||
|
||||
if let status = Persistence.Status.fetch(in: managedObjectContext,
|
||||
context: Persistence.Status.PersistContext(
|
||||
domain: domain,
|
||||
entity: responseOne.value,
|
||||
me: me,
|
||||
statusCache: nil,
|
||||
userCache: nil,
|
||||
networkDate: responseOne.networkDate
|
||||
)) {
|
||||
result = status
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue