diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/PinTextField.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/PinTextField.kt index fa00fb9959..0a593a7a8d 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/PinTextField.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/PinTextField.kt @@ -2,6 +2,7 @@ package com.tangem.core.ui.components.fields import androidx.compose.animation.* import androidx.compose.animation.core.tween +import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -36,9 +37,9 @@ fun PinTextField( value: String, length: Int, isPasswordVisual: Boolean, + pinTextColor: PinTextColor, onValueChange: (String) -> Unit, modifier: Modifier = Modifier, - wrongCode: Boolean = false, ) { val focusRequester = remember { FocusRequester() } val textFieldValue = remember(value) { @@ -72,7 +73,7 @@ fun PinTextField( CellDecoration( length = length, isPasswordVisual = isPasswordVisual, - wrongCode = wrongCode, + pinTextColor = pinTextColor, value = value, ) }, @@ -84,17 +85,25 @@ fun PinTextField( } } -@Suppress("MagicNumber") +enum class PinTextColor { + Primary, + WrongCode, + Success, +} + +@Suppress("MagicNumber", "LongMethod") @Composable private fun CellDecoration( length: Int, - wrongCode: Boolean, + pinTextColor: PinTextColor, value: String, modifier: Modifier = Modifier, isPasswordVisual: Boolean = false, ) { val textMeasurer = rememberTextMeasurer() - val width = textMeasurer.measure("0") + val minSize = textMeasurer.measure("0") + val minWidth = maxOf(minSize.size.width.dp + 8.dp, 24.dp + 3.dp) // 24.dp is the minimum width of a pin cell + val minHeight = maxOf(minSize.size.height.dp, 48.dp) // 48.dp is the minimum height of a pin cell Row( modifier = modifier, @@ -107,6 +116,18 @@ private fun CellDecoration( "" } + val color = when (pinTextColor) { + PinTextColor.Primary -> { + if (isPasswordVisual) { + TangemTheme.colors.icon.informative + } else { + TangemTheme.colors.text.primary1 + } + } + PinTextColor.WrongCode -> TangemTheme.colors.icon.warning + PinTextColor.Success -> TangemTheme.colors.icon.accent + } + Box( modifier = Modifier .background( @@ -119,26 +140,34 @@ private fun CellDecoration( targetState = char, transitionSpec = { ( - fadeIn(animationSpec = tween(220, delayMillis = 90)) + - slideInVertically(animationSpec = tween(330, delayMillis = 0)) + fadeIn(animationSpec = tween(90, delayMillis = 90)) + + slideInVertically(animationSpec = tween(220, delayMillis = 0)) ) .togetherWith( fadeOut(animationSpec = tween(90)) + slideOutVertically(tween(220)), ) }, ) { text -> - Text( - modifier = Modifier.sizeIn(minWidth = width.size.width.dp + 8.dp, minHeight = 48.dp), - text = text, - style = TangemTheme.typography.h3, - color = if (wrongCode) { - TangemTheme.colors.text.warning - } else { - TangemTheme.colors.text.primary1 - }, - textAlign = TextAlign.Center, - lineHeight = 48.sp, - ) + if (isPasswordVisual && text.isNotEmpty()) { + Canvas( + Modifier.sizeIn(minWidth = minWidth, minHeight = minHeight), + ) { + drawCircle( + color = color, + radius = 4.dp.toPx(), + center = center, + ) + } + } else { + Text( + modifier = Modifier.sizeIn(minWidth = minWidth, minHeight = minHeight), + text = text, + style = TangemTheme.typography.h3, + color = color, + textAlign = TextAlign.Center, + lineHeight = 48.sp, + ) + } } } } @@ -152,10 +181,18 @@ private fun Preview() { var text by remember { mutableStateOf("123") } Column { + PinTextField( + value = text, + onValueChange = { text = it }, + isPasswordVisual = true, + pinTextColor = PinTextColor.Success, + length = 6, + ) PinTextField( value = text, onValueChange = { text = it }, isPasswordVisual = false, + pinTextColor = PinTextColor.Primary, length = 6, ) diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/ui/AccessCode.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/ui/AccessCode.kt index 987c94c953..230f97bab4 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/ui/AccessCode.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/ui/AccessCode.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.tangem.core.res.R import com.tangem.core.ui.components.PrimaryButton +import com.tangem.core.ui.components.fields.PinTextColor import com.tangem.core.ui.components.fields.PinTextField import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme @@ -75,6 +76,7 @@ internal fun AccessCode(state: AccessCodeUM, modifier: Modifier = Modifier) { length = state.accessCodeLength, isPasswordVisual = true, value = state.accessCode, + pinTextColor = PinTextColor.Primary, onValueChange = state.onAccessCodeChange, ) } diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/DefaultHotAccessCodeRequestComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/DefaultHotAccessCodeRequestComponent.kt index 5bacfc9fef..6777d34243 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/DefaultHotAccessCodeRequestComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/DefaultHotAccessCodeRequestComponent.kt @@ -26,8 +26,7 @@ internal class DefaultHotAccessCodeRequestComponent @AssistedInject constructor( } override suspend fun successfulAuthentication() { - // TODO handle successful authentication - // TODO add delay + model.successfulAuthentication() } override suspend fun requestPassword(hasBiometry: Boolean): HotWalletPasswordRequester.Result { diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/HotAccessCodeRequestModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/HotAccessCodeRequestModel.kt index 305bd305bb..f210270b41 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/HotAccessCodeRequestModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/HotAccessCodeRequestModel.kt @@ -2,6 +2,7 @@ package com.tangem.features.hotwallet.accesscoderequest import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model +import com.tangem.core.ui.components.fields.PinTextColor import com.tangem.domain.wallets.hot.HotWalletPasswordRequester import com.tangem.features.hotwallet.accesscode.ACCESS_CODE_LENGTH import com.tangem.features.hotwallet.accesscoderequest.entity.HotAccessCodeRequestUM @@ -45,13 +46,23 @@ internal class HotAccessCodeRequestModel @Inject constructor( suspend fun wrongAccessCode() { uiState.update { it.copy( - wrongAccessCode = true, + accessCodeColor = PinTextColor.WrongCode, onAccessCodeChange = {}, ) } delay(timeMillis = 500) // Delay to show the wrong access code state } + suspend fun successfulAuthentication() { + uiState.update { + it.copy( + accessCodeColor = PinTextColor.Success, + onAccessCodeChange = {}, + ) + } + delay(timeMillis = 200) // Delay to show the success state + } + private fun getInitialState() = HotAccessCodeRequestUM( onDismiss = ::dismiss, onAccessCodeChange = ::onAccessCodeChange, @@ -66,7 +77,10 @@ internal class HotAccessCodeRequestModel @Inject constructor( if (accessCode.length > ACCESS_CODE_LENGTH) return uiState.update { - it.copy(accessCode = accessCode, wrongAccessCode = false) + it.copy( + accessCode = accessCode, + accessCodeColor = PinTextColor.Primary, + ) } if (accessCode.length == ACCESS_CODE_LENGTH) { diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/entity/HotAccessCodeRequestUM.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/entity/HotAccessCodeRequestUM.kt index 82b7e51ec8..62f2b4d051 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/entity/HotAccessCodeRequestUM.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/entity/HotAccessCodeRequestUM.kt @@ -1,9 +1,11 @@ package com.tangem.features.hotwallet.accesscoderequest.entity +import com.tangem.core.ui.components.fields.PinTextColor + internal data class HotAccessCodeRequestUM( val isShown: Boolean = false, val accessCode: String = "", - val wrongAccessCode: Boolean = false, + val accessCodeColor: PinTextColor = PinTextColor.Primary, val useBiometricVisible: Boolean = true, val useBiometricClick: () -> Unit = {}, val onAccessCodeChange: (String) -> Unit = {}, diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/ui/HotAccessCodeRequestFullScreenContent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/ui/HotAccessCodeRequestFullScreenContent.kt index bc64b660c4..9ae3230ec4 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/ui/HotAccessCodeRequestFullScreenContent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscoderequest/ui/HotAccessCodeRequestFullScreenContent.kt @@ -20,6 +20,7 @@ import com.tangem.core.ui.components.SpacerH import com.tangem.core.ui.components.SpacerH24 import com.tangem.core.ui.components.appbar.TangemTopAppBar import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM +import com.tangem.core.ui.components.fields.PinTextColor import com.tangem.core.ui.components.fields.PinTextField import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.haptic.TangemHapticEffect @@ -85,7 +86,7 @@ internal fun HotAccessCodeRequestFullScreenContent(state: HotAccessCodeRequestUM length = 6, isPasswordVisual = true, value = state.accessCode, - wrongCode = state.wrongAccessCode, + pinTextColor = state.accessCodeColor, onValueChange = state.onAccessCodeChange, ) } @@ -106,9 +107,15 @@ internal fun HotAccessCodeRequestFullScreenContent(state: HotAccessCodeRequestUM val hapticManager = LocalHapticManager.current - LaunchedEffect(state.wrongAccessCode) { - if (state.wrongAccessCode) { - hapticManager.perform(TangemHapticEffect.View.Reject) + LaunchedEffect(state.accessCodeColor) { + when (state.accessCodeColor) { + PinTextColor.WrongCode -> { + hapticManager.perform(TangemHapticEffect.View.Reject) + } + PinTextColor.Success -> { + hapticManager.perform(TangemHapticEffect.View.Confirm) + } + else -> Unit } } }