OutgoingVerificationViewEvents -> OutgoingVerificationViewEvent

This commit is contained in:
Benoit Marty
2026-08-25 10:30:19 +02:00
parent 2dda3b807d
commit 328d2d1260
7 changed files with 49 additions and 49 deletions
@@ -93,16 +93,16 @@ class OutgoingVerificationPresenter(
observeVerificationService() observeVerificationService()
} }
fun handleEvent(event: OutgoingVerificationViewEvents) { fun handleEvent(event: OutgoingVerificationViewEvent) {
Timber.d("Verification user action: ${event::class.simpleName}") Timber.d("Verification user action: ${event::class.simpleName}")
when (event) { when (event) {
// Just relay the event to the state machine // Just relay the event to the state machine
OutgoingVerificationViewEvents.RequestVerification -> StateMachineEvent.RequestVerification(verificationRequest) OutgoingVerificationViewEvent.RequestVerification -> StateMachineEvent.RequestVerification(verificationRequest)
OutgoingVerificationViewEvents.StartSasVerification -> StateMachineEvent.StartSasVerification OutgoingVerificationViewEvent.StartSasVerification -> StateMachineEvent.StartSasVerification
OutgoingVerificationViewEvents.ConfirmVerification -> StateMachineEvent.AcceptChallenge OutgoingVerificationViewEvent.ConfirmVerification -> StateMachineEvent.AcceptChallenge
OutgoingVerificationViewEvents.DeclineVerification -> StateMachineEvent.DeclineChallenge OutgoingVerificationViewEvent.DeclineVerification -> StateMachineEvent.DeclineChallenge
OutgoingVerificationViewEvents.Cancel -> StateMachineEvent.Cancel OutgoingVerificationViewEvent.Cancel -> StateMachineEvent.Cancel
OutgoingVerificationViewEvents.Reset -> StateMachineEvent.Reset OutgoingVerificationViewEvent.Reset -> StateMachineEvent.Reset
}.let { stateMachineEvent -> }.let { stateMachineEvent ->
stateAndDispatch.dispatchAction(stateMachineEvent) stateAndDispatch.dispatchAction(stateMachineEvent)
} }
@@ -16,7 +16,7 @@ import io.element.android.libraries.matrix.api.verification.VerificationRequest
data class OutgoingVerificationState( data class OutgoingVerificationState(
val step: Step, val step: Step,
val request: VerificationRequest.Outgoing, val request: VerificationRequest.Outgoing,
val eventSink: (OutgoingVerificationViewEvents) -> Unit, val eventSink: (OutgoingVerificationViewEvent) -> Unit,
) { ) {
@Stable @Stable
sealed interface Step { sealed interface Step {
@@ -79,7 +79,7 @@ internal fun anOutgoingSessionVerificationRequest() = VerificationRequest.Outgoi
internal fun anOutgoingVerificationState( internal fun anOutgoingVerificationState(
step: Step = Step.Initial, step: Step = Step.Initial,
request: VerificationRequest.Outgoing = anOutgoingSessionVerificationRequest(), request: VerificationRequest.Outgoing = anOutgoingSessionVerificationRequest(),
eventSink: (OutgoingVerificationViewEvents) -> Unit = {}, eventSink: (OutgoingVerificationViewEvent) -> Unit = {},
) = OutgoingVerificationState( ) = OutgoingVerificationState(
step = step, step = step,
request = request, request = request,
@@ -65,13 +65,13 @@ fun OutgoingVerificationView(
val step = state.step val step = state.step
fun cancelOrResetFlow() { fun cancelOrResetFlow() {
when (step) { when (step) {
is Step.Canceled -> state.eventSink(OutgoingVerificationViewEvents.Reset) is Step.Canceled -> state.eventSink(OutgoingVerificationViewEvent.Reset)
Step.Initial -> onBack() Step.Initial -> onBack()
Step.Completed -> onFinish() Step.Completed -> onFinish()
Step.Ready, is Step.AwaitingOtherDeviceResponse -> state.eventSink(OutgoingVerificationViewEvents.Cancel) Step.Ready, is Step.AwaitingOtherDeviceResponse -> state.eventSink(OutgoingVerificationViewEvent.Cancel)
is Step.Verifying -> { is Step.Verifying -> {
if (!step.state.isLoading()) { if (!step.state.isLoading()) {
state.eventSink(OutgoingVerificationViewEvents.DeclineVerification) state.eventSink(OutgoingVerificationViewEvent.DeclineVerification)
} }
} }
else -> Unit else -> Unit
@@ -261,7 +261,7 @@ private fun OutgoingVerificationBottomMenu(
text = stringResource(CommonStrings.action_start_verification), text = stringResource(CommonStrings.action_start_verification),
enabled = !isWaiting, enabled = !isWaiting,
showProgress = isWaiting, showProgress = isWaiting,
onClick = { eventSink(OutgoingVerificationViewEvents.RequestVerification) }, onClick = { eventSink(OutgoingVerificationViewEvent.RequestVerification) },
) )
InvisibleButton() InvisibleButton()
} }
@@ -281,7 +281,7 @@ private fun OutgoingVerificationBottomMenu(
Button( Button(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
text = stringResource(CommonStrings.action_start), text = stringResource(CommonStrings.action_start),
onClick = { eventSink(OutgoingVerificationViewEvents.StartSasVerification) }, onClick = { eventSink(OutgoingVerificationViewEvent.StartSasVerification) },
) )
TextButton( TextButton(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@@ -299,7 +299,7 @@ private fun OutgoingVerificationBottomMenu(
enabled = !isVerifying, enabled = !isVerifying,
showProgress = isVerifying, showProgress = isVerifying,
onClick = { onClick = {
eventSink(OutgoingVerificationViewEvents.ConfirmVerification) eventSink(OutgoingVerificationViewEvent.ConfirmVerification)
}, },
) )
TextButton( TextButton(
@@ -307,7 +307,7 @@ private fun OutgoingVerificationBottomMenu(
text = stringResource(R.string.screen_session_verification_they_dont_match), text = stringResource(R.string.screen_session_verification_they_dont_match),
enabled = !isVerifying, enabled = !isVerifying,
onClick = { onClick = {
eventSink(OutgoingVerificationViewEvents.DeclineVerification) eventSink(OutgoingVerificationViewEvent.DeclineVerification)
}, },
) )
} }
@@ -8,11 +8,11 @@
package io.element.android.features.verifysession.impl.outgoing package io.element.android.features.verifysession.impl.outgoing
sealed interface OutgoingVerificationViewEvents { sealed interface OutgoingVerificationViewEvent {
data object RequestVerification : OutgoingVerificationViewEvents data object RequestVerification : OutgoingVerificationViewEvent
data object StartSasVerification : OutgoingVerificationViewEvents data object StartSasVerification : OutgoingVerificationViewEvent
data object ConfirmVerification : OutgoingVerificationViewEvents data object ConfirmVerification : OutgoingVerificationViewEvent
data object DeclineVerification : OutgoingVerificationViewEvents data object DeclineVerification : OutgoingVerificationViewEvent
data object Cancel : OutgoingVerificationViewEvents data object Cancel : OutgoingVerificationViewEvent
data object Reset : OutgoingVerificationViewEvents data object Reset : OutgoingVerificationViewEvent
} }
@@ -102,7 +102,7 @@ class OutgoingVerificationPresenterTest {
val initialState = awaitItem() val initialState = awaitItem()
assertThat(initialState.step).isEqualTo(Step.Initial) assertThat(initialState.step).isEqualTo(Step.Initial)
val eventSink = initialState.eventSink val eventSink = initialState.eventSink
eventSink(OutgoingVerificationViewEvents.Cancel) eventSink(OutgoingVerificationViewEvent.Cancel)
assertThat(awaitItem().step).isEqualTo(Step.Exit) assertThat(awaitItem().step).isEqualTo(Step.Exit)
} }
@@ -118,7 +118,7 @@ class OutgoingVerificationPresenterTest {
val presenter = createOutgoingVerificationPresenter(service) val presenter = createOutgoingVerificationPresenter(service)
presenter.test { presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service) val state = requestVerificationAndAwaitVerifyingState(service)
state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification) state.eventSink(OutgoingVerificationViewEvent.ConfirmVerification)
// Cancelling // Cancelling
assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java) assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java)
service.emitVerificationFlowState(VerificationFlowState.DidFail) service.emitVerificationFlowState(VerificationFlowState.DidFail)
@@ -134,7 +134,7 @@ class OutgoingVerificationPresenterTest {
) )
val presenter = createOutgoingVerificationPresenter(service) val presenter = createOutgoingVerificationPresenter(service)
presenter.test { presenter.test {
awaitItem().eventSink(OutgoingVerificationViewEvents.RequestVerification) awaitItem().eventSink(OutgoingVerificationViewEvent.RequestVerification)
service.emitVerificationFlowState(VerificationFlowState.DidFail) service.emitVerificationFlowState(VerificationFlowState.DidFail)
assertThat(awaitItem().step).isInstanceOf(Step.AwaitingOtherDeviceResponse::class.java) assertThat(awaitItem().step).isInstanceOf(Step.AwaitingOtherDeviceResponse::class.java)
assertThat(awaitItem().step).isEqualTo(Step.Canceled) assertThat(awaitItem().step).isEqualTo(Step.Canceled)
@@ -151,7 +151,7 @@ class OutgoingVerificationPresenterTest {
val presenter = createOutgoingVerificationPresenter(service) val presenter = createOutgoingVerificationPresenter(service)
presenter.test { presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service) val state = requestVerificationAndAwaitVerifyingState(service)
state.eventSink(OutgoingVerificationViewEvents.Cancel) state.eventSink(OutgoingVerificationViewEvent.Cancel)
assertThat(awaitItem().step).isEqualTo(Step.Canceled) assertThat(awaitItem().step).isEqualTo(Step.Canceled)
} }
} }
@@ -181,7 +181,7 @@ class OutgoingVerificationPresenterTest {
val state = requestVerificationAndAwaitVerifyingState(service) val state = requestVerificationAndAwaitVerifyingState(service)
service.emitVerificationFlowState(VerificationFlowState.DidCancel) service.emitVerificationFlowState(VerificationFlowState.DidCancel)
assertThat(awaitItem().step).isEqualTo(Step.Canceled) assertThat(awaitItem().step).isEqualTo(Step.Canceled)
state.eventSink(OutgoingVerificationViewEvents.Reset) state.eventSink(OutgoingVerificationViewEvent.Reset)
// Went back to initial state // Went back to initial state
assertThat(awaitItem().step).isEqualTo(Step.Initial) assertThat(awaitItem().step).isEqualTo(Step.Initial)
cancelAndIgnoreRemainingEvents() cancelAndIgnoreRemainingEvents()
@@ -204,7 +204,7 @@ class OutgoingVerificationPresenterTest {
service, service,
SessionVerificationData.Emojis(emojis) SessionVerificationData.Emojis(emojis)
) )
state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification) state.eventSink(OutgoingVerificationViewEvent.ConfirmVerification)
assertThat(awaitItem().step).isEqualTo( assertThat(awaitItem().step).isEqualTo(
Step.Verifying( Step.Verifying(
SessionVerificationData.Emojis(emojis), SessionVerificationData.Emojis(emojis),
@@ -235,7 +235,7 @@ class OutgoingVerificationPresenterTest {
) )
presenter.test { presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service) val state = requestVerificationAndAwaitVerifyingState(service)
state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification) state.eventSink(OutgoingVerificationViewEvent.ConfirmVerification)
assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java) assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java)
service.emitVerificationFlowState(VerificationFlowState.DidFinish) service.emitVerificationFlowState(VerificationFlowState.DidFinish)
assertThat(awaitItem().step).isEqualTo(Step.Completed) assertThat(awaitItem().step).isEqualTo(Step.Completed)
@@ -261,7 +261,7 @@ class OutgoingVerificationPresenterTest {
) )
presenter.test { presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service) val state = requestVerificationAndAwaitVerifyingState(service)
state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification) state.eventSink(OutgoingVerificationViewEvent.ConfirmVerification)
assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java) assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java)
service.emitVerificationFlowState(VerificationFlowState.DidFinish) service.emitVerificationFlowState(VerificationFlowState.DidFinish)
assertThat(awaitItem().step).isEqualTo(Step.Completed) assertThat(awaitItem().step).isEqualTo(Step.Completed)
@@ -286,7 +286,7 @@ class OutgoingVerificationPresenterTest {
) )
presenter.test { presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service) val state = requestVerificationAndAwaitVerifyingState(service)
state.eventSink(OutgoingVerificationViewEvents.ConfirmVerification) state.eventSink(OutgoingVerificationViewEvent.ConfirmVerification)
assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java) assertThat(awaitItem().step).isInstanceOf(Step.Verifying::class.java)
service.emitVerificationFlowState(VerificationFlowState.DidFinish) service.emitVerificationFlowState(VerificationFlowState.DidFinish)
// The recovery state never leaves UNKNOWN, so the 10 seconds timeout fires and the flow completes anyway // The recovery state never leaves UNKNOWN, so the 10 seconds timeout fires and the flow completes anyway
@@ -306,7 +306,7 @@ class OutgoingVerificationPresenterTest {
val presenter = createOutgoingVerificationPresenter(service) val presenter = createOutgoingVerificationPresenter(service)
presenter.test { presenter.test {
val state = requestVerificationAndAwaitVerifyingState(service) val state = requestVerificationAndAwaitVerifyingState(service)
state.eventSink(OutgoingVerificationViewEvents.DeclineVerification) state.eventSink(OutgoingVerificationViewEvent.DeclineVerification)
assertThat(awaitItem().step).isEqualTo( assertThat(awaitItem().step).isEqualTo(
Step.Verifying( Step.Verifying(
SessionVerificationData.Emojis(emptyList()), SessionVerificationData.Emojis(emptyList()),
@@ -362,7 +362,7 @@ class OutgoingVerificationPresenterTest {
): OutgoingVerificationState { ): OutgoingVerificationState {
var state = awaitItem() var state = awaitItem()
assertThat(state.step).isEqualTo(Step.Initial) assertThat(state.step).isEqualTo(Step.Initial)
state.eventSink(OutgoingVerificationViewEvents.RequestVerification) state.eventSink(OutgoingVerificationViewEvent.RequestVerification)
testScope.advanceUntilIdle() testScope.advanceUntilIdle()
// Await for other device response: // Await for other device response:
fakeService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest) fakeService.emitVerificationFlowState(VerificationFlowState.DidAcceptVerificationRequest)
@@ -371,7 +371,7 @@ class OutgoingVerificationPresenterTest {
// Await for the state to be Ready // Await for the state to be Ready
state = awaitItem() state = awaitItem()
assertThat(state.step).isEqualTo(Step.Ready) assertThat(state.step).isEqualTo(Step.Ready)
state.eventSink(OutgoingVerificationViewEvents.StartSasVerification) state.eventSink(OutgoingVerificationViewEvent.StartSasVerification)
testScope.advanceUntilIdle() testScope.advanceUntilIdle()
// Await for other device response (again): // Await for other device response (again):
fakeService.emitVerificationFlowState(VerificationFlowState.DidStartSasVerification) fakeService.emitVerificationFlowState(VerificationFlowState.DidStartSasVerification)
@@ -29,7 +29,7 @@ import org.junit.Test
class OutgoingVerificationViewTest : RobolectricTest() { class OutgoingVerificationViewTest : RobolectricTest() {
@Test @Test
fun `back key pressed - when canceled resets the flow`() = runAndroidComposeUiTest { fun `back key pressed - when canceled resets the flow`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvents>() val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvent>()
setOutgoingVerificationView( setOutgoingVerificationView(
anOutgoingVerificationState( anOutgoingVerificationState(
step = OutgoingVerificationState.Step.Canceled, step = OutgoingVerificationState.Step.Canceled,
@@ -37,12 +37,12 @@ class OutgoingVerificationViewTest : RobolectricTest() {
), ),
) )
pressBackKey() pressBackKey()
eventsRecorder.assertSingle(OutgoingVerificationViewEvents.Reset) eventsRecorder.assertSingle(OutgoingVerificationViewEvent.Reset)
} }
@Test @Test
fun `back key pressed - when awaiting response cancels the verification`() = runAndroidComposeUiTest { fun `back key pressed - when awaiting response cancels the verification`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvents>() val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvent>()
setOutgoingVerificationView( setOutgoingVerificationView(
anOutgoingVerificationState( anOutgoingVerificationState(
step = OutgoingVerificationState.Step.AwaitingOtherDeviceResponse, step = OutgoingVerificationState.Step.AwaitingOtherDeviceResponse,
@@ -50,12 +50,12 @@ class OutgoingVerificationViewTest : RobolectricTest() {
), ),
) )
pressBackKey() pressBackKey()
eventsRecorder.assertSingle(OutgoingVerificationViewEvents.Cancel) eventsRecorder.assertSingle(OutgoingVerificationViewEvent.Cancel)
} }
@Test @Test
fun `back key pressed - when ready to verify cancels the verification`() = runAndroidComposeUiTest { fun `back key pressed - when ready to verify cancels the verification`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvents>() val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvent>()
setOutgoingVerificationView( setOutgoingVerificationView(
anOutgoingVerificationState( anOutgoingVerificationState(
step = OutgoingVerificationState.Step.Ready, step = OutgoingVerificationState.Step.Ready,
@@ -63,12 +63,12 @@ class OutgoingVerificationViewTest : RobolectricTest() {
), ),
) )
pressBackKey() pressBackKey()
eventsRecorder.assertSingle(OutgoingVerificationViewEvents.Cancel) eventsRecorder.assertSingle(OutgoingVerificationViewEvent.Cancel)
} }
@Test @Test
fun `back key pressed - when verifying and not loading declines the verification`() = runAndroidComposeUiTest { fun `back key pressed - when verifying and not loading declines the verification`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvents>() val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvent>()
setOutgoingVerificationView( setOutgoingVerificationView(
anOutgoingVerificationState( anOutgoingVerificationState(
step = OutgoingVerificationState.Step.Verifying( step = OutgoingVerificationState.Step.Verifying(
@@ -79,12 +79,12 @@ class OutgoingVerificationViewTest : RobolectricTest() {
), ),
) )
pressBackKey() pressBackKey()
eventsRecorder.assertSingle(OutgoingVerificationViewEvents.DeclineVerification) eventsRecorder.assertSingle(OutgoingVerificationViewEvent.DeclineVerification)
} }
@Test @Test
fun `back key pressed - when verifying and loading does nothing`() = runAndroidComposeUiTest { fun `back key pressed - when verifying and loading does nothing`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvents>() val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvent>()
setOutgoingVerificationView( setOutgoingVerificationView(
anOutgoingVerificationState( anOutgoingVerificationState(
step = OutgoingVerificationState.Step.Verifying( step = OutgoingVerificationState.Step.Verifying(
@@ -113,7 +113,7 @@ class OutgoingVerificationViewTest : RobolectricTest() {
@Test @Test
fun `when flow is completed and the user clicks on the done button, the expected callback is invoked`() = runAndroidComposeUiTest { fun `when flow is completed and the user clicks on the done button, the expected callback is invoked`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvents>(expectEvents = false) val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvent>(expectEvents = false)
ensureCalledOnce { callback -> ensureCalledOnce { callback ->
setOutgoingVerificationView( setOutgoingVerificationView(
anOutgoingVerificationState( anOutgoingVerificationState(
@@ -128,7 +128,7 @@ class OutgoingVerificationViewTest : RobolectricTest() {
@Test @Test
fun `clicking on they match emits the expected event`() = runAndroidComposeUiTest { fun `clicking on they match emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvents>() val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvent>()
setOutgoingVerificationView( setOutgoingVerificationView(
anOutgoingVerificationState( anOutgoingVerificationState(
step = OutgoingVerificationState.Step.Verifying( step = OutgoingVerificationState.Step.Verifying(
@@ -139,12 +139,12 @@ class OutgoingVerificationViewTest : RobolectricTest() {
), ),
) )
clickOn(R.string.screen_session_verification_they_match) clickOn(R.string.screen_session_verification_they_match)
eventsRecorder.assertSingle(OutgoingVerificationViewEvents.ConfirmVerification) eventsRecorder.assertSingle(OutgoingVerificationViewEvent.ConfirmVerification)
} }
@Test @Test
fun `clicking on they do not match emits the expected event`() = runAndroidComposeUiTest { fun `clicking on they do not match emits the expected event`() = runAndroidComposeUiTest {
val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvents>() val eventsRecorder = EventsRecorder<OutgoingVerificationViewEvent>()
setOutgoingVerificationView( setOutgoingVerificationView(
anOutgoingVerificationState( anOutgoingVerificationState(
step = OutgoingVerificationState.Step.Verifying( step = OutgoingVerificationState.Step.Verifying(
@@ -155,7 +155,7 @@ class OutgoingVerificationViewTest : RobolectricTest() {
), ),
) )
clickOn(R.string.screen_session_verification_they_dont_match) clickOn(R.string.screen_session_verification_they_dont_match)
eventsRecorder.assertSingle(OutgoingVerificationViewEvents.DeclineVerification) eventsRecorder.assertSingle(OutgoingVerificationViewEvent.DeclineVerification)
} }
private fun AndroidComposeUiTest<ComponentActivity>.setOutgoingVerificationView( private fun AndroidComposeUiTest<ComponentActivity>.setOutgoingVerificationView(