From e959ca018673d1cf81ec37022af11612c898893f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 20 Feb 2024 10:51:35 +0100 Subject: [PATCH] Do not show verification banner for the last device, show the enter recovery key banner. --- .../roomlist/impl/RoomListPresenter.kt | 23 +++++++++--------- .../roomlist/impl/RoomListPresenterTests.kt | 24 +++++++++++++++++++ 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt index d5a09355cb..1467595de0 100644 --- a/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt +++ b/features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt @@ -109,22 +109,23 @@ class RoomListPresenter @Inject constructor( val isMigrating = migrationScreenPresenter.present().isMigrating var securityBannerDismissed by rememberSaveable { mutableStateOf(false) } - val displayVerificationPrompt by sessionVerificationService.canVerifySessionFlow.collectAsState(initial = false) - val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState() - val secureStorageFlag by featureFlagService.isFeatureEnabledFlow(FeatureFlags.SecureStorage) - .collectAsState(initial = null) - val displayRecoveryKeyPrompt by remember { - derivedStateOf { - secureStorageFlag == true && - recoveryState == RecoveryState.INCOMPLETE - } + val canVerifySession by sessionVerificationService.canVerifySessionFlow.collectAsState(initial = false) + var isLastDevice by remember { mutableStateOf(false) } + LaunchedEffect(Unit) { + isLastDevice = encryptionService.isLastDevice().getOrNull() ?: false } + val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState() + val secureStorageFlag by featureFlagService.isFeatureEnabledFlow(FeatureFlags.SecureStorage).collectAsState(initial = null) val securityBannerState by remember { derivedStateOf { when { securityBannerDismissed -> SecurityBannerState.None - displayVerificationPrompt -> SecurityBannerState.SessionVerification - displayRecoveryKeyPrompt -> SecurityBannerState.RecoveryKeyConfirmation + canVerifySession -> if (isLastDevice) { + SecurityBannerState.RecoveryKeyConfirmation + } else { + SecurityBannerState.SessionVerification + } + secureStorageFlag == true && recoveryState == RecoveryState.INCOMPLETE -> SecurityBannerState.RecoveryKeyConfirmation else -> SecurityBannerState.None } } diff --git a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt index c62d7a3adb..8b937db63d 100644 --- a/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt +++ b/features/roomlist/impl/src/test/kotlin/io/element/android/features/roomlist/impl/RoomListPresenterTests.kt @@ -235,6 +235,30 @@ class RoomListPresenterTests { } } + @Test + fun `present - handle RecoveryKeyConfirmation last session`() = runTest { + val scope = CoroutineScope(context = coroutineContext + SupervisorJob()) + val presenter = createRoomListPresenter( + coroutineScope = scope, + client = FakeMatrixClient( + encryptionService = FakeEncryptionService().apply { + givenIsLastDevice(true) + } + ), + ) + moleculeFlow(RecompositionMode.Immediate) { + presenter.present() + }.test { + skipItems(2) + val eventSink = awaitItem().eventSink + // For the last session, the state is not SessionVerification, but RecoveryKeyConfirmation + assertThat(awaitItem().securityBannerState).isEqualTo(SecurityBannerState.RecoveryKeyConfirmation) + eventSink(RoomListEvents.DismissRequestVerificationPrompt) + assertThat(awaitItem().securityBannerState).isEqualTo(SecurityBannerState.None) + scope.cancel() + } + } + @Test fun `present - handle DismissRequestVerificationPrompt`() = runTest { val scope = CoroutineScope(context = coroutineContext + SupervisorJob())