element-ios/Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewC...

231 lines
8.7 KiB
Swift

/*
Copyright 2020-2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import UIKit
protocol SecureBackupSetupIntroViewControllerDelegate: AnyObject {
func secureBackupSetupIntroViewControllerDidTapUseKey(_ secureBackupSetupIntroViewController: SecureBackupSetupIntroViewController)
func secureBackupSetupIntroViewControllerDidTapUsePassphrase(_ secureBackupSetupIntroViewController: SecureBackupSetupIntroViewController)
func secureBackupSetupIntroViewControllerDidCancel(_ secureBackupSetupIntroViewController: SecureBackupSetupIntroViewController, showSkipAlert: Bool)
func secureBackupSetupIntroViewControllerDidTapConnectToKeyBackup(_ secureBackupSetupIntroViewController: SecureBackupSetupIntroViewController)
}
@objcMembers
final class SecureBackupSetupIntroViewController: UIViewController {
// MARK: - Properties
// MARK: Outlets
@IBOutlet private weak var informationLabel: UILabel!
@IBOutlet private weak var topSeparatorView: UIView!
@IBOutlet private weak var secureKeyCell: SecureBackupSetupIntroCell!
@IBOutlet private weak var securePassphraseCell: SecureBackupSetupIntroCell!
// MARK: Private
private var viewModel: SecureBackupSetupIntroViewModelType!
private var cancellable: Bool!
private var theme: Theme!
private var activityIndicatorPresenter: ActivityIndicatorPresenter!
private var errorPresenter: MXKErrorPresentation!
// MARK: Public
weak var delegate: SecureBackupSetupIntroViewControllerDelegate?
// MARK: - Setup
class func instantiate(with viewModel: SecureBackupSetupIntroViewModelType, cancellable: Bool) -> SecureBackupSetupIntroViewController {
let viewController = StoryboardScene.SecureBackupSetupIntroViewController.initialScene.instantiate()
viewController.viewModel = viewModel
viewController.cancellable = cancellable
viewController.theme = ThemeService.shared().theme
return viewController
}
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.vc_removeBackTitle()
self.setupViews()
self.activityIndicatorPresenter = ActivityIndicatorPresenter()
self.errorPresenter = MXKErrorAlertPresentation()
self.registerThemeServiceDidChangeThemeNotification()
self.update(theme: self.theme)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.checkKeyBackup()
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return self.theme.statusBarStyle
}
// MARK: - Private
private func setupViews() {
if self.cancellable {
let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in
guard let self = self else {
return
}
self.delegate?.secureBackupSetupIntroViewControllerDidCancel(self, showSkipAlert: true)
}
self.navigationItem.rightBarButtonItem = cancelBarButtonItem
}
self.title = VectorL10n.secureKeyBackupSetupIntroTitle
self.informationLabel.text = VectorL10n.secureKeyBackupSetupIntroInfo
self.secureKeyCell.fill(title: VectorL10n.secureKeyBackupSetupIntroUseSecurityKeyTitle,
information: VectorL10n.secureKeyBackupSetupIntroUseSecurityKeyInfo,
image: Asset.Images.secretsSetupKey.image)
self.secureKeyCell.action = { [weak self] in
guard let self = self else {
return
}
self.delegate?.secureBackupSetupIntroViewControllerDidTapUseKey(self)
}
self.securePassphraseCell.fill(title: VectorL10n.secureKeyBackupSetupIntroUseSecurityPassphraseTitle,
information: VectorL10n.secureKeyBackupSetupIntroUseSecurityPassphraseInfo,
image: Asset.Images.secretsSetupPassphrase.image)
self.securePassphraseCell.action = { [weak self] in
guard let self = self else {
return
}
self.delegate?.secureBackupSetupIntroViewControllerDidTapUsePassphrase(self)
}
setupBackupMethods()
}
private func setupBackupMethods() {
let secureBackupSetupMethods = self.viewModel.homeserverEncryptionConfiguration.secureBackupSetupMethods
// Hide setup methods that are not listed
if !secureBackupSetupMethods.contains(.key) {
self.secureKeyCell.isHidden = true
}
if !secureBackupSetupMethods.contains(.passphrase) {
self.securePassphraseCell.isHidden = true
}
}
private func renderLoading() {
self.activityIndicatorPresenter.presentActivityIndicator(on: self.view, animated: true)
}
private func renderLoaded() {
self.activityIndicatorPresenter.removeCurrentActivityIndicator(animated: true)
}
private func render(error: Error) {
self.activityIndicatorPresenter.removeCurrentActivityIndicator(animated: true)
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
}
private func update(theme: Theme) {
self.theme = theme
self.view.backgroundColor = theme.headerBackgroundColor
if let navigationBar = self.navigationController?.navigationBar {
theme.applyStyle(onNavigationBar: navigationBar)
}
self.informationLabel.textColor = theme.textPrimaryColor
self.topSeparatorView.backgroundColor = theme.lineBreakColor
self.secureKeyCell.update(theme: theme)
self.securePassphraseCell.update(theme: theme)
}
private func registerThemeServiceDidChangeThemeNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
}
@objc private func themeDidChange() {
self.update(theme: ThemeService.shared().theme)
}
// TODO: To remove
private func checkKeyBackup() {
guard self.viewModel.checkKeyBackup else {
return
}
guard let keyBackup = self.viewModel.keyBackup else {
return
}
// If a backup already exists and we do not have the private key,
// we need to get this private key first. Ask the user to make a key backup restore to catch it
if keyBackup.keyBackupVersion != nil && keyBackup.hasPrivateKeyInCryptoStore == false {
let alertController = UIAlertController(title: VectorL10n.secureKeyBackupSetupExistingBackupErrorTitle,
message: VectorL10n.secureKeyBackupSetupExistingBackupErrorInfo,
preferredStyle: .alert)
let connectAction = UIAlertAction(title: VectorL10n.secureKeyBackupSetupExistingBackupErrorUnlockIt, style: .default) { (_) in
self.delegate?.secureBackupSetupIntroViewControllerDidTapConnectToKeyBackup(self)
}
let resetAction = UIAlertAction(title: VectorL10n.secureKeyBackupSetupExistingBackupErrorDeleteIt, style: .destructive) { (_) in
self.deleteKeybackup()
}
let cancelAction = UIAlertAction(title: VectorL10n.cancel, style: .cancel) { (_) in
self.delegate?.secureBackupSetupIntroViewControllerDidCancel(self, showSkipAlert: false)
}
alertController.addAction(connectAction)
alertController.addAction(resetAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true)
}
}
// TODO: Move to view model
private func deleteKeybackup() {
guard let keyBackup = self.viewModel.keyBackup, let keybackupVersion = keyBackup.keyBackupVersion?.version else {
return
}
self.renderLoading()
keyBackup.deleteVersion(keybackupVersion, success: { [weak self] in
guard let self = self else {
return
}
self.renderLoaded()
self.checkKeyBackup()
}, failure: { [weak self] (error) in
guard let self = self else {
return
}
self.render(error: error)
})
}
}