Updated on 2026-08-14
This commit is contained in:
parent
dfa6798f07
commit
5883823eb3
71 changed files with 221 additions and 285 deletions
|
|
@ -46,7 +46,7 @@ internal class DefaultHotCryptoLoader @Inject constructor(
|
||||||
override suspend fun update(currencies: List<CryptoCurrency>) {
|
override suspend fun update(currencies: List<CryptoCurrency>) {
|
||||||
updateInternal { hotToken ->
|
updateInternal { hotToken ->
|
||||||
currencies.none {
|
currencies.none {
|
||||||
it.id.rawCurrencyId == hotToken.id &&
|
it.id.rawCurrencyId?.value == hotToken.id &&
|
||||||
(it as? CryptoCurrency.Token)?.contractAddress == hotToken.contractAddress &&
|
(it as? CryptoCurrency.Token)?.contractAddress == hotToken.contractAddress &&
|
||||||
it.network.id.value == hotToken.networkId
|
it.network.id.value == hotToken.networkId
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,9 @@ internal class DefaultQuotesStore(
|
||||||
private val dataStore: StringKeyDataStore<StoredQuote>,
|
private val dataStore: StringKeyDataStore<StoredQuote>,
|
||||||
) : QuotesStore {
|
) : QuotesStore {
|
||||||
|
|
||||||
override fun get(currenciesIds: Set<CryptoCurrency.ID>): Flow<Set<StoredQuote>> {
|
override fun get(currenciesIds: Set<CryptoCurrency.RawID>): Flow<Set<StoredQuote>> {
|
||||||
return channelFlow {
|
return channelFlow {
|
||||||
val flows = currenciesIds.mapNotNull { currencyId ->
|
val flows = currenciesIds.map { currencyId -> dataStore.get(currencyId.value) }
|
||||||
currencyId.rawCurrencyId?.let(dataStore::get)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataStore.isEmpty() || flows.isEmpty()) {
|
if (dataStore.isEmpty() || flows.isEmpty()) {
|
||||||
send(emptySet())
|
send(emptySet())
|
||||||
|
|
@ -30,12 +28,8 @@ internal class DefaultQuotesStore(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getSync(currenciesIds: Set<CryptoCurrency.ID>): Set<StoredQuote> {
|
override suspend fun getSync(currenciesIds: Set<CryptoCurrency.RawID>): Set<StoredQuote> {
|
||||||
return currenciesIds.mapNotNull { currencyId ->
|
return currenciesIds.mapNotNull { currencyId -> dataStore.getSyncOrNull(currencyId.value) }.toSet()
|
||||||
currencyId.rawCurrencyId?.let {
|
|
||||||
dataStore.getSyncOrNull(it)
|
|
||||||
}
|
|
||||||
}.toSet()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun store(response: QuotesResponse) {
|
override suspend fun store(response: QuotesResponse) {
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface QuotesStore {
|
interface QuotesStore {
|
||||||
|
|
||||||
fun get(currenciesIds: Set<CryptoCurrency.ID>): Flow<Set<StoredQuote>>
|
fun get(currenciesIds: Set<CryptoCurrency.RawID>): Flow<Set<StoredQuote>>
|
||||||
|
|
||||||
suspend fun getSync(currenciesIds: Set<CryptoCurrency.ID>): Set<StoredQuote>
|
suspend fun getSync(currenciesIds: Set<CryptoCurrency.RawID>): Set<StoredQuote>
|
||||||
|
|
||||||
suspend fun store(response: QuotesResponse)
|
suspend fun store(response: QuotesResponse)
|
||||||
}
|
}
|
||||||
|
|
@ -18,7 +18,7 @@ class CryptoCurrencyFactory(
|
||||||
@Suppress("LongParameterList") // Yep, it's long
|
@Suppress("LongParameterList") // Yep, it's long
|
||||||
fun createToken(
|
fun createToken(
|
||||||
network: Network,
|
network: Network,
|
||||||
rawId: String?,
|
rawId: CryptoCurrency.RawID?,
|
||||||
name: String,
|
name: String,
|
||||||
symbol: String,
|
symbol: String,
|
||||||
decimals: Int,
|
decimals: Int,
|
||||||
|
|
@ -125,7 +125,7 @@ class CryptoCurrencyFactory(
|
||||||
symbol = cryptoCurrency.symbol,
|
symbol = cryptoCurrency.symbol,
|
||||||
contractAddress = cryptoCurrency.contractAddress,
|
contractAddress = cryptoCurrency.contractAddress,
|
||||||
decimals = cryptoCurrency.decimals,
|
decimals = cryptoCurrency.decimals,
|
||||||
id = cryptoCurrency.id.rawCurrencyId,
|
id = cryptoCurrency.id.rawCurrencyId?.value,
|
||||||
)
|
)
|
||||||
val blockchain = Blockchain.fromNetworkId(cryptoCurrency.network.backendId) ?: Blockchain.Unknown
|
val blockchain = Blockchain.fromNetworkId(cryptoCurrency.network.backendId) ?: Blockchain.Unknown
|
||||||
val id = getTokenId(network, sdkToken)
|
val id = getTokenId(network, sdkToken)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import com.tangem.blockchain.common.Blockchain
|
||||||
import com.tangem.blockchain.common.IconsUtil
|
import com.tangem.blockchain.common.IconsUtil
|
||||||
import com.tangem.blockchainsdk.utils.toCoinId
|
import com.tangem.blockchainsdk.utils.toCoinId
|
||||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
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.CryptoCurrency.ID
|
||||||
import com.tangem.domain.tokens.model.Network
|
import com.tangem.domain.tokens.model.Network
|
||||||
import com.tangem.blockchain.common.Token as SdkToken
|
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 {
|
fun getTokenId(network: Network, sdkToken: SdkToken): ID {
|
||||||
val sdkTokenId = 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) {
|
val suffix = if (rawTokenId == null) {
|
||||||
CustomCurrencyIdSuffix(contractAddress)
|
CustomCurrencyIdSuffix(contractAddress)
|
||||||
} else {
|
} else {
|
||||||
CurrencyIdSuffix(rawTokenId, contractAddress)
|
CurrencyIdSuffix(rawTokenId.value, contractAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ID(TOKEN_ID_PREFIX, getCurrencyIdBody(network), suffix)
|
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? {
|
fun getTokenIconUrl(blockchain: Blockchain, token: SdkToken): String? {
|
||||||
val tokenId = token.id
|
val tokenId = token.id
|
||||||
|
val rawID = tokenId?.let { CryptoCurrency.RawID(it) }
|
||||||
|
|
||||||
return if (tokenId == null) {
|
return if (rawID == null) {
|
||||||
IconsUtil.getTokenIconUri(blockchain, token)?.toString()
|
IconsUtil.getTokenIconUri(blockchain, token)?.toString()
|
||||||
} else {
|
} else {
|
||||||
getTokenIconUrlFromDefaultHost(tokenId)
|
getTokenIconUrlFromDefaultHost(rawID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,7 +62,7 @@ fun getCoinIconUrl(blockchain: Blockchain): String? {
|
||||||
val coinId = when (blockchain) {
|
val coinId = when (blockchain) {
|
||||||
Blockchain.Unknown -> null
|
Blockchain.Unknown -> null
|
||||||
else -> blockchain.toCoinId()
|
else -> blockchain.toCoinId()
|
||||||
}
|
}?.let { CryptoCurrency.RawID(it) }
|
||||||
|
|
||||||
return coinId?.let(::getTokenIconUrlFromDefaultHost)
|
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 {
|
return buildString {
|
||||||
append(DEFAULT_TOKENS_ICONS_HOST)
|
append(DEFAULT_TOKENS_ICONS_HOST)
|
||||||
append('/')
|
append('/')
|
||||||
append(TOKEN_ICON_SIZE)
|
append(TOKEN_ICON_SIZE)
|
||||||
append('/')
|
append('/')
|
||||||
append(tokenId)
|
append(tokenId.value)
|
||||||
append('.')
|
append('.')
|
||||||
append(TOKEN_ICON_EXT)
|
append(TOKEN_ICON_EXT)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ class UserTokensResponseFactory {
|
||||||
val blockchain = getBlockchain(currency.network.id)
|
val blockchain = getBlockchain(currency.network.id)
|
||||||
|
|
||||||
return UserTokensResponse.Token(
|
return UserTokensResponse.Token(
|
||||||
id = currency.id.rawCurrencyId,
|
id = currency.id.rawCurrencyId?.value,
|
||||||
networkId = blockchain.toNetworkId(),
|
networkId = blockchain.toNetworkId(),
|
||||||
derivationPath = currency.network.derivationPath.value,
|
derivationPath = currency.network.derivationPath.value,
|
||||||
name = currency.name,
|
name = currency.name,
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ internal class DefaultCustomTokensRepository(
|
||||||
if (coinNetwork != null) {
|
if (coinNetwork != null) {
|
||||||
cryptoCurrencyFactory.createToken(
|
cryptoCurrencyFactory.createToken(
|
||||||
network = network,
|
network = network,
|
||||||
rawId = coin.id,
|
rawId = CryptoCurrency.RawID(coin.id),
|
||||||
name = coin.name,
|
name = coin.name,
|
||||||
symbol = coin.symbol,
|
symbol = coin.symbol,
|
||||||
decimals = coinNetwork.decimalCount!!.toInt(),
|
decimals = coinNetwork.decimalCount!!.toInt(),
|
||||||
|
|
@ -154,7 +154,7 @@ internal class DefaultCustomTokensRepository(
|
||||||
override suspend fun createToken(
|
override suspend fun createToken(
|
||||||
managedCryptoCurrency: ManagedCryptoCurrency.Token,
|
managedCryptoCurrency: ManagedCryptoCurrency.Token,
|
||||||
sourceNetwork: ManagedCryptoCurrency.SourceNetwork.Default,
|
sourceNetwork: ManagedCryptoCurrency.SourceNetwork.Default,
|
||||||
rawId: String?,
|
rawId: CryptoCurrency.RawID?,
|
||||||
): CryptoCurrency.Token {
|
): CryptoCurrency.Token {
|
||||||
return cryptoCurrencyFactory.createToken(
|
return cryptoCurrencyFactory.createToken(
|
||||||
network = sourceNetwork.network,
|
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
|
||||||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
||||||
import com.tangem.domain.models.scan.ScanResponse
|
import com.tangem.domain.models.scan.ScanResponse
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.domain.tokens.model.Network
|
import com.tangem.domain.tokens.model.Network
|
||||||
|
|
||||||
internal class ManagedCryptoCurrencyFactory(
|
internal class ManagedCryptoCurrencyFactory(
|
||||||
|
|
@ -114,7 +115,11 @@ internal class ManagedCryptoCurrencyFactory(
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ManagedCryptoCurrency.Custom.Token(
|
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,
|
name = token.name,
|
||||||
symbol = token.symbol,
|
symbol = token.symbol,
|
||||||
iconUrl = token.id?.let { getIconUrl(it) },
|
iconUrl = token.id?.let { getIconUrl(it) },
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ dependencies {
|
||||||
implementation(projects.domain.markets)
|
implementation(projects.domain.markets)
|
||||||
implementation(projects.domain.models)
|
implementation(projects.domain.models)
|
||||||
implementation(projects.domain.tokens.models)
|
implementation(projects.domain.tokens.models)
|
||||||
|
implementation(projects.domain.tokens)
|
||||||
|
|
||||||
implementation(projects.data.common)
|
implementation(projects.data.common)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -125,10 +125,10 @@ internal class DefaultMarketsTokenRepository(
|
||||||
override suspend fun getChart(
|
override suspend fun getChart(
|
||||||
fiatCurrencyCode: String,
|
fiatCurrencyCode: String,
|
||||||
interval: PriceChangeInterval,
|
interval: PriceChangeInterval,
|
||||||
tokenId: String,
|
tokenId: CryptoCurrency.RawID,
|
||||||
tokenSymbol: String,
|
tokenSymbol: String,
|
||||||
) = withContext(dispatcherProvider.io) {
|
) = withContext(dispatcherProvider.io) {
|
||||||
val mappedTokenId = getTokenIdIfL2Network(tokenId)
|
val mappedTokenId = getTokenIdIfL2Network(tokenId.value)
|
||||||
val response = marketsApi.getCoinChart(
|
val response = marketsApi.getCoinChart(
|
||||||
currency = fiatCurrencyCode,
|
currency = fiatCurrencyCode,
|
||||||
coinId = mappedTokenId,
|
coinId = mappedTokenId,
|
||||||
|
|
@ -161,10 +161,10 @@ internal class DefaultMarketsTokenRepository(
|
||||||
override suspend fun getChartPreview(
|
override suspend fun getChartPreview(
|
||||||
fiatCurrencyCode: String,
|
fiatCurrencyCode: String,
|
||||||
interval: PriceChangeInterval,
|
interval: PriceChangeInterval,
|
||||||
tokenId: String,
|
tokenId: CryptoCurrency.RawID,
|
||||||
tokenSymbol: String,
|
tokenSymbol: String,
|
||||||
) = withContext(dispatcherProvider.io) {
|
) = withContext(dispatcherProvider.io) {
|
||||||
val mappedTokenId = getTokenIdIfL2Network(tokenId)
|
val mappedTokenId = getTokenIdIfL2Network(tokenId.value)
|
||||||
|
|
||||||
val chart = catchListErrorAndSendEvent {
|
val chart = catchListErrorAndSendEvent {
|
||||||
marketsApi.getCoinsListCharts(
|
marketsApi.getCoinsListCharts(
|
||||||
|
|
@ -193,7 +193,7 @@ internal class DefaultMarketsTokenRepository(
|
||||||
|
|
||||||
override suspend fun getTokenInfo(
|
override suspend fun getTokenInfo(
|
||||||
fiatCurrencyCode: String,
|
fiatCurrencyCode: String,
|
||||||
tokenId: String,
|
tokenId: CryptoCurrency.RawID,
|
||||||
tokenSymbol: String,
|
tokenSymbol: String,
|
||||||
languageCode: String,
|
languageCode: String,
|
||||||
) = withContext(dispatcherProvider.io) {
|
) = withContext(dispatcherProvider.io) {
|
||||||
|
|
@ -203,16 +203,16 @@ internal class DefaultMarketsTokenRepository(
|
||||||
) {
|
) {
|
||||||
marketsApi.getCoinMarketData(
|
marketsApi.getCoinMarketData(
|
||||||
currency = fiatCurrencyCode,
|
currency = fiatCurrencyCode,
|
||||||
coinId = tokenId,
|
coinId = tokenId.value,
|
||||||
language = languageCode,
|
language = languageCode,
|
||||||
).getOrThrow()
|
).getOrThrow()
|
||||||
}
|
}
|
||||||
|
|
||||||
val resultResponse = result.applyL2Compatibility(tokenId)
|
val resultResponse = result.applyL2Compatibility(tokenId.value)
|
||||||
return@withContext tokenMarketInfoConverter.convert(resultResponse)
|
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) {
|
withContext(dispatcherProvider.io) {
|
||||||
// for second markets iteration we should use extended api method with all required fields
|
// for second markets iteration we should use extended api method with all required fields
|
||||||
|
|
||||||
|
|
@ -222,7 +222,7 @@ internal class DefaultMarketsTokenRepository(
|
||||||
) {
|
) {
|
||||||
tangemTechApi.getQuotes(
|
tangemTechApi.getQuotes(
|
||||||
currencyId = fiatCurrencyCode,
|
currencyId = fiatCurrencyCode,
|
||||||
coinIds = tokenId,
|
coinIds = tokenId.value,
|
||||||
fields = marketsQuoteFields.joinToString(separator = ","),
|
fields = marketsQuoteFields.joinToString(separator = ","),
|
||||||
).getOrThrow()
|
).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) {
|
return withContext(dispatcherProvider.io) {
|
||||||
cacheRegistry.invokeOnExpire(key = "coins/$tokenId/exchanges", skipCache = false) {
|
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)
|
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.PriceChangeInterval
|
||||||
import com.tangem.domain.markets.TokenMarket
|
import com.tangem.domain.markets.TokenMarket
|
||||||
import com.tangem.domain.markets.TokenMarketListConfig
|
import com.tangem.domain.markets.TokenMarketListConfig
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
|
||||||
internal object TokenMarketChartsConverter {
|
internal object TokenMarketChartsConverter {
|
||||||
|
|
||||||
fun convert(
|
fun convert(
|
||||||
chartsToCopy: TokenMarket.Charts,
|
chartsToCopy: TokenMarket.Charts,
|
||||||
tokenId: String,
|
tokenId: CryptoCurrency.RawID,
|
||||||
interval: TokenMarketListConfig.Interval,
|
interval: TokenMarketListConfig.Interval,
|
||||||
value: TokenMarketChartListResponse,
|
value: TokenMarketChartListResponse,
|
||||||
): TokenMarket.Charts {
|
): 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."
|
"$tokenId is not found in the response. This shouldn't have happened."
|
||||||
}
|
}
|
||||||
return when (interval) {
|
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.datasource.api.markets.models.response.TokenMarketListResponse
|
||||||
import com.tangem.domain.markets.TokenMarket
|
import com.tangem.domain.markets.TokenMarket
|
||||||
import com.tangem.domain.markets.TokenQuotesShort
|
import com.tangem.domain.markets.TokenQuotesShort
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.utils.converter.Converter
|
import com.tangem.utils.converter.Converter
|
||||||
|
|
||||||
internal object TokenMarketListConverter : Converter<TokenMarketListResponse, List<TokenMarket>> {
|
internal object TokenMarketListConverter : Converter<TokenMarketListResponse, List<TokenMarket>> {
|
||||||
|
|
@ -18,7 +19,7 @@ internal object TokenMarketListConverter : Converter<TokenMarketListResponse, Li
|
||||||
|
|
||||||
return value.tokens.map { token ->
|
return value.tokens.map { token ->
|
||||||
TokenMarket(
|
TokenMarket(
|
||||||
id = token.id,
|
id = CryptoCurrency.RawID(token.id),
|
||||||
name = token.name,
|
name = token.name,
|
||||||
symbol = token.symbol,
|
symbol = token.symbol,
|
||||||
marketRating = token.marketRating,
|
marketRating = token.marketRating,
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,13 @@ package com.tangem.data.markets.converters
|
||||||
|
|
||||||
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
|
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
|
||||||
import com.tangem.domain.markets.TokenQuotesShort
|
import com.tangem.domain.markets.TokenQuotesShort
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
internal object TokenQuotesShortConverter {
|
internal object TokenQuotesShortConverter {
|
||||||
|
|
||||||
fun convert(tokenId: String, value: QuotesResponse): TokenQuotesShort {
|
fun convert(tokenId: CryptoCurrency.RawID, value: QuotesResponse): TokenQuotesShort {
|
||||||
val quote = requireNotNull(value.quotes[tokenId]) {
|
val quote = requireNotNull(value.quotes[tokenId.value]) {
|
||||||
"$tokenId is not found in the response. This shouldn't have happened."
|
"$tokenId is not found in the response. This shouldn't have happened."
|
||||||
}
|
}
|
||||||
return TokenQuotesShort(
|
return TokenQuotesShort(
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ internal class HotCryptoCurrencyConverter(
|
||||||
val currency = if (contractAddress != null && decimals != null) {
|
val currency = if (contractAddress != null && decimals != null) {
|
||||||
cryptoCurrencyFactory.createToken(
|
cryptoCurrencyFactory.createToken(
|
||||||
network = network,
|
network = network,
|
||||||
rawId = value.id,
|
rawId = CryptoCurrency.RawID(value.id),
|
||||||
name = value.name,
|
name = value.name,
|
||||||
symbol = value.symbol,
|
symbol = value.symbol,
|
||||||
decimals = decimals,
|
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 ->
|
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)
|
@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 ->
|
return userWalletsStore.userWallets.flatMapLatest { userWallets ->
|
||||||
userWallets.forEach { fetchTokensIfCacheExpired(userWallet = it, refresh = false) }
|
userWallets.forEach { fetchTokensIfCacheExpired(userWallet = it, refresh = false) }
|
||||||
|
|
||||||
|
|
@ -496,7 +498,7 @@ internal class DefaultCurrenciesRepository(
|
||||||
if (userWallet.isMultiCurrency) {
|
if (userWallet.isMultiCurrency) {
|
||||||
getSavedUserTokensResponse(userWallet.walletId).map { storedTokens ->
|
getSavedUserTokensResponse(userWallet.walletId).map { storedTokens ->
|
||||||
val filterResponse = storedTokens.tokens.filter {
|
val filterResponse = storedTokens.tokens.filter {
|
||||||
getL2CompatibilityTokenComparison(it, currencyRawId)
|
getL2CompatibilityTokenComparison(it, currencyRawId.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
responseCurrenciesFactory.createCurrencies(
|
responseCurrenciesFactory.createCurrencies(
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ internal class DefaultQuotesRepository(
|
||||||
private val mutex = Mutex()
|
private val mutex = Mutex()
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@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>(
|
return appPreferencesStore.getObject<CurrenciesResponse.Currency>(
|
||||||
key = PreferencesKeys.SELECTED_APP_CURRENCY_KEY,
|
key = PreferencesKeys.SELECTED_APP_CURRENCY_KEY,
|
||||||
)
|
)
|
||||||
|
|
@ -51,7 +51,7 @@ internal class DefaultQuotesRepository(
|
||||||
.flowOn(dispatchers.io)
|
.flowOn(dispatchers.io)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.ID>) {
|
override suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.RawID>) {
|
||||||
withContext(dispatchers.io) {
|
withContext(dispatchers.io) {
|
||||||
val selectedAppCurrency = requireNotNull(
|
val selectedAppCurrency = requireNotNull(
|
||||||
value = appPreferencesStore.getObjectSyncOrNull<CurrenciesResponse.Currency>(
|
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) {
|
return withContext(dispatchers.io) {
|
||||||
val selectedAppCurrency = requireNotNull(
|
val selectedAppCurrency = requireNotNull(
|
||||||
value = appPreferencesStore.getObjectSyncOrNull<CurrenciesResponse.Currency>(
|
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) {
|
return withContext(dispatchers.io) {
|
||||||
val setOfCurrencyId = setOf(currencyId)
|
val setOfCurrencyId = setOf(currencyId)
|
||||||
val quote = quotesStore.getSync(setOfCurrencyId).firstOrNull()
|
val quote = quotesStore.getSync(setOfCurrencyId).firstOrNull()
|
||||||
|
|
@ -90,7 +90,7 @@ internal class DefaultQuotesRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun fetchExpiredQuotes(
|
private suspend fun fetchExpiredQuotes(
|
||||||
currenciesIds: Set<CryptoCurrency.ID>,
|
currenciesIds: Set<CryptoCurrency.RawID>,
|
||||||
appCurrencyId: String,
|
appCurrencyId: String,
|
||||||
refresh: Boolean,
|
refresh: Boolean,
|
||||||
) {
|
) {
|
||||||
|
|
@ -110,15 +110,17 @@ internal class DefaultQuotesRepository(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun fetchQuotes(rawCurrenciesIds: Set<String>, appCurrencyId: String) {
|
private suspend fun fetchQuotes(rawCurrenciesIds: Set<CryptoCurrency.RawID>, appCurrencyId: String) {
|
||||||
val replacementIdsResult = quotesUnsupportedCurrenciesAdapter.replaceUnsupportedCurrencies(rawCurrenciesIds)
|
val replacementIdsResult = quotesUnsupportedCurrenciesAdapter.replaceUnsupportedCurrencies(
|
||||||
|
rawCurrenciesIds.map { it.value }.toSet(),
|
||||||
|
)
|
||||||
val response = safeApiCallWithTimeout(
|
val response = safeApiCallWithTimeout(
|
||||||
call = {
|
call = {
|
||||||
val coinIds = replacementIdsResult.idsForRequest.joinToString(separator = ",")
|
val coinIds = replacementIdsResult.idsForRequest.joinToString(separator = ",")
|
||||||
tangemTechApi.getQuotes(appCurrencyId, coinIds).bind()
|
tangemTechApi.getQuotes(appCurrencyId, coinIds).bind()
|
||||||
},
|
},
|
||||||
onError = { error ->
|
onError = { error ->
|
||||||
cacheRegistry.invalidate(rawCurrenciesIds.map(::getQuoteCacheKey))
|
cacheRegistry.invalidate(rawCurrenciesIds.map { getQuoteCacheKey(it) })
|
||||||
|
|
||||||
throw error
|
throw error
|
||||||
},
|
},
|
||||||
|
|
@ -132,21 +134,20 @@ internal class DefaultQuotesRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun filterExpiredCurrenciesIds(
|
private suspend fun filterExpiredCurrenciesIds(
|
||||||
currenciesIds: Set<CryptoCurrency.ID>,
|
currenciesIds: Set<CryptoCurrency.RawID>,
|
||||||
refresh: Boolean,
|
refresh: Boolean,
|
||||||
): Set<String> {
|
): Set<CryptoCurrency.RawID> {
|
||||||
return currenciesIds.fold(hashSetOf()) { acc, currencyId ->
|
return currenciesIds.fold(hashSetOf()) { acc, currencyId ->
|
||||||
val rawCurrencyId = currencyId.rawCurrencyId
|
if (currencyId !in acc) {
|
||||||
if (rawCurrencyId != null && rawCurrencyId !in acc) {
|
|
||||||
cacheRegistry.invokeOnExpire(
|
cacheRegistry.invokeOnExpire(
|
||||||
key = getQuoteCacheKey(rawCurrencyId),
|
key = getQuoteCacheKey(currencyId),
|
||||||
skipCache = refresh,
|
skipCache = refresh,
|
||||||
block = { acc.add(rawCurrencyId) },
|
block = { acc.add(currencyId) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
acc
|
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 com.tangem.utils.converter.Converter
|
||||||
import java.math.BigDecimal
|
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>> {
|
internal class QuotesConverter : Converter<QuotesConverterValue, Set<Quote>> {
|
||||||
|
|
||||||
override fun convert(value: QuotesConverterValue): Set<Quote> {
|
override fun convert(value: QuotesConverterValue): Set<Quote> {
|
||||||
val (setOfCurrencyId, setOfStoredQuote) = value
|
val (setOfCurrencyId, setOfStoredQuote) = value
|
||||||
return setOfCurrencyId.mapTo(hashSetOf()) { id ->
|
return setOfCurrencyId.mapTo(hashSetOf()) { id ->
|
||||||
setOfStoredQuote.find { id.rawCurrencyId == it.rawCurrencyId }
|
setOfStoredQuote.find { id.value == it.rawCurrencyId }
|
||||||
?.let(::convertExistStoredQuote)
|
?.let(::convertExistStoredQuote)
|
||||||
?: Quote.Empty(id.rawCurrencyId)
|
?: Quote.Empty(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -23,7 +23,7 @@ internal class QuotesConverter : Converter<QuotesConverterValue, Set<Quote>> {
|
||||||
val (rawCurrencyId, responseQuote) = storedQuote
|
val (rawCurrencyId, responseQuote) = storedQuote
|
||||||
|
|
||||||
return Quote.Value(
|
return Quote.Value(
|
||||||
rawCurrencyId = rawCurrencyId,
|
rawCurrencyId = CryptoCurrency.RawID(rawCurrencyId),
|
||||||
fiatRate = responseQuote.price ?: BigDecimal.ZERO,
|
fiatRate = responseQuote.price ?: BigDecimal.ZERO,
|
||||||
priceChange = (responseQuote.priceChange24h ?: BigDecimal.ZERO).movePointLeft(2),
|
priceChange = (responseQuote.priceChange24h ?: BigDecimal.ZERO).movePointLeft(2),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,7 @@ class DefaultWalletManagersFacade(
|
||||||
symbol = currency.symbol,
|
symbol = currency.symbol,
|
||||||
contractAddress = currency.contractAddress,
|
contractAddress = currency.contractAddress,
|
||||||
decimals = currency.decimals,
|
decimals = currency.decimals,
|
||||||
id = currency.id.rawCurrencyId,
|
id = currency.id.rawCurrencyId?.value,
|
||||||
)
|
)
|
||||||
TransactionHistoryRequest.FilterType.Contract(blockchainToken)
|
TransactionHistoryRequest.FilterType.Contract(blockchainToken)
|
||||||
}
|
}
|
||||||
|
|
@ -255,7 +255,7 @@ class DefaultWalletManagersFacade(
|
||||||
symbol = currency.symbol,
|
symbol = currency.symbol,
|
||||||
contractAddress = currency.contractAddress,
|
contractAddress = currency.contractAddress,
|
||||||
decimals = currency.decimals,
|
decimals = currency.decimals,
|
||||||
id = currency.id.rawCurrencyId,
|
id = currency.id.rawCurrencyId?.value,
|
||||||
)
|
)
|
||||||
TransactionHistoryRequest.FilterType.Contract(blockchainToken)
|
TransactionHistoryRequest.FilterType.Contract(blockchainToken)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.tangem.domain.walletmanager.model
|
package com.tangem.domain.walletmanager.model
|
||||||
|
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
sealed class CryptoCurrencyAmount {
|
sealed class CryptoCurrencyAmount {
|
||||||
|
|
@ -9,7 +10,7 @@ sealed class CryptoCurrencyAmount {
|
||||||
data class Coin(override val value: BigDecimal) : CryptoCurrencyAmount()
|
data class Coin(override val value: BigDecimal) : CryptoCurrencyAmount()
|
||||||
|
|
||||||
data class Token(
|
data class Token(
|
||||||
val tokenId: String?,
|
val tokenId: CryptoCurrency.RawID?,
|
||||||
val tokenContractAddress: String,
|
val tokenContractAddress: String,
|
||||||
override val value: BigDecimal,
|
override val value: BigDecimal,
|
||||||
) : CryptoCurrencyAmount()
|
) : CryptoCurrencyAmount()
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ internal class CryptoCurrencyTypeConverter : Converter<CryptoCurrency, CryptoCur
|
||||||
symbol = value.symbol,
|
symbol = value.symbol,
|
||||||
contractAddress = value.contractAddress,
|
contractAddress = value.contractAddress,
|
||||||
decimals = value.decimals,
|
decimals = value.decimals,
|
||||||
id = value.id.rawCurrencyId,
|
id = value.id.rawCurrencyId?.value,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ internal class SdkTokenConverter : Converter<CryptoCurrency.Token, SdkToken> {
|
||||||
|
|
||||||
override fun convert(value: CryptoCurrency.Token): SdkToken {
|
override fun convert(value: CryptoCurrency.Token): SdkToken {
|
||||||
return SdkToken(
|
return SdkToken(
|
||||||
id = value.id.rawCurrencyId,
|
id = value.id.rawCurrencyId?.value,
|
||||||
name = value.name,
|
name = value.name,
|
||||||
symbol = value.symbol,
|
symbol = value.symbol,
|
||||||
contractAddress = value.contractAddress,
|
contractAddress = value.contractAddress,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package com.tangem.domain.walletmanager.utils
|
||||||
|
|
||||||
import com.tangem.blockchain.common.*
|
import com.tangem.blockchain.common.*
|
||||||
import com.tangem.blockchainsdk.utils.amountToCreateAccount
|
import com.tangem.blockchainsdk.utils.amountToCreateAccount
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.domain.walletmanager.model.Address
|
import com.tangem.domain.walletmanager.model.Address
|
||||||
import com.tangem.domain.walletmanager.model.CryptoCurrencyAmount
|
import com.tangem.domain.walletmanager.model.CryptoCurrencyAmount
|
||||||
import com.tangem.domain.walletmanager.model.CryptoCurrencyTransaction
|
import com.tangem.domain.walletmanager.model.CryptoCurrencyTransaction
|
||||||
|
|
@ -84,7 +85,11 @@ internal class UpdateWalletManagerResultFactory {
|
||||||
val demoAmounts = hashSetOf<CryptoCurrencyAmount>(CryptoCurrencyAmount.Coin(amountValue))
|
val demoAmounts = hashSetOf<CryptoCurrencyAmount>(CryptoCurrencyAmount.Coin(amountValue))
|
||||||
|
|
||||||
return tokens.mapTo(demoAmounts) { token ->
|
return tokens.mapTo(demoAmounts) { token ->
|
||||||
CryptoCurrencyAmount.Token(token.id, token.contractAddress, amountValue)
|
CryptoCurrencyAmount.Token(
|
||||||
|
tokenId = token.id?.let { CryptoCurrency.RawID(it) },
|
||||||
|
tokenContractAddress = token.contractAddress,
|
||||||
|
value = amountValue,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,7 +112,7 @@ internal class UpdateWalletManagerResultFactory {
|
||||||
private fun createCurrencyAmount(amount: Amount): CryptoCurrencyAmount? {
|
private fun createCurrencyAmount(amount: Amount): CryptoCurrencyAmount? {
|
||||||
return when (val type = amount.type) {
|
return when (val type = amount.type) {
|
||||||
is AmountType.Token -> CryptoCurrencyAmount.Token(
|
is AmountType.Token -> CryptoCurrencyAmount.Token(
|
||||||
tokenId = type.token.id,
|
tokenId = type.token.id?.let { CryptoCurrency.RawID(it) },
|
||||||
tokenContractAddress = type.token.contractAddress,
|
tokenContractAddress = type.token.contractAddress,
|
||||||
value = getCurrencyAmountValue(amount) ?: return null,
|
value = getCurrencyAmountValue(amount) ?: return null,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ class SaveManagedTokensUseCase(
|
||||||
is ManagedCryptoCurrency.SourceNetwork.Default -> customTokensRepository.createToken(
|
is ManagedCryptoCurrency.SourceNetwork.Default -> customTokensRepository.createToken(
|
||||||
managedCryptoCurrency = token,
|
managedCryptoCurrency = token,
|
||||||
sourceNetwork = sourceNetwork,
|
sourceNetwork = sourceNetwork,
|
||||||
rawId = token.id.value,
|
rawId = CryptoCurrency.RawID(token.id.value),
|
||||||
)
|
)
|
||||||
is ManagedCryptoCurrency.SourceNetwork.Main -> customTokensRepository.createCoin(
|
is ManagedCryptoCurrency.SourceNetwork.Main -> customTokensRepository.createCoin(
|
||||||
userWalletId = userWalletId,
|
userWalletId = userWalletId,
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ interface CustomTokensRepository {
|
||||||
suspend fun createToken(
|
suspend fun createToken(
|
||||||
managedCryptoCurrency: ManagedCryptoCurrency.Token,
|
managedCryptoCurrency: ManagedCryptoCurrency.Token,
|
||||||
sourceNetwork: ManagedCryptoCurrency.SourceNetwork.Default,
|
sourceNetwork: ManagedCryptoCurrency.SourceNetwork.Default,
|
||||||
rawId: String?,
|
rawId: CryptoCurrency.RawID?,
|
||||||
): CryptoCurrency.Token
|
): CryptoCurrency.Token
|
||||||
|
|
||||||
suspend fun createCustomToken(
|
suspend fun createCustomToken(
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
api(projects.domain.tokens.models)
|
||||||
implementation(projects.domain.core)
|
implementation(projects.domain.core)
|
||||||
|
|
||||||
implementation(deps.kotlin.serialization)
|
implementation(deps.kotlin.serialization)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
package com.tangem.domain.markets
|
package com.tangem.domain.markets
|
||||||
|
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
data class TokenMarket(
|
data class TokenMarket(
|
||||||
val id: String,
|
val id: CryptoCurrency.RawID,
|
||||||
val name: String,
|
val name: String,
|
||||||
val symbol: String,
|
val symbol: String,
|
||||||
val marketRating: Int?,
|
val marketRating: Int?,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
package com.tangem.domain.markets
|
package com.tangem.domain.markets
|
||||||
|
|
||||||
import com.tangem.domain.core.serialization.SerializedBigDecimal
|
import com.tangem.domain.core.serialization.SerializedBigDecimal
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class TokenMarketParams(
|
data class TokenMarketParams(
|
||||||
val id: String,
|
val id: CryptoCurrency.RawID,
|
||||||
val name: String,
|
val name: String,
|
||||||
val symbol: String,
|
val symbol: String,
|
||||||
val tokenQuotes: Quotes,
|
val tokenQuotes: Quotes,
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,10 @@ class GetCurrencyQuotesUseCase(
|
||||||
interval: PriceChangeInterval,
|
interval: PriceChangeInterval,
|
||||||
refresh: Boolean,
|
refresh: Boolean,
|
||||||
): Flow<Option<Quote.Value>> {
|
): Flow<Option<Quote.Value>> {
|
||||||
|
val rawId = currencyID.rawCurrencyId ?: return flowOf(None)
|
||||||
|
|
||||||
return quotesRepository.getQuotesUpdates(
|
return quotesRepository.getQuotesUpdates(
|
||||||
currenciesIds = setOf(currencyID),
|
currenciesIds = setOf(rawId),
|
||||||
refresh = refresh,
|
refresh = refresh,
|
||||||
).map { it.filterIsInstance<Quote.Value>().firstOrNull().toOption() }.catch { emit(None) }
|
).map { it.filterIsInstance<Quote.Value>().firstOrNull().toOption() }.catch { emit(None) }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package com.tangem.domain.markets
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get token exchanges use case
|
* Get token exchanges use case
|
||||||
|
|
@ -12,7 +13,7 @@ class GetTokenExchangesUseCase(
|
||||||
private val marketsTokenRepository: MarketsTokenRepository,
|
private val marketsTokenRepository: MarketsTokenRepository,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(tokenId: String): Either<Throwable, List<TokenMarketExchange>> {
|
suspend operator fun invoke(tokenId: CryptoCurrency.RawID): Either<Throwable, List<TokenMarketExchange>> {
|
||||||
return Either.catch { marketsTokenRepository.getTokenExchanges(tokenId = tokenId) }
|
return Either.catch { marketsTokenRepository.getTokenExchanges(tokenId = tokenId) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,13 +3,14 @@ package com.tangem.domain.markets
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
|
||||||
class GetTokenFullQuotesUseCase(
|
class GetTokenFullQuotesUseCase(
|
||||||
private val marketsTokenRepository: MarketsTokenRepository,
|
private val marketsTokenRepository: MarketsTokenRepository,
|
||||||
) {
|
) {
|
||||||
suspend operator fun invoke(
|
suspend operator fun invoke(
|
||||||
appCurrency: AppCurrency,
|
appCurrency: AppCurrency,
|
||||||
tokenId: String,
|
tokenId: CryptoCurrency.RawID,
|
||||||
tokenSymbol: String,
|
tokenSymbol: String,
|
||||||
): Either<Unit, TokenQuotes> {
|
): Either<Unit, TokenQuotes> {
|
||||||
return Either.catch {
|
return Either.catch {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.tangem.domain.markets
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.utils.SupportedLanguages
|
import com.tangem.utils.SupportedLanguages
|
||||||
|
|
||||||
class GetTokenMarketInfoUseCase(
|
class GetTokenMarketInfoUseCase(
|
||||||
|
|
@ -11,7 +12,7 @@ class GetTokenMarketInfoUseCase(
|
||||||
|
|
||||||
suspend operator fun invoke(
|
suspend operator fun invoke(
|
||||||
appCurrency: AppCurrency,
|
appCurrency: AppCurrency,
|
||||||
tokenId: String,
|
tokenId: CryptoCurrency.RawID,
|
||||||
tokenSymbol: String,
|
tokenSymbol: String,
|
||||||
): Either<Unit, TokenMarketInfo> {
|
): Either<Unit, TokenMarketInfo> {
|
||||||
return Either.catch {
|
return Either.catch {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.tangem.domain.markets
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
|
||||||
class GetTokenPriceChartUseCase(
|
class GetTokenPriceChartUseCase(
|
||||||
private val marketsTokenRepository: MarketsTokenRepository,
|
private val marketsTokenRepository: MarketsTokenRepository,
|
||||||
|
|
@ -11,7 +12,7 @@ class GetTokenPriceChartUseCase(
|
||||||
suspend operator fun invoke(
|
suspend operator fun invoke(
|
||||||
appCurrency: AppCurrency,
|
appCurrency: AppCurrency,
|
||||||
interval: PriceChangeInterval,
|
interval: PriceChangeInterval,
|
||||||
tokenId: String,
|
tokenId: CryptoCurrency.RawID,
|
||||||
tokenSymbol: String,
|
tokenSymbol: String,
|
||||||
preview: Boolean,
|
preview: Boolean,
|
||||||
): Either<Unit, TokenChart> {
|
): Either<Unit, TokenChart> {
|
||||||
|
|
|
||||||
|
|
@ -15,25 +15,29 @@ interface MarketsTokenRepository {
|
||||||
suspend fun getChart(
|
suspend fun getChart(
|
||||||
fiatCurrencyCode: String,
|
fiatCurrencyCode: String,
|
||||||
interval: PriceChangeInterval,
|
interval: PriceChangeInterval,
|
||||||
tokenId: String,
|
tokenId: CryptoCurrency.RawID,
|
||||||
tokenSymbol: String,
|
tokenSymbol: String,
|
||||||
): TokenChart
|
): TokenChart
|
||||||
|
|
||||||
suspend fun getChartPreview(
|
suspend fun getChartPreview(
|
||||||
fiatCurrencyCode: String,
|
fiatCurrencyCode: String,
|
||||||
interval: PriceChangeInterval,
|
interval: PriceChangeInterval,
|
||||||
tokenId: String,
|
tokenId: CryptoCurrency.RawID,
|
||||||
tokenSymbol: String,
|
tokenSymbol: String,
|
||||||
): TokenChart
|
): TokenChart
|
||||||
|
|
||||||
suspend fun getTokenInfo(
|
suspend fun getTokenInfo(
|
||||||
fiatCurrencyCode: String,
|
fiatCurrencyCode: String,
|
||||||
tokenId: String,
|
tokenId: CryptoCurrency.RawID,
|
||||||
tokenSymbol: String,
|
tokenSymbol: String,
|
||||||
languageCode: String,
|
languageCode: String,
|
||||||
): TokenMarketInfo
|
): TokenMarketInfo
|
||||||
|
|
||||||
suspend fun getTokenQuotes(fiatCurrencyCode: String, tokenId: String, tokenSymbol: String): TokenQuotes
|
suspend fun getTokenQuotes(
|
||||||
|
fiatCurrencyCode: String,
|
||||||
|
tokenId: CryptoCurrency.RawID,
|
||||||
|
tokenSymbol: String,
|
||||||
|
): TokenQuotes
|
||||||
|
|
||||||
suspend fun createCryptoCurrency(
|
suspend fun createCryptoCurrency(
|
||||||
userWalletId: UserWalletId,
|
userWalletId: UserWalletId,
|
||||||
|
|
@ -46,5 +50,5 @@ interface MarketsTokenRepository {
|
||||||
*
|
*
|
||||||
* @param tokenId token id
|
* @param tokenId token id
|
||||||
*/
|
*/
|
||||||
suspend fun getTokenExchanges(tokenId: String): List<TokenMarketExchange>
|
suspend fun getTokenExchanges(tokenId: CryptoCurrency.RawID): List<TokenMarketExchange>
|
||||||
}
|
}
|
||||||
|
|
@ -21,8 +21,6 @@ data class Yield(
|
||||||
val preferredValidators: List<Validator>
|
val preferredValidators: List<Validator>
|
||||||
get() = validators.filter { it.preferred }
|
get() = validators.filter { it.preferred }
|
||||||
|
|
||||||
fun getCurrentToken(rawCurrencyId: String?) = tokens.firstOrNull { rawCurrencyId == it.coinGeckoId } ?: token
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Status(
|
data class Status(
|
||||||
val enter: Boolean,
|
val enter: Boolean,
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ sealed class CryptoCurrency {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Represents a raw cryptocurrency ID. If it is a custom token, the value will be `null`. */
|
/** Represents a raw cryptocurrency ID. If it is a custom token, the value will be `null`. */
|
||||||
val rawCurrencyId: String? get() = (suffix as? Suffix.RawID)?.rawId
|
val rawCurrencyId: RawID? get() = (suffix as? Suffix.RawID)?.rawId?.let { RawID(it) }
|
||||||
|
|
||||||
val contractAddress: String? get() = (suffix as? Suffix.RawID)?.contractAddress
|
val contractAddress: String? get() = (suffix as? Suffix.RawID)?.contractAddress
|
||||||
|
|
||||||
|
|
@ -196,6 +196,16 @@ sealed class CryptoCurrency {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a raw cryptocurrency ID. Used for backend calls.
|
||||||
|
* Use with caution, as it does not provide the same level of uniqueness as [ID].
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
@JvmInline
|
||||||
|
value class RawID(val value: String) {
|
||||||
|
override fun toString(): String = value
|
||||||
|
}
|
||||||
|
|
||||||
protected fun checkProperties() {
|
protected fun checkProperties() {
|
||||||
require(name.isNotBlank()) { "Crypto currency name must not be blank" }
|
require(name.isNotBlank()) { "Crypto currency name must not be blank" }
|
||||||
require(symbol.isNotBlank()) { "Crypto currency symbol must not be blank" }
|
require(symbol.isNotBlank()) { "Crypto currency symbol must not be blank" }
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ import java.math.BigDecimal
|
||||||
|
|
||||||
sealed interface Quote {
|
sealed interface Quote {
|
||||||
|
|
||||||
val rawCurrencyId: String?
|
val rawCurrencyId: CryptoCurrency.RawID
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents unknown financial information for a specific cryptocurrency.
|
* Represents unknown financial information for a specific cryptocurrency.
|
||||||
*
|
*
|
||||||
* @property rawCurrencyId The raw cryptocurrency ID. If it is a custom token, the value will be `null`.
|
* @property rawCurrencyId The raw cryptocurrency ID.
|
||||||
*/
|
*/
|
||||||
data class Empty(override val rawCurrencyId: String?) : Quote
|
data class Empty(override val rawCurrencyId: CryptoCurrency.RawID) : Quote
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents financial information for a specific cryptocurrency, including its fiat exchange rate and price change.
|
* Represents financial information for a specific cryptocurrency, including its fiat exchange rate and price change.
|
||||||
|
|
@ -21,7 +21,7 @@ sealed interface Quote {
|
||||||
* @property priceChange The price change for the cryptocurrency.
|
* @property priceChange The price change for the cryptocurrency.
|
||||||
*/
|
*/
|
||||||
data class Value(
|
data class Value(
|
||||||
override val rawCurrencyId: String,
|
override val rawCurrencyId: CryptoCurrency.RawID,
|
||||||
val fiatRate: BigDecimal,
|
val fiatRate: BigDecimal,
|
||||||
val priceChange: BigDecimal,
|
val priceChange: BigDecimal,
|
||||||
) : Quote
|
) : Quote
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.tangem.domain.tokens.model.staking
|
||||||
|
|
||||||
|
import com.tangem.domain.staking.model.stakekit.Yield
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
|
||||||
|
fun Yield.getCurrentToken(rawCurrencyId: CryptoCurrency.RawID?) =
|
||||||
|
tokens.firstOrNull { rawCurrencyId?.value == it.coinGeckoId } ?: token
|
||||||
|
|
@ -37,7 +37,7 @@ class FetchCardTokenListUseCase(
|
||||||
}
|
}
|
||||||
val fetchQuotes = async {
|
val fetchQuotes = async {
|
||||||
fetchQuotes(
|
fetchQuotes(
|
||||||
currenciesIds = currencies.mapTo(destination = hashSetOf(), transform = CryptoCurrency::id),
|
currenciesIds = currencies.mapNotNullTo(destination = hashSetOf()) { it.id.rawCurrencyId },
|
||||||
refresh = refresh,
|
refresh = refresh,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -79,7 +79,7 @@ class FetchCardTokenListUseCase(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean) {
|
private suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.RawID>, refresh: Boolean) {
|
||||||
catch(
|
catch(
|
||||||
block = { quotesRepository.getQuotesSync(currenciesIds, refresh) },
|
block = { quotesRepository.getQuotesSync(currenciesIds, refresh) },
|
||||||
catch = { /* Ignore error */ },
|
catch = { /* Ignore error */ },
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ class FetchCurrencyStatusUseCase(
|
||||||
|
|
||||||
private suspend fun Raise<CurrencyStatusError>.fetchQuote(currencyId: CryptoCurrency.ID, refresh: Boolean) {
|
private suspend fun Raise<CurrencyStatusError>.fetchQuote(currencyId: CryptoCurrency.ID, refresh: Boolean) {
|
||||||
catch(
|
catch(
|
||||||
block = { quotesRepository.getQuotesSync(setOf(currencyId), refresh) },
|
block = { quotesRepository.getQuotesSync(setOfNotNull(currencyId.rawCurrencyId), refresh) },
|
||||||
) {
|
) {
|
||||||
raise(CurrencyStatusError.DataError(it))
|
raise(CurrencyStatusError.DataError(it))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,10 @@ class FetchTokenListUseCase(
|
||||||
|
|
||||||
private suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean) {
|
private suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean) {
|
||||||
catch(
|
catch(
|
||||||
block = { quotesRepository.getQuotesSync(currenciesIds, refresh) },
|
block = {
|
||||||
|
val rawIds = currenciesIds.mapNotNull { it.rawCurrencyId }.toSet()
|
||||||
|
quotesRepository.getQuotesSync(rawIds, refresh)
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
/* Ignore error */
|
/* Ignore error */
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ class GetAllWalletsCryptoCurrencyStatusesUseCase(
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
operator fun invoke(
|
operator fun invoke(
|
||||||
currencyRawId: String,
|
currencyRawId: CryptoCurrency.RawID,
|
||||||
): Flow<Map<UserWallet, List<Either<CurrencyStatusError, CryptoCurrencyStatus>>>> {
|
): Flow<Map<UserWallet, List<Either<CurrencyStatusError, CryptoCurrencyStatus>>>> {
|
||||||
return currenciesRepository.getAllWalletsCryptoCurrencies(currencyRawId)
|
return currenciesRepository.getAllWalletsCryptoCurrencies(currencyRawId)
|
||||||
.flatMapLatest { userWalletsWithCurrencies: Map<UserWallet, List<CryptoCurrency>> ->
|
.flatMapLatest { userWalletsWithCurrencies: Map<UserWallet, List<CryptoCurrency>> ->
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ class RefreshMultiCurrencyWalletQuotesUseCase(
|
||||||
private suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.ID>) {
|
private suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.ID>) {
|
||||||
catch(
|
catch(
|
||||||
block = {
|
block = {
|
||||||
quotesRepository.fetchQuotes(currenciesIds)
|
quotesRepository.fetchQuotes(currenciesIds.mapNotNullTo(hashSetOf(), CryptoCurrency.ID::rawCurrencyId))
|
||||||
},
|
},
|
||||||
catch = { /* Ignore error */ },
|
catch = { /* Ignore error */ },
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ internal class CurrenciesStatusesLceOperations(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getQuotes(tokensIds: NonEmptySet<CryptoCurrency.ID>): Flow<Either<TokenListError, Set<Quote>>> {
|
private fun getQuotes(tokensIds: NonEmptySet<CryptoCurrency.ID>): Flow<Either<TokenListError, Set<Quote>>> {
|
||||||
return quotesRepository.getQuotesUpdates(tokensIds)
|
return quotesRepository.getQuotesUpdates(tokensIds.mapNotNull { it.rawCurrencyId }.toSet())
|
||||||
.map<Set<Quote>, Either<TokenListError, Set<Quote>>> { it.right() }
|
.map<Set<Quote>, Either<TokenListError, Set<Quote>>> { it.right() }
|
||||||
.retryWhen { cause, _ ->
|
.retryWhen { cause, _ ->
|
||||||
emit(TokenListError.DataError(cause).left())
|
emit(TokenListError.DataError(cause).left())
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,8 @@ internal class CurrenciesStatusesOperations(
|
||||||
currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWalletId).toNonEmptyListOrNull()
|
currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWalletId).toNonEmptyListOrNull()
|
||||||
?: return emptyList<CryptoCurrencyStatus>().right()
|
?: return emptyList<CryptoCurrencyStatus>().right()
|
||||||
val (networks, currenciesIds) = getIds(nonEmptyCurrencies)
|
val (networks, currenciesIds) = getIds(nonEmptyCurrencies)
|
||||||
val quotes = quotesRepository.getQuotesSync(currenciesIds, false).right()
|
val rawIds = currenciesIds.mapNotNull { it.rawCurrencyId }.toSet()
|
||||||
|
val quotes = quotesRepository.getQuotesSync(rawIds, false).right()
|
||||||
val networkStatuses =
|
val networkStatuses =
|
||||||
networksRepository.getNetworkStatusesSync(userWalletId, networks, false).right()
|
networksRepository.getNetworkStatusesSync(userWalletId, networks, false).right()
|
||||||
val yieldBalances = getYieldBalancesSync(nonEmptyCurrencies)
|
val yieldBalances = getYieldBalancesSync(nonEmptyCurrencies)
|
||||||
|
|
@ -60,7 +61,8 @@ internal class CurrenciesStatusesOperations(
|
||||||
} else {
|
} else {
|
||||||
currenciesRepository.getMultiCurrencyWalletCurrency(userWalletId, cryptoCurrencyId)
|
currenciesRepository.getMultiCurrencyWalletCurrency(userWalletId, cryptoCurrencyId)
|
||||||
}
|
}
|
||||||
val quotes = quotesRepository.getQuoteSync(cryptoCurrencyId)?.right() ?: Error.EmptyQuotes.left()
|
val quote = cryptoCurrencyId.rawCurrencyId?.let { quotesRepository.getQuoteSync(it) }?.right()
|
||||||
|
?: Error.EmptyQuotes.left()
|
||||||
val networkStatuses =
|
val networkStatuses =
|
||||||
networksRepository.getNetworkStatusesSync(
|
networksRepository.getNetworkStatusesSync(
|
||||||
userWalletId,
|
userWalletId,
|
||||||
|
|
@ -71,7 +73,7 @@ internal class CurrenciesStatusesOperations(
|
||||||
}.right()
|
}.right()
|
||||||
val yieldBalances = getYieldBalanceSync(currency)
|
val yieldBalances = getYieldBalanceSync(currency)
|
||||||
|
|
||||||
return createCurrencyStatus(currency, quotes, networkStatuses, yieldBalances)
|
return createCurrencyStatus(currency, quote, networkStatuses, yieldBalances)
|
||||||
},
|
},
|
||||||
catch = { raise(Error.DataError(it)) },
|
catch = { raise(Error.DataError(it)) },
|
||||||
)
|
)
|
||||||
|
|
@ -104,7 +106,10 @@ internal class CurrenciesStatusesOperations(
|
||||||
catch = { raise(Error.DataError(it)) },
|
catch = { raise(Error.DataError(it)) },
|
||||||
)
|
)
|
||||||
val quotes = catch(
|
val quotes = catch(
|
||||||
block = { quotesRepository.getQuoteSync(currency.id)?.right() ?: Error.EmptyQuotes.left() },
|
block = {
|
||||||
|
currency.id.rawCurrencyId?.let { quotesRepository.getQuoteSync(it) }
|
||||||
|
?.right() ?: Error.EmptyQuotes.left()
|
||||||
|
},
|
||||||
catch = { Error.DataError(it).left() },
|
catch = { Error.DataError(it).left() },
|
||||||
)
|
)
|
||||||
val networkStatus = catch(
|
val networkStatus = catch(
|
||||||
|
|
@ -176,8 +181,7 @@ internal class CurrenciesStatusesOperations(
|
||||||
fun getCurrencyStatusFlow(
|
fun getCurrencyStatusFlow(
|
||||||
currency: CryptoCurrency,
|
currency: CryptoCurrency,
|
||||||
includeQuotes: Boolean = true,
|
includeQuotes: Boolean = true,
|
||||||
): Flow<Either<Error,
|
): Flow<Either<Error, CryptoCurrencyStatus>> {
|
||||||
CryptoCurrencyStatus,>,> {
|
|
||||||
val (networks, currenciesIds) = getIds(nonEmptyListOf(currency))
|
val (networks, currenciesIds) = getIds(nonEmptyListOf(currency))
|
||||||
|
|
||||||
val quoteFlow = if (includeQuotes) {
|
val quoteFlow = if (includeQuotes) {
|
||||||
|
|
@ -344,7 +348,8 @@ internal class CurrenciesStatusesOperations(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getQuotes(tokensIds: NonEmptySet<CryptoCurrency.ID>): Flow<Either<Error, Set<Quote>>> {
|
private fun getQuotes(tokensIds: NonEmptySet<CryptoCurrency.ID>): Flow<Either<Error, Set<Quote>>> {
|
||||||
return quotesRepository.getQuotesUpdates(tokensIds)
|
val rawIds = tokensIds.mapNotNull { it.rawCurrencyId }.toSet()
|
||||||
|
return quotesRepository.getQuotesUpdates(rawIds)
|
||||||
.map<Set<Quote>, Either<Error, Set<Quote>>> { quotes ->
|
.map<Set<Quote>, Either<Error, Set<Quote>>> { quotes ->
|
||||||
if (quotes.isEmpty()) Error.EmptyQuotes.left() else quotes.right()
|
if (quotes.isEmpty()) Error.EmptyQuotes.left() else quotes.right()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ internal class CurrencyStatusOperations(
|
||||||
val yieldBalanceData = yieldBalance as? YieldBalance.Data
|
val yieldBalanceData = yieldBalance as? YieldBalance.Data
|
||||||
val isCurrentAddressStaking = yieldBalanceData?.address == status.address.defaultAddress.value
|
val isCurrentAddressStaking = yieldBalanceData?.address == status.address.defaultAddress.value
|
||||||
val filteredTokenBalances = yieldBalanceData?.balance?.items?.filter {
|
val filteredTokenBalances = yieldBalanceData?.balance?.items?.filter {
|
||||||
it.token.coinGeckoId == currency.id.rawCurrencyId
|
it.token.coinGeckoId == currency.id.rawCurrencyId?.value
|
||||||
}
|
}
|
||||||
val currentYieldBalance = if (isCurrentAddressStaking && filteredTokenBalances?.isNotEmpty() == true) {
|
val currentYieldBalance = if (isCurrentAddressStaking && filteredTokenBalances?.isNotEmpty() == true) {
|
||||||
yieldBalanceData.copy(
|
yieldBalanceData.copy(
|
||||||
|
|
|
||||||
|
|
@ -248,7 +248,7 @@ interface CurrenciesRepository {
|
||||||
): CryptoCurrency.Token
|
): CryptoCurrency.Token
|
||||||
|
|
||||||
/** Get crypto currencies by [currencyRawId] from all user wallets */
|
/** Get crypto currencies by [currencyRawId] from all user wallets */
|
||||||
fun getAllWalletsCryptoCurrencies(currencyRawId: String): Flow<Map<UserWallet, List<CryptoCurrency>>>
|
fun getAllWalletsCryptoCurrencies(currencyRawId: CryptoCurrency.RawID): Flow<Map<UserWallet, List<CryptoCurrency>>>
|
||||||
|
|
||||||
fun isNetworkFeeZero(userWalletId: UserWalletId, network: Network): Boolean
|
fun isNetworkFeeZero(userWalletId: UserWalletId, network: Network): Boolean
|
||||||
}
|
}
|
||||||
|
|
@ -17,7 +17,7 @@ interface QuotesRepository {
|
||||||
* @param currenciesIds The unique identifiers of the cryptocurrencies for which quotes are to be retrieved.
|
* @param currenciesIds The unique identifiers of the cryptocurrencies for which quotes are to be retrieved.
|
||||||
* @return A [Flow] emitting a set of quotes corresponding to the specified cryptocurrencies.
|
* @return A [Flow] emitting a set of quotes corresponding to the specified cryptocurrencies.
|
||||||
*/
|
*/
|
||||||
fun getQuotesUpdates(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean = false): Flow<Set<Quote>>
|
fun getQuotesUpdates(currenciesIds: Set<CryptoCurrency.RawID>, refresh: Boolean = false): Flow<Set<Quote>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves quotes for a set of specified cryptocurrencies, identified by their unique IDs.
|
* Retrieves quotes for a set of specified cryptocurrencies, identified by their unique IDs.
|
||||||
|
|
@ -28,9 +28,9 @@ interface QuotesRepository {
|
||||||
* @param refresh A boolean flag indicating whether the data should be refreshed.
|
* @param refresh A boolean flag indicating whether the data should be refreshed.
|
||||||
* @return A [Flow] emitting a set of quotes corresponding to the specified cryptocurrencies.
|
* @return A [Flow] emitting a set of quotes corresponding to the specified cryptocurrencies.
|
||||||
*/
|
*/
|
||||||
suspend fun getQuotesSync(currenciesIds: Set<CryptoCurrency.ID>, refresh: Boolean): Set<Quote>
|
suspend fun getQuotesSync(currenciesIds: Set<CryptoCurrency.RawID>, refresh: Boolean): Set<Quote>
|
||||||
|
|
||||||
suspend fun getQuoteSync(currencyId: CryptoCurrency.ID): Quote?
|
suspend fun getQuoteSync(currencyId: CryptoCurrency.RawID): Quote?
|
||||||
|
|
||||||
suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.ID>)
|
suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.RawID>)
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.tangem.domain.tokens.mock
|
package com.tangem.domain.tokens.mock
|
||||||
|
|
||||||
import arrow.core.nonEmptySetOf
|
import arrow.core.nonEmptySetOf
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.domain.tokens.model.Quote
|
import com.tangem.domain.tokens.model.Quote
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
|
@ -67,11 +68,10 @@ internal object MockQuotes {
|
||||||
priceChange = BigDecimal("-0.10"),
|
priceChange = BigDecimal("-0.10"),
|
||||||
)
|
)
|
||||||
|
|
||||||
val quote11 = Quote.Empty(null)
|
val quote11 = Quote.Empty(CryptoCurrency.RawID("null"))
|
||||||
val quote12 = Quote.Empty("null")
|
|
||||||
|
|
||||||
val quotes = nonEmptySetOf(
|
val quotes = nonEmptySetOf(
|
||||||
quote1, quote2, quote3, quote4, quote5, quote6, quote7, quote8, quote9, quote10,
|
quote1, quote2, quote3, quote4, quote5, quote6, quote7, quote8, quote9, quote10,
|
||||||
quote11, quote12,
|
quote11,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -153,7 +153,9 @@ internal class MockCurrenciesRepository(
|
||||||
error("not implemented")
|
error("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAllWalletsCryptoCurrencies(currencyRawId: String): Flow<Map<UserWallet, List<CryptoCurrency>>> {
|
override fun getAllWalletsCryptoCurrencies(
|
||||||
|
currencyRawId: CryptoCurrency.RawID,
|
||||||
|
): Flow<Map<UserWallet, List<CryptoCurrency>>> {
|
||||||
return emptyFlow()
|
return emptyFlow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,18 +13,18 @@ internal class MockQuotesRepository(
|
||||||
private val quotes: Flow<Either<DataError, Set<Quote>>>,
|
private val quotes: Flow<Either<DataError, Set<Quote>>>,
|
||||||
) : QuotesRepository {
|
) : QuotesRepository {
|
||||||
|
|
||||||
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 quotes.map { it.getOrElse { e -> throw e } }
|
return quotes.map { it.getOrElse { e -> throw e } }
|
||||||
}
|
}
|
||||||
|
|
||||||
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 getQuotesUpdates(currenciesIds).first()
|
return getQuotesUpdates(currenciesIds).first()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getQuoteSync(currencyId: CryptoCurrency.ID): Quote {
|
override suspend fun getQuoteSync(currencyId: CryptoCurrency.RawID): Quote {
|
||||||
return quotes.map { it.getOrElse { e -> throw e } }.first()
|
return quotes.map { it.getOrElse { e -> throw e } }.first()
|
||||||
.first { it.rawCurrencyId == currencyId.rawCurrencyId }
|
.first { it.rawCurrencyId == currencyId }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.ID>) {}
|
override suspend fun fetchQuotes(currenciesIds: Set<CryptoCurrency.RawID>) {}
|
||||||
}
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ import com.tangem.core.decompose.context.child
|
||||||
import com.tangem.core.decompose.model.getOrCreateModel
|
import com.tangem.core.decompose.model.getOrCreateModel
|
||||||
import com.tangem.core.ui.res.LocalMainBottomSheetColor
|
import com.tangem.core.ui.res.LocalMainBottomSheetColor
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.features.markets.details.MarketsTokenDetailsComponent
|
import com.tangem.features.markets.details.MarketsTokenDetailsComponent
|
||||||
import com.tangem.features.markets.details.MarketsTokenDetailsComponent.Params
|
import com.tangem.features.markets.details.MarketsTokenDetailsComponent.Params
|
||||||
import com.tangem.features.markets.details.impl.analytics.MarketDetailsAnalyticsEvent
|
import com.tangem.features.markets.details.impl.analytics.MarketDetailsAnalyticsEvent
|
||||||
|
|
@ -38,7 +39,7 @@ internal class DefaultMarketsTokenDetailsComponent @AssistedInject constructor(
|
||||||
// applying l2 compatibility
|
// applying l2 compatibility
|
||||||
private val updatedParams = params.copy(
|
private val updatedParams = params.copy(
|
||||||
token = params.token.copy(
|
token = params.token.copy(
|
||||||
id = getTokenIdIfL2Network(params.token.id),
|
id = CryptoCurrency.RawID(getTokenIdIfL2Network(params.token.id.value)),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
private val analyticsParams = params.analyticsParams
|
private val analyticsParams = params.analyticsParams
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
modelScope.launch {
|
modelScope.launch {
|
||||||
sendFeedbackEmailUseCase(
|
sendFeedbackEmailUseCase(
|
||||||
type = FeedbackEmailType.CurrencyDescriptionError(
|
type = FeedbackEmailType.CurrencyDescriptionError(
|
||||||
currencyId = params.token.id,
|
currencyId = params.token.id.value,
|
||||||
currencyName = params.token.name,
|
currencyName = params.token.name,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import com.tangem.domain.tokens.GetAllWalletsCryptoCurrencyStatusesUseCase
|
||||||
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
||||||
import com.tangem.domain.tokens.GetWalletTotalBalanceUseCase
|
import com.tangem.domain.tokens.GetWalletTotalBalanceUseCase
|
||||||
import com.tangem.domain.tokens.error.TokenListError
|
import com.tangem.domain.tokens.error.TokenListError
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.domain.tokens.model.TotalFiatBalance
|
import com.tangem.domain.tokens.model.TotalFiatBalance
|
||||||
import com.tangem.domain.wallets.models.UserWallet
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.domain.wallets.models.UserWalletId
|
import com.tangem.domain.wallets.models.UserWalletId
|
||||||
|
|
@ -37,7 +38,7 @@ internal class PortfolioDataLoader @Inject constructor(
|
||||||
|
|
||||||
/** Load data by [currencyRawId] */
|
/** Load data by [currencyRawId] */
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
fun load(currencyRawId: String): Flow<PortfolioData> {
|
fun load(currencyRawId: CryptoCurrency.RawID): Flow<PortfolioData> {
|
||||||
return combine(
|
return combine(
|
||||||
flow = getAllWalletsCryptoCurrenciesData(currencyRawId = currencyRawId),
|
flow = getAllWalletsCryptoCurrenciesData(currencyRawId = currencyRawId),
|
||||||
flow2 = getSelectedAppCurrencyFlow(),
|
flow2 = getSelectedAppCurrencyFlow(),
|
||||||
|
|
@ -62,7 +63,7 @@ internal class PortfolioDataLoader @Inject constructor(
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
private fun getAllWalletsCryptoCurrenciesData(
|
private fun getAllWalletsCryptoCurrenciesData(
|
||||||
currencyRawId: String,
|
currencyRawId: CryptoCurrency.RawID,
|
||||||
): Flow<Map<UserWallet, List<PortfolioData.CryptoCurrencyData>>> {
|
): Flow<Map<UserWallet, List<PortfolioData.CryptoCurrencyData>>> {
|
||||||
return getAllWalletsCryptoCurrencyStatusesUseCase(currencyRawId)
|
return getAllWalletsCryptoCurrencyStatusesUseCase(currencyRawId)
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ internal class SelectNetworkUMConverter(
|
||||||
|
|
||||||
override fun convert(value: TokenMarketParams): SelectNetworkUM {
|
override fun convert(value: TokenMarketParams): SelectNetworkUM {
|
||||||
return SelectNetworkUM(
|
return SelectNetworkUM(
|
||||||
tokenId = value.id,
|
tokenId = value.id.value,
|
||||||
iconUrl = value.imageUrl,
|
iconUrl = value.imageUrl,
|
||||||
tokenName = value.name,
|
tokenName = value.name,
|
||||||
tokenCurrencySymbol = value.symbol,
|
tokenCurrencySymbol = value.symbol,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
import com.tangem.domain.markets.GetMarketsTokenListFlowUseCase
|
import com.tangem.domain.markets.GetMarketsTokenListFlowUseCase
|
||||||
import com.tangem.domain.markets.TokenMarket
|
import com.tangem.domain.markets.TokenMarket
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.features.markets.entry.BottomSheetState
|
import com.tangem.features.markets.entry.BottomSheetState
|
||||||
import com.tangem.features.markets.tokenlist.impl.analytics.MarketsListAnalyticsEvent
|
import com.tangem.features.markets.tokenlist.impl.analytics.MarketsListAnalyticsEvent
|
||||||
import com.tangem.features.markets.tokenlist.impl.model.statemanager.MarketsListBatchFlowManager
|
import com.tangem.features.markets.tokenlist.impl.model.statemanager.MarketsListBatchFlowManager
|
||||||
|
|
@ -48,7 +49,7 @@ internal class MarketsListModel @Inject constructor(
|
||||||
initialValue = AppCurrency.Default,
|
initialValue = AppCurrency.Default,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val visibleItemIds = MutableStateFlow<List<String>>(emptyList())
|
private val visibleItemIds = MutableStateFlow<List<CryptoCurrency.RawID>>(emptyList())
|
||||||
|
|
||||||
private val marketsListUMStateManager = MarketsListUMStateManager(
|
private val marketsListUMStateManager = MarketsListUMStateManager(
|
||||||
currentVisibleIds = Provider { visibleItemIds.value },
|
currentVisibleIds = Provider { visibleItemIds.value },
|
||||||
|
|
@ -72,8 +73,8 @@ internal class MarketsListModel @Inject constructor(
|
||||||
getMarketsTokenListFlowUseCase = getMarketsTokenListFlowUseCase,
|
getMarketsTokenListFlowUseCase = getMarketsTokenListFlowUseCase,
|
||||||
batchFlowType = GetMarketsTokenListFlowUseCase.BatchFlowType.Search,
|
batchFlowType = GetMarketsTokenListFlowUseCase.BatchFlowType.Search,
|
||||||
currentAppCurrency = Provider { currentAppCurrency.value },
|
currentAppCurrency = Provider { currentAppCurrency.value },
|
||||||
currentTrendInterval = Provider { marketsListUMStateManager.selectedInterval }, // FIXME fix on backend
|
currentTrendInterval = Provider { marketsListUMStateManager.selectedInterval },
|
||||||
currentSortByType = Provider { SortByTypeUM.Rating }, // FIXME maybe fix on backend
|
currentSortByType = Provider { SortByTypeUM.Rating },
|
||||||
currentSearchText = Provider { marketsListUMStateManager.searchQuery },
|
currentSearchText = Provider { marketsListUMStateManager.searchQuery },
|
||||||
modelScope = modelScope,
|
modelScope = modelScope,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package com.tangem.features.markets.tokenlist.impl.model.statemanager
|
||||||
|
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
import com.tangem.domain.markets.*
|
import com.tangem.domain.markets.*
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.features.markets.tokenlist.impl.model.converters.MarketsTokenItemConverter
|
import com.tangem.features.markets.tokenlist.impl.model.converters.MarketsTokenItemConverter
|
||||||
import com.tangem.features.markets.tokenlist.impl.model.utils.logAction
|
import com.tangem.features.markets.tokenlist.impl.model.utils.logAction
|
||||||
import com.tangem.features.markets.tokenlist.impl.model.utils.logStatus
|
import com.tangem.features.markets.tokenlist.impl.model.utils.logStatus
|
||||||
|
|
@ -302,7 +303,7 @@ internal class MarketsListBatchFlowManager(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getBatchKeysByItemIds(ids: List<String>): Set<Int> {
|
fun getBatchKeysByItemIds(ids: List<CryptoCurrency.RawID>): Set<Int> {
|
||||||
val currentData = batchFlow.state.value.data
|
val currentData = batchFlow.state.value.data
|
||||||
|
|
||||||
return currentData
|
return currentData
|
||||||
|
|
@ -311,7 +312,7 @@ internal class MarketsListBatchFlowManager(
|
||||||
.toSet()
|
.toSet()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTokenById(id: String): TokenMarket? {
|
fun getTokenById(id: CryptoCurrency.RawID): TokenMarket? {
|
||||||
return batchFlow.state.value.data.map { it.data }.flatten().find { it.id == id }
|
return batchFlow.state.value.data.map { it.data }.flatten().find { it.id == id }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import com.tangem.core.ui.components.fields.entity.SearchBarUM
|
||||||
import com.tangem.core.ui.event.consumedEvent
|
import com.tangem.core.ui.event.consumedEvent
|
||||||
import com.tangem.core.ui.event.triggeredEvent
|
import com.tangem.core.ui.event.triggeredEvent
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.features.markets.impl.R
|
import com.tangem.features.markets.impl.R
|
||||||
import com.tangem.features.markets.tokenlist.impl.ui.state.*
|
import com.tangem.features.markets.tokenlist.impl.ui.state.*
|
||||||
import com.tangem.utils.Provider
|
import com.tangem.utils.Provider
|
||||||
|
|
@ -18,9 +19,9 @@ import kotlinx.coroutines.flow.update
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
internal class MarketsListUMStateManager(
|
internal class MarketsListUMStateManager(
|
||||||
private val currentVisibleIds: Provider<List<String>>,
|
private val currentVisibleIds: Provider<List<CryptoCurrency.RawID>>,
|
||||||
private val onLoadMoreUiItems: () -> Unit,
|
private val onLoadMoreUiItems: () -> Unit,
|
||||||
private val visibleItemsChanged: (itemsKeys: List<String>) -> Unit,
|
private val visibleItemsChanged: (itemsKeys: List<CryptoCurrency.RawID>) -> Unit,
|
||||||
private val onRetryButtonClicked: () -> Unit,
|
private val onRetryButtonClicked: () -> Unit,
|
||||||
private val onTokenClick: (MarketsListItemUM) -> Unit,
|
private val onTokenClick: (MarketsListItemUM) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import com.tangem.core.ui.extensions.stringResourceSafe
|
||||||
import com.tangem.core.ui.res.LocalMainBottomSheetColor
|
import com.tangem.core.ui.res.LocalMainBottomSheetColor
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.features.markets.entry.BottomSheetState
|
import com.tangem.features.markets.entry.BottomSheetState
|
||||||
import com.tangem.features.markets.impl.R
|
import com.tangem.features.markets.impl.R
|
||||||
import com.tangem.features.markets.tokenlist.impl.ui.components.MarketsListLazyColumn
|
import com.tangem.features.markets.tokenlist.impl.ui.components.MarketsListLazyColumn
|
||||||
|
|
@ -298,7 +299,7 @@ private fun Preview() {
|
||||||
items = MarketChartListItemPreviewDataProvider().values
|
items = MarketChartListItemPreviewDataProvider().values
|
||||||
.flatMap { item -> List(size = 10) { item } }
|
.flatMap { item -> List(size = 10) { item } }
|
||||||
.mapIndexed { index, item ->
|
.mapIndexed { index, item ->
|
||||||
item.copy(id = index.toString())
|
item.copy(id = CryptoCurrency.RawID(index.toString()))
|
||||||
}
|
}
|
||||||
.toImmutableList(),
|
.toImmutableList(),
|
||||||
showUnder100kTokensNotification = false,
|
showUnder100kTokensNotification = false,
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import com.tangem.core.ui.event.EventEffect
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.features.markets.impl.R
|
import com.tangem.features.markets.impl.R
|
||||||
import com.tangem.features.markets.tokenlist.impl.ui.state.ListUM
|
import com.tangem.features.markets.tokenlist.impl.ui.state.ListUM
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -88,7 +89,7 @@ internal fun MarketsListLazyColumn(
|
||||||
is ListUM.Content -> {
|
is ListUM.Content -> {
|
||||||
items(
|
items(
|
||||||
items = state.items,
|
items = state.items,
|
||||||
key = { it.id + TOKEN_LAZY_LIST_ID_SEPARATOR + it.marketCap.toString() },
|
key = { it.id.value + TOKEN_LAZY_LIST_ID_SEPARATOR + it.marketCap.toString() },
|
||||||
) { item ->
|
) { item ->
|
||||||
MarketsListItem(
|
MarketsListItem(
|
||||||
model = item,
|
model = item,
|
||||||
|
|
@ -193,6 +194,7 @@ private fun VisibleItemsTracker(listState: LazyListState, state: ListUM) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
listState.layoutInfo.visibleItemsInfo.mapNotNull {
|
listState.layoutInfo.visibleItemsInfo.mapNotNull {
|
||||||
(it.key as? String)?.split(TOKEN_LAZY_LIST_ID_SEPARATOR)?.first()
|
(it.key as? String)?.split(TOKEN_LAZY_LIST_ID_SEPARATOR)?.first()
|
||||||
|
?.let { rawId -> CryptoCurrency.RawID(rawId) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,14 @@ package com.tangem.features.markets.tokenlist.impl.ui.preview
|
||||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||||
import com.tangem.common.ui.charts.state.MarketChartRawData
|
import com.tangem.common.ui.charts.state.MarketChartRawData
|
||||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.features.markets.tokenlist.impl.ui.state.MarketsListItemUM
|
import com.tangem.features.markets.tokenlist.impl.ui.state.MarketsListItemUM
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
internal class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvider<MarketsListItemUM>(
|
internal class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvider<MarketsListItemUM>(
|
||||||
collection = listOf(
|
collection = listOf(
|
||||||
MarketsListItemUM(
|
MarketsListItemUM(
|
||||||
id = "1",
|
id = CryptoCurrency.RawID("1"),
|
||||||
name = "Bitcoin",
|
name = "Bitcoin",
|
||||||
currencySymbol = "BTC",
|
currencySymbol = "BTC",
|
||||||
iconUrl = "",
|
iconUrl = "",
|
||||||
|
|
@ -25,7 +26,7 @@ internal class MarketChartListItemPreviewDataProvider : CollectionPreviewParamet
|
||||||
isUnder100kMarketCap = false,
|
isUnder100kMarketCap = false,
|
||||||
),
|
),
|
||||||
MarketsListItemUM(
|
MarketsListItemUM(
|
||||||
id = "1",
|
id = CryptoCurrency.RawID("1"),
|
||||||
name = "Bitcoin",
|
name = "Bitcoin",
|
||||||
currencySymbol = "BTC",
|
currencySymbol = "BTC",
|
||||||
iconUrl = null,
|
iconUrl = null,
|
||||||
|
|
@ -38,7 +39,7 @@ internal class MarketChartListItemPreviewDataProvider : CollectionPreviewParamet
|
||||||
isUnder100kMarketCap = false,
|
isUnder100kMarketCap = false,
|
||||||
),
|
),
|
||||||
MarketsListItemUM(
|
MarketsListItemUM(
|
||||||
id = "1",
|
id = CryptoCurrency.RawID("1"),
|
||||||
name = "Bitcoin Bitcoin Bitcoin Bitcoin Bitcoin Bitcoin Bitcoin",
|
name = "Bitcoin Bitcoin Bitcoin Bitcoin Bitcoin Bitcoin Bitcoin",
|
||||||
currencySymbol = "BTC",
|
currencySymbol = "BTC",
|
||||||
iconUrl = null,
|
iconUrl = null,
|
||||||
|
|
@ -53,7 +54,7 @@ internal class MarketChartListItemPreviewDataProvider : CollectionPreviewParamet
|
||||||
isUnder100kMarketCap = false,
|
isUnder100kMarketCap = false,
|
||||||
),
|
),
|
||||||
MarketsListItemUM(
|
MarketsListItemUM(
|
||||||
id = "1",
|
id = CryptoCurrency.RawID("1"),
|
||||||
name = "Bitcoin",
|
name = "Bitcoin",
|
||||||
currencySymbol = "BTC",
|
currencySymbol = "BTC",
|
||||||
iconUrl = null,
|
iconUrl = null,
|
||||||
|
|
@ -68,7 +69,7 @@ internal class MarketChartListItemPreviewDataProvider : CollectionPreviewParamet
|
||||||
isUnder100kMarketCap = false,
|
isUnder100kMarketCap = false,
|
||||||
),
|
),
|
||||||
MarketsListItemUM(
|
MarketsListItemUM(
|
||||||
id = "1",
|
id = CryptoCurrency.RawID("1"),
|
||||||
name = "Bitcoin",
|
name = "Bitcoin",
|
||||||
currencySymbol = "BTC",
|
currencySymbol = "BTC",
|
||||||
iconUrl = null,
|
iconUrl = null,
|
||||||
|
|
@ -83,7 +84,7 @@ internal class MarketChartListItemPreviewDataProvider : CollectionPreviewParamet
|
||||||
isUnder100kMarketCap = false,
|
isUnder100kMarketCap = false,
|
||||||
),
|
),
|
||||||
MarketsListItemUM(
|
MarketsListItemUM(
|
||||||
id = "1",
|
id = CryptoCurrency.RawID("1"),
|
||||||
name = "Bitcoin",
|
name = "Bitcoin",
|
||||||
currencySymbol = "BTC",
|
currencySymbol = "BTC",
|
||||||
iconUrl = null,
|
iconUrl = null,
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@ import androidx.compose.runtime.Immutable
|
||||||
import com.tangem.common.ui.charts.state.MarketChartLook
|
import com.tangem.common.ui.charts.state.MarketChartLook
|
||||||
import com.tangem.common.ui.charts.state.MarketChartRawData
|
import com.tangem.common.ui.charts.state.MarketChartRawData
|
||||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class MarketsListItemUM(
|
data class MarketsListItemUM(
|
||||||
val id: String,
|
val id: CryptoCurrency.RawID,
|
||||||
val name: String,
|
val name: String,
|
||||||
val currencySymbol: String,
|
val currencySymbol: String,
|
||||||
val iconUrl: String?,
|
val iconUrl: String?,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import com.tangem.core.ui.components.fields.entity.SearchBarUM
|
||||||
import com.tangem.core.ui.event.StateEvent
|
import com.tangem.core.ui.event.StateEvent
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
|
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||||
import com.tangem.features.markets.impl.R
|
import com.tangem.features.markets.impl.R
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
|
|
@ -44,7 +45,7 @@ sealed class ListUM {
|
||||||
val showUnder100kTokensNotification: Boolean,
|
val showUnder100kTokensNotification: Boolean,
|
||||||
val showUnder100kTokensNotificationWasHidden: Boolean,
|
val showUnder100kTokensNotificationWasHidden: Boolean,
|
||||||
val loadMore: () -> Unit,
|
val loadMore: () -> Unit,
|
||||||
val visibleIdsChanged: (List<String>) -> Unit,
|
val visibleIdsChanged: (List<CryptoCurrency.RawID>) -> Unit,
|
||||||
val onShowTokensUnder100kClicked: () -> Unit,
|
val onShowTokensUnder100kClicked: () -> Unit,
|
||||||
val triggerScrollReset: StateEvent<Unit>,
|
val triggerScrollReset: StateEvent<Unit>,
|
||||||
val onItemClick: (MarketsListItemUM) -> Unit,
|
val onItemClick: (MarketsListItemUM) -> Unit,
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,7 @@ internal class FeeStateFactory(
|
||||||
symbol = this.token.symbol,
|
symbol = this.token.symbol,
|
||||||
contractAddress = this.token.contractAddress,
|
contractAddress = this.token.contractAddress,
|
||||||
decimals = this.token.decimals,
|
decimals = this.token.decimals,
|
||||||
id = this.token.id.rawCurrencyId,
|
id = this.token.id.rawCurrencyId?.value,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ internal class RewardsValidatorStateConverter(
|
||||||
formattedCryptoAmount = cryptoAmount,
|
formattedCryptoAmount = cryptoAmount,
|
||||||
fiatAmount = fiatValue,
|
fiatAmount = fiatValue,
|
||||||
formattedFiatAmount = formattedFiatAmount,
|
formattedFiatAmount = formattedFiatAmount,
|
||||||
rawCurrencyId = cryptoCurrency.id.rawCurrencyId,
|
rawCurrencyId = cryptoCurrency.id.rawCurrencyId?.value,
|
||||||
pendingActions = balance.pendingActions.toPersistentList(),
|
pendingActions = balance.pendingActions.toPersistentList(),
|
||||||
isClickable = true,
|
isClickable = true,
|
||||||
type = balance.type,
|
type = balance.type,
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||||
import com.tangem.domain.staking.model.stakekit.transaction.ActionParams
|
import com.tangem.domain.staking.model.stakekit.transaction.ActionParams
|
||||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate
|
import com.tangem.domain.staking.model.stakekit.transaction.StakingGasEstimate
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||||
|
import com.tangem.domain.tokens.model.staking.getCurrentToken
|
||||||
import com.tangem.domain.transaction.error.GetFeeError
|
import com.tangem.domain.transaction.error.GetFeeError
|
||||||
import com.tangem.domain.transaction.usecase.GetFeeUseCase
|
import com.tangem.domain.transaction.usecase.GetFeeUseCase
|
||||||
import com.tangem.domain.wallets.models.UserWallet
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
|
||||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransactionStatus
|
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransactionStatus
|
||||||
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransactionType
|
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransactionType
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||||
|
import com.tangem.domain.tokens.model.staking.getCurrentToken
|
||||||
import com.tangem.domain.transaction.error.SendTransactionError
|
import com.tangem.domain.transaction.error.SendTransactionError
|
||||||
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
|
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
|
||||||
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
|
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
|
||||||
|
|
|
||||||
|
|
@ -2134,14 +2134,17 @@ internal class SwapInteractorImpl @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getQuotes(vararg ids: CryptoCurrency.ID): Map<CryptoCurrency.ID, Quote.Value> {
|
private suspend fun getQuotes(vararg ids: CryptoCurrency.ID): Map<CryptoCurrency.ID, Quote.Value> {
|
||||||
val set = ids.toSet().getQuotesOrEmpty(false).filterIsInstance<Quote.Value>()
|
val set = ids.mapNotNull { it.rawCurrencyId }
|
||||||
|
.toSet()
|
||||||
|
.getQuotesOrEmpty(false)
|
||||||
|
.filterIsInstance<Quote.Value>()
|
||||||
|
|
||||||
return ids
|
return ids
|
||||||
.mapNotNull { id -> set.find { it.rawCurrencyId == id.rawCurrencyId }?.let { id to it } }
|
.mapNotNull { id -> set.find { it.rawCurrencyId == id.rawCurrencyId }?.let { id to it } }
|
||||||
.toMap()
|
.toMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun Set<CryptoCurrency.ID>.getQuotesOrEmpty(refresh: Boolean): Set<Quote> {
|
private suspend fun Set<CryptoCurrency.RawID>.getQuotesOrEmpty(refresh: Boolean): Set<Quote> {
|
||||||
return try {
|
return try {
|
||||||
quotesRepository.getQuotesSync(this, refresh)
|
quotesRepository.getQuotesSync(this, refresh)
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,8 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
|
||||||
|
|
||||||
private suspend fun Set<CryptoCurrency.ID>.getQuotesOrEmpty(refresh: Boolean): Set<Quote> {
|
private suspend fun Set<CryptoCurrency.ID>.getQuotesOrEmpty(refresh: Boolean): Set<Quote> {
|
||||||
return try {
|
return try {
|
||||||
quotesRepository.getQuotesSync(this, refresh)
|
val rawIds = mapNotNull { it.rawCurrencyId }.toSet()
|
||||||
|
quotesRepository.getQuotesSync(rawIds, refresh)
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
emptySet()
|
emptySet()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue