Updated on 2026-08-14

This commit is contained in:
Tangem 2022-05-25 11:19:20 +03:00
commit 97dfc6c840
8 changed files with 127 additions and 82 deletions

View file

@ -93,7 +93,7 @@ dependencies {
implementation 'com.google.android.play:core-ktx:1.8.1'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation 'com.tangem:blockchain:develop-82'
implementation 'com.tangem:blockchain:develop-83'
// implementation 'com.tangem:blockchain:0.0.1'
implementation 'com.tangem.tangem-sdk-kotlin:core:develop-142'
implementation 'com.tangem.tangem-sdk-kotlin:android:develop-142'

View file

@ -59,7 +59,7 @@ dependencies {
implementation implementation(project(path: ':network'))
implementation implementation(project(path: ':common'))
implementation 'com.tangem:blockchain:develop-82'
implementation 'com.tangem:blockchain:develop-83'
// implementation 'com.tangem:blockchain:0.0.1'
implementation 'com.tangem.tangem-sdk-kotlin:core:develop-142'
implementation 'com.tangem.tangem-sdk-kotlin:android:develop-142'

View file

@ -17,7 +17,8 @@ class AddCustomTokenService(
networkId: String? = null,
active: Boolean? = null,
): Result<List<CoinsResponse.Coin>> {
val result = tangemTechService.coins(contractAddress, networkId, active)
val networksIds = selectNetworksForSearch(networkId)
val result = tangemTechService.coins(contractAddress, networksIds, active)
return when (result) {
is Result.Success -> {
var coinsList = mutableListOf<CoinsResponse.Coin>()
@ -40,4 +41,9 @@ class AddCustomTokenService(
is Result.Failure -> result
}
}
private fun selectNetworksForSearch(networkId: String?): String {
return networkId ?: supportedTokenNetworkIds.joinToString(",")
}
}

View file

@ -20,7 +20,11 @@ import com.tangem.domain.common.form.*
import com.tangem.domain.features.addCustomToken.*
import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.*
import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.*
import com.tangem.domain.redux.*
import com.tangem.domain.redux.BaseStoreHub
import com.tangem.domain.redux.DomainState
import com.tangem.domain.redux.ReStoreReducer
import com.tangem.domain.redux.domainStore
import com.tangem.domain.redux.extensions.dispatchOnMain
import com.tangem.domain.redux.global.DomainGlobalAction
import com.tangem.domain.redux.global.DomainGlobalState
import com.tangem.network.api.tangemTech.CoinsResponse
@ -105,14 +109,14 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
null -> {
// valid contract address
ContractAddress.removeError()
changeBlockchainNetworkList()
reassembleBlockchainNetworkList()
findTokenAndUpdateFields(contractAddress)
}
AddCustomTokenError.InvalidContractAddress -> {
// invalid contract address
ContractAddress.addError(error)
updateTokenDetailFields(hubState.tokensAnyFieldsIsFilled())
changeBlockchainNetworkList()
reassembleBlockchainNetworkList()
checkAndUpdateAddButton()
}
AddCustomTokenError.FieldIsEmpty -> {
@ -120,7 +124,7 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
ContractAddress.removeError()
clearTokenDetailsFields()
updateTokenDetailFields(false)
changeBlockchainNetworkList()
reassembleBlockchainNetworkList()
checkAndUpdateAddButton()
}
}
@ -129,21 +133,22 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
null -> {
// valid contract address
ContractAddress.removeError()
changeBlockchainNetworkList()
reassembleBlockchainNetworkList()
findTokenAndUpdateFields(contractAddress)
}
else -> {
ContractAddress.removeError()
clearTokenDetailsFields()
updateTokenDetailFields(false)
changeBlockchainNetworkList()
reassembleBlockchainNetworkList()
checkAndUpdateAddButton()
}
}
}
updateDerivationPath(Network.getFieldValue())
}
private suspend fun changeBlockchainNetworkList() {
private suspend fun reassembleBlockchainNetworkList() {
val state = hubState
val networkBlockchainList = Network.getField<TokenBlockchainField>().itemList
val card = requireNotNull(globalState.scanResponse?.card)
@ -159,13 +164,12 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
val listSameSize = networkBlockchainList.size == newNetworkBlockchainList.size
val newListLessThanOld = networkBlockchainList.size > newNetworkBlockchainList.size
if (listSameSize || newListLessThanOld) {
// check selected blockchain
// check selected blockchain
val selectedBlockchain = Network.getFieldValue<Blockchain>()
val defaultSelection = Blockchain.Unknown
if (!newNetworkBlockchainList.contains(selectedBlockchain)
&& newNetworkBlockchainList.contains(defaultSelection)
) {
// selectedBlockchain not present in the new list. Change selection to default
&& newNetworkBlockchainList.contains(defaultSelection)) {
// selectedBlockchain not present in the new list. Change selection to default
newNetworkField.data = Field.Data(defaultSelection, false)
}
}
@ -175,6 +179,31 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
dispatchOnMain(UpdateForm(hubState))
}
private suspend fun updateDerivationPath(blockchainNetwork: Blockchain) {
val state = hubState
val derivationIsSupportedByNetwork = blockchainNetwork.isEvm() || blockchainNetwork == Blockchain.Unknown
if (DerivationPath.isFilled() && !derivationIsSupportedByNetwork) {
// reset to default
val derivationField = DerivationPath.getField<TokenDerivationPathField>()
derivationField.data = derivationField.data.copy(
value = Blockchain.Unknown,
isUserInput = false
)
state.setField(derivationField)
dispatchOnMain(UpdateForm(hubState))
}
if (state.screenState.derivationPath.isEnabled != derivationIsSupportedByNetwork) {
val action = Screen.UpdateTokenFields(listOf(
DerivationPath to state.screenState.derivationPath.copy(
isEnabled = derivationIsSupportedByNetwork,
),
))
dispatchOnMain(action)
}
}
private suspend fun requestInfoAboutToken(
contractAddress: String,
): List<CoinsResponse.Coin> {
@ -382,6 +411,58 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
derivationStyle = hubState.cardDerivationStyle
)
private suspend fun fillTokenFields(
token: CoinsResponse.Coin,
coinNetwork: CoinsResponse.Coin.Network,
) {
val blockchain = Blockchain.fromNetworkId(coinNetwork.networkId) ?: Blockchain.Unknown
Network.setFieldValue(Field.Data(blockchain, false))
Name.setFieldValue(Field.Data(token.name, false))
Symbol.setFieldValue(Field.Data(token.symbol, false))
Decimals.setFieldValue(Field.Data(coinNetwork.decimalCount.toString(), false))
dispatchOnMain(UpdateForm(hubState))
}
private suspend fun clearTokenDetailsFields() {
Name.setFieldValue(Field.Data("", false))
Symbol.setFieldValue(Field.Data("", false))
Decimals.setFieldValue(Field.Data("", false))
dispatchOnMain(UpdateForm(hubState))
}
private suspend fun updateTokenDetailFields(isEnabled: Boolean = true) {
val state = hubState
val action = Screen.UpdateTokenFields(listOf(
Name to state.screenState.name.copy(isEnabled = isEnabled),
Symbol to state.screenState.symbol.copy(isEnabled = isEnabled),
Decimals to state.screenState.decimals.copy(isEnabled = isEnabled),
))
dispatchOnMain(action)
}
private suspend fun updateBlockchainNetworkField(isEnabled: Boolean) {
val action = Screen.UpdateTokenFields(listOf(
Name to hubState.screenState.name.copy(isEnabled = isEnabled),
))
dispatchOnMain(action)
}
private suspend fun updateAddButton(isEnabled: Boolean) {
dispatchOnMain(Screen.UpdateAddButton(ViewStates.AddButton(isEnabled)))
}
private fun canHandleToken(blockchain: Blockchain): Boolean {
val card = globalState.scanResponse?.card ?: return false
return card.canHandleToken(blockchain)
}
@Throws
private fun throwUnAppropriateInitialization(objName: String) {
throw AddCustomTokenException.UnAppropriateInitializationException(
"AddCustomTokenHub", "$objName must be not NULL"
)
}
private suspend fun CustomTokenFieldId.addError(error: AddCustomTokenError) {
dispatchOnMain(FieldError.Add(this, error))
}
@ -469,46 +550,6 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
return error
}
private suspend fun fillTokenFields(
token: CoinsResponse.Coin,
coinNetwork: CoinsResponse.Coin.Network,
) {
val blockchain = Blockchain.fromNetworkId(coinNetwork.networkId) ?: Blockchain.Unknown
Network.setFieldValue(Field.Data(blockchain, false))
Name.setFieldValue(Field.Data(token.name, false))
Symbol.setFieldValue(Field.Data(token.symbol, false))
Decimals.setFieldValue(Field.Data(coinNetwork.decimalCount.toString(), false))
dispatchOnMain(UpdateForm(hubState))
}
private suspend fun clearTokenDetailsFields() {
Name.setFieldValue(Field.Data("", false))
Symbol.setFieldValue(Field.Data("", false))
Decimals.setFieldValue(Field.Data("", false))
dispatchOnMain(UpdateForm(hubState))
}
private suspend fun updateTokenDetailFields(isEnabled: Boolean = true) {
val state = hubState
val action = Screen.UpdateTokenFields(listOf(
Name to state.screenState.name.copy(isEnabled = isEnabled),
Symbol to state.screenState.symbol.copy(isEnabled = isEnabled),
Decimals to state.screenState.decimals.copy(isEnabled = isEnabled),
))
dispatchOnMain(action)
}
private suspend fun updateBlockchainNetworkField(isEnabled: Boolean) {
val action = Screen.UpdateTokenFields(listOf(
Name to hubState.screenState.name.copy(isEnabled = isEnabled),
))
dispatchOnMain(action)
}
private suspend fun updateAddButton(isEnabled: Boolean) {
dispatchOnMain(Screen.UpdateAddButton(ViewStates.AddButton(isEnabled)))
}
private suspend fun AddCustomTokenError.Warning.add() {
dispatchOnMain(Warning.Add(setOf(this)))
}
@ -520,18 +561,6 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
private suspend fun AddCustomTokenError.Warning.replaceBy(to: AddCustomTokenError.Warning) {
dispatchOnMain(Warning.Replace(setOf(this), setOf(to)))
}
private fun canHandleToken(blockchain: Blockchain): Boolean {
val card = globalState.scanResponse?.card ?: return false
return card.canHandleToken(blockchain)
}
@Throws
private fun throwUnAppropriateInitialization(objName: String) {
throw AddCustomTokenException.UnAppropriateInitializationException(
"AddCustomTokenHub", "$objName must be not NULL"
)
}
}
private class AddCustomTokenReducer(
@ -671,6 +700,13 @@ private class AddCustomTokenReducer(
newScreenState.copy(decimals = it.second)
}
}
DerivationPath -> {
if (state.screenState.derivationPath == it.second) {
newScreenState
} else {
newScreenState.copy(derivationPath = it.second)
}
}
else -> newScreenState
}
}

View file

@ -191,7 +191,7 @@ data class AddCustomTokenState(
val default = Blockchain.Unknown
typedNetworksList.add(0, default)
return typedNetworksList
return typedNetworksList.sortByName()
}
private fun createFormValidators(): Map<CustomTokenFieldId, CustomTokenValidator<out Any>> {
@ -206,9 +206,9 @@ data class AddCustomTokenState(
private fun getSupportedDerivations(card: Card): List<Blockchain> {
val evmBlockchains = Blockchain.values().filter {
card.isTestCard == it.isTestnet() && it.getChainId() != null
card.isTestCard == it.isTestnet() && it.isEvm()
}
return listOf(Blockchain.Unknown) + evmBlockchains
return (listOf(Blockchain.Unknown) + evmBlockchains).sortByName()
}
private fun createInitialScreenState(): ScreenState {
@ -277,6 +277,8 @@ data class AddCustomTokenState(
}
}
private fun List<Blockchain>.sortByName(): List<Blockchain> = this.sortedBy { it.fullName }
enum class CustomTokenType {
Token, Blockchain
}

View file

@ -2,8 +2,6 @@ package com.tangem.domain.redux
import android.webkit.ValueCallback
import com.tangem.domain.common.FeatureCoroutineExceptionHandler
import com.tangem.domain.common.extensions.withIOContext
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.redux.global.DomainGlobalState
import kotlinx.coroutines.*
import org.rekotlin.Action
@ -105,13 +103,4 @@ internal abstract class BaseStoreHub<State>(
protected abstract fun getHubState(storeState: DomainState): State
protected abstract fun updateStoreState(storeState: DomainState, newHubState: State): DomainState
}
internal suspend inline fun ReStoreHub<*, *>.dispatchOnMain(vararg actions: Action) {
withMainContext { actions.forEach { domainStore.dispatch(it) } }
}
internal suspend inline fun ReStoreHub<*, *>.dispatchOnIO(vararg actions: Action) {
withIOContext { actions.forEach { domainStore.dispatch(it) } }
}
}

View file

@ -0,0 +1,12 @@
package com.tangem.domain.redux.extensions
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.redux.domainStore
import org.rekotlin.Action
/**
[REDACTED_AUTHOR]
*/
internal suspend inline fun dispatchOnMain(vararg actions: Action) {
withMainContext { actions.forEach { domainStore.dispatch(it) } }
}

View file

@ -11,7 +11,7 @@ interface TangemTechApi {
@GET("coins")
suspend fun coins(
@Query("contractAddress") contractAddress: String? = null,
@Query("networkId") networkId: String? = null,
@Query("networkIds") networkId: String? = null,
@Query("active") active: Boolean? = null,
): CoinsResponse