element-ios/Riot/Modules/Settings/Discovery/SettingsDiscoveryTableViewS...

228 lines
8.1 KiB
Swift

/*
Copyright 2019-2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import Foundation
@objc protocol SettingsDiscoveryTableViewSectionDelegate: AnyObject {
func settingsDiscoveryTableViewSection(_ settingsDiscoveryTableViewSection: SettingsDiscoveryTableViewSection, tableViewCellClass: MXKTableViewCell.Type, forRow: Int) -> MXKTableViewCell
func settingsDiscoveryTableViewSectionDidUpdate(_ settingsDiscoveryTableViewSection: SettingsDiscoveryTableViewSection)
}
private enum DiscoverySectionRows {
case button(title: String, action: () -> Void)
case threePid(threePid: MX3PID)
}
@objc final class SettingsDiscoveryTableViewSection: NSObject, Themable {
// MARK: - Constants
private enum Constants {
static let defaultFont = UIFont.systemFont(ofSize: 17.0)
}
// MARK: - Properties
@objc weak var delegate: SettingsDiscoveryTableViewSectionDelegate?
@objc var attributedFooterTitle: NSAttributedString?
@objc var footerShouldScrollToUserSettings = false
// MARK: Private
private var theme: Theme!
private var viewModel: SettingsDiscoveryViewModel
// Need to know the state to make `cellForRow` deliver cells accordingly
private var viewState: SettingsDiscoveryViewState = .loading {
didSet {
self.updateRows()
}
}
private var discoveryRows: [DiscoverySectionRows] = []
// MARK: - Setup
@objc init(viewModel: SettingsDiscoveryViewModel) {
self.theme = ThemeService.shared().theme
self.viewModel = viewModel
super.init()
self.viewModel.viewDelegate = self
self.viewModel.process(viewAction: .load)
self.registerThemeServiceDidChangeThemeNotification()
}
// MARK: - Public
@objc func numberOfRows() -> Int {
return self.discoveryRows.count
}
@objc func cellForRow(atRow row: Int) -> UITableViewCell {
let discoveryRow = self.discoveryRows[row]
var cell: UITableViewCell?
let enableInteraction: Bool
if case .loading = self.viewState {
enableInteraction = false
} else {
enableInteraction = true
}
switch discoveryRow {
case .button(title: let title, action: let action):
if let buttonCell: MXKTableViewCellWithButton = self.cellType(at: row) {
buttonCell.mxkButton.setTitle(title, for: .normal)
buttonCell.mxkButton.setTitle(title, for: .highlighted)
buttonCell.mxkButton.vc_addAction(action: action)
buttonCell.mxkButton.isEnabled = enableInteraction
cell = buttonCell
}
case .threePid(let threePid):
if let detailCell: MXKTableViewCell = self.cellType(at: row) {
detailCell.vc_setAccessoryDisclosureIndicator(withTheme: self.theme)
let formattedThreePid: String?
switch threePid.medium {
case .email:
formattedThreePid = threePid.address
case .msisdn:
formattedThreePid = MXKTools.readableMSISDN(threePid.address)
default:
formattedThreePid = nil
}
detailCell.textLabel?.text = formattedThreePid
detailCell.isUserInteractionEnabled = enableInteraction
cell = detailCell
}
}
return cell ?? UITableViewCell()
}
@objc func reload() {
self.viewModel.process(viewAction: .load)
}
@objc func selectRow(_ row: Int) {
let discoveryRow = self.discoveryRows[row]
switch discoveryRow {
case .threePid(threePid: let threePid):
self.viewModel.process(viewAction: .select(threePid: threePid))
default:
break
}
}
func update(theme: Theme) {
self.theme = theme
self.updateRows()
}
// MARK: - Private
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 cellType<T: MXKTableViewCell>(at row: Int) -> T? {
let klass: T.Type = T.self
let tableViewCell = delegate?.settingsDiscoveryTableViewSection(self, tableViewCellClass: klass, forRow: row)
return tableViewCell as? T
}
private func updateRows() {
// reset the footer
attributedFooterTitle = nil
footerShouldScrollToUserSettings = false
let discoveryRows: [DiscoverySectionRows]
switch self.viewState {
case .loading:
discoveryRows = self.discoveryRows
case .loaded(let displayMode):
switch displayMode {
case .noIdentityServer:
discoveryRows = []
attributedFooterTitle = NSAttributedString(string: VectorL10n.settingsDiscoveryNoIdentityServer)
case .termsNotSigned(let host):
discoveryRows = [
.button(title: VectorL10n.settingsDiscoveryAcceptTerms, action: { [weak self] in
self?.viewModel.process(viewAction: .acceptTerms)
})
]
attributedFooterTitle = NSAttributedString(string: VectorL10n.settingsDiscoveryTermsNotSigned(host))
case .noThreePidsAdded:
discoveryRows = []
attributedFooterTitle = threePidsManagementInfoAttributedString()
footerShouldScrollToUserSettings = true
case .threePidsAdded(let emails, let phoneNumbers):
let emailThreePids = emails.map { (email) -> DiscoverySectionRows in
return .threePid(threePid: email)
}
let phoneNumbersThreePids = phoneNumbers.map { (phoneNumber) -> DiscoverySectionRows in
return .threePid(threePid: phoneNumber)
}
discoveryRows = emailThreePids + phoneNumbersThreePids
attributedFooterTitle = threePidsManagementInfoAttributedString()
footerShouldScrollToUserSettings = true
}
case .error:
discoveryRows = [
.button(title: VectorL10n.retry, action: { [weak self] in
self?.viewModel.process(viewAction: .load)
})
]
attributedFooterTitle = NSAttributedString(string: VectorL10n.settingsDiscoveryErrorMessage)
}
self.discoveryRows = discoveryRows
}
private func threePidsManagementInfoAttributedString() -> NSAttributedString {
let attributedInfoString = NSMutableAttributedString(string: VectorL10n.settingsDiscoveryThreePidsManagementInformationPart1)
attributedInfoString.append(NSAttributedString(string: VectorL10n.settingsDiscoveryThreePidsManagementInformationPart2,
attributes: [.foregroundColor: self.theme.tintColor]))
attributedInfoString.append(NSAttributedString(string: VectorL10n.settingsDiscoveryThreePidsManagementInformationPart3))
return attributedInfoString
}
}
// MARK: - SettingsDiscoveryViewModelViewDelegate
extension SettingsDiscoveryTableViewSection: SettingsDiscoveryViewModelViewDelegate {
func settingsDiscoveryViewModel(_ viewModel: SettingsDiscoveryViewModelType, didUpdateViewState viewState: SettingsDiscoveryViewState) {
self.viewState = viewState
// The tableview datasource will call `self.cellForRow()`
self.delegate?.settingsDiscoveryTableViewSectionDidUpdate(self)
}
}