Create pin : render failures
This commit is contained in:
parent
e5bcfb3936
commit
7ba9a0af77
7 changed files with 61 additions and 35 deletions
|
|
@ -18,5 +18,5 @@ package io.element.android.features.lockscreen.impl.create
|
||||||
|
|
||||||
sealed interface CreatePinEvents {
|
sealed interface CreatePinEvents {
|
||||||
data class OnPinEntryChanged(val entryAsText: String) : CreatePinEvents
|
data class OnPinEntryChanged(val entryAsText: String) : CreatePinEvents
|
||||||
data object OnClearValidationFailure : CreatePinEvents
|
data object ClearFailure : CreatePinEvents
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import io.element.android.features.lockscreen.impl.create.model.PinEntry
|
import io.element.android.features.lockscreen.impl.create.model.PinEntry
|
||||||
import io.element.android.features.lockscreen.impl.create.validation.PinCreationFailure
|
import io.element.android.features.lockscreen.impl.create.validation.CreatePinFailure
|
||||||
import io.element.android.features.lockscreen.impl.create.validation.PinValidator
|
import io.element.android.features.lockscreen.impl.create.validation.PinValidator
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -44,8 +44,8 @@ class CreatePinPresenter @Inject constructor(
|
||||||
var isConfirmationStep by remember {
|
var isConfirmationStep by remember {
|
||||||
mutableStateOf(false)
|
mutableStateOf(false)
|
||||||
}
|
}
|
||||||
var creationFailure by remember {
|
var createPinFailure by remember {
|
||||||
mutableStateOf<PinCreationFailure?>(null)
|
mutableStateOf<CreatePinFailure?>(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleEvents(event: CreatePinEvents) {
|
fun handleEvents(event: CreatePinEvents) {
|
||||||
|
|
@ -57,7 +57,7 @@ class CreatePinPresenter @Inject constructor(
|
||||||
if (confirmPinEntry == choosePinEntry) {
|
if (confirmPinEntry == choosePinEntry) {
|
||||||
//TODO save in db and navigate to next screen
|
//TODO save in db and navigate to next screen
|
||||||
} else {
|
} else {
|
||||||
creationFailure = PinCreationFailure.ConfirmationPinNotMatching
|
createPinFailure = CreatePinFailure.ConfirmationPinNotMatching
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -65,26 +65,26 @@ class CreatePinPresenter @Inject constructor(
|
||||||
if (choosePinEntry.isPinComplete()) {
|
if (choosePinEntry.isPinComplete()) {
|
||||||
when (val pinValidationResult = pinValidator.isPinValid(choosePinEntry)) {
|
when (val pinValidationResult = pinValidator.isPinValid(choosePinEntry)) {
|
||||||
is PinValidator.Result.Invalid -> {
|
is PinValidator.Result.Invalid -> {
|
||||||
creationFailure = pinValidationResult.failure
|
createPinFailure = pinValidationResult.failure
|
||||||
}
|
}
|
||||||
PinValidator.Result.Valid -> isConfirmationStep = true
|
PinValidator.Result.Valid -> isConfirmationStep = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CreatePinEvents.OnClearValidationFailure -> {
|
CreatePinEvents.ClearFailure -> {
|
||||||
when (creationFailure) {
|
when (createPinFailure) {
|
||||||
is PinCreationFailure.ConfirmationPinNotMatching -> {
|
is CreatePinFailure.ConfirmationPinNotMatching -> {
|
||||||
choosePinEntry = PinEntry.empty(PIN_SIZE)
|
choosePinEntry = PinEntry.empty(PIN_SIZE)
|
||||||
confirmPinEntry = PinEntry.empty(PIN_SIZE)
|
confirmPinEntry = PinEntry.empty(PIN_SIZE)
|
||||||
}
|
}
|
||||||
is PinCreationFailure.ChosenPinBlacklisted -> {
|
is CreatePinFailure.ChosenPinBlacklisted -> {
|
||||||
choosePinEntry = PinEntry.empty(PIN_SIZE)
|
choosePinEntry = PinEntry.empty(PIN_SIZE)
|
||||||
}
|
}
|
||||||
null -> Unit
|
null -> Unit
|
||||||
}
|
}
|
||||||
isConfirmationStep = false
|
isConfirmationStep = false
|
||||||
creationFailure = null
|
createPinFailure = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +93,7 @@ class CreatePinPresenter @Inject constructor(
|
||||||
choosePinEntry = choosePinEntry,
|
choosePinEntry = choosePinEntry,
|
||||||
confirmPinEntry = confirmPinEntry,
|
confirmPinEntry = confirmPinEntry,
|
||||||
isConfirmationStep = isConfirmationStep,
|
isConfirmationStep = isConfirmationStep,
|
||||||
creationFailure = creationFailure,
|
createPinFailure = createPinFailure,
|
||||||
eventSink = ::handleEvents
|
eventSink = ::handleEvents
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,13 @@
|
||||||
package io.element.android.features.lockscreen.impl.create
|
package io.element.android.features.lockscreen.impl.create
|
||||||
|
|
||||||
import io.element.android.features.lockscreen.impl.create.model.PinEntry
|
import io.element.android.features.lockscreen.impl.create.model.PinEntry
|
||||||
import io.element.android.features.lockscreen.impl.create.validation.PinCreationFailure
|
import io.element.android.features.lockscreen.impl.create.validation.CreatePinFailure
|
||||||
|
|
||||||
data class CreatePinState(
|
data class CreatePinState(
|
||||||
val choosePinEntry: PinEntry,
|
val choosePinEntry: PinEntry,
|
||||||
val confirmPinEntry: PinEntry,
|
val confirmPinEntry: PinEntry,
|
||||||
val isConfirmationStep: Boolean,
|
val isConfirmationStep: Boolean,
|
||||||
val creationFailure: PinCreationFailure?,
|
val createPinFailure: CreatePinFailure?,
|
||||||
val eventSink: (CreatePinEvents) -> Unit
|
val eventSink: (CreatePinEvents) -> Unit
|
||||||
) {
|
) {
|
||||||
val activePinEntry = if (isConfirmationStep) {
|
val activePinEntry = if (isConfirmationStep) {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ package io.element.android.features.lockscreen.impl.create
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.lockscreen.impl.create.model.PinEntry
|
import io.element.android.features.lockscreen.impl.create.model.PinEntry
|
||||||
import io.element.android.features.lockscreen.impl.create.validation.PinCreationFailure
|
import io.element.android.features.lockscreen.impl.create.validation.CreatePinFailure
|
||||||
|
|
||||||
open class CreatePinStateProvider : PreviewParameterProvider<CreatePinState> {
|
open class CreatePinStateProvider : PreviewParameterProvider<CreatePinState> {
|
||||||
override val values: Sequence<CreatePinState>
|
override val values: Sequence<CreatePinState>
|
||||||
|
|
@ -31,6 +31,17 @@ open class CreatePinStateProvider : PreviewParameterProvider<CreatePinState> {
|
||||||
choosePinEntry = PinEntry.empty(4).fillWith("1789"),
|
choosePinEntry = PinEntry.empty(4).fillWith("1789"),
|
||||||
isConfirmationStep = true,
|
isConfirmationStep = true,
|
||||||
),
|
),
|
||||||
|
aCreatePinState(
|
||||||
|
choosePinEntry = PinEntry.empty(4).fillWith("1789"),
|
||||||
|
confirmPinEntry = PinEntry.empty(4).fillWith("1788"),
|
||||||
|
isConfirmationStep = true,
|
||||||
|
creationFailure = CreatePinFailure.ConfirmationPinNotMatching
|
||||||
|
),
|
||||||
|
aCreatePinState(
|
||||||
|
choosePinEntry = PinEntry.empty(4).fillWith("1111"),
|
||||||
|
creationFailure = CreatePinFailure.ChosenPinBlacklisted
|
||||||
|
),
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,12 +49,12 @@ fun aCreatePinState(
|
||||||
choosePinEntry: PinEntry = PinEntry.empty(4),
|
choosePinEntry: PinEntry = PinEntry.empty(4),
|
||||||
confirmPinEntry: PinEntry = PinEntry.empty(4),
|
confirmPinEntry: PinEntry = PinEntry.empty(4),
|
||||||
isConfirmationStep: Boolean = false,
|
isConfirmationStep: Boolean = false,
|
||||||
creationFailure: PinCreationFailure? = null,
|
creationFailure: CreatePinFailure? = null,
|
||||||
) = CreatePinState(
|
) = CreatePinState(
|
||||||
choosePinEntry = choosePinEntry,
|
choosePinEntry = choosePinEntry,
|
||||||
confirmPinEntry = confirmPinEntry,
|
confirmPinEntry = confirmPinEntry,
|
||||||
isConfirmationStep = isConfirmationStep,
|
isConfirmationStep = isConfirmationStep,
|
||||||
creationFailure = creationFailure,
|
createPinFailure = creationFailure,
|
||||||
eventSink = {}
|
eventSink = {}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,12 +42,13 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.features.lockscreen.impl.create.model.PinDigit
|
import io.element.android.features.lockscreen.impl.create.model.PinDigit
|
||||||
import io.element.android.features.lockscreen.impl.create.model.PinEntry
|
import io.element.android.features.lockscreen.impl.create.model.PinEntry
|
||||||
|
import io.element.android.features.lockscreen.impl.create.validation.CreatePinFailure
|
||||||
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
import io.element.android.libraries.designsystem.atomic.molecules.IconTitleSubtitleMolecule
|
||||||
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
|
import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage
|
||||||
import io.element.android.libraries.designsystem.components.button.BackButton
|
import io.element.android.libraries.designsystem.components.button.BackButton
|
||||||
|
import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
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.Scaffold
|
import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||||
|
|
@ -76,7 +77,6 @@ fun CreatePinView(
|
||||||
.padding(padding)
|
.padding(padding)
|
||||||
.consumeWindowInsets(padding),
|
.consumeWindowInsets(padding),
|
||||||
header = { CreatePinHeader(state.isConfirmationStep) },
|
header = { CreatePinHeader(state.isConfirmationStep) },
|
||||||
footer = { CreatePinFooter() },
|
|
||||||
content = { CreatePinContent(state) }
|
content = { CreatePinContent(state) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -96,17 +96,6 @@ private fun CreatePinHeader(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun CreatePinFooter() {
|
|
||||||
Button(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
text = "Continue",
|
|
||||||
onClick = {
|
|
||||||
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun CreatePinContent(
|
private fun CreatePinContent(
|
||||||
state: CreatePinState,
|
state: CreatePinState,
|
||||||
|
|
@ -121,6 +110,32 @@ private fun CreatePinContent(
|
||||||
.padding(top = 36.dp)
|
.padding(top = 36.dp)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
)
|
)
|
||||||
|
if (state.createPinFailure != null) {
|
||||||
|
ErrorDialog(
|
||||||
|
modifier = modifier,
|
||||||
|
title = state.createPinFailure.title(),
|
||||||
|
content = state.createPinFailure.content(),
|
||||||
|
onDismiss = {
|
||||||
|
state.eventSink(CreatePinEvents.ClearFailure)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun CreatePinFailure.content(): String {
|
||||||
|
return when (this) {
|
||||||
|
CreatePinFailure.ChosenPinBlacklisted -> "You cannot choose this as your PIN code for security reasons"
|
||||||
|
CreatePinFailure.ConfirmationPinNotMatching -> "Please enter the same PIN twice"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun CreatePinFailure.title(): String {
|
||||||
|
return when (this) {
|
||||||
|
CreatePinFailure.ChosenPinBlacklisted -> "Choose a different PIN"
|
||||||
|
CreatePinFailure.ConfirmationPinNotMatching -> "PINs don't match"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
package io.element.android.features.lockscreen.impl.create.validation
|
package io.element.android.features.lockscreen.impl.create.validation
|
||||||
|
|
||||||
sealed interface PinCreationFailure {
|
sealed interface CreatePinFailure {
|
||||||
data object ChosenPinBlacklisted : PinCreationFailure
|
data object ChosenPinBlacklisted : CreatePinFailure
|
||||||
data object ConfirmationPinNotMatching : PinCreationFailure
|
data object ConfirmationPinNotMatching : CreatePinFailure
|
||||||
}
|
}
|
||||||
|
|
@ -25,14 +25,14 @@ class PinValidator @Inject constructor() {
|
||||||
|
|
||||||
sealed interface Result {
|
sealed interface Result {
|
||||||
data object Valid : Result
|
data object Valid : Result
|
||||||
data class Invalid(val failure: PinCreationFailure) : Result
|
data class Invalid(val failure: CreatePinFailure) : Result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isPinValid(pinEntry: PinEntry): Result {
|
fun isPinValid(pinEntry: PinEntry): Result {
|
||||||
val pinAsText = pinEntry.toText()
|
val pinAsText = pinEntry.toText()
|
||||||
val isBlacklisted = BLACKLIST.any { it == pinAsText }
|
val isBlacklisted = BLACKLIST.any { it == pinAsText }
|
||||||
return if (isBlacklisted) {
|
return if (isBlacklisted) {
|
||||||
Result.Invalid(PinCreationFailure.ChosenPinBlacklisted)
|
Result.Invalid(CreatePinFailure.ChosenPinBlacklisted)
|
||||||
} else {
|
} else {
|
||||||
Result.Valid
|
Result.Valid
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue