// // 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 Combine import SwiftUI typealias SecureBackupRecoveryKeyScreenViewModelType = StateStoreViewModel class SecureBackupRecoveryKeyScreenViewModel: SecureBackupRecoveryKeyScreenViewModelType, SecureBackupRecoveryKeyScreenViewModelProtocol { private let secureBackupController: SecureBackupControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol private var actionsSubject: PassthroughSubject = .init() var actions: AnyPublisher { actionsSubject.eraseToAnyPublisher() } init(secureBackupController: SecureBackupControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol) { self.secureBackupController = secureBackupController self.userIndicatorController = userIndicatorController super.init(initialViewState: .init(mode: secureBackupController.recoveryKeyState.value.viewMode, bindings: .init())) secureBackupController.recoveryKeyState .receive(on: DispatchQueue.main) .sink(receiveValue: { [weak userIndicatorController] state in let loadingIndicatorIdentifier = "SecureBackupRecoveryKeyScreenLoading" switch state { case .settingUp: userIndicatorController?.submitIndicator(.init(id: loadingIndicatorIdentifier, type: .modal, title: L10n.commonLoading, persistent: true)) default: userIndicatorController?.retractIndicatorWithId(loadingIndicatorIdentifier) } }) .store(in: &cancellables) } // MARK: - Public override func process(viewAction: SecureBackupRecoveryKeyScreenViewAction) { MXLog.info("View model: received view action: \(viewAction)") switch viewAction { case .generateKey: Task { switch await secureBackupController.generateRecoveryKey() { case .success(let key): state.recoveryKey = key case .failure(let error): MXLog.error("Failed generating recovery key with error: \(error)") state.bindings.alertInfo = .init(id: .init()) } } case .copyKey: UIPasteboard.general.string = state.recoveryKey userIndicatorController.submitIndicator(.init(title: "Copied recovery key")) state.doneButtonEnabled = true case .keySaved: state.doneButtonEnabled = true case .confirmKey: Task { switch await secureBackupController.confirmRecoveryKey(state.bindings.confirmationRecoveryKey) { case .success: actionsSubject.send(.done(mode: context.viewState.mode)) case .failure(let error): MXLog.error("Failed confirming recovery key with error: \(error)") state.bindings.alertInfo = .init(id: .init()) } } case .cancel: actionsSubject.send(.cancel) case .done: state.bindings.alertInfo = .init(id: .init(), title: L10n.screenRecoveryKeySetupConfirmationTitle, message: L10n.screenRecoveryKeySetupConfirmationDescription, primaryButton: .init(title: L10n.actionCancel, role: .cancel, action: nil), secondaryButton: .init(title: L10n.actionContinue, action: { [weak self] in guard let self else { return } actionsSubject.send(.done(mode: context.viewState.mode)) })) } } } extension SecureBackupRecoveryKeyState { var viewMode: SecureBackupRecoveryKeyScreenViewMode { switch self { case .disabled: return .setupRecovery case .enabled: return .changeRecovery case .incomplete: return .fixRecovery default: fatalError("Invalid recovery state") } } }