mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
Encryption Flow Coordinators. (#3471)
* Manage the secure backup screens with flow coordinators.
* Add UI tests for the EncryptionSettingsFlowCoordinator.
* Realise that the settings flow can't reset anymore and remove the sub-flow 🤦♂️
* Add UI tests for the EncryptionResetFlowCoordinator.
This commit is contained in:
+8
-13
@@ -8,17 +8,12 @@
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
struct EncryptionResetPasswordScreenCoordinatorParameters { }
|
||||
struct EncryptionResetPasswordScreenCoordinatorParameters {
|
||||
let passwordPublisher: PassthroughSubject<String, Never>
|
||||
}
|
||||
|
||||
enum EncryptionResetPasswordScreenCoordinatorAction: CustomStringConvertible {
|
||||
case resetIdentity(String)
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .resetIdentity:
|
||||
"resetIdentity"
|
||||
}
|
||||
}
|
||||
enum EncryptionResetPasswordScreenCoordinatorAction {
|
||||
case passwordEntered
|
||||
}
|
||||
|
||||
final class EncryptionResetPasswordScreenCoordinator: CoordinatorProtocol {
|
||||
@@ -35,7 +30,7 @@ final class EncryptionResetPasswordScreenCoordinator: CoordinatorProtocol {
|
||||
init(parameters: EncryptionResetPasswordScreenCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
|
||||
viewModel = EncryptionResetPasswordScreenViewModel()
|
||||
viewModel = EncryptionResetPasswordScreenViewModel(passwordPublisher: parameters.passwordPublisher)
|
||||
}
|
||||
|
||||
func start() {
|
||||
@@ -44,8 +39,8 @@ final class EncryptionResetPasswordScreenCoordinator: CoordinatorProtocol {
|
||||
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .resetIdentity(let password):
|
||||
self.actionsSubject.send(.resetIdentity(password))
|
||||
case .passwordEntered:
|
||||
self.actionsSubject.send(.passwordEntered)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
+3
-10
@@ -7,15 +7,8 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
enum EncryptionResetPasswordScreenViewModelAction: CustomStringConvertible {
|
||||
case resetIdentity(String)
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .resetIdentity:
|
||||
"resetIdentity"
|
||||
}
|
||||
}
|
||||
enum EncryptionResetPasswordScreenViewModelAction {
|
||||
case passwordEntered
|
||||
}
|
||||
|
||||
struct EncryptionResetPasswordScreenViewState: BindableState {
|
||||
@@ -28,5 +21,5 @@ struct EncryptionResetPasswordScreenViewStateBindings {
|
||||
}
|
||||
|
||||
enum EncryptionResetPasswordScreenViewAction {
|
||||
case resetIdentity
|
||||
case submit
|
||||
}
|
||||
|
||||
+8
-3
@@ -11,12 +11,16 @@ import SwiftUI
|
||||
typealias EncryptionResetPasswordScreenViewModelType = StateStoreViewModel<EncryptionResetPasswordScreenViewState, EncryptionResetPasswordScreenViewAction>
|
||||
|
||||
class EncryptionResetPasswordScreenViewModel: EncryptionResetPasswordScreenViewModelType, EncryptionResetPasswordScreenViewModelProtocol {
|
||||
private let passwordPublisher: PassthroughSubject<String, Never>
|
||||
|
||||
private let actionsSubject: PassthroughSubject<EncryptionResetPasswordScreenViewModelAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<EncryptionResetPasswordScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init() {
|
||||
init(passwordPublisher: PassthroughSubject<String, Never>) {
|
||||
self.passwordPublisher = passwordPublisher
|
||||
|
||||
super.init(initialViewState: .init(bindings: .init(password: "")))
|
||||
}
|
||||
|
||||
@@ -26,8 +30,9 @@ class EncryptionResetPasswordScreenViewModel: EncryptionResetPasswordScreenViewM
|
||||
MXLog.info("View model: received view action: \(viewAction)")
|
||||
|
||||
switch viewAction {
|
||||
case .resetIdentity:
|
||||
actionsSubject.send(.resetIdentity(state.bindings.password))
|
||||
case .submit:
|
||||
passwordPublisher.send(state.bindings.password)
|
||||
actionsSubject.send(.passwordEntered)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-3
@@ -5,6 +5,7 @@
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Compound
|
||||
import SwiftUI
|
||||
|
||||
@@ -32,9 +33,10 @@ struct EncryptionResetPasswordScreen: View {
|
||||
.padding(16)
|
||||
} bottomContent: {
|
||||
Button(L10n.actionResetIdentity, role: .destructive) {
|
||||
context.send(viewAction: .resetIdentity)
|
||||
context.send(viewAction: .submit)
|
||||
}
|
||||
.buttonStyle(.compound(.primary))
|
||||
.accessibilityIdentifier(A11yIdentifiers.encryptionResetPasswordScreen.submit)
|
||||
}
|
||||
.background()
|
||||
.backgroundStyle(.compound.bgCanvasDefault)
|
||||
@@ -58,8 +60,9 @@ struct EncryptionResetPasswordScreen: View {
|
||||
.focused($textFieldFocus)
|
||||
.submitLabel(.done)
|
||||
.onSubmit {
|
||||
context.send(viewAction: .resetIdentity)
|
||||
context.send(viewAction: .submit)
|
||||
}
|
||||
.accessibilityIdentifier(A11yIdentifiers.encryptionResetPasswordScreen.passwordField)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,7 +70,8 @@ struct EncryptionResetPasswordScreen: View {
|
||||
// MARK: - Previews
|
||||
|
||||
struct EncryptionResetPasswordScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = EncryptionResetPasswordScreenViewModel()
|
||||
static let passwordPublisher = PassthroughSubject<String, Never>()
|
||||
static let viewModel = EncryptionResetPasswordScreenViewModel(passwordPublisher: passwordPublisher)
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
EncryptionResetPasswordScreen(context: viewModel.context)
|
||||
|
||||
+4
-23
@@ -9,14 +9,14 @@ import Combine
|
||||
import SwiftUI
|
||||
|
||||
enum EncryptionResetScreenCoordinatorAction {
|
||||
case cancel
|
||||
case requestOIDCAuthorisation(URL)
|
||||
case requestPassword(passwordPublisher: PassthroughSubject<String, Never>)
|
||||
case resetFinished
|
||||
case cancel
|
||||
}
|
||||
|
||||
struct EncryptionResetScreenCoordinatorParameters {
|
||||
let clientProxy: ClientProxyProtocol
|
||||
let navigationStackCoordinator: NavigationStackCoordinator
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
}
|
||||
|
||||
@@ -43,10 +43,10 @@ final class EncryptionResetScreenCoordinator: CoordinatorProtocol {
|
||||
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .requestPassword:
|
||||
presentPasswordScreen()
|
||||
case .requestOIDCAuthorisation(let url):
|
||||
self.actionsSubject.send(.requestOIDCAuthorisation(url))
|
||||
case .requestPassword(let passwordPublisher):
|
||||
self.actionsSubject.send(.requestPassword(passwordPublisher: passwordPublisher))
|
||||
case .resetFinished:
|
||||
self.actionsSubject.send(.resetFinished)
|
||||
case .cancel:
|
||||
@@ -63,23 +63,4 @@ final class EncryptionResetScreenCoordinator: CoordinatorProtocol {
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(EncryptionResetScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func presentPasswordScreen() {
|
||||
let coordinator = EncryptionResetPasswordScreenCoordinator(parameters: .init())
|
||||
|
||||
coordinator.actionsPublisher.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .resetIdentity(let password):
|
||||
viewModel.continueResetFlowWith(password: password)
|
||||
parameters.navigationStackCoordinator.pop()
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
parameters.navigationStackCoordinator.push(coordinator)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -5,10 +5,11 @@
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
|
||||
enum EncryptionResetScreenViewModelAction {
|
||||
case requestPassword
|
||||
case requestPassword(passwordPublisher: PassthroughSubject<String, Never>)
|
||||
case requestOIDCAuthorisation(url: URL)
|
||||
case resetFinished
|
||||
case cancel
|
||||
|
||||
+9
-7
@@ -21,6 +21,7 @@ class EncryptionResetScreenViewModel: EncryptionResetScreenViewModelType, Encryp
|
||||
}
|
||||
|
||||
private var identityResetHandle: IdentityResetHandle?
|
||||
private var passwordCancellable: AnyCancellable?
|
||||
|
||||
init(clientProxy: ClientProxyProtocol, userIndicatorController: UserIndicatorControllerProtocol) {
|
||||
self.clientProxy = clientProxy
|
||||
@@ -46,12 +47,6 @@ class EncryptionResetScreenViewModel: EncryptionResetScreenViewModelType, Encryp
|
||||
}
|
||||
}
|
||||
|
||||
func continueResetFlowWith(password: String) {
|
||||
Task {
|
||||
await resetWith(password: password)
|
||||
}
|
||||
}
|
||||
|
||||
func stop() {
|
||||
Task {
|
||||
await identityResetHandle?.cancel()
|
||||
@@ -80,7 +75,14 @@ class EncryptionResetScreenViewModel: EncryptionResetScreenViewModelType, Encryp
|
||||
|
||||
switch handle.authType() {
|
||||
case .uiaa:
|
||||
actionsSubject.send(.requestPassword)
|
||||
let passwordPublisher = PassthroughSubject<String, Never>()
|
||||
passwordCancellable = passwordPublisher.sink { [weak self] password in
|
||||
guard let self else { return }
|
||||
passwordCancellable = nil
|
||||
Task { await self.resetWith(password: password) }
|
||||
}
|
||||
|
||||
actionsSubject.send(.requestPassword(passwordPublisher: passwordPublisher))
|
||||
case .oidc(let oidcInfo):
|
||||
guard let url = URL(string: oidcInfo.approvalUrl) else {
|
||||
fatalError("Invalid URL received through identity reset handle: \(oidcInfo.approvalUrl)")
|
||||
|
||||
-1
@@ -12,6 +12,5 @@ protocol EncryptionResetScreenViewModelProtocol {
|
||||
var actionsPublisher: AnyPublisher<EncryptionResetScreenViewModelAction, Never> { get }
|
||||
var context: EncryptionResetScreenViewModelType.Context { get }
|
||||
|
||||
func continueResetFlowWith(password: String)
|
||||
func stop()
|
||||
}
|
||||
|
||||
+1
@@ -19,6 +19,7 @@ struct EncryptionResetScreen: View {
|
||||
context.send(viewAction: .reset)
|
||||
}
|
||||
.buttonStyle(.compound(.primary))
|
||||
.accessibilityIdentifier(A11yIdentifiers.encryptionResetScreen.continueReset)
|
||||
}
|
||||
.background()
|
||||
.backgroundStyle(.compound.bgSubtleSecondary)
|
||||
|
||||
Reference in New Issue
Block a user