diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeComponent.kt index b3f1b8afa4..ec2f7d3ca4 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeComponent.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.arkivanov.essenty.lifecycle.doOnResume import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent @@ -21,6 +22,12 @@ internal class AccessCodeComponent @AssistedInject constructor( private val model: AccessCodeModel = getOrCreateModel(params) + init { + lifecycle.doOnResume { + model.onResume() + } + } + @Composable override fun Content(modifier: Modifier) { val state by model.uiState.collectAsStateWithLifecycle() diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeModel.kt index 58636050b1..96fc1b4b46 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeModel.kt @@ -27,6 +27,7 @@ import com.tangem.hot.sdk.model.HotAuth import com.tangem.hot.sdk.model.UnlockHotWallet import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update @@ -36,6 +37,9 @@ import timber.log.Timber import javax.inject.Inject import kotlin.coroutines.resume +private const val SUCCESS_DISPLAY_DURATION_MS = 200L +private const val ERROR_DISPLAY_DURATION_MS = 500L + @Suppress("LongParameterList") @Stable @ModelScoped @@ -59,49 +63,93 @@ internal class AccessCodeModel @Inject constructor( internal val uiState: StateFlow field = MutableStateFlow(getInitialState()) + internal fun onResume() { + uiState.update { currentState -> + currentState.copy( + onAccessCodeChange = ::onAccessCodeChange, + ) + } + } + private fun getInitialState() = AccessCodeUM( accessCode = "", accessCodeColor = PinTextColor.Primary, onAccessCodeChange = ::onAccessCodeChange, isConfirmMode = params.accessCodeToConfirm != null, - buttonEnabled = false, - buttonInProgress = false, - onButtonClick = ::onButtonClick, ) private fun onAccessCodeChange(value: String) { - uiState.update { - it.copy( + val isConfirmMode = uiState.value.isConfirmMode + val accessCodeLength = uiState.value.accessCodeLength + + if (value.length > accessCodeLength) return + + uiState.update { currentState -> + currentState.copy( accessCode = value, - buttonEnabled = if (params.accessCodeToConfirm != null) { - value == params.accessCodeToConfirm - } else { - value.length == uiState.value.accessCodeLength - }, - accessCodeColor = when { - params.accessCodeToConfirm == null -> PinTextColor.Primary - value.length != uiState.value.accessCodeLength -> PinTextColor.Primary - value == params.accessCodeToConfirm -> PinTextColor.Primary - else -> PinTextColor.WrongCode - }, + accessCodeColor = PinTextColor.Primary, ) } + + if (value.length == accessCodeLength) { + uiState.update { currentState -> + currentState.copy( + onAccessCodeChange = {}, + ) + } + + if (isConfirmMode) { + modelScope.launch { + if (value == params.accessCodeToConfirm) { + showSuccessAndProceed(params.accessCodeToConfirm) + } else { + showErrorAndReset() + } + } + } else { + onNewCodeSet() + } + } } - private fun onButtonClick() { - if (params.accessCodeToConfirm == null) { - params.callbacks.onNewAccessCodeInput(params.userWalletId, uiState.value.accessCode) - } else { - setCode(params.userWalletId, params.accessCodeToConfirm) + private fun onNewCodeSet() { + val accessCode = uiState.value.accessCode + uiState.update { currentState -> + currentState.copy( + accessCode = "", + ) + } + params.callbacks.onNewAccessCodeInput(params.userWalletId, accessCode) + } + + private suspend fun showSuccessAndProceed(accessCodeToConfirm: String) { + uiState.update { currentState -> + currentState.copy( + accessCodeColor = PinTextColor.Success, + ) + } + delay(timeMillis = SUCCESS_DISPLAY_DURATION_MS) + setCode(params.userWalletId, accessCodeToConfirm) + } + + private suspend fun showErrorAndReset() { + uiState.update { currentState -> + currentState.copy( + accessCodeColor = PinTextColor.WrongCode, + ) + } + delay(timeMillis = ERROR_DISPLAY_DURATION_MS) + uiState.update { currentState -> + currentState.copy( + accessCode = "", + accessCodeColor = PinTextColor.Primary, + onAccessCodeChange = ::onAccessCodeChange, + ) } } private fun setCode(userWalletId: UserWalletId, accessCode: String) { modelScope.launch { - uiState.update { - it.copy(buttonInProgress = true) - } - runCatching { val userWallet = getUserWalletUseCase(userWalletId) .getOrElse { error("User wallet with id $userWalletId not found") } @@ -150,10 +198,6 @@ internal class AccessCodeModel @Inject constructor( params.callbacks.onAccessCodeUpdated(params.userWalletId) }.onFailure { Timber.e(it) - - uiState.update { - it.copy(buttonInProgress = false) - } } } } diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/entity/AccessCodeUM.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/entity/AccessCodeUM.kt index be65b51a49..1bc94c1d7c 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/entity/AccessCodeUM.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/entity/AccessCodeUM.kt @@ -8,9 +8,6 @@ internal data class AccessCodeUM( val accessCodeColor: PinTextColor, val onAccessCodeChange: (String) -> Unit, val isConfirmMode: Boolean, - val buttonEnabled: Boolean, - val buttonInProgress: Boolean, - val onButtonClick: () -> Unit, ) { val accessCodeLength: Int = ACCESS_CODE_LENGTH } \ No newline at end of file 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 87161aa57b..5d0526fe6b 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 @@ -11,7 +11,6 @@ import androidx.compose.ui.text.style.TextAlign 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 @@ -25,6 +24,7 @@ internal fun AccessCode(state: AccessCodeUM, modifier: Modifier = Modifier) { Column( modifier = modifier .fillMaxSize() + .background(TangemTheme.colors.background.primary) .navigationBarsPadding(), ) { Column( @@ -32,7 +32,6 @@ internal fun AccessCode(state: AccessCodeUM, modifier: Modifier = Modifier) { .padding(top = 16.dp) .weight(1f) .fillMaxSize() - .background(TangemTheme.colors.background.primary) .padding(horizontal = 16.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { @@ -81,23 +80,6 @@ internal fun AccessCode(state: AccessCodeUM, modifier: Modifier = Modifier) { ) } } - - PrimaryButton( - modifier = Modifier - .fillMaxWidth() - .padding(start = 16.dp, end = 16.dp, bottom = 16.dp) - .imePadding(), - text = stringResourceSafe( - if (state.isConfirmMode) { - R.string.common_confirm - } else { - R.string.common_continue - }, - ), - onClick = state.onButtonClick, - enabled = state.buttonEnabled, - showProgress = state.buttonInProgress, - ) } } @@ -112,9 +94,6 @@ private fun PreviewSet() { accessCodeColor = PinTextColor.Primary, onAccessCodeChange = {}, isConfirmMode = false, - buttonEnabled = false, - buttonInProgress = false, - onButtonClick = {}, ), ) } @@ -131,9 +110,6 @@ private fun PreviewConfirm() { accessCodeColor = PinTextColor.Success, onAccessCodeChange = {}, isConfirmMode = true, - buttonEnabled = true, - buttonInProgress = false, - onButtonClick = {}, ), ) } diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/updateaccesscode/DefaultUpdateAccessCodeComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/updateaccesscode/DefaultUpdateAccessCodeComponent.kt index 127f506d0c..790f91c922 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/updateaccesscode/DefaultUpdateAccessCodeComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/updateaccesscode/DefaultUpdateAccessCodeComponent.kt @@ -6,6 +6,8 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.decompose.router.stack.childStack +import com.arkivanov.decompose.value.ObserveLifecycleMode +import com.arkivanov.decompose.value.subscribe import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.model.getOrCreateModel @@ -14,6 +16,7 @@ import com.tangem.features.hotwallet.updateaccesscode.routing.UpdateAccessCodeCh import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import kotlinx.coroutines.launch internal class DefaultUpdateAccessCodeComponent @AssistedInject constructor( @Assisted private val appComponentContext: AppComponentContext, @@ -38,6 +41,17 @@ internal class DefaultUpdateAccessCodeComponent @AssistedInject constructor( }, ) + init { + innerStack.subscribe( + lifecycle = lifecycle, + mode = ObserveLifecycleMode.CREATE_DESTROY, + ) { stack -> + componentScope.launch { + model.currentRoute.emit(stack.active.configuration) + } + } + } + @Composable override fun Content(modifier: Modifier) { val stackState by innerStack.subscribeAsState()