diff --git a/common/test/src/main/java/com/tangem/common/test/data/quote/QuoteResponseExt.kt b/common/test/src/main/java/com/tangem/common/test/data/quote/QuoteResponseExt.kt index ac570f6d81..5e294cc4c0 100644 --- a/common/test/src/main/java/com/tangem/common/test/data/quote/QuoteResponseExt.kt +++ b/common/test/src/main/java/com/tangem/common/test/data/quote/QuoteResponseExt.kt @@ -1,14 +1,14 @@ package com.tangem.common.test.data.quote import com.tangem.datasource.api.tangemTech.models.QuotesResponse -import com.tangem.datasource.local.quote.converter.QuoteConverter +import com.tangem.datasource.local.quote.converter.QuoteStatusConverter import com.tangem.domain.models.StatusSource -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus -fun QuotesResponse.Quote.toDomain(rawCurrencyId: String, source: StatusSource = StatusSource.ACTUAL): Quote { - return QuoteConverter(source = source).convert(value = mapOf(rawCurrencyId to this).entries.first()) +fun QuotesResponse.Quote.toDomain(rawCurrencyId: String, source: StatusSource = StatusSource.ACTUAL): QuoteStatus { + return QuoteStatusConverter(source = source).convert(value = mapOf(rawCurrencyId to this).entries.first()) } -fun Pair.toDomain(source: StatusSource = StatusSource.ACTUAL): Quote { - return QuoteConverter(source = source).convert(value = mapOf(this).entries.first()) +fun Pair.toDomain(source: StatusSource = StatusSource.ACTUAL): QuoteStatus { + return QuoteStatusConverter(source = source).convert(value = mapOf(this).entries.first()) } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/quote/converter/QuoteConverter.kt b/core/datasource/src/main/java/com/tangem/datasource/local/quote/converter/QuoteStatusConverter.kt similarity index 64% rename from core/datasource/src/main/java/com/tangem/datasource/local/quote/converter/QuoteConverter.kt rename to core/datasource/src/main/java/com/tangem/datasource/local/quote/converter/QuoteStatusConverter.kt index 555eb268f9..d3ee90a729 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/quote/converter/QuoteConverter.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/quote/converter/QuoteStatusConverter.kt @@ -3,21 +3,20 @@ package com.tangem.datasource.local.quote.converter import com.tangem.datasource.api.tangemTech.models.QuotesResponse import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import com.tangem.utils.converter.Converter import com.tangem.utils.extensions.orZero /** - * Converter from [QuotesResponse.Quote] to [Quote.Value] + * Converter from [QuotesResponse.Quote] to [QuoteStatus] * * @property source status source * [REDACTED_AUTHOR] */ -class QuoteConverter( +class QuoteStatusConverter( private val source: StatusSource, -) : - Converter, Quote.Value> { +) : Converter, QuoteStatus> { /** * Secondary constructor @@ -28,14 +27,16 @@ class QuoteConverter( source = if (isCached) StatusSource.CACHE else StatusSource.ACTUAL, ) - override fun convert(value: Map.Entry): Quote.Value { + override fun convert(value: Map.Entry): QuoteStatus { val (currencyId, quote) = value - return Quote.Value( + return QuoteStatus( rawCurrencyId = CryptoCurrency.RawID(currencyId), - fiatRate = quote.price.orZero(), - priceChange = quote.priceChange24h.orZero().movePointLeft(2), - source = source, + value = QuoteStatus.Data( + source = source, + fiatRate = quote.price.orZero(), + priceChange = quote.priceChange24h.orZero().movePointLeft(2), + ), ) } } \ No newline at end of file diff --git a/data/onramp/src/main/java/com/tangem/data/onramp/converters/HotCryptoCurrencyConverter.kt b/data/onramp/src/main/java/com/tangem/data/onramp/converters/HotCryptoCurrencyConverter.kt index a0cadbe035..c1ece25f69 100644 --- a/data/onramp/src/main/java/com/tangem/data/onramp/converters/HotCryptoCurrencyConverter.kt +++ b/data/onramp/src/main/java/com/tangem/data/onramp/converters/HotCryptoCurrencyConverter.kt @@ -11,7 +11,7 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.onramp.model.HotCryptoCurrency -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import com.tangem.utils.converter.Converter import java.math.BigDecimal @@ -40,7 +40,7 @@ internal class HotCryptoCurrencyConverter( return HotCryptoCurrency( cryptoCurrency = currency.setupIconUrl(id = rawId.value), - quote = createQuote( + quoteStatus = createQuote( fiatRate = value.currentPrice, priceChange = value.priceChangePercentage, rawCurrencyId = rawId, @@ -89,16 +89,18 @@ internal class HotCryptoCurrencyConverter( fiatRate: BigDecimal?, priceChange: BigDecimal?, rawCurrencyId: CryptoCurrency.RawID, - ): Quote { + ): QuoteStatus { return if (fiatRate != null && priceChange != null) { - Quote.Value( + QuoteStatus( rawCurrencyId = rawCurrencyId, - fiatRate = fiatRate, - priceChange = priceChange.movePointLeft(2), - source = StatusSource.ACTUAL, // It doesn't matter + value = QuoteStatus.Data( + fiatRate = fiatRate, + priceChange = priceChange.movePointLeft(2), + source = StatusSource.ACTUAL, // It doesn't matter + ), ) } else { - Quote.Empty(rawCurrencyId) + QuoteStatus(rawCurrencyId = rawCurrencyId) } } diff --git a/data/quotes/src/main/java/com/tangem/data/quotes/DefaultQuotesRepositoryV2.kt b/data/quotes/src/main/java/com/tangem/data/quotes/DefaultQuotesRepositoryV2.kt index 616b9cebc6..ed18015ad2 100644 --- a/data/quotes/src/main/java/com/tangem/data/quotes/DefaultQuotesRepositoryV2.kt +++ b/data/quotes/src/main/java/com/tangem/data/quotes/DefaultQuotesRepositoryV2.kt @@ -3,7 +3,7 @@ package com.tangem.data.quotes import com.tangem.data.quotes.store.QuotesStoreV2 import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.quotes.QuotesRepositoryV2 -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import javax.inject.Inject /** @@ -17,9 +17,10 @@ internal class DefaultQuotesRepositoryV2 @Inject constructor( private val quotesStore: QuotesStoreV2, ) : QuotesRepositoryV2 { - override suspend fun getMultiQuoteSyncOrNull(currenciesIds: Set): Set? { + override suspend fun getMultiQuoteSyncOrNull(currenciesIds: Set): Set? { return quotesStore.getAllSyncOrNull()?.mapTo(hashSetOf()) { - it.takeIf { it.rawCurrencyId in currenciesIds } ?: Quote.Empty(it.rawCurrencyId) + it.takeIf { it.rawCurrencyId in currenciesIds } + ?: QuoteStatus(rawCurrencyId = it.rawCurrencyId) } } } \ No newline at end of file diff --git a/data/quotes/src/main/java/com/tangem/data/quotes/multi/DefaultMultiQuoteUpdater.kt b/data/quotes/src/main/java/com/tangem/data/quotes/multi/DefaultMultiQuoteUpdater.kt index 06e492857e..8f0ae6297f 100644 --- a/data/quotes/src/main/java/com/tangem/data/quotes/multi/DefaultMultiQuoteUpdater.kt +++ b/data/quotes/src/main/java/com/tangem/data/quotes/multi/DefaultMultiQuoteUpdater.kt @@ -7,7 +7,7 @@ import com.tangem.datasource.appcurrency.AppCurrencyResponseStore import com.tangem.domain.core.utils.EitherFlow import com.tangem.domain.quotes.multi.MultiQuoteFetcher import com.tangem.domain.quotes.multi.MultiQuoteUpdater -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.saveIn @@ -61,7 +61,7 @@ internal class DefaultMultiQuoteUpdater @Inject constructor( .filterNotNull() .mapLatest { appCurrency -> val currenciesIds = quotesStore.getAllSyncOrNull().orEmpty() - .mapTo(destination = hashSetOf(), transform = Quote::rawCurrencyId) + .mapTo(destination = hashSetOf(), transform = QuoteStatus::rawCurrencyId) multiQuoteFetcher( params = MultiQuoteFetcher.Params(currenciesIds = currenciesIds, appCurrencyId = appCurrency.id), diff --git a/data/quotes/src/main/java/com/tangem/data/quotes/single/DefaultSingleQuoteProducer.kt b/data/quotes/src/main/java/com/tangem/data/quotes/single/DefaultSingleQuoteProducer.kt index d8450243b6..796d0dceac 100644 --- a/data/quotes/src/main/java/com/tangem/data/quotes/single/DefaultSingleQuoteProducer.kt +++ b/data/quotes/src/main/java/com/tangem/data/quotes/single/DefaultSingleQuoteProducer.kt @@ -2,7 +2,7 @@ package com.tangem.data.quotes.single import com.tangem.data.quotes.store.QuotesStoreV2 import com.tangem.domain.quotes.single.SingleQuoteProducer -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -24,9 +24,9 @@ internal class DefaultSingleQuoteProducer @AssistedInject constructor( private val dispatchers: CoroutineDispatcherProvider, ) : SingleQuoteProducer { - override val fallback: Quote = Quote.Empty(rawCurrencyId = params.rawCurrencyId) + override val fallback: QuoteStatus = QuoteStatus(rawCurrencyId = params.rawCurrencyId) - override fun produce(): Flow { + override fun produce(): Flow { return quotesStore.get() .mapNotNull { quotes -> quotes.firstOrNull { it.rawCurrencyId == params.rawCurrencyId } } .distinctUntilChanged() diff --git a/data/quotes/src/main/java/com/tangem/data/quotes/store/DefaultQuotesStoreV2.kt b/data/quotes/src/main/java/com/tangem/data/quotes/store/DefaultQuotesStoreV2.kt index ccad728a92..2158d4d6a8 100644 --- a/data/quotes/src/main/java/com/tangem/data/quotes/store/DefaultQuotesStoreV2.kt +++ b/data/quotes/src/main/java/com/tangem/data/quotes/store/DefaultQuotesStoreV2.kt @@ -3,10 +3,10 @@ package com.tangem.data.quotes.store import androidx.datastore.core.DataStore import com.tangem.datasource.api.tangemTech.models.QuotesResponse import com.tangem.datasource.local.datastore.RuntimeSharedStore -import com.tangem.datasource.local.quote.converter.QuoteConverter +import com.tangem.datasource.local.quote.converter.QuoteStatusConverter import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.extensions.addOrReplace import kotlinx.coroutines.CoroutineScope @@ -26,7 +26,7 @@ internal typealias CurrencyIdWithQuote = Map * @param dispatchers dispatchers */ internal class DefaultQuotesStoreV2( - private val runtimeStore: RuntimeSharedStore>, + private val runtimeStore: RuntimeSharedStore>, private val persistenceDataStore: DataStore, dispatchers: CoroutineDispatcherProvider, ) : QuotesStoreV2 { @@ -40,14 +40,14 @@ internal class DefaultQuotesStoreV2( if (cachedStatuses.isNullOrEmpty()) return@launch runtimeStore.store( - value = QuoteConverter(isCached = true).convertSet(input = cachedStatuses.entries), + value = QuoteStatusConverter(isCached = true).convertSet(input = cachedStatuses.entries), ) } } - override fun get(): Flow> = runtimeStore.get() + override fun get(): Flow> = runtimeStore.get() - override suspend fun getAllSyncOrNull(): Set? = runtimeStore.getSyncOrNull() + override suspend fun getAllSyncOrNull(): Set? = runtimeStore.getSyncOrNull() override suspend fun refresh(currenciesIds: Set) { updateStatusSourceInRuntime(currenciesIds = currenciesIds, source = StatusSource.CACHE) @@ -56,7 +56,7 @@ internal class DefaultQuotesStoreV2( override suspend fun storeActual(values: Map) { coroutineScope { launch { - val quotes = QuoteConverter(isCached = false).convertSet(input = values.entries) + val quotes = QuoteStatusConverter(isCached = false).convertSet(input = values.entries) storeInRuntimeStore(values = quotes) } launch { storeInPersistenceStore(values = values) } @@ -66,14 +66,14 @@ internal class DefaultQuotesStoreV2( override suspend fun storeError(currenciesIds: Set) { updateStatusSourceInRuntime( currenciesIds = currenciesIds, - ifNotFound = Quote::Empty, + ifNotFound = ::QuoteStatus, source = StatusSource.ONLY_CACHE, ) } private suspend fun updateStatusSourceInRuntime( currenciesIds: Set, - ifNotFound: (CryptoCurrency.RawID) -> Quote? = { null }, + ifNotFound: (CryptoCurrency.RawID) -> QuoteStatus? = { null }, source: StatusSource, ) { runtimeStore.update(default = emptySet()) { stored -> @@ -82,14 +82,14 @@ internal class DefaultQuotesStoreV2( ?: ifNotFound(id) ?: return@mapNotNullTo null - quote.copySealed(source = source) + quote.copy(value = quote.value.copySealed(source = source)) } stored.addOrReplace(items = updatedQuotes) { old, new -> old.rawCurrencyId == new.rawCurrencyId } } } - private suspend fun storeInRuntimeStore(values: Set) { + private suspend fun storeInRuntimeStore(values: Set) { runtimeStore.update(default = emptySet()) { saved -> saved.addOrReplace(items = values) { prev, new -> prev.rawCurrencyId == new.rawCurrencyId } } diff --git a/data/quotes/src/main/java/com/tangem/data/quotes/store/QuotesStoreV2.kt b/data/quotes/src/main/java/com/tangem/data/quotes/store/QuotesStoreV2.kt index 1fb13a9d5e..b29bb240af 100644 --- a/data/quotes/src/main/java/com/tangem/data/quotes/store/QuotesStoreV2.kt +++ b/data/quotes/src/main/java/com/tangem/data/quotes/store/QuotesStoreV2.kt @@ -2,17 +2,17 @@ package com.tangem.data.quotes.store import com.tangem.datasource.api.tangemTech.models.QuotesResponse import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import kotlinx.coroutines.flow.Flow -/** Store of [Quote]'es set */ +/** Store of [QuoteStatus]'es set */ internal interface QuotesStoreV2 { /** Get flow of quotes */ - fun get(): Flow> + fun get(): Flow> /** Get all quotes synchronously or null */ - suspend fun getAllSyncOrNull(): Set? + suspend fun getAllSyncOrNull(): Set? /** Refresh status of [currenciesIds] */ suspend fun refresh(currenciesIds: Set) diff --git a/data/quotes/src/test/java/com/tangem/data/quotes/multi/DefaultMultiQuoteFetcherTest.kt b/data/quotes/src/test/java/com/tangem/data/quotes/multi/DefaultMultiQuoteStatusFetcherTest.kt similarity index 99% rename from data/quotes/src/test/java/com/tangem/data/quotes/multi/DefaultMultiQuoteFetcherTest.kt rename to data/quotes/src/test/java/com/tangem/data/quotes/multi/DefaultMultiQuoteStatusFetcherTest.kt index 63fec8ab17..d49e65f597 100644 --- a/data/quotes/src/test/java/com/tangem/data/quotes/multi/DefaultMultiQuoteFetcherTest.kt +++ b/data/quotes/src/test/java/com/tangem/data/quotes/multi/DefaultMultiQuoteStatusFetcherTest.kt @@ -23,7 +23,7 @@ import java.math.BigDecimal /** [REDACTED_AUTHOR] */ -internal class DefaultMultiQuoteFetcherTest { +internal class DefaultMultiQuoteStatusFetcherTest { private val tangemTechApi = mockk(relaxed = true) private val appCurrencyResponseStore = mockk(relaxed = true) diff --git a/data/quotes/src/test/java/com/tangem/data/quotes/multi/DefaultMultiQuoteUpdaterTest.kt b/data/quotes/src/test/java/com/tangem/data/quotes/multi/DefaultMultiQuoteStatusUpdaterTest.kt similarity index 99% rename from data/quotes/src/test/java/com/tangem/data/quotes/multi/DefaultMultiQuoteUpdaterTest.kt rename to data/quotes/src/test/java/com/tangem/data/quotes/multi/DefaultMultiQuoteStatusUpdaterTest.kt index f14962f52b..d51f1c0460 100644 --- a/data/quotes/src/test/java/com/tangem/data/quotes/multi/DefaultMultiQuoteUpdaterTest.kt +++ b/data/quotes/src/test/java/com/tangem/data/quotes/multi/DefaultMultiQuoteStatusUpdaterTest.kt @@ -16,7 +16,7 @@ import org.junit.Test /** [REDACTED_AUTHOR] */ -internal class DefaultMultiQuoteUpdaterTest { +internal class DefaultMultiQuoteStatusUpdaterTest { private val appCurrencyResponseStore: AppCurrencyResponseStore = mockk() private val quotesStore: QuotesStoreV2 = mockk() diff --git a/data/quotes/src/test/java/com/tangem/data/quotes/single/DefaultSingleQuoteFetcherTest.kt b/data/quotes/src/test/java/com/tangem/data/quotes/single/DefaultSingleQuoteStatusFetcherTest.kt similarity index 99% rename from data/quotes/src/test/java/com/tangem/data/quotes/single/DefaultSingleQuoteFetcherTest.kt rename to data/quotes/src/test/java/com/tangem/data/quotes/single/DefaultSingleQuoteStatusFetcherTest.kt index a16f80b3d7..83108ee088 100644 --- a/data/quotes/src/test/java/com/tangem/data/quotes/single/DefaultSingleQuoteFetcherTest.kt +++ b/data/quotes/src/test/java/com/tangem/data/quotes/single/DefaultSingleQuoteStatusFetcherTest.kt @@ -21,7 +21,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Test import java.math.BigDecimal -internal class DefaultSingleQuoteFetcherTest { +internal class DefaultSingleQuoteStatusFetcherTest { private val tangemTechApi = mockk(relaxed = true) private val appCurrencyResponseStore = mockk(relaxed = true) diff --git a/data/quotes/src/test/java/com/tangem/data/quotes/single/DefaultSingleQuoteProducerTest.kt b/data/quotes/src/test/java/com/tangem/data/quotes/single/DefaultSingleQuoteStatusProducerTest.kt similarity index 79% rename from data/quotes/src/test/java/com/tangem/data/quotes/single/DefaultSingleQuoteProducerTest.kt rename to data/quotes/src/test/java/com/tangem/data/quotes/single/DefaultSingleQuoteStatusProducerTest.kt index ce3be77d28..774a1b9d9f 100644 --- a/data/quotes/src/test/java/com/tangem/data/quotes/single/DefaultSingleQuoteProducerTest.kt +++ b/data/quotes/src/test/java/com/tangem/data/quotes/single/DefaultSingleQuoteStatusProducerTest.kt @@ -6,7 +6,7 @@ import com.tangem.data.quotes.store.QuotesStoreV2 import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.quotes.single.SingleQuoteProducer -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import io.mockk.every import io.mockk.mockk @@ -19,7 +19,7 @@ import java.math.BigDecimal /** [REDACTED_AUTHOR] */ -internal class DefaultSingleQuoteProducerTest { +internal class DefaultSingleQuoteStatusProducerTest { private val params = SingleQuoteProducer.Params( rawCurrencyId = CryptoCurrency.RawID(value = "BTC"), @@ -35,11 +35,11 @@ internal class DefaultSingleQuoteProducerTest { @Test fun `test that flow is mapped for network from params`() = runTest { - val status = Quote.Empty(rawCurrencyId = params.rawCurrencyId) + val status = QuoteStatus(rawCurrencyId = params.rawCurrencyId) val storeQuote = flowOf( setOf( status, - Quote.Empty(rawCurrencyId = CryptoCurrency.RawID(value = "ETH")), + QuoteStatus(rawCurrencyId = CryptoCurrency.RawID(value = "ETH")), ), ) @@ -57,7 +57,7 @@ internal class DefaultSingleQuoteProducerTest { @Test fun `test that flow is updated if quote is updated`() = runTest { - val storeQuote = MutableSharedFlow>(replay = 2, extraBufferCapacity = 1) + val storeQuote = MutableSharedFlow>(replay = 2, extraBufferCapacity = 1) every { quotesStore.get() } returns storeQuote @@ -66,7 +66,7 @@ internal class DefaultSingleQuoteProducerTest { verify { quotesStore.get() } // first emit - val status = Quote.Empty(rawCurrencyId = params.rawCurrencyId) + val status = QuoteStatus(rawCurrencyId = params.rawCurrencyId) storeQuote.emit(value = setOf(status)) val values1 = getEmittedValues(flow = actual) @@ -75,11 +75,13 @@ internal class DefaultSingleQuoteProducerTest { Truth.assertThat(values1).isEqualTo(listOf(status)) // second emit - val updatedStatus = Quote.Value( + val updatedStatus = QuoteStatus( rawCurrencyId = params.rawCurrencyId, - fiatRate = BigDecimal.ONE, - priceChange = BigDecimal.ZERO, - source = StatusSource.ACTUAL, + value = QuoteStatus.Data( + fiatRate = BigDecimal.ONE, + priceChange = BigDecimal.ZERO, + source = StatusSource.ACTUAL, + ), ) storeQuote.emit(value = setOf(updatedStatus)) @@ -91,7 +93,7 @@ internal class DefaultSingleQuoteProducerTest { @Test fun `test that flow is filtered the same status`() = runTest { - val storeQuote = MutableSharedFlow>(replay = 2, extraBufferCapacity = 1) + val storeQuote = MutableSharedFlow>(replay = 2, extraBufferCapacity = 1) every { quotesStore.get() } returns storeQuote @@ -100,7 +102,7 @@ internal class DefaultSingleQuoteProducerTest { verify { quotesStore.get() } // first emit - val status = Quote.Empty(rawCurrencyId = params.rawCurrencyId) + val status = QuoteStatus(rawCurrencyId = params.rawCurrencyId) storeQuote.emit(value = setOf(status)) val values1 = getEmittedValues(flow = actual) @@ -120,11 +122,13 @@ internal class DefaultSingleQuoteProducerTest { @Test fun `test if flow throws exception`() = runTest { val exception = IllegalStateException() - val status = Quote.Value( + val status = QuoteStatus( rawCurrencyId = params.rawCurrencyId, - fiatRate = BigDecimal.ONE, - priceChange = BigDecimal.ZERO, - source = StatusSource.ACTUAL, + value = QuoteStatus.Data( + fiatRate = BigDecimal.ONE, + priceChange = BigDecimal.ZERO, + source = StatusSource.ACTUAL, + ), ) val innerFlow = MutableStateFlow(value = false) @@ -146,7 +150,7 @@ internal class DefaultSingleQuoteProducerTest { val values1 = getEmittedValues(flow = actual) Truth.assertThat(values1.size).isEqualTo(1) - val fallbackStatus = Quote.Empty(rawCurrencyId = params.rawCurrencyId) + val fallbackStatus = QuoteStatus(rawCurrencyId = params.rawCurrencyId) Truth.assertThat(values1).isEqualTo(listOf(fallbackStatus)) innerFlow.emit(value = true) @@ -160,7 +164,7 @@ internal class DefaultSingleQuoteProducerTest { fun `test if flow doesn't contain network from params`() = runTest { val storeFlow = flowOf( setOf( - Quote.Empty(rawCurrencyId = CryptoCurrency.RawID(value = "ETH")), + QuoteStatus(rawCurrencyId = CryptoCurrency.RawID(value = "ETH")), ), ) diff --git a/data/quotes/src/test/java/com/tangem/data/quotes/store/QuotesStoreGetMethodTest.kt b/data/quotes/src/test/java/com/tangem/data/quotes/store/QuotesStoreGetMethodTest.kt index d5a517f5e2..b957e0624e 100644 --- a/data/quotes/src/test/java/com/tangem/data/quotes/store/QuotesStoreGetMethodTest.kt +++ b/data/quotes/src/test/java/com/tangem/data/quotes/store/QuotesStoreGetMethodTest.kt @@ -6,7 +6,7 @@ import com.tangem.common.test.data.quote.toDomain import com.tangem.common.test.datastore.MockStateDataStore import com.tangem.common.test.utils.getEmittedValues import com.tangem.datasource.local.datastore.RuntimeSharedStore -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import kotlinx.coroutines.test.runTest import org.junit.Test @@ -17,7 +17,7 @@ import java.math.BigDecimal */ internal class QuotesStoreGetMethodTest { - private val runtimeStore = RuntimeSharedStore>() + private val runtimeStore = RuntimeSharedStore>() private val persistenceStore = MockStateDataStore(default = emptyMap()) private val store = DefaultQuotesStoreV2( @@ -32,7 +32,7 @@ internal class QuotesStoreGetMethodTest { val values = getEmittedValues(flow = actual) - Truth.assertThat(values).isEqualTo(emptyList>()) + Truth.assertThat(values).isEqualTo(emptyList>()) } @Test @@ -43,7 +43,7 @@ internal class QuotesStoreGetMethodTest { val values = getEmittedValues(flow = actual) - Truth.assertThat(values).isEqualTo(listOf(emptySet())) + Truth.assertThat(values).isEqualTo(listOf(emptySet())) } @Test @@ -74,7 +74,7 @@ internal class QuotesStoreGetMethodTest { val actual = store.getAllSyncOrNull() - Truth.assertThat(actual).isEqualTo(emptySet()) + Truth.assertThat(actual).isEqualTo(emptySet()) } @Test diff --git a/data/quotes/src/test/java/com/tangem/data/quotes/store/QuotesStoreInitializationTest.kt b/data/quotes/src/test/java/com/tangem/data/quotes/store/QuotesStoreInitializationTest.kt index 2452a66004..e60dd2bd09 100644 --- a/data/quotes/src/test/java/com/tangem/data/quotes/store/QuotesStoreInitializationTest.kt +++ b/data/quotes/src/test/java/com/tangem/data/quotes/store/QuotesStoreInitializationTest.kt @@ -7,7 +7,7 @@ import com.tangem.common.test.data.quote.toDomain import com.tangem.common.test.datastore.MockStateDataStore import com.tangem.datasource.local.datastore.RuntimeSharedStore import com.tangem.domain.models.StatusSource -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import io.mockk.every import io.mockk.mockk @@ -23,7 +23,7 @@ internal class QuotesStoreInitializationTest { @Test fun `test initialization if cache store is empty`() = runTest { - val runtimeStore = RuntimeSharedStore>() + val runtimeStore = RuntimeSharedStore>() val persistenceStore: DataStore = mockk() every { persistenceStore.data } returns emptyFlow() @@ -39,7 +39,7 @@ internal class QuotesStoreInitializationTest { @Test fun `test initialization if cache store contains empty map`() = runTest { - val runtimeStore = RuntimeSharedStore>() + val runtimeStore = RuntimeSharedStore>() val persistenceStore = MockStateDataStore(default = emptyMap()) DefaultQuotesStoreV2( @@ -53,7 +53,7 @@ internal class QuotesStoreInitializationTest { @Test fun `test initialization if cache store is not empty`() = runTest { - val runtimeStore = RuntimeSharedStore>() + val runtimeStore = RuntimeSharedStore>() val persistenceStore = MockStateDataStore(default = emptyMap()) val btcQuote = "BTC" to MockQuoteResponseFactory.createSinglePrice(BigDecimal.ZERO) diff --git a/data/quotes/src/test/java/com/tangem/data/quotes/store/QuotesStoreUpdateMethodsTest.kt b/data/quotes/src/test/java/com/tangem/data/quotes/store/QuotesStoreUpdateMethodsTest.kt index e935753dcf..d3224d5c48 100644 --- a/data/quotes/src/test/java/com/tangem/data/quotes/store/QuotesStoreUpdateMethodsTest.kt +++ b/data/quotes/src/test/java/com/tangem/data/quotes/store/QuotesStoreUpdateMethodsTest.kt @@ -7,7 +7,7 @@ import com.tangem.common.test.datastore.MockStateDataStore import com.tangem.datasource.local.datastore.RuntimeSharedStore import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.test.runTest @@ -19,7 +19,7 @@ import java.math.BigDecimal */ internal class QuotesStoreUpdateMethodsTest { - private val runtimeStore = RuntimeSharedStore>() + private val runtimeStore = RuntimeSharedStore>() private val persistenceStore = MockStateDataStore(default = emptyMap()) private val store = DefaultQuotesStoreV2( @@ -37,8 +37,8 @@ internal class QuotesStoreUpdateMethodsTest { store.refresh(currenciesIds = currenciesIds) - Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(emptySet()) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(emptySet()) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) } @Test @@ -50,10 +50,12 @@ internal class QuotesStoreUpdateMethodsTest { store.refresh(currenciesIds = setOf(quote.rawCurrencyId)) - val runtimeExpected = setOf(quote.copySealed(source = StatusSource.CACHE)) + val runtimeExpected = setOf( + quote.copy(value = quote.value.copySealed(source = StatusSource.CACHE)), + ) Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) } @Test @@ -94,10 +96,12 @@ internal class QuotesStoreUpdateMethodsTest { store.storeError(currenciesIds = currenciesIds) - val runtimeExpected = currenciesIds.map(Quote::Empty).toSet() + val runtimeExpected = currenciesIds + .map { QuoteStatus(rawCurrencyId = it, value = QuoteStatus.Empty) } + .toSet() Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) } @Test @@ -107,7 +111,7 @@ internal class QuotesStoreUpdateMethodsTest { runtimeStore.store( value = setOf( status.toDomain(rawCurrencyId = "BTC", source = StatusSource.CACHE), - Quote.Empty(rawCurrencyId = CryptoCurrency.RawID(value = "ETH")), + QuoteStatus(rawCurrencyId = CryptoCurrency.RawID(value = "ETH"), value = QuoteStatus.Empty), ), ) @@ -120,10 +124,10 @@ internal class QuotesStoreUpdateMethodsTest { val runtimeExpected = setOf( status.toDomain(rawCurrencyId = "BTC", source = StatusSource.ONLY_CACHE), - Quote.Empty(rawCurrencyId = CryptoCurrency.RawID(value = "ETH")), + QuoteStatus(rawCurrencyId = CryptoCurrency.RawID(value = "ETH"), value = QuoteStatus.Empty), ) Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) - Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) + Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) } } \ No newline at end of file diff --git a/domain/markets/src/main/java/com/tangem/domain/markets/GetCurrencyQuotesUseCase.kt b/domain/markets/src/main/java/com/tangem/domain/markets/GetCurrencyQuotesUseCase.kt index d146df5090..f73cd4e58d 100644 --- a/domain/markets/src/main/java/com/tangem/domain/markets/GetCurrencyQuotesUseCase.kt +++ b/domain/markets/src/main/java/com/tangem/domain/markets/GetCurrencyQuotesUseCase.kt @@ -6,7 +6,7 @@ import arrow.core.toOption import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.quotes.single.SingleQuoteProducer import com.tangem.domain.quotes.single.SingleQuoteSupplier -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flowOf @@ -20,13 +20,13 @@ class GetCurrencyQuotesUseCase( currencyID: CryptoCurrency.ID, interval: PriceChangeInterval, refresh: Boolean, - ): Flow> { + ): Flow> { val rawId = currencyID.rawCurrencyId ?: return flowOf(None) return singleQuoteSupplier( params = SingleQuoteProducer.Params(rawCurrencyId = rawId), ) - .map { (it as? Quote.Value).toOption() } + .map { (it.value as? QuoteStatus.Data).toOption() } .catch { emit(None) } } } \ No newline at end of file diff --git a/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTPriceUseCase.kt b/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTPriceUseCase.kt index 4dc44a7590..fa6e95358c 100644 --- a/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTPriceUseCase.kt +++ b/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTPriceUseCase.kt @@ -6,7 +6,7 @@ import com.tangem.domain.nft.models.NFTSalePrice import com.tangem.domain.nft.repository.NFTRepository import com.tangem.domain.quotes.single.SingleQuoteProducer import com.tangem.domain.quotes.single.SingleQuoteSupplier -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.fold import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -31,12 +31,15 @@ class GetNFTPriceUseCase( assetId = nftAsset.id, ) - val quoteValue = quote as? Quote.Value - if (nftPrice !is NFTSalePrice.Value) { nftPrice } else { - nftPrice.copy(fiatValue = quoteValue?.fiatRate?.multiply(nftPrice.value)) + nftPrice.copy( + fiatValue = quote.fold( + onData = { fiatRate.multiply(nftPrice.value) }, + onEmpty = { null }, + ), + ) } } } diff --git a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/HotCryptoCurrency.kt b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/HotCryptoCurrency.kt index 389cc866e3..aeb549ea8f 100644 --- a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/HotCryptoCurrency.kt +++ b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/HotCryptoCurrency.kt @@ -1,17 +1,17 @@ package com.tangem.domain.onramp.model import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus /** * Hot crypto currency * * @property cryptoCurrency crypto currency - * @property quote quote + * @property quoteStatus quote status * [REDACTED_AUTHOR] */ data class HotCryptoCurrency( val cryptoCurrency: CryptoCurrency, - val quote: Quote, + val quoteStatus: QuoteStatus, ) \ No newline at end of file diff --git a/domain/quotes/src/main/java/com/tangem/domain/quotes/QuotesRepositoryV2.kt b/domain/quotes/src/main/java/com/tangem/domain/quotes/QuotesRepositoryV2.kt index f9d517cd42..dd446b9f0a 100644 --- a/domain/quotes/src/main/java/com/tangem/domain/quotes/QuotesRepositoryV2.kt +++ b/domain/quotes/src/main/java/com/tangem/domain/quotes/QuotesRepositoryV2.kt @@ -1,7 +1,7 @@ package com.tangem.domain.quotes import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus /** * Quotes repository @@ -11,5 +11,5 @@ import com.tangem.domain.tokens.model.Quote interface QuotesRepositoryV2 { /** Get quotes by [currenciesIds] synchronously or null */ - suspend fun getMultiQuoteSyncOrNull(currenciesIds: Set): Set? + suspend fun getMultiQuoteSyncOrNull(currenciesIds: Set): Set? } \ No newline at end of file diff --git a/domain/quotes/src/main/java/com/tangem/domain/quotes/single/SingleQuoteProducer.kt b/domain/quotes/src/main/java/com/tangem/domain/quotes/single/SingleQuoteProducer.kt index c7fd024e28..4a8f30bccb 100644 --- a/domain/quotes/src/main/java/com/tangem/domain/quotes/single/SingleQuoteProducer.kt +++ b/domain/quotes/src/main/java/com/tangem/domain/quotes/single/SingleQuoteProducer.kt @@ -2,14 +2,14 @@ package com.tangem.domain.quotes.single import com.tangem.domain.core.flow.FlowProducer import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus /** * Producer of quote [CryptoCurrency.RawID] * [REDACTED_AUTHOR] */ -interface SingleQuoteProducer : FlowProducer { +interface SingleQuoteProducer : FlowProducer { data class Params(val rawCurrencyId: CryptoCurrency.RawID) diff --git a/domain/quotes/src/main/java/com/tangem/domain/quotes/single/SingleQuoteSupplier.kt b/domain/quotes/src/main/java/com/tangem/domain/quotes/single/SingleQuoteSupplier.kt index e65dbb492d..dbbda17215 100644 --- a/domain/quotes/src/main/java/com/tangem/domain/quotes/single/SingleQuoteSupplier.kt +++ b/domain/quotes/src/main/java/com/tangem/domain/quotes/single/SingleQuoteSupplier.kt @@ -1,7 +1,7 @@ package com.tangem.domain.quotes.single import com.tangem.domain.core.flow.FlowCachingSupplier -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus /** * Supplier of quote [SingleQuoteProducer.Params] @@ -14,4 +14,4 @@ import com.tangem.domain.tokens.model.Quote abstract class SingleQuoteSupplier( override val factory: SingleQuoteProducer.Factory, override val keyCreator: (SingleQuoteProducer.Params) -> String, -) : FlowCachingSupplier() \ No newline at end of file +) : FlowCachingSupplier() \ No newline at end of file diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/Quote.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/Quote.kt deleted file mode 100644 index a92dfb0ab3..0000000000 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/Quote.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.tangem.domain.tokens.model - -import com.tangem.domain.models.StatusSource -import com.tangem.domain.models.currency.CryptoCurrency -import java.math.BigDecimal - -sealed interface Quote { - - val rawCurrencyId: CryptoCurrency.RawID - - fun copySealed(source: StatusSource): Quote { - return when (this) { - is Empty -> this - is Value -> copy(source = source) - } - } - - /** - * Represents unknown financial information for a specific cryptocurrency. - * - * @property rawCurrencyId The raw cryptocurrency ID. - */ - data class Empty(override val rawCurrencyId: CryptoCurrency.RawID) : Quote - - /** - * Represents financial information for a specific cryptocurrency, including its fiat exchange rate and price change. - * - * @property rawCurrencyId The unique identifier of the cryptocurrency for which the financial information is provided. - * @property fiatRate The current fiat exchange rate for the cryptocurrency. - * @property priceChange The price change for the cryptocurrency. - * @property source source of data - */ - data class Value( - override val rawCurrencyId: CryptoCurrency.RawID, - val fiatRate: BigDecimal, - val priceChange: BigDecimal, - val source: StatusSource, - ) : Quote -} \ No newline at end of file diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/QuoteStatus.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/QuoteStatus.kt new file mode 100644 index 0000000000..81fb8a8dd5 --- /dev/null +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/QuoteStatus.kt @@ -0,0 +1,69 @@ +package com.tangem.domain.tokens.model + +import com.tangem.domain.models.StatusSource +import com.tangem.domain.models.currency.CryptoCurrency +import java.math.BigDecimal + +/** + * Represents the status of a specific currency quote + * + * @property rawCurrencyId the unique identifier of the cryptocurrency for which the financial information is provided + * @property value the specific status value + */ +data class QuoteStatus(val rawCurrencyId: CryptoCurrency.RawID, val value: Value) { + + /** Constructor for creating the [Empty] status of a specific currency quote */ + constructor(rawCurrencyId: CryptoCurrency.RawID) : this(rawCurrencyId = rawCurrencyId, value = Empty) + + /** Represents the various possible statuses of a quote */ + sealed interface Value { + + /** Status source */ + val source: StatusSource + + fun copySealed(source: StatusSource): Value { + return when (this) { + is Empty -> this + is Data -> copy(source = source) + } + } + } + + /** Represents unknown financial information for a specific cryptocurrency */ + data object Empty : Value { + override val source: StatusSource = StatusSource.ACTUAL + } + + /** + * Represents financial information for a specific cryptocurrency, including its fiat exchange rate and + * price change. + * + * @property source status source + * @property fiatRate the current fiat exchange rate for the cryptocurrency + * @property priceChange the price change for the cryptocurrency + */ + data class Data( + override val source: StatusSource, + val fiatRate: BigDecimal, + val priceChange: BigDecimal, + ) : Value +} + +/** Applies the given [function] if [QuoteStatus.Value] is [QuoteStatus.Data] or does nothing */ +inline fun QuoteStatus.mapData(function: QuoteStatus.Data.() -> Unit): QuoteStatus { + if (value is QuoteStatus.Data) value.function() + + return this +} + +/** + * Applies the given [onData] or [onEmpty] functions depending on [QuoteStatus.Data] or [QuoteStatus.Empty] + * + * @param T type of result + */ +inline fun QuoteStatus.fold(onData: QuoteStatus.Data.() -> T, onEmpty: QuoteStatus.Empty.() -> T): T { + return when (value) { + is QuoteStatus.Data -> value.onData() + is QuoteStatus.Empty -> value.onEmpty() + } +} \ No newline at end of file diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/Token.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/Token.kt index 9c088e4fce..244197386c 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/Token.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/Token.kt @@ -6,7 +6,7 @@ package com.tangem.domain.tokens.model * @property id The unique identifier of the token. * @property networks List of networks associated with the token. * @property isAvailable Indicates whether this token is supported in Tangem app. - * @property quote Equivalent prices for the token. + * @property quoteStatus Equivalent prices for the token. * @property name The name of the token. * @property symbol The brief name of the token, e.g., "BTC". * @property iconUrl URL of the token's icon. @@ -15,7 +15,7 @@ data class Token( val id: String, val networks: List, val isAvailable: Boolean, - val quote: Quote?, + val quoteStatus: QuoteStatus?, val name: String, val symbol: String, val iconUrl: String, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt index 6e8d810651..44d9a9574c 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/BaseCurrencyStatusOperations.kt @@ -23,7 +23,7 @@ import com.tangem.domain.staking.single.SingleYieldBalanceProducer import com.tangem.domain.staking.single.SingleYieldBalanceSupplier import com.tangem.domain.tokens.TokensFeatureToggles import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.utils.CurrencyStatusProxyCreator @@ -52,7 +52,7 @@ abstract class BaseCurrencyStatusOperations( protected val currencyStatusProxyCreator = CurrencyStatusProxyCreator(stakingRepository) - protected abstract fun getQuotes(id: CryptoCurrency.RawID): Flow>> + protected abstract fun getQuotes(id: CryptoCurrency.RawID): Flow>> protected abstract fun getNetworksStatuses( userWalletId: UserWalletId, @@ -120,7 +120,7 @@ abstract class BaseCurrencyStatusOperations( combine(quoteFlow, statusFlow, yieldBalanceFlow) { maybeQuote, maybeNetworkStatus, maybeYieldBalance -> currencyStatusProxyCreator.createCurrencyStatus( currency = currency, - maybeQuote = maybeQuote, + maybeQuoteStatus = maybeQuote, maybeNetworkStatus = maybeNetworkStatus, maybeYieldBalance = maybeYieldBalance, ) @@ -129,7 +129,7 @@ abstract class BaseCurrencyStatusOperations( combine(quoteFlow, statusFlow) { maybeQuote, maybeNetworkStatus -> currencyStatusProxyCreator.createCurrencyStatus( currency = currency, - maybeQuote = maybeQuote, + maybeQuoteStatus = maybeQuote, maybeNetworkStatus = maybeNetworkStatus, maybeYieldBalance = null, ) @@ -210,7 +210,7 @@ abstract class BaseCurrencyStatusOperations( return currencyStatusProxyCreator.createCurrencyStatus( currency = currency, - maybeQuote = quote, + maybeQuoteStatus = quote, maybeNetworkStatus = networkStatuses, maybeYieldBalance = yieldBalances, ) @@ -303,7 +303,7 @@ abstract class BaseCurrencyStatusOperations( return currencyStatusProxyCreator.createCurrencyStatus( currency = currency, - maybeQuote = quotes, + maybeQuoteStatus = quotes, maybeNetworkStatus = networkStatus, maybeYieldBalance = yieldBalances, ) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt index 0ba91d1225..e547b6549b 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CachedCurrenciesStatusesOperations.kt @@ -31,7 +31,7 @@ import com.tangem.domain.staking.single.SingleYieldBalanceSupplier import com.tangem.domain.tokens.TokensFeatureToggles import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.utils.extractAddress @@ -121,7 +121,7 @@ class CachedCurrenciesStatusesOperations( val (networks, currenciesIds) = getIds(currencies) fun createCurrenciesStatuses( - maybeQuotes: Either>?, + maybeQuotes: Either>?, maybeNetworkStatuses: Either>?, maybeYieldBalances: Either?, isUpdating: Boolean, @@ -212,7 +212,7 @@ class CachedCurrenciesStatusesOperations( private fun createCurrenciesStatuses( currencies: NonEmptyList, - maybeQuotes: Either>?, + maybeQuotes: Either>?, maybeNetworkStatuses: Either>?, maybeYieldBalances: Either?, isUpdating: Boolean, @@ -238,7 +238,7 @@ class CachedCurrenciesStatusesOperations( val currencyStatus = currencyStatusProxyCreator.createCurrencyStatus( currency = currency, - quote = quote, + quoteStatus = quote, networkStatus = networkStatus, yieldBalance = yieldBalance, ignoreQuote = quotesRetrievingFailed, @@ -272,7 +272,7 @@ class CachedCurrenciesStatusesOperations( .distinctUntilChanged() } - private fun getQuotes(tokensIds: NonEmptySet): Flow>> { + private fun getQuotes(tokensIds: NonEmptySet): Flow>> { return getQuotesUpdates( rawCurrencyIds = tokensIds.mapNotNullTo( destination = hashSetOf(), @@ -281,11 +281,11 @@ class CachedCurrenciesStatusesOperations( ) } - override fun getQuotes(id: CryptoCurrency.RawID): Flow>> { + override fun getQuotes(id: CryptoCurrency.RawID): Flow>> { return singleQuoteSupplier( params = SingleQuoteProducer.Params(rawCurrencyId = id), ) - .map>> { setOf(it).right() } + .map>> { setOf(it).right() } .distinctUntilChanged() } @@ -353,9 +353,11 @@ class CachedCurrenciesStatusesOperations( } // temporary code because token list is built using networks list - private fun getQuotesUpdates(rawCurrencyIds: Set): EitherFlow> { + private fun getQuotesUpdates( + rawCurrencyIds: Set, + ): EitherFlow> { return channelFlow { - val state = MutableStateFlow(emptySet()) + val state = MutableStateFlow(emptySet()) rawCurrencyIds.onEach { launch { @@ -375,7 +377,7 @@ class CachedCurrenciesStatusesOperations( .onEach(::send) .launchIn(scope = this) } - .map, Either>> { it.right() } + .map, Either>> { it.right() } .distinctUntilChanged() } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt index f890486cc2..4e065d68c5 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt @@ -5,28 +5,22 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import java.math.BigDecimal internal class CurrencyStatusOperations( private val currency: CryptoCurrency, - private val quote: Quote?, + private val quoteStatus: QuoteStatus?, private val networkStatus: NetworkStatus?, private val yieldBalance: YieldBalance?, private val ignoreQuote: Boolean, ) { - private val Quote?.fiatRate: BigDecimal? - get() = when (this) { - is Quote.Value -> this.fiatRate - is Quote.Empty, null -> null - } + private val QuoteStatus?.fiatRate: BigDecimal? + get() = (this?.value as? QuoteStatus.Data)?.fiatRate - private val Quote?.priceChange: BigDecimal? - get() = when (this) { - is Quote.Value -> this.priceChange - is Quote.Empty, null -> null - } + private val QuoteStatus?.priceChange: BigDecimal? + get() = (this?.value as? QuoteStatus.Data)?.priceChange fun createTokenStatus(): CryptoCurrencyStatus = CryptoCurrencyStatus(currency, createStatus()) @@ -41,12 +35,12 @@ internal class CurrencyStatusOperations( } private fun createMissedDerivationStatus(): CryptoCurrencyStatus.MissedDerivation = - CryptoCurrencyStatus.MissedDerivation(priceChange = quote?.priceChange, fiatRate = quote?.fiatRate) + CryptoCurrencyStatus.MissedDerivation(priceChange = quoteStatus?.priceChange, fiatRate = quoteStatus?.fiatRate) private fun createUnreachableStatus(status: NetworkStatus.Unreachable): CryptoCurrencyStatus.Unreachable { return CryptoCurrencyStatus.Unreachable( - priceChange = quote?.priceChange, - fiatRate = quote?.fiatRate, + priceChange = quoteStatus?.priceChange, + fiatRate = quoteStatus?.fiatRate, networkAddress = status.address, ) } @@ -54,13 +48,13 @@ internal class CurrencyStatusOperations( private fun createNoAccountStatus(status: NetworkStatus.NoAccount): CryptoCurrencyStatus.NoAccount { return CryptoCurrencyStatus.NoAccount( amountToCreateAccount = status.amountToCreateAccount, - fiatAmount = if (quote == null) null else BigDecimal.ZERO, - priceChange = quote?.priceChange, - fiatRate = quote?.fiatRate, + fiatAmount = if (quoteStatus == null) null else BigDecimal.ZERO, + priceChange = quoteStatus?.priceChange, + fiatRate = quoteStatus?.fiatRate, networkAddress = status.address, sources = CryptoCurrencyStatus.Sources( networkSource = status.source, - quoteSource = (quote as? Quote.Value)?.source ?: StatusSource.ACTUAL, + quoteSource = quoteStatus?.value?.source ?: StatusSource.ACTUAL, ), ) } @@ -75,7 +69,10 @@ internal class CurrencyStatusOperations( return CryptoCurrencyStatus.Loading } is NetworkStatus.Amount.NotFound -> { - return CryptoCurrencyStatus.NoAmount(priceChange = quote?.priceChange, fiatRate = quote?.fiatRate) + return CryptoCurrencyStatus.NoAmount( + priceChange = quoteStatus?.priceChange, + fiatRate = quoteStatus?.fiatRate, + ) } is NetworkStatus.Amount.Loaded -> amount.value } @@ -96,24 +93,27 @@ internal class CurrencyStatusOperations( } else { null } + + val quoteValue = quoteStatus?.value + // order is important for correct total balance calculation return when { currency is CryptoCurrency.Token && currency.isCustom -> CryptoCurrencyStatus.Custom( amount = amount, - fiatAmount = calculateFiatAmountOrNull(amount, quote?.fiatRate), - fiatRate = quote?.fiatRate, - priceChange = quote?.priceChange, + fiatAmount = calculateFiatAmountOrNull(amount, quoteStatus?.fiatRate), + fiatRate = quoteStatus?.fiatRate, + priceChange = quoteStatus?.priceChange, hasCurrentNetworkTransactions = hasCurrentNetworkTransactions, pendingTransactions = currentTransactions, networkAddress = networkStatusValue.address, yieldBalance = currentYieldBalance, sources = CryptoCurrencyStatus.Sources( networkSource = networkStatusValue.source, - quoteSource = (quote as? Quote.Value)?.source ?: StatusSource.ACTUAL, + quoteSource = quoteStatus?.value?.source ?: StatusSource.ACTUAL, yieldBalanceSource = currentYieldBalance?.source ?: StatusSource.ACTUAL, ), ) - quote is Quote.Empty || ignoreQuote -> CryptoCurrencyStatus.NoQuote( + quoteValue is QuoteStatus.Empty || ignoreQuote -> CryptoCurrencyStatus.NoQuote( amount = amount, hasCurrentNetworkTransactions = hasCurrentNetworkTransactions, pendingTransactions = currentTransactions, @@ -121,22 +121,22 @@ internal class CurrencyStatusOperations( yieldBalance = currentYieldBalance, sources = CryptoCurrencyStatus.Sources( networkSource = networkStatusValue.source, - quoteSource = (quote as? Quote.Value)?.source ?: StatusSource.ACTUAL, + quoteSource = quoteStatus?.value?.source ?: StatusSource.ACTUAL, yieldBalanceSource = currentYieldBalance?.source ?: StatusSource.ACTUAL, ), ) - quote is Quote.Value -> CryptoCurrencyStatus.Loaded( + quoteValue is QuoteStatus.Data -> CryptoCurrencyStatus.Loaded( amount = amount, - fiatAmount = calculateFiatAmount(amount, quote.fiatRate), - fiatRate = quote.fiatRate, - priceChange = quote.priceChange, + fiatAmount = calculateFiatAmount(amount, quoteValue.fiatRate), + fiatRate = quoteValue.fiatRate, + priceChange = quoteValue.priceChange, hasCurrentNetworkTransactions = hasCurrentNetworkTransactions, pendingTransactions = currentTransactions, networkAddress = networkStatusValue.address, yieldBalance = currentYieldBalance, sources = CryptoCurrencyStatus.Sources( networkSource = networkStatusValue.source, - quoteSource = quote.source, + quoteSource = quoteValue.source, yieldBalanceSource = currentYieldBalance?.source ?: StatusSource.ACTUAL, ), ) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt index 337ffb44db..6ed4a05e88 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/utils/CurrencyStatusProxyCreator.kt @@ -10,7 +10,7 @@ import com.tangem.domain.staking.model.stakekit.YieldBalance import com.tangem.domain.staking.model.stakekit.YieldBalanceList import com.tangem.domain.staking.repositories.StakingRepository import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error import com.tangem.domain.tokens.operations.CurrencyStatusOperations @@ -27,14 +27,14 @@ class CurrencyStatusProxyCreator( fun createCurrencyStatus( currency: CryptoCurrency, - maybeQuote: Either, + maybeQuoteStatus: Either, maybeNetworkStatus: Either, maybeYieldBalance: Either?, ): Either = either { var quoteRetrievingFailed = false val networkStatus = maybeNetworkStatus.bind() - val quote = arrow.core.raise.recover({ maybeQuote.bind() }) { + val quote = arrow.core.raise.recover({ maybeQuoteStatus.bind() }) { quoteRetrievingFailed = true null } @@ -42,7 +42,7 @@ class CurrencyStatusProxyCreator( createCurrencyStatus( currency = currency, - quote = quote, + quoteStatus = quote, networkStatus = networkStatus, ignoreQuote = quoteRetrievingFailed, yieldBalance = yieldBalance, @@ -51,14 +51,14 @@ class CurrencyStatusProxyCreator( fun createCurrenciesStatuses( currencies: NonEmptyList, - maybeQuotes: Either>?, + maybeQuotes: Either>?, maybeNetworkStatuses: Either>?, maybeYieldBalances: Either?, ): Either> = either { var quotesRetrievingFailed = false val networksStatuses = maybeNetworkStatuses?.bind()?.toNonEmptySetOrNull() - val quotes: Set? = maybeQuotes?.fold( + val quoteStatuses: Set? = maybeQuotes?.fold( ifLeft = { quotesRetrievingFailed = true null @@ -74,7 +74,7 @@ class CurrencyStatusProxyCreator( val yieldBalances = maybeYieldBalances?.getOrNull() currencies.map { currency -> - val quote = quotes?.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId } + val quote = quoteStatuses?.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId } val networkStatus = networksStatuses?.firstOrNull { it.network == currency.network } val address = extractAddress(networkStatus) @@ -89,7 +89,7 @@ class CurrencyStatusProxyCreator( } createCurrencyStatus( currency = currency, - quote = quote, + quoteStatus = quote, networkStatus = networkStatus, ignoreQuote = quotesRetrievingFailed, yieldBalance = yieldBalance, @@ -99,14 +99,14 @@ class CurrencyStatusProxyCreator( fun createCurrencyStatus( currency: CryptoCurrency, - quote: Quote?, + quoteStatus: QuoteStatus?, networkStatus: NetworkStatus?, ignoreQuote: Boolean, yieldBalance: YieldBalance?, ): CryptoCurrencyStatus { val currencyStatusOperations = CurrencyStatusOperations( currency = currency, - quote = quote, + quoteStatus = quoteStatus, networkStatus = networkStatus, ignoreQuote = ignoreQuote, yieldBalance = yieldBalance, diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockQuotes.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockQuotes.kt index 0d786ca54c..ea2555d8c5 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockQuotes.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockQuotes.kt @@ -3,83 +3,103 @@ package com.tangem.domain.tokens.mock import arrow.core.nonEmptySetOf import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import java.math.BigDecimal @Suppress("MemberVisibilityCanBePrivate") internal object MockQuotes { - val quote1 = Quote.Value( + val quote1 = QuoteStatus( rawCurrencyId = MockTokens.token1.id.rawCurrencyId!!, - fiatRate = BigDecimal("1.23"), - priceChange = BigDecimal("0.01"), - source = StatusSource.ACTUAL, + value = QuoteStatus.Data( + fiatRate = BigDecimal("1.23"), + priceChange = BigDecimal("0.01"), + source = StatusSource.ACTUAL, + ), ) - val quote2 = Quote.Value( + val quote2 = QuoteStatus( rawCurrencyId = MockTokens.token2.id.rawCurrencyId!!, - fiatRate = BigDecimal("2.34"), - priceChange = BigDecimal("-0.02"), - source = StatusSource.ACTUAL, + value = QuoteStatus.Data( + fiatRate = BigDecimal("2.34"), + priceChange = BigDecimal("-0.02"), + source = StatusSource.ACTUAL, + ), ) - val quote3 = Quote.Value( + val quote3 = QuoteStatus( rawCurrencyId = MockTokens.token3.id.rawCurrencyId!!, - fiatRate = BigDecimal("3.45"), - priceChange = BigDecimal("0.03"), - source = StatusSource.ACTUAL, + value = QuoteStatus.Data( + fiatRate = BigDecimal("3.45"), + priceChange = BigDecimal("0.03"), + source = StatusSource.ACTUAL, + ), ) - val quote4 = Quote.Value( + val quote4 = QuoteStatus( rawCurrencyId = MockTokens.token4.id.rawCurrencyId!!, - fiatRate = BigDecimal("4.56"), - priceChange = BigDecimal("-0.04"), - source = StatusSource.ACTUAL, + value = QuoteStatus.Data( + fiatRate = BigDecimal("4.56"), + priceChange = BigDecimal("-0.04"), + source = StatusSource.ACTUAL, + ), ) - val quote5 = Quote.Value( + val quote5 = QuoteStatus( rawCurrencyId = MockTokens.token5.id.rawCurrencyId!!, - fiatRate = BigDecimal("5.67"), - priceChange = BigDecimal("0.05"), - source = StatusSource.ACTUAL, + value = QuoteStatus.Data( + fiatRate = BigDecimal("5.67"), + priceChange = BigDecimal("0.05"), + source = StatusSource.ACTUAL, + ), ) - val quote6 = Quote.Value( + val quote6 = QuoteStatus( rawCurrencyId = MockTokens.token6.id.rawCurrencyId!!, - fiatRate = BigDecimal("6.78"), - priceChange = BigDecimal("-0.06"), - source = StatusSource.ACTUAL, + value = QuoteStatus.Data( + fiatRate = BigDecimal("6.78"), + priceChange = BigDecimal("-0.06"), + source = StatusSource.ACTUAL, + ), ) - val quote7 = Quote.Value( + val quote7 = QuoteStatus( rawCurrencyId = MockTokens.token7.id.rawCurrencyId!!, - fiatRate = BigDecimal("7.89"), - priceChange = BigDecimal("0.07"), - source = StatusSource.ACTUAL, + value = QuoteStatus.Data( + fiatRate = BigDecimal("7.89"), + priceChange = BigDecimal("0.07"), + source = StatusSource.ACTUAL, + ), ) - val quote8 = Quote.Value( + val quote8 = QuoteStatus( rawCurrencyId = MockTokens.token8.id.rawCurrencyId!!, - fiatRate = BigDecimal("8.90"), - priceChange = BigDecimal("-0.08"), - source = StatusSource.ACTUAL, + value = QuoteStatus.Data( + fiatRate = BigDecimal("8.90"), + priceChange = BigDecimal("-0.08"), + source = StatusSource.ACTUAL, + ), ) - val quote9 = Quote.Value( + val quote9 = QuoteStatus( rawCurrencyId = MockTokens.token9.id.rawCurrencyId!!, - fiatRate = BigDecimal("9.01"), - priceChange = BigDecimal("0.09"), - source = StatusSource.ACTUAL, + value = QuoteStatus.Data( + fiatRate = BigDecimal("9.01"), + priceChange = BigDecimal("0.09"), + source = StatusSource.ACTUAL, + ), ) - val quote10 = Quote.Value( + val quote10 = QuoteStatus( rawCurrencyId = MockTokens.token10.id.rawCurrencyId!!, - fiatRate = BigDecimal("10.12"), - priceChange = BigDecimal("-0.10"), - source = StatusSource.ACTUAL, + value = QuoteStatus.Data( + fiatRate = BigDecimal("10.12"), + priceChange = BigDecimal("-0.10"), + source = StatusSource.ACTUAL, + ), ) - val quote11 = Quote.Empty(CryptoCurrency.RawID("null")) + val quote11 = QuoteStatus(rawCurrencyId = CryptoCurrency.RawID("null"), value = QuoteStatus.Empty) val quotes = nonEmptySetOf( quote1, quote2, quote3, quote4, quote5, quote6, quote7, quote8, quote9, quote10, diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt index c3f573af01..3a0e07cb0e 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt @@ -4,7 +4,8 @@ import arrow.core.nonEmptyListOf import com.tangem.domain.models.network.NetworkAddress import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus +import com.tangem.domain.tokens.model.fold import java.math.BigDecimal @Suppress("MemberVisibilityCanBePrivate") @@ -13,8 +14,8 @@ internal object MockTokensStates { val tokenState1 = CryptoCurrencyStatus( currency = MockTokens.token1, value = CryptoCurrencyStatus.Unreachable( - priceChange = MockQuotes.quote1.priceChange, - fiatRate = MockQuotes.quote1.fiatRate, + priceChange = MockQuotes.quote1.getPriceChange(), + fiatRate = MockQuotes.quote1.getFiatRate(), networkAddress = NetworkAddress.Single( defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary), ), @@ -24,8 +25,8 @@ internal object MockTokensStates { val tokenState2 = CryptoCurrencyStatus( currency = MockTokens.token2, value = CryptoCurrencyStatus.Unreachable( - priceChange = MockQuotes.quote2.priceChange, - fiatRate = MockQuotes.quote2.fiatRate, + priceChange = MockQuotes.quote2.getPriceChange(), + fiatRate = MockQuotes.quote2.getFiatRate(), networkAddress = NetworkAddress.Single( defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary), ), @@ -35,8 +36,8 @@ internal object MockTokensStates { val tokenState3 = CryptoCurrencyStatus( currency = MockTokens.token3, value = CryptoCurrencyStatus.Unreachable( - priceChange = MockQuotes.quote3.priceChange, - fiatRate = MockQuotes.quote3.fiatRate, + priceChange = MockQuotes.quote3.getPriceChange(), + fiatRate = MockQuotes.quote3.getFiatRate(), networkAddress = NetworkAddress.Single( defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary), ), @@ -46,24 +47,24 @@ internal object MockTokensStates { val tokenState4 = CryptoCurrencyStatus( currency = MockTokens.token4, value = CryptoCurrencyStatus.MissedDerivation( - priceChange = MockQuotes.quote4.priceChange, - fiatRate = MockQuotes.quote4.fiatRate, + priceChange = MockQuotes.quote4.getPriceChange(), + fiatRate = MockQuotes.quote4.getFiatRate(), ), ) val tokenState5 = CryptoCurrencyStatus( currency = MockTokens.token5, value = CryptoCurrencyStatus.MissedDerivation( - priceChange = MockQuotes.quote5.priceChange, - fiatRate = MockQuotes.quote5.fiatRate, + priceChange = MockQuotes.quote5.getPriceChange(), + fiatRate = MockQuotes.quote5.getFiatRate(), ), ) val tokenState6 = CryptoCurrencyStatus( currency = MockTokens.token6, value = CryptoCurrencyStatus.MissedDerivation( - priceChange = MockQuotes.quote6.priceChange, - fiatRate = MockQuotes.quote6.fiatRate, + priceChange = MockQuotes.quote6.getPriceChange(), + fiatRate = MockQuotes.quote6.getFiatRate(), ), ) @@ -71,8 +72,8 @@ internal object MockTokensStates { currency = MockTokens.token7, value = CryptoCurrencyStatus.NoAccount( fiatAmount = BigDecimal.ZERO, - priceChange = MockQuotes.quote7.priceChange, - fiatRate = MockQuotes.quote7.fiatRate, + priceChange = MockQuotes.quote7.getPriceChange(), + fiatRate = MockQuotes.quote7.getFiatRate(), amountToCreateAccount = MockNetworks.amountToCreateAccount, networkAddress = NetworkAddress.Single( defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary), @@ -85,8 +86,8 @@ internal object MockTokensStates { currency = MockTokens.token8, value = CryptoCurrencyStatus.NoAccount( fiatAmount = BigDecimal.ZERO, - priceChange = MockQuotes.quote8.priceChange, - fiatRate = MockQuotes.quote8.fiatRate, + priceChange = MockQuotes.quote8.getPriceChange(), + fiatRate = MockQuotes.quote8.getFiatRate(), amountToCreateAccount = MockNetworks.amountToCreateAccount, networkAddress = NetworkAddress.Single( defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary), @@ -99,8 +100,8 @@ internal object MockTokensStates { currency = MockTokens.token9, value = CryptoCurrencyStatus.NoAccount( fiatAmount = BigDecimal.ZERO, - priceChange = MockQuotes.quote9.priceChange, - fiatRate = MockQuotes.quote9.fiatRate, + priceChange = MockQuotes.quote9.getPriceChange(), + fiatRate = MockQuotes.quote9.getFiatRate(), amountToCreateAccount = MockNetworks.amountToCreateAccount, networkAddress = NetworkAddress.Single( defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary), @@ -113,8 +114,8 @@ internal object MockTokensStates { currency = MockTokens.token10, value = CryptoCurrencyStatus.NoAccount( fiatAmount = BigDecimal.ZERO, - priceChange = MockQuotes.quote10.priceChange, - fiatRate = MockQuotes.quote10.fiatRate, + priceChange = MockQuotes.quote10.getPriceChange(), + fiatRate = MockQuotes.quote10.getFiatRate(), amountToCreateAccount = MockNetworks.amountToCreateAccount, networkAddress = NetworkAddress.Single( defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary), @@ -145,8 +146,8 @@ internal object MockTokensStates { )?.value ?: BigDecimal.ZERO val quote = MockQuotes.quotes.first { it.rawCurrencyId == status.currency.id.rawCurrencyId } - val value = when (quote) { - is Quote.Empty -> CryptoCurrencyStatus.NoQuote( + val value = when (val value = quote.value) { + is QuoteStatus.Empty -> CryptoCurrencyStatus.NoQuote( amount = status.value.amount!!, pendingTransactions = emptySet(), hasCurrentNetworkTransactions = false, @@ -156,11 +157,11 @@ internal object MockTokensStates { yieldBalance = null, sources = CryptoCurrencyStatus.Sources(), ) - is Quote.Value -> CryptoCurrencyStatus.Loaded( + is QuoteStatus.Data -> CryptoCurrencyStatus.Loaded( amount = amount, - fiatAmount = amount * quote.fiatRate, - fiatRate = quote.fiatRate, - priceChange = quote.priceChange, + fiatAmount = amount * value.fiatRate, + fiatRate = value.fiatRate, + priceChange = value.priceChange, pendingTransactions = emptySet(), hasCurrentNetworkTransactions = false, networkAddress = requireNotNull(networkStatus.value as? NetworkStatus.Verified).address, @@ -187,4 +188,7 @@ internal object MockTokensStates { ), ) } + + private fun QuoteStatus.getPriceChange(): BigDecimal? = fold(onData = { priceChange }, onEmpty = { null }) + private fun QuoteStatus.getFiatRate(): BigDecimal? = fold(onData = { fiatRate }, onEmpty = { null }) } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/converter/HotTokenItemStateConverter.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/converter/HotTokenItemStateConverter.kt index a6e5e646ce..887c06034a 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/converter/HotTokenItemStateConverter.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/converter/HotTokenItemStateConverter.kt @@ -10,7 +10,8 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.format.bigdecimal.percent import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.onramp.model.HotCryptoCurrency -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus +import com.tangem.domain.tokens.model.fold import com.tangem.utils.converter.Converter import java.math.BigDecimal @@ -30,7 +31,7 @@ internal class HotTokenItemStateConverter( id = value.cryptoCurrency.id.value, iconState = CryptoCurrencyToIconStateConverter().convert(value.cryptoCurrency), titleState = TokenItemState.TitleState.Content(text = stringReference(value.cryptoCurrency.name)), - subtitleState = value.quote.getCryptoPriceState(appCurrency), + subtitleState = value.quoteStatus.getCryptoPriceState(appCurrency), fiatAmountState = null, subtitle2State = null, onItemClick = onItemClick.let { onItemClick -> { onItemClick(it, value) } }, @@ -38,17 +39,17 @@ internal class HotTokenItemStateConverter( ) } - private fun Quote.getCryptoPriceState(appCurrency: AppCurrency): TokenItemState.SubtitleState { - return when (this) { - is Quote.Empty -> TokenItemState.SubtitleState.Unknown - is Quote.Value -> { + private fun QuoteStatus.getCryptoPriceState(appCurrency: AppCurrency): TokenItemState.SubtitleState { + return fold( + onData = { TokenItemState.SubtitleState.CryptoPriceContent( price = fiatRate.getFormattedCryptoPrice(appCurrency), priceChangePercent = priceChange.format { percent() }, type = priceChange.getPriceChangeType(), ) - } - } + }, + onEmpty = { TokenItemState.SubtitleState.Unknown }, + ) } private fun BigDecimal.getFormattedCryptoPrice(appCurrency: AppCurrency): String { diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 410639462f..672cd819f8 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -25,7 +25,7 @@ import com.tangem.domain.tokens.GetCurrencyCheckUseCase import com.tangem.domain.tokens.GetMultiCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.FeePaidCurrency -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.CurrencyChecksRepository @@ -1034,7 +1034,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( val rates = getQuotes(fromToken.currency.id) val fromTokenSwapInfo = TokenSwapInfo( tokenAmount = amount, - amountFiat = rates[fromToken.currency.id]?.fiatRate?.multiply(amount.value) + amountFiat = rates[fromToken.currency.id]?.multiply(amount.value) ?: BigDecimal.ZERO, cryptoCurrencyStatus = fromToken, ) @@ -1130,7 +1130,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( else -> getNativeToken(networkId = fromToken.network.backendId).id } val rates = getQuotes(feeCurrencyId) - return rates[feeCurrencyId]?.fiatRate?.let { rate -> + return rates[feeCurrencyId]?.let { rate -> fees.map { fee -> rate.multiply(fee).format { fiat( @@ -1233,7 +1233,7 @@ internal class SwapInteractorImpl @AssistedInject constructor( val rates = getQuotes(fromToken.currency.id) val fromTokenSwapInfo = TokenSwapInfo( tokenAmount = amount, - amountFiat = rates[fromToken.currency.id]?.fiatRate?.multiply(amount.value) + amountFiat = rates[fromToken.currency.id]?.multiply(amount.value) ?: BigDecimal.ZERO, cryptoCurrencyStatus = fromToken, ) @@ -1307,20 +1307,20 @@ internal class SwapInteractorImpl @AssistedInject constructor( fromTokenInfo = TokenSwapInfo( tokenAmount = fromTokenAmount, cryptoCurrencyStatus = fromTokenStatus, - amountFiat = rates[fromToken.id]?.fiatRate?.multiply(fromTokenAmount.value) + amountFiat = rates[fromToken.id]?.multiply(fromTokenAmount.value) ?: BigDecimal.ZERO, ), toTokenInfo = TokenSwapInfo( tokenAmount = toTokenAmount, cryptoCurrencyStatus = toTokenStatus, - amountFiat = rates[toToken.id]?.fiatRate?.multiply(toTokenAmount.value) + amountFiat = rates[toToken.id]?.multiply(toTokenAmount.value) ?: BigDecimal.ZERO, ), priceImpact = calculatePriceImpact( fromTokenAmount = fromTokenAmount.value, - fromRate = rates[fromToken.id]?.fiatRate?.toDouble() ?: 0.0, + fromRate = rates[fromToken.id]?.toDouble() ?: 0.0, toTokenAmount = toTokenAmount.value, - toRate = rates[toToken.id]?.fiatRate?.toDouble() ?: 0.0, + toRate = rates[toToken.id]?.toDouble() ?: 0.0, ), swapDataModel = swapData, txFee = txFeeState, @@ -1795,18 +1795,20 @@ internal class SwapInteractorImpl @AssistedInject constructor( return PriceImpact.Value(value) } - private suspend fun getQuotes(vararg ids: CryptoCurrency.ID): Map { + private suspend fun getQuotes(vararg ids: CryptoCurrency.ID): Map { val set = ids.mapNotNull { it.rawCurrencyId } .toSet() .getQuotesOrEmpty() - .filterIsInstance() return ids - .mapNotNull { id -> set.find { it.rawCurrencyId == id.rawCurrencyId }?.let { id to it } } + .mapNotNull { id -> + set.find { it.rawCurrencyId == id.rawCurrencyId && it.value is QuoteStatus.Data } + ?.let { id to (it.value as QuoteStatus.Data).fiatRate } + } .toMap() } - private suspend fun Set.getQuotesOrEmpty(): Set { + private suspend fun Set.getQuotesOrEmpty(): Set { return runCatching { quotesRepositoryV2.getMultiQuoteSyncOrNull(currenciesIds = this) } .getOrNull() .orEmpty() diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt index f02dc11cf4..4c5cb72a5b 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSwapTransactionsStateConverter.kt @@ -17,8 +17,9 @@ import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday import com.tangem.core.ui.utils.toTimeFormat import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent +import com.tangem.domain.tokens.model.mapData import com.tangem.feature.swap.domain.models.domain.ExchangeStatus import com.tangem.feature.swap.domain.models.domain.ExchangeStatus.Companion.isFailed import com.tangem.feature.swap.domain.models.domain.ExchangeStatusModel @@ -57,7 +58,7 @@ internal class TokenDetailsSwapTransactionsStateConverter( fun convert( savedTransactions: List, - quotes: Set, + quoteStatuses: Set, ): PersistentList { val result = mutableListOf() @@ -73,12 +74,17 @@ internal class TokenDetailsSwapTransactionsStateConverter( val fromAmount = transaction.fromCryptoAmount var toFiatAmount: BigDecimal? = null var fromFiatAmount: BigDecimal? = null - quotes.forEach { quote -> - if (quote is Quote.Value && quote.rawCurrencyId == toCryptoCurrencyRawId) { - toFiatAmount = quote.fiatRate.multiply(toAmount) + quoteStatuses.forEach { quote -> + quote.mapData { + if (quote.rawCurrencyId == toCryptoCurrencyRawId) { + toFiatAmount = fiatRate.multiply(toAmount) + } } - if (quote is Quote.Value && quote.rawCurrencyId == fromCryptoCurrencyRawId) { - fromFiatAmount = quote.fiatRate.multiply(fromAmount) + + quote.mapData { + if (quote.rawCurrencyId == fromCryptoCurrencyRawId) { + fromFiatAmount = fiatRate.multiply(fromAmount) + } } } val statusModel = transaction.status diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt index 0a75b5e7d7..13c9843f25 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt @@ -8,7 +8,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.quotes.QuotesRepositoryV2 import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.model.QuoteStatus import com.tangem.domain.tokens.model.analytics.TokenExchangeAnalyticsEvent import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.swap.domain.SwapTransactionRepository @@ -66,7 +66,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor( getExchangeStatusState( savedTransactions = savedTransactions, - quotes = quotes, + quoteStatuses = quotes, ) } } @@ -155,7 +155,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor( private fun getExchangeStatusState( savedTransactions: List?, - quotes: Set, + quoteStatuses: Set, ): PersistentList { if (savedTransactions == null) { return persistentListOf() @@ -163,7 +163,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor( return swapTransactionsStateConverter.convert( savedTransactions = savedTransactions, - quotes = quotes, + quoteStatuses = quoteStatuses, ) } @@ -187,7 +187,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor( } } - private suspend fun Set.getQuotesOrEmpty(): Set { + private suspend fun Set.getQuotesOrEmpty(): Set { val rawIds = mapNotNull { it.rawCurrencyId }.toSet() return runCatching { quotesRepositoryV2.getMultiQuoteSyncOrNull(currenciesIds = rawIds) }