Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-13 18:03:37 +03:00
parent 4d44840d3f
commit f8d0276d45
4 changed files with 75 additions and 17 deletions

View file

@ -5,6 +5,7 @@ import com.tangem.common.extensions.toHexString
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.ui.extensions.stringReference
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.visa.SetVisaPinCodeUseCase
@ -57,24 +58,28 @@ internal class OnboardingVisaPinCodeModel @Inject constructor(
}
private fun onPinCodeChange(pin: String) {
if (pin.all { it.isDigit() }) {
if (PinCodeValidation.validateAllDigits(pin)) {
val isError = PinCodeValidation.validateLength(pin) && PinCodeValidation.validate(pin).not()
_uiState.update {
it.copy(
pinCode = pin,
submitButtonEnabled = checkPinCode(pin),
submitButtonEnabled = PinCodeValidation.validate(pin),
error = if (isError) stringReference("Invalid PIN: avoid sequences or repeats") else null,
)
}
}
}
private fun onSubmitClick() {
if (checkPinCode(_uiState.value.pinCode).not()) return
val pinCode = _uiState.value.pinCode
if (PinCodeValidation.validate(pinCode).not()) return
modelScope.launch {
loading(true)
setVisaPinCodeUseCase(
pinCode = _uiState.value.pinCode,
pinCode = pinCode,
visaCardId = visaCardId,
activationOrderId = params.activationOrderInfo.orderId,
).onLeft {
@ -88,10 +93,6 @@ internal class OnboardingVisaPinCodeModel @Inject constructor(
}
}
private fun checkPinCode(pin: String): Boolean {
return pin.length == PIN_CODE_LENGTH
}
private fun loading(state: Boolean) {
_uiState.update { it.copy(submitButtonLoading = state) }
}

View file

@ -0,0 +1,31 @@
package com.tangem.features.onboarding.v2.visa.impl.child.pincode.model
internal object PinCodeValidation {
private const val PIN_CODE_LENGTH = 4
fun validate(pinCode: String): Boolean {
return sequenceOf(
::validateAllDigits,
::validateLength,
::validateNoRepeatedDigits,
::validateNoConsecutiveDigits,
).all { it(pinCode) }
}
fun validateAllDigits(pinCode: String): Boolean {
return pinCode.all { it.isDigit() }
}
fun validateLength(pinCode: String): Boolean {
return pinCode.length == PIN_CODE_LENGTH
}
private fun validateNoRepeatedDigits(pinCode: String): Boolean {
return pinCode.toSet().size == PIN_CODE_LENGTH
}
private fun validateNoConsecutiveDigits(pinCode: String): Boolean {
return pinCode.windowed(2).all { it[0] + 1 == it[1] }.not()
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.features.onboarding.v2.visa.impl.child.pincode.ui
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@ -31,7 +32,10 @@ import com.tangem.common.ui.navigationButtons.NavigationButton
import com.tangem.common.ui.navigationButtons.NavigationPrimaryButton
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerH16
import com.tangem.core.ui.components.SpacerH4
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.onboarding.v2.visa.impl.child.pincode.ui.state.OnboardingVisaPinCodeUM
@ -91,13 +95,29 @@ internal fun OnboardingVisaPinCode(state: OnboardingVisaPinCodeUM, modifier: Mod
@Composable
private fun PinCodeSection(state: OnboardingVisaPinCodeUM, modifier: Modifier = Modifier) {
val focusRequester = remember { FocusRequester() }
Column(horizontalAlignment = Alignment.CenterHorizontally) {
PinCode(
modifier = modifier,
value = state.pinCode,
onValueChange = state.onPinCodeChange,
focusRequester = focusRequester,
)
PinCode(
modifier = modifier,
value = state.pinCode,
onValueChange = state.onPinCodeChange,
focusRequester = focusRequester,
)
AnimatedVisibility(
visible = state.error != null,
) {
val error = remember(this) { requireNotNull(state.error) }
Column {
SpacerH4()
Text(
text = error.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.warning,
textAlign = TextAlign.Center,
)
}
}
}
LaunchedEffect(Unit) {
delay(timeMillis = 300)
@ -134,9 +154,11 @@ private fun PinCode(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done,
),
keyboardActions = KeyboardActions(onDone = {
keyboardController?.hide()
},),
keyboardActions = KeyboardActions(
onDone = {
keyboardController?.hide()
},
),
modifier = Modifier
.alpha(alpha = 0.01f)
.focusRequester(focusRequester),
@ -194,6 +216,7 @@ private fun Preview() {
OnboardingVisaPinCode(
state = OnboardingVisaPinCodeUM(
pinCode = "1234",
error = stringReference("PIN Code error"),
),
)
}

View file

@ -1,7 +1,10 @@
package com.tangem.features.onboarding.v2.visa.impl.child.pincode.ui.state
import com.tangem.core.ui.extensions.TextReference
internal data class OnboardingVisaPinCodeUM(
val pinCode: String = "",
val error: TextReference? = null,
val onPinCodeChange: (String) -> Unit = {},
val submitButtonLoading: Boolean = false,
val submitButtonEnabled: Boolean = true,