Refactoring
This commit is contained in:
parent
df5ca6ddb2
commit
367d79ed2c
|
@ -102,15 +102,24 @@ public extension IdentityDatabase {
|
|||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func updatePreferences(_ preferences: Identity.Preferences,
|
||||
func updatePreferences(_ preferences: Mastodon.Preferences,
|
||||
forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
|
||||
databaseQueue.writePublisher {
|
||||
let data = try IdentityRecord.databaseJSONEncoder(for: "preferences").encode(preferences)
|
||||
|
||||
try IdentityRecord
|
||||
.filter(Column("id") == identityID)
|
||||
.updateAll($0, Column("preferences").set(to: data))
|
||||
guard let storedPreferences = try IdentityRecord.filter(Column("id") == identityID)
|
||||
.fetchOne($0)?
|
||||
.preferences else {
|
||||
throw IdentityDatabaseError.identityNotFound
|
||||
}
|
||||
|
||||
try Self.writePreferences(storedPreferences.updated(from: preferences), id: identityID)($0)
|
||||
}
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func updatePreferences(_ preferences: Identity.Preferences,
|
||||
forIdentityID identityID: UUID) -> AnyPublisher<Never, Error> {
|
||||
databaseQueue.writePublisher(updates: Self.writePreferences(preferences, id: identityID))
|
||||
.ignoreOutput()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
@ -201,6 +210,16 @@ private extension IdentityDatabase {
|
|||
.asRequest(of: IdentityResult.self)
|
||||
}
|
||||
|
||||
private static func writePreferences(_ preferences: Identity.Preferences, id: UUID) -> (Database) throws -> Void {
|
||||
{
|
||||
let data = try IdentityRecord.databaseJSONEncoder(for: "preferences").encode(preferences)
|
||||
|
||||
try IdentityRecord
|
||||
.filter(Column("id") == id)
|
||||
.updateAll($0, Column("preferences").set(to: data))
|
||||
}
|
||||
}
|
||||
|
||||
private static func migrate(_ writer: DatabaseWriter) throws {
|
||||
var migrator = DatabaseMigrator()
|
||||
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Combine
|
||||
import DB
|
||||
import Foundation
|
||||
|
||||
public class IdentifiedEnvironment {
|
||||
@Published public private(set) var identity: Identity
|
||||
public let appEnvironment: AppEnvironment
|
||||
public let identityService: IdentityService
|
||||
public let observationErrors: AnyPublisher<Error, Never>
|
||||
|
||||
init(id: UUID, database: IdentityDatabase, environment: AppEnvironment) throws {
|
||||
appEnvironment = environment
|
||||
|
||||
// The scheduling on the observation is immediate so an initial value can be extracted
|
||||
let sharedObservation = database.identityObservation(id: id).share()
|
||||
var initialIdentity: Identity?
|
||||
|
||||
_ = sharedObservation.first().sink(
|
||||
receiveCompletion: { _ in },
|
||||
receiveValue: { initialIdentity = $0 })
|
||||
|
||||
guard let identity = initialIdentity else { throw IdentityDatabaseError.identityNotFound }
|
||||
|
||||
self.identity = identity
|
||||
identityService = try IdentityService(id: identity.id,
|
||||
instanceURL: identity.url,
|
||||
database: database,
|
||||
environment: environment)
|
||||
|
||||
let observationErrorsSubject = PassthroughSubject<Error, Never>()
|
||||
|
||||
self.observationErrors = observationErrorsSubject.eraseToAnyPublisher()
|
||||
|
||||
sharedObservation.catch { error -> Empty<Identity, Never> in
|
||||
observationErrorsSubject.send(error)
|
||||
|
||||
return Empty()
|
||||
}
|
||||
.assign(to: &$identity)
|
||||
}
|
||||
}
|
|
@ -11,16 +11,16 @@ public struct AllIdentitiesService {
|
|||
public let mostRecentlyUsedIdentityID: AnyPublisher<UUID?, Never>
|
||||
public let instanceFilterService: InstanceFilterService
|
||||
|
||||
private let identityDatabase: IdentityDatabase
|
||||
private let database: IdentityDatabase
|
||||
private let environment: AppEnvironment
|
||||
|
||||
public init(environment: AppEnvironment) throws {
|
||||
self.identityDatabase = try IdentityDatabase(inMemory: environment.inMemoryContent,
|
||||
self.database = try IdentityDatabase(inMemory: environment.inMemoryContent,
|
||||
fixture: environment.identityFixture,
|
||||
keychain: environment.keychain)
|
||||
self.environment = environment
|
||||
|
||||
mostRecentlyUsedIdentityID = identityDatabase.mostRecentlyUsedIdentityIDObservation()
|
||||
mostRecentlyUsedIdentityID = database.mostRecentlyUsedIdentityIDObservation()
|
||||
.replaceError(with: nil)
|
||||
.eraseToAnyPublisher()
|
||||
instanceFilterService = InstanceFilterService(environment: environment)
|
||||
|
@ -28,14 +28,12 @@ public struct AllIdentitiesService {
|
|||
}
|
||||
|
||||
public extension AllIdentitiesService {
|
||||
func identityService(id: UUID) throws -> IdentityService {
|
||||
try IdentityService(identityID: id,
|
||||
identityDatabase: identityDatabase,
|
||||
environment: environment)
|
||||
func identifiedEnvironment(id: UUID) throws -> IdentifiedEnvironment {
|
||||
try IdentifiedEnvironment(id: id, database: database, environment: environment)
|
||||
}
|
||||
|
||||
func createIdentity(id: UUID, instanceURL: URL) -> AnyPublisher<Never, Error> {
|
||||
identityDatabase.createIdentity(id: id, url: instanceURL)
|
||||
database.createIdentity(id: id, url: instanceURL)
|
||||
}
|
||||
|
||||
func authorizeIdentity(id: UUID, instanceURL: URL) -> AnyPublisher<Never, Error> {
|
||||
|
@ -61,7 +59,7 @@ public extension AllIdentitiesService {
|
|||
|
||||
mastodonAPIClient.instanceURL = identity.url
|
||||
|
||||
return identityDatabase.deleteIdentity(id: identity.id)
|
||||
return database.deleteIdentity(id: identity.id)
|
||||
.collect()
|
||||
.tryMap { _ in
|
||||
DeletionEndpoint.oauthRevoke(
|
||||
|
@ -80,10 +78,10 @@ public extension AllIdentitiesService {
|
|||
}
|
||||
|
||||
func updatePushSubscriptions(deviceToken: Data) -> AnyPublisher<Never, Error> {
|
||||
identityDatabase.identitiesWithOutdatedDeviceTokens(deviceToken: deviceToken)
|
||||
database.identitiesWithOutdatedDeviceTokens(deviceToken: deviceToken)
|
||||
.tryMap { identities -> [AnyPublisher<Never, Never>] in
|
||||
try identities.map {
|
||||
try identityService(id: $0.id)
|
||||
try IdentityService(id: $0.id, instanceURL: $0.url, database: database, environment: environment)
|
||||
.createPushSubscription(deviceToken: deviceToken, alerts: $0.pushSubscriptionAlerts)
|
||||
.catch { _ in Empty() } // don't want to disrupt pipeline
|
||||
.eraseToAnyPublisher()
|
||||
|
|
|
@ -8,10 +8,8 @@ import Mastodon
|
|||
import MastodonAPI
|
||||
import Secrets
|
||||
|
||||
public class IdentityService {
|
||||
@Published public private(set) var identity: Identity
|
||||
public let observationErrors: AnyPublisher<Error, Never>
|
||||
|
||||
public struct IdentityService {
|
||||
private let identityID: UUID
|
||||
private let identityDatabase: IdentityDatabase
|
||||
private let contentDatabase: ContentDatabase
|
||||
private let environment: AppEnvironment
|
||||
|
@ -19,40 +17,20 @@ public class IdentityService {
|
|||
private let secrets: Secrets
|
||||
private let observationErrorsInput = PassthroughSubject<Error, Never>()
|
||||
|
||||
init(identityID: UUID,
|
||||
identityDatabase: IdentityDatabase,
|
||||
environment: AppEnvironment) throws {
|
||||
self.identityDatabase = identityDatabase
|
||||
init(id: UUID, instanceURL: URL, database: IdentityDatabase, environment: AppEnvironment) throws {
|
||||
identityID = id
|
||||
identityDatabase = database
|
||||
self.environment = environment
|
||||
observationErrors = observationErrorsInput.eraseToAnyPublisher()
|
||||
|
||||
let observation = identityDatabase.identityObservation(id: identityID).share()
|
||||
var initialIdentity: Identity?
|
||||
|
||||
_ = observation.first().sink(
|
||||
receiveCompletion: { _ in },
|
||||
receiveValue: { initialIdentity = $0 })
|
||||
|
||||
guard let identity = initialIdentity else { throw IdentityDatabaseError.identityNotFound }
|
||||
|
||||
self.identity = identity
|
||||
secrets = Secrets(
|
||||
identityID: identityID,
|
||||
identityID: id,
|
||||
keychain: environment.keychain)
|
||||
mastodonAPIClient = MastodonAPIClient(session: environment.session)
|
||||
mastodonAPIClient.instanceURL = identity.url
|
||||
mastodonAPIClient.instanceURL = instanceURL
|
||||
mastodonAPIClient.accessToken = try? secrets.getAccessToken()
|
||||
|
||||
contentDatabase = try ContentDatabase(identityID: identityID,
|
||||
contentDatabase = try ContentDatabase(identityID: id,
|
||||
inMemory: environment.inMemoryContent,
|
||||
keychain: environment.keychain)
|
||||
|
||||
observation.catch { [weak self] error -> Empty<Identity, Never> in
|
||||
self?.observationErrorsInput.send(error)
|
||||
|
||||
return Empty()
|
||||
}
|
||||
.assign(to: &$identity)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,28 +38,24 @@ public extension IdentityService {
|
|||
var isAuthorized: Bool { mastodonAPIClient.accessToken != nil }
|
||||
|
||||
func updateLastUse() -> AnyPublisher<Never, Error> {
|
||||
identityDatabase.updateLastUsedAt(identityID: identity.id)
|
||||
identityDatabase.updateLastUsedAt(identityID: identityID)
|
||||
}
|
||||
|
||||
func verifyCredentials() -> AnyPublisher<Never, Error> {
|
||||
mastodonAPIClient.request(AccountEndpoint.verifyCredentials)
|
||||
.zip(Just(identity.id).first().setFailureType(to: Error.self))
|
||||
.flatMap(identityDatabase.updateAccount)
|
||||
.flatMap { identityDatabase.updateAccount($0, forIdentityID: identityID) }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func refreshServerPreferences() -> AnyPublisher<Never, Error> {
|
||||
mastodonAPIClient.request(PreferencesEndpoint.preferences)
|
||||
.zip(Just(self).first().setFailureType(to: Error.self))
|
||||
.map { ($1.identity.preferences.updated(from: $0), $1.identity.id) }
|
||||
.flatMap(identityDatabase.updatePreferences)
|
||||
.flatMap { identityDatabase.updatePreferences($0, forIdentityID: identityID) }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func refreshInstance() -> AnyPublisher<Never, Error> {
|
||||
mastodonAPIClient.request(InstanceEndpoint.instance)
|
||||
.zip(Just(identity.id).first().setFailureType(to: Error.self))
|
||||
.flatMap(identityDatabase.updateInstance)
|
||||
.flatMap { identityDatabase.updateInstance($0, forIdentityID: identityID) }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
@ -90,7 +64,7 @@ public extension IdentityService {
|
|||
}
|
||||
|
||||
func recentIdentitiesObservation() -> AnyPublisher<[Identity], Error> {
|
||||
identityDatabase.recentIdentitiesObservation(excluding: identity.id)
|
||||
identityDatabase.recentIdentitiesObservation(excluding: identityID)
|
||||
}
|
||||
|
||||
func refreshLists() -> AnyPublisher<Never, Error> {
|
||||
|
@ -145,8 +119,7 @@ public extension IdentityService {
|
|||
|
||||
func deleteFilter(id: String) -> AnyPublisher<Never, Error> {
|
||||
mastodonAPIClient.request(DeletionEndpoint.filter(id: id))
|
||||
.map { _ in id }
|
||||
.flatMap(contentDatabase.deleteFilter(id:))
|
||||
.flatMap { _ in contentDatabase.deleteFilter(id: id) }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
@ -159,12 +132,10 @@ public extension IdentityService {
|
|||
}
|
||||
|
||||
func updatePreferences(_ preferences: Identity.Preferences) -> AnyPublisher<Never, Error> {
|
||||
identityDatabase.updatePreferences(preferences, forIdentityID: identity.id)
|
||||
identityDatabase.updatePreferences(preferences, forIdentityID: identityID)
|
||||
.collect()
|
||||
.zip(Just(self).first().setFailureType(to: Error.self))
|
||||
.filter { $1.identity.preferences.useServerPostingReadingPreferences }
|
||||
.map { _ in () }
|
||||
.flatMap(refreshServerPreferences)
|
||||
.filter { _ in preferences.useServerPostingReadingPreferences }
|
||||
.flatMap { _ in refreshServerPreferences() }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
@ -179,7 +150,6 @@ public extension IdentityService {
|
|||
return Fail(error: error).eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
let identityID = identity.id
|
||||
let endpoint = Self.pushSubscriptionEndpointURL
|
||||
.appendingPathComponent(deviceToken.base16EncodedString())
|
||||
.appendingPathComponent(identityID.uuidString)
|
||||
|
@ -196,9 +166,7 @@ public extension IdentityService {
|
|||
}
|
||||
|
||||
func updatePushSubscription(alerts: PushSubscription.Alerts) -> AnyPublisher<Never, Error> {
|
||||
let identityID = identity.id
|
||||
|
||||
return mastodonAPIClient.request(PushSubscriptionEndpoint.update(alerts: alerts))
|
||||
mastodonAPIClient.request(PushSubscriptionEndpoint.update(alerts: alerts))
|
||||
.map { ($0.alerts, nil, identityID) }
|
||||
.flatMap(identityDatabase.updatePushSubscription(alerts:deviceToken:forIdentityID:))
|
||||
.eraseToAnyPublisher()
|
||||
|
|
|
@ -15,13 +15,13 @@ public class EditFilterViewModel: ObservableObject {
|
|||
didSet { filter.expiresAt = date }
|
||||
}
|
||||
|
||||
private let identityService: IdentityService
|
||||
private let environment: IdentifiedEnvironment
|
||||
private let saveCompletedInput = PassthroughSubject<Void, Never>()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init(filter: Filter, identityService: IdentityService) {
|
||||
init(filter: Filter, environment: IdentifiedEnvironment) {
|
||||
self.filter = filter
|
||||
self.identityService = identityService
|
||||
self.environment = environment
|
||||
date = filter.expiresAt ?? Date()
|
||||
saveCompleted = saveCompletedInput.eraseToAnyPublisher()
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ public extension EditFilterViewModel {
|
|||
}
|
||||
|
||||
func save() {
|
||||
(isNew ? identityService.createFilter(filter) : identityService.updateFilter(filter))
|
||||
(isNew ? environment.identityService.createFilter(filter) : environment.identityService.updateFilter(filter))
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.handleEvents(
|
||||
receiveSubscription: { [weak self] _ in self?.saving = true },
|
||||
|
|
|
@ -10,19 +10,19 @@ public class FiltersViewModel: ObservableObject {
|
|||
@Published public var expiredFilters = [Filter]()
|
||||
@Published public var alertItem: AlertItem?
|
||||
|
||||
private let identityService: IdentityService
|
||||
private let environment: IdentifiedEnvironment
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init(identityService: IdentityService) {
|
||||
self.identityService = identityService
|
||||
init(environment: IdentifiedEnvironment) {
|
||||
self.environment = environment
|
||||
|
||||
let now = Date()
|
||||
|
||||
identityService.activeFiltersObservation(date: now)
|
||||
environment.identityService.activeFiltersObservation(date: now)
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.assign(to: &$activeFilters)
|
||||
|
||||
identityService.expiredFiltersObservation(date: now)
|
||||
environment.identityService.expiredFiltersObservation(date: now)
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.assign(to: &$expiredFilters)
|
||||
}
|
||||
|
@ -30,20 +30,20 @@ public class FiltersViewModel: ObservableObject {
|
|||
|
||||
public extension FiltersViewModel {
|
||||
func refreshFilters() {
|
||||
identityService.refreshFilters()
|
||||
environment.identityService.refreshFilters()
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.sink { _ in }
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func delete(filter: Filter) {
|
||||
identityService.deleteFilter(id: filter.id)
|
||||
environment.identityService.deleteFilter(id: filter.id)
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.sink { _ in }
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func editFilterViewModel(filter: Filter) -> EditFilterViewModel {
|
||||
EditFilterViewModel(filter: filter, identityService: identityService)
|
||||
EditFilterViewModel(filter: filter, environment: environment)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,18 +5,18 @@ import Foundation
|
|||
import ServiceLayer
|
||||
|
||||
public class IdentitiesViewModel: ObservableObject {
|
||||
@Published public private(set) var identity: Identity
|
||||
public let currentIdentityID: UUID
|
||||
@Published public var identities = [Identity]()
|
||||
@Published public var alertItem: AlertItem?
|
||||
|
||||
private let identityService: IdentityService
|
||||
private let environment: IdentifiedEnvironment
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init(identityService: IdentityService) {
|
||||
self.identityService = identityService
|
||||
identity = identityService.identity
|
||||
init(environment: IdentifiedEnvironment) {
|
||||
self.environment = environment
|
||||
currentIdentityID = environment.identity.id
|
||||
|
||||
identityService.identitiesObservation()
|
||||
environment.identityService.identitiesObservation()
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.assign(to: &$identities)
|
||||
}
|
||||
|
|
|
@ -10,13 +10,13 @@ public class ListsViewModel: ObservableObject {
|
|||
@Published public private(set) var creatingList = false
|
||||
@Published public var alertItem: AlertItem?
|
||||
|
||||
private let identityService: IdentityService
|
||||
private let environment: IdentifiedEnvironment
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init(identityService: IdentityService) {
|
||||
self.identityService = identityService
|
||||
init(environment: IdentifiedEnvironment) {
|
||||
self.environment = environment
|
||||
|
||||
identityService.listsObservation()
|
||||
environment.identityService.listsObservation()
|
||||
.map {
|
||||
$0.compactMap {
|
||||
guard case let .list(list) = $0 else { return nil }
|
||||
|
@ -31,14 +31,14 @@ public class ListsViewModel: ObservableObject {
|
|||
|
||||
public extension ListsViewModel {
|
||||
func refreshLists() {
|
||||
identityService.refreshLists()
|
||||
environment.identityService.refreshLists()
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.sink { _ in }
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func createList(title: String) {
|
||||
identityService.createList(title: title)
|
||||
environment.identityService.createList(title: title)
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.handleEvents(
|
||||
receiveSubscription: { [weak self] _ in self?.creatingList = true },
|
||||
|
@ -48,7 +48,7 @@ public extension ListsViewModel {
|
|||
}
|
||||
|
||||
func delete(list: MastodonList) {
|
||||
identityService.deleteList(id: list.id)
|
||||
environment.identityService.deleteList(id: list.id)
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.sink { _ in }
|
||||
.store(in: &cancellables)
|
||||
|
|
|
@ -9,14 +9,14 @@ public class NotificationTypesPreferencesViewModel: ObservableObject {
|
|||
@Published public var pushSubscriptionAlerts: PushSubscription.Alerts
|
||||
@Published public var alertItem: AlertItem?
|
||||
|
||||
private let identityService: IdentityService
|
||||
private let environment: IdentifiedEnvironment
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init(identityService: IdentityService) {
|
||||
self.identityService = identityService
|
||||
pushSubscriptionAlerts = identityService.identity.pushSubscriptionAlerts
|
||||
init(environment: IdentifiedEnvironment) {
|
||||
self.environment = environment
|
||||
pushSubscriptionAlerts = environment.identity.pushSubscriptionAlerts
|
||||
|
||||
identityService.$identity
|
||||
environment.$identity
|
||||
.map(\.pushSubscriptionAlerts)
|
||||
.dropFirst()
|
||||
.removeDuplicates()
|
||||
|
@ -32,14 +32,14 @@ public class NotificationTypesPreferencesViewModel: ObservableObject {
|
|||
|
||||
private extension NotificationTypesPreferencesViewModel {
|
||||
func update(alerts: PushSubscription.Alerts) {
|
||||
guard alerts != identityService.identity.pushSubscriptionAlerts else { return }
|
||||
guard alerts != environment.identity.pushSubscriptionAlerts else { return }
|
||||
|
||||
identityService.updatePushSubscription(alerts: alerts)
|
||||
environment.identityService.updatePushSubscription(alerts: alerts)
|
||||
.sink { [weak self] in
|
||||
guard let self = self, case let .failure(error) = $0 else { return }
|
||||
|
||||
self.alertItem = AlertItem(error: error)
|
||||
self.pushSubscriptionAlerts = self.identityService.identity.pushSubscriptionAlerts
|
||||
self.pushSubscriptionAlerts = self.environment.identity.pushSubscriptionAlerts
|
||||
} receiveValue: { _ in }
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
|
|
@ -8,14 +8,14 @@ public class PostingReadingPreferencesViewModel: ObservableObject {
|
|||
@Published public var preferences: Identity.Preferences
|
||||
@Published public var alertItem: AlertItem?
|
||||
|
||||
private let identityService: IdentityService
|
||||
private let environment: IdentifiedEnvironment
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init(identityService: IdentityService) {
|
||||
self.identityService = identityService
|
||||
preferences = identityService.identity.preferences
|
||||
init(environment: IdentifiedEnvironment) {
|
||||
self.environment = environment
|
||||
preferences = environment.identity.preferences
|
||||
|
||||
identityService.$identity
|
||||
environment.$identity
|
||||
.map(\.preferences)
|
||||
.dropFirst()
|
||||
.removeDuplicates()
|
||||
|
@ -23,7 +23,7 @@ public class PostingReadingPreferencesViewModel: ObservableObject {
|
|||
|
||||
$preferences
|
||||
.dropFirst()
|
||||
.flatMap(identityService.updatePreferences)
|
||||
.flatMap(environment.identityService.updatePreferences)
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.sink { _ in }
|
||||
.store(in: &cancellables)
|
||||
|
|
|
@ -7,26 +7,26 @@ public class PreferencesViewModel: ObservableObject {
|
|||
public let handle: String
|
||||
public let shouldShowNotificationTypePreferences: Bool
|
||||
|
||||
private let identityService: IdentityService
|
||||
private let environment: IdentifiedEnvironment
|
||||
|
||||
init(identityService: IdentityService) {
|
||||
self.identityService = identityService
|
||||
handle = identityService.identity.handle
|
||||
init(environment: IdentifiedEnvironment) {
|
||||
self.environment = environment
|
||||
handle = environment.identity.handle
|
||||
|
||||
shouldShowNotificationTypePreferences = identityService.identity.lastRegisteredDeviceToken != nil
|
||||
shouldShowNotificationTypePreferences = environment.identity.lastRegisteredDeviceToken != nil
|
||||
}
|
||||
}
|
||||
|
||||
public extension PreferencesViewModel {
|
||||
func postingReadingPreferencesViewModel() -> PostingReadingPreferencesViewModel {
|
||||
PostingReadingPreferencesViewModel(identityService: identityService)
|
||||
PostingReadingPreferencesViewModel(environment: environment)
|
||||
}
|
||||
|
||||
func notificationTypesPreferencesViewModel() -> NotificationTypesPreferencesViewModel {
|
||||
NotificationTypesPreferencesViewModel(identityService: identityService)
|
||||
NotificationTypesPreferencesViewModel(environment: environment)
|
||||
}
|
||||
|
||||
func filtersViewModel() -> FiltersViewModel {
|
||||
FiltersViewModel(identityService: identityService)
|
||||
FiltersViewModel(environment: environment)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ public final class RootViewModel: ObservableObject {
|
|||
@Published public private(set) var tabNavigationViewModel: TabNavigationViewModel?
|
||||
|
||||
@Published private var mostRecentlyUsedIdentityID: UUID?
|
||||
private let environment: AppEnvironment
|
||||
private let allIdentitiesService: AllIdentitiesService
|
||||
private let userNotificationService: UserNotificationService
|
||||
private let registerForRemoteNotifications: () -> AnyPublisher<Data, Error>
|
||||
|
@ -15,6 +16,7 @@ public final class RootViewModel: ObservableObject {
|
|||
|
||||
public init(environment: AppEnvironment,
|
||||
registerForRemoteNotifications: @escaping () -> AnyPublisher<Data, Error>) throws {
|
||||
self.environment = environment
|
||||
allIdentitiesService = try AllIdentitiesService(environment: environment)
|
||||
userNotificationService = UserNotificationService(environment: environment)
|
||||
self.registerForRemoteNotifications = registerForRemoteNotifications
|
||||
|
@ -41,34 +43,34 @@ public extension RootViewModel {
|
|||
return
|
||||
}
|
||||
|
||||
let identityService: IdentityService
|
||||
let identifiedEnvironment: IdentifiedEnvironment
|
||||
|
||||
do {
|
||||
identityService = try allIdentitiesService.identityService(id: id)
|
||||
identifiedEnvironment = try allIdentitiesService.identifiedEnvironment(id: id)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
identityService.observationErrors
|
||||
identifiedEnvironment.observationErrors
|
||||
.receive(on: RunLoop.main)
|
||||
.map { [weak self] _ in self?.mostRecentlyUsedIdentityID }
|
||||
.sink { [weak self] in self?.newIdentitySelected(id: $0) }
|
||||
.store(in: &cancellables)
|
||||
|
||||
identityService.updateLastUse()
|
||||
identifiedEnvironment.identityService.updateLastUse()
|
||||
.sink { _ in } receiveValue: { _ in }
|
||||
.store(in: &cancellables)
|
||||
|
||||
userNotificationService.isAuthorized()
|
||||
.filter { $0 }
|
||||
.zip(registerForRemoteNotifications())
|
||||
.filter { identityService.identity.lastRegisteredDeviceToken != $1 }
|
||||
.map { ($1, identityService.identity.pushSubscriptionAlerts) }
|
||||
.flatMap(identityService.createPushSubscription(deviceToken:alerts:))
|
||||
.filter { identifiedEnvironment.identity.lastRegisteredDeviceToken != $1 }
|
||||
.map { ($1, identifiedEnvironment.identity.pushSubscriptionAlerts) }
|
||||
.flatMap(identifiedEnvironment.identityService.createPushSubscription(deviceToken:alerts:))
|
||||
.sink { _ in } receiveValue: { _ in }
|
||||
.store(in: &cancellables)
|
||||
|
||||
tabNavigationViewModel = TabNavigationViewModel(identityService: identityService)
|
||||
tabNavigationViewModel = TabNavigationViewModel(environment: identifiedEnvironment)
|
||||
}
|
||||
|
||||
func deleteIdentity(_ identity: Identity) {
|
||||
|
|
|
@ -6,25 +6,25 @@ import ServiceLayer
|
|||
public class SecondaryNavigationViewModel: ObservableObject {
|
||||
@Published public private(set) var identity: Identity
|
||||
|
||||
private let identityService: IdentityService
|
||||
private let environment: IdentifiedEnvironment
|
||||
|
||||
init(identityService: IdentityService) {
|
||||
self.identityService = identityService
|
||||
identity = identityService.identity
|
||||
identityService.$identity.dropFirst().assign(to: &$identity)
|
||||
init(environment: IdentifiedEnvironment) {
|
||||
self.environment = environment
|
||||
identity = environment.identity
|
||||
environment.$identity.dropFirst().assign(to: &$identity)
|
||||
}
|
||||
}
|
||||
|
||||
public extension SecondaryNavigationViewModel {
|
||||
func identitiesViewModel() -> IdentitiesViewModel {
|
||||
IdentitiesViewModel(identityService: identityService)
|
||||
IdentitiesViewModel(environment: environment)
|
||||
}
|
||||
|
||||
func listsViewModel() -> ListsViewModel {
|
||||
ListsViewModel(identityService: identityService)
|
||||
ListsViewModel(environment: environment)
|
||||
}
|
||||
|
||||
func preferencesViewModel() -> PreferencesViewModel {
|
||||
PreferencesViewModel(identityService: identityService)
|
||||
PreferencesViewModel(environment: environment)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,19 +14,19 @@ public class TabNavigationViewModel: ObservableObject {
|
|||
@Published public var alertItem: AlertItem?
|
||||
public var selectedTab: Tab? = .timelines
|
||||
|
||||
private let identityService: IdentityService
|
||||
private let environment: IdentifiedEnvironment
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init(identityService: IdentityService) {
|
||||
self.identityService = identityService
|
||||
identity = identityService.identity
|
||||
identityService.$identity.dropFirst().assign(to: &$identity)
|
||||
init(environment: IdentifiedEnvironment) {
|
||||
self.environment = environment
|
||||
identity = environment.identity
|
||||
environment.$identity.dropFirst().assign(to: &$identity)
|
||||
|
||||
identityService.recentIdentitiesObservation()
|
||||
environment.identityService.recentIdentitiesObservation()
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.assign(to: &$recentIdentities)
|
||||
|
||||
identityService.listsObservation()
|
||||
environment.identityService.listsObservation()
|
||||
.map { Timeline.nonLists + $0 }
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.assign(to: &$timelinesAndLists)
|
||||
|
@ -54,42 +54,42 @@ public extension TabNavigationViewModel {
|
|||
}
|
||||
|
||||
func refreshIdentity() {
|
||||
if identityService.isAuthorized {
|
||||
identityService.verifyCredentials()
|
||||
if environment.identityService.isAuthorized {
|
||||
environment.identityService.verifyCredentials()
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.sink { _ in }
|
||||
.store(in: &cancellables)
|
||||
|
||||
identityService.refreshLists()
|
||||
environment.identityService.refreshLists()
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.sink { _ in }
|
||||
.store(in: &cancellables)
|
||||
|
||||
identityService.refreshFilters()
|
||||
environment.identityService.refreshFilters()
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.sink { _ in }
|
||||
.store(in: &cancellables)
|
||||
|
||||
if identity.preferences.useServerPostingReadingPreferences {
|
||||
identityService.refreshServerPreferences()
|
||||
environment.identityService.refreshServerPreferences()
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.sink { _ in }
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
}
|
||||
|
||||
identityService.refreshInstance()
|
||||
environment.identityService.refreshInstance()
|
||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||
.sink { _ in }
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func secondaryNavigationViewModel() -> SecondaryNavigationViewModel {
|
||||
SecondaryNavigationViewModel(identityService: identityService)
|
||||
SecondaryNavigationViewModel(environment: environment)
|
||||
}
|
||||
|
||||
func viewModel(timeline: Timeline) -> StatusListViewModel {
|
||||
StatusListViewModel(statusListService: identityService.service(timeline: timeline))
|
||||
StatusListViewModel(statusListService: environment.identityService.service(timeline: timeline))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,12 +43,12 @@ struct IdentitiesView: View {
|
|||
Spacer()
|
||||
}
|
||||
Spacer()
|
||||
if identity.id == viewModel.identity.id {
|
||||
if identity.id == viewModel.currentIdentityID {
|
||||
Image(systemName: "checkmark.circle")
|
||||
}
|
||||
}
|
||||
}
|
||||
.disabled(identity.id == viewModel.identity.id)
|
||||
.disabled(identity.id == viewModel.currentIdentityID)
|
||||
.buttonStyle(PlainButtonStyle())
|
||||
}
|
||||
.onDelete {
|
||||
|
|
Loading…
Reference in New Issue