Files
bundesmessenger-ios/Riot/Modules/Rooms/SearchableDirectory/SearchableDirectoryViewController.swift
T
2020-09-09 15:05:06 +03:00

330 lines
12 KiB
Swift

// File created from simpleScreenTemplate
// $ createSimpleScreen.sh Rooms2 SearchableDirectory
/*
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
@objc protocol SearchableDirectoryViewControllerDelegate: NSObjectProtocol {
func searchableDirectoryViewControllerDidCancel(_ viewController: SearchableDirectoryViewController)
func searchableDirectoryViewControllerDidSelect(_ viewController: SearchableDirectoryViewController, room: MXPublicRoom)
func searchableDirectoryViewControllerDidTapCreateNewRoom(_ viewController: SearchableDirectoryViewController)
}
final class SearchableDirectoryViewController: MXKViewController {
// MARK: - Properties
// MARK: Outlets
@IBOutlet private weak var mainTableView: UITableView!
@IBOutlet private weak var createRoomButton: UIButton! {
didSet {
createRoomButton.setTitle(VectorL10n.searchableDirectoryCreateNewRoom, for: .normal)
}
}
// MARK: Private
private var theme: Theme!
private var dataSource: PublicRoomsDirectoryDataSource!
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
}()
// MARK: Public
@objc weak var delegate: SearchableDirectoryViewControllerDelegate?
@objc func display(withDataSource dataSource: PublicRoomsDirectoryDataSource) {
self.dataSource = dataSource
self.dataSource.delegate = self
if isViewLoaded {
self.mainTableView.reloadData()
}
}
// MARK: - Setup
@objc class func instantiate(withSession session: MXSession) -> SearchableDirectoryViewController {
let viewController = StoryboardScene.SearchableDirectoryViewController.initialScene.instantiate()
viewController.theme = ThemeService.shared().theme
viewController.addMatrixSession(session)
return viewController
}
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.title = "Template"
self.vc_removeBackTitle()
self.setupViews()
self.registerThemeServiceDidChangeThemeNotification()
self.update(theme: self.theme)
self.mainTableView.tableFooterView = UIView()
triggerPagination()
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return self.theme.statusBarStyle
}
// MARK: - Private
private func setupViews() {
self.mainTableView.register(headerFooterViewType: DirectoryNetworkTableHeaderFooterView.self)
self.mainTableView.register(cellType: DirectoryRoomTableViewCell.self)
self.mainTableView.rowHeight = 76
let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in
self?.cancelButtonAction()
}
self.navigationItem.rightBarButtonItem = cancelBarButtonItem
self.navigationItem.titleView = mainSearchBar
}
private func update(theme: Theme) {
self.theme = theme
self.view.backgroundColor = theme.backgroundColor
self.mainTableView.backgroundColor = theme.backgroundColor
self.mainTableView.separatorColor = theme.lineBreakColor
if let navigationBar = self.navigationController?.navigationBar {
theme.applyStyle(onNavigationBar: navigationBar)
navigationBar.setBackgroundImage(UIImage.vc_image(from: theme.headerBackgroundColor), for: .default)
}
theme.applyStyle(onSearchBar: mainSearchBar)
theme.applyStyle(onButton: createRoomButton)
self.mainTableView.reloadData()
}
private func registerThemeServiceDidChangeThemeNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
}
private func addSpinnerFooterView() {
footerSpinnerView.startAnimating()
self.mainTableView.tableFooterView = footerSpinnerView
}
private func removeSpinnerFooterView() {
footerSpinnerView.stopAnimating()
self.mainTableView.tableFooterView = UIView()
}
private func triggerPagination(force: Bool = false) {
if !force && (dataSource.hasReachedPaginationEnd || footerSpinnerView.superview != nil) {
// We got all public rooms or we are already paginating
// Do nothing
return
}
self.addSpinnerFooterView()
dataSource.paginate({ [weak self] (roomsAdded) in
guard let self = self else { return }
if roomsAdded > 0 {
self.mainTableView.reloadData()
}
self.removeSpinnerFooterView()
}, failure: { [weak self] (error) in
guard let self = self else { return }
self.removeSpinnerFooterView()
})
}
// MARK: - Override
override func addMatrixSession(_ mxSession: MXSession!) {
super.addMatrixSession(mxSession)
if dataSource == nil {
display(withDataSource: PublicRoomsDirectoryDataSource(matrixSession: mxSession))
}
}
// MARK: - Actions
@objc private func themeDidChange() {
self.update(theme: ThemeService.shared().theme)
}
private func cancelButtonAction() {
self.delegate?.searchableDirectoryViewControllerDidCancel(self)
}
@IBAction private func createRoomButtonTapped(_ sender: UIButton) {
self.delegate?.searchableDirectoryViewControllerDidTapCreateNewRoom(self)
}
}
// MARK: - UITableViewDataSource
extension SearchableDirectoryViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Int(dataSource?.roomsCount ?? 0)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: DirectoryRoomTableViewCell = tableView.dequeueReusableCell(for: indexPath)
if let room = dataSource.room(at: indexPath) {
cell.configure(withRoom: room, session: dataSource.mxSession)
}
cell.update(theme: self.theme)
return cell
}
}
// MARK: - UITableViewDataDelegate
extension SearchableDirectoryViewController: 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)
guard let room = dataSource.room(at: indexPath) else { return }
delegate?.searchableDirectoryViewControllerDidSelect(self, room: room)
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// Trigger inconspicuous pagination when user scrolls down
if (scrollView.contentSize.height - scrollView.contentOffset.y - scrollView.frame.size.height) < 300 {
self.triggerPagination()
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
guard let view: DirectoryNetworkTableHeaderFooterView = tableView.dequeueReusableHeaderFooterView() else {
return nil
}
if let name = self.dataSource.directoryServerDisplayname {
let title = VectorL10n.searchableDirectoryXNetwork(name)
view.configure(withViewModel: DirectoryNetworkVM(title: title))
}
view.update(theme: self.theme)
view.delegate = self
return view
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 40
}
}
// MARK: - UISearchBarDelegate
extension SearchableDirectoryViewController {
override func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
dataSource.searchPattern = searchText
triggerPagination(force: true)
}
}
// MARK: - MXKDataSourceDelegate
extension SearchableDirectoryViewController: MXKDataSourceDelegate {
func cellViewClass(for cellData: MXKCellData!) -> MXKCellRendering.Type! {
return nil
}
func cellReuseIdentifier(for cellData: MXKCellData!) -> String! {
return nil
}
func dataSource(_ dataSource: MXKDataSource!, didCellChange changes: Any!) {
}
func dataSource(_ dataSource: MXKDataSource!, didStateChange state: MXKDataSourceState) {
self.mainTableView.reloadData()
}
}
// MARK: - DirectoryNetworkTableHeaderFooterViewDelegate
extension SearchableDirectoryViewController: DirectoryNetworkTableHeaderFooterViewDelegate {
func directoryNetworkTableHeaderFooterViewDidTapSwitch(_ view: DirectoryNetworkTableHeaderFooterView) {
let controller = DirectoryServerPickerViewController()
let source = MXKDirectoryServersDataSource(matrixSession: self.mainSession)
source?.finalizeInitialization()
source?.roomDirectoryServers = BuildSettings.publicRoomsDirectoryServers
controller.display(with: source) { [weak self] (cellData) in
guard let self = self else { return }
guard let cellData = cellData else { return }
if let thirdpartyProtocolInstance = cellData.thirdPartyProtocolInstance {
self.dataSource.thirdpartyProtocolInstance = thirdpartyProtocolInstance
} else if let homeserver = cellData.homeserver {
self.dataSource.includeAllNetworks = cellData.includeAllNetworks
self.dataSource.homeserver = homeserver
}
self.triggerPagination()
}
let navController = RiotNavigationController(rootViewController: controller)
self.navigationController?.present(navController, animated: true, completion: nil)
}
}