diff --git a/Riot/Modules/Onboarding/AuthenticationCoordinator.swift b/Riot/Modules/Onboarding/AuthenticationCoordinator.swift index 5e794f14f..b46da79ec 100644 --- a/Riot/Modules/Onboarding/AuthenticationCoordinator.swift +++ b/Riot/Modules/Onboarding/AuthenticationCoordinator.swift @@ -233,6 +233,9 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc let store = MXFileStore(credentials: credentials) let userDisplayName = await store.displayName(ofUserWithId: userId) ?? "" + let cryptoStore = MXRealmCryptoStore(credentials: credentials) + let keyBackupNeeded = (cryptoStore?.inboundGroupSessions(toBackup: 1) ?? []).count > 0 + let softLogoutCredentials = SoftLogoutCredentials(userId: userId, homeserverName: credentials.homeServerName() ?? "", userDisplayName: userDisplayName, @@ -240,7 +243,8 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc let parameters = AuthenticationSoftLogoutCoordinatorParameters(navigationRouter: navigationRouter, authenticationService: authenticationService, - credentials: softLogoutCredentials) + credentials: softLogoutCredentials, + keyBackupNeeded: keyBackupNeeded) let coordinator = AuthenticationSoftLogoutCoordinator(parameters: parameters) coordinator.callback = { [weak self] result in guard let self = self else { return } diff --git a/RiotSwiftUI/Modules/Authentication/SoftLogout/AuthenticationSoftLogoutModels.swift b/RiotSwiftUI/Modules/Authentication/SoftLogout/AuthenticationSoftLogoutModels.swift index f569706ad..ab414b306 100644 --- a/RiotSwiftUI/Modules/Authentication/SoftLogout/AuthenticationSoftLogoutModels.swift +++ b/RiotSwiftUI/Modules/Authentication/SoftLogout/AuthenticationSoftLogoutModels.swift @@ -42,11 +42,15 @@ enum AuthenticationSoftLogoutViewModelResult { // MARK: View struct AuthenticationSoftLogoutViewState: BindableState { + /// Soft logout credentials var credentials: SoftLogoutCredentials /// Data about the selected homeserver. var homeserver: AuthenticationHomeserverViewData + /// Flag indicating soft logged out user needs backup for some keys + var keyBackupNeeded: Bool + /// View state that can be bound to from SwiftUI. var bindings: AuthenticationSoftLogoutBindings @@ -59,6 +63,11 @@ struct AuthenticationSoftLogoutViewState: BindableState { var showSSOButtons: Bool { !homeserver.ssoIdentityProviders.isEmpty } + + /// Whether to show recover encryption keys message + var showRecoverEncryptionKeysMessage: Bool { + keyBackupNeeded + } /// Whether the password is valid and the user can continue. var hasInvalidPassword: Bool { diff --git a/RiotSwiftUI/Modules/Authentication/SoftLogout/AuthenticationSoftLogoutViewModel.swift b/RiotSwiftUI/Modules/Authentication/SoftLogout/AuthenticationSoftLogoutViewModel.swift index 3f059004a..bd9fcee28 100644 --- a/RiotSwiftUI/Modules/Authentication/SoftLogout/AuthenticationSoftLogoutViewModel.swift +++ b/RiotSwiftUI/Modules/Authentication/SoftLogout/AuthenticationSoftLogoutViewModel.swift @@ -33,10 +33,12 @@ class AuthenticationSoftLogoutViewModel: AuthenticationSoftLogoutViewModelType, init(credentials: SoftLogoutCredentials, homeserver: AuthenticationHomeserverViewData, + keyBackupNeeded: Bool, password: String = "") { let bindings = AuthenticationSoftLogoutBindings(password: password) let viewState = AuthenticationSoftLogoutViewState(credentials: credentials, homeserver: homeserver, + keyBackupNeeded: keyBackupNeeded, bindings: bindings) super.init(initialViewState: viewState) } diff --git a/RiotSwiftUI/Modules/Authentication/SoftLogout/Coordinator/AuthenticationSoftLogoutCoordinator.swift b/RiotSwiftUI/Modules/Authentication/SoftLogout/Coordinator/AuthenticationSoftLogoutCoordinator.swift index 412685f96..466a7f8d4 100644 --- a/RiotSwiftUI/Modules/Authentication/SoftLogout/Coordinator/AuthenticationSoftLogoutCoordinator.swift +++ b/RiotSwiftUI/Modules/Authentication/SoftLogout/Coordinator/AuthenticationSoftLogoutCoordinator.swift @@ -21,6 +21,7 @@ struct AuthenticationSoftLogoutCoordinatorParameters { let navigationRouter: NavigationRouterType let authenticationService: AuthenticationService let credentials: SoftLogoutCredentials + let keyBackupNeeded: Bool } enum AuthenticationSoftLogoutCoordinatorResult { @@ -74,7 +75,8 @@ final class AuthenticationSoftLogoutCoordinator: Coordinator, Presentable { let homeserver = parameters.authenticationService.state.homeserver let viewModel = AuthenticationSoftLogoutViewModel(credentials: parameters.credentials, - homeserver: homeserver.viewData) + homeserver: homeserver.viewData, + keyBackupNeeded: parameters.keyBackupNeeded) let view = AuthenticationSoftLogoutScreen(viewModel: viewModel.context) authenticationSoftLogoutViewModel = viewModel authenticationSoftLogoutHostingController = VectorHostingController(rootView: view) diff --git a/RiotSwiftUI/Modules/Authentication/SoftLogout/MockAuthenticationSoftLogoutScreenState.swift b/RiotSwiftUI/Modules/Authentication/SoftLogout/MockAuthenticationSoftLogoutScreenState.swift index 3f71e8397..160cf5c08 100644 --- a/RiotSwiftUI/Modules/Authentication/SoftLogout/MockAuthenticationSoftLogoutScreenState.swift +++ b/RiotSwiftUI/Modules/Authentication/SoftLogout/MockAuthenticationSoftLogoutScreenState.swift @@ -29,6 +29,7 @@ enum MockAuthenticationSoftLogoutScreenState: MockScreenState, CaseIterable { case ssoOnly case noSSO case fallback + case noKeyBackup /// The associated screen var screenType: Any.Type { @@ -45,20 +46,29 @@ enum MockAuthenticationSoftLogoutScreenState: MockScreenState, CaseIterable { switch self { case .emptyPassword: viewModel = AuthenticationSoftLogoutViewModel(credentials: credentials, - homeserver: .mockMatrixDotOrg) + homeserver: .mockMatrixDotOrg, + keyBackupNeeded: true) case .enteredPassword: viewModel = AuthenticationSoftLogoutViewModel(credentials: credentials, homeserver: .mockMatrixDotOrg, + keyBackupNeeded: true, password: "12345678") case .ssoOnly: viewModel = AuthenticationSoftLogoutViewModel(credentials: credentials, - homeserver: .mockEnterpriseSSO) + homeserver: .mockEnterpriseSSO, + keyBackupNeeded: true) case .noSSO: viewModel = AuthenticationSoftLogoutViewModel(credentials: credentials, - homeserver: .mockBasicServer) + homeserver: .mockBasicServer, + keyBackupNeeded: true) case .fallback: viewModel = AuthenticationSoftLogoutViewModel(credentials: credentials, - homeserver: .mockFallback) + homeserver: .mockFallback, + keyBackupNeeded: true) + case .noKeyBackup: + viewModel = AuthenticationSoftLogoutViewModel(credentials: credentials, + homeserver: .mockFallback, + keyBackupNeeded: false) } // can simulate service and viewModel actions here if needs be. diff --git a/RiotSwiftUI/Modules/Authentication/SoftLogout/Test/UI/AuthenticationSoftLogoutUITests.swift b/RiotSwiftUI/Modules/Authentication/SoftLogout/Test/UI/AuthenticationSoftLogoutUITests.swift index 09bb661c9..52bd56ca5 100644 --- a/RiotSwiftUI/Modules/Authentication/SoftLogout/Test/UI/AuthenticationSoftLogoutUITests.swift +++ b/RiotSwiftUI/Modules/Authentication/SoftLogout/Test/UI/AuthenticationSoftLogoutUITests.swift @@ -40,6 +40,8 @@ class AuthenticationSoftLogoutUITests: MockScreenTest { verifyNoSSO() case .fallback: verifyFallback() + case .noKeyBackup: + verifyNoKeyBackup() } } @@ -195,4 +197,8 @@ class AuthenticationSoftLogoutUITests: MockScreenTest { XCTAssertEqual(ssoButtons.count, 0, "There should be no SSO button shown.") } + func verifyNoKeyBackup() { + XCTAssertFalse(app.staticTexts["messageLabel2"].exists, "The message 2 should not be shown.") + } + } diff --git a/RiotSwiftUI/Modules/Authentication/SoftLogout/Test/Unit/AuthenticationSoftLogoutViewModelTests.swift b/RiotSwiftUI/Modules/Authentication/SoftLogout/Test/Unit/AuthenticationSoftLogoutViewModelTests.swift index 0b74e1004..4d9c8142c 100644 --- a/RiotSwiftUI/Modules/Authentication/SoftLogout/Test/Unit/AuthenticationSoftLogoutViewModelTests.swift +++ b/RiotSwiftUI/Modules/Authentication/SoftLogout/Test/Unit/AuthenticationSoftLogoutViewModelTests.swift @@ -26,7 +26,8 @@ class AuthenticationSoftLogoutViewModelTests: XCTestCase { userDisplayName: "mock_username", deviceId: nil) let viewModel = AuthenticationSoftLogoutViewModel(credentials: credentials, - homeserver: .mockMatrixDotOrg) + homeserver: .mockMatrixDotOrg, + keyBackupNeeded: true) let context = viewModel.context // Given a view model where the user hasn't yet sent the verification email. @@ -34,6 +35,7 @@ class AuthenticationSoftLogoutViewModelTests: XCTestCase { XCTAssert(context.viewState.hasInvalidPassword, "The view model should start with an invalid password.") XCTAssert(context.viewState.showSSOButtons, "The view model should show SSO buttons for the given homeserver.") XCTAssert(context.viewState.showLoginForm, "The view model should show login form for the given homeserver.") + XCTAssert(context.viewState.showRecoverEncryptionKeysMessage, "The view model should show recover encryption keys message.") } @MainActor func testInitialStateForNoSSO() async { @@ -42,7 +44,8 @@ class AuthenticationSoftLogoutViewModelTests: XCTestCase { userDisplayName: "mock_username", deviceId: nil) let viewModel = AuthenticationSoftLogoutViewModel(credentials: credentials, - homeserver: .mockBasicServer) + homeserver: .mockBasicServer, + keyBackupNeeded: false) let context = viewModel.context // Given a view model where the user hasn't yet sent the verification email. @@ -50,6 +53,7 @@ class AuthenticationSoftLogoutViewModelTests: XCTestCase { XCTAssert(context.viewState.hasInvalidPassword, "The view model should start with an invalid password.") XCTAssertFalse(context.viewState.showSSOButtons, "The view model should not show SSO buttons for the given homeserver.") XCTAssert(context.viewState.showLoginForm, "The view model should show login form for the given homeserver.") + XCTAssertFalse(context.viewState.showRecoverEncryptionKeysMessage, "The view model should not show recover encryption keys message.") } } diff --git a/RiotSwiftUI/Modules/Authentication/SoftLogout/View/AuthenticationSoftLogoutScreen.swift b/RiotSwiftUI/Modules/Authentication/SoftLogout/View/AuthenticationSoftLogoutScreen.swift index 907d48676..df0ce24b1 100644 --- a/RiotSwiftUI/Modules/Authentication/SoftLogout/View/AuthenticationSoftLogoutScreen.swift +++ b/RiotSwiftUI/Modules/Authentication/SoftLogout/View/AuthenticationSoftLogoutScreen.swift @@ -77,11 +77,13 @@ struct AuthenticationSoftLogoutScreen: View { .foregroundColor(theme.colors.primaryContent) .accessibilityIdentifier("messageLabel1") - Text(VectorL10n.authSoftlogoutRecoverEncryptionKeys) - .font(theme.fonts.body) - .multilineTextAlignment(.leading) - .foregroundColor(theme.colors.primaryContent) - .accessibilityIdentifier("messageLabel2") + if viewModel.viewState.showRecoverEncryptionKeysMessage { + Text(VectorL10n.authSoftlogoutRecoverEncryptionKeys) + .font(theme.fonts.body) + .multilineTextAlignment(.leading) + .foregroundColor(theme.colors.primaryContent) + .accessibilityIdentifier("messageLabel2") + } } }