element-ios/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableVi...

340 lines
14 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 UIKit
@objc protocol SettingsSecureBackupTableViewSectionDelegate: AnyObject {
// Table view rendering
func settingsSecureBackupTableViewSectionDidUpdate(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection)
func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, textCellForRow: Int) -> MXKTableViewCellWithTextView
func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, buttonCellForRow: Int) -> MXKTableViewCellWithButton
// Secure backup
func settingsSecureBackupTableViewSectionShowSecureBackupReset(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection)
// Key backup
func settingsSecureBackupTableViewSectionShowKeyBackupCreate(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection)
func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion)
func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showKeyBackupDeleteConfirm keyBackupVersion: MXKeyBackupVersion)
// Life cycle
func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showActivityIndicator show: Bool)
func settingsSecureBackupTableViewSection(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection, showError error: Error)
}
private enum BackupRows {
case info(text: String)
case createSecureBackupAction
case resetSecureBackupAction
case createKeyBackupAction
case restoreFromKeyBackupAction(keyBackupVersion: MXKeyBackupVersion, title: String)
case deleteKeyBackupAction(keyBackupVersion: MXKeyBackupVersion)
}
/// SettingsSecureBackupTableViewSection provides UITableViewCells to manage secure backup and key backup.
///
/// All states are described in SettingsSecureBackupViewState.
/// All actions in SettingsSecureBackupViewAction.
@objc final class SettingsSecureBackupTableViewSection: NSObject {
// MARK: - Properties
@objc weak var delegate: SettingsSecureBackupTableViewSectionDelegate?
// MARK: Private
// This view class holds the model because the model is in pure Swift
// whereas this class can be used from objC
private var viewModel: SettingsSecureBackupViewModelType!
// Need to know the state to make `cellForRow` deliver cells accordingly
private var viewState: SettingsSecureBackupViewState = .loading {
didSet {
self.updateBackupRows()
}
}
private var userDevice: MXDeviceInfo
private var backupRows: [BackupRows] = []
// MARK: - Public
@objc init(withRecoveryService recoveryService: MXRecoveryService, keyBackup: MXKeyBackup, userDevice: MXDeviceInfo) {
self.viewModel = SettingsSecureBackupViewModel(recoveryService: recoveryService, keyBackup: keyBackup)
self.userDevice = userDevice
super.init()
self.viewModel.viewDelegate = self
self.viewModel.process(viewAction: .load)
}
@objc func numberOfRows() -> Int {
return self.backupRows.count
}
@objc func cellForRow(atRow row: Int) -> UITableViewCell {
let backupRow = self.backupRows[row]
var cell: UITableViewCell
switch backupRow {
case .info(let text):
cell = self.textCell(atRow: row, text: text)
case .createSecureBackupAction:
cell = self.buttonCellForCreateSecureBackup(atRow: row)
case .resetSecureBackupAction:
cell = self.buttonCellForResetSecureBackup(atRow: row)
case .createKeyBackupAction:
cell = self.buttonCellForCreateKeyBackup(atRow: row)
case .restoreFromKeyBackupAction(keyBackupVersion: let keyBackupVersion, let title):
cell = self.buttonCellForRestoreFromKeyBackup(keyBackupVersion: keyBackupVersion, title: title, atRow: row)
case .deleteKeyBackupAction(keyBackupVersion: let keyBackupVersion):
cell = self.buttonCellForDeleteKeyBackup(keyBackupVersion: keyBackupVersion, atRow: row)
}
return cell
}
@objc func reload() {
self.viewModel.process(viewAction: .load)
}
@objc func deleteKeyBackup(keyBackupVersion: MXKeyBackupVersion) {
self.viewModel.process(viewAction: .deleteKeyBackup(keyBackupVersion))
}
// MARK: - Data Computing
private func updateBackupRows() {
let backupRows: [BackupRows]
switch self.viewState {
case .loading:
backupRows = [
.info(text: VectorL10n.securitySettingsSecureBackupInfoChecking)
]
case .noSecureBackup(let keyBackupState):
switch keyBackupState {
case .noKeyBackup:
let noBackup = VectorL10n.settingsKeyBackupInfoNone
let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning
let infoText = [noBackup, signoutWarning].joined(separator: "\n")
backupRows = [
.info(text: infoText),
.createSecureBackupAction
]
case .keyBackup(let keyBackupVersion, _, let progress):
if let progress = progress {
backupRows = [
.info(text: importProgressText(for: progress)),
.deleteKeyBackupAction(keyBackupVersion: keyBackupVersion)
]
} else {
backupRows = [
.info(text: VectorL10n.securitySettingsSecureBackupInfoValid),
.restoreFromKeyBackupAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.securitySettingsSecureBackupRestore),
.deleteKeyBackupAction(keyBackupVersion: keyBackupVersion)
]
}
case .keyBackupNotTrusted(let keyBackupVersion, _):
backupRows = [
.info(text: VectorL10n.securitySettingsSecureBackupInfoValid),
.restoreFromKeyBackupAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.securitySettingsSecureBackupRestore),
.deleteKeyBackupAction(keyBackupVersion: keyBackupVersion)
]
}
case .secureBackup(let keyBackupState):
switch keyBackupState {
case .noKeyBackup:
let noBackup = VectorL10n.settingsKeyBackupInfoNone
let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning
let infoText = [noBackup, signoutWarning].joined(separator: "\n")
backupRows = [
.info(text: infoText),
.createKeyBackupAction,
.resetSecureBackupAction
]
case .keyBackup(let keyBackupVersion, _, let progress):
if let progress = progress {
backupRows = [
.info(text: importProgressText(for: progress)),
.deleteKeyBackupAction(keyBackupVersion: keyBackupVersion),
.resetSecureBackupAction
]
} else {
backupRows = [
.info(text: VectorL10n.securitySettingsSecureBackupInfoValid),
.restoreFromKeyBackupAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.securitySettingsSecureBackupRestore),
.deleteKeyBackupAction(keyBackupVersion: keyBackupVersion),
.resetSecureBackupAction
]
}
case .keyBackupNotTrusted(let keyBackupVersion, _):
backupRows = [
.info(text: VectorL10n.securitySettingsSecureBackupInfoValid),
.restoreFromKeyBackupAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.securitySettingsSecureBackupRestore),
.deleteKeyBackupAction(keyBackupVersion: keyBackupVersion),
.resetSecureBackupAction
]
}
}
self.backupRows = backupRows
}
private func importProgressText(for progress: Progress) -> String {
let percentage = Int(round(progress.fractionCompleted * 100))
return VectorL10n.keyBackupRecoverFromPrivateKeyInfo + " \(percentage)%"
}
// MARK: - Cells -
private func textCell(atRow row: Int, text: String) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell()
}
let cell = delegate.settingsSecureBackupTableViewSection(self, textCellForRow: row)
cell.mxkTextView.text = text
return cell
}
// MARK: - Button cells
private func buttonCellForCreateSecureBackup(atRow row: Int) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell()
}
let cell: MXKTableViewCellWithButton = delegate.settingsSecureBackupTableViewSection(self, buttonCellForRow: row)
let btnTitle = VectorL10n.securitySettingsSecureBackupSetup
cell.mxkButton.setTitle(btnTitle, for: .normal)
cell.mxkButton.setTitle(btnTitle, for: .highlighted)
cell.mxkButton.vc_addAction {
self.viewModel.process(viewAction: .createSecureBackup)
}
return cell
}
private func buttonCellForResetSecureBackup(atRow row: Int) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell()
}
let cell: MXKTableViewCellWithButton = delegate.settingsSecureBackupTableViewSection(self, buttonCellForRow: row)
let btnTitle = VectorL10n.securitySettingsSecureBackupReset
cell.mxkButton.setTitle(btnTitle, for: .normal)
cell.mxkButton.setTitle(btnTitle, for: .highlighted)
cell.mxkButton.tintColor = ThemeService.shared().theme.warningColor
cell.mxkButton.vc_addAction {
self.viewModel.process(viewAction: .resetSecureBackup)
}
return cell
}
private func buttonCellForCreateKeyBackup(atRow row: Int) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell()
}
let cell: MXKTableViewCellWithButton = delegate.settingsSecureBackupTableViewSection(self, buttonCellForRow: row)
let btnTitle = VectorL10n.securitySettingsSecureBackupSetup
cell.mxkButton.setTitle(btnTitle, for: .normal)
cell.mxkButton.setTitle(btnTitle, for: .highlighted)
cell.mxkButton.vc_addAction {
self.viewModel.process(viewAction: .createKeyBackup)
}
return cell
}
private func buttonCellForRestoreFromKeyBackup(keyBackupVersion: MXKeyBackupVersion, title: String, atRow row: Int) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell()
}
let cell: MXKTableViewCellWithButton = delegate.settingsSecureBackupTableViewSection(self, buttonCellForRow: row)
cell.mxkButton.setTitle(title, for: .normal)
cell.mxkButton.setTitle(title, for: .highlighted)
cell.mxkButton.vc_addAction {
self.viewModel.process(viewAction: .restoreFromKeyBackup(keyBackupVersion))
}
return cell
}
private func buttonCellForDeleteKeyBackup(keyBackupVersion: MXKeyBackupVersion, atRow row: Int) -> UITableViewCell {
guard let delegate = self.delegate else {
return UITableViewCell()
}
let cell: MXKTableViewCellWithButton = delegate.settingsSecureBackupTableViewSection(self, buttonCellForRow: row)
let btnTitle = VectorL10n.securitySettingsSecureBackupDelete
cell.mxkButton.setTitle(btnTitle, for: .normal)
cell.mxkButton.setTitle(btnTitle, for: .highlighted)
cell.mxkButton.tintColor = ThemeService.shared().theme.warningColor
cell.mxkButton.vc_addAction {
self.viewModel.process(viewAction: .confirmDeleteKeyBackup(keyBackupVersion))
}
return cell
}
}
// MARK: - KeyBackupSetupRecoveryKeyViewModelViewDelegate
extension SettingsSecureBackupTableViewSection: SettingsSecureBackupViewModelViewDelegate {
func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateViewState viewState: SettingsSecureBackupViewState) {
self.viewState = viewState
// The tableview datasource will call `self.cellForRow()`
self.delegate?.settingsSecureBackupTableViewSectionDidUpdate(self)
}
func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsSecureBackupNetworkRequestViewState) {
switch networkRequestViewSate {
case .loading:
self.delegate?.settingsSecureBackupTableViewSection(self, showActivityIndicator: true)
case .loaded:
self.delegate?.settingsSecureBackupTableViewSection(self, showActivityIndicator: false)
case .error(let error):
self.delegate?.settingsSecureBackupTableViewSection(self, showError: error)
}
}
func settingsSecureBackupViewModelShowSecureBackupReset(_ viewModel: SettingsSecureBackupViewModelType) {
self.delegate?.settingsSecureBackupTableViewSectionShowSecureBackupReset(self)
}
func settingsSecureBackupViewModelShowKeyBackupCreate(_ viewModel: SettingsSecureBackupViewModelType) {
self.delegate?.settingsSecureBackupTableViewSectionShowKeyBackupCreate(self)
}
func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion) {
self.delegate?.settingsSecureBackupTableViewSection(self, showKeyBackupRecover: keyBackupVersion)
}
func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, showKeyBackupDeleteConfirm keyBackupVersion: MXKeyBackupVersion) {
self.delegate?.settingsSecureBackupTableViewSection(self, showKeyBackupDeleteConfirm: keyBackupVersion)
}
}