From 182ecb5b910795add1680129ab49ce9e56ec80fa Mon Sep 17 00:00:00 2001 From: Justin Mazzocchi <2831158+jzzocc@users.noreply.github.com> Date: Wed, 9 Sep 2020 15:48:56 -0700 Subject: [PATCH] Refactoring --- .../PreviewViewModels/PreviewViewModels.swift | 2 +- .../ViewModels/Entities/Identification.swift | 2 +- .../Sources/ViewModels/RootViewModel.swift | 48 ++++++++++--------- .../ViewModels/TabNavigationViewModel.swift | 15 +++--- Views/ListsView.swift | 6 +-- Views/RootView.swift | 8 ++-- Views/SecondaryNavigationView.swift | 21 ++++---- Views/TabNavigationView.swift | 11 +++-- 8 files changed, 58 insertions(+), 55 deletions(-) diff --git a/ViewModels/Sources/PreviewViewModels/PreviewViewModels.swift b/ViewModels/Sources/PreviewViewModels/PreviewViewModels.swift index eae675d..b67f30c 100644 --- a/ViewModels/Sources/PreviewViewModels/PreviewViewModels.swift +++ b/ViewModels/Sources/PreviewViewModels/PreviewViewModels.swift @@ -47,7 +47,7 @@ public extension RootViewModel { } public extension Identification { - static let preview = RootViewModel.preview.identification! + static let preview = RootViewModel.preview.navigationViewModel!.identification } // swiftlint:enable force_try diff --git a/ViewModels/Sources/ViewModels/Entities/Identification.swift b/ViewModels/Sources/ViewModels/Entities/Identification.swift index 72d8372..f8a6c39 100644 --- a/ViewModels/Sources/ViewModels/Entities/Identification.swift +++ b/ViewModels/Sources/ViewModels/Entities/Identification.swift @@ -5,7 +5,7 @@ import Foundation import ServiceLayer public final class Identification: ObservableObject { - @Published private(set) var identity: Identity + @Published private(set) public var identity: Identity let service: IdentityService init(identity: Identity, observation: AnyPublisher, service: IdentityService) { diff --git a/ViewModels/Sources/ViewModels/RootViewModel.swift b/ViewModels/Sources/ViewModels/RootViewModel.swift index 357b31b..b126bbd 100644 --- a/ViewModels/Sources/ViewModels/RootViewModel.swift +++ b/ViewModels/Sources/ViewModels/RootViewModel.swift @@ -5,24 +5,7 @@ import Foundation import ServiceLayer public final class RootViewModel: ObservableObject { - @Published public private(set) var identification: Identification? { - didSet { - guard let identification = identification else { return } - - identification.service.updateLastUse() - .sink { _ in } receiveValue: { _ in } - .store(in: &cancellables) - - userNotificationService.isAuthorized() - .filter { $0 } - .zip(registerForRemoteNotifications()) - .filter { identification.identity.lastRegisteredDeviceToken != $1 } - .map { ($1, identification.identity.pushSubscriptionAlerts) } - .flatMap(identification.service.createPushSubscription(deviceToken:alerts:)) - .sink { _ in } receiveValue: { _ in } - .store(in: &cancellables) - } - } + @Published public private(set) var navigationViewModel: TabNavigationViewModel? @Published private var mostRecentlyUsedIdentityID: UUID? private let environment: AppEnvironment @@ -74,10 +57,12 @@ public extension RootViewModel { private extension RootViewModel { func identitySelected(id: UUID?, immediate: Bool) { + navigationViewModel?.presentingSecondaryNavigation = false + guard let id = id, let identityService = try? allIdentitiesService.identityService(id: id) else { - identification = nil + navigationViewModel = nil return } @@ -93,13 +78,30 @@ private extension RootViewModel { .share() observation - .filter { [weak self] in $0.id != self?.identification?.identity.id } - .map { - Identification( + .filter { [weak self] in $0.id != self?.navigationViewModel?.identification.identity.id } + .map { [weak self] in + let identification = Identification( identity: $0, observation: observation.eraseToAnyPublisher(), service: identityService) + + if let self = self { + identification.service.updateLastUse() + .sink { _ in } receiveValue: { _ in } + .store(in: &self.cancellables) + + self.userNotificationService.isAuthorized() + .filter { $0 } + .zip(self.registerForRemoteNotifications()) + .filter { identification.identity.lastRegisteredDeviceToken != $1 } + .map { ($1, identification.identity.pushSubscriptionAlerts) } + .flatMap(identification.service.createPushSubscription(deviceToken:alerts:)) + .sink { _ in } receiveValue: { _ in } + .store(in: &self.cancellables) + } + + return TabNavigationViewModel(identification: identification) } - .assign(to: &$identification) + .assign(to: &$navigationViewModel) } } diff --git a/ViewModels/Sources/ViewModels/TabNavigationViewModel.swift b/ViewModels/Sources/ViewModels/TabNavigationViewModel.swift index bb9c140..0e33815 100644 --- a/ViewModels/Sources/ViewModels/TabNavigationViewModel.swift +++ b/ViewModels/Sources/ViewModels/TabNavigationViewModel.swift @@ -6,7 +6,7 @@ import Mastodon import ServiceLayer public final class TabNavigationViewModel: ObservableObject { - @Published public private(set) var identity: Identity + public let identification: Identification @Published public private(set) var recentIdentities = [Identity]() @Published public var timeline: Timeline @Published public private(set) var timelinesAndLists: [Timeline] @@ -14,18 +14,19 @@ public final class TabNavigationViewModel: ObservableObject { @Published public var alertItem: AlertItem? public var selectedTab: Tab? = .timelines - private let identification: Identification private var cancellables = Set() public init(identification: Identification) { self.identification = identification - identity = identification.identity timeline = identification.service.isAuthorized ? .home : .local timelinesAndLists = identification.service.isAuthorized ? Timeline.authenticatedDefaults : Timeline.unauthenticatedDefaults - identification.$identity.dropFirst().assign(to: &$identity) + identification.$identity + .sink { [weak self] _ in self?.objectWillChange.send() } + .store(in: &cancellables) + identification.service.recentIdentitiesObservation() .assignErrorsToAlertItem(to: \.alertItem, on: self) .assign(to: &$recentIdentities) @@ -51,9 +52,9 @@ public extension TabNavigationViewModel { var timelineSubtitle: String { switch timeline { case .home, .list: - return identity.handle + return identification.identity.handle case .local, .federated, .tag: - return identity.instance?.uri ?? "" + return identification.identity.instance?.uri ?? "" } } @@ -72,7 +73,7 @@ public extension TabNavigationViewModel { .sink { _ in } .store(in: &cancellables) - if identity.preferences.useServerPostingReadingPreferences { + if identification.identity.preferences.useServerPostingReadingPreferences { identification.service.refreshServerPreferences() .assignErrorsToAlertItem(to: \.alertItem, on: self) .sink { _ in } diff --git a/Views/ListsView.swift b/Views/ListsView.swift index 6745464..ada2cdd 100644 --- a/Views/ListsView.swift +++ b/Views/ListsView.swift @@ -5,7 +5,7 @@ import ViewModels struct ListsView: View { @StateObject var viewModel: ListsViewModel - @EnvironmentObject var tabNavigationViewModel: TabNavigationViewModel + @EnvironmentObject var rootViewModel: RootViewModel @State private var newListTitle = "" var body: some View { @@ -28,8 +28,8 @@ struct ListsView: View { Section { ForEach(viewModel.lists) { list in Button(list.title) { - tabNavigationViewModel.timeline = .list(list) - tabNavigationViewModel.presentingSecondaryNavigation = false + rootViewModel.navigationViewModel?.timeline = .list(list) + rootViewModel.navigationViewModel?.presentingSecondaryNavigation = false } } .onDelete { diff --git a/Views/RootView.swift b/Views/RootView.swift index d80a109..78e27c6 100644 --- a/Views/RootView.swift +++ b/Views/RootView.swift @@ -7,11 +7,9 @@ struct RootView: View { @StateObject var viewModel: RootViewModel var body: some View { - if let identification = viewModel.identification { - TabNavigationView() - .id(UUID()) - .environmentObject(identification) - .environmentObject(TabNavigationViewModel(identification: identification)) + if let navigationViewModel = viewModel.navigationViewModel { + TabNavigationView(viewModel: navigationViewModel) + .id(navigationViewModel.identification.identity.id) .environmentObject(viewModel) .transition(.opacity) } else { diff --git a/Views/SecondaryNavigationView.swift b/Views/SecondaryNavigationView.swift index ccdba93..558ccbf 100644 --- a/Views/SecondaryNavigationView.swift +++ b/Views/SecondaryNavigationView.swift @@ -5,8 +5,9 @@ import SwiftUI import ViewModels struct SecondaryNavigationView: View { + @ObservedObject var viewModel: TabNavigationViewModel @EnvironmentObject var identification: Identification - @EnvironmentObject var tabNavigationViewModel: TabNavigationViewModel + @EnvironmentObject var rootViewModel: RootViewModel @Environment(\.displayScale) var displayScale: CGFloat var body: some View { @@ -17,25 +18,25 @@ struct SecondaryNavigationView: View { destination: IdentitiesView(viewModel: .init(identification: identification)), label: { HStack { - KFImage(tabNavigationViewModel.identity.image, + KFImage(identification.identity.image, options: .downsampled(dimension: 50, scaleFactor: displayScale)) VStack(alignment: .leading) { - if tabNavigationViewModel.identity.authenticated { - if let account = tabNavigationViewModel.identity.account { + if identification.identity.authenticated { + if let account = identification.identity.account { CustomEmojiText( text: account.displayName, emoji: account.emojis, textStyle: .headline) } - Text(tabNavigationViewModel.identity.handle) + Text(identification.identity.handle) .font(.subheadline) .foregroundColor(.secondary) .lineLimit(1) .minimumScaleFactor(0.5) } else { - Text(tabNavigationViewModel.identity.handle) + Text(identification.identity.handle) .font(.headline) - if let instance = tabNavigationViewModel.identity.instance { + if let instance = identification.identity.instance { Text(instance.uri) .font(.subheadline) .foregroundColor(.secondary) @@ -68,7 +69,7 @@ struct SecondaryNavigationView: View { .toolbar { ToolbarItem(placement: .cancellationAction) { Button { - tabNavigationViewModel.presentingSecondaryNavigation = false + viewModel.presentingSecondaryNavigation = false } label: { Image(systemName: "xmark.circle.fill") } @@ -84,9 +85,9 @@ import PreviewViewModels struct SecondaryNavigationView_Previews: PreviewProvider { static var previews: some View { - SecondaryNavigationView() + SecondaryNavigationView(viewModel: TabNavigationViewModel(identification: .preview)) .environmentObject(Identification.preview) - .environmentObject(TabNavigationViewModel(identification: .preview)) + .environmentObject(RootViewModel.preview) } } #endif diff --git a/Views/TabNavigationView.swift b/Views/TabNavigationView.swift index ca25d5e..21e3edb 100644 --- a/Views/TabNavigationView.swift +++ b/Views/TabNavigationView.swift @@ -6,7 +6,7 @@ import SwiftUI import ViewModels struct TabNavigationView: View { - @EnvironmentObject var viewModel: TabNavigationViewModel + @ObservedObject var viewModel: TabNavigationViewModel @EnvironmentObject var rootViewModel: RootViewModel @Environment(\.displayScale) var displayScale: CGFloat @@ -24,8 +24,10 @@ struct TabNavigationView: View { .tag(tab) } } + .environmentObject(viewModel.identification) .sheet(isPresented: $viewModel.presentingSecondaryNavigation) { - SecondaryNavigationView() + SecondaryNavigationView(viewModel: viewModel) + .environmentObject(viewModel.identification) .environmentObject(viewModel) .environmentObject(rootViewModel) } @@ -81,7 +83,7 @@ private extension TabNavigationView { Button { viewModel.presentingSecondaryNavigation.toggle() } label: { - KFImage(viewModel.identity.image, + KFImage(viewModel.identification.identity.image, options: .downsampled(dimension: 28, scaleFactor: displayScale)) .placeholder { Image(systemName: "gear") } .renderingMode(.original) @@ -156,9 +158,8 @@ import PreviewViewModels struct TabNavigation_Previews: PreviewProvider { static var previews: some View { - TabNavigationView() + TabNavigationView(viewModel: TabNavigationViewModel(identification: .preview)) .environmentObject(Identification.preview) - .environmentObject(TabNavigationViewModel(identification: .preview)) .environmentObject(RootViewModel.preview) } }