Only offer to verify if a cross-signed device is available (#5433)
* Only offer to verify if a cross-signed device is available * Fix tests * use the right exception mapper * adjust flag name and logic in ChooseSelfVerificationState * add comment * switch order of states to match previous logic
This commit is contained in:
@@ -26,7 +26,7 @@ class ChooseSelfVerificationModePresenter(
|
||||
) : Presenter<ChooseSelfVerificationModeState> {
|
||||
@Composable
|
||||
override fun present(): ChooseSelfVerificationModeState {
|
||||
val isLastDevice by encryptionService.isLastDevice.collectAsState()
|
||||
val hasDevicesToVerifyAgainst by encryptionService.hasDevicesToVerifyAgainst.collectAsState()
|
||||
val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState()
|
||||
val canEnterRecoveryKey by remember { derivedStateOf { recoveryState == RecoveryState.INCOMPLETE } }
|
||||
|
||||
@@ -39,7 +39,7 @@ class ChooseSelfVerificationModePresenter(
|
||||
}
|
||||
|
||||
return ChooseSelfVerificationModeState(
|
||||
isLastDevice = isLastDevice,
|
||||
canUseAnotherDevice = hasDevicesToVerifyAgainst,
|
||||
canEnterRecoveryKey = canEnterRecoveryKey,
|
||||
directLogoutState = directLogoutState,
|
||||
eventSink = ::eventHandler,
|
||||
|
||||
@@ -10,7 +10,7 @@ package io.element.android.features.ftue.impl.sessionverification.choosemode
|
||||
import io.element.android.features.logout.api.direct.DirectLogoutState
|
||||
|
||||
data class ChooseSelfVerificationModeState(
|
||||
val isLastDevice: Boolean,
|
||||
val canUseAnotherDevice: Boolean,
|
||||
val canEnterRecoveryKey: Boolean,
|
||||
val directLogoutState: DirectLogoutState,
|
||||
val eventSink: (ChooseSelfVerificationModeEvent) -> Unit,
|
||||
|
||||
@@ -13,18 +13,18 @@ import io.element.android.features.logout.api.direct.aDirectLogoutState
|
||||
class ChooseSelfVerificationModeStateProvider :
|
||||
PreviewParameterProvider<ChooseSelfVerificationModeState> {
|
||||
override val values = sequenceOf(
|
||||
aChooseSelfVerificationModeState(isLastDevice = true, canEnterRecoveryKey = true),
|
||||
aChooseSelfVerificationModeState(isLastDevice = true, canEnterRecoveryKey = false),
|
||||
aChooseSelfVerificationModeState(isLastDevice = false, canEnterRecoveryKey = true),
|
||||
aChooseSelfVerificationModeState(isLastDevice = false, canEnterRecoveryKey = false),
|
||||
aChooseSelfVerificationModeState(canUseAnotherDevice = false, canEnterRecoveryKey = true),
|
||||
aChooseSelfVerificationModeState(canUseAnotherDevice = false, canEnterRecoveryKey = false),
|
||||
aChooseSelfVerificationModeState(canUseAnotherDevice = true, canEnterRecoveryKey = true),
|
||||
aChooseSelfVerificationModeState(canUseAnotherDevice = true, canEnterRecoveryKey = false),
|
||||
)
|
||||
}
|
||||
|
||||
fun aChooseSelfVerificationModeState(
|
||||
isLastDevice: Boolean = false,
|
||||
canUseAnotherDevice: Boolean = true,
|
||||
canEnterRecoveryKey: Boolean = true,
|
||||
) = ChooseSelfVerificationModeState(
|
||||
isLastDevice = isLastDevice,
|
||||
canUseAnotherDevice = canUseAnotherDevice,
|
||||
canEnterRecoveryKey = canEnterRecoveryKey,
|
||||
directLogoutState = aDirectLogoutState(),
|
||||
eventSink = {},
|
||||
|
||||
@@ -76,7 +76,7 @@ fun ChooseSelfVerificationModeView(
|
||||
ButtonColumnMolecule(
|
||||
modifier = Modifier.padding(bottom = 16.dp)
|
||||
) {
|
||||
if (state.isLastDevice.not()) {
|
||||
if (state.canUseAnotherDevice) {
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(R.string.screen_identity_use_another_device),
|
||||
|
||||
@@ -24,15 +24,15 @@ class ChooseSessionVerificationModePresenterTest {
|
||||
@Test
|
||||
fun `initial state - is relayed from EncryptionService`() = runTest {
|
||||
val encryptionService = FakeEncryptionService().apply {
|
||||
// Is last device
|
||||
emitIsLastDevice(true)
|
||||
// Has device to verify against
|
||||
emitHasDevicesToVerifyAgainst(false)
|
||||
// Can enter recovery key
|
||||
emitRecoveryState(RecoveryState.INCOMPLETE)
|
||||
}
|
||||
val presenter = createPresenter(encryptionService = encryptionService)
|
||||
presenter.test {
|
||||
awaitItem().run {
|
||||
assertThat(isLastDevice).isTrue()
|
||||
assertThat(canUseAnotherDevice).isFalse()
|
||||
assertThat(canEnterRecoveryKey).isTrue()
|
||||
assertThat(directLogoutState.logoutAction.isUninitialized()).isTrue()
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ class ChooseSessionVerificationModeViewTest {
|
||||
fun `clicking on use another device calls the callback`() {
|
||||
ensureCalledOnce { callback ->
|
||||
rule.setChooseSelfVerificationModeView(
|
||||
aChooseSelfVerificationModeState(isLastDevice = false),
|
||||
aChooseSelfVerificationModeState(canUseAnotherDevice = true),
|
||||
onUseAnotherDevice = callback,
|
||||
)
|
||||
rule.clickOn(R.string.screen_identity_use_another_device)
|
||||
|
||||
@@ -17,6 +17,7 @@ interface EncryptionService {
|
||||
val recoveryStateStateFlow: StateFlow<RecoveryState>
|
||||
val enableRecoveryProgressStateFlow: StateFlow<EnableRecoveryProgress>
|
||||
val isLastDevice: StateFlow<Boolean>
|
||||
val hasDevicesToVerifyAgainst: StateFlow<Boolean>
|
||||
|
||||
suspend fun enableBackups(): Result<Unit>
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.encryption.IdentityResetHandle
|
||||
import io.element.android.libraries.matrix.api.encryption.RecoveryState
|
||||
import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
|
||||
import io.element.android.libraries.matrix.api.sync.SyncState
|
||||
import io.element.android.libraries.matrix.impl.exception.mapClientException
|
||||
import io.element.android.libraries.matrix.impl.sync.RustSyncService
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
@@ -96,6 +97,20 @@ internal class RustEncryptionService(
|
||||
}
|
||||
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, false)
|
||||
|
||||
/**
|
||||
* Check if the user has any devices available to verify against every 5 seconds.
|
||||
* TODO This is a temporary workaround, when we will have a way to observe
|
||||
* the sessions, this code will have to be updated.
|
||||
*/
|
||||
override val hasDevicesToVerifyAgainst: StateFlow<Boolean> = flow {
|
||||
while (currentCoroutineContext().isActive) {
|
||||
val result = hasDevicesToVerifyAgainst().getOrDefault(false)
|
||||
emit(result)
|
||||
delay(5_000)
|
||||
}
|
||||
}
|
||||
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, false)
|
||||
|
||||
override suspend fun enableBackups(): Result<Unit> = withContext(dispatchers.io) {
|
||||
runCatchingExceptions {
|
||||
service.enableBackups()
|
||||
@@ -171,6 +186,14 @@ internal class RustEncryptionService(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun hasDevicesToVerifyAgainst(): Result<Boolean> = withContext(dispatchers.io) {
|
||||
runCatchingExceptions {
|
||||
service.hasDevicesToVerifyAgainst()
|
||||
}.mapFailure {
|
||||
it.mapClientException()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun resetRecoveryKey(): Result<String> = withContext(dispatchers.io) {
|
||||
runCatchingExceptions {
|
||||
service.resetRecoveryKey()
|
||||
|
||||
@@ -32,6 +32,10 @@ class FakeFfiEncryption : Encryption(NoPointer) {
|
||||
return false
|
||||
}
|
||||
|
||||
override suspend fun hasDevicesToVerifyAgainst(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun backupState(): BackupState {
|
||||
return BackupState.ENABLED
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ class FakeEncryptionService(
|
||||
override val recoveryStateStateFlow: MutableStateFlow<RecoveryState> = MutableStateFlow(RecoveryState.UNKNOWN)
|
||||
override val enableRecoveryProgressStateFlow: MutableStateFlow<EnableRecoveryProgress> = MutableStateFlow(EnableRecoveryProgress.Starting)
|
||||
override val isLastDevice: MutableStateFlow<Boolean> = MutableStateFlow(false)
|
||||
override val hasDevicesToVerifyAgainst: MutableStateFlow<Boolean> = MutableStateFlow(true)
|
||||
private var waitForBackupUploadSteadyStateFlow: Flow<BackupUploadState> = flowOf()
|
||||
|
||||
private var recoverFailure: Exception? = null
|
||||
@@ -83,6 +84,10 @@ class FakeEncryptionService(
|
||||
this.isLastDevice.value = isLastDevice
|
||||
}
|
||||
|
||||
fun emitHasDevicesToVerifyAgainst(hasDevicesToVerifyAgainst: Boolean) {
|
||||
this.hasDevicesToVerifyAgainst.value = hasDevicesToVerifyAgainst
|
||||
}
|
||||
|
||||
override suspend fun resetRecoveryKey(): Result<String> = simulateLongTask {
|
||||
return Result.success(FAKE_RECOVERY_KEY)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user