From 367d79ed2c596648e46a844673fc4dc193054eae Mon Sep 17 00:00:00 2001 From: Justin Mazzocchi <2831158+jzzocc@users.noreply.github.com> Date: Mon, 7 Sep 2020 09:33:36 -0700 Subject: [PATCH] Refactoring --- DB/Sources/DB/Identity/IdentityDatabase.swift | 29 ++++++-- .../Entities/IdentifiedEnvironment.swift | 43 ++++++++++++ .../Services/AllIdentitiesService.swift | 20 +++--- .../Services/IdentityService.swift | 68 +++++-------------- .../ViewModels/EditFilterViewModel.swift | 8 +-- .../Sources/ViewModels/FiltersViewModel.swift | 16 ++--- .../ViewModels/IdentitiesViewModel.swift | 12 ++-- .../Sources/ViewModels/ListsViewModel.swift | 14 ++-- ...otificationTypesPreferencesViewModel.swift | 16 ++--- .../PostingReadingPreferencesViewModel.swift | 12 ++-- .../ViewModels/PreferencesViewModel.swift | 16 ++--- .../Sources/ViewModels/RootViewModel.swift | 18 ++--- .../SecondaryNavigationViewModel.swift | 16 ++--- .../ViewModels/TabNavigationViewModel.swift | 30 ++++---- Views/IdentitiesView.swift | 4 +- 15 files changed, 176 insertions(+), 146 deletions(-) create mode 100644 ServiceLayer/Sources/ServiceLayer/Entities/IdentifiedEnvironment.swift diff --git a/DB/Sources/DB/Identity/IdentityDatabase.swift b/DB/Sources/DB/Identity/IdentityDatabase.swift index 0e27dca..2180d90 100644 --- a/DB/Sources/DB/Identity/IdentityDatabase.swift +++ b/DB/Sources/DB/Identity/IdentityDatabase.swift @@ -102,19 +102,28 @@ public extension IdentityDatabase { .eraseToAnyPublisher() } - func updatePreferences(_ preferences: Identity.Preferences, + func updatePreferences(_ preferences: Mastodon.Preferences, forIdentityID identityID: UUID) -> AnyPublisher { databaseQueue.writePublisher { - let data = try IdentityRecord.databaseJSONEncoder(for: "preferences").encode(preferences) + guard let storedPreferences = try IdentityRecord.filter(Column("id") == identityID) + .fetchOne($0)? + .preferences else { + throw IdentityDatabaseError.identityNotFound + } - try IdentityRecord - .filter(Column("id") == identityID) - .updateAll($0, Column("preferences").set(to: data)) + try Self.writePreferences(storedPreferences.updated(from: preferences), id: identityID)($0) } .ignoreOutput() .eraseToAnyPublisher() } + func updatePreferences(_ preferences: Identity.Preferences, + forIdentityID identityID: UUID) -> AnyPublisher { + databaseQueue.writePublisher(updates: Self.writePreferences(preferences, id: identityID)) + .ignoreOutput() + .eraseToAnyPublisher() + } + func updatePushSubscription(alerts: PushSubscription.Alerts, deviceToken: Data? = nil, forIdentityID identityID: UUID) -> AnyPublisher { @@ -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() diff --git a/ServiceLayer/Sources/ServiceLayer/Entities/IdentifiedEnvironment.swift b/ServiceLayer/Sources/ServiceLayer/Entities/IdentifiedEnvironment.swift new file mode 100644 index 0000000..89acfa2 --- /dev/null +++ b/ServiceLayer/Sources/ServiceLayer/Entities/IdentifiedEnvironment.swift @@ -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 + + 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() + + self.observationErrors = observationErrorsSubject.eraseToAnyPublisher() + + sharedObservation.catch { error -> Empty in + observationErrorsSubject.send(error) + + return Empty() + } + .assign(to: &$identity) + } +} diff --git a/ServiceLayer/Sources/ServiceLayer/Services/AllIdentitiesService.swift b/ServiceLayer/Sources/ServiceLayer/Services/AllIdentitiesService.swift index c6c3eb2..02ff0cc 100644 --- a/ServiceLayer/Sources/ServiceLayer/Services/AllIdentitiesService.swift +++ b/ServiceLayer/Sources/ServiceLayer/Services/AllIdentitiesService.swift @@ -11,16 +11,16 @@ public struct AllIdentitiesService { public let mostRecentlyUsedIdentityID: AnyPublisher 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 { - identityDatabase.createIdentity(id: id, url: instanceURL) + database.createIdentity(id: id, url: instanceURL) } func authorizeIdentity(id: UUID, instanceURL: URL) -> AnyPublisher { @@ -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 { - identityDatabase.identitiesWithOutdatedDeviceTokens(deviceToken: deviceToken) + database.identitiesWithOutdatedDeviceTokens(deviceToken: deviceToken) .tryMap { identities -> [AnyPublisher] 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() diff --git a/ServiceLayer/Sources/ServiceLayer/Services/IdentityService.swift b/ServiceLayer/Sources/ServiceLayer/Services/IdentityService.swift index 47675a5..af9bd07 100644 --- a/ServiceLayer/Sources/ServiceLayer/Services/IdentityService.swift +++ b/ServiceLayer/Sources/ServiceLayer/Services/IdentityService.swift @@ -8,10 +8,8 @@ import Mastodon import MastodonAPI import Secrets -public class IdentityService { - @Published public private(set) var identity: Identity - public let observationErrors: AnyPublisher - +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() - 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 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 { - identityDatabase.updateLastUsedAt(identityID: identity.id) + identityDatabase.updateLastUsedAt(identityID: identityID) } func verifyCredentials() -> AnyPublisher { 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 { 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 { 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 { @@ -145,8 +119,7 @@ public extension IdentityService { func deleteFilter(id: String) -> AnyPublisher { 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 { - 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 { - 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() diff --git a/ViewModels/Sources/ViewModels/EditFilterViewModel.swift b/ViewModels/Sources/ViewModels/EditFilterViewModel.swift index 477178d..c590242 100644 --- a/ViewModels/Sources/ViewModels/EditFilterViewModel.swift +++ b/ViewModels/Sources/ViewModels/EditFilterViewModel.swift @@ -15,13 +15,13 @@ public class EditFilterViewModel: ObservableObject { didSet { filter.expiresAt = date } } - private let identityService: IdentityService + private let environment: IdentifiedEnvironment private let saveCompletedInput = PassthroughSubject() private var cancellables = Set() - 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 }, diff --git a/ViewModels/Sources/ViewModels/FiltersViewModel.swift b/ViewModels/Sources/ViewModels/FiltersViewModel.swift index 065bfe7..451b76d 100644 --- a/ViewModels/Sources/ViewModels/FiltersViewModel.swift +++ b/ViewModels/Sources/ViewModels/FiltersViewModel.swift @@ -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() - 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) } } diff --git a/ViewModels/Sources/ViewModels/IdentitiesViewModel.swift b/ViewModels/Sources/ViewModels/IdentitiesViewModel.swift index ac84e87..bcd4cb4 100644 --- a/ViewModels/Sources/ViewModels/IdentitiesViewModel.swift +++ b/ViewModels/Sources/ViewModels/IdentitiesViewModel.swift @@ -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() - 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) } diff --git a/ViewModels/Sources/ViewModels/ListsViewModel.swift b/ViewModels/Sources/ViewModels/ListsViewModel.swift index 9a8a350..0fe9b2b 100644 --- a/ViewModels/Sources/ViewModels/ListsViewModel.swift +++ b/ViewModels/Sources/ViewModels/ListsViewModel.swift @@ -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() - 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) diff --git a/ViewModels/Sources/ViewModels/NotificationTypesPreferencesViewModel.swift b/ViewModels/Sources/ViewModels/NotificationTypesPreferencesViewModel.swift index c5118bc..b592796 100644 --- a/ViewModels/Sources/ViewModels/NotificationTypesPreferencesViewModel.swift +++ b/ViewModels/Sources/ViewModels/NotificationTypesPreferencesViewModel.swift @@ -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() - 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) } diff --git a/ViewModels/Sources/ViewModels/PostingReadingPreferencesViewModel.swift b/ViewModels/Sources/ViewModels/PostingReadingPreferencesViewModel.swift index 0544153..5b03c01 100644 --- a/ViewModels/Sources/ViewModels/PostingReadingPreferencesViewModel.swift +++ b/ViewModels/Sources/ViewModels/PostingReadingPreferencesViewModel.swift @@ -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() - 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) diff --git a/ViewModels/Sources/ViewModels/PreferencesViewModel.swift b/ViewModels/Sources/ViewModels/PreferencesViewModel.swift index c5564c9..7ca4e6c 100644 --- a/ViewModels/Sources/ViewModels/PreferencesViewModel.swift +++ b/ViewModels/Sources/ViewModels/PreferencesViewModel.swift @@ -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) } } diff --git a/ViewModels/Sources/ViewModels/RootViewModel.swift b/ViewModels/Sources/ViewModels/RootViewModel.swift index adf4d3c..44d0c5c 100644 --- a/ViewModels/Sources/ViewModels/RootViewModel.swift +++ b/ViewModels/Sources/ViewModels/RootViewModel.swift @@ -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 @@ -15,6 +16,7 @@ public final class RootViewModel: ObservableObject { public init(environment: AppEnvironment, registerForRemoteNotifications: @escaping () -> AnyPublisher) 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) { diff --git a/ViewModels/Sources/ViewModels/SecondaryNavigationViewModel.swift b/ViewModels/Sources/ViewModels/SecondaryNavigationViewModel.swift index fd00ead..76e6759 100644 --- a/ViewModels/Sources/ViewModels/SecondaryNavigationViewModel.swift +++ b/ViewModels/Sources/ViewModels/SecondaryNavigationViewModel.swift @@ -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) } } diff --git a/ViewModels/Sources/ViewModels/TabNavigationViewModel.swift b/ViewModels/Sources/ViewModels/TabNavigationViewModel.swift index 98fa00c..7b37dce 100644 --- a/ViewModels/Sources/ViewModels/TabNavigationViewModel.swift +++ b/ViewModels/Sources/ViewModels/TabNavigationViewModel.swift @@ -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() - 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)) } } diff --git a/Views/IdentitiesView.swift b/Views/IdentitiesView.swift index 58e47f4..5f17281 100644 --- a/Views/IdentitiesView.swift +++ b/Views/IdentitiesView.swift @@ -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 {