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

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

* Disabled the back button on the verification screen only when verified and waiting for the security state publisher
This commit is contained in:
Stefan Ceriu 2025-01-31 17:31:21 +02:00 committed by GitHub
parent 4856ffd3b2
commit 97069850f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 20 additions and 10 deletions

View File

@ -38,7 +38,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
enum Event: EventType {
case next
case nextSkippingIdentityConfimed
case nextSkippingIdentityConfirmed
}
private let stateMachine: StateMachine<State, Event>
@ -79,6 +79,8 @@ 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,
@ -86,15 +88,14 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
// 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
stateMachine.tryEvent(.nextSkippingIdentityConfirmed)
}
}
@ -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)
@ -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
@ -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) {
@ -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
@ -251,7 +260,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
presentRecoveryKeyScreen()
case .skip:
appSettings.hasRunIdentityConfirmationOnboarding = true
stateMachine.tryEvent(.nextSkippingIdentityConfimed)
stateMachine.tryEvent(.nextSkippingIdentityConfirmed)
case .reset:
startEncryptionResetFlow()
case .logout:

View File

@ -220,6 +220,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
}
func attemptStartingOnboarding() {
MXLog.info("Attempting to start onboarding")
if onboardingFlowCoordinator.shouldStart {
clearRoute(animated: false)
onboardingFlowCoordinator.start()
@ -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 }

View File

@ -24,6 +24,7 @@ struct SessionVerificationScreen: View {
.background()
.backgroundStyle(.compound.bgCanvasDefault)
.interactiveDismissDisabled()
.navigationBarBackButtonHidden(context.viewState.verificationState == .verified)
}
// MARK: - Private

View File

@ -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)