From e5d7b0a12b135e2841842dfa5bcd83a2929e0346 Mon Sep 17 00:00:00 2001 From: Justin Mazzocchi <2831158+jzzocc@users.noreply.github.com> Date: Fri, 28 Aug 2020 17:06:09 -0700 Subject: [PATCH] Timeline selection --- Localizations/Localizable.strings | 3 + Model/Timeline.swift | 2 +- View Models/TabNavigationViewModel.swift | 47 ++++++++++++++- Views/TabNavigationView.swift | 75 ++++++++++++++++-------- 4 files changed, 101 insertions(+), 26 deletions(-) diff --git a/Localizations/Localizable.strings b/Localizations/Localizable.strings index da406ea..8e3f470 100644 --- a/Localizations/Localizable.strings +++ b/Localizations/Localizable.strings @@ -38,3 +38,6 @@ "status.visibility.public" = "Public"; "status.visibility.unlisted" = "Unlisted"; "status.visibility.private" = "Private"; +"timelines.home" = "Home"; +"timelines.local" = "Local"; +"timelines.federated" = "Federated"; diff --git a/Model/Timeline.swift b/Model/Timeline.swift index 0a005c5..015d946 100644 --- a/Model/Timeline.swift +++ b/Model/Timeline.swift @@ -2,7 +2,7 @@ import Foundation -enum Timeline { +enum Timeline: Identifiable { case home case local case federated diff --git a/View Models/TabNavigationViewModel.swift b/View Models/TabNavigationViewModel.swift index c6f3842..8ceb0b5 100644 --- a/View Models/TabNavigationViewModel.swift +++ b/View Models/TabNavigationViewModel.swift @@ -6,7 +6,8 @@ import Combine class TabNavigationViewModel: ObservableObject { @Published private(set) var identity: Identity @Published private(set) var recentIdentities = [Identity]() - @Published private(set) var timelineViewModel: StatusListViewModel + @Published private(set) var timeline = Timeline.home + @Published private(set) var timelinesAndLists = TabNavigationViewModel.timelines @Published var presentingSecondaryNavigation = false @Published var alertItem: AlertItem? var selectedTab: Tab? = .timelines @@ -17,7 +18,6 @@ class TabNavigationViewModel: ObservableObject { init(identityService: IdentityService) { self.identityService = identityService identity = identityService.identity - timelineViewModel = StatusListViewModel(statusListService: identityService.service(timeline: .home)) identityService.$identity.dropFirst().assign(to: &$identity) identityService.recentIdentitiesObservation() @@ -27,6 +27,37 @@ class TabNavigationViewModel: ObservableObject { } extension TabNavigationViewModel { + var timelineSubtitle: String { + switch timeline { + case .home, .list: + return identity.handle + case .local, .federated: + return identity.instance?.uri ?? "" + } + } + + func title(timeline: Timeline) -> String { + switch timeline { + case .home: + return NSLocalizedString("timelines.home", comment: "") + case .local: + return NSLocalizedString("timelines.local", comment: "") + case .federated: + return NSLocalizedString("timelines.federated", comment: "") + case let .list(list): + return list.title + } + } + + func systemImageName(timeline: Timeline) -> String { + switch timeline { + case .home: return "house" + case .local: return "person.3" + case .federated: return "globe" + case .list: return "scroll" + } + } + func refreshIdentity() { if identityService.isAuthorized { identityService.verifyCredentials() @@ -51,6 +82,18 @@ extension TabNavigationViewModel { func secondaryNavigationViewModel() -> SecondaryNavigationViewModel { SecondaryNavigationViewModel(identityService: identityService) } + + func viewModel(timeline: Timeline) -> StatusListViewModel { + StatusListViewModel(statusListService: identityService.service(timeline: timeline)) + } + + func select(timeline: Timeline) { + self.timeline = timeline + } +} + +private extension TabNavigationViewModel { + static let timelines: [Timeline] = [.home, .local, .federated] } extension TabNavigationViewModel { diff --git a/Views/TabNavigationView.swift b/Views/TabNavigationView.swift index d3016db..3a376ae 100644 --- a/Views/TabNavigationView.swift +++ b/Views/TabNavigationView.swift @@ -40,36 +40,65 @@ private extension TabNavigationView { func view(tab: TabNavigationViewModel.Tab) -> some View { switch tab { case .timelines: - StatusListView(viewModel: viewModel.timelineViewModel) + StatusListView(viewModel: viewModel.viewModel(timeline: viewModel.timeline)) + .id(viewModel.timeline.id) .edgesIgnoringSafeArea(.all) - .navigationBarTitle(viewModel.identity.handle, displayMode: .inline) + .navigationBarTitle(viewModel.title(timeline: viewModel.timeline), displayMode: .inline) + .toolbar { + ToolbarItem(placement: .principal) { + VStack { + Text(viewModel.title(timeline: viewModel.timeline)) + .font(.headline) + Text(viewModel.timelineSubtitle) + .font(.footnote) + .foregroundColor(.secondary) + } + } + } .navigationBarItems( - leading: Button { - viewModel.presentingSecondaryNavigation.toggle() + leading: secondaryNavigationButton, + trailing: Menu { + ForEach(viewModel.timelinesAndLists) { timeline in + Button { + viewModel.select(timeline: timeline) + } label: { + Label(viewModel.title(timeline: timeline), + systemImage: viewModel.systemImageName(timeline: timeline)) + } + } } label: { - KFImage(viewModel.identity.image, - options: .downsampled(dimension: 28, scaleFactor: displayScale)) - .placeholder { Image(systemName: "gear") } - .renderingMode(.original) - .contextMenu(ContextMenu { - ForEach(viewModel.recentIdentities) { recentIdentity in - Button { - rootViewModel.newIdentitySelected(id: recentIdentity.id) - } label: { - Label( - title: { Text(recentIdentity.handle) }, - icon: { - KFImage(recentIdentity.image, - options: .downsampled(dimension: 28, scaleFactor: displayScale)) - .renderingMode(.original) - }) - } - } - }) + Image(systemName: viewModel.systemImageName(timeline: viewModel.timeline)) }) default: Text(tab.title) } } + + @ViewBuilder + var secondaryNavigationButton: some View { + Button { + viewModel.presentingSecondaryNavigation.toggle() + } label: { + KFImage(viewModel.identity.image, + options: .downsampled(dimension: 28, scaleFactor: displayScale)) + .placeholder { Image(systemName: "gear") } + .renderingMode(.original) + .contextMenu(ContextMenu { + ForEach(viewModel.recentIdentities) { recentIdentity in + Button { + rootViewModel.newIdentitySelected(id: recentIdentity.id) + } label: { + Label( + title: { Text(recentIdentity.handle) }, + icon: { + KFImage(recentIdentity.image, + options: .downsampled(dimension: 28, scaleFactor: displayScale)) + .renderingMode(.original) + }) + } + } + }) + } + } } #if DEBUG