// Copyright © 2021 Metabolist. All rights reserved. import Combine import SwiftUI import ViewModels final class MainNavigationViewController: UITabBarController { private let viewModel: NavigationViewModel private let rootViewModel: RootViewModel private var cancellables = Set() 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() viewModel.$presentingSecondaryNavigation.sink { [weak self] in if $0 { self?.presentSecondaryNavigation() } else { self?.dismissSecondaryNavigation() } } .store(in: &cancellables) viewModel.identityContext.$identity.map(\.pending) .removeDuplicates() .sink { [weak self] in self?.setupViewControllers(pending: $0) } .store(in: &cancellables) viewModel.navigations .sink { [weak self] in self?.handle(navigation: $0) } .store(in: &cancellables) NotificationCenter.default.publisher(for: UIScene.willEnterForegroundNotification) .sink { [weak self] _ in self?.viewModel.refreshIdentity() } .store(in: &cancellables) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) viewModel.refreshIdentity() } } private extension MainNavigationViewController { static let secondaryNavigationViewTag = UUID().hashValue func setupViewControllers(pending: Bool) { var controllers: [UIViewController] = [ TimelinesViewController( viewModel: viewModel, rootViewModel: rootViewModel) ] if viewModel.identityContext.identity.authenticated && !pending { tabBar.isHidden = false controllers.append(ExploreViewController(viewModel: viewModel.exploreViewModel(), rootViewModel: rootViewModel)) controllers.append(NotificationsViewController(viewModel: viewModel, rootViewModel: rootViewModel)) let conversationsViewController = TableViewController( viewModel: viewModel.conversationsViewModel(), rootViewModel: rootViewModel) conversationsViewController.tabBarItem = NavigationViewModel.Tab.messages.tabBarItem conversationsViewController.navigationItem.title = NavigationViewModel.Tab.messages.title controllers.append(conversationsViewController) setupNewStatusButton() } else { tabBar.isHidden = true } 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 } let newStatusViewModel = self.rootViewModel.newStatusViewModel( identityContext: self.viewModel.identityContext) let newStatusViewController = NewStatusViewController(viewModel: newStatusViewModel) let newStatusNavigationController = UINavigationController(rootViewController: newStatusViewController) if UIDevice.current.userInterfaceIdiom == .phone { newStatusNavigationController.modalPresentationStyle = .fullScreen } else { newStatusNavigationController.isModalInPresentation = true } self.present(newStatusNavigationController, animated: true) }) view.addSubview(newStatusButtonView) newStatusButtonView.translatesAutoresizingMaskIntoConstraints = false 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) 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() { 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 }) hostingController.navigationItem.titleView = SecondaryNavigationTitleView(viewModel: viewModel) let navigationController = UINavigationController(rootViewController: hostingController) navigationController.view.tag = Self.secondaryNavigationViewTag present(navigationController, animated: true) } func dismissSecondaryNavigation() { if presentedViewController?.view.tag == Self.secondaryNavigationViewTag { 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) } }