Merge pull request #3426 from element-hq/feature/fga/fix_self_verification_flow

Feature/fga/fix self verification flow
This commit is contained in:
ganfra 2024-09-09 13:01:27 +02:00 committed by GitHub
commit aa40c3618b
2 changed files with 39 additions and 29 deletions

View file

@ -70,8 +70,17 @@ fun VerifySelfSessionView(
onSuccessLogout: (String?) -> Unit, onSuccessLogout: (String?) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
fun resetFlow() { fun cancelOrResetFlow() {
state.eventSink(VerifySelfSessionViewEvents.Reset) when (state.verificationFlowStep) {
is FlowStep.Canceled -> state.eventSink(VerifySelfSessionViewEvents.Reset)
is FlowStep.AwaitingOtherDeviceResponse, FlowStep.Ready -> state.eventSink(VerifySelfSessionViewEvents.Cancel)
is FlowStep.Verifying -> {
if (!state.verificationFlowStep.state.isLoading()) {
state.eventSink(VerifySelfSessionViewEvents.DeclineVerification)
}
}
else -> Unit
}
} }
val latestOnFinish by rememberUpdatedState(newValue = onFinish) val latestOnFinish by rememberUpdatedState(newValue = onFinish)
@ -81,16 +90,7 @@ fun VerifySelfSessionView(
} }
} }
BackHandler { BackHandler {
when (state.verificationFlowStep) { cancelOrResetFlow()
is FlowStep.Canceled -> resetFlow()
is FlowStep.AwaitingOtherDeviceResponse, FlowStep.Ready -> state.eventSink(VerifySelfSessionViewEvents.Cancel)
is FlowStep.Verifying -> {
if (!state.verificationFlowStep.state.isLoading()) {
state.eventSink(VerifySelfSessionViewEvents.DeclineVerification)
}
}
else -> Unit
}
} }
val verificationFlowStep = state.verificationFlowStep val verificationFlowStep = state.verificationFlowStep
@ -133,9 +133,9 @@ fun VerifySelfSessionView(
footer = { footer = {
BottomMenu( BottomMenu(
screenState = state, screenState = state,
goBack = ::resetFlow, onCancelClick = ::cancelOrResetFlow,
onEnterRecoveryKey = onEnterRecoveryKey, onEnterRecoveryKey = onEnterRecoveryKey,
onFinish = onFinish, onContinueClick = onFinish,
onResetKey = onResetKey, onResetKey = onResetKey,
) )
} }
@ -268,8 +268,8 @@ private fun BottomMenu(
screenState: VerifySelfSessionState, screenState: VerifySelfSessionState,
onEnterRecoveryKey: () -> Unit, onEnterRecoveryKey: () -> Unit,
onResetKey: () -> Unit, onResetKey: () -> Unit,
goBack: () -> Unit, onCancelClick: () -> Unit,
onFinish: () -> Unit, onContinueClick: () -> Unit,
) { ) {
val verificationViewState = screenState.verificationFlowStep val verificationViewState = screenState.verificationFlowStep
val eventSink = screenState.eventSink val eventSink = screenState.eventSink
@ -316,7 +316,7 @@ private fun BottomMenu(
TextButton( TextButton(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
text = stringResource(CommonStrings.action_cancel), text = stringResource(CommonStrings.action_cancel),
onClick = goBack, onClick = onCancelClick,
) )
} }
} }
@ -330,7 +330,7 @@ private fun BottomMenu(
TextButton( TextButton(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
text = stringResource(CommonStrings.action_cancel), text = stringResource(CommonStrings.action_cancel),
onClick = goBack, onClick = onCancelClick,
) )
} }
} }
@ -375,7 +375,7 @@ private fun BottomMenu(
Button( Button(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
text = stringResource(CommonStrings.action_continue), text = stringResource(CommonStrings.action_continue),
onClick = onFinish, onClick = onContinueClick,
) )
// Placeholder so the 1st button keeps its vertical position // Placeholder so the 1st button keeps its vertical position
Spacer(modifier = Modifier.height(48.dp)) Spacer(modifier = Modifier.height(48.dp))

View file

@ -26,6 +26,8 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withTimeout import kotlinx.coroutines.withTimeout
import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.Client
import org.matrix.rustcomponents.sdk.Encryption import org.matrix.rustcomponents.sdk.Encryption
@ -95,18 +97,19 @@ class RustSessionVerificationService(
updateVerificationStatus() updateVerificationStatus()
} }
} }
.launchIn(sessionCoroutineScope) .launchIn(sessionCoroutineScope)
} }
override suspend fun requestVerification() = tryOrFail { override suspend fun requestVerification() = tryOrFail {
if (!this::verificationController.isInitialized) { initVerificationControllerIfNeeded()
verificationController = client.getSessionVerificationController()
verificationController.setDelegate(this)
}
verificationController.requestVerification() verificationController.requestVerification()
} }
override suspend fun cancelVerification() = tryOrFail { verificationController.cancelVerification() } override suspend fun cancelVerification() = tryOrFail {
verificationController.cancelVerification()
// We need to manually set the state to canceled, as the Rust SDK doesn't always call `didCancel` when it should
didCancel()
}
override suspend fun approveVerification() = tryOrFail { verificationController.approveVerification() } override suspend fun approveVerification() = tryOrFail { verificationController.approveVerification() }
@ -193,6 +196,16 @@ class RustSessionVerificationService(
} }
} }
private var initControllerMutex = Mutex()
private suspend fun initVerificationControllerIfNeeded() = initControllerMutex.withLock {
if (!this::verificationController.isInitialized) {
tryOrFail {
verificationController = client.getSessionVerificationController()
verificationController.setDelegate(this)
}
}
}
private suspend fun updateVerificationStatus() { private suspend fun updateVerificationStatus() {
if (verificationFlowState.value == VerificationFlowState.Finished) { if (verificationFlowState.value == VerificationFlowState.Finished) {
// Calling `encryptionService.verificationState()` performs a network call and it will deadlock if there is no network // Calling `encryptionService.verificationState()` performs a network call and it will deadlock if there is no network
@ -212,10 +225,7 @@ class RustSessionVerificationService(
// Otherwise, just check the current verification status from the session verification controller instead // Otherwise, just check the current verification status from the session verification controller instead
Timber.d("Updating verification status: flow is pending or was finished some time ago") Timber.d("Updating verification status: flow is pending or was finished some time ago")
runCatching { runCatching {
if (!this@RustSessionVerificationService::verificationController.isInitialized) { initVerificationControllerIfNeeded()
verificationController = client.getSessionVerificationController()
verificationController.setDelegate(this@RustSessionVerificationService)
}
_sessionVerifiedStatus.value = if (verificationController.isVerified()) { _sessionVerifiedStatus.value = if (verificationController.isVerified()) {
SessionVerifiedStatus.Verified SessionVerifiedStatus.Verified
} else { } else {