mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-22 16:21:00 +00:00
Secondary navigation refactor
This commit is contained in:
parent
db8be2f00b
commit
3211ebf719
7 changed files with 75 additions and 47 deletions
|
@ -87,6 +87,7 @@
|
||||||
"registration.password-confirmation-mismatch" = "Password and password confirmation do not match";
|
"registration.password-confirmation-mismatch" = "Password and password confirmation do not match";
|
||||||
"secondary-navigation.manage-accounts" = "Manage Accounts";
|
"secondary-navigation.manage-accounts" = "Manage Accounts";
|
||||||
"secondary-navigation.lists" = "Lists";
|
"secondary-navigation.lists" = "Lists";
|
||||||
|
"secondary-navigation.my-profile" = "My Profile";
|
||||||
"secondary-navigation.preferences" = "Preferences";
|
"secondary-navigation.preferences" = "Preferences";
|
||||||
"identities.accounts" = "Accounts";
|
"identities.accounts" = "Accounts";
|
||||||
"identities.browsing" = "Browsing";
|
"identities.browsing" = "Browsing";
|
||||||
|
|
|
@ -9,6 +9,8 @@ import MastodonAPI
|
||||||
import Secrets
|
import Secrets
|
||||||
|
|
||||||
public struct IdentityService {
|
public struct IdentityService {
|
||||||
|
public let navigationService: NavigationService
|
||||||
|
|
||||||
private let id: Identity.Id
|
private let id: Identity.Id
|
||||||
private let identityDatabase: IdentityDatabase
|
private let identityDatabase: IdentityDatabase
|
||||||
private let contentDatabase: ContentDatabase
|
private let contentDatabase: ContentDatabase
|
||||||
|
@ -36,6 +38,10 @@ public struct IdentityService {
|
||||||
inMemory: environment.inMemoryContent,
|
inMemory: environment.inMemoryContent,
|
||||||
appGroup: AppEnvironment.appGroup,
|
appGroup: AppEnvironment.appGroup,
|
||||||
keychain: environment.keychain)
|
keychain: environment.keychain)
|
||||||
|
|
||||||
|
navigationService = NavigationService(
|
||||||
|
mastodonAPIClient: mastodonAPIClient,
|
||||||
|
contentDatabase: contentDatabase)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,10 +244,6 @@ public extension IdentityService {
|
||||||
mastodonAPIClient.request(StatusEndpoint.post(statusComponents)).map(\.id).eraseToAnyPublisher()
|
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 {
|
func service(accountList: AccountsEndpoint, titleComponents: [String]? = nil) -> AccountListService {
|
||||||
AccountListService(
|
AccountListService(
|
||||||
endpoint: accountList,
|
endpoint: accountList,
|
||||||
|
|
|
@ -12,6 +12,7 @@ public struct TimelineService {
|
||||||
public let nextPageMaxId: AnyPublisher<String, Never>
|
public let nextPageMaxId: AnyPublisher<String, Never>
|
||||||
public let preferLastPresentIdOverNextPageMaxId = true
|
public let preferLastPresentIdOverNextPageMaxId = true
|
||||||
public let title: AnyPublisher<String, Never>
|
public let title: AnyPublisher<String, Never>
|
||||||
|
public let titleLocalizationComponents: AnyPublisher<[String], Never>
|
||||||
|
|
||||||
private let timeline: Timeline
|
private let timeline: Timeline
|
||||||
private let mastodonAPIClient: MastodonAPIClient
|
private let mastodonAPIClient: MastodonAPIClient
|
||||||
|
@ -26,10 +27,22 @@ public struct TimelineService {
|
||||||
navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
navigationService = NavigationService(mastodonAPIClient: mastodonAPIClient, contentDatabase: contentDatabase)
|
||||||
nextPageMaxId = nextPageMaxIdSubject.eraseToAnyPublisher()
|
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()
|
title = Just("#".appending(tag)).eraseToAnyPublisher()
|
||||||
} else {
|
titleLocalizationComponents = Empty().eraseToAnyPublisher()
|
||||||
|
case .favorites:
|
||||||
title = Empty().eraseToAnyPublisher()
|
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) }
|
.sink { [weak self] in self?.setupViewControllers(pending: $0) }
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
viewModel.timelineNavigations.map { _ in }
|
viewModel.navigations
|
||||||
.merge(with: viewModel.followRequestNavigations.map { _ in })
|
.sink { [weak self] in self?.handle(navigation: $0) }
|
||||||
.sink { [weak self] in self?.selectedIndex = 0 }
|
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
NotificationCenter.default.publisher(for: UIScene.willEnterForegroundNotification)
|
NotificationCenter.default.publisher(for: UIScene.willEnterForegroundNotification)
|
||||||
|
@ -142,4 +141,29 @@ private extension MainNavigationViewController {
|
||||||
dismiss(animated: true)
|
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)
|
animated: !UIAccessibility.isReduceMotionEnabled)
|
||||||
},
|
},
|
||||||
for: .valueChanged)
|
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 final class NavigationViewModel: ObservableObject {
|
||||||
public let identityContext: IdentityContext
|
public let identityContext: IdentityContext
|
||||||
public let timelineNavigations: AnyPublisher<Timeline, Never>
|
public let navigations: AnyPublisher<Navigation, Never>
|
||||||
public let followRequestNavigations: AnyPublisher<CollectionViewModel, Never>
|
|
||||||
|
|
||||||
@Published public private(set) var recentIdentities = [Identity]()
|
@Published public private(set) var recentIdentities = [Identity]()
|
||||||
@Published public var presentingSecondaryNavigation = false
|
@Published public var presentingSecondaryNavigation = false
|
||||||
@Published public var alertItem: AlertItem?
|
@Published public var alertItem: AlertItem?
|
||||||
|
|
||||||
private let timelineNavigationsSubject = PassthroughSubject<Timeline, Never>()
|
private let navigationsSubject = PassthroughSubject<Navigation, Never>()
|
||||||
private let followRequestNavigationsSubject = PassthroughSubject<CollectionViewModel, Never>()
|
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
public init(identityContext: IdentityContext) {
|
public init(identityContext: IdentityContext) {
|
||||||
self.identityContext = identityContext
|
self.identityContext = identityContext
|
||||||
timelineNavigations = timelineNavigationsSubject.eraseToAnyPublisher()
|
navigations = navigationsSubject.eraseToAnyPublisher()
|
||||||
followRequestNavigations = followRequestNavigationsSubject.eraseToAnyPublisher()
|
|
||||||
|
|
||||||
identityContext.$identity
|
identityContext.$identity
|
||||||
.sink { [weak self] _ in self?.objectWillChange.send() }
|
.sink { [weak self] _ in self?.objectWillChange.send() }
|
||||||
|
@ -34,7 +31,7 @@ public final class NavigationViewModel: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension NavigationViewModel {
|
public extension NavigationViewModel {
|
||||||
enum Tab: CaseIterable {
|
enum Tab: Int, CaseIterable {
|
||||||
case timelines
|
case timelines
|
||||||
case explore
|
case explore
|
||||||
case notifications
|
case notifications
|
||||||
|
@ -95,25 +92,27 @@ public extension NavigationViewModel {
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func navigateToProfile(id: Account.Id) {
|
||||||
|
presentingSecondaryNavigation = false
|
||||||
|
navigationsSubject.send(.profile(identityContext.service.navigationService.profileService(id: id)))
|
||||||
|
}
|
||||||
|
|
||||||
func navigate(timeline: Timeline) {
|
func navigate(timeline: Timeline) {
|
||||||
presentingSecondaryNavigation = false
|
presentingSecondaryNavigation = false
|
||||||
timelineNavigationsSubject.send(timeline)
|
navigationsSubject.send(
|
||||||
|
.collection(identityContext.service.navigationService.timelineService(timeline: timeline)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func navigateToFollowerRequests() {
|
func navigateToFollowerRequests() {
|
||||||
let followRequestsViewModel = CollectionItemsViewModel(
|
|
||||||
collectionService: identityContext.service.service(
|
|
||||||
accountList: .followRequests,
|
|
||||||
titleComponents: ["follow-requests"]),
|
|
||||||
identityContext: identityContext)
|
|
||||||
|
|
||||||
presentingSecondaryNavigation = false
|
presentingSecondaryNavigation = false
|
||||||
followRequestNavigationsSubject.send(followRequestsViewModel)
|
navigationsSubject.send(.collection(identityContext.service.service(
|
||||||
|
accountList: .followRequests,
|
||||||
|
titleComponents: ["follow-requests"])))
|
||||||
}
|
}
|
||||||
|
|
||||||
func viewModel(timeline: Timeline) -> CollectionItemsViewModel {
|
func viewModel(timeline: Timeline) -> CollectionItemsViewModel {
|
||||||
CollectionItemsViewModel(
|
CollectionItemsViewModel(
|
||||||
collectionService: identityContext.service.service(timeline: timeline),
|
collectionService: identityContext.service.navigationService.timelineService(timeline: timeline),
|
||||||
identityContext: identityContext)
|
identityContext: identityContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,17 @@ struct SecondaryNavigationView: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
Section {
|
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(
|
NavigationLink(
|
||||||
destination: IdentitiesView(viewModel: .init(identityContext: viewModel.identityContext))
|
destination: IdentitiesView(viewModel: .init(identityContext: viewModel.identityContext))
|
||||||
.environmentObject(rootViewModel),
|
.environmentObject(rootViewModel),
|
||||||
|
|
Loading…
Reference in a new issue