From 2f325fa46f9b0303128dfe3f62d0188ef942e603 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 8 Jul 2024 16:50:43 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../presentation/AddCustomTokenFragment.kt | 7 +- .../viewmodels/AddCustomTokenViewModel.kt | 539 ++++++++++-------- 2 files changed, 298 insertions(+), 248 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/AddCustomTokenFragment.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/AddCustomTokenFragment.kt index da295c57c5..f2b19a713d 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/AddCustomTokenFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/AddCustomTokenFragment.kt @@ -2,9 +2,11 @@ package com.tangem.tap.features.customtoken.impl.presentation import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.components.SystemBarsEffect import com.tangem.core.ui.res.TangemTheme @@ -34,9 +36,12 @@ internal class AddCustomTokenFragment : ComposeFragment() { SystemBarsEffect { setSystemBarsColor(color = statusBarColor) } + + val state by viewModel.uiState.collectAsStateWithLifecycle() + AddCustomTokenScreen( modifier = Modifier.systemBarsPadding(), - stateHolder = viewModel.uiState, + stateHolder = state, ) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt index 5f523da11c..3b9c1f3f5d 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt @@ -1,9 +1,6 @@ package com.tangem.tap.features.customtoken.impl.presentation.viewmodels import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.lifecycle.DefaultLifecycleObserver @@ -49,6 +46,9 @@ import com.tangem.wallet.R import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.flow.updateAndGet import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -85,8 +85,7 @@ internal class AddCustomTokenViewModel @Inject constructor( private var currentCryptoCurrencies: List = emptyList() /** Screen state */ - var uiState by mutableStateOf(getInitialUiState()) - private set + val uiState: MutableStateFlow = MutableStateFlow(value = getInitialUiState()) private var foundToken: FoundToken? = null @@ -376,44 +375,48 @@ internal class AddCustomTokenViewModel @Inject constructor( } .onSuccess { token -> foundToken = token - uiState = uiState.copySealed( - form = uiState.form.copy( - contractAddressInputField = uiState.form.contractAddressInputField.copy( - isLoading = false, - ), - networkSelectorField = uiState.form.networkSelectorField.copy( - selectedItem = formStateBuilder.createNetworkSelectorItem( - blockchain = Blockchain.fromNetworkId(token.network.id) - ?: Blockchain.Unknown, + uiState.update { state -> + state.copySealed( + form = state.form.copy( + contractAddressInputField = state.form.contractAddressInputField.copy( + isLoading = false, + ), + networkSelectorField = state.form.networkSelectorField.copy( + selectedItem = formStateBuilder.createNetworkSelectorItem( + blockchain = Blockchain.fromNetworkId(token.network.id) + ?: Blockchain.Unknown, + ), + ), + tokenNameInputField = state.form.tokenNameInputField.copy( + value = token.name, + isEnabled = false, + ), + tokenSymbolInputField = state.form.tokenSymbolInputField.copy( + value = token.symbol, + isEnabled = false, + ), + decimalsInputField = state.form.decimalsInputField.copy( + value = token.network.decimalCount, + isEnabled = false, ), ), - tokenNameInputField = uiState.form.tokenNameInputField.copy( - value = token.name, - isEnabled = false, - ), - tokenSymbolInputField = uiState.form.tokenSymbolInputField.copy( - value = token.symbol, - isEnabled = false, - ), - decimalsInputField = uiState.form.decimalsInputField.copy( - value = token.network.decimalCount, - isEnabled = false, - ), - ), - ) + ) + } } .onFailure { foundToken = null - uiState = uiState.copySealed( - form = uiState.form.copy( - contractAddressInputField = uiState.form.contractAddressInputField.copy( - isLoading = false, + uiState.update { state -> + state.copySealed( + form = state.form.copy( + contractAddressInputField = state.form.contractAddressInputField.copy( + isLoading = false, + ), + tokenNameInputField = state.form.tokenNameInputField.copy(isEnabled = true), + tokenSymbolInputField = state.form.tokenSymbolInputField.copy(isEnabled = true), + decimalsInputField = state.form.decimalsInputField.copy(isEnabled = true), ), - tokenNameInputField = uiState.form.tokenNameInputField.copy(isEnabled = true), - tokenSymbolInputField = uiState.form.tokenSymbolInputField.copy(isEnabled = true), - decimalsInputField = uiState.form.decimalsInputField.copy(isEnabled = true), - ), - ) + ) + } Timber.e(it) } @@ -423,28 +426,31 @@ internal class AddCustomTokenViewModel @Inject constructor( } private fun isDerivationPathSelected(): Boolean { - return with(uiState.form.derivationPathSelectorField?.selectedItem?.blockchain) { - this != null && this != Blockchain.Unknown || - uiState.form.derivationPathSelectorField?.selectedItem?.type == DerivationPathSelectorType.CUSTOM && - !uiState.warnings.contains(AddCustomTokenWarning.WrongDerivationPath) - } + val blockchain = uiState.value.form.derivationPathSelectorField?.selectedItem?.blockchain + val selectorType = uiState.value.form.derivationPathSelectorField?.selectedItem?.type + + return blockchain != null && blockchain != Blockchain.Unknown || + selectorType == DerivationPathSelectorType.CUSTOM && + !uiState.value.warnings.contains(AddCustomTokenWarning.WrongDerivationPath) } private fun updateWarnings() { - uiState = uiState.copySealed( - warnings = buildSet { - when (getCustomTokenType()) { - CustomTokenType.TOKEN -> { - addAll(getTokenWarningSet()) - } + uiState.update { state -> + state.copySealed( + warnings = buildSet { + when (getCustomTokenType()) { + CustomTokenType.TOKEN -> { + addAll(getTokenWarningSet()) + } - CustomTokenType.BLOCKCHAIN -> { - if (isCustomTokenAlreadyAdded()) add(AddCustomTokenWarning.TokenAlreadyAdded) - if (isDerivationPathSelected()) add(AddCustomTokenWarning.PotentialScamToken) + CustomTokenType.BLOCKCHAIN -> { + if (isCustomTokenAlreadyAdded()) add(AddCustomTokenWarning.TokenAlreadyAdded) + if (isDerivationPathSelected()) add(AddCustomTokenWarning.PotentialScamToken) + } } - } - }, - ) + }, + ) + } } private fun getCustomTokenType(): CustomTokenType { @@ -456,24 +462,24 @@ internal class AddCustomTokenViewModel @Inject constructor( } private fun isAnyTokenFieldsFilled(): Boolean { - return with(uiState.form) { + return with(uiState.value.form) { contractAddressInputField.value.isNotEmpty() || tokenNameInputField.value.isNotEmpty() || tokenSymbolInputField.value.isNotEmpty() || decimalsInputField.value.isNotEmpty() } } private fun isAllTokenFieldsFilled(): Boolean { - return with(uiState.form) { + return with(uiState.value.form) { contractAddressInputField.value.isNotEmpty() && tokenNameInputField.value.isNotEmpty() && tokenSymbolInputField.value.isNotEmpty() && decimalsInputField.value.isNotEmpty() } } private fun getTokenWarningSet(): Set { - val networkSelectorValue = uiState.form.networkSelectorField.selectedItem.blockchain + val networkSelectorValue = uiState.value.form.networkSelectorField.selectedItem.blockchain val isContractAddressFieldEmpty = ContractAddressValidator.validate( - address = uiState.form.contractAddressInputField.value, + address = uiState.value.form.contractAddressInputField.value, blockchain = networkSelectorValue, ).let { it is ContractAddressValidatorResult.Error && it.type == AddCustomTokenError.FIELD_IS_EMPTY @@ -507,66 +513,70 @@ internal class AddCustomTokenViewModel @Inject constructor( } private fun isNetworkSelected(): Boolean { - return uiState.form.networkSelectorField.selectedItem.blockchain != Blockchain.Unknown + return uiState.value.form.networkSelectorField.selectedItem.blockchain != Blockchain.Unknown } private fun updateFloatingButton() { - uiState = updateStateWithDerivationError(uiState) + uiState.update { state -> + updateStateWithDerivationError(state) + } if (isCustomTokenAlreadyAdded()) { - uiState = uiState.copySealed( - warnings = uiState.warnings + AddCustomTokenWarning.TokenAlreadyAdded, - floatingButton = uiState.floatingButton.copy(isEnabled = false), - ) + uiState.update { state -> + state.copySealed( + warnings = state.warnings + AddCustomTokenWarning.TokenAlreadyAdded, + floatingButton = state.floatingButton.copy(isEnabled = false), + ) + } return } - val isCorrectDerivationInput = !uiState.warnings.contains(AddCustomTokenWarning.WrongDerivationPath) - val state = when { - isAllTokenFieldsFilled() && isNetworkSelected() -> { - val networkSelectorValue = uiState.form.networkSelectorField.selectedItem.blockchain - val error = ContractAddressValidator.validate( - address = uiState.form.contractAddressInputField.value, - blockchain = networkSelectorValue, - ) + uiState.update { state -> + val isCorrectDerivationInput = !state.warnings.contains(AddCustomTokenWarning.WrongDerivationPath) + val updatedState = when { + isAllTokenFieldsFilled() && isNetworkSelected() -> { + val networkSelectorValue = state.form.networkSelectorField.selectedItem.blockchain + val error = ContractAddressValidator.validate( + address = state.form.contractAddressInputField.value, + blockchain = networkSelectorValue, + ) - val isSupportedToken = getSelectedWalletSyncUseCase().fold( - ifLeft = { false }, - ifRight = { - it.scanResponse.card.canHandleToken( - blockchain = networkSelectorValue, - cardTypesResolver = it.scanResponse.cardTypesResolver, - ) - }, - ) - - uiState.copySealed( - floatingButton = uiState.floatingButton.copy( - isEnabled = error is ContractAddressValidatorResult.Success && - isSupportedToken && isCorrectDerivationInput, - ), - ) - } - - isAnyTokenFieldsFilled() -> { - uiState.copySealed(floatingButton = uiState.floatingButton.copy(isEnabled = false)) - } - - else -> { - uiState.copySealed( - floatingButton = uiState.floatingButton.copy( - isEnabled = if (isNetworkSelected()) { - !isBlockchainAlreadyAdded() && isCorrectDerivationInput - } else { - false + val isSupportedToken = getSelectedWalletSyncUseCase().fold( + ifLeft = { false }, + ifRight = { + it.scanResponse.card.canHandleToken( + blockchain = networkSelectorValue, + cardTypesResolver = it.scanResponse.cardTypesResolver, + ) }, - ), - ) - } - } + ) - uiState = state.copySealed( - warnings = state.warnings - AddCustomTokenWarning.TokenAlreadyAdded, - ) + state.copySealed( + floatingButton = state.floatingButton.copy( + isEnabled = error is ContractAddressValidatorResult.Success && + isSupportedToken && isCorrectDerivationInput, + ), + ) + } + isAnyTokenFieldsFilled() -> { + state.copySealed(floatingButton = state.floatingButton.copy(isEnabled = false)) + } + else -> { + state.copySealed( + floatingButton = state.floatingButton.copy( + isEnabled = if (isNetworkSelected()) { + !isBlockchainAlreadyAdded() && isCorrectDerivationInput + } else { + false + }, + ), + ) + } + } + + updatedState.copySealed( + warnings = updatedState.warnings - AddCustomTokenWarning.TokenAlreadyAdded, + ) + } } private fun updateStateWithDerivationError(uiState: AddCustomTokenStateHolder): AddCustomTokenStateHolder { @@ -599,11 +609,11 @@ internal class AddCustomTokenViewModel @Inject constructor( } private fun isTokenAlreadyAdded(): Boolean { - val networkSelectorValue = uiState.form.networkSelectorField.selectedItem.blockchain + val networkSelectorValue = uiState.value.form.networkSelectorField.selectedItem.blockchain val networkId = Blockchain.fromNetworkId(networkSelectorValue.toNetworkId())?.id val contractAddress = convertTokenAddress( blockchain = networkSelectorValue, - address = uiState.form.contractAddressInputField.value, + address = uiState.value.form.contractAddressInputField.value, ) ?: return false // invalid address can't be "already added" return currentCryptoCurrencies @@ -628,7 +638,7 @@ internal class AddCustomTokenViewModel @Inject constructor( return currentCryptoCurrencies .filterIsInstance() .any { coin -> - coin.network.id.value == uiState.form.networkSelectorField.selectedItem.blockchain.id && + coin.network.id.value == uiState.value.form.networkSelectorField.selectedItem.blockchain.id && coin.network.derivationPath.value == getDerivationPath()?.rawPath } } @@ -637,39 +647,43 @@ internal class AddCustomTokenViewModel @Inject constructor( when { isNetworkSelected() && type == AddCustomTokenError.INVALID_CONTRACT_ADDRESS -> { val isAnotherTokenFieldsFilled = isAnyTokenFieldsFilled() - uiState = uiState.copySealed( - form = uiState.form.copy( - contractAddressInputField = uiState.form.contractAddressInputField.copy( - isError = true, - error = TextReference.Res( - id = R.string.custom_token_creation_error_invalid_contract_address, + uiState.update { state -> + state.copySealed( + form = state.form.copy( + contractAddressInputField = state.form.contractAddressInputField.copy( + isError = true, + error = TextReference.Res( + id = R.string.custom_token_creation_error_invalid_contract_address, + ), + ), + tokenNameInputField = state.form.tokenNameInputField.copy( + isEnabled = isAnotherTokenFieldsFilled, + ), + tokenSymbolInputField = state.form.tokenSymbolInputField.copy( + isEnabled = isAnotherTokenFieldsFilled, + ), + decimalsInputField = state.form.decimalsInputField.copy( + isEnabled = isAnotherTokenFieldsFilled, ), ), - tokenNameInputField = uiState.form.tokenNameInputField.copy( - isEnabled = isAnotherTokenFieldsFilled, - ), - tokenSymbolInputField = uiState.form.tokenSymbolInputField.copy( - isEnabled = isAnotherTokenFieldsFilled, - ), - decimalsInputField = uiState.form.decimalsInputField.copy( - isEnabled = isAnotherTokenFieldsFilled, - ), - ), - ) + ) + } } !isNetworkSelected() || type == AddCustomTokenError.FIELD_IS_EMPTY -> { - uiState = uiState.copySealed( - form = uiState.form.copy( - contractAddressInputField = uiState.form.contractAddressInputField.copy(isError = false), - tokenNameInputField = uiState.form.tokenNameInputField.copy(value = "", isEnabled = false), - tokenSymbolInputField = uiState.form.tokenSymbolInputField.copy( - value = "", - isEnabled = false, + uiState.update { state -> + state.copySealed( + form = state.form.copy( + contractAddressInputField = state.form.contractAddressInputField.copy(isError = false), + tokenNameInputField = state.form.tokenNameInputField.copy(value = "", isEnabled = false), + tokenSymbolInputField = state.form.tokenSymbolInputField.copy( + value = "", + isEnabled = false, + ), + decimalsInputField = state.form.decimalsInputField.copy(value = "", isEnabled = false), ), - decimalsInputField = uiState.form.decimalsInputField.copy(value = "", isEnabled = false), - ), - ) + ) + } } else -> Unit @@ -677,11 +691,11 @@ internal class AddCustomTokenViewModel @Inject constructor( } private fun getDerivationPath(): DerivationPath? { - return when (uiState.form.derivationPathSelectorField?.selectedItem?.type) { + return when (uiState.value.form.derivationPathSelectorField?.selectedItem?.type) { DerivationPathSelectorType.CUSTOM -> - createDerivationPathOrNull(uiState.form.derivationPathInputField?.value ?: "") + createDerivationPathOrNull(uiState.value.form.derivationPathInputField?.value ?: "") else -> - getDerivationPathForBlockchain(uiState.form.derivationPathSelectorField?.selectedItem?.blockchain) + getDerivationPathForBlockchain(uiState.value.form.derivationPathSelectorField?.selectedItem?.blockchain) } } @@ -704,7 +718,7 @@ internal class AddCustomTokenViewModel @Inject constructor( ) val derivationNetwork = if (blockchain == Blockchain.Unknown) { - uiState.form.networkSelectorField.selectedItem.blockchain + uiState.value.form.networkSelectorField.selectedItem.blockchain } else { blockchain } @@ -739,13 +753,15 @@ internal class AddCustomTokenViewModel @Inject constructor( } fun onContactAddressValueChange(enteredValue: String) { - uiState = uiState.copySealed( - form = uiState.form.copy( - contractAddressInputField = uiState.form.contractAddressInputField.copy(value = enteredValue), - ), - ) + uiState.update { state -> + state.copySealed( + form = state.form.copy( + contractAddressInputField = state.form.contractAddressInputField.copy(value = enteredValue), + ), + ) + } - val selectedNetwork = uiState.form.networkSelectorField.selectedItem.blockchain + val selectedNetwork = uiState.value.form.networkSelectorField.selectedItem.blockchain val validatorResult = ContractAddressValidator.validate( address = enteredValue, blockchain = selectedNetwork, @@ -753,14 +769,16 @@ internal class AddCustomTokenViewModel @Inject constructor( when (validatorResult) { is ContractAddressValidatorResult.Success -> { - uiState = uiState.copySealed( - form = uiState.form.copy( - contractAddressInputField = uiState.form.contractAddressInputField.copy( - isError = false, - isLoading = true, + uiState.update { state -> + state.copySealed( + form = state.form.copy( + contractAddressInputField = state.form.contractAddressInputField.copy( + isError = false, + isLoading = true, + ), ), - ), - ) + ) + } updateForm(address = enteredValue, selectedNetwork = selectedNetwork) } @@ -773,76 +791,92 @@ internal class AddCustomTokenViewModel @Inject constructor( } fun onNetworkSelectorItemClick(index: Int) { - val selectedItem = requireNotNull(uiState.form.networkSelectorField.items.getOrNull(index)) - uiState = uiState.copySealed( - form = uiState.form.copy( - networkSelectorField = uiState.form.networkSelectorField.copy( - selectedItem = selectedItem, + val state = uiState.updateAndGet { state -> + val selectedItem = requireNotNull(state.form.networkSelectorField.items.getOrNull(index)) + state.copySealed( + form = state.form.copy( + networkSelectorField = state.form.networkSelectorField.copy( + selectedItem = selectedItem, + ), + showTokenFields = selectedItem.blockchain.canHandleTokens() && + // workaround cause in Terra we support only 1 token + selectedItem.blockchain != Blockchain.TerraV1, ), - showTokenFields = selectedItem.blockchain.canHandleTokens() && - // workaround cause in Terra we support only 1 token - selectedItem.blockchain != Blockchain.TerraV1, - ), - ) - onContactAddressValueChange(uiState.form.contractAddressInputField.value) + ) + } + + onContactAddressValueChange(state.form.contractAddressInputField.value) } fun onTokenNameValueChange(enteredValue: String) { - uiState = uiState.copySealed( - form = uiState.form.copy( - tokenNameInputField = uiState.form.tokenNameInputField.copy(value = enteredValue), - ), - ) + uiState.update { state -> + state.copySealed( + form = state.form.copy( + tokenNameInputField = state.form.tokenNameInputField.copy(value = enteredValue), + ), + ) + } + updateFloatingButton() } fun onTokenSymbolValueChange(enteredValue: String) { - uiState = uiState.copySealed( - form = uiState.form.copy( - tokenSymbolInputField = uiState.form.tokenSymbolInputField.copy(value = enteredValue), - ), - ) + uiState.update { state -> + state.copySealed( + form = state.form.copy( + tokenSymbolInputField = state.form.tokenSymbolInputField.copy(value = enteredValue), + ), + ) + } updateFloatingButton() } fun onDecimalsValueChange(enteredValue: String) { - uiState = uiState.copySealed( - form = uiState.form.copy( - decimalsInputField = uiState.form.decimalsInputField.copy(value = enteredValue), - ), - ) + uiState.update { state -> + state.copySealed( + form = state.form.copy( + decimalsInputField = state.form.decimalsInputField.copy(value = enteredValue), + ), + ) + } updateFloatingButton() } fun onDerivationPathSelectorItemClick(index: Int) { - val derivationSelector = requireNotNull(uiState.form.derivationPathSelectorField) - val selected = requireNotNull(derivationSelector.items.getOrNull(index)) - val derivationInputField = requireNotNull(uiState.form.derivationPathInputField) - uiState = uiState.copySealed( - form = uiState.form.copy( - derivationPathSelectorField = derivationSelector.copy( - selectedItem = selected, + uiState.update { state -> + val derivationSelector = requireNotNull(state.form.derivationPathSelectorField) + val selected = requireNotNull(derivationSelector.items.getOrNull(index)) + val derivationInputField = requireNotNull(state.form.derivationPathInputField) + + state.copySealed( + form = state.form.copy( + derivationPathSelectorField = derivationSelector.copy( + selectedItem = selected, + ), + derivationPathInputField = derivationInputField.copy( + showField = selected.type == DerivationPathSelectorType.CUSTOM, + ), ), - derivationPathInputField = derivationInputField.copy( - showField = selected.type == DerivationPathSelectorType.CUSTOM, - ), - ), - ) + ) + } + updateFloatingButton() } fun onDerivationPathValueChange(enteredValue: String) { - uiState = uiState.copySealed( - form = uiState.form.copy( - derivationPathInputField = uiState.form.derivationPathInputField?.copy(value = enteredValue), - ), - ) + uiState.update { state -> + state.copySealed( + form = state.form.copy( + derivationPathInputField = state.form.derivationPathInputField?.copy(value = enteredValue), + ), + ) + } updateFloatingButton() } fun onAddCustomTokenClick() { if (!isNetworkSelected()) return - val blockchain = uiState.form.networkSelectorField.selectedItem.blockchain + val blockchain = uiState.value.form.networkSelectorField.selectedItem.blockchain when (getSupportBlockchainType(blockchain)) { SupportBlockchainType.SUPPORTED -> { /* no-op */ @@ -861,15 +895,16 @@ internal class AddCustomTokenViewModel @Inject constructor( CustomTokenType.TOKEN -> { val contractAddress = convertTokenAddress( blockchain = blockchain, - address = foundToken?.network?.contractAddress ?: uiState.form.contractAddressInputField.value, + address = foundToken?.network?.contractAddress ?: uiState.value.form.contractAddressInputField + .value, ) ?: error("Contract address is invalid") // impossible to add a token with invalid address CustomCurrency.CustomToken( token = Token( - name = uiState.form.tokenNameInputField.value, - symbol = uiState.form.tokenSymbolInputField.value, + name = uiState.value.form.tokenNameInputField.value, + symbol = uiState.value.form.tokenSymbolInputField.value, contractAddress = contractAddress, - decimals = requireNotNull(uiState.form.decimalsInputField.value.toIntOrNull()), + decimals = requireNotNull(uiState.value.form.decimalsInputField.value.toIntOrNull()), id = foundToken?.id, ), network = blockchain, @@ -887,21 +922,25 @@ internal class AddCustomTokenViewModel @Inject constructor( analyticsSender.sendWhenAddTokenButtonClicked(currency) viewModelScope.launch { - uiState = uiState.copySealed( - floatingButton = uiState.floatingButton.copy( - isEnabled = false, - showProgress = true, - ), - ) + uiState.update { state -> + state.copySealed( + floatingButton = state.floatingButton.copy( + isEnabled = false, + showProgress = true, + ), + ) + } val result = featureInteractor.saveToken(currency) - uiState = uiState.copySealed( - floatingButton = uiState.floatingButton.copy( - isEnabled = true, - showProgress = false, - ), - ) + uiState.update { state -> + state.copySealed( + floatingButton = state.floatingButton.copy( + isEnabled = true, + showProgress = false, + ), + ) + } result .onSuccess { featureRouter.openWalletScreen() } @@ -917,7 +956,7 @@ internal class AddCustomTokenViewModel @Inject constructor( Blockchain.Cardano -> { cardanoTokenAddressConverter.convertToFingerprint( address = address, - symbol = uiState.form.tokenSymbolInputField.value, + symbol = uiState.value.form.tokenSymbolInputField.value, ) } else -> address @@ -927,46 +966,52 @@ internal class AddCustomTokenViewModel @Inject constructor( private inner class TestActionsHandler { fun onClearAddressButtonClick() { - uiState = uiState.copySealed( - form = uiState.form.copy( - contractAddressInputField = uiState.form.contractAddressInputField.copy( - value = "", - isLoading = false, - isError = false, - error = null, - ), - ), - ) - } - - fun onResetButtonClick() { - with(uiState.form) { - uiState = uiState.copySealed( - form = uiState.form.copy( - contractAddressInputField = contractAddressInputField.copy( + uiState.update { state -> + state.copySealed( + form = state.form.copy( + contractAddressInputField = state.form.contractAddressInputField.copy( value = "", isLoading = false, isError = false, error = null, ), - networkSelectorField = networkSelectorField.copy( - selectedItem = formStateBuilder.createNetworkSelectorItem(blockchain = Blockchain.Unknown), - ), - tokenNameInputField = tokenNameInputField.copy(value = "", isEnabled = false), - tokenSymbolInputField = tokenSymbolInputField.copy(value = "", isEnabled = false), - decimalsInputField = decimalsInputField.copy(value = "", isEnabled = false), - derivationPathSelectorField = derivationPathSelectorField?.copy( - isEnabled = true, - selectedItem = formStateBuilder.createDerivationPathSelectorAdditionalItem( - blockchain = Blockchain.Unknown, - type = DerivationPathSelectorType.DEFAULT, - derivationPath = "", - ), - ), ), ) } } + + fun onResetButtonClick() { + uiState.update { state -> + with(state.form) { + state.copySealed( + form = state.form.copy( + contractAddressInputField = contractAddressInputField.copy( + value = "", + isLoading = false, + isError = false, + error = null, + ), + networkSelectorField = networkSelectorField.copy( + selectedItem = formStateBuilder.createNetworkSelectorItem( + blockchain = Blockchain.Unknown, + ), + ), + tokenNameInputField = tokenNameInputField.copy(value = "", isEnabled = false), + tokenSymbolInputField = tokenSymbolInputField.copy(value = "", isEnabled = false), + decimalsInputField = decimalsInputField.copy(value = "", isEnabled = false), + derivationPathSelectorField = derivationPathSelectorField?.copy( + isEnabled = true, + selectedItem = formStateBuilder.createDerivationPathSelectorAdditionalItem( + blockchain = Blockchain.Unknown, + type = DerivationPathSelectorType.DEFAULT, + derivationPath = "", + ), + ), + ), + ) + } + } + } } private companion object {