element-ios/Riot/Modules/Rooms/ShowDirectory/ShowDirectoryViewController.swift

378 lines
13 KiB
Swift
Raw Normal View History

2020-09-10 09:50:26 +00:00
// File created from ScreenTemplate
2020-09-10 11:56:46 +00:00
// $ createScreen.sh Rooms/ShowDirectory ShowDirectory
2020-09-10 09:50:26 +00:00
/*
Copyright 2020 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import UIKit
final class ShowDirectoryViewController: UIViewController {
// MARK: - Constants
private enum Constants {
static let networkHeaderViewEstimatedHeight: CGFloat = 40.0
}
2020-09-10 09:50:26 +00:00
// MARK: - Properties
// MARK: Outlets
2020-09-10 11:56:46 +00:00
@IBOutlet private weak var mainTableView: UITableView!
@IBOutlet private weak var vibrancyEffectView: UIVisualEffectView!
@IBOutlet private weak var vibrancyEffectContentView: UIView!
@IBOutlet private weak var blurEffectView: UIVisualEffectView!
@IBOutlet private weak var blurEffectContentView: UIView!
2020-09-10 11:56:46 +00:00
@IBOutlet private weak var createRoomButton: UIButton! {
didSet {
createRoomButton.setTitle(VectorL10n.searchableDirectoryCreateNewRoom, for: .normal)
}
}
2020-09-10 09:50:26 +00:00
// MARK: Private
private var viewModel: ShowDirectoryViewModelType!
private var theme: Theme!
private var keyboardAvoider: KeyboardAvoider?
private var errorPresenter: MXKErrorPresentation!
private var activityPresenter: ActivityIndicatorPresenter!
2020-09-10 11:56:46 +00:00
private lazy var footerSpinnerView: UIActivityIndicatorView = {
let spinner = UIActivityIndicatorView(style: .whiteLarge)
spinner.transform = CGAffineTransform(scaleX: 0.75, y: 0.75)
spinner.color = .darkGray
spinner.hidesWhenStopped = false
spinner.backgroundColor = .clear
spinner.startAnimating()
return spinner
}()
private lazy var mainSearchBar: UISearchBar = {
let bar = UISearchBar(frame: CGRect(origin: .zero, size: CGSize(width: 600, height: 44)))
bar.autoresizingMask = .flexibleWidth
bar.showsCancelButton = false
bar.placeholder = VectorL10n.searchableDirectorySearchPlaceholder
bar.setBackgroundImage(UIImage.vc_image(from: .clear), for: .any, barMetrics: .default)
bar.delegate = self
return bar
}()
private var sections: [ShowDirectorySection] = []
2020-09-10 09:50:26 +00:00
// MARK: - Setup
class func instantiate(with viewModel: ShowDirectoryViewModelType) -> ShowDirectoryViewController {
let viewController = StoryboardScene.ShowDirectoryViewController.initialScene.instantiate()
viewController.viewModel = viewModel
viewController.theme = ThemeService.shared().theme
return viewController
}
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.setupViews()
2020-09-10 11:56:46 +00:00
self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.mainTableView)
2020-09-10 09:50:26 +00:00
self.activityPresenter = ActivityIndicatorPresenter()
self.errorPresenter = MXKErrorAlertPresentation()
self.registerThemeServiceDidChangeThemeNotification()
self.update(theme: self.theme)
self.viewModel.viewDelegate = self
2020-09-10 11:56:46 +00:00
self.viewModel.process(viewAction: .loadData(false))
2020-09-10 09:50:26 +00:00
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.keyboardAvoider?.startAvoiding()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.keyboardAvoider?.stopAvoiding()
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return self.theme.statusBarStyle
}
// MARK: - Private
2020-09-10 11:56:46 +00:00
private func addSpinnerFooterView() {
footerSpinnerView.startAnimating()
self.mainTableView.tableFooterView = footerSpinnerView
}
private func removeSpinnerFooterView() {
footerSpinnerView.stopAnimating()
self.mainTableView.tableFooterView = UIView()
}
2020-09-10 09:50:26 +00:00
private func update(theme: Theme) {
self.theme = theme
self.view.backgroundColor = theme.headerBackgroundColor
2020-09-10 11:56:46 +00:00
self.mainTableView.backgroundColor = theme.backgroundColor
self.mainTableView.separatorColor = theme.lineBreakColor
2020-09-10 09:50:26 +00:00
if let navigationBar = self.navigationController?.navigationBar {
theme.applyStyle(onNavigationBar: navigationBar)
2020-09-10 11:56:46 +00:00
navigationBar.setBackgroundImage(UIImage.vc_image(from: theme.headerBackgroundColor), for: .default)
2020-09-10 09:50:26 +00:00
}
2020-09-10 11:56:46 +00:00
theme.applyStyle(onSearchBar: mainSearchBar)
theme.applyStyle(onButton: createRoomButton)
if #available(iOS 13.0, *) {
vibrancyEffectView.overrideUserInterfaceStyle = theme.userInterfaceStyle
vibrancyEffectContentView.overrideUserInterfaceStyle = theme.userInterfaceStyle
blurEffectView.overrideUserInterfaceStyle = theme.userInterfaceStyle
blurEffectContentView.overrideUserInterfaceStyle = theme.userInterfaceStyle
}
2020-09-10 11:56:46 +00:00
self.mainTableView.reloadData()
2020-09-10 09:50:26 +00:00
}
private func registerThemeServiceDidChangeThemeNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
}
@objc private func themeDidChange() {
self.update(theme: ThemeService.shared().theme)
}
private func setupViews() {
2020-09-10 11:56:46 +00:00
self.mainTableView.keyboardDismissMode = .interactive
self.mainTableView.register(headerFooterViewType: DirectoryNetworkTableHeaderFooterView.self)
self.mainTableView.register(cellType: DirectoryRoomTableViewCell.self)
self.mainTableView.rowHeight = 76
self.mainTableView.tableFooterView = UIView()
2020-09-10 09:50:26 +00:00
let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in
self?.cancelButtonAction()
}
self.navigationItem.rightBarButtonItem = cancelBarButtonItem
2020-09-10 11:56:46 +00:00
self.navigationItem.titleView = mainSearchBar
2020-09-10 09:50:26 +00:00
}
private func render(viewState: ShowDirectoryViewState) {
switch viewState {
case .loading:
self.renderLoading()
case .loaded(let sections):
self.renderLoaded(sections: sections)
2020-09-10 09:50:26 +00:00
case .error(let error):
self.render(error: error)
case .loadedWithoutUpdate:
self.renderLoadedWithoutUpdate()
2020-09-10 09:50:26 +00:00
}
}
private func renderLoading() {
2020-09-10 11:56:46 +00:00
addSpinnerFooterView()
2020-09-10 09:50:26 +00:00
}
private func renderLoaded(sections: [ShowDirectorySection]) {
removeSpinnerFooterView()
self.sections = sections
self.mainTableView.reloadData()
}
private func renderLoadedWithoutUpdate() {
2020-09-10 11:56:46 +00:00
removeSpinnerFooterView()
2020-09-10 09:50:26 +00:00
}
private func render(error: Error) {
2020-09-10 11:56:46 +00:00
removeSpinnerFooterView()
self.errorPresenter.presentError(from: self, forError: error, animated: true) {
// If the join failed, reload the table view
self.mainTableView.reloadData()
}
2020-09-10 09:50:26 +00:00
}
// MARK: - Actions
2020-09-10 11:56:46 +00:00
@IBAction private func createRoomButtonTapped(_ sender: UIButton) {
viewModel.process(viewAction: .createNewRoom)
2020-09-10 09:50:26 +00:00
}
private func cancelButtonAction() {
self.viewModel.process(viewAction: .cancel)
}
}
2020-09-10 11:56:46 +00:00
// MARK: - UITableViewDataSource
extension ShowDirectoryViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return self.sections.count
2020-09-10 11:56:46 +00:00
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let directorySection = self.sections[section]
switch directorySection {
case .searchInput:
return 1
case.publicRoomsDirectory(let viewModel):
return viewModel.roomsCount
}
2020-09-10 11:56:46 +00:00
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let section = self.sections[indexPath.section]
let cellViewModel: DirectoryRoomTableViewCellVM?
switch section {
case .searchInput(let searchInputViewData):
cellViewModel = searchInputViewData
case.publicRoomsDirectory(let viewModel):
cellViewModel = viewModel.roomViewModel(at: indexPath.row)
}
2020-09-10 11:56:46 +00:00
let cell: DirectoryRoomTableViewCell = tableView.dequeueReusableCell(for: indexPath)
if let cellViewModel = cellViewModel {
cell.configure(withViewModel: cellViewModel)
2020-09-10 11:56:46 +00:00
}
cell.indexPath = indexPath
cell.delegate = self
cell.update(theme: self.theme)
return cell
}
}
// MARK: - UITableViewDataDelegate
extension ShowDirectoryViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.backgroundColor = theme.backgroundColor
// Update the selected background view
cell.selectedBackgroundView = UIView()
cell.selectedBackgroundView?.backgroundColor = theme.selectedBackgroundColor
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
viewModel.process(viewAction: .selectRoom(indexPath))
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// Trigger inconspicuous pagination when user scrolls down
if (scrollView.contentSize.height - scrollView.contentOffset.y - scrollView.frame.size.height) < 300 {
viewModel.process(viewAction: .loadData(false))
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let sectionHeaderView: UIView?
let directorySection = self.sections[section]
switch directorySection {
case .searchInput:
sectionHeaderView = nil
case .publicRoomsDirectory(let viewModel):
guard let view: DirectoryNetworkTableHeaderFooterView = tableView.dequeueReusableHeaderFooterView() else {
return nil
}
if let name = viewModel.directoryServerDisplayname {
let title = VectorL10n.searchableDirectoryXNetwork(name)
view.configure(withViewModel: DirectoryNetworkVM(title: title))
}
view.update(theme: self.theme)
view.delegate = self
sectionHeaderView = view
2020-09-10 11:56:46 +00:00
}
return sectionHeaderView
2020-09-10 11:56:46 +00:00
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return UITableView.automaticDimension
2020-09-10 11:56:46 +00:00
}
func tableView(_ tableView: UITableView,
estimatedHeightForHeaderInSection section: Int) -> CGFloat {
let directorySection = self.sections[section]
let estimatedHeight: CGFloat
switch directorySection {
case .searchInput:
estimatedHeight = 0.0
case .publicRoomsDirectory:
estimatedHeight = Constants.networkHeaderViewEstimatedHeight
}
return estimatedHeight
}
2020-09-10 11:56:46 +00:00
}
// MARK: - UISearchBarDelegate
extension ShowDirectoryViewController {
override func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
viewModel.process(viewAction: .search(searchText))
}
}
// MARK: -
extension ShowDirectoryViewController: DirectoryRoomTableViewCellDelegate {
func directoryRoomTableViewCellDidTapJoin(_ cell: DirectoryRoomTableViewCell) {
cell.startJoining()
viewModel.process(viewAction: .joinRoom(cell.indexPath))
}
}
// MARK: - DirectoryNetworkTableHeaderFooterViewDelegate
extension ShowDirectoryViewController: DirectoryNetworkTableHeaderFooterViewDelegate {
func directoryNetworkTableHeaderFooterViewDidTapSwitch(_ view: DirectoryNetworkTableHeaderFooterView) {
viewModel.process(viewAction: .switchServer)
}
}
2020-09-10 09:50:26 +00:00
// MARK: - ShowDirectoryViewModelViewDelegate
extension ShowDirectoryViewController: ShowDirectoryViewModelViewDelegate {
func showDirectoryViewModel(_ viewModel: ShowDirectoryViewModelType, didUpdateViewState viewSate: ShowDirectoryViewState) {
self.render(viewState: viewSate)
}
}