element-ios/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift

272 lines
9.7 KiB
Swift

//
// Copyright 2021-2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
//
import Foundation
class SpaceMenuViewController: UIViewController {
// MARK: - Constants
private enum Constants {
static let estimatedRowHeight: CGFloat = 64.0
}
// MARK: Private
private var theme: Theme!
private var session: MXSession!
private var spaceId: String!
private var viewModel: SpaceMenuViewModelType!
private var errorPresenter: MXKErrorPresentation!
private var activityPresenter: ActivityIndicatorPresenter!
// MARK: Outlets
@IBOutlet private weak var avatarView: SpaceAvatarView!
@IBOutlet private weak var titleLabel: UILabel!
@IBOutlet private weak var subtitleLabel: UILabel!
@IBOutlet private weak var closeButton: UIButton!
@IBOutlet private weak var tableView: UITableView!
@IBOutlet private weak var bottomMargin: NSLayoutConstraint!
// MARK: - Setup
class func instantiate(forSpaceWithId spaceId: String, matrixSession: MXSession, viewModel: SpaceMenuViewModelType!) -> SpaceMenuViewController {
let viewController = StoryboardScene.SpaceMenuViewController.initialScene.instantiate()
viewController.session = matrixSession
viewController.spaceId = spaceId
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()
self.activityPresenter = ActivityIndicatorPresenter()
self.errorPresenter = MXKErrorAlertPresentation()
self.registerThemeServiceDidChangeThemeNotification()
self.update(theme: self.theme)
self.viewModel.viewDelegate = self
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
AnalyticsScreenTracker.trackScreen(.spaceMenu)
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return self.theme.statusBarStyle
}
override var preferredContentSize: CGSize {
get {
return CGSize(width: 320, height: self.tableView.frame.minY + Constants.estimatedRowHeight * CGFloat(self.viewModel.menuItems.count) + self.bottomMargin.constant)
}
set {
super.preferredContentSize = newValue
}
}
// MARK: - IBActions
@IBAction private func closeAction(sender: UIButton) {
self.viewModel.process(viewAction: .dismiss)
}
// MARK: - Private
private func update(theme: Theme) {
self.theme = theme
self.view.backgroundColor = theme.colors.background
self.titleLabel.textColor = theme.colors.primaryContent
self.titleLabel.font = theme.fonts.title3SB
self.subtitleLabel.textColor = theme.colors.secondaryContent
self.subtitleLabel.font = theme.fonts.caption1
self.closeButton.backgroundColor = theme.roomInputTextBorder
self.closeButton.tintColor = theme.noticeSecondaryColor
if self.spaceId == SpaceListViewModel.Constants.homeSpaceId {
let defaultAsset = ThemeService.shared().isCurrentThemeDark() ? Asset.Images.spaceHomeIconDark : Asset.Images.spaceHomeIconLight
let avatarViewData = AvatarViewData(matrixItemId: self.spaceId, displayName: nil, avatarUrl: nil, mediaManager: session.mediaManager, fallbackImage: .image(defaultAsset.image, .center))
self.avatarView.fill(with: avatarViewData)
}
}
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() {
setupTableView()
if self.spaceId == SpaceListViewModel.Constants.homeSpaceId {
self.titleLabel.text = VectorL10n.titleHome
self.subtitleLabel.text = VectorL10n.settingsTitle
return
}
guard let space = self.session.spaceService.getSpace(withId: self.spaceId), let summary = space.summary else {
MXLog.error("[SpaceMenuViewController] setupViews: no space found")
return
}
let avatarViewData = AvatarViewData(matrixItemId: summary.roomId, displayName: summary.displayName, avatarUrl: summary.avatar, mediaManager: self.session.mediaManager, fallbackImage: .matrixItem(summary.roomId, summary.displayName))
self.titleLabel.text = space.summary?.displayName
// TODO: display members instead once done on android
// self.subtitleLabel.text = space.membersId.count == 1 ? VectorL10n.roomTitleOneMember :
// VectorL10n.roomTitleMembers("\(space.membersId.count)")
self.subtitleLabel.text = summary.topic
self.avatarView.fill(with: avatarViewData)
self.closeButton.layer.masksToBounds = true
self.closeButton.layer.cornerRadius = self.closeButton.bounds.height / 2
}
private func setupTableView() {
self.tableView.separatorStyle = .none
self.tableView.rowHeight = UITableView.automaticDimension
self.tableView.estimatedRowHeight = Constants.estimatedRowHeight
self.tableView.allowsSelection = true
self.tableView.register(cellType: SpaceMenuListViewCell.self)
self.tableView.register(cellType: SpaceMenuSwitchViewCell.self)
self.tableView.tableFooterView = UIView()
}
private func render(viewState: SpaceMenuViewState) {
switch viewState {
case .loading:
self.renderLoading()
case .loaded:
self.renderLoaded()
case .leaveOptions(let displayName, let isAdmin):
self.renderLeaveOptions(displayName: displayName, isAdmin: isAdmin)
case .error(let error):
self.render(error: error)
case .deselect:
self.renderDeselect()
}
}
private func renderLoading() {
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
}
private func renderLoaded() {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
self.renderDeselect()
}
private func renderLeaveOptions(displayName: String, isAdmin: Bool) {
var message = VectorL10n.leaveSpaceMessage(displayName)
if isAdmin {
message += "\n\n" + VectorL10n.leaveSpaceMessageAdminWarning
}
let alert = UIAlertController(title: VectorL10n.leaveSpaceTitle(displayName), message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: VectorL10n.leaveSpaceOnlyAction, style: .default, handler: { [weak self] action in
self?.viewModel.process(viewAction: .leaveSpaceAndKeepRooms)
}))
alert.addAction(UIAlertAction(title: VectorL10n.leaveSpaceAndAllRoomsAction, style: .destructive, handler: { [weak self] action in
self?.viewModel.process(viewAction: .leaveSpaceAndLeaveRooms)
}))
alert.addAction(UIAlertAction(title: VectorL10n.cancel, style: .cancel, handler: { [weak self] action in
self?.renderDeselect()
}))
self.present(alert, animated: true, completion: nil)
}
private func render(error: Error) {
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
}
private func renderDeselect() {
if let selectedRow = self.tableView.indexPathForSelectedRow {
self.tableView.deselectRow(at: selectedRow, animated: true)
}
}
}
// MARK: - SlidingModalPresentable
extension SpaceMenuViewController: SlidingModalPresentable {
func allowsDismissOnBackgroundTap() -> Bool {
return true
}
func layoutHeightFittingWidth(_ width: CGFloat) -> CGFloat {
return self.preferredContentSize.height
}
}
// MARK: - SpaceMenuViewModelViewDelegate
extension SpaceMenuViewController: SpaceMenuViewModelViewDelegate {
func spaceMenuViewModel(_ viewModel: SpaceMenuViewModelType, didUpdateViewState viewSate: SpaceMenuViewState) {
self.render(viewState: viewSate)
}
}
// MARK: - UITableViewDataSource
extension SpaceMenuViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.menuItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let viewData = viewModel.menuItems[indexPath.row]
let cell = viewData.style == .toggle ? tableView.dequeueReusableCell(for: indexPath, cellType: SpaceMenuSwitchViewCell.self) :
tableView.dequeueReusableCell(for: indexPath, cellType: SpaceMenuListViewCell.self)
if let cell = cell as? SpaceMenuCell {
cell.update(theme: self.theme)
cell.update(with: viewData)
}
return cell
}
}
// MARK: - UITableViewDelegate
extension SpaceMenuViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.viewModel.process(viewAction: .selectRow(at: indexPath))
}
}