metatext-app-ios-iphone-ipad/View Controllers/ExploreViewController.swift

203 lines
7.3 KiB
Swift
Raw Normal View History

2021-01-23 04:48:33 +01:00
// Copyright © 2021 Metabolist. All rights reserved.
2021-01-25 08:42:39 +01:00
import Combine
2021-01-23 04:48:33 +01:00
import UIKit
import ViewModels
final class ExploreViewController: UICollectionViewController {
2021-03-03 07:55:35 +01:00
private let webfingerIndicatorView = WebfingerIndicatorView()
2021-01-23 04:48:33 +01:00
private let viewModel: ExploreViewModel
private let rootViewModel: RootViewModel
2021-01-25 08:42:39 +01:00
private var cancellables = Set<AnyCancellable>()
2021-01-23 04:48:33 +01:00
2021-01-30 23:27:49 +01:00
private lazy var dataSource: ExploreDataSource = {
.init(collectionView: collectionView, viewModel: viewModel)
}()
init(viewModel: ExploreViewModel, rootViewModel: RootViewModel) {
2021-01-23 04:48:33 +01:00
self.viewModel = viewModel
self.rootViewModel = rootViewModel
2021-01-30 23:27:49 +01:00
super.init(collectionViewLayout: Self.layout())
2021-01-23 04:48:33 +01:00
tabBarItem = UITabBarItem(
title: NSLocalizedString("main-navigation.explore", comment: ""),
image: UIImage(systemName: "magnifyingglass"),
selectedImage: nil)
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
2021-03-03 07:55:35 +01:00
// swiftlint:disable:next function_body_length
2021-01-23 04:48:33 +01:00
override func viewDidLoad() {
super.viewDidLoad()
2021-01-30 23:27:49 +01:00
collectionView.dataSource = dataSource
collectionView.backgroundColor = .systemBackground
collectionView.contentInset.bottom = Self.bottomInset
2021-02-02 04:39:32 +01:00
collectionView.isAccessibilityElement = false
collectionView.shouldGroupAccessibilityChildren = true
2021-01-30 23:27:49 +01:00
clearsSelectionOnViewWillAppear = true
collectionView.refreshControl = UIRefreshControl()
collectionView.refreshControl?.addAction(
UIAction { [weak self] _ in
self?.viewModel.refresh() },
for: .valueChanged)
2021-01-23 04:48:33 +01:00
navigationItem.title = NSLocalizedString("main-navigation.explore", comment: "")
2021-01-25 03:10:41 +01:00
let searchResultsController = TableViewController(
viewModel: viewModel.searchViewModel,
rootViewModel: rootViewModel,
insetBottom: false,
parentNavigationController: navigationController)
2021-01-23 04:48:33 +01:00
2021-01-25 03:10:41 +01:00
let searchController = UISearchController(searchResultsController: searchResultsController)
2021-01-23 04:48:33 +01:00
searchController.searchResultsUpdater = self
2021-02-17 03:27:28 +01:00
searchController.searchBar.keyboardType = .twitter
2021-01-23 04:48:33 +01:00
navigationItem.searchController = searchController
2021-01-25 08:42:39 +01:00
2021-03-03 07:55:35 +01:00
view.addSubview(webfingerIndicatorView)
webfingerIndicatorView.translatesAutoresizingMaskIntoConstraints = false
2021-01-31 02:43:48 +01:00
viewModel.identityContext.$appPreferences.sink { appPreferences in
searchController.searchBar.scopeButtonTitles = SearchScope.allCases.map {
$0.title(statusWord: appPreferences.statusWord)
}
}
.store(in: &cancellables)
2021-03-03 07:55:35 +01:00
NSLayoutConstraint.activate([
webfingerIndicatorView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
webfingerIndicatorView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor)
])
2021-01-30 23:27:49 +01:00
viewModel.events.sink { [weak self] in self?.handle(event: $0) }.store(in: &cancellables)
viewModel.$loading.sink { [weak self] in
guard let self = self else { return }
let refreshControlVisibile = self.collectionView.refreshControl?.isRefreshing ?? false
if !$0, refreshControlVisibile {
self.collectionView.refreshControl?.endRefreshing()
}
}
.store(in: &cancellables)
2021-02-09 02:19:25 +01:00
viewModel.searchViewModel.searchScopeChanges.sink { [weak self] in
searchController.searchBar.selectedScopeButtonIndex = $0.rawValue
self?.updateSearchResults(for: searchController)
2021-01-25 08:42:39 +01:00
}
.store(in: &cancellables)
2021-01-23 04:48:33 +01:00
}
2021-01-30 23:27:49 +01:00
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
viewModel.refresh()
}
override func collectionView(_ collectionView: UICollectionView,
shouldHighlightItemAt indexPath: IndexPath) -> Bool {
dataSource.itemIdentifier(for: indexPath) != .instance
}
2021-01-30 23:27:49 +01:00
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
guard let item = dataSource.itemIdentifier(for: indexPath) else { return }
viewModel.select(item: item)
}
2021-01-23 04:48:33 +01:00
}
extension ExploreViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
2021-01-25 08:42:39 +01:00
if let scope = SearchScope(rawValue: searchController.searchBar.selectedScopeButtonIndex) {
if scope != viewModel.searchViewModel.scope,
let scrollView = searchController.searchResultsController?.view as? UIScrollView {
scrollView.setContentOffset(.init(x: 0, y: -scrollView.safeAreaInsets.top), animated: false)
}
2021-01-25 03:10:41 +01:00
viewModel.searchViewModel.scope = scope
}
2021-01-23 04:48:33 +01:00
viewModel.searchViewModel.query = searchController.searchBar.text ?? ""
}
}
2021-01-30 23:27:49 +01:00
2021-02-06 02:16:59 +01:00
extension ExploreViewController: ScrollableToTop {
func scrollToTop(animated: Bool) {
collectionView.scrollToTop(animated: animated)
}
}
2021-03-03 07:55:35 +01:00
extension ExploreViewController: NavigationHandling {
func handle(navigation: Navigation) {
switch navigation {
case let .collection(collectionService):
let vc = TableViewController(
viewModel: CollectionItemsViewModel(
collectionService: collectionService,
identityContext: viewModel.identityContext),
rootViewModel: rootViewModel,
parentNavigationController: nil)
show(vc, sender: self)
webfingerIndicatorView.stopAnimating()
case let .profile(profileService):
let vc = ProfileViewController(
viewModel: ProfileViewModel(
profileService: profileService,
identityContext: viewModel.identityContext),
rootViewModel: rootViewModel,
identityContext: viewModel.identityContext,
parentNavigationController: nil)
show(vc, sender: self)
webfingerIndicatorView.stopAnimating()
case let .url(url):
open(url: url, identityContext: viewModel.identityContext)
webfingerIndicatorView.stopAnimating()
case .webfingerStart:
webfingerIndicatorView.startAnimating()
case .webfingerEnd:
webfingerIndicatorView.stopAnimating()
default:
break
}
}
}
2021-01-30 23:27:49 +01:00
private extension ExploreViewController {
static let bottomInset: CGFloat = .newStatusButtonDimension + .defaultSpacing * 4
2021-01-30 23:27:49 +01:00
static func layout() -> UICollectionViewLayout {
var listConfiguration = UICollectionLayoutListConfiguration(appearance: .plain)
listConfiguration.headerMode = .supplementary
return UICollectionViewCompositionalLayout(
sectionProvider: {
let section = NSCollectionLayoutSection.list(using: listConfiguration, layoutEnvironment: $1)
2021-01-30 23:27:49 +01:00
if UIDevice.current.userInterfaceIdiom == .pad {
section.contentInsetsReference = .readableContent
}
2021-01-30 23:27:49 +01:00
return section
})
2021-01-30 23:27:49 +01:00
}
func handle(event: ExploreViewModel.Event) {
switch event {
case let .navigation(navigation):
handle(navigation: navigation)
}
}
}