metatext-app-ios-iphone-ipad/ServiceLayer/Sources/ServiceLayer/Services/NavigationService.swift

222 lines
7.7 KiB
Swift
Raw Normal View History

2020-09-15 03:39:35 +02:00
// Copyright © 2020 Metabolist. All rights reserved.
import Combine
import DB
import Foundation
import Mastodon
import MastodonAPI
2020-09-25 07:39:06 +02:00
public enum Navigation {
2020-09-15 03:39:35 +02:00
case url(URL)
2020-10-05 08:36:22 +02:00
case collection(CollectionService)
2020-09-27 04:03:53 +02:00
case profile(ProfileService)
2021-02-05 03:56:14 +01:00
case notification(NotificationService)
2021-01-25 08:42:39 +01:00
case searchScope(SearchScope)
2020-09-26 08:37:30 +02:00
case webfingerStart
case webfingerEnd
2020-09-15 03:39:35 +02:00
}
2020-09-25 07:39:06 +02:00
public struct NavigationService {
private let environment: AppEnvironment
2020-09-15 03:39:35 +02:00
private let mastodonAPIClient: MastodonAPIClient
private let contentDatabase: ContentDatabase
2020-10-05 21:58:03 +02:00
private let status: Status?
2020-09-15 03:39:35 +02:00
init(environment: AppEnvironment,
mastodonAPIClient: MastodonAPIClient,
contentDatabase: ContentDatabase,
status: Status? = nil) {
self.environment = environment
2020-09-15 03:39:35 +02:00
self.mastodonAPIClient = mastodonAPIClient
self.contentDatabase = contentDatabase
2020-10-05 21:58:03 +02:00
self.status = status
2020-09-15 03:39:35 +02:00
}
}
2020-09-25 07:39:06 +02:00
public extension NavigationService {
func item(url: URL) -> AnyPublisher<Navigation, Never> {
2020-09-15 03:39:35 +02:00
if let tag = tag(url: url) {
2020-09-25 07:39:06 +02:00
return Just(
2020-10-05 08:36:22 +02:00
.collection(
2020-10-05 09:04:15 +02:00
TimelineService(
2020-09-25 07:39:06 +02:00
timeline: .tag(tag),
environment: environment,
2020-09-25 07:39:06 +02:00
mastodonAPIClient: mastodonAPIClient,
contentDatabase: contentDatabase)))
.eraseToAnyPublisher()
2020-10-06 00:50:05 +02:00
} else if let accountId = accountId(url: url) {
return Just(.profile(profileService(id: accountId))).eraseToAnyPublisher()
} else if mastodonAPIClient.instanceURL.host == url.host, let statusId = url.statusId {
return Just(.collection(contextService(id: statusId))).eraseToAnyPublisher()
2020-09-15 03:39:35 +02:00
}
2020-09-26 08:37:30 +02:00
if url.shouldWebfinger {
return webfinger(url: url)
} else {
return Just(.url(url)).eraseToAnyPublisher()
}
2020-09-15 03:39:35 +02:00
}
2020-09-25 07:39:06 +02:00
2020-10-06 00:50:05 +02:00
func contextService(id: Status.Id) -> ContextService {
ContextService(id: id, environment: environment,
mastodonAPIClient: mastodonAPIClient,
contentDatabase: contentDatabase)
2020-09-25 07:39:06 +02:00
}
2020-10-06 00:50:05 +02:00
func profileService(id: Account.Id) -> ProfileService {
ProfileService(id: id,
environment: environment,
mastodonAPIClient: mastodonAPIClient,
contentDatabase: contentDatabase)
2020-09-25 07:39:06 +02:00
}
2021-02-08 06:24:06 +01:00
func profileService(account: Account, relationship: Relationship? = nil) -> ProfileService {
ProfileService(account: account,
relationship: relationship,
environment: environment,
2021-02-08 06:24:06 +01:00
mastodonAPIClient: mastodonAPIClient,
contentDatabase: contentDatabase)
}
2020-09-25 07:39:06 +02:00
func statusService(status: Status) -> StatusService {
StatusService(environment: environment,
status: status,
mastodonAPIClient: mastodonAPIClient,
contentDatabase: contentDatabase)
2020-09-25 07:39:06 +02:00
}
func accountService(account: Account) -> AccountService {
AccountService(account: account,
environment: environment,
mastodonAPIClient: mastodonAPIClient,
contentDatabase: contentDatabase)
2020-09-25 07:39:06 +02:00
}
2020-10-04 10:39:54 +02:00
func loadMoreService(loadMore: LoadMore) -> LoadMoreService {
LoadMoreService(loadMore: loadMore, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
2020-10-30 08:11:24 +01:00
}
func notificationService(notification: MastodonNotification) -> NotificationService {
NotificationService(
notification: notification,
environment: environment,
2020-10-30 08:11:24 +01:00
mastodonAPIClient: mastodonAPIClient,
contentDatabase: contentDatabase)
2020-10-29 07:03:45 +01:00
}
func conversationService(conversation: Conversation) -> ConversationService {
ConversationService(
conversation: conversation,
environment: environment,
2020-10-29 07:03:45 +01:00
mastodonAPIClient: mastodonAPIClient,
contentDatabase: contentDatabase)
2021-01-24 04:12:30 +01:00
}
2021-04-25 21:38:36 +02:00
func announcementService(announcement: Announcement) -> AnnouncementService {
AnnouncementService(announcement: announcement,
environment: environment,
mastodonAPIClient: mastodonAPIClient,
contentDatabase: contentDatabase)
}
2021-01-24 04:12:30 +01:00
func timelineService(timeline: Timeline) -> TimelineService {
TimelineService(timeline: timeline,
environment: environment,
mastodonAPIClient: mastodonAPIClient,
contentDatabase: contentDatabase)
2020-10-04 10:39:54 +02:00
}
2020-09-15 03:39:35 +02:00
}
2020-09-25 07:39:06 +02:00
private extension NavigationService {
2020-09-15 03:39:35 +02:00
func tag(url: URL) -> String? {
2021-03-29 08:04:14 +02:00
if status?.tags.first(where: { $0.url.url.path.lowercased() == url.path.lowercased() }) != nil {
2020-09-15 03:39:35 +02:00
return url.lastPathComponent
} else if
mastodonAPIClient.instanceURL.host == url.host {
return url.tag
}
return nil
}
2020-10-06 00:50:05 +02:00
func accountId(url: URL) -> String? {
2021-03-29 08:04:14 +02:00
if let mentionId = status?.mentions.first(where: {
$0.url.url.path.lowercased() == url.path.lowercased()
})?.id {
2020-10-06 00:50:05 +02:00
return mentionId
2020-09-15 03:39:35 +02:00
} else if
mastodonAPIClient.instanceURL.host == url.host {
2020-10-06 00:50:05 +02:00
return url.accountId
2020-09-15 03:39:35 +02:00
}
return nil
}
2020-09-26 08:37:30 +02:00
func webfinger(url: URL) -> AnyPublisher<Navigation, Never> {
let navigationSubject = PassthroughSubject<Navigation, Never>()
2021-01-23 04:48:33 +01:00
let request = mastodonAPIClient.request(ResultsEndpoint.search(.init(query: url.absoluteString, resolve: true)))
2020-09-26 08:37:30 +02:00
.handleEvents(
receiveSubscription: { _ in navigationSubject.send(.webfingerStart) },
receiveCompletion: { _ in navigationSubject.send(.webfingerEnd) })
.map { results -> Navigation in
if let tag = results.hashtags.first {
2020-10-05 08:36:22 +02:00
return .collection(
2020-10-05 09:04:15 +02:00
TimelineService(
2020-09-26 08:37:30 +02:00
timeline: .tag(tag.name),
environment: environment,
2020-09-26 08:37:30 +02:00
mastodonAPIClient: mastodonAPIClient,
contentDatabase: contentDatabase))
} else if let account = results.accounts.first {
2020-09-27 04:03:53 +02:00
return .profile(profileService(account: account))
2020-09-26 08:37:30 +02:00
} else if let status = results.statuses.first {
2020-10-05 09:04:15 +02:00
return .collection(contextService(id: status.id))
2020-09-26 08:37:30 +02:00
} else {
return .url(url)
}
}
.replaceError(with: .url(url))
return navigationSubject.merge(with: request).eraseToAnyPublisher()
}
2020-09-15 03:39:35 +02:00
}
private extension URL {
var isAccountURL: Bool {
(pathComponents.count == 2 && pathComponents[1].starts(with: "@"))
|| (pathComponents.count == 3 && pathComponents[0...1] == ["/", "users"])
}
2020-10-06 00:50:05 +02:00
var accountId: Account.Id? {
if let accountId = pathComponents.last, pathComponents == ["/", "web", "accounts", accountId] {
return accountId
2020-09-15 03:39:35 +02:00
}
return nil
}
2020-10-06 00:50:05 +02:00
var statusId: Status.Id? {
guard let statusId = pathComponents.last else { return nil }
2020-09-15 03:39:35 +02:00
if pathComponents.count == 3, pathComponents[1].starts(with: "@") {
2020-10-06 00:50:05 +02:00
return statusId
} else if pathComponents == ["/", "web", "statuses", statusId] {
return statusId
2020-09-15 03:39:35 +02:00
}
return nil
}
var tag: String? {
if let tag = pathComponents.last, pathComponents == ["/", "tags", tag] {
return tag
}
return nil
}
var shouldWebfinger: Bool {
2020-10-06 00:50:05 +02:00
isAccountURL || accountId != nil || statusId != nil || tag != nil
2020-09-15 03:39:35 +02:00
}
}