Updated on 2026-08-14
This commit is contained in:
parent
0e515c142e
commit
24c6425299
7 changed files with 170 additions and 54 deletions
|
|
@ -2,6 +2,7 @@ package com.tangem.managetokens.presentation.addcustomtoken.state.factory
|
|||
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.event.triggeredEvent
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.tokens.error.AddCustomTokenError
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
|
|
@ -15,6 +16,7 @@ import kotlinx.collections.immutable.persistentSetOf
|
|||
import kotlinx.collections.immutable.toPersistentList
|
||||
import kotlinx.collections.immutable.toPersistentSet
|
||||
|
||||
@Suppress("LargeClass")
|
||||
internal class AddCustomTokenStateFactory(
|
||||
private val currentStateProvider: Provider<AddCustomTokenState>,
|
||||
private val clickIntents: AddCustomTokenClickIntents,
|
||||
|
|
@ -72,8 +74,11 @@ internal class AddCustomTokenStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
private fun getListOfDerivations(supportedNetworks: List<Network>): List<Derivation> {
|
||||
return supportedNetworks.mapNotNull { network ->
|
||||
private fun getListOfDerivations(
|
||||
networksListToGenerateDerivations: List<Network>,
|
||||
filterOnlyHardenedDerivations: Boolean = false,
|
||||
): List<Derivation> {
|
||||
return networksListToGenerateDerivations.mapNotNull { network ->
|
||||
network.derivationPath.value?.let { rawPath ->
|
||||
Derivation(
|
||||
networkName = network.name,
|
||||
|
|
@ -82,6 +87,12 @@ internal class AddCustomTokenStateFactory(
|
|||
networkId = network.backendId,
|
||||
onDerivationSelected = clickIntents::onDerivationSelected,
|
||||
)
|
||||
}.takeIf { derivation ->
|
||||
if (filterOnlyHardenedDerivations) {
|
||||
derivation?.let { allNodesHardened(createDerivationPathOrNull(it.path)) } ?: false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -218,41 +229,52 @@ internal class AddCustomTokenStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
fun updateStateOnNetworkSelected(networkItemState: NetworkItemState, supportsTokens: Boolean): AddCustomTokenState {
|
||||
fun updateStateOnNetworkSelected(
|
||||
networkItemState: NetworkItemState,
|
||||
supportsTokens: Boolean,
|
||||
networks: List<Network>,
|
||||
requiresHardenedDerivationOnly: Boolean,
|
||||
): AddCustomTokenState {
|
||||
val uiState = currentStateProvider()
|
||||
val tokenData = if (supportsTokens) {
|
||||
uiState.tokenData
|
||||
?: CustomTokenData(
|
||||
contractAddressTextField = TextFieldState.Editable(
|
||||
value = "",
|
||||
isEnabled = true,
|
||||
onValueChange = clickIntents::onContractAddressChange,
|
||||
onFocusExit = clickIntents::onContractAddressFocusExit,
|
||||
),
|
||||
nameTextField = TextFieldState.Editable(
|
||||
value = "",
|
||||
isEnabled = false,
|
||||
onValueChange = clickIntents::onTokenNameChange,
|
||||
onFocusExit = clickIntents::onTokenNameFocusExit,
|
||||
),
|
||||
symbolTextField = TextFieldState.Editable(
|
||||
value = "",
|
||||
isEnabled = false,
|
||||
onValueChange = clickIntents::onSymbolChange,
|
||||
onFocusExit = clickIntents::onSymbolFocusExit,
|
||||
),
|
||||
decimalsTextField = TextFieldState.Editable(
|
||||
value = "",
|
||||
isEnabled = false,
|
||||
onValueChange = clickIntents::onDecimalsChange,
|
||||
onFocusExit = clickIntents::onDecimalsFocusExit,
|
||||
),
|
||||
)
|
||||
uiState.tokenData ?: CustomTokenData(
|
||||
contractAddressTextField = TextFieldState.Editable(
|
||||
value = "",
|
||||
isEnabled = true,
|
||||
onValueChange = clickIntents::onContractAddressChange,
|
||||
onFocusExit = clickIntents::onContractAddressFocusExit,
|
||||
),
|
||||
nameTextField = TextFieldState.Editable(
|
||||
value = "",
|
||||
isEnabled = false,
|
||||
onValueChange = clickIntents::onTokenNameChange,
|
||||
onFocusExit = clickIntents::onTokenNameFocusExit,
|
||||
),
|
||||
symbolTextField = TextFieldState.Editable(
|
||||
value = "",
|
||||
isEnabled = false,
|
||||
onValueChange = clickIntents::onSymbolChange,
|
||||
onFocusExit = clickIntents::onSymbolFocusExit,
|
||||
),
|
||||
decimalsTextField = TextFieldState.Editable(
|
||||
value = "",
|
||||
isEnabled = false,
|
||||
onValueChange = clickIntents::onDecimalsChange,
|
||||
onFocusExit = clickIntents::onDecimalsFocusExit,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val derivations = getListOfDerivations(networks, requiresHardenedDerivationOnly)
|
||||
val chooseDerivationState = createChooseDerivationState(derivations)
|
||||
|
||||
return uiState.copy(
|
||||
chooseNetworkState = uiState.chooseNetworkState.copy(selectedNetwork = networkItemState),
|
||||
chooseNetworkState = uiState.chooseNetworkState.copy(
|
||||
selectedNetwork = networkItemState,
|
||||
),
|
||||
chooseDerivationState = chooseDerivationState,
|
||||
tokenData = tokenData,
|
||||
addTokenButton = uiState.addTokenButton.copy(isEnabled = true),
|
||||
)
|
||||
|
|
@ -291,6 +313,47 @@ internal class AddCustomTokenStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
fun updateOnCustomDerivationEntered(input: String, requiresHardenedDerivationOnly: Boolean): AddCustomTokenState {
|
||||
val uiState = currentStateProvider()
|
||||
val path = createDerivationPathOrNull(input)
|
||||
val isWrongDerivationForWallet2 = isWrongDerivationForWallet2(
|
||||
requiresHardenedDerivationOnly = requiresHardenedDerivationOnly,
|
||||
derivationPath = path,
|
||||
)
|
||||
val enterDerivationState = uiState.chooseDerivationState?.enterCustomDerivationState?.copy(
|
||||
confirmButtonEnabled = path != null && !isWrongDerivationForWallet2,
|
||||
derivationIncorrect = input.isNotBlank() && path == null || isWrongDerivationForWallet2,
|
||||
)
|
||||
return uiState.copy(
|
||||
chooseDerivationState = uiState.chooseDerivationState?.copy(
|
||||
enterCustomDerivationState = enterDerivationState,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun isWrongDerivationForWallet2(
|
||||
requiresHardenedDerivationOnly: Boolean,
|
||||
derivationPath: DerivationPath?,
|
||||
): Boolean {
|
||||
return if (requiresHardenedDerivationOnly) {
|
||||
!allNodesHardened(derivationPath)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun allNodesHardened(derivationPath: DerivationPath?): Boolean {
|
||||
return derivationPath?.nodes?.all { it.isHardened } ?: false
|
||||
}
|
||||
|
||||
private fun createDerivationPathOrNull(rawPath: String): DerivationPath? {
|
||||
return try {
|
||||
DerivationPath(rawPath)
|
||||
} catch (error: Throwable) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun updateStateOnLoadingTokenInfo(contractAddress: String): AddCustomTokenState {
|
||||
return currentStateProvider().copy(
|
||||
tokenData = CustomTokenData(
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@ import androidx.lifecycle.ViewModel
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.crypto.hdWallet.HDWalletError
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
|
@ -56,6 +54,7 @@ internal class AddCustomTokenViewModel @Inject constructor(
|
|||
private val validateContractAddressUseCase: ValidateContractAddressUseCase,
|
||||
private val getNetworksSupportedByWallet: GetNetworksSupportedByWallet,
|
||||
private val areTokensSupportedByNetworkUseCase: AreTokensSupportedByNetworkUseCase,
|
||||
private val requiresHardenedDerivationOnlyUseCase: RequiresHardenedDerivationOnlyUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : ViewModel(), AddCustomTokenClickIntents, DefaultLifecycleObserver {
|
||||
|
||||
|
|
@ -135,13 +134,25 @@ internal class AddCustomTokenViewModel @Inject constructor(
|
|||
|
||||
private fun selectNetwork(networkItemState: NetworkItemState) {
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
val selectedWalletId = getSelectedWalletSyncUseCase().getOrNull()?.walletId
|
||||
// TODO [REDACTED_TASK_KEY]
|
||||
val selectedWalletId = getSelectedWalletSyncUseCase().getOrNull()?.walletId ?: return@launch
|
||||
val supportsTokens = areTokensSupportedByNetworkUseCase(
|
||||
networkId = networkItemState.id,
|
||||
userWalletId = selectedWalletId,
|
||||
).getOrNull() ?: false
|
||||
|
||||
val networksForDerivations = getSupportedNetworks(selectedWalletId)
|
||||
|
||||
withContext(dispatchers.main) {
|
||||
uiState = stateFactory.updateStateOnNetworkSelected(networkItemState, supportsTokens)
|
||||
uiState = stateFactory.updateStateOnNetworkSelected(
|
||||
networkItemState = networkItemState,
|
||||
supportsTokens = supportsTokens,
|
||||
networks = networksForDerivations,
|
||||
requiresHardenedDerivationOnly = requiresHardenedDerivationOnly(
|
||||
networkId = networkItemState.id,
|
||||
userWalletId = selectedWalletId,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -347,6 +358,8 @@ internal class AddCustomTokenViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onCustomDerivationChange(input: String) {
|
||||
val selectedWallet = getSelectedWalletSyncUseCase().getOrNull() ?: return
|
||||
|
||||
analyticsEventHandler.send(ManageTokens.CustomTokenDerivationSelected(ManageTokens.Derivation.CUSTOM.value))
|
||||
uiState = uiState.copy(
|
||||
chooseDerivationState = uiState.chooseDerivationState?.copy(
|
||||
|
|
@ -356,25 +369,19 @@ internal class AddCustomTokenViewModel @Inject constructor(
|
|||
),
|
||||
)
|
||||
debouncer.debounce(waitMs = DEFAULT_WAIT_TIME_MS, coroutineScope = viewModelScope + dispatchers.io) {
|
||||
val path = createDerivationPathOrNull(input)
|
||||
val enterDerivationState = uiState.chooseDerivationState?.enterCustomDerivationState?.copy(
|
||||
confirmButtonEnabled = path != null,
|
||||
derivationIncorrect = input.isNotBlank() && path == null,
|
||||
)
|
||||
uiState = uiState.copy(
|
||||
chooseDerivationState = uiState.chooseDerivationState?.copy(
|
||||
enterCustomDerivationState = enterDerivationState,
|
||||
),
|
||||
val networkId = uiState.chooseNetworkState.selectedNetwork?.id ?: return@debounce
|
||||
uiState = stateFactory.updateOnCustomDerivationEntered(
|
||||
input = input,
|
||||
requiresHardenedDerivationOnly = requiresHardenedDerivationOnly(networkId, selectedWallet.walletId),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDerivationPathOrNull(rawPath: String): DerivationPath? {
|
||||
return try {
|
||||
DerivationPath(rawPath)
|
||||
} catch (error: HDWalletError) {
|
||||
null
|
||||
}
|
||||
private suspend fun requiresHardenedDerivationOnly(networkId: String, userWalletId: UserWalletId): Boolean {
|
||||
return requiresHardenedDerivationOnlyUseCase.invoke(
|
||||
networkId = networkId,
|
||||
userWalletId = userWalletId,
|
||||
).getOrElse { false }
|
||||
}
|
||||
|
||||
override fun onCustomDerivationSelected() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue