metatext/View Controllers/MainNavigationViewController.swift

240 lines
9 KiB
Swift
Raw Permalink Normal View History

2021-01-20 23:33:53 +00:00
// Copyright © 2021 Metabolist. All rights reserved.
2021-01-21 08:45:09 +00:00
import Combine
import SwiftUI
2021-01-20 23:33:53 +00:00
import ViewModels
final class MainNavigationViewController: UITabBarController {
private let viewModel: NavigationViewModel
private let rootViewModel: RootViewModel
2021-01-21 08:45:09 +00:00
private var cancellables = Set<AnyCancellable>()
2021-01-20 23:33:53 +00:00
init(viewModel: NavigationViewModel, rootViewModel: RootViewModel) {
self.viewModel = viewModel
self.rootViewModel = rootViewModel
super.init(nibName: nil, bundle: nil)
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
2021-02-06 01:16:59 +00:00
delegate = self
2021-02-04 21:33:29 +00:00
viewModel.$presentedNewStatusViewModel.sink { [weak self] in
if let newStatusViewModel = $0 {
self?.presentNewStatus(newStatusViewModel: newStatusViewModel)
} else {
self?.dismissNewStatus()
}
}
.store(in: &cancellables)
2021-02-05 19:10:01 +00:00
viewModel.$presentingSecondaryNavigation.sink { [weak self] in
2021-01-21 08:45:09 +00:00
if $0 {
self?.presentSecondaryNavigation()
} else {
self?.dismissSecondaryNavigation()
}
}
.store(in: &cancellables)
2021-01-27 22:23:52 +00:00
viewModel.identityContext.$identity.map(\.pending)
.removeDuplicates()
.sink { [weak self] in self?.setupViewControllers(pending: $0) }
.store(in: &cancellables)
2021-01-27 23:49:13 +00:00
viewModel.navigations
2021-03-03 06:55:35 +00:00
.receive(on: DispatchQueue.main)
2021-01-27 23:49:13 +00:00
.sink { [weak self] in self?.handle(navigation: $0) }
2021-01-21 08:45:09 +00:00
.store(in: &cancellables)
2021-01-27 22:23:52 +00:00
NotificationCenter.default.publisher(for: UIScene.willEnterForegroundNotification)
.sink { [weak self] _ in self?.viewModel.refreshIdentity() }
.store(in: &cancellables)
2021-01-21 08:45:09 +00:00
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
viewModel.refreshIdentity()
}
}
2021-02-06 01:16:59 +00:00
extension MainNavigationViewController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController,
shouldSelect viewController: UIViewController) -> Bool {
if viewController === selectedViewController,
let navigationController = viewController as? UINavigationController,
navigationController.viewControllers.count == 1 {
(navigationController.viewControllers.first as? ScrollableToTop)?.scrollToTop(animated: true)
}
return true
}
}
2021-03-03 06:55:35 +00:00
extension MainNavigationViewController: NavigationHandling {
func handle(navigation: Navigation) {
switch navigation {
case .notification:
let index = NavigationViewModel.Tab.notifications.rawValue
guard let viewControllers = viewControllers,
viewControllers.count > index,
let notificationsNavigationController = viewControllers[index] as? UINavigationController,
let notificationsViewController =
notificationsNavigationController.viewControllers.first as? NotificationsViewController
else { break }
selectedIndex = index
notificationsNavigationController.popToRootViewController(animated: false)
notificationsViewController.handle(navigation: navigation)
default:
((selectedViewController as? UINavigationController)?
.topViewController as? NavigationHandling)?
.handle(navigation: navigation)
}
}
}
2021-01-21 08:45:09 +00:00
private extension MainNavigationViewController {
2021-01-28 03:58:23 +00:00
static let secondaryNavigationViewTag = UUID().hashValue
2021-02-04 21:33:29 +00:00
static let newStatusViewTag = UUID().hashValue
2021-01-28 03:58:23 +00:00
2021-01-27 22:23:52 +00:00
func setupViewControllers(pending: Bool) {
2021-01-23 03:48:33 +00:00
var controllers: [UIViewController] = [
TimelinesViewController(
viewModel: viewModel,
rootViewModel: rootViewModel)
2021-01-23 03:48:33 +00:00
]
2021-01-21 08:45:09 +00:00
2021-01-27 22:23:52 +00:00
if viewModel.identityContext.identity.authenticated && !pending {
2021-01-29 06:14:48 +00:00
tabBar.isHidden = false
controllers.append(ExploreViewController(viewModel: viewModel.exploreViewModel(),
rootViewModel: rootViewModel))
2021-01-26 02:10:24 +00:00
controllers.append(NotificationsViewController(viewModel: viewModel, rootViewModel: rootViewModel))
2021-01-20 23:33:53 +00:00
let conversationsViewController = TableViewController(
2021-01-27 22:23:52 +00:00
viewModel: viewModel.conversationsViewModel(),
rootViewModel: rootViewModel)
2021-01-20 23:33:53 +00:00
conversationsViewController.tabBarItem = NavigationViewModel.Tab.messages.tabBarItem
conversationsViewController.navigationItem.title = NavigationViewModel.Tab.messages.title
2021-01-21 08:45:09 +00:00
controllers.append(conversationsViewController)
2021-01-27 22:23:52 +00:00
setupNewStatusButton()
2021-01-29 06:14:48 +00:00
} else {
tabBar.isHidden = true
2021-01-21 08:45:09 +00:00
}
let secondaryNavigationButton = SecondaryNavigationButton(viewModel: viewModel, rootViewModel: rootViewModel)
for controller in controllers {
controller.navigationItem.leftBarButtonItem = secondaryNavigationButton
}
viewControllers = controllers.map(UINavigationController.init(rootViewController:))
}
func setupNewStatusButton() {
let newStatusButtonView = NewStatusButtonView(primaryAction: UIAction { [weak self] _ in
guard let self = self else { return }
2021-02-04 21:33:29 +00:00
self.viewModel.presentedNewStatusViewModel =
self.rootViewModel.newStatusViewModel(identityContext: self.viewModel.identityContext)
2021-01-21 08:45:09 +00:00
})
view.addSubview(newStatusButtonView)
newStatusButtonView.translatesAutoresizingMaskIntoConstraints = false
2021-02-01 19:53:47 +00:00
viewModel.identityContext.$appPreferences.map(\.statusWord).removeDuplicates().sink {
switch $0 {
case .toot:
newStatusButtonView.button.accessibilityLabel =
NSLocalizedString("compose-button.accessibility-label.toot", comment: "")
case.post:
newStatusButtonView.button.accessibilityLabel =
NSLocalizedString("compose-button.accessibility-label.post", comment: "")
}
}
.store(in: &cancellables)
2021-01-21 08:45:09 +00:00
NSLayoutConstraint.activate([
newStatusButtonView.widthAnchor.constraint(equalToConstant: .newStatusButtonDimension),
newStatusButtonView.heightAnchor.constraint(equalToConstant: .newStatusButtonDimension),
newStatusButtonView.trailingAnchor.constraint(
equalTo: view.safeAreaLayoutGuide.trailingAnchor,
constant: -.defaultSpacing * 2),
newStatusButtonView.bottomAnchor.constraint(equalTo: tabBar.topAnchor, constant: -.defaultSpacing * 2)
])
}
func presentSecondaryNavigation() {
2021-02-05 19:10:01 +00:00
if let presentedViewController = presentedViewController {
if presentedViewController.view.tag == Self.secondaryNavigationViewTag {
return
} else {
dismiss(animated: true)
}
}
2021-01-21 08:45:09 +00:00
let secondaryNavigationView = SecondaryNavigationView(viewModel: viewModel)
.environmentObject(rootViewModel)
let hostingController = UIHostingController(rootView: secondaryNavigationView)
hostingController.navigationItem.leftBarButtonItem = UIBarButtonItem(
systemItem: .close,
primaryAction: UIAction { [weak self] _ in self?.viewModel.presentingSecondaryNavigation = false })
2021-01-28 01:13:17 +00:00
hostingController.navigationItem.titleView = SecondaryNavigationTitleView(viewModel: viewModel)
2021-01-21 08:45:09 +00:00
let navigationController = UINavigationController(rootViewController: hostingController)
2021-01-28 03:58:23 +00:00
navigationController.view.tag = Self.secondaryNavigationViewTag
2021-01-21 08:45:09 +00:00
present(navigationController, animated: true)
}
func dismissSecondaryNavigation() {
2021-01-28 03:58:23 +00:00
if presentedViewController?.view.tag == Self.secondaryNavigationViewTag {
2021-01-21 08:45:09 +00:00
dismiss(animated: true)
2021-01-20 23:33:53 +00:00
}
}
2021-01-27 23:49:13 +00:00
2021-02-04 21:33:29 +00:00
func presentNewStatus(newStatusViewModel: NewStatusViewModel) {
2021-02-05 19:10:01 +00:00
if let presentedViewController = presentedViewController {
if presentedViewController.view.tag == Self.newStatusViewTag {
return
} else {
dismiss(animated: true)
}
}
2021-02-04 21:33:29 +00:00
let newStatusViewController = NewStatusViewController(viewModel: newStatusViewModel,
rootViewModel: rootViewModel)
let navigationController = UINavigationController(rootViewController: newStatusViewController)
if UIDevice.current.userInterfaceIdiom == .phone {
navigationController.modalPresentationStyle = .overFullScreen
} else {
navigationController.isModalInPresentation = true
}
navigationController.view.tag = Self.newStatusViewTag
present(navigationController, animated: true)
}
func dismissNewStatus() {
if presentedViewController?.view.tag == Self.newStatusViewTag {
dismiss(animated: true)
}
}
2021-01-20 23:33:53 +00:00
}