2023-02-27 05:39:07 +00:00
|
|
|
import Combine
|
2022-11-29 10:46:02 +00:00
|
|
|
import Foundation
|
2022-12-17 12:37:46 +00:00
|
|
|
import Models
|
2022-12-27 07:31:47 +00:00
|
|
|
import Network
|
2023-09-18 05:01:23 +00:00
|
|
|
import Observation
|
2023-01-17 10:36:01 +00:00
|
|
|
import SwiftUI
|
2022-11-29 10:46:02 +00:00
|
|
|
|
2023-02-21 17:52:30 +00:00
|
|
|
public enum RouterDestination: Hashable {
|
2022-11-29 10:46:02 +00:00
|
|
|
case accountDetail(id: String)
|
2022-12-17 12:37:46 +00:00
|
|
|
case accountDetailWithAccount(account: Account)
|
2023-01-25 20:18:34 +00:00
|
|
|
case accountSettingsWithAccount(account: Account, appAccount: AppAccount)
|
2022-11-29 10:46:02 +00:00
|
|
|
case statusDetail(id: String)
|
2023-02-10 17:21:05 +00:00
|
|
|
case statusDetailWithStatus(status: Status)
|
2023-01-06 11:14:05 +00:00
|
|
|
case remoteStatusDetail(url: URL)
|
2023-02-10 17:21:05 +00:00
|
|
|
case conversationDetail(conversation: Conversation)
|
2022-12-21 19:26:38 +00:00
|
|
|
case hashTag(tag: String, account: String?)
|
2023-01-02 18:23:44 +00:00
|
|
|
case list(list: Models.List)
|
2022-12-23 17:47:19 +00:00
|
|
|
case followers(id: String)
|
|
|
|
case following(id: String)
|
2023-01-24 05:35:43 +00:00
|
|
|
case favoritedBy(id: String)
|
2022-12-24 12:41:25 +00:00
|
|
|
case rebloggedBy(id: String)
|
2023-02-13 06:00:42 +00:00
|
|
|
case accountsList(accounts: [Account])
|
2023-03-02 19:15:07 +00:00
|
|
|
case trendingTimeline
|
2023-09-27 06:38:17 +00:00
|
|
|
case trendingLinks(cards: [Card])
|
2023-03-02 19:15:07 +00:00
|
|
|
case tagsList(tags: [Tag])
|
2022-11-29 10:46:02 +00:00
|
|
|
}
|
|
|
|
|
2023-11-14 18:48:14 +00:00
|
|
|
public enum WindowDestinationEditor: Hashable, Codable {
|
2023-10-23 17:12:25 +00:00
|
|
|
case newStatusEditor(visibility: Models.Visibility)
|
2023-10-26 04:23:00 +00:00
|
|
|
case editStatusEditor(status: Status)
|
|
|
|
case replyToStatusEditor(status: Status)
|
|
|
|
case quoteStatusEditor(status: Status)
|
2024-01-03 12:40:53 +00:00
|
|
|
case mentionStatusEditor(account: Account, visibility: Models.Visibility)
|
2024-02-10 10:26:22 +00:00
|
|
|
case quoteLinkStatusEditor(link: URL)
|
2023-10-23 17:12:25 +00:00
|
|
|
}
|
|
|
|
|
2023-11-14 18:48:14 +00:00
|
|
|
public enum WindowDestinationMedia: Hashable, Codable {
|
|
|
|
case mediaViewer(attachments: [MediaAttachment], selectedAttachment: MediaAttachment)
|
|
|
|
}
|
|
|
|
|
2023-02-21 17:52:30 +00:00
|
|
|
public enum SheetDestination: Identifiable {
|
2023-01-05 11:21:54 +00:00
|
|
|
case newStatusEditor(visibility: Models.Visibility)
|
2022-12-27 12:38:10 +00:00
|
|
|
case editStatusEditor(status: Status)
|
|
|
|
case replyToStatusEditor(status: Status)
|
|
|
|
case quoteStatusEditor(status: Status)
|
2024-02-10 10:26:22 +00:00
|
|
|
case quoteLinkStatusEditor(link: URL)
|
2023-01-04 17:37:58 +00:00
|
|
|
case mentionStatusEditor(account: Account, visibility: Models.Visibility)
|
2023-11-28 08:18:52 +00:00
|
|
|
case listCreate
|
2023-01-02 18:23:44 +00:00
|
|
|
case listEdit(list: Models.List)
|
|
|
|
case listAddAccount(account: Account)
|
2023-01-06 11:14:05 +00:00
|
|
|
case addAccount
|
2023-01-06 16:14:34 +00:00
|
|
|
case addRemoteLocalTimeline
|
2023-07-19 05:46:25 +00:00
|
|
|
case addTagGroup
|
2023-01-19 20:19:19 +00:00
|
|
|
case statusEditHistory(status: String)
|
2023-01-26 06:34:29 +00:00
|
|
|
case settings
|
2023-12-28 15:03:16 +00:00
|
|
|
case about
|
|
|
|
case support
|
2023-01-27 11:18:17 +00:00
|
|
|
case accountPushNotficationsSettings
|
2023-02-13 20:12:18 +00:00
|
|
|
case report(status: Status)
|
2023-02-19 14:29:07 +00:00
|
|
|
case shareImage(image: UIImage, status: Status)
|
2023-08-04 10:40:21 +00:00
|
|
|
case editTagGroup(tagGroup: TagGroup, onSaved: ((TagGroup) -> Void)?)
|
2024-01-11 17:55:35 +00:00
|
|
|
case timelineContentFilter
|
2023-01-17 10:36:01 +00:00
|
|
|
|
2022-12-20 08:37:07 +00:00
|
|
|
public var id: String {
|
|
|
|
switch self {
|
2023-01-27 11:18:17 +00:00
|
|
|
case .editStatusEditor, .newStatusEditor, .replyToStatusEditor, .quoteStatusEditor,
|
2024-02-14 11:48:14 +00:00
|
|
|
.mentionStatusEditor, .quoteLinkStatusEditor:
|
2023-09-16 12:15:03 +00:00
|
|
|
"statusEditor"
|
2023-11-28 08:18:52 +00:00
|
|
|
case .listCreate:
|
|
|
|
"listCreate"
|
2023-01-02 18:23:44 +00:00
|
|
|
case .listEdit:
|
2023-09-16 12:15:03 +00:00
|
|
|
"listEdit"
|
2023-01-02 18:23:44 +00:00
|
|
|
case .listAddAccount:
|
2023-09-16 12:15:03 +00:00
|
|
|
"listAddAccount"
|
2023-01-06 11:14:05 +00:00
|
|
|
case .addAccount:
|
2023-09-16 12:15:03 +00:00
|
|
|
"addAccount"
|
2023-07-19 05:44:35 +00:00
|
|
|
case .addTagGroup:
|
2023-09-16 12:15:03 +00:00
|
|
|
"addTagGroup"
|
2023-01-06 16:14:34 +00:00
|
|
|
case .addRemoteLocalTimeline:
|
2023-09-16 12:15:03 +00:00
|
|
|
"addRemoteLocalTimeline"
|
2023-01-19 20:19:19 +00:00
|
|
|
case .statusEditHistory:
|
2023-09-16 12:15:03 +00:00
|
|
|
"statusEditHistory"
|
2023-02-13 20:12:18 +00:00
|
|
|
case .report:
|
2023-09-16 12:15:03 +00:00
|
|
|
"report"
|
2023-02-19 14:29:07 +00:00
|
|
|
case .shareImage:
|
2023-09-16 12:15:03 +00:00
|
|
|
"shareImage"
|
2023-08-04 10:40:21 +00:00
|
|
|
case .editTagGroup:
|
2023-09-16 12:15:03 +00:00
|
|
|
"editTagGroup"
|
2023-12-28 15:03:16 +00:00
|
|
|
case .settings, .support, .about, .accountPushNotficationsSettings:
|
|
|
|
"settings"
|
2024-01-11 17:55:35 +00:00
|
|
|
case .timelineContentFilter:
|
|
|
|
"timelineContentFilter"
|
2022-12-20 08:37:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-30 15:55:13 +00:00
|
|
|
@MainActor
|
2023-09-18 05:01:23 +00:00
|
|
|
@Observable public class RouterPath {
|
2022-12-27 07:31:47 +00:00
|
|
|
public var client: Client?
|
2023-01-08 18:56:16 +00:00
|
|
|
public var urlHandler: ((URL) -> OpenURLAction.Result)?
|
2023-01-17 10:36:01 +00:00
|
|
|
|
2023-09-18 05:01:23 +00:00
|
|
|
public var path: [RouterDestination] = []
|
|
|
|
public var presentedSheet: SheetDestination?
|
2023-01-17 10:36:01 +00:00
|
|
|
|
2022-11-29 10:46:02 +00:00
|
|
|
public init() {}
|
2023-01-17 10:36:01 +00:00
|
|
|
|
2023-02-21 17:52:30 +00:00
|
|
|
public func navigate(to: RouterDestination) {
|
2022-11-29 10:46:02 +00:00
|
|
|
path.append(to)
|
|
|
|
}
|
2023-01-17 10:36:01 +00:00
|
|
|
|
2022-12-19 14:51:25 +00:00
|
|
|
public func handleStatus(status: AnyStatus, url: URL) -> OpenURLAction.Result {
|
2023-09-16 12:15:03 +00:00
|
|
|
if url.pathComponents.count == 3, url.pathComponents[1] == "tags",
|
|
|
|
url.host() == status.account.url?.host(),
|
|
|
|
let tag = url.pathComponents.last
|
2023-01-17 10:36:01 +00:00
|
|
|
{
|
2023-01-25 05:14:55 +00:00
|
|
|
// OK this test looks weird but it's
|
|
|
|
// A 3 component path i.e. ["/", "tags", "tagname"]
|
|
|
|
// That is on the same host as the person that posted the tag,
|
|
|
|
// i.e. not a link that matches the pattern but elsewhere on the internet
|
|
|
|
// In those circumstances, hijack the link and goto the tags page instead
|
2022-12-21 19:26:38 +00:00
|
|
|
navigate(to: .hashTag(tag: tag, account: nil))
|
2022-12-20 14:37:51 +00:00
|
|
|
return .handled
|
|
|
|
} else if let mention = status.mentions.first(where: { $0.url == url }) {
|
2022-12-19 14:51:25 +00:00
|
|
|
navigate(to: .accountDetail(id: mention.id))
|
|
|
|
return .handled
|
2023-09-16 12:15:03 +00:00
|
|
|
} else if let client,
|
2023-01-12 17:25:37 +00:00
|
|
|
client.isAuth,
|
|
|
|
client.hasConnection(with: url),
|
2023-01-17 10:36:01 +00:00
|
|
|
let id = Int(url.lastPathComponent)
|
|
|
|
{
|
2023-03-01 05:46:55 +00:00
|
|
|
if !StatusEmbedCache.shared.badStatusesURLs.contains(url) {
|
|
|
|
if url.absoluteString.contains(client.server) {
|
|
|
|
navigate(to: .statusDetail(id: String(id)))
|
|
|
|
} else {
|
|
|
|
navigate(to: .remoteStatusDetail(url: url))
|
|
|
|
}
|
|
|
|
return .handled
|
2022-12-30 15:55:13 +00:00
|
|
|
}
|
2022-12-19 14:51:25 +00:00
|
|
|
}
|
2023-01-08 18:56:16 +00:00
|
|
|
return urlHandler?(url) ?? .systemAction
|
2022-12-19 14:51:25 +00:00
|
|
|
}
|
2023-01-17 10:36:01 +00:00
|
|
|
|
2022-12-23 14:28:22 +00:00
|
|
|
public func handle(url: URL) -> OpenURLAction.Result {
|
|
|
|
if url.pathComponents.contains(where: { $0 == "tags" }),
|
2023-01-17 10:36:01 +00:00
|
|
|
let tag = url.pathComponents.last
|
|
|
|
{
|
2022-12-23 14:28:22 +00:00
|
|
|
navigate(to: .hashTag(tag: tag, account: nil))
|
|
|
|
return .handled
|
2024-01-01 08:23:06 +00:00
|
|
|
} else if url.lastPathComponent.first == "@",
|
2024-02-14 11:48:14 +00:00
|
|
|
let host = url.host,
|
|
|
|
!host.hasPrefix("www")
|
|
|
|
{
|
2023-01-01 10:40:18 +00:00
|
|
|
let acct = "\(url.lastPathComponent)@\(host)"
|
|
|
|
Task {
|
|
|
|
await navigateToAccountFrom(acct: acct, url: url)
|
|
|
|
}
|
|
|
|
return .handled
|
2023-09-16 12:15:03 +00:00
|
|
|
} else if let client,
|
2023-01-27 19:35:16 +00:00
|
|
|
client.isAuth,
|
|
|
|
client.hasConnection(with: url),
|
2023-01-27 19:36:40 +00:00
|
|
|
let id = Int(url.lastPathComponent)
|
|
|
|
{
|
2023-01-27 19:35:16 +00:00
|
|
|
if url.absoluteString.contains(client.server) {
|
|
|
|
navigate(to: .statusDetail(id: String(id)))
|
|
|
|
} else {
|
|
|
|
navigate(to: .remoteStatusDetail(url: url))
|
|
|
|
}
|
|
|
|
return .handled
|
2022-12-23 14:28:22 +00:00
|
|
|
}
|
2023-01-08 18:56:16 +00:00
|
|
|
return urlHandler?(url) ?? .systemAction
|
2022-12-23 14:28:22 +00:00
|
|
|
}
|
2023-01-17 10:36:01 +00:00
|
|
|
|
2023-01-06 11:14:05 +00:00
|
|
|
public func navigateToAccountFrom(acct: String, url: URL) async {
|
2023-01-01 08:19:00 +00:00
|
|
|
guard let client else { return }
|
2023-01-29 10:17:43 +00:00
|
|
|
let results: SearchResults? = try? await client.get(endpoint: Search.search(query: acct,
|
|
|
|
type: "accounts",
|
|
|
|
offset: nil,
|
|
|
|
following: nil),
|
|
|
|
forceVersion: .v2)
|
|
|
|
if let account = results?.accounts.first {
|
|
|
|
navigate(to: .accountDetailWithAccount(account: account))
|
|
|
|
} else {
|
2023-02-27 05:39:07 +00:00
|
|
|
_ = await UIApplication.shared.open(url)
|
2023-01-01 08:19:00 +00:00
|
|
|
}
|
|
|
|
}
|
2023-01-17 10:36:01 +00:00
|
|
|
|
2023-01-06 11:14:05 +00:00
|
|
|
public func navigateToAccountFrom(url: URL) async {
|
2023-01-01 08:19:00 +00:00
|
|
|
guard let client else { return }
|
2023-01-29 10:17:43 +00:00
|
|
|
let results: SearchResults? = try? await client.get(endpoint: Search.search(query: url.absoluteString,
|
|
|
|
type: "accounts",
|
|
|
|
offset: nil,
|
|
|
|
following: nil),
|
|
|
|
forceVersion: .v2)
|
|
|
|
if let account = results?.accounts.first {
|
|
|
|
navigate(to: .accountDetailWithAccount(account: account))
|
|
|
|
} else {
|
2023-02-27 05:39:07 +00:00
|
|
|
_ = await UIApplication.shared.open(url)
|
2023-01-01 08:19:00 +00:00
|
|
|
}
|
|
|
|
}
|
2022-11-29 10:46:02 +00:00
|
|
|
}
|