From a2e602f1f1f9c8cdd6a89334254518d03598d11e Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 11 Aug 2023 15:03:22 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tangem/data/tokens/di/TokensDataModule.kt | 12 ++- .../repository/DefaultQuotesRepository.kt | 81 +++++++++++++++++++ .../tokens/repository/MockQuotesRepository.kt | 29 ------- .../data/tokens/utils/QuotesConverter.kt | 18 +++++ 4 files changed, 109 insertions(+), 31 deletions(-) create mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt delete mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt create mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/QuotesConverter.kt diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt index a5adf06ad8..7d62d72157 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt @@ -3,8 +3,9 @@ package com.tangem.data.tokens.di import com.tangem.data.common.cache.CacheRegistry import com.tangem.data.tokens.repository.DefaultCurrenciesRepository import com.tangem.data.tokens.repository.DefaultNetworksRepository -import com.tangem.data.tokens.repository.MockQuotesRepository +import com.tangem.data.tokens.repository.DefaultQuotesRepository import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.local.quote.QuotesStore import com.tangem.datasource.local.token.UserTokensStore import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.tokens.repository.CurrenciesRepository @@ -36,7 +37,14 @@ internal object TokensDataModule { @Provides @Singleton - fun provideQuotesRepository(): QuotesRepository = MockQuotesRepository() + fun provideQuotesRepository( + tangemTechApi: TangemTechApi, + quotesStore: QuotesStore, + cacheRegistry: CacheRegistry, + dispatchers: CoroutineDispatcherProvider, + ): QuotesRepository { + return DefaultQuotesRepository(tangemTechApi, quotesStore, cacheRegistry, dispatchers) + } @Provides @Singleton diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt new file mode 100644 index 0000000000..ea7fdbd998 --- /dev/null +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt @@ -0,0 +1,81 @@ +package com.tangem.data.tokens.repository + +import com.tangem.data.common.cache.CacheRegistry +import com.tangem.data.tokens.utils.QuotesConverter +import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.local.quote.QuotesStore +import com.tangem.domain.tokens.models.CryptoCurrency +import com.tangem.domain.tokens.models.Quote +import com.tangem.domain.tokens.repository.QuotesRepository +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import timber.log.Timber + +internal class DefaultQuotesRepository( + private val tangemTechApi: TangemTechApi, + private val quotesStore: QuotesStore, + private val cacheRegistry: CacheRegistry, + private val dispatchers: CoroutineDispatcherProvider, +) : QuotesRepository { + + private val quotesConverter = QuotesConverter() + + override fun getQuotes(currenciesIds: Set, refresh: Boolean): Flow> { + return channelFlow { + launch(dispatchers.io) { + quotesStore.get(currenciesIds) + .map(quotesConverter::convertSet) + .collect(::send) + } + + launch(dispatchers.io) { + fetchExpiredQuotes(currenciesIds, refresh) + } + } + } + + private suspend fun fetchExpiredQuotes(currenciesIds: Set, refresh: Boolean) { + val expiredCurrenciesIds = filterExpiredCurrenciesIds(currenciesIds, refresh) + if (expiredCurrenciesIds.isEmpty()) return + + fetchQuotes(expiredCurrenciesIds) + } + + private suspend fun fetchQuotes(rawCurrenciesIds: Set) { + try { + val response = tangemTechApi.getQuotes( + currencyId = "usd", // TODO: [REDACTED_JIRA] + coinIds = rawCurrenciesIds.joinToString(separator = ","), + ) + + quotesStore.store(response) + } catch (e: Throwable) { + Timber.e(e, "Unable to fetch quotes for: $rawCurrenciesIds") + throw e + } + } + + private suspend fun filterExpiredCurrenciesIds( + currenciesIds: Set, + refresh: Boolean, + ): Set { + return currenciesIds.fold(hashSetOf()) { acc, currencyId -> + val rawCurrencyId = currencyId.rawCurrencyId + + if (rawCurrencyId != null && rawCurrencyId !in acc) { + cacheRegistry.invokeOnExpire( + key = getQuoteCacheKey(rawCurrencyId), + skipCache = refresh, + block = { acc.add(rawCurrencyId) }, + ) + } + + acc + } + } + + private fun getQuoteCacheKey(rawCurrencyId: String): String = "quote_$rawCurrencyId" +} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt deleted file mode 100644 index 31e9c03dc5..0000000000 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.tangem.data.tokens.repository - -import com.tangem.domain.tokens.models.CryptoCurrency -import com.tangem.domain.tokens.models.Quote -import com.tangem.domain.tokens.repository.QuotesRepository -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.channelFlow -import java.math.BigDecimal -import kotlin.random.Random - -internal class MockQuotesRepository : QuotesRepository { - - override fun getQuotes(currenciesIds: Set, refresh: Boolean): Flow> { - return channelFlow { - val quotes = currenciesIds.mapNotNullTo(hashSetOf()) { id -> - Quote( - rawCurrencyId = id.rawCurrencyId ?: return@mapNotNullTo null, - fiatRate = BigDecimal.ZERO, - priceChange = BigDecimal.ZERO, - ) - } - - delay(Random.nextLong(from = 200, until = 2_000)) - - send(quotes) - } - } -} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/QuotesConverter.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/QuotesConverter.kt new file mode 100644 index 0000000000..b771150c67 --- /dev/null +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/QuotesConverter.kt @@ -0,0 +1,18 @@ +package com.tangem.data.tokens.utils + +import com.tangem.datasource.local.quote.model.StoredQuote +import com.tangem.domain.tokens.models.Quote +import com.tangem.utils.converter.Converter + +internal class QuotesConverter : Converter { + + override fun convert(value: StoredQuote): Quote { + val (rawCurrencyId, responseQuote) = value + + return Quote( + rawCurrencyId = rawCurrencyId, + fiatRate = responseQuote.price, + priceChange = responseQuote.priceChange.movePointLeft(2), + ) + } +} \ No newline at end of file