mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
Add a state machine to the AuthenticationFlowCoordinator. (#4103)
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Combine
|
import Combine
|
||||||
|
import SwiftState
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
@@ -24,6 +25,72 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||||
private let qrCodeLoginService: QRCodeLoginServiceProtocol
|
private let qrCodeLoginService: QRCodeLoginServiceProtocol
|
||||||
|
|
||||||
|
enum State: StateType {
|
||||||
|
/// The state machine hasn't started.
|
||||||
|
case initial
|
||||||
|
|
||||||
|
/// The initial screen shown when you first launch the app.
|
||||||
|
case startScreen
|
||||||
|
|
||||||
|
/// The screen used for the whole QR Code flow.
|
||||||
|
case qrCodeLoginScreen
|
||||||
|
|
||||||
|
/// The screen to continue authentication with the current server.
|
||||||
|
case serverConfirmationScreen
|
||||||
|
/// The screen to choose a different server.
|
||||||
|
case serverSelectionScreen
|
||||||
|
/// The web authentication session is being presented.
|
||||||
|
case oidcAuthentication
|
||||||
|
/// The screen to login with a password.
|
||||||
|
case loginScreen
|
||||||
|
|
||||||
|
/// The screen to report an error.
|
||||||
|
case bugReportFlow
|
||||||
|
|
||||||
|
/// The flow is complete.
|
||||||
|
case complete
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Event: EventType {
|
||||||
|
/// The flow is being started.
|
||||||
|
case start
|
||||||
|
|
||||||
|
/// The user would like to login with a QR code.
|
||||||
|
case loginWithQR
|
||||||
|
/// The user would like to login without a QR code.
|
||||||
|
case loginManually
|
||||||
|
/// The user would like to register a new account.
|
||||||
|
case register
|
||||||
|
/// The user encountered a problem.
|
||||||
|
case reportProblem
|
||||||
|
|
||||||
|
/// The QR login flow was aborted.
|
||||||
|
case cancelledLoginWithQR
|
||||||
|
/// The user aborted manual login.
|
||||||
|
case cancelledServerConfirmation
|
||||||
|
|
||||||
|
/// The user would like to enter a different server.
|
||||||
|
case changeServer(AuthenticationFlow)
|
||||||
|
/// The user is no longer selecting a server.
|
||||||
|
case dismissedServerSelection
|
||||||
|
|
||||||
|
/// Show the web authentication session for OIDC.
|
||||||
|
case continueWithOIDC
|
||||||
|
/// The web authentication session was aborted.
|
||||||
|
case cancelledOIDCAuthentication
|
||||||
|
/// Show the screen to login with password.
|
||||||
|
case continueWithPassword
|
||||||
|
/// The password login was aborted.
|
||||||
|
case cancelledPasswordLogin
|
||||||
|
|
||||||
|
/// The user has finished reporting a problem (or viewing the logs).
|
||||||
|
case bugReportFlowComplete
|
||||||
|
|
||||||
|
/// The user has successfully signed in!
|
||||||
|
case signedIn
|
||||||
|
}
|
||||||
|
|
||||||
|
private let stateMachine: StateMachine<State, Event>
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
private var oidcPresenter: OIDCAuthenticationPresenter?
|
private var oidcPresenter: OIDCAuthenticationPresenter?
|
||||||
@@ -51,10 +118,13 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
self.qrCodeLoginService = qrCodeLoginService
|
self.qrCodeLoginService = qrCodeLoginService
|
||||||
|
|
||||||
navigationStackCoordinator = NavigationStackCoordinator()
|
navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
|
|
||||||
|
stateMachine = .init(state: .initial)
|
||||||
|
configureStateMachine()
|
||||||
}
|
}
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
showStartScreen()
|
stateMachine.tryEvent(.start)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
||||||
@@ -65,7 +135,78 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Setup
|
||||||
|
|
||||||
|
private func configureStateMachine() {
|
||||||
|
stateMachine.addRoutes(event: .start, transitions: [.initial => .startScreen]) { [weak self] _ in
|
||||||
|
self?.showStartScreen()
|
||||||
|
}
|
||||||
|
|
||||||
|
// QR Code
|
||||||
|
|
||||||
|
stateMachine.addRoutes(event: .loginWithQR, transitions: [.startScreen => .qrCodeLoginScreen]) { [weak self] _ in
|
||||||
|
self?.showQRCodeLoginScreen()
|
||||||
|
}
|
||||||
|
stateMachine.addRoutes(event: .cancelledLoginWithQR, transitions: [.qrCodeLoginScreen => .startScreen])
|
||||||
|
|
||||||
|
// Manual Authentication
|
||||||
|
|
||||||
|
stateMachine.addRoutes(event: .loginManually, transitions: [.startScreen => .serverConfirmationScreen]) { [weak self] _ in
|
||||||
|
self?.showServerConfirmationScreen(authenticationFlow: .login)
|
||||||
|
}
|
||||||
|
stateMachine.addRoutes(event: .register, transitions: [.startScreen => .serverConfirmationScreen]) { [weak self] _ in
|
||||||
|
self?.showServerConfirmationScreen(authenticationFlow: .register)
|
||||||
|
}
|
||||||
|
stateMachine.addRoutes(event: .cancelledServerConfirmation, transitions: [.serverConfirmationScreen => .startScreen])
|
||||||
|
|
||||||
|
stateMachine.addRoutes(event: .changeServer(.login), transitions: [.serverConfirmationScreen => .serverSelectionScreen]) { [weak self] _ in
|
||||||
|
self?.showServerSelectionScreen(authenticationFlow: .login)
|
||||||
|
}
|
||||||
|
stateMachine.addRoutes(event: .changeServer(.register), transitions: [.serverConfirmationScreen => .serverSelectionScreen]) { [weak self] _ in
|
||||||
|
self?.showServerSelectionScreen(authenticationFlow: .register)
|
||||||
|
}
|
||||||
|
stateMachine.addRoutes(event: .dismissedServerSelection, transitions: [.serverSelectionScreen => .serverConfirmationScreen])
|
||||||
|
|
||||||
|
stateMachine.addRoutes(event: .continueWithOIDC, transitions: [.serverConfirmationScreen => .oidcAuthentication]) { [weak self] context in
|
||||||
|
guard let (oidcData, window) = context.userInfo as? (OIDCAuthorizationDataProxy, UIWindow) else {
|
||||||
|
fatalError("Missing the OIDC data and presentation anchor.")
|
||||||
|
}
|
||||||
|
self?.showOIDCAuthentication(oidcData: oidcData, presentationAnchor: window)
|
||||||
|
}
|
||||||
|
stateMachine.addRoutes(event: .cancelledOIDCAuthentication, transitions: [.oidcAuthentication => .serverConfirmationScreen])
|
||||||
|
|
||||||
|
stateMachine.addRoutes(event: .continueWithPassword, transitions: [.serverConfirmationScreen => .loginScreen]) { [weak self] _ in
|
||||||
|
self?.showLoginScreen()
|
||||||
|
}
|
||||||
|
stateMachine.addRoutes(event: .cancelledPasswordLogin, transitions: [.loginScreen => .serverConfirmationScreen])
|
||||||
|
|
||||||
|
// Bug Report
|
||||||
|
|
||||||
|
stateMachine.addRoutes(event: .reportProblem, transitions: [.startScreen => .bugReportFlow]) { [weak self] _ in
|
||||||
|
self?.startBugReportFlow()
|
||||||
|
}
|
||||||
|
stateMachine.addRoutes(event: .bugReportFlowComplete, transitions: [.bugReportFlow => .startScreen])
|
||||||
|
|
||||||
|
// Completion
|
||||||
|
|
||||||
|
stateMachine.addRoutes(event: .signedIn, transitions: [.qrCodeLoginScreen => .complete,
|
||||||
|
.oidcAuthentication => .complete,
|
||||||
|
.loginScreen => .complete]) { [weak self] context in
|
||||||
|
guard let userSession = context.userInfo as? UserSessionProtocol else { fatalError("The user session wasn't included in the context") }
|
||||||
|
self?.userHasSignedIn(userSession: userSession)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unhandled
|
||||||
|
|
||||||
|
stateMachine.addErrorHandler { context in
|
||||||
|
switch (context.fromState, context.toState) {
|
||||||
|
case (.complete, .complete):
|
||||||
|
break // Ignore all events triggered by
|
||||||
|
default:
|
||||||
|
fatalError("Unexpected transition: \(context)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func showStartScreen() {
|
private func showStartScreen() {
|
||||||
let parameters = AuthenticationStartScreenParameters(showCreateAccountButton: appSettings.showCreateAccountButton,
|
let parameters = AuthenticationStartScreenParameters(showCreateAccountButton: appSettings.showCreateAccountButton,
|
||||||
@@ -77,14 +218,14 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case .loginManually:
|
|
||||||
showServerConfirmationScreen(authenticationFlow: .login)
|
|
||||||
case .loginWithQR:
|
case .loginWithQR:
|
||||||
startQRCodeLogin()
|
stateMachine.tryEvent(.loginWithQR)
|
||||||
|
case .loginManually:
|
||||||
|
stateMachine.tryEvent(.loginManually)
|
||||||
case .register:
|
case .register:
|
||||||
showServerConfirmationScreen(authenticationFlow: .register)
|
stateMachine.tryEvent(.register)
|
||||||
case .reportProblem:
|
case .reportProblem:
|
||||||
showReportProblemScreen()
|
stateMachine.tryEvent(.reportProblem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
@@ -94,7 +235,9 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
navigationRootCoordinator.setRootCoordinator(navigationStackCoordinator)
|
navigationRootCoordinator.setRootCoordinator(navigationStackCoordinator)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func startQRCodeLogin() {
|
// MARK: - QR Code
|
||||||
|
|
||||||
|
private func showQRCodeLoginScreen() {
|
||||||
let coordinator = QRCodeLoginScreenCoordinator(parameters: .init(qrCodeLoginService: qrCodeLoginService,
|
let coordinator = QRCodeLoginScreenCoordinator(parameters: .init(qrCodeLoginService: qrCodeLoginService,
|
||||||
orientationManager: appMediator.windowManager,
|
orientationManager: appMediator.windowManager,
|
||||||
appMediator: appMediator))
|
appMediator: appMediator))
|
||||||
@@ -105,29 +248,25 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
switch action {
|
switch action {
|
||||||
case .signInManually:
|
case .signInManually:
|
||||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
showServerConfirmationScreen(authenticationFlow: .login)
|
stateMachine.tryEvent(.cancelledLoginWithQR)
|
||||||
|
stateMachine.tryEvent(.loginManually)
|
||||||
case .cancel:
|
case .cancel:
|
||||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
|
stateMachine.tryEvent(.cancelledLoginWithQR)
|
||||||
case .done(let userSession):
|
case .done(let userSession):
|
||||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||||
// Since the qr code login flow includes verification
|
// Since the qr code login flow includes verification
|
||||||
appSettings.hasRunIdentityConfirmationOnboarding = true
|
appSettings.hasRunIdentityConfirmationOnboarding = true
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.userHasSignedIn(userSession: userSession)
|
self.stateMachine.tryEvent(.signedIn, userInfo: userSession)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
navigationStackCoordinator.setSheetCoordinator(coordinator)
|
navigationStackCoordinator.setSheetCoordinator(coordinator) // Don't use the callback (interactive dismiss disabled), choose the event with the action.
|
||||||
}
|
}
|
||||||
|
|
||||||
private func showReportProblemScreen() {
|
// MARK: - Manual Authentication
|
||||||
bugReportFlowCoordinator = BugReportFlowCoordinator(parameters: .init(presentationMode: .sheet(navigationStackCoordinator),
|
|
||||||
userIndicatorController: userIndicatorController,
|
|
||||||
bugReportService: bugReportService,
|
|
||||||
userSession: nil))
|
|
||||||
bugReportFlowCoordinator?.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func showServerConfirmationScreen(authenticationFlow: AuthenticationFlow) {
|
private func showServerConfirmationScreen(authenticationFlow: AuthenticationFlow) {
|
||||||
// Reset the service back to the default homeserver before continuing. This ensures
|
// Reset the service back to the default homeserver before continuing. This ensures
|
||||||
@@ -144,16 +283,18 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case .continueWithOIDC(let oidcData, let window):
|
case .continueWithOIDC(let oidcData, let window):
|
||||||
showOIDCAuthentication(oidcData: oidcData, presentationAnchor: window)
|
stateMachine.tryEvent(.continueWithOIDC, userInfo: (oidcData, window))
|
||||||
case .continueWithPassword:
|
case .continueWithPassword:
|
||||||
showLoginScreen()
|
stateMachine.tryEvent(.continueWithPassword)
|
||||||
case .changeServer:
|
case .changeServer:
|
||||||
showServerSelectionScreen(authenticationFlow: authenticationFlow)
|
stateMachine.tryEvent(.changeServer(authenticationFlow))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
navigationStackCoordinator.push(coordinator)
|
navigationStackCoordinator.push(coordinator) { [weak self] in
|
||||||
|
self?.stateMachine.tryEvent(.cancelledServerConfirmation)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func showServerSelectionScreen(authenticationFlow: AuthenticationFlow) {
|
private func showServerSelectionScreen(authenticationFlow: AuthenticationFlow) {
|
||||||
@@ -178,7 +319,9 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
navigationCoordinator.setRootCoordinator(coordinator)
|
navigationCoordinator.setRootCoordinator(coordinator)
|
||||||
navigationStackCoordinator.setSheetCoordinator(navigationCoordinator)
|
navigationStackCoordinator.setSheetCoordinator(navigationCoordinator) { [weak self] in
|
||||||
|
self?.stateMachine.tryEvent(.dismissedServerSelection)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func showOIDCAuthentication(oidcData: OIDCAuthorizationDataProxy, presentationAnchor: UIWindow) {
|
private func showOIDCAuthentication(oidcData: OIDCAuthorizationDataProxy, presentationAnchor: UIWindow) {
|
||||||
@@ -191,9 +334,10 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
Task {
|
Task {
|
||||||
switch await presenter.authenticate(using: oidcData) {
|
switch await presenter.authenticate(using: oidcData) {
|
||||||
case .success(let userSession):
|
case .success(let userSession):
|
||||||
userHasSignedIn(userSession: userSession)
|
stateMachine.tryEvent(.signedIn, userInfo: userSession)
|
||||||
case .failure:
|
case .failure:
|
||||||
break // Nothing to do, the alerts are handled by the presenter.
|
stateMachine.tryEvent(.cancelledOIDCAuthentication)
|
||||||
|
// Nothing more to do, the alerts are handled by the presenter.
|
||||||
}
|
}
|
||||||
oidcPresenter = nil
|
oidcPresenter = nil
|
||||||
}
|
}
|
||||||
@@ -211,7 +355,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case .signedIn(let userSession):
|
case .signedIn(let userSession):
|
||||||
userHasSignedIn(userSession: userSession)
|
stateMachine.tryEvent(.signedIn, userInfo: userSession)
|
||||||
case .configuredForOIDC:
|
case .configuredForOIDC:
|
||||||
// Pop back to the confirmation screen for OIDC login to continue.
|
// Pop back to the confirmation screen for OIDC login to continue.
|
||||||
navigationStackCoordinator.pop(animated: false)
|
navigationStackCoordinator.pop(animated: false)
|
||||||
@@ -219,9 +363,32 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
navigationStackCoordinator.push(coordinator)
|
navigationStackCoordinator.push(coordinator) { [weak self] in
|
||||||
|
self?.stateMachine.tryEvent(.cancelledPasswordLogin)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Bug Report
|
||||||
|
|
||||||
|
private func startBugReportFlow() {
|
||||||
|
let coordinator = BugReportFlowCoordinator(parameters: .init(presentationMode: .sheet(navigationStackCoordinator),
|
||||||
|
userIndicatorController: userIndicatorController,
|
||||||
|
bugReportService: bugReportService,
|
||||||
|
userSession: nil))
|
||||||
|
coordinator.actionsPublisher.sink { [weak self] action in
|
||||||
|
switch action {
|
||||||
|
case .complete:
|
||||||
|
self?.stateMachine.tryEvent(.bugReportFlowComplete)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
bugReportFlowCoordinator = coordinator
|
||||||
|
coordinator.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Completion
|
||||||
|
|
||||||
private func userHasSignedIn(userSession: UserSessionProtocol) {
|
private func userHasSignedIn(userSession: UserSessionProtocol) {
|
||||||
delegate?.authenticationFlowCoordinator(didLoginWithSession: userSession)
|
delegate?.authenticationFlowCoordinator(didLoginWithSession: userSession)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,11 @@
|
|||||||
import Combine
|
import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
enum BugReportFlowCoordinatorAction: Equatable {
|
||||||
|
/// The flow is complete.
|
||||||
|
case complete
|
||||||
|
}
|
||||||
|
|
||||||
struct BugReportFlowCoordinatorParameters {
|
struct BugReportFlowCoordinatorParameters {
|
||||||
enum PresentationMode {
|
enum PresentationMode {
|
||||||
case sheet(NavigationStackCoordinator)
|
case sheet(NavigationStackCoordinator)
|
||||||
@@ -24,6 +29,11 @@ class BugReportFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
private let parameters: BugReportFlowCoordinatorParameters
|
private let parameters: BugReportFlowCoordinatorParameters
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
private let actionsSubject: PassthroughSubject<BugReportFlowCoordinatorAction, Never> = .init()
|
||||||
|
var actionsPublisher: AnyPublisher<BugReportFlowCoordinatorAction, Never> {
|
||||||
|
actionsSubject.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
private var internalNavigationStackCoordinator: NavigationStackCoordinator?
|
private var internalNavigationStackCoordinator: NavigationStackCoordinator?
|
||||||
|
|
||||||
private var isModallyPresented: Bool {
|
private var isModallyPresented: Bool {
|
||||||
@@ -79,11 +89,15 @@ class BugReportFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
case .sheet(let navigationStackCoordinator):
|
case .sheet(let navigationStackCoordinator):
|
||||||
let internalNavigationStackCoordinator = NavigationStackCoordinator()
|
let internalNavigationStackCoordinator = NavigationStackCoordinator()
|
||||||
internalNavigationStackCoordinator.setRootCoordinator(coordinator)
|
internalNavigationStackCoordinator.setRootCoordinator(coordinator)
|
||||||
navigationStackCoordinator.setSheetCoordinator(internalNavigationStackCoordinator)
|
navigationStackCoordinator.setSheetCoordinator(internalNavigationStackCoordinator) { [weak self] in
|
||||||
|
self?.actionsSubject.send(.complete)
|
||||||
|
}
|
||||||
self.internalNavigationStackCoordinator = internalNavigationStackCoordinator
|
self.internalNavigationStackCoordinator = internalNavigationStackCoordinator
|
||||||
case .push(let navigationStackCoordinator):
|
case .push(let navigationStackCoordinator):
|
||||||
internalNavigationStackCoordinator = navigationStackCoordinator
|
internalNavigationStackCoordinator = navigationStackCoordinator
|
||||||
navigationStackCoordinator.push(coordinator)
|
navigationStackCoordinator.push(coordinator) { [weak self] in
|
||||||
|
self?.actionsSubject.send(.complete)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,6 @@ class EncryptionSettingsFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||||
private let navigationStackCoordinator: NavigationStackCoordinator
|
private let navigationStackCoordinator: NavigationStackCoordinator
|
||||||
|
|
||||||
// periphery:ignore - retaining purpose
|
|
||||||
private var encryptionResetFlowCoordinator: EncryptionResetFlowCoordinator?
|
|
||||||
|
|
||||||
enum State: StateType {
|
enum State: StateType {
|
||||||
/// The state machine hasn't started.
|
/// The state machine hasn't started.
|
||||||
case initial
|
case initial
|
||||||
|
|||||||
+3
-3
@@ -10,15 +10,15 @@ import SwiftUI
|
|||||||
// MARK: - Coordinator
|
// MARK: - Coordinator
|
||||||
|
|
||||||
enum AuthenticationStartScreenCoordinatorAction {
|
enum AuthenticationStartScreenCoordinatorAction {
|
||||||
case loginManually
|
|
||||||
case loginWithQR
|
case loginWithQR
|
||||||
|
case loginManually
|
||||||
case register
|
case register
|
||||||
case reportProblem
|
case reportProblem
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AuthenticationStartScreenViewModelAction {
|
enum AuthenticationStartScreenViewModelAction {
|
||||||
case loginManually
|
|
||||||
case loginWithQR
|
case loginWithQR
|
||||||
|
case loginManually
|
||||||
case register
|
case register
|
||||||
case reportProblem
|
case reportProblem
|
||||||
}
|
}
|
||||||
@@ -30,8 +30,8 @@ struct AuthenticationStartScreenViewState: BindableState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum AuthenticationStartScreenViewAction {
|
enum AuthenticationStartScreenViewAction {
|
||||||
case loginManually
|
|
||||||
case loginWithQR
|
case loginWithQR
|
||||||
|
case loginManually
|
||||||
case register
|
case register
|
||||||
case reportProblem
|
case reportProblem
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -25,10 +25,10 @@ class AuthenticationStartScreenViewModel: AuthenticationStartScreenViewModelType
|
|||||||
|
|
||||||
override func process(viewAction: AuthenticationStartScreenViewAction) {
|
override func process(viewAction: AuthenticationStartScreenViewAction) {
|
||||||
switch viewAction {
|
switch viewAction {
|
||||||
case .loginManually:
|
|
||||||
actionsSubject.send(.loginManually)
|
|
||||||
case .loginWithQR:
|
case .loginWithQR:
|
||||||
actionsSubject.send(.loginWithQR)
|
actionsSubject.send(.loginWithQR)
|
||||||
|
case .loginManually:
|
||||||
|
actionsSubject.send(.loginManually)
|
||||||
case .register:
|
case .register:
|
||||||
actionsSubject.send(.register)
|
actionsSubject.send(.register)
|
||||||
case .reportProblem:
|
case .reportProblem:
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ enum OIDCError: Error {
|
|||||||
case unknown
|
case unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
struct OIDCAuthorizationDataProxy: Equatable {
|
struct OIDCAuthorizationDataProxy: Hashable {
|
||||||
let underlyingData: OAuthAuthorizationData
|
let underlyingData: OAuthAuthorizationData
|
||||||
|
|
||||||
var url: URL {
|
var url: URL {
|
||||||
@@ -77,8 +77,12 @@ struct OIDCAuthorizationDataProxy: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension OAuthAuthorizationData: @retroactive Equatable {
|
extension OAuthAuthorizationData: @retroactive Hashable {
|
||||||
public static func == (lhs: MatrixRustSDK.OAuthAuthorizationData, rhs: MatrixRustSDK.OAuthAuthorizationData) -> Bool {
|
public static func == (lhs: MatrixRustSDK.OAuthAuthorizationData, rhs: MatrixRustSDK.OAuthAuthorizationData) -> Bool {
|
||||||
lhs.loginUrl() == rhs.loginUrl()
|
lhs.loginUrl() == rhs.loginUrl()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func hash(into hasher: inout Hasher) {
|
||||||
|
hasher.combine(loginUrl())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user