346 lines
15 KiB
Swift
346 lines
15 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 UIKit
|
|
|
|
@objc protocol SettingsKeyBackupTableViewSectionDelegate: AnyObject {
|
|
func settingsKeyBackupTableViewSectionDidUpdate(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection)
|
|
|
|
func settingsKeyBackupTableViewSection(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, textCellForRow: Int) -> MXKTableViewCellWithTextView
|
|
func settingsKeyBackupTableViewSection(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, buttonCellForRow: Int) -> MXKTableViewCellWithButton
|
|
|
|
|
|
func settingsKeyBackupTableViewSectionShowKeyBackupSetup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection)
|
|
func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion)
|
|
func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showKeyBackupDeleteConfirm keyBackupVersion: MXKeyBackupVersion)
|
|
|
|
func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showActivityIndicator show: Bool)
|
|
func settingsKeyBackup(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, showError error: Error)
|
|
}
|
|
|
|
private enum BackupRows {
|
|
case info(text: String)
|
|
case createAction
|
|
case restoreAction(keyBackupVersion: MXKeyBackupVersion, title: String)
|
|
case deleteAction(keyBackupVersion: MXKeyBackupVersion)
|
|
}
|
|
|
|
@objc final class SettingsKeyBackupTableViewSection: NSObject {
|
|
|
|
// MARK: - Properties
|
|
|
|
@objc weak var delegate: SettingsKeyBackupTableViewSectionDelegate?
|
|
|
|
// 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: SettingsKeyBackupViewModelType!
|
|
|
|
// Need to know the state to make `cellForRow` deliver cells accordingly
|
|
private var viewState: SettingsKeyBackupViewState = .checkingBackup {
|
|
didSet {
|
|
self.updateBackupRows()
|
|
}
|
|
}
|
|
|
|
private var userDevice: MXDeviceInfo
|
|
|
|
private var backupRows: [BackupRows] = []
|
|
|
|
// MARK: - Public
|
|
|
|
@objc init(withKeyBackup keyBackup: MXKeyBackup, userDevice: MXDeviceInfo) {
|
|
self.viewModel = SettingsKeyBackupViewModel(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 {
|
|
guard let delegate = self.delegate else {
|
|
return UITableViewCell()
|
|
}
|
|
|
|
let backupRow = self.backupRows[row]
|
|
|
|
var cell: UITableViewCell
|
|
switch backupRow {
|
|
case .info(let infoText):
|
|
let infoCell: MXKTableViewCellWithTextView = delegate.settingsKeyBackupTableViewSection(self, textCellForRow: row)
|
|
infoCell.mxkTextView.text = infoText
|
|
cell = infoCell
|
|
case .createAction:
|
|
cell = self.buttonCellForCreate(atRow: row)
|
|
case .restoreAction(keyBackupVersion: let keyBackupVersion, let title):
|
|
cell = self.buttonCellForRestore(keyBackupVersion: keyBackupVersion, title: title, atRow: row)
|
|
case .deleteAction(keyBackupVersion: let keyBackupVersion):
|
|
cell = self.buttonCellForDelete(keyBackupVersion: keyBackupVersion, atRow: row)
|
|
}
|
|
|
|
return cell
|
|
}
|
|
|
|
@objc func reload() {
|
|
self.viewModel.process(viewAction: .load)
|
|
}
|
|
|
|
@objc func delete(keyBackupVersion: MXKeyBackupVersion) {
|
|
self.viewModel.process(viewAction: .delete(keyBackupVersion))
|
|
}
|
|
|
|
// MARK: - Data Computing
|
|
|
|
private func updateBackupRows() {
|
|
|
|
let backupRows: [BackupRows]
|
|
|
|
switch self.viewState {
|
|
case .checkingBackup:
|
|
|
|
let info = VectorL10n.settingsKeyBackupInfo
|
|
let checking = VectorL10n.settingsKeyBackupInfoChecking
|
|
let strings = [info, "", checking]
|
|
let text = strings.joined(separator: "\n")
|
|
|
|
backupRows = [
|
|
.info(text: text)
|
|
]
|
|
|
|
case .noBackup:
|
|
|
|
let noBackup = VectorL10n.settingsKeyBackupInfoNone
|
|
let info = VectorL10n.settingsKeyBackupInfo
|
|
let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning
|
|
let strings = [noBackup, "", info, "", signoutWarning]
|
|
let backupInfoText = strings.joined(separator: "\n")
|
|
|
|
backupRows = [
|
|
.info(text: backupInfoText),
|
|
.createAction
|
|
]
|
|
|
|
case .backup(let keyBackupVersion, let keyBackupVersionTrust):
|
|
|
|
let info = VectorL10n.settingsKeyBackupInfo
|
|
let backupStatus = VectorL10n.settingsKeyBackupInfoValid
|
|
let backupStrings = [info, "", backupStatus]
|
|
let backupInfoText = backupStrings.joined(separator: "\n")
|
|
|
|
let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersion.version ?? "")
|
|
let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm)
|
|
let uploadStatus = VectorL10n.settingsKeyBackupInfoProgressDone
|
|
let additionalStrings = [version, algorithm, uploadStatus]
|
|
let additionalInfoText = additionalStrings.joined(separator: "\n")
|
|
|
|
let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust)
|
|
let backupTrustInfoText = backupTrust.joined(separator: "\n")
|
|
|
|
var backupViewStateRows: [BackupRows] = [
|
|
.info(text: backupInfoText),
|
|
.info(text: additionalInfoText),
|
|
.info(text: backupTrustInfoText)
|
|
]
|
|
|
|
// TODO: Do not display restore button if all keys are stored on the device
|
|
if true {
|
|
backupViewStateRows.append(.restoreAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.settingsKeyBackupButtonRestore))
|
|
}
|
|
|
|
backupViewStateRows.append(.deleteAction(keyBackupVersion: keyBackupVersion))
|
|
|
|
backupRows = backupViewStateRows
|
|
|
|
case .backupAndRunning(let keyBackupVersion, let keyBackupVersionTrust, let backupProgress):
|
|
|
|
let info = VectorL10n.settingsKeyBackupInfo
|
|
let backupStatus = VectorL10n.settingsKeyBackupInfoValid
|
|
let backupStrings = [info, "", backupStatus]
|
|
let backupInfoText = backupStrings.joined(separator: "\n")
|
|
|
|
let remaining = backupProgress.totalUnitCount - backupProgress.completedUnitCount
|
|
let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersion.version ?? "")
|
|
let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm)
|
|
let uploadStatus = VectorL10n.settingsKeyBackupInfoProgress(String(remaining))
|
|
let additionalStrings = [version, algorithm, uploadStatus]
|
|
let additionalInfoText = additionalStrings.joined(separator: "\n")
|
|
|
|
let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust)
|
|
let backupTrustInfoText = backupTrust.joined(separator: "\n")
|
|
|
|
var backupAndRunningViewStateRows: [BackupRows] = [
|
|
.info(text: backupInfoText),
|
|
.info(text: additionalInfoText),
|
|
.info(text: backupTrustInfoText)
|
|
]
|
|
|
|
// TODO: Do not display restore button if all keys are stored on the device
|
|
if true {
|
|
backupAndRunningViewStateRows.append(.restoreAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.settingsKeyBackupButtonRestore))
|
|
}
|
|
|
|
backupAndRunningViewStateRows.append(.deleteAction(keyBackupVersion: keyBackupVersion))
|
|
|
|
backupRows = backupAndRunningViewStateRows
|
|
|
|
case .backupNotTrusted(let keyBackupVersion, let keyBackupVersionTrust):
|
|
|
|
let info = VectorL10n.settingsKeyBackupInfo
|
|
let backupStatus = VectorL10n.settingsKeyBackupInfoNotValid
|
|
let signoutWarning = VectorL10n.settingsKeyBackupInfoSignoutWarning
|
|
let backupStrings = [info, "", backupStatus, "", signoutWarning]
|
|
let backupInfoText = backupStrings.joined(separator: "\n")
|
|
|
|
let version = VectorL10n.settingsKeyBackupInfoVersion(keyBackupVersion.version ?? "")
|
|
let algorithm = VectorL10n.settingsKeyBackupInfoAlgorithm(keyBackupVersion.algorithm)
|
|
let additionalStrings = [version, algorithm]
|
|
let additionalInfoText = additionalStrings.joined(separator: "\n")
|
|
|
|
let backupTrust = self.stringForKeyBackupTrust(keyBackupVersionTrust)
|
|
let backupTrustInfoText = backupTrust.joined(separator: "\n")
|
|
|
|
var backupNotTrustedViewStateRows: [BackupRows] = [
|
|
.info(text: backupInfoText),
|
|
.info(text: additionalInfoText),
|
|
.info(text: backupTrustInfoText)
|
|
]
|
|
|
|
// TODO: Do not display restore button if all keys are stored on the device
|
|
if true {
|
|
backupNotTrustedViewStateRows.append(.restoreAction(keyBackupVersion: keyBackupVersion, title: VectorL10n.settingsKeyBackupButtonConnect))
|
|
}
|
|
|
|
backupNotTrustedViewStateRows.append(.deleteAction(keyBackupVersion: keyBackupVersion))
|
|
|
|
backupRows = backupNotTrustedViewStateRows
|
|
}
|
|
|
|
self.backupRows = backupRows
|
|
}
|
|
|
|
private func stringForKeyBackupTrust(_ keyBackupVersionTrust: MXKeyBackupVersionTrust) -> [String] {
|
|
|
|
return keyBackupVersionTrust.signatures.map { (signature) -> String in
|
|
guard let device = signature.device else {
|
|
return VectorL10n.settingsKeyBackupInfoTrustSignatureUnknown(signature.deviceId)
|
|
}
|
|
|
|
let displayName = device.displayName ?? device.deviceId ?? ""
|
|
|
|
if device.fingerprint == self.userDevice.fingerprint {
|
|
return VectorL10n.settingsKeyBackupInfoTrustSignatureValid
|
|
} else if signature.valid
|
|
&& (device.trustLevel.localVerificationStatus == .verified) {
|
|
return VectorL10n.settingsKeyBackupInfoTrustSignatureValidDeviceVerified(displayName)
|
|
} else if signature.valid
|
|
&& (device.trustLevel.localVerificationStatus != .verified) {
|
|
return VectorL10n.settingsKeyBackupInfoTrustSignatureValidDeviceUnverified(displayName)
|
|
} else if !signature.valid
|
|
&& (device.trustLevel.localVerificationStatus == .verified) {
|
|
return VectorL10n.settingsKeyBackupInfoTrustSignatureInvalidDeviceVerified(displayName)
|
|
} else if !signature.valid
|
|
&& (device.trustLevel.localVerificationStatus != .verified) {
|
|
return VectorL10n.settingsKeyBackupInfoTrustSignatureInvalidDeviceUnverified(displayName)
|
|
}
|
|
|
|
return ""
|
|
}
|
|
}
|
|
|
|
// MARK: - Button cells
|
|
|
|
private func buttonCellForCreate(atRow row: Int) -> UITableViewCell {
|
|
|
|
guard let delegate = self.delegate else {
|
|
return UITableViewCell()
|
|
}
|
|
|
|
let cell: MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: row)
|
|
|
|
let btnTitle = VectorL10n.settingsKeyBackupButtonCreate
|
|
cell.mxkButton.setTitle(btnTitle, for: .normal)
|
|
cell.mxkButton.setTitle(btnTitle, for: .highlighted)
|
|
|
|
cell.mxkButton.vc_addAction {
|
|
self.viewModel.process(viewAction: .create)
|
|
}
|
|
|
|
return cell
|
|
}
|
|
|
|
private func buttonCellForRestore(keyBackupVersion: MXKeyBackupVersion, title: String, atRow row: Int) -> UITableViewCell {
|
|
guard let delegate = self.delegate else {
|
|
return UITableViewCell()
|
|
}
|
|
|
|
let cell: MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: row)
|
|
cell.mxkButton.setTitle(title, for: .normal)
|
|
cell.mxkButton.setTitle(title, for: .highlighted)
|
|
cell.mxkButton.vc_addAction {
|
|
self.viewModel.process(viewAction: .restore(keyBackupVersion))
|
|
}
|
|
return cell
|
|
}
|
|
|
|
private func buttonCellForDelete(keyBackupVersion: MXKeyBackupVersion, atRow row: Int) -> UITableViewCell {
|
|
guard let delegate = self.delegate else {
|
|
return UITableViewCell()
|
|
}
|
|
|
|
let cell: MXKTableViewCellWithButton = delegate.settingsKeyBackupTableViewSection(self, buttonCellForRow: row)
|
|
let btnTitle = VectorL10n.settingsKeyBackupButtonDelete
|
|
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: .confirmDelete(keyBackupVersion))
|
|
}
|
|
|
|
return cell
|
|
}
|
|
}
|
|
|
|
|
|
// MARK: - KeyBackupSetupRecoveryKeyViewModelViewDelegate
|
|
extension SettingsKeyBackupTableViewSection: SettingsKeyBackupViewModelViewDelegate {
|
|
func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateViewState viewState: SettingsKeyBackupViewState) {
|
|
self.viewState = viewState
|
|
|
|
// The tableview datasource will call `self.cellForRow()`
|
|
self.delegate?.settingsKeyBackupTableViewSectionDidUpdate(self)
|
|
}
|
|
|
|
func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsKeyBackupNetworkRequestViewState) {
|
|
switch networkRequestViewSate {
|
|
case .loading:
|
|
self.delegate?.settingsKeyBackup(self, showActivityIndicator: true)
|
|
case .loaded:
|
|
self.delegate?.settingsKeyBackup(self, showActivityIndicator: false)
|
|
case .error(let error):
|
|
self.delegate?.settingsKeyBackup(self, showError: error)
|
|
}
|
|
}
|
|
|
|
func settingsKeyBackupViewModelShowKeyBackupSetup(_ viewModel: SettingsKeyBackupViewModelType) {
|
|
self.delegate?.settingsKeyBackupTableViewSectionShowKeyBackupSetup(self)
|
|
}
|
|
|
|
func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showKeyBackupRecover keyBackupVersion: MXKeyBackupVersion) {
|
|
self.delegate?.settingsKeyBackup(self, showKeyBackupRecover: keyBackupVersion)
|
|
}
|
|
|
|
func settingsKeyBackup(_ viewModel: SettingsKeyBackupViewModelType, showKeyBackupDeleteConfirm keyBackupVersion: MXKeyBackupVersion) {
|
|
self.delegate?.settingsKeyBackup(self, showKeyBackupDeleteConfirm: keyBackupVersion)
|
|
}
|
|
}
|