Updated on 2026-08-14
This commit is contained in:
parent
1675b44cd3
commit
da0318964e
25 changed files with 186 additions and 212 deletions
|
|
@ -2,8 +2,8 @@ package com.tangem.tap.domain.tokens
|
|||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.domain.common.extensions.fromNetworkId
|
||||
import com.tangem.datasource.api.tangemTech.CoinsResponse
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class CurrencyFromJson(
|
||||
|
|
@ -26,7 +26,6 @@ data class CurrenciesFromJson(
|
|||
val coins: List<CurrencyFromJson>
|
||||
)
|
||||
|
||||
|
||||
fun List<ContractFromJson>.toContracts(): List<Contract> {
|
||||
return mapNotNull { Contract.fromJsonObject(it) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,15 +4,17 @@ import com.squareup.moshi.JsonAdapter
|
|||
import com.squareup.moshi.Moshi
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.domain.common.extensions.getListOfCoins
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.datasource.api.tangemTech.CoinsResponse
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechService
|
||||
import com.tangem.datasource.api.common.MoshiConverter
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.tap.common.AssetReader
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class LoadAvailableCoinsService(
|
||||
private val networkService: TangemTechService,
|
||||
private val tangemTechApi: TangemTechApi,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val assetReader: AssetReader,
|
||||
) {
|
||||
private val moshi: Moshi by lazy { MoshiConverter.defaultMoshi() }
|
||||
|
|
@ -55,16 +57,23 @@ class LoadAvailableCoinsService(
|
|||
private suspend fun loadCoins(
|
||||
supportedBlockchains: List<Blockchain>,
|
||||
offset: Int,
|
||||
searchInput: String? = null
|
||||
searchInput: String? = null,
|
||||
): Result<CoinsResponse> {
|
||||
val networkIds = supportedBlockchains.toSet().map { it.toNetworkId() }
|
||||
return networkService.getListOfCoins(
|
||||
networkIds = networkIds,
|
||||
active = true,
|
||||
offset = offset,
|
||||
limit = LOAD_PER_PAGE,
|
||||
searchText = searchInput,
|
||||
)
|
||||
return withContext(dispatchers.io) {
|
||||
runCatching {
|
||||
tangemTechApi.getCoins(
|
||||
networkIds = supportedBlockchains.toSet().map(Blockchain::toNetworkId).joinToString(","),
|
||||
active = true,
|
||||
searchText = searchInput,
|
||||
offset = offset,
|
||||
limit = LOAD_PER_PAGE,
|
||||
)
|
||||
}
|
||||
.onSuccess { return@withContext Result.Success(it) }
|
||||
.onFailure { return@withContext Result.Failure(it) }
|
||||
|
||||
throw IllegalStateException("Unreachable code because runCatching must return result")
|
||||
}
|
||||
}
|
||||
|
||||
fun getTestnetCoins(): List<Currency> {
|
||||
|
|
@ -88,7 +97,6 @@ class LoadAvailableCoinsService(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
data class LoadedCoins(
|
||||
val currencies: List<Currency>,
|
||||
val moreAvailable: Boolean,
|
||||
|
|
|
|||
|
|
@ -4,21 +4,21 @@ import com.squareup.moshi.JsonAdapter
|
|||
import com.squareup.moshi.Types
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.DerivationStyle
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.domain.common.extensions.getTokens
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechService
|
||||
import com.tangem.datasource.api.common.MoshiConverter
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.tap.common.FileReader
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.domain.tokens.models.TokenDao
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Deprecated("Use this only for migration")
|
||||
class OldUserTokensRepository(
|
||||
private val fileReader: FileReader,
|
||||
private val tangemNetworkService: TangemTechService,
|
||||
private val tangemTechApi: TangemTechApi,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
private val moshi = MoshiConverter.defaultMoshi()
|
||||
private val blockchainsAdapter: JsonAdapter<List<Blockchain>> = moshi.adapter(
|
||||
|
|
@ -104,20 +104,27 @@ class OldUserTokensRepository(
|
|||
return blockchainNetworks
|
||||
}
|
||||
|
||||
private suspend fun getTokensIds(tokens: List<TokenDao>): Map<String, String> = coroutineScope {
|
||||
private suspend fun getTokensIds(tokens: List<TokenDao>): Map<String, String> = withContext(dispatchers.io) {
|
||||
tokens.map {
|
||||
async {
|
||||
tangemNetworkService.getTokens(
|
||||
tangemTechApi.getCoins(
|
||||
contractAddress = it.contractAddress,
|
||||
networkId = it.blockchainDao.toBlockchain().toNetworkId(),
|
||||
networkIds = it.blockchainDao.toBlockchain().toNetworkId(),
|
||||
active = true,
|
||||
)
|
||||
}
|
||||
}.map { it.await() }
|
||||
.map { (it as? Result.Success)?.data?.coins?.firstOrNull()?.id }
|
||||
}
|
||||
.map {
|
||||
runCatching { it.await() }
|
||||
.onSuccess { return@map it.coins.firstOrNull()?.id }
|
||||
.onFailure { return@map null }
|
||||
|
||||
throw IllegalStateException("Unreachable code because runCatching must return result")
|
||||
}
|
||||
.mapIndexedNotNull { index, id ->
|
||||
if (id == null) null else tokens[index].contractAddress to id
|
||||
}.toMap()
|
||||
}
|
||||
.toMap()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import com.tangem.tap.features.wallet.models.Currency
|
|||
import com.tangem.tap.features.wallet.models.toBlockchainNetworks
|
||||
import com.tangem.tap.features.wallet.models.toCurrencies
|
||||
import com.tangem.tap.network.NetworkConnectivity
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
@ -120,9 +119,12 @@ class UserTokensRepository(
|
|||
// TODO("After adding DI") get dependencies by DI
|
||||
fun init(context: Context, tangemTechService: TangemTechService): UserTokensRepository {
|
||||
val fileReader = AndroidFileReader(context)
|
||||
val dispatchers = AppCoroutineDispatcherProvider()
|
||||
|
||||
val oldUserTokensRepository = OldUserTokensRepository(
|
||||
fileReader = fileReader,
|
||||
tangemNetworkService = store.state.domainNetworks.tangemTechService,
|
||||
tangemTechApi = tangemTechService.api,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
val storageService = UserTokensStorageService(
|
||||
oldUserTokensRepository = oldUserTokensRepository,
|
||||
|
|
@ -132,7 +134,7 @@ class UserTokensRepository(
|
|||
return UserTokensRepository(
|
||||
storageService = storageService,
|
||||
tangemTechApi = tangemTechService.api,
|
||||
dispatchers = AppCoroutineDispatcherProvider(),
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
package com.tangem.tap.domain.tokens.converters
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.models.TokenBody
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
/** Converter from domain model [Currency] to data model [TokenBody] */
|
||||
object CurrencyConverter : Converter<Currency, TokenBody> {
|
||||
/** Converter from domain model [Currency] to data model [UserTokensResponse.Token] */
|
||||
object CurrencyConverter : Converter<Currency, UserTokensResponse.Token> {
|
||||
|
||||
override fun convert(value: Currency) = TokenBody(
|
||||
override fun convert(value: Currency) = UserTokensResponse.Token(
|
||||
id = value.coinId,
|
||||
networkId = value.blockchain.toNetworkId(),
|
||||
derivationPath = value.derivationPath,
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ import com.tangem.tap.store
|
|||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import com.tangem.tap.walletCurrenciesManager
|
||||
import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Middleware
|
||||
|
|
@ -83,8 +84,9 @@ class TokensMiddleware {
|
|||
.filter { !it.isTestnet() }
|
||||
|
||||
val loadCoinsService = LoadAvailableCoinsService(
|
||||
store.state.domainNetworks.tangemTechService,
|
||||
assetReader,
|
||||
tangemTechApi = store.state.domainNetworks.tangemTechService.api,
|
||||
dispatchers = AppCoroutineDispatcherProvider(),
|
||||
assetReader = assetReader,
|
||||
)
|
||||
|
||||
scope.launch {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.tap.features.wallet.models
|
||||
|
||||
import com.tangem.blockchain.common.DerivationStyle
|
||||
import com.tangem.datasource.api.tangemTech.models.TokenBody
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.domain.common.extensions.fromNetworkId
|
||||
import com.tangem.domain.common.extensions.toCoinId
|
||||
import com.tangem.domain.features.addCustomToken.CustomCurrency
|
||||
|
|
@ -98,7 +98,7 @@ sealed interface Currency {
|
|||
)
|
||||
}
|
||||
|
||||
fun fromTokenResponse(tokenBody: TokenBody): Currency? {
|
||||
fun fromTokenResponse(tokenBody: UserTokensResponse.Token): Currency? {
|
||||
val blockchain = com.tangem.blockchain.common.Blockchain.fromNetworkId(tokenBody.networkId)
|
||||
?: return null
|
||||
return when {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.tap.features.wallet.redux.middlewares
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.models.Currency
|
||||
import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse
|
||||
import com.tangem.tap.common.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.MainScreen
|
||||
|
|
@ -38,7 +38,7 @@ class AppCurrencyMiddleware(
|
|||
store.dispatchDialogShow(
|
||||
WalletDialog.CurrencySelectionDialog(
|
||||
currenciesList = storedFiatCurrencies.mapToUiModel(),
|
||||
currentAppCurrency = appCurrencyProvider.invoke()
|
||||
currentAppCurrency = appCurrencyProvider.invoke(),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ class AppCurrencyMiddleware(
|
|||
store.dispatchDialogShow(
|
||||
WalletDialog.CurrencySelectionDialog(
|
||||
currenciesList = currenciesList.mapToUiModel(),
|
||||
currentAppCurrency = appCurrencyProvider.invoke()
|
||||
currentAppCurrency = appCurrencyProvider.invoke(),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -77,7 +77,7 @@ class AppCurrencyMiddleware(
|
|||
}
|
||||
}
|
||||
|
||||
private fun List<Currency>.mapToUiModel(): List<FiatCurrency> {
|
||||
private fun List<CurrenciesResponse.Currency>.mapToUiModel(): List<FiatCurrency> {
|
||||
return this.map {
|
||||
FiatCurrency(
|
||||
code = it.code,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.tap.persistence
|
|||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import com.tangem.datasource.api.tangemTech.models.Currency
|
||||
import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
|
||||
/**
|
||||
|
|
@ -32,14 +32,14 @@ class FiatCurrenciesPrefStorage(
|
|||
preferences.edit { putString(APP_CURRENCY_KEY, json) }
|
||||
}
|
||||
|
||||
fun save(currencies: List<Currency>) {
|
||||
fun save(currencies: List<CurrenciesResponse.Currency>) {
|
||||
val json: String = converter.toJson(currencies)
|
||||
return preferences.edit().putString(FIAT_CURRENCIES_KEY, json).apply()
|
||||
}
|
||||
|
||||
fun restore(): List<Currency> {
|
||||
fun restore(): List<CurrenciesResponse.Currency> {
|
||||
val json = preferences.getString(FIAT_CURRENCIES_KEY, "")
|
||||
val type = converter.typedList(Currency::class.java)
|
||||
val type = converter.typedList(CurrenciesResponse.Currency::class.java)
|
||||
if (json.isNullOrBlank()) return emptyList()
|
||||
|
||||
return converter.fromJson(json, type) ?: emptyList()
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
package com.tangem.datasource.api.tangemTech
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface HttpResponse
|
||||
sealed interface TangemTechResponse : HttpResponse
|
||||
|
||||
data class CoinsResponse(
|
||||
val imageHost: String?,
|
||||
val coins: List<Coin>,
|
||||
val total: Int,
|
||||
) : TangemTechResponse {
|
||||
|
||||
data class Coin(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val active: Boolean,
|
||||
val networks: List<Network> = listOf(),
|
||||
) : TangemTechResponse {
|
||||
|
||||
data class Network(
|
||||
val networkId: String,
|
||||
val contractAddress: String? = null,
|
||||
val decimalCount: BigDecimal? = null,
|
||||
val exchangeable: Boolean? = false,
|
||||
) : TangemTechResponse
|
||||
}
|
||||
}
|
||||
|
||||
//rates.keys = networkId's
|
||||
data class RatesResponse(val rates: Map<String, Double>) : TangemTechResponse
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.datasource.api.tangemTech
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.GeoResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.RatesResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
|
|
@ -10,12 +12,14 @@ import retrofit2.http.Path
|
|||
import retrofit2.http.Query
|
||||
|
||||
/**
|
||||
* Interface of Tangem Tech API
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface TangemTechApi {
|
||||
|
||||
@GET("coins")
|
||||
suspend fun coins(
|
||||
suspend fun getCoins(
|
||||
@Query("contractAddress") contractAddress: String? = null,
|
||||
@Query("exchangeable") exchangeable: Boolean? = null,
|
||||
@Query("networkIds") networkIds: String? = null,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
package com.tangem.datasource.api.tangemTech
|
||||
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.common.services.performRequest
|
||||
import com.tangem.datasource.api.common.AddHeaderInterceptor
|
||||
import com.tangem.datasource.api.common.CacheControlHttpInterceptor
|
||||
import com.tangem.datasource.api.common.createRetrofitInstance
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -21,26 +17,6 @@ class TangemTechService(
|
|||
|
||||
var api: TangemTechApi = createApi()
|
||||
|
||||
suspend fun coins(
|
||||
contractAddress: String? = null,
|
||||
networkIds: String? = null,
|
||||
active: Boolean? = null,
|
||||
searchText: String? = null,
|
||||
offset: Int? = null,
|
||||
limit: Int? = null,
|
||||
): Result<CoinsResponse> = withContext(Dispatchers.IO) {
|
||||
performRequest {
|
||||
api.coins(
|
||||
contractAddress = contractAddress,
|
||||
networkIds = networkIds,
|
||||
active = active,
|
||||
searchText = searchText,
|
||||
offset = offset,
|
||||
limit = limit,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun addHeaderInterceptors(interceptors: List<AddHeaderInterceptor>) {
|
||||
headerInterceptors.removeAll(interceptors)
|
||||
headerInterceptors.addAll(interceptors)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tangem.datasource.api.tangemTech.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class CoinsResponse(
|
||||
@Json(name = "imageHost") val imageHost: String?,
|
||||
@Json(name = "coins") val coins: List<Coin>,
|
||||
@Json(name = "total") val total: Int,
|
||||
) {
|
||||
|
||||
data class Coin(
|
||||
@Json(name = "id") val id: String,
|
||||
@Json(name = "name") val name: String,
|
||||
@Json(name = "symbol") val symbol: String,
|
||||
@Json(name = "active") val active: Boolean,
|
||||
@Json(name = "networks") val networks: List<Network> = listOf(),
|
||||
) {
|
||||
|
||||
data class Network(
|
||||
@Json(name = "networkId") val networkId: String,
|
||||
@Json(name = "contractAddress") val contractAddress: String? = null,
|
||||
@Json(name = "decimalCount") val decimalCount: BigDecimal? = null,
|
||||
@Json(name = "exchangeable") val exchangeable: Boolean? = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,14 +3,15 @@ package com.tangem.datasource.api.tangemTech.models
|
|||
import com.squareup.moshi.Json
|
||||
|
||||
data class CurrenciesResponse(
|
||||
@Json(name = "currencies") val currencies: List<Currency>
|
||||
)
|
||||
@Json(name = "currencies") val currencies: List<Currency>,
|
||||
) {
|
||||
|
||||
data class Currency(
|
||||
@Json(name = "id") val id: String,
|
||||
@Json(name = "code") val code: String, // this is an uppercase id
|
||||
@Json(name = "name") val name: String,
|
||||
@Json(name = "rateBTC") val rateBTC: String,
|
||||
@Json(name = "unit") val unit: String, // $, €, ₽
|
||||
@Json(name = "type") val type: String
|
||||
)
|
||||
data class Currency(
|
||||
@Json(name = "id") val id: String,
|
||||
@Json(name = "code") val code: String, // this is an uppercase id
|
||||
@Json(name = "name") val name: String,
|
||||
@Json(name = "rateBTC") val rateBTC: String,
|
||||
@Json(name = "unit") val unit: String, // $, €, ₽
|
||||
@Json(name = "type") val type: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.datasource.api.tangemTech.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
//rates.keys = networkId's
|
||||
data class RatesResponse(
|
||||
@Json(name = "rates") val rates: Map<String, Double>,
|
||||
)
|
||||
|
|
@ -6,15 +6,16 @@ data class UserTokensResponse(
|
|||
@Json(name = "version") val version: Int = 0,
|
||||
@Json(name = "group") val group: String? = null,
|
||||
@Json(name = "sort") val sort: String? = null,
|
||||
@Json(name = "tokens") val tokens: List<TokenBody> = emptyList(),
|
||||
)
|
||||
@Json(name = "tokens") val tokens: List<Token> = emptyList(),
|
||||
) {
|
||||
|
||||
data class TokenBody(
|
||||
@Json(name = "id") val id: String? = null,
|
||||
@Json(name = "networkId") val networkId: String,
|
||||
@Json(name = "derivationPath") val derivationPath: String? = null,
|
||||
@Json(name = "name") val name: String,
|
||||
@Json(name = "symbol") val symbol: String,
|
||||
@Json(name = "decimals") val decimals: Int,
|
||||
@Json(name = "contractAddress") val contractAddress: String?,
|
||||
)
|
||||
data class Token(
|
||||
@Json(name = "id") val id: String? = null,
|
||||
@Json(name = "networkId") val networkId: String,
|
||||
@Json(name = "derivationPath") val derivationPath: String? = null,
|
||||
@Json(name = "name") val name: String,
|
||||
@Json(name = "symbol") val symbol: String,
|
||||
@Json(name = "decimals") val decimals: Int,
|
||||
@Json(name = "contractAddress") val contractAddress: String?,
|
||||
)
|
||||
}
|
||||
|
|
@ -72,6 +72,7 @@ android {
|
|||
|
||||
dependencies {
|
||||
implementation(project(":core:datasource"))
|
||||
implementation(project(":core:utils"))
|
||||
implementation(project(":common"))
|
||||
|
||||
/** Tangem libraries */
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.domain
|
||||
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.datasource.api.tangemTech.CoinsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -14,6 +14,6 @@ sealed interface DomainDialog {
|
|||
val items: List<CoinsResponse.Coin.Network>,
|
||||
val networkIdConverter: (String) -> String,
|
||||
val onSelect: (CoinsResponse.Coin.Network) -> Unit,
|
||||
val onClose: VoidCallback = {}
|
||||
val onClose: VoidCallback = {},
|
||||
) : DomainDialog
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package com.tangem.domain.common.extensions
|
||||
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.datasource.api.tangemTech.CoinsResponse
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechService
|
||||
|
||||
suspend fun TangemTechService.getTokens(
|
||||
contractAddress: String,
|
||||
networkId: String? = null,
|
||||
active: Boolean? = null,
|
||||
): Result<CoinsResponse> = coins(
|
||||
contractAddress = contractAddress,
|
||||
networkIds = networkId,
|
||||
active = active
|
||||
)
|
||||
|
||||
suspend fun TangemTechService.getListOfCoins(
|
||||
networkIds: List<String>,
|
||||
active: Boolean? = null,
|
||||
searchText: String? = null,
|
||||
offset: Int? = null,
|
||||
limit: Int? = null
|
||||
): Result<CoinsResponse> = coins(
|
||||
networkIds = networkIds.joinToString(","),
|
||||
active = active,
|
||||
searchText = searchText,
|
||||
offset = offset,
|
||||
limit = limit
|
||||
)
|
||||
|
|
@ -1,15 +1,16 @@
|
|||
package com.tangem.domain.features.addCustomToken
|
||||
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.domain.common.extensions.getTokens
|
||||
import com.tangem.datasource.api.tangemTech.CoinsResponse
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechService
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class AddCustomTokenService(
|
||||
private val tangemTechService: TangemTechService,
|
||||
private val tangemTechApi: TangemTechApi,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val supportedTokenNetworkIds: List<String>,
|
||||
) {
|
||||
|
||||
|
|
@ -17,13 +18,17 @@ class AddCustomTokenService(
|
|||
contractAddress: String,
|
||||
networkId: String? = null,
|
||||
active: Boolean? = null,
|
||||
): Result<List<CoinsResponse.Coin>> {
|
||||
val networksIds = selectNetworksForSearch(networkId)
|
||||
val result = tangemTechService.getTokens(contractAddress, networksIds, active)
|
||||
return when (result) {
|
||||
is Result.Success -> {
|
||||
): List<CoinsResponse.Coin> = withContext(dispatchers.io) {
|
||||
runCatching {
|
||||
tangemTechApi.getCoins(
|
||||
contractAddress = contractAddress,
|
||||
networkIds = selectNetworksForSearch(networkId),
|
||||
active = active,
|
||||
)
|
||||
}
|
||||
.onSuccess { response ->
|
||||
var coinsList = mutableListOf<CoinsResponse.Coin>()
|
||||
result.data.coins.forEach { coin ->
|
||||
response.coins.forEach { coin ->
|
||||
val networksWithTheSameAddress = coin.networks
|
||||
.filter { it.contractAddress != null || it.decimalCount != null }
|
||||
.filter { it.contractAddress?.equals(contractAddress, ignoreCase = true) == true }
|
||||
|
|
@ -37,10 +42,13 @@ class AddCustomTokenService(
|
|||
// https://tangem.slack.com/archives/GMXC6PP71/p1649672562078679
|
||||
coinsList = mutableListOf(coinsList[0])
|
||||
}
|
||||
Result.Success(coinsList)
|
||||
return@withContext coinsList
|
||||
}
|
||||
is Result.Failure -> result
|
||||
}
|
||||
.onFailure {
|
||||
return@withContext emptyList()
|
||||
}
|
||||
|
||||
throw IllegalStateException("Unreachable code because runCatching must return result")
|
||||
}
|
||||
|
||||
private fun selectNetworksForSearch(networkId: String?): String {
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ package com.tangem.domain.features.addCustomToken.redux
|
|||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.DerivationStyle
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.domain.AddCustomTokenError
|
||||
import com.tangem.domain.DomainWrapped
|
||||
import com.tangem.domain.common.form.Field
|
||||
import com.tangem.domain.common.form.FieldId
|
||||
import com.tangem.domain.features.addCustomToken.CustomCurrency
|
||||
import com.tangem.domain.features.addCustomToken.CustomTokenFieldId
|
||||
import com.tangem.datasource.api.tangemTech.CoinsResponse
|
||||
import org.rekotlin.Action
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.domain.features.addCustomToken.redux
|
|||
import android.webkit.ValueCallback
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.common.services.Result
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.domain.AddCustomTokenError
|
||||
import com.tangem.domain.AddCustomTokenError.Warning.PotentialScamToken
|
||||
import com.tangem.domain.AddCustomTokenError.Warning.TokenAlreadyAdded
|
||||
|
|
@ -56,7 +56,7 @@ 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.datasource.api.tangemTech.CoinsResponse
|
||||
import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -232,11 +232,13 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
|
|||
}
|
||||
|
||||
if (state.screenState.derivationPath.isEnabled != derivationIsSupportedByNetwork) {
|
||||
val action = Screen.UpdateTokenFields(listOf(
|
||||
DerivationPath to state.screenState.derivationPath.copy(
|
||||
isEnabled = derivationIsSupportedByNetwork,
|
||||
val action = Screen.UpdateTokenFields(
|
||||
listOf(
|
||||
DerivationPath to state.screenState.derivationPath.copy(
|
||||
isEnabled = derivationIsSupportedByNetwork,
|
||||
),
|
||||
),
|
||||
))
|
||||
)
|
||||
dispatchOnMain(action)
|
||||
}
|
||||
}
|
||||
|
|
@ -297,10 +299,12 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
|
|||
}
|
||||
}
|
||||
|
||||
dispatchOnMain(Warning.Replace(
|
||||
remove = warningsRemove,
|
||||
add = warningsAdd,
|
||||
))
|
||||
dispatchOnMain(
|
||||
Warning.Replace(
|
||||
remove = warningsRemove,
|
||||
add = warningsAdd,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun updateAddButton() {
|
||||
|
|
@ -340,9 +344,7 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun requestInfoAboutToken(
|
||||
contractAddress: String,
|
||||
): List<CoinsResponse.Coin> {
|
||||
private suspend fun requestInfoAboutToken(contractAddress: String): List<CoinsResponse.Coin> {
|
||||
val tangemTechServiceManager = requireNotNull(hubState.tangemTechServiceManager)
|
||||
dispatchOnMain(Screen.UpdateTokenFields(listOf(ContractAddress to ViewStates.TokenField(isLoading = true))))
|
||||
|
||||
|
|
@ -355,15 +357,8 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
|
|||
// got the result faster than 500ms and the delay would only be the difference between them.
|
||||
delay(500)
|
||||
|
||||
val foundTokensResult = tangemTechServiceManager.findToken(contractAddress, selectedNetworkId)
|
||||
val result = when (foundTokensResult) {
|
||||
is Result.Success -> foundTokensResult.data
|
||||
is Result.Failure -> {
|
||||
// val warning = Warning.Network.CheckAddressRequestError
|
||||
// dispatchOnMain(Warning.Add(setOf(warning)))
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
val result = tangemTechServiceManager.findToken(contractAddress, selectedNetworkId)
|
||||
|
||||
dispatchOnMain(Screen.UpdateTokenFields(listOf(ContractAddress to ViewStates.TokenField(isLoading = false))))
|
||||
return result
|
||||
}
|
||||
|
|
@ -431,10 +426,7 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
|
|||
derivationStyle = hubState.cardDerivationStyle
|
||||
)
|
||||
|
||||
private suspend fun fillTokenFields(
|
||||
token: CoinsResponse.Coin,
|
||||
coinNetwork: CoinsResponse.Coin.Network,
|
||||
) {
|
||||
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))
|
||||
|
|
@ -460,11 +452,13 @@ internal class AddCustomTokenHub : BaseStoreHub<AddCustomTokenState>("AddCustomT
|
|||
|
||||
private suspend fun enableDisableTokenDetailFields(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),
|
||||
))
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
@ -597,8 +591,9 @@ private class AddCustomTokenReducer(
|
|||
.filter { it.canHandleTokens() }
|
||||
.map { it.toNetworkId() }
|
||||
val tangemTechServiceManager = AddCustomTokenService(
|
||||
tangemTechService = globalState.networkServices.tangemTechService,
|
||||
supportedTokenNetworkIds = supportedTokenNetworkIds
|
||||
tangemTechApi = globalState.networkServices.tangemTechService.api,
|
||||
dispatchers = AppCoroutineDispatcherProvider(),
|
||||
supportedTokenNetworkIds = supportedTokenNetworkIds,
|
||||
)
|
||||
val form = Form(AddCustomTokenState.createFormFields(card, CustomTokenType.Blockchain))
|
||||
state.copy(
|
||||
|
|
@ -745,5 +740,4 @@ private class AddCustomTokenReducer(
|
|||
private fun updateFormState(state: AddCustomTokenState): AddCustomTokenState {
|
||||
return state.copy(form = Form(state.form.fieldList))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.domain.features.addCustomToken.redux
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.DerivationStyle
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.domain.AddCustomTokenError
|
||||
import com.tangem.domain.DomainWrapped
|
||||
import com.tangem.domain.common.CardDTO
|
||||
|
|
@ -37,7 +38,6 @@ import com.tangem.domain.features.addCustomToken.TokenDerivationPathField
|
|||
import com.tangem.domain.features.addCustomToken.TokenField
|
||||
import com.tangem.domain.redux.DomainState
|
||||
import com.tangem.domain.redux.state.StringActionStateConverter
|
||||
import com.tangem.datasource.api.tangemTech.CoinsResponse
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.StateType
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ internal class SwapRepositoryImpl @Inject constructor(
|
|||
|
||||
override suspend fun getExchangeableTokens(networkId: String): List<Currency> {
|
||||
return withContext(coroutineDispatcher.io) {
|
||||
tokensConverter.convertList(tangemTechApi.coins(exchangeable = true, networkIds = networkId).coins)
|
||||
tokensConverter.convertList(tangemTechApi.getCoins(exchangeable = true, networkIds = networkId).coins)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.feature.swap.converters
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.CoinsResponse
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.feature.swap.domain.models.data.Currency
|
||||
import com.tangem.utils.converter.Converter
|
||||
import javax.inject.Inject
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue