Updated on 2026-08-14
This commit is contained in:
parent
c9af7551a7
commit
2f325fa46f
2 changed files with 298 additions and 248 deletions
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<CryptoCurrency> = emptyList()
|
||||
|
||||
/** Screen state */
|
||||
var uiState by mutableStateOf(getInitialUiState())
|
||||
private set
|
||||
val uiState: MutableStateFlow<AddCustomTokenStateHolder> = 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<AddCustomTokenWarning> {
|
||||
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<CryptoCurrency.Coin>()
|
||||
.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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue