Secondary navigation refactor
This commit is contained in:
parent
db8be2f00b
commit
3211ebf719
|
@ -87,6 +87,7 @@
|
|||
"registration.password-confirmation-mismatch" = "Password and password confirmation do not match";
|
||||
"secondary-navigation.manage-accounts" = "Manage Accounts";
|
||||
"secondary-navigation.lists" = "Lists";
|
||||
"secondary-navigation.my-profile" = "My Profile";
|
||||
"secondary-navigation.preferences" = "Preferences";
|
||||
"identities.accounts" = "Accounts";
|
||||
"identities.browsing" = "Browsing";
|
||||
|
|
|
@ -9,6 +9,8 @@ import MastodonAPI
|
|||
import Secrets
|
||||
|
||||
public struct IdentityService {
|
||||
public let navigationService: NavigationService
|
||||
|
||||
private let id: Identity.Id
|
||||
private let identityDatabase: IdentityDatabase
|
||||
private let contentDatabase: ContentDatabase
|
||||
|
@ -36,6 +38,10 @@ public struct IdentityService {
|
|||
inMemory: environment.inMemoryContent,
|
||||
appGroup: AppEnvironment.appGroup,
|
||||
keychain: environment.keychain)
|
||||
|
||||
navigationService = NavigationService(
|
||||
mastodonAPIClient: mastodonAPIClient,
|
||||
contentDatabase: contentDatabase)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,10 +244,6 @@ public extension IdentityService {
|
|||
mastodonAPIClient.request(StatusEndpoint.post(statusComponents)).map(\.id).eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func service(timeline: Timeline) -> TimelineService {
|
||||
TimelineService(timeline: timeline, mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
||||
}
|
||||
|
||||
func service(accountList: AccountsEndpoint, titleComponents: [String]? = nil) -> AccountListService {
|
||||
AccountListService(
|
||||
endpoint: accountList,
|
||||
|
|
|
@ -12,6 +12,7 @@ public struct TimelineService {
|
|||
public let nextPageMaxId: AnyPublisher<String, Never>
|
||||
public let preferLastPresentIdOverNextPageMaxId = true
|
||||
public let title: AnyPublisher<String, Never>
|
||||
public let titleLocalizationComponents: AnyPublisher<[String], Never>
|
||||
|
||||
private let timeline: Timeline
|
||||
private let mastodonAPIClient: MastodonAPIClient
|
||||
|
@ -26,10 +27,22 @@ public struct TimelineService {
|
|||
navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
||||
nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher()
|
||||
|
||||
if case let .tag(tag) = timeline {
|
||||
switch timeline {
|
||||
case let .list(list):
|
||||
title = Just(list.title).eraseToAnyPublisher()
|
||||
titleLocalizationComponents = Empty().eraseToAnyPublisher()
|
||||
case let .tag(tag):
|
||||
title = Just("#".appending(tag)).eraseToAnyPublisher()
|
||||
} else {
|
||||
titleLocalizationComponents = Empty().eraseToAnyPublisher()
|
||||
case .favorites:
|
||||
title = Empty().eraseToAnyPublisher()
|
||||
titleLocalizationComponents = Just(["favorites"]).eraseToAnyPublisher()
|
||||
case .bookmarks:
|
||||
title = Empty().eraseToAnyPublisher()
|
||||
titleLocalizationComponents = Just(["bookmarks"]).eraseToAnyPublisher()
|
||||
default:
|
||||
title = Empty().eraseToAnyPublisher()
|
||||
titleLocalizationComponents = Empty().eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,9 +40,8 @@ final class MainNavigationViewController: UITabBarController {
|
|||
.sink { [weak self] in self?.setupViewControllers(pending: $0) }
|
||||
.store(in: &cancellables)
|
||||
|
||||
viewModel.timelineNavigations.map { _ in }
|
||||
.merge(with: viewModel.followRequestNavigations.map { _ in })
|
||||
.sink { [weak self] in self?.selectedIndex = 0 }
|
||||
viewModel.navigations
|
||||
.sink { [weak self] in self?.handle(navigation: $0) }
|
||||
.store(in: &cancellables)
|
||||
|
||||
NotificationCenter.default.publisher(for: UIScene.willEnterForegroundNotification)
|
||||
|
@ -142,4 +141,29 @@ private extension MainNavigationViewController {
|
|||
dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
func handle(navigation: Navigation) {
|
||||
let vc: UIViewController
|
||||
|
||||
switch navigation {
|
||||
case let .collection(collectionService):
|
||||
vc = TableViewController(
|
||||
viewModel: CollectionItemsViewModel(
|
||||
collectionService: collectionService,
|
||||
identityContext: viewModel.identityContext),
|
||||
rootViewModel: rootViewModel)
|
||||
case let .profile(profileService):
|
||||
vc = ProfileViewController(
|
||||
viewModel: ProfileViewModel(
|
||||
profileService: profileService,
|
||||
identityContext: viewModel.identityContext),
|
||||
rootViewModel: rootViewModel,
|
||||
identityContext: viewModel.identityContext,
|
||||
parentNavigationController: nil)
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
selectedViewController?.show(vc, sender: self)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,28 +69,6 @@ final class TimelinesViewController: UIPageViewController {
|
|||
animated: !UIAccessibility.isReduceMotionEnabled)
|
||||
},
|
||||
for: .valueChanged)
|
||||
|
||||
viewModel.timelineNavigations.sink { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
let vc = TableViewController(
|
||||
viewModel: self.viewModel.viewModel(timeline: $0),
|
||||
rootViewModel: self.rootViewModel)
|
||||
|
||||
vc.navigationItem.title = $0.title
|
||||
|
||||
self.show(vc, sender: self)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
viewModel.followRequestNavigations.sink { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
let vc = TableViewController(viewModel: $0, rootViewModel: self.rootViewModel)
|
||||
|
||||
self.show(vc, sender: self)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,21 +7,18 @@ import ServiceLayer
|
|||
|
||||
public final class NavigationViewModel: ObservableObject {
|
||||
public let identityContext: IdentityContext
|
||||
public let timelineNavigations: AnyPublisher<Timeline, Never>
|
||||
public let followRequestNavigations: AnyPublisher<CollectionViewModel, Never>
|
||||
public let navigations: AnyPublisher<Navigation, Never>
|
||||
|
||||
@Published public private(set) var recentIdentities = [Identity]()
|
||||
@Published public var presentingSecondaryNavigation = false
|
||||
@Published public var alertItem: AlertItem?
|
||||
|
||||
private let timelineNavigationsSubject = PassthroughSubject<Timeline, Never>()
|
||||
private let followRequestNavigationsSubject = PassthroughSubject<CollectionViewModel, Never>()
|
||||
private let navigationsSubject = PassthroughSubject<Navigation, Never>()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
public init(identityContext: IdentityContext) {
|
||||
self.identityContext = identityContext
|
||||
timelineNavigations = timelineNavigationsSubject.eraseToAnyPublisher()
|
||||
followRequestNavigations = followRequestNavigationsSubject.eraseToAnyPublisher()
|
||||
navigations = navigationsSubject.eraseToAnyPublisher()
|
||||
|
||||
identityContext.$identity
|
||||
.sink { [weak self] _ in self?.objectWillChange.send() }
|
||||
|
@ -34,7 +31,7 @@ public final class NavigationViewModel: ObservableObject {
|
|||
}
|
||||
|
||||
public extension NavigationViewModel {
|
||||
enum Tab: CaseIterable {
|
||||
enum Tab: Int, CaseIterable {
|
||||
case timelines
|
||||
case explore
|
||||
case notifications
|
||||
|
@ -95,25 +92,27 @@ public extension NavigationViewModel {
|
|||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func navigateToProfile(id: Account.Id) {
|
||||
presentingSecondaryNavigation = false
|
||||
navigationsSubject.send(.profile(identityContext.service.navigationService.profileService(id: id)))
|
||||
}
|
||||
|
||||
func navigate(timeline: Timeline) {
|
||||
presentingSecondaryNavigation = false
|
||||
timelineNavigationsSubject.send(timeline)
|
||||
navigationsSubject.send(
|
||||
.collection(identityContext.service.navigationService.timelineService(timeline: timeline)))
|
||||
}
|
||||
|
||||
func navigateToFollowerRequests() {
|
||||
let followRequestsViewModel = CollectionItemsViewModel(
|
||||
collectionService: identityContext.service.service(
|
||||
accountList: .followRequests,
|
||||
titleComponents: ["follow-requests"]),
|
||||
identityContext: identityContext)
|
||||
|
||||
presentingSecondaryNavigation = false
|
||||
followRequestNavigationsSubject.send(followRequestsViewModel)
|
||||
navigationsSubject.send(.collection(identityContext.service.service(
|
||||
accountList: .followRequests,
|
||||
titleComponents: ["follow-requests"])))
|
||||
}
|
||||
|
||||
func viewModel(timeline: Timeline) -> CollectionItemsViewModel {
|
||||
CollectionItemsViewModel(
|
||||
collectionService: identityContext.service.service(timeline: timeline),
|
||||
collectionService: identityContext.service.navigationService.timelineService(timeline: timeline),
|
||||
identityContext: identityContext)
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,17 @@ struct SecondaryNavigationView: View {
|
|||
var body: some View {
|
||||
Form {
|
||||
Section {
|
||||
if let id = viewModel.identityContext.identity.account?.id {
|
||||
Button {
|
||||
viewModel.navigateToProfile(id: id)
|
||||
} label: {
|
||||
Label {
|
||||
Text("secondary-navigation.my-profile").foregroundColor(.primary)
|
||||
} icon: {
|
||||
Image(systemName: "person.crop.square")
|
||||
}
|
||||
}
|
||||
}
|
||||
NavigationLink(
|
||||
destination: IdentitiesView(viewModel: .init(identityContext: viewModel.identityContext))
|
||||
.environmentObject(rootViewModel),
|
||||
|
|
Loading…
Reference in New Issue