mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-29 04:36:58 +02:00
Add a SignOutFlowPresenter. (#6854)
Used in AllChats, UserSessions and Settings. (TabBarCoordinator is unnecessary as signout will be removed from there).
This commit is contained in:
@@ -189,8 +189,7 @@ typedef void (^blockSettingsViewController_onReadyToDestroy)(void);
|
||||
|
||||
@interface SettingsViewController () <UITextFieldDelegate, MXKCountryPickerViewControllerDelegate, MXKLanguagePickerViewControllerDelegate, DeactivateAccountViewControllerDelegate,
|
||||
NotificationSettingsCoordinatorBridgePresenterDelegate,
|
||||
SecureBackupSetupCoordinatorBridgePresenterDelegate,
|
||||
SignOutAlertPresenterDelegate,
|
||||
SignOutFlowPresenterDelegate,
|
||||
SingleImagePickerPresenterDelegate,
|
||||
SettingsDiscoveryTableViewSectionDelegate, SettingsDiscoveryViewModelCoordinatorDelegate,
|
||||
SettingsIdentityServerCoordinatorBridgePresenterDelegate,
|
||||
@@ -267,7 +266,7 @@ ChangePasswordCoordinatorBridgePresenterDelegate>
|
||||
|
||||
@property (nonatomic, strong) NotificationSettingsCoordinatorBridgePresenter *notificationSettingsBridgePresenter;
|
||||
|
||||
@property (nonatomic, strong) SignOutAlertPresenter *signOutAlertPresenter;
|
||||
@property (nonatomic, strong) SignOutFlowPresenter *signOutFlowPresenter;
|
||||
@property (nonatomic, weak) UIButton *signOutButton;
|
||||
@property (nonatomic, strong) SingleImagePickerPresenter *imagePickerPresenter;
|
||||
|
||||
@@ -275,12 +274,8 @@ ChangePasswordCoordinatorBridgePresenterDelegate>
|
||||
@property (nonatomic, strong) SettingsDiscoveryTableViewSection *settingsDiscoveryTableViewSection;
|
||||
@property (nonatomic, strong) SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter *discoveryThreePidDetailsPresenter;
|
||||
|
||||
@property (nonatomic, strong) SecureBackupSetupCoordinatorBridgePresenter *secureBackupSetupCoordinatorBridgePresenter;
|
||||
|
||||
@property (nonatomic, strong) TableViewSections *tableViewSections;
|
||||
|
||||
@property (nonatomic, strong) CrossSigningSetupCoordinatorBridgePresenter *crossSigningSetupCoordinatorBridgePresenter;
|
||||
|
||||
@property (nonatomic, strong) ReauthenticationCoordinatorBridgePresenter *reauthenticationCoordinatorBridgePresenter;
|
||||
|
||||
@property (nonatomic, strong) UserInteractiveAuthenticationService *userInteractiveAuthenticationService;
|
||||
@@ -701,9 +696,6 @@ ChangePasswordCoordinatorBridgePresenterDelegate>
|
||||
}];
|
||||
[self userInterfaceThemeDidChange];
|
||||
|
||||
self.signOutAlertPresenter = [SignOutAlertPresenter new];
|
||||
self.signOutAlertPresenter.delegate = self;
|
||||
|
||||
_tableViewSections = [TableViewSections new];
|
||||
_tableViewSections.delegate = self;
|
||||
[self updateSections];
|
||||
@@ -770,8 +762,7 @@ ChangePasswordCoordinatorBridgePresenterDelegate>
|
||||
|
||||
[super destroy];
|
||||
}
|
||||
|
||||
_secureBackupSetupCoordinatorBridgePresenter = nil;
|
||||
|
||||
identityServerSettingsCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
@@ -2964,13 +2955,11 @@ ChangePasswordCoordinatorBridgePresenterDelegate>
|
||||
{
|
||||
self.signOutButton = (UIButton*)sender;
|
||||
|
||||
MXKeyBackup *keyBackup = self.mainSession.crypto.backup;
|
||||
SignOutFlowPresenter *flowPresenter = [[SignOutFlowPresenter alloc] initWithSession:self.mainSession presentingViewController:self];
|
||||
flowPresenter.delegate = self;
|
||||
|
||||
[self.signOutAlertPresenter presentFor:keyBackup.state
|
||||
areThereKeysToBackup:keyBackup.hasKeysToBackup
|
||||
from:self
|
||||
sourceView:self.signOutButton
|
||||
animated:YES];
|
||||
[flowPresenter startWithSourceView:self.signOutButton];
|
||||
self.signOutFlowPresenter = flowPresenter;
|
||||
}
|
||||
|
||||
- (void)onRemove3PID:(NSIndexPath*)indexPath
|
||||
@@ -4181,123 +4170,25 @@ ChangePasswordCoordinatorBridgePresenterDelegate>
|
||||
self.notificationSettingsBridgePresenter = nil;
|
||||
}
|
||||
|
||||
#pragma mark - SignOutFlowPresenterDelegate
|
||||
|
||||
#pragma mark - SecureBackupSetupCoordinatorBridgePresenter
|
||||
|
||||
- (void)showSecureBackupSetupFromSignOutFlow
|
||||
- (void)signOutFlowPresenterDidStartLoading:(SignOutFlowPresenter *)presenter
|
||||
{
|
||||
if (self.canSetupSecureBackup)
|
||||
{
|
||||
[self setupSecureBackup2];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set up cross-signing first
|
||||
[self setupCrossSigningWithTitle:[VectorL10n secureKeyBackupSetupIntroTitle]
|
||||
message:[VectorL10n securitySettingsUserPasswordDescription]
|
||||
success:^{
|
||||
[self setupSecureBackup2];
|
||||
} failure:^(NSError *error) {
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setupSecureBackup2
|
||||
{
|
||||
SecureBackupSetupCoordinatorBridgePresenter *secureBackupSetupCoordinatorBridgePresenter = [[SecureBackupSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession allowOverwrite:YES];
|
||||
secureBackupSetupCoordinatorBridgePresenter.delegate = self;
|
||||
|
||||
[secureBackupSetupCoordinatorBridgePresenter presentFrom:self animated:YES];
|
||||
|
||||
self.secureBackupSetupCoordinatorBridgePresenter = secureBackupSetupCoordinatorBridgePresenter;
|
||||
}
|
||||
|
||||
- (BOOL)canSetupSecureBackup
|
||||
{
|
||||
return [self.mainSession vc_canSetupSecureBackup];
|
||||
}
|
||||
|
||||
#pragma mark - SecureBackupSetupCoordinatorBridgePresenterDelegate
|
||||
|
||||
- (void)secureBackupSetupCoordinatorBridgePresenterDelegateDidComplete:(SecureBackupSetupCoordinatorBridgePresenter *)coordinatorBridgePresenter
|
||||
{
|
||||
[self.secureBackupSetupCoordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
|
||||
self.secureBackupSetupCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
- (void)secureBackupSetupCoordinatorBridgePresenterDelegateDidCancel:(SecureBackupSetupCoordinatorBridgePresenter *)coordinatorBridgePresenter
|
||||
{
|
||||
[self.secureBackupSetupCoordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
|
||||
self.secureBackupSetupCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
#pragma mark - SignOutAlertPresenterDelegate
|
||||
|
||||
- (void)signOutAlertPresenterDidTapBackupAction:(SignOutAlertPresenter * _Nonnull)presenter
|
||||
{
|
||||
[self showSecureBackupSetupFromSignOutFlow];
|
||||
}
|
||||
|
||||
- (void)signOutAlertPresenterDidTapSignOutAction:(SignOutAlertPresenter * _Nonnull)presenter
|
||||
{
|
||||
// Prevent user to perform user interaction in settings when sign out
|
||||
// TODO: Prevent user interaction in all application (navigation controller and split view controller included)
|
||||
[self startActivityIndicator];
|
||||
self.view.userInteractionEnabled = NO;
|
||||
self.signOutButton.enabled = NO;
|
||||
|
||||
[self startActivityIndicator];
|
||||
|
||||
MXWeakify(self);
|
||||
|
||||
[[AppDelegate theDelegate] logoutWithConfirmation:NO completion:^(BOOL isLoggedOut) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
[self stopActivityIndicator];
|
||||
|
||||
self.view.userInteractionEnabled = YES;
|
||||
self.signOutButton.enabled = YES;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setupCrossSigningWithTitle:(NSString*)title
|
||||
message:(NSString*)message
|
||||
success:(void (^)(void))success
|
||||
failure:(void (^)(NSError *error))failure
|
||||
|
||||
- (void)signOutFlowPresenterDidStopLoading:(SignOutFlowPresenter *)presenter
|
||||
{
|
||||
[self startActivityIndicator];
|
||||
self.view.userInteractionEnabled = NO;
|
||||
|
||||
MXWeakify(self);
|
||||
|
||||
void (^animationCompletion)(void) = ^void () {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
[self stopActivityIndicator];
|
||||
self.view.userInteractionEnabled = YES;
|
||||
[self.crossSigningSetupCoordinatorBridgePresenter dismissWithAnimated:YES completion:^{}];
|
||||
self.crossSigningSetupCoordinatorBridgePresenter = nil;
|
||||
};
|
||||
|
||||
CrossSigningSetupCoordinatorBridgePresenter *crossSigningSetupCoordinatorBridgePresenter = [[CrossSigningSetupCoordinatorBridgePresenter alloc] initWithSession:self.mainSession];
|
||||
|
||||
[crossSigningSetupCoordinatorBridgePresenter presentWith:title
|
||||
message:message
|
||||
from:self
|
||||
animated:YES
|
||||
success:^{
|
||||
animationCompletion();
|
||||
success();
|
||||
} cancel:^{
|
||||
animationCompletion();
|
||||
failure(nil);
|
||||
} failure:^(NSError * _Nonnull error) {
|
||||
animationCompletion();
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
failure(error);
|
||||
}];
|
||||
|
||||
self.crossSigningSetupCoordinatorBridgePresenter = crossSigningSetupCoordinatorBridgePresenter;
|
||||
[self stopActivityIndicator];
|
||||
self.view.userInteractionEnabled = YES;
|
||||
self.signOutButton.enabled = YES;
|
||||
}
|
||||
|
||||
- (void)signOutFlowPresenter:(SignOutFlowPresenter *)presenter didFailWith:(NSError *)error
|
||||
{
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
}
|
||||
|
||||
#pragma mark - SingleImagePickerPresenterDelegate
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
//
|
||||
// Copyright 2022 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user