154 lines
6.6 KiB
Swift
154 lines
6.6 KiB
Swift
//
|
|
// Copyright 2022-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 SignOutFlowPresenterDelegate {
|
|
/// The presenter is starting an operation that might take while and the UI should indicate this.
|
|
func signOutFlowPresenterDidStartLoading(_ presenter: SignOutFlowPresenter)
|
|
/// The presenter has finished an operation and the UI should indicate this if necessary.
|
|
func signOutFlowPresenterDidStopLoading(_ presenter: SignOutFlowPresenter)
|
|
/// The presenter encountered an error and has stopped.
|
|
func signOutFlowPresenter(_ presenter: SignOutFlowPresenter, didFailWith error: Error)
|
|
}
|
|
|
|
/// This class provides a reusable component to present the sign out flow
|
|
/// for the current session, including the initial prompt, and any follow-up
|
|
/// key-backup setup that is necessary for the user.
|
|
@objcMembers class SignOutFlowPresenter: NSObject {
|
|
private let session: MXSession
|
|
private let presentingViewController: UIViewController
|
|
|
|
private var signOutAlertPresenter = SignOutAlertPresenter()
|
|
|
|
weak var delegate: SignOutFlowPresenterDelegate?
|
|
|
|
init(session: MXSession, presentingViewController: UIViewController) {
|
|
self.session = session
|
|
self.presentingViewController = presentingViewController
|
|
|
|
super.init()
|
|
|
|
signOutAlertPresenter.delegate = self
|
|
}
|
|
|
|
/// Starts the flow without a specific source view. On iPad any popups
|
|
/// will show from the presenting view controller itself.
|
|
func start() {
|
|
start(sourceView: presentingViewController.view)
|
|
}
|
|
|
|
/// Starts the flow, presenting any popups on iPad from the specified view.
|
|
func start(sourceView: UIView?) {
|
|
guard let keyBackup = session.crypto?.backup else { return }
|
|
|
|
signOutAlertPresenter.present(for: keyBackup.state,
|
|
areThereKeysToBackup: keyBackup.hasKeysToBackup,
|
|
from: presentingViewController,
|
|
sourceView: sourceView ?? presentingViewController.view,
|
|
animated: true)
|
|
}
|
|
|
|
// MARK: - SecureBackupSetupCoordinatorBridgePresenter
|
|
|
|
private var secureBackupSetupCoordinatorBridgePresenter: SecureBackupSetupCoordinatorBridgePresenter?
|
|
private var crossSigningSetupCoordinatorBridgePresenter: CrossSigningSetupCoordinatorBridgePresenter?
|
|
|
|
private func showSecureBackupSetupFromSignOutFlow() {
|
|
if canSetupSecureBackup {
|
|
setupSecureBackup()
|
|
} else {
|
|
// Set up cross-signing first
|
|
setupCrossSigning(title: VectorL10n.secureKeyBackupSetupIntroTitle,
|
|
message: VectorL10n.securitySettingsUserPasswordDescription) { [weak self] result in
|
|
guard let self = self else { return }
|
|
switch result {
|
|
case .success(let isCompleted):
|
|
if isCompleted {
|
|
self.setupSecureBackup()
|
|
}
|
|
case .failure(let error):
|
|
self.delegate?.signOutFlowPresenter(self, didFailWith: error)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private var canSetupSecureBackup: Bool {
|
|
return session.vc_canSetupSecureBackup()
|
|
}
|
|
|
|
private func setupSecureBackup() {
|
|
let secureBackupSetupCoordinatorBridgePresenter = SecureBackupSetupCoordinatorBridgePresenter(session: session, allowOverwrite: true)
|
|
secureBackupSetupCoordinatorBridgePresenter.delegate = self
|
|
secureBackupSetupCoordinatorBridgePresenter.present(from: presentingViewController, animated: true)
|
|
self.secureBackupSetupCoordinatorBridgePresenter = secureBackupSetupCoordinatorBridgePresenter
|
|
}
|
|
|
|
private func setupCrossSigning(title: String, message: String, completion: @escaping (Result<Bool, Error>) -> Void) {
|
|
delegate?.signOutFlowPresenterDidStartLoading(self)
|
|
|
|
let dismissAnimation = { [weak self] in
|
|
guard let self = self else { return }
|
|
|
|
self.delegate?.signOutFlowPresenterDidStopLoading(self)
|
|
self.crossSigningSetupCoordinatorBridgePresenter?.dismiss(animated: true, completion: {
|
|
self.crossSigningSetupCoordinatorBridgePresenter = nil
|
|
})
|
|
}
|
|
|
|
let crossSigningSetupCoordinatorBridgePresenter = CrossSigningSetupCoordinatorBridgePresenter(session: session)
|
|
crossSigningSetupCoordinatorBridgePresenter.present(with: title, message: message, from: presentingViewController, animated: true) {
|
|
dismissAnimation()
|
|
completion(.success(true))
|
|
} cancel: {
|
|
dismissAnimation()
|
|
completion(.success(false))
|
|
} failure: { error in
|
|
dismissAnimation()
|
|
completion(.failure(error))
|
|
}
|
|
|
|
self.crossSigningSetupCoordinatorBridgePresenter = crossSigningSetupCoordinatorBridgePresenter
|
|
}
|
|
}
|
|
|
|
// MARK: - SignOutAlertPresenterDelegate
|
|
extension SignOutFlowPresenter: SignOutAlertPresenterDelegate {
|
|
|
|
func signOutAlertPresenterDidTapSignOutAction(_ presenter: SignOutAlertPresenter) {
|
|
// Allow presenting screen to black user interaction when signing out
|
|
// TODO: Prevent user interaction in all application (navigation controller and split view controller included)
|
|
delegate?.signOutFlowPresenterDidStartLoading(self)
|
|
|
|
AppDelegate.theDelegate().logout(withConfirmation: false) { [weak self] isLoggedOut in
|
|
guard let self = self else { return }
|
|
self.delegate?.signOutFlowPresenterDidStopLoading(self)
|
|
}
|
|
}
|
|
|
|
func signOutAlertPresenterDidTapBackupAction(_ presenter: SignOutAlertPresenter) {
|
|
showSecureBackupSetupFromSignOutFlow()
|
|
}
|
|
|
|
}
|
|
|
|
// MARK: - SecureBackupSetupCoordinatorBridgePresenterDelegate
|
|
extension SignOutFlowPresenter: SecureBackupSetupCoordinatorBridgePresenterDelegate {
|
|
func secureBackupSetupCoordinatorBridgePresenterDelegateDidCancel(_ coordinatorBridgePresenter: SecureBackupSetupCoordinatorBridgePresenter) {
|
|
coordinatorBridgePresenter.dismiss(animated: true) {
|
|
self.secureBackupSetupCoordinatorBridgePresenter = nil
|
|
}
|
|
}
|
|
|
|
func secureBackupSetupCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: SecureBackupSetupCoordinatorBridgePresenter) {
|
|
coordinatorBridgePresenter.dismiss(animated: true) {
|
|
self.secureBackupSetupCoordinatorBridgePresenter = nil
|
|
}
|
|
}
|
|
}
|