Updated on 2026-08-14
This commit is contained in:
parent
dfa6798f07
commit
5883823eb3
71 changed files with 221 additions and 285 deletions
|
|
@ -18,7 +18,7 @@ class CryptoCurrencyFactory(
|
|||
@Suppress("LongParameterList") // Yep, it's long
|
||||
fun createToken(
|
||||
network: Network,
|
||||
rawId: String?,
|
||||
rawId: CryptoCurrency.RawID?,
|
||||
name: String,
|
||||
symbol: String,
|
||||
decimals: Int,
|
||||
|
|
@ -125,7 +125,7 @@ class CryptoCurrencyFactory(
|
|||
symbol = cryptoCurrency.symbol,
|
||||
contractAddress = cryptoCurrency.contractAddress,
|
||||
decimals = cryptoCurrency.decimals,
|
||||
id = cryptoCurrency.id.rawCurrencyId,
|
||||
id = cryptoCurrency.id.rawCurrencyId?.value,
|
||||
)
|
||||
val blockchain = Blockchain.fromNetworkId(cryptoCurrency.network.backendId) ?: Blockchain.Unknown
|
||||
val id = getTokenId(network, sdkToken)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.blockchain.common.Blockchain
|
|||
import com.tangem.blockchain.common.IconsUtil
|
||||
import com.tangem.blockchainsdk.utils.toCoinId
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency.ID
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.blockchain.common.Token as SdkToken
|
||||
|
|
@ -31,15 +32,16 @@ fun getCoinId(network: Network, coinId: String): ID {
|
|||
|
||||
fun getTokenId(network: Network, sdkToken: SdkToken): ID {
|
||||
val sdkTokenId = sdkToken.id
|
||||
val tokenId = sdkTokenId?.let { CryptoCurrency.RawID(it) }
|
||||
|
||||
return getTokenId(network, sdkTokenId, sdkToken.contractAddress)
|
||||
return getTokenId(network, tokenId, sdkToken.contractAddress)
|
||||
}
|
||||
|
||||
fun getTokenId(network: Network, rawTokenId: String?, contractAddress: String): ID {
|
||||
fun getTokenId(network: Network, rawTokenId: CryptoCurrency.RawID?, contractAddress: String): ID {
|
||||
val suffix = if (rawTokenId == null) {
|
||||
CustomCurrencyIdSuffix(contractAddress)
|
||||
} else {
|
||||
CurrencyIdSuffix(rawTokenId, contractAddress)
|
||||
CurrencyIdSuffix(rawTokenId.value, contractAddress)
|
||||
}
|
||||
|
||||
return ID(TOKEN_ID_PREFIX, getCurrencyIdBody(network), suffix)
|
||||
|
|
@ -47,11 +49,12 @@ fun getTokenId(network: Network, rawTokenId: String?, contractAddress: String):
|
|||
|
||||
fun getTokenIconUrl(blockchain: Blockchain, token: SdkToken): String? {
|
||||
val tokenId = token.id
|
||||
val rawID = tokenId?.let { CryptoCurrency.RawID(it) }
|
||||
|
||||
return if (tokenId == null) {
|
||||
return if (rawID == null) {
|
||||
IconsUtil.getTokenIconUri(blockchain, token)?.toString()
|
||||
} else {
|
||||
getTokenIconUrlFromDefaultHost(tokenId)
|
||||
getTokenIconUrlFromDefaultHost(rawID)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +62,7 @@ fun getCoinIconUrl(blockchain: Blockchain): String? {
|
|||
val coinId = when (blockchain) {
|
||||
Blockchain.Unknown -> null
|
||||
else -> blockchain.toCoinId()
|
||||
}
|
||||
}?.let { CryptoCurrency.RawID(it) }
|
||||
|
||||
return coinId?.let(::getTokenIconUrlFromDefaultHost)
|
||||
}
|
||||
|
|
@ -84,13 +87,13 @@ private fun getCurrencyIdBody(network: Network): CurrencyIdBody {
|
|||
}
|
||||
}
|
||||
|
||||
fun getTokenIconUrlFromDefaultHost(tokenId: String): String {
|
||||
fun getTokenIconUrlFromDefaultHost(tokenId: CryptoCurrency.RawID): String {
|
||||
return buildString {
|
||||
append(DEFAULT_TOKENS_ICONS_HOST)
|
||||
append('/')
|
||||
append(TOKEN_ICON_SIZE)
|
||||
append('/')
|
||||
append(tokenId)
|
||||
append(tokenId.value)
|
||||
append('.')
|
||||
append(TOKEN_ICON_EXT)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class UserTokensResponseFactory {
|
|||
val blockchain = getBlockchain(currency.network.id)
|
||||
|
||||
return UserTokensResponse.Token(
|
||||
id = currency.id.rawCurrencyId,
|
||||
id = currency.id.rawCurrencyId?.value,
|
||||
networkId = blockchain.toNetworkId(),
|
||||
derivationPath = currency.network.derivationPath.value,
|
||||
name = currency.name,
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ internal class DefaultCustomTokensRepository(
|
|||
if (coinNetwork != null) {
|
||||
cryptoCurrencyFactory.createToken(
|
||||
network = network,
|
||||
rawId = coin.id,
|
||||
rawId = CryptoCurrency.RawID(coin.id),
|
||||
name = coin.name,
|
||||
symbol = coin.symbol,
|
||||
decimals = coinNetwork.decimalCount!!.toInt(),
|
||||
|
|
@ -154,7 +154,7 @@ internal class DefaultCustomTokensRepository(
|
|||
override suspend fun createToken(
|
||||
managedCryptoCurrency: ManagedCryptoCurrency.Token,
|
||||
sourceNetwork: ManagedCryptoCurrency.SourceNetwork.Default,
|
||||
rawId: String?,
|
||||
rawId: CryptoCurrency.RawID?,
|
||||
): CryptoCurrency.Token {
|
||||
return cryptoCurrencyFactory.createToken(
|
||||
network = sourceNetwork.network,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import com.tangem.domain.common.util.derivationStyleProvider
|
|||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
|
||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
|
||||
internal class ManagedCryptoCurrencyFactory(
|
||||
|
|
@ -114,7 +115,11 @@ internal class ManagedCryptoCurrencyFactory(
|
|||
)
|
||||
} else {
|
||||
ManagedCryptoCurrency.Custom.Token(
|
||||
currencyId = getTokenId(network, token.id, contractAddress),
|
||||
currencyId = getTokenId(
|
||||
network = network,
|
||||
rawTokenId = token.id?.let { CryptoCurrency.RawID(it) },
|
||||
contractAddress = contractAddress,
|
||||
),
|
||||
name = token.name,
|
||||
symbol = token.symbol,
|
||||
iconUrl = token.id?.let { getIconUrl(it) },
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ dependencies {
|
|||
implementation(projects.domain.markets)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.tokens)
|
||||
|
||||
implementation(projects.data.common)
|
||||
|
||||
|
|
|
|||
|
|
@ -125,10 +125,10 @@ internal class DefaultMarketsTokenRepository(
|
|||
override suspend fun getChart(
|
||||
fiatCurrencyCode: String,
|
||||
interval: PriceChangeInterval,
|
||||
tokenId: String,
|
||||
tokenId: CryptoCurrency.RawID,
|
||||
tokenSymbol: String,
|
||||
) = withContext(dispatcherProvider.io) {
|
||||
val mappedTokenId = getTokenIdIfL2Network(tokenId)
|
||||
val mappedTokenId = getTokenIdIfL2Network(tokenId.value)
|
||||
val response = marketsApi.getCoinChart(
|
||||
currency = fiatCurrencyCode,
|
||||
coinId = mappedTokenId,
|
||||
|
|
@ -161,10 +161,10 @@ internal class DefaultMarketsTokenRepository(
|
|||
override suspend fun getChartPreview(
|
||||
fiatCurrencyCode: String,
|
||||
interval: PriceChangeInterval,
|
||||
tokenId: String,
|
||||
tokenId: CryptoCurrency.RawID,
|
||||
tokenSymbol: String,
|
||||
) = withContext(dispatcherProvider.io) {
|
||||
val mappedTokenId = getTokenIdIfL2Network(tokenId)
|
||||
val mappedTokenId = getTokenIdIfL2Network(tokenId.value)
|
||||
|
||||
val chart = catchListErrorAndSendEvent {
|
||||
marketsApi.getCoinsListCharts(
|
||||
|
|
@ -193,7 +193,7 @@ internal class DefaultMarketsTokenRepository(
|
|||
|
||||
override suspend fun getTokenInfo(
|
||||
fiatCurrencyCode: String,
|
||||
tokenId: String,
|
||||
tokenId: CryptoCurrency.RawID,
|
||||
tokenSymbol: String,
|
||||
languageCode: String,
|
||||
) = withContext(dispatcherProvider.io) {
|
||||
|
|
@ -203,16 +203,16 @@ internal class DefaultMarketsTokenRepository(
|
|||
) {
|
||||
marketsApi.getCoinMarketData(
|
||||
currency = fiatCurrencyCode,
|
||||
coinId = tokenId,
|
||||
coinId = tokenId.value,
|
||||
language = languageCode,
|
||||
).getOrThrow()
|
||||
}
|
||||
|
||||
val resultResponse = result.applyL2Compatibility(tokenId)
|
||||
val resultResponse = result.applyL2Compatibility(tokenId.value)
|
||||
return@withContext tokenMarketInfoConverter.convert(resultResponse)
|
||||
}
|
||||
|
||||
override suspend fun getTokenQuotes(fiatCurrencyCode: String, tokenId: String, tokenSymbol: String) =
|
||||
override suspend fun getTokenQuotes(fiatCurrencyCode: String, tokenId: CryptoCurrency.RawID, tokenSymbol: String) =
|
||||
withContext(dispatcherProvider.io) {
|
||||
// for second markets iteration we should use extended api method with all required fields
|
||||
|
||||
|
|
@ -222,7 +222,7 @@ internal class DefaultMarketsTokenRepository(
|
|||
) {
|
||||
tangemTechApi.getQuotes(
|
||||
currencyId = fiatCurrencyCode,
|
||||
coinIds = tokenId,
|
||||
coinIds = tokenId.value,
|
||||
fields = marketsQuoteFields.joinToString(separator = ","),
|
||||
).getOrThrow()
|
||||
}
|
||||
|
|
@ -263,10 +263,10 @@ internal class DefaultMarketsTokenRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getTokenExchanges(tokenId: String): List<TokenMarketExchange> {
|
||||
override suspend fun getTokenExchanges(tokenId: CryptoCurrency.RawID): List<TokenMarketExchange> {
|
||||
return withContext(dispatcherProvider.io) {
|
||||
cacheRegistry.invokeOnExpire(key = "coins/$tokenId/exchanges", skipCache = false) {
|
||||
val response = marketsApi.getCoinExchanges(coinId = tokenId).getOrThrow()
|
||||
val response = marketsApi.getCoinExchanges(coinId = tokenId.value).getOrThrow()
|
||||
|
||||
tokenExchangesStore.store(value = response.exchanges)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,16 +4,17 @@ import com.tangem.datasource.api.markets.models.response.TokenMarketChartListRes
|
|||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import com.tangem.domain.markets.TokenMarket
|
||||
import com.tangem.domain.markets.TokenMarketListConfig
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
||||
internal object TokenMarketChartsConverter {
|
||||
|
||||
fun convert(
|
||||
chartsToCopy: TokenMarket.Charts,
|
||||
tokenId: String,
|
||||
tokenId: CryptoCurrency.RawID,
|
||||
interval: TokenMarketListConfig.Interval,
|
||||
value: TokenMarketChartListResponse,
|
||||
): TokenMarket.Charts {
|
||||
val prices = requireNotNull(value[tokenId]) {
|
||||
val prices = requireNotNull(value[tokenId.value]) {
|
||||
"$tokenId is not found in the response. This shouldn't have happened."
|
||||
}
|
||||
return when (interval) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.data.markets.converters
|
|||
import com.tangem.datasource.api.markets.models.response.TokenMarketListResponse
|
||||
import com.tangem.domain.markets.TokenMarket
|
||||
import com.tangem.domain.markets.TokenQuotesShort
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal object TokenMarketListConverter : Converter<TokenMarketListResponse, List<TokenMarket>> {
|
||||
|
|
@ -18,7 +19,7 @@ internal object TokenMarketListConverter : Converter<TokenMarketListResponse, Li
|
|||
|
||||
return value.tokens.map { token ->
|
||||
TokenMarket(
|
||||
id = token.id,
|
||||
id = CryptoCurrency.RawID(token.id),
|
||||
name = token.name,
|
||||
symbol = token.symbol,
|
||||
marketRating = token.marketRating,
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@ package com.tangem.data.markets.converters
|
|||
|
||||
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
|
||||
import com.tangem.domain.markets.TokenQuotesShort
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal object TokenQuotesShortConverter {
|
||||
|
||||
fun convert(tokenId: String, value: QuotesResponse): TokenQuotesShort {
|
||||
val quote = requireNotNull(value.quotes[tokenId]) {
|
||||
fun convert(tokenId: CryptoCurrency.RawID, value: QuotesResponse): TokenQuotesShort {
|
||||
val quote = requireNotNull(value.quotes[tokenId.value]) {
|
||||
"$tokenId is not found in the response. This shouldn't have happened."
|
||||
}
|
||||
return TokenQuotesShort(
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ internal class HotCryptoCurrencyConverter(
|
|||
val currency = if (contractAddress != null && decimals != null) {
|
||||
cryptoCurrencyFactory.createToken(
|
||||
network = network,
|
||||
rawId = value.id,
|
||||
rawId = CryptoCurrency.RawID(value.id),
|
||||
name = value.name,
|
||||
symbol = value.symbol,
|
||||
decimals = decimals,
|
||||
|
|
|
|||
|
|
@ -579,9 +579,9 @@ internal class DefaultStakingRepository(
|
|||
}
|
||||
}
|
||||
|
||||
private fun findPrefetchedYield(yields: List<Yield>, currencyId: String, symbol: String): Yield? {
|
||||
private fun findPrefetchedYield(yields: List<Yield>, currencyId: CryptoCurrency.RawID, symbol: String): Yield? {
|
||||
return yields.find { yield ->
|
||||
yield.tokens.any { it.coinGeckoId == currencyId && it.symbol == symbol }
|
||||
yield.tokens.any { it.coinGeckoId == currencyId.value && it.symbol == symbol }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,82 +0,0 @@
|
|||
package com.tangem.data.tokens.paging
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Quote
|
||||
import com.tangem.domain.tokens.model.Token
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
||||
/**
|
||||
* A PagingSource responsible for retrieving tokens from the Tangem Tech API and adding to them quotes information.
|
||||
*
|
||||
* @property api Tangem Tech API
|
||||
* @property quotesRepository Repository providing quotes data.
|
||||
* @property dispatchers Coroutine dispatchers provider.
|
||||
* @property searchText The search text used to filter tokens.
|
||||
*/
|
||||
internal class CoinsPagingSource(
|
||||
private val api: TangemTechApi,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val searchText: String?,
|
||||
) : PagingSource<Int, Token>() {
|
||||
|
||||
override fun getRefreshKey(state: PagingState<Int, Token>): Int? {
|
||||
return state.anchorPosition?.let { anchorPosition ->
|
||||
state.closestPageToPosition(anchorPosition)?.prevKey?.plus(other = 1)
|
||||
?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(other = 1)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Token> {
|
||||
val page = params.key ?: 0
|
||||
|
||||
return com.tangem.utils.coroutines.runCatching(dispatchers.io) {
|
||||
api.getCoins(
|
||||
active = true, // TODO change when voting functionality is implemented
|
||||
searchText = searchText,
|
||||
offset = page * params.loadSize,
|
||||
limit = params.loadSize,
|
||||
).getOrThrow()
|
||||
}.fold(
|
||||
onSuccess = { response ->
|
||||
val coinsIds = response.coins.map { coin ->
|
||||
CryptoCurrency.ID(
|
||||
prefix = CryptoCurrency.ID.Prefix.TOKEN_PREFIX,
|
||||
body = CryptoCurrency.ID.Body.NetworkId(coin.id),
|
||||
suffix = CryptoCurrency.ID.Suffix.RawID(coin.id),
|
||||
)
|
||||
}
|
||||
|
||||
try {
|
||||
val quotes = quotesRepository.getQuotesSync(currenciesIds = coinsIds.toSet(), refresh = false)
|
||||
LoadResult.Page(
|
||||
data = CoinsResponseConverter.convert(
|
||||
CoinsData(
|
||||
response.coins,
|
||||
response.imageHost,
|
||||
quotes,
|
||||
),
|
||||
),
|
||||
prevKey = if (page == 0) null else page.minus(other = 1),
|
||||
nextKey = if (response.coins.isEmpty()) null else page.plus(other = 1),
|
||||
)
|
||||
} catch (t: Throwable) {
|
||||
LoadResult.Error(t)
|
||||
}
|
||||
},
|
||||
onFailure = { LoadResult.Error(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class CoinsData(
|
||||
val coins: List<CoinsResponse.Coin>,
|
||||
val imageHost: String?,
|
||||
val quotes: Set<Quote>,
|
||||
)
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
package com.tangem.data.tokens.paging
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.fromNetworkId
|
||||
import com.tangem.data.common.currency.getNetworkStandardType
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Token
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
/**
|
||||
* Converter from data model [CoinsResponse] to list of domain models [Token]
|
||||
*/
|
||||
internal object CoinsResponseConverter : Converter<CoinsData, List<Token>> {
|
||||
|
||||
override fun convert(value: CoinsData): List<Token> {
|
||||
return value.coins.map { coin ->
|
||||
val id = CryptoCurrency.ID(
|
||||
CryptoCurrency.ID.Prefix.TOKEN_PREFIX,
|
||||
CryptoCurrency.ID.Body.NetworkId(coin.id),
|
||||
CryptoCurrency.ID.Suffix.RawID(coin.id),
|
||||
)
|
||||
val quote = value.quotes.find { it.rawCurrencyId == id.rawCurrencyId }
|
||||
Token(
|
||||
id = id.rawCurrencyId ?: coin.name,
|
||||
name = coin.name,
|
||||
symbol = coin.symbol,
|
||||
iconUrl = getIconUrl(coin.id, value.imageHost),
|
||||
isAvailable = coin.active,
|
||||
networks = coin.networks.mapNotNull { network ->
|
||||
val blockchain = Blockchain.fromNetworkId(network.networkId) ?: return@mapNotNull null
|
||||
Token.Network(
|
||||
networkId = network.networkId,
|
||||
standardType = getNetworkStandardType(blockchain).name,
|
||||
name = blockchain.fullName,
|
||||
address = network.contractAddress,
|
||||
iconUrl = getIconUrl(network.networkId, value.imageHost),
|
||||
decimalCount = network.decimalCount?.toInt(),
|
||||
)
|
||||
},
|
||||
quote = quote,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getIconUrl(id: String, imageHost: String? = null): String {
|
||||
return "${imageHost ?: DEFAULT_IMAGE_HOST}large/$id.png"
|
||||
}
|
||||
|
||||
private const val DEFAULT_IMAGE_HOST =
|
||||
"https://s3.eu-central-1.amazonaws.com/tangem.api/coins/"
|
||||
|
|
@ -486,7 +486,9 @@ internal class DefaultCurrenciesRepository(
|
|||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun getAllWalletsCryptoCurrencies(currencyRawId: String): Flow<Map<UserWallet, List<CryptoCurrency>>> {
|
||||
override fun getAllWalletsCryptoCurrencies(
|
||||
currencyRawId: CryptoCurrency.RawID,
|
||||
): Flow<Map<UserWallet, List<CryptoCurrency>>> {
|
||||
return userWalletsStore.userWallets.flatMapLatest { userWallets ->
|
||||
userWallets.forEach { fetchTokensIfCacheExpired(userWallet = it, refresh = false) }
|
||||
|
||||
|
|
@ -496,7 +498,7 @@ internal class DefaultCurrenciesRepository(
|
|||
if (userWallet.isMultiCurrency) {
|
||||
getSavedUserTokensResponse(userWallet.walletId).map { storedTokens ->
|
||||
val filterResponse = storedTokens.tokens.filter {
|
||||
getL2CompatibilityTokenComparison(it, currencyRawId)
|
||||
getL2CompatibilityTokenComparison(it, currencyRawId.value)
|
||||
}
|
||||
|
||||
responseCurrenciesFactory.createCurrencies(
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ internal class DefaultQuotesRepository(
|
|||
private val mutex = Mutex()
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun getQuotesUpdates(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean): Flow<Set<Quote>> {
|
||||
override fun getQuotesUpdates(currenciesIds: Set<CryptoCurrency.RawID>, refresh: Boolean): Flow<Set<Quote>> {
|
||||
return appPreferencesStore.getObject<CurrenciesResponse.Currency>(
|
||||
key = PreferencesKeys.SELECTED_APP_CURRENCY_KEY,
|
||||
)
|
||||
|
|
@ -51,7 +51,7 @@ internal class DefaultQuotesRepository(
|
|||
.flowOn(dispatchers.io)
|
||||
}
|
||||
|
||||
override suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.ID>) {
|
||||
override suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.RawID>) {
|
||||
withContext(dispatchers.io) {
|
||||
val selectedAppCurrency = requireNotNull(
|
||||
value = appPreferencesStore.getObjectSyncOrNull<CurrenciesResponse.Currency>(
|
||||
|
|
@ -64,7 +64,7 @@ internal class DefaultQuotesRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getQuotesSync(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean): Set<Quote> {
|
||||
override suspend fun getQuotesSync(currenciesIds: Set<CryptoCurrency.RawID>, refresh: Boolean): Set<Quote> {
|
||||
return withContext(dispatchers.io) {
|
||||
val selectedAppCurrency = requireNotNull(
|
||||
value = appPreferencesStore.getObjectSyncOrNull<CurrenciesResponse.Currency>(
|
||||
|
|
@ -81,7 +81,7 @@ internal class DefaultQuotesRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun getQuoteSync(currencyId: CryptoCurrency.ID): Quote? {
|
||||
override suspend fun getQuoteSync(currencyId: CryptoCurrency.RawID): Quote? {
|
||||
return withContext(dispatchers.io) {
|
||||
val setOfCurrencyId = setOf(currencyId)
|
||||
val quote = quotesStore.getSync(setOfCurrencyId).firstOrNull()
|
||||
|
|
@ -90,7 +90,7 @@ internal class DefaultQuotesRepository(
|
|||
}
|
||||
|
||||
private suspend fun fetchExpiredQuotes(
|
||||
currenciesIds: Set<CryptoCurrency.ID>,
|
||||
currenciesIds: Set<CryptoCurrency.RawID>,
|
||||
appCurrencyId: String,
|
||||
refresh: Boolean,
|
||||
) {
|
||||
|
|
@ -110,15 +110,17 @@ internal class DefaultQuotesRepository(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchQuotes(rawCurrenciesIds: Set<String>, appCurrencyId: String) {
|
||||
val replacementIdsResult = quotesUnsupportedCurrenciesAdapter.replaceUnsupportedCurrencies(rawCurrenciesIds)
|
||||
private suspend fun fetchQuotes(rawCurrenciesIds: Set<CryptoCurrency.RawID>, appCurrencyId: String) {
|
||||
val replacementIdsResult = quotesUnsupportedCurrenciesAdapter.replaceUnsupportedCurrencies(
|
||||
rawCurrenciesIds.map { it.value }.toSet(),
|
||||
)
|
||||
val response = safeApiCallWithTimeout(
|
||||
call = {
|
||||
val coinIds = replacementIdsResult.idsForRequest.joinToString(separator = ",")
|
||||
tangemTechApi.getQuotes(appCurrencyId, coinIds).bind()
|
||||
},
|
||||
onError = { error ->
|
||||
cacheRegistry.invalidate(rawCurrenciesIds.map(::getQuoteCacheKey))
|
||||
cacheRegistry.invalidate(rawCurrenciesIds.map { getQuoteCacheKey(it) })
|
||||
|
||||
throw error
|
||||
},
|
||||
|
|
@ -132,21 +134,20 @@ internal class DefaultQuotesRepository(
|
|||
}
|
||||
|
||||
private suspend fun filterExpiredCurrenciesIds(
|
||||
currenciesIds: Set<CryptoCurrency.ID>,
|
||||
currenciesIds: Set<CryptoCurrency.RawID>,
|
||||
refresh: Boolean,
|
||||
): Set<String> {
|
||||
): Set<CryptoCurrency.RawID> {
|
||||
return currenciesIds.fold(hashSetOf()) { acc, currencyId ->
|
||||
val rawCurrencyId = currencyId.rawCurrencyId
|
||||
if (rawCurrencyId != null && rawCurrencyId !in acc) {
|
||||
if (currencyId !in acc) {
|
||||
cacheRegistry.invokeOnExpire(
|
||||
key = getQuoteCacheKey(rawCurrencyId),
|
||||
key = getQuoteCacheKey(currencyId),
|
||||
skipCache = refresh,
|
||||
block = { acc.add(rawCurrencyId) },
|
||||
block = { acc.add(currencyId) },
|
||||
)
|
||||
}
|
||||
acc
|
||||
}
|
||||
}
|
||||
|
||||
private fun getQuoteCacheKey(rawCurrencyId: String): String = "quote_$rawCurrencyId"
|
||||
private fun getQuoteCacheKey(rawCurrencyId: CryptoCurrency.RawID): String = "quote_${rawCurrencyId.value}"
|
||||
}
|
||||
|
|
@ -6,16 +6,16 @@ import com.tangem.domain.tokens.model.Quote
|
|||
import com.tangem.utils.converter.Converter
|
||||
import java.math.BigDecimal
|
||||
|
||||
typealias QuotesConverterValue = Pair<Set<CryptoCurrency.ID>, Set<StoredQuote>>
|
||||
typealias QuotesConverterValue = Pair<Set<CryptoCurrency.RawID>, Set<StoredQuote>>
|
||||
|
||||
internal class QuotesConverter : Converter<QuotesConverterValue, Set<Quote>> {
|
||||
|
||||
override fun convert(value: QuotesConverterValue): Set<Quote> {
|
||||
val (setOfCurrencyId, setOfStoredQuote) = value
|
||||
return setOfCurrencyId.mapTo(hashSetOf()) { id ->
|
||||
setOfStoredQuote.find { id.rawCurrencyId == it.rawCurrencyId }
|
||||
setOfStoredQuote.find { id.value == it.rawCurrencyId }
|
||||
?.let(::convertExistStoredQuote)
|
||||
?: Quote.Empty(id.rawCurrencyId)
|
||||
?: Quote.Empty(id)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ internal class QuotesConverter : Converter<QuotesConverterValue, Set<Quote>> {
|
|||
val (rawCurrencyId, responseQuote) = storedQuote
|
||||
|
||||
return Quote.Value(
|
||||
rawCurrencyId = rawCurrencyId,
|
||||
rawCurrencyId = CryptoCurrency.RawID(rawCurrencyId),
|
||||
fiatRate = responseQuote.price ?: BigDecimal.ZERO,
|
||||
priceChange = (responseQuote.priceChange24h ?: BigDecimal.ZERO).movePointLeft(2),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue