Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure multiple mandatory verification flows can be ran consecutively (e.g. following encryption resets) #3722

Merged
merged 2 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {

enum Event: EventType {
case next
case nextSkippingIdentityConfimed
case nextSkippingIdentityConfirmed
}

private let stateMachine: StateMachine<State, Event>
Expand Down Expand Up @@ -79,22 +79,23 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {

stateMachine = .init(state: .initial)

configureStateMachine()

// Verification can change as part of the onboarding flow by verifying with
// another device, using a recovery key or by resetting one's crypto identity.
// It can also happen that onboarding started before it had a chance to update,
// usually seen when registering a new account.
// Handle all those cases here instead of spreading them throughout the code.
verificationStateCancellable = userSession.sessionSecurityStatePublisher
.map(\.verificationState)
.removeDuplicates()
.receive(on: DispatchQueue.main)
.sink { [weak self] value in
guard let self,
value == .verified,
stateMachine.state == .identityConfirmation else { return }

appSettings.hasRunIdentityConfirmationOnboarding = true
stateMachine.tryEvent(.nextSkippingIdentityConfimed)
self.verificationStateCancellable = nil
stefanceriu marked this conversation as resolved.
Show resolved Hide resolved
stateMachine.tryEvent(.nextSkippingIdentityConfirmed)
}
}

Expand All @@ -111,8 +112,6 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
fatalError("This flow coordinator shouldn't have been started")
}

configureStateMachine()

rootNavigationStackCoordinator.setFullScreenCoverCoordinator(navigationStackCoordinator, animated: !isNewLogin)

stateMachine.tryEvent(.next)
Expand Down Expand Up @@ -146,6 +145,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
}

private func configureStateMachine() {
stateMachine.addRoute(.init(fromState: .finished, toState: .initial))
stateMachine.addRouteMapping { [weak self] event, fromState, _ in
guard let self else {
return nil
Expand All @@ -164,7 +164,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
return .finished

case (.identityConfirmation, _, _, _, _):
if event == .nextSkippingIdentityConfimed {
if event == .nextSkippingIdentityConfirmed {
// Used when the verification state has updated to verified
// after starting the onboarding flow
switch (requiresAppLockSetup, requiresAnalyticsSetup, requiresNotificationsSetup) {
Expand Down Expand Up @@ -225,9 +225,18 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
presentNotificationPermissionsScreen()
case (_, _, .finished):
rootNavigationStackCoordinator.setFullScreenCoverCoordinator(nil)
stateMachine.tryState(.initial)
case (.finished, _, .initial):
break
default:
fatalError("Unknown transition: \(context)")
}

if let event = context.event {
MXLog.info("Transitioning from `\(context.fromState)` to `\(context.toState)` with event `\(event)`")
} else {
MXLog.info("Transitioning from \(context.fromState)` to `\(context.toState)`")
}
}

stateMachine.addErrorHandler { context in
Expand All @@ -251,7 +260,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
presentRecoveryKeyScreen()
case .skip:
appSettings.hasRunIdentityConfirmationOnboarding = true
stateMachine.tryEvent(.nextSkippingIdentityConfimed)
stateMachine.tryEvent(.nextSkippingIdentityConfirmed)
case .reset:
startEncryptionResetFlow()
case .logout:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
}

func attemptStartingOnboarding() {
MXLog.info("Attempting to start onboarding")

if onboardingFlowCoordinator.shouldStart {
clearRoute(animated: false)
onboardingFlowCoordinator.start()
Expand Down Expand Up @@ -340,7 +342,6 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
userSession.sessionSecurityStatePublisher
.map(\.verificationState)
.filter { $0 != .unknown }
.removeDuplicates()
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
guard let self else { return }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ struct SessionVerificationScreen: View {
.background()
.backgroundStyle(.compound.bgCanvasDefault)
.interactiveDismissDisabled()
.navigationBarBackButtonHidden(context.viewState.verificationState == .verified)
}

// MARK: - Private
Expand Down
1 change: 0 additions & 1 deletion ElementX/Sources/Services/Session/UserSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ class UserSession: UserSessionProtocol {
MXLog.info("Session security state changed, verificationState: \($0), recoveryState: \($1)")
return SessionSecurityState(verificationState: $0, recoveryState: $1)
}
.removeDuplicates()
.receive(on: DispatchQueue.main)
.sink { [weak self] value in
self?.sessionSecurityStateSubject.send(value)
Expand Down
Loading