Iterate on login error: add a cancel button that fully close the flow.

tom
This commit is contained in:
Benoit Marty 2026-01-28 16:09:31 +01:00 committed by Benoit Marty
parent bcd707d307
commit 6f0f315b7e
10 changed files with 120 additions and 28 deletions

View file

@ -20,6 +20,7 @@ import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.pop
import com.bumble.appyx.navmodel.backstack.operation.push
import com.bumble.appyx.navmodel.backstack.operation.singleTop
import dev.zacsweers.metro.AppScope
@ -196,7 +197,12 @@ class LoginFlowNode(
createNode<ChooseAccountProviderNode>(buildContext, listOf(callback))
}
NavTarget.QrCode -> {
createNode<QrCodeLoginFlowNode>(buildContext)
val callback = object : QrCodeLoginFlowNode.Callback {
override fun navigateBack() {
backstack.pop()
}
}
createNode<QrCodeLoginFlowNode>(buildContext, listOf(callback))
}
is NavTarget.ConfirmAccountProvider -> {
val inputs = ConfirmAccountProviderNode.Inputs(

View file

@ -37,6 +37,7 @@ import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.architecture.callback
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.DependencyInjectionGraphOwner
@ -64,6 +65,12 @@ class QrCodeLoginFlowNode(
buildContext = buildContext,
plugins = plugins,
), DependencyInjectionGraphOwner {
interface Callback : Plugin {
fun navigateBack()
}
private val callback: Callback = callback()
private var authenticationJob: Job? = null
override val graph = qrCodeLoginGraphFactory.create()
@ -85,7 +92,6 @@ class QrCodeLoginFlowNode(
override fun onBuilt() {
super.onBuilt()
observeLoginStep()
}
@ -178,7 +184,13 @@ class QrCodeLoginFlowNode(
}
is NavTarget.Error -> {
val callback = object : QrCodeErrorNode.Callback {
override fun onRetry() = reset()
override fun onRetry() {
reset()
}
override fun onCancel() {
callback.navigateBack()
}
}
createNode<QrCodeErrorNode>(buildContext, plugins = listOf(navTarget.errorType, callback))
}

View file

@ -31,6 +31,7 @@ class QrCodeErrorNode(
) : Node(buildContext = buildContext, plugins = plugins) {
interface Callback : Plugin {
fun onRetry()
fun onCancel()
}
private val callback: Callback = callback()
@ -43,6 +44,7 @@ class QrCodeErrorNode(
errorScreenType = qrCodeErrorScreenType,
appName = buildMeta.productionApplicationName,
onRetry = callback::onRetry,
onCancel = callback::onCancel,
)
}
}

View file

@ -35,6 +35,7 @@ import io.element.android.libraries.designsystem.components.BigIcon
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.designsystem.theme.components.Button
import io.element.android.libraries.designsystem.theme.components.OutlinedButton
import io.element.android.libraries.designsystem.theme.components.Text
import io.element.android.libraries.ui.strings.CommonStrings
import kotlinx.collections.immutable.persistentListOf
@ -44,16 +45,22 @@ fun QrCodeErrorView(
errorScreenType: QrCodeErrorScreenType,
appName: String,
onRetry: () -> Unit,
onCancel: () -> Unit,
modifier: Modifier = Modifier,
) {
BackHandler(onBack = onRetry)
BackHandler(onBack = onCancel)
FlowStepPage(
modifier = modifier,
iconStyle = BigIcon.Style.AlertSolid,
title = titleText(errorScreenType, appName),
subTitle = subtitleText(errorScreenType, appName),
content = { Content(errorScreenType) },
buttons = { Buttons(onRetry) },
buttons = {
Buttons(
onRetry = onRetry,
onCancel = onCancel,
)
},
)
}
@ -118,11 +125,19 @@ private fun Content(errorScreenType: QrCodeErrorScreenType) {
}
@Composable
private fun Buttons(onRetry: () -> Unit) {
private fun Buttons(
onRetry: () -> Unit,
onCancel: () -> Unit,
) {
Button(
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.screen_qr_code_login_start_over_button),
onClick = onRetry
text = stringResource(CommonStrings.action_try_again),
onClick = onRetry,
)
OutlinedButton(
modifier = Modifier.fillMaxWidth(),
text = stringResource(CommonStrings.action_cancel),
onClick = onCancel,
)
}
@ -133,7 +148,8 @@ internal fun QrCodeErrorViewPreview(@PreviewParameter(QrCodeErrorScreenTypeProvi
QrCodeErrorView(
errorScreenType = errorScreenType,
appName = "Element X",
onRetry = {}
onRetry = {},
onCancel = {},
)
}
}