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

218 lines
8.5 KiB
Swift
Raw Normal View History

2020-08-07 23:57:18 +02:00
// Copyright © 2020 Metabolist. All rights reserved.
2020-09-05 04:31:43 +02:00
import Combine
2020-09-03 05:28:34 +02:00
import DB
2020-08-07 23:57:18 +02:00
import Foundation
2020-08-31 01:33:11 +02:00
import Mastodon
import MastodonAPI
2020-09-04 02:54:05 +02:00
import Secrets
2020-08-07 23:57:18 +02:00
2020-08-31 12:21:01 +02:00
public class IdentityService {
@Published public private(set) var identity: Identity
public let observationErrors: AnyPublisher<Error, Never>
2020-08-07 23:57:18 +02:00
private let identityDatabase: IdentityDatabase
2020-08-18 07:13:37 +02:00
private let contentDatabase: ContentDatabase
2020-08-09 07:37:04 +02:00
private let environment: AppEnvironment
private let mastodonAPIClient: MastodonAPIClient
2020-09-04 02:54:05 +02:00
private let secrets: Secrets
2020-08-07 23:57:18 +02:00
private let observationErrorsInput = PassthroughSubject<Error, Never>()
init(identityID: UUID,
identityDatabase: IdentityDatabase,
environment: AppEnvironment) throws {
self.identityDatabase = identityDatabase
2020-08-09 07:37:04 +02:00
self.environment = environment
2020-08-07 23:57:18 +02:00
observationErrors = observationErrorsInput.eraseToAnyPublisher()
let observation = identityDatabase.identityObservation(id: identityID).share()
2020-08-07 23:57:18 +02:00
var initialIdentity: Identity?
2020-08-09 10:04:43 +02:00
_ = observation.first().sink(
2020-08-07 23:57:18 +02:00
receiveCompletion: { _ in },
receiveValue: { initialIdentity = $0 })
guard let identity = initialIdentity else { throw IdentityDatabaseError.identityNotFound }
self.identity = identity
2020-09-04 02:54:05 +02:00
secrets = Secrets(
2020-08-09 10:04:43 +02:00
identityID: identityID,
2020-09-04 02:54:05 +02:00
keychain: environment.keychain)
mastodonAPIClient = MastodonAPIClient(session: environment.session)
mastodonAPIClient.instanceURL = identity.url
2020-09-04 08:44:04 +02:00
mastodonAPIClient.accessToken = try? secrets.getAccessToken()
2020-08-07 23:57:18 +02:00
2020-09-04 08:12:06 +02:00
contentDatabase = try ContentDatabase(identityID: identityID,
inMemory: environment.inMemoryContent,
keychain: environment.keychain)
2020-08-18 07:13:37 +02:00
2020-08-07 23:57:18 +02:00
observation.catch { [weak self] error -> Empty<Identity, Never> in
self?.observationErrorsInput.send(error)
return Empty()
}
.assign(to: &$identity)
}
}
2020-08-31 12:21:01 +02:00
public extension IdentityService {
var isAuthorized: Bool { mastodonAPIClient.accessToken != nil }
2020-08-07 23:57:18 +02:00
2020-08-26 11:19:38 +02:00
func updateLastUse() -> AnyPublisher<Never, Error> {
identityDatabase.updateLastUsedAt(identityID: identity.id)
2020-08-09 07:37:04 +02:00
}
2020-08-26 11:19:38 +02:00
func verifyCredentials() -> AnyPublisher<Never, Error> {
mastodonAPIClient.request(AccountEndpoint.verifyCredentials)
2020-08-12 10:45:01 +02:00
.zip(Just(identity.id).first().setFailureType(to: Error.self))
.flatMap(identityDatabase.updateAccount)
2020-08-07 23:57:18 +02:00
.eraseToAnyPublisher()
}
2020-08-26 11:19:38 +02:00
func refreshServerPreferences() -> AnyPublisher<Never, Error> {
mastodonAPIClient.request(PreferencesEndpoint.preferences)
2020-08-12 10:45:01 +02:00
.zip(Just(self).first().setFailureType(to: Error.self))
2020-08-07 23:57:18 +02:00
.map { ($1.identity.preferences.updated(from: $0), $1.identity.id) }
.flatMap(identityDatabase.updatePreferences)
2020-08-07 23:57:18 +02:00
.eraseToAnyPublisher()
}
2020-08-26 11:19:38 +02:00
func refreshInstance() -> AnyPublisher<Never, Error> {
mastodonAPIClient.request(InstanceEndpoint.instance)
2020-08-12 10:45:01 +02:00
.zip(Just(identity.id).first().setFailureType(to: Error.self))
.flatMap(identityDatabase.updateInstance)
2020-08-07 23:57:18 +02:00
.eraseToAnyPublisher()
}
func identitiesObservation() -> AnyPublisher<[Identity], Error> {
identityDatabase.identitiesObservation()
2020-08-07 23:57:18 +02:00
}
func recentIdentitiesObservation() -> AnyPublisher<[Identity], Error> {
identityDatabase.recentIdentitiesObservation(excluding: identity.id)
2020-08-07 23:57:18 +02:00
}
2020-08-29 05:50:58 +02:00
func refreshLists() -> AnyPublisher<Never, Error> {
mastodonAPIClient.request(ListsEndpoint.lists)
2020-08-29 12:26:26 +02:00
.flatMap(contentDatabase.setLists(_:))
2020-08-29 05:50:58 +02:00
.eraseToAnyPublisher()
}
func createList(title: String) -> AnyPublisher<Never, Error> {
mastodonAPIClient.request(ListEndpoint.create(title: title))
2020-08-29 05:50:58 +02:00
.flatMap(contentDatabase.createList(_:))
.eraseToAnyPublisher()
}
func deleteList(id: String) -> AnyPublisher<Never, Error> {
mastodonAPIClient.request(DeletionEndpoint.list(id: id))
2020-08-29 05:50:58 +02:00
.map { _ in id }
.flatMap(contentDatabase.deleteList(id:))
.eraseToAnyPublisher()
}
2020-08-29 12:26:26 +02:00
func listsObservation() -> AnyPublisher<[Timeline], Error> {
contentDatabase.listsObservation()
}
func refreshFilters() -> AnyPublisher<Never, Error> {
mastodonAPIClient.request(FiltersEndpoint.filters)
2020-08-29 12:26:26 +02:00
.flatMap(contentDatabase.setFilters(_:))
.eraseToAnyPublisher()
}
func createFilter(_ filter: Filter) -> AnyPublisher<Never, Error> {
mastodonAPIClient.request(FilterEndpoint.create(phrase: filter.phrase,
2020-08-29 12:26:26 +02:00
context: filter.context,
irreversible: filter.irreversible,
wholeWord: filter.wholeWord,
expiresIn: filter.expiresAt))
.flatMap(contentDatabase.createFilter(_:))
.eraseToAnyPublisher()
}
func updateFilter(_ filter: Filter) -> AnyPublisher<Never, Error> {
mastodonAPIClient.request(FilterEndpoint.update(id: filter.id,
2020-08-29 12:26:26 +02:00
phrase: filter.phrase,
context: filter.context,
irreversible: filter.irreversible,
wholeWord: filter.wholeWord,
expiresIn: filter.expiresAt))
.flatMap(contentDatabase.createFilter(_:))
.eraseToAnyPublisher()
}
func deleteFilter(id: String) -> AnyPublisher<Never, Error> {
mastodonAPIClient.request(DeletionEndpoint.filter(id: id))
2020-08-29 12:26:26 +02:00
.map { _ in id }
.flatMap(contentDatabase.deleteFilter(id:))
.eraseToAnyPublisher()
}
2020-08-30 02:32:34 +02:00
func activeFiltersObservation(date: Date) -> AnyPublisher<[Filter], Error> {
contentDatabase.activeFiltersObservation(date: date)
}
func expiredFiltersObservation(date: Date) -> AnyPublisher<[Filter], Error> {
contentDatabase.expiredFiltersObservation(date: date)
2020-08-29 12:26:26 +02:00
}
2020-08-26 11:19:38 +02:00
func updatePreferences(_ preferences: Identity.Preferences) -> AnyPublisher<Never, Error> {
identityDatabase.updatePreferences(preferences, forIdentityID: identity.id)
2020-08-30 09:16:37 +02:00
.collect()
2020-08-15 02:14:21 +02:00
.zip(Just(self).first().setFailureType(to: Error.self))
.filter { $1.identity.preferences.useServerPostingReadingPreferences }
.map { _ in () }
.flatMap(refreshServerPreferences)
.eraseToAnyPublisher()
2020-08-07 23:57:18 +02:00
}
2020-08-14 03:24:53 +02:00
2020-08-26 11:19:38 +02:00
func createPushSubscription(deviceToken: String, alerts: PushSubscription.Alerts) -> AnyPublisher<Never, Error> {
2020-08-14 03:24:53 +02:00
let publicKey: String
let auth: String
do {
2020-09-04 02:54:05 +02:00
publicKey = try secrets.generatePushKeyAndReturnPublicKey().base64EncodedString()
auth = try secrets.generatePushAuth().base64EncodedString()
2020-08-14 03:24:53 +02:00
} catch {
return Fail(error: error).eraseToAnyPublisher()
}
let identityID = identity.id
let endpoint = Self.pushSubscriptionEndpointURL
.appendingPathComponent(deviceToken)
.appendingPathComponent(identityID.uuidString)
return mastodonAPIClient.request(
2020-08-14 03:24:53 +02:00
PushSubscriptionEndpoint.create(
endpoint: endpoint,
publicKey: publicKey,
auth: auth,
alerts: alerts))
2020-08-14 23:41:55 +02:00
.map { ($0.alerts, deviceToken, identityID) }
.flatMap(identityDatabase.updatePushSubscription(alerts:deviceToken:forIdentityID:))
.eraseToAnyPublisher()
}
2020-08-26 11:19:38 +02:00
func updatePushSubscription(alerts: PushSubscription.Alerts) -> AnyPublisher<Never, Error> {
2020-08-14 23:41:55 +02:00
let identityID = identity.id
return mastodonAPIClient.request(PushSubscriptionEndpoint.update(alerts: alerts))
2020-08-14 23:41:55 +02:00
.map { ($0.alerts, nil, identityID) }
.flatMap(identityDatabase.updatePushSubscription(alerts:deviceToken:forIdentityID:))
2020-08-14 03:24:53 +02:00
.eraseToAnyPublisher()
}
2020-08-18 07:13:37 +02:00
func service(timeline: Timeline) -> StatusListService {
StatusListService(timeline: timeline, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
2020-08-18 07:13:37 +02:00
}
2020-08-14 03:24:53 +02:00
}
private extension IdentityService {
#if DEBUG
static let pushSubscriptionEndpointURL = URL(string: "https://metatext-apns.metabolist.com/push?sandbox=true")!
#else
static let pushSubscriptionEndpointURL = URL(string: "https://metatext-apns.metabolist.com/push")!
#endif
2020-08-07 23:57:18 +02:00
}