From d6bf0992d7860d69efea742fdb9d9ca612f86dc0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 22 Apr 2025 18:30:36 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/TokensDomainModule.kt | 5 ++ .../data/staking/DefaultStakingRepository.kt | 56 +++++++++------ .../data/staking/di/StakingDataModule.kt | 10 ++- .../multi/DefaultMultiYieldBalanceFetcher.kt | 3 +- .../DefaultSingleYieldBalanceFetcher.kt | 3 +- .../store/DefaultYieldsBalancesStore.kt | 60 ++++++++++------ .../data/staking/store/YieldsBalancesStore.kt | 6 ++ .../YieldsBalancesStoreUpdateMethodsTest.kt | 15 +--- .../staking/repositories/StakingRepository.kt | 9 ++- .../BaseCurrencyStatusOperations.kt | 45 ++++++++---- .../CachedCurrenciesStatusesOperations.kt | 70 ++++++++++++++++--- ...PrimaryCurrencyStatusUpdatesUseCaseTest.kt | 1 + .../repository/MockStakingRepository.kt | 11 ++- 13 files changed, 209 insertions(+), 85 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index 695196deaf..075ab6d9f7 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -11,6 +11,7 @@ import com.tangem.domain.quotes.QuotesRepositoryV2 import com.tangem.domain.quotes.multi.MultiQuoteFetcher import com.tangem.domain.quotes.single.SingleQuoteSupplier import com.tangem.domain.staking.repositories.StakingRepository +import com.tangem.domain.staking.single.SingleYieldBalanceSupplier import com.tangem.domain.tokens.* import com.tangem.domain.tokens.operations.BaseCurrenciesStatusesOperations import com.tangem.domain.tokens.operations.BaseCurrencyStatusOperations @@ -409,6 +410,7 @@ internal object TokensDomainModule { multiNetworkStatusFetcher: MultiNetworkStatusFetcher, multiQuoteFetcher: MultiQuoteFetcher, singleQuoteSupplier: SingleQuoteSupplier, + singleYieldBalanceSupplier: SingleYieldBalanceSupplier, ): BaseCurrenciesStatusesOperations { return CachedCurrenciesStatusesOperations( currenciesRepository = currenciesRepository, @@ -421,6 +423,7 @@ internal object TokensDomainModule { multiNetworkStatusFetcher = multiNetworkStatusFetcher, multiQuoteFetcher = multiQuoteFetcher, singleQuoteSupplier = singleQuoteSupplier, + singleYieldBalanceSupplier = singleYieldBalanceSupplier, tokensFeatureToggles = tokensFeatureToggles, ) } @@ -439,6 +442,7 @@ internal object TokensDomainModule { multiNetworkStatusFetcher: MultiNetworkStatusFetcher, multiQuoteFetcher: MultiQuoteFetcher, singleQuoteSupplier: SingleQuoteSupplier, + singleYieldBalanceSupplier: SingleYieldBalanceSupplier, ): BaseCurrencyStatusOperations { return CachedCurrenciesStatusesOperations( currenciesRepository = currenciesRepository, @@ -451,6 +455,7 @@ internal object TokensDomainModule { multiNetworkStatusFetcher = multiNetworkStatusFetcher, multiQuoteFetcher = multiQuoteFetcher, singleQuoteSupplier = singleQuoteSupplier, + singleYieldBalanceSupplier = singleYieldBalanceSupplier, tokensFeatureToggles = tokensFeatureToggles, ) } diff --git a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt index 3ccb9e47aa..03e1b51935 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/DefaultStakingRepository.kt @@ -24,6 +24,8 @@ import com.tangem.data.staking.converters.transaction.GasEstimateConverter import com.tangem.data.staking.converters.transaction.StakingTransactionConverter import com.tangem.data.staking.converters.transaction.StakingTransactionStatusConverter import com.tangem.data.staking.converters.transaction.StakingTransactionTypeConverter +import com.tangem.data.staking.store.YieldsBalancesStore +import com.tangem.data.staking.utils.StakingIdFactory import com.tangem.data.staking.utils.StakingIdFactory.Companion.integrationIdMap import com.tangem.datasource.api.common.response.ApiResponse import com.tangem.datasource.api.common.response.getOrThrow @@ -65,7 +67,6 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.extensions.orZero import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch -import kotlinx.coroutines.plus import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull import timber.log.Timber @@ -76,11 +77,13 @@ internal class DefaultStakingRepository( private val stakeKitApi: StakeKitApi, private val stakingYieldsStore: StakingYieldsStore, private val stakingBalanceStore: StakingBalanceStore, + private val stakingBalanceStoreV2: YieldsBalancesStore, private val cacheRegistry: CacheRegistry, private val dispatchers: CoroutineDispatcherProvider, private val walletManagersFacade: WalletManagersFacade, private val getUserWalletUseCase: GetUserWalletUseCase, private val stakingFeatureToggles: StakingFeatureToggles, + private val stakingIdFactory: StakingIdFactory, moshi: Moshi, ) : StakingRepository { @@ -429,7 +432,7 @@ internal class DefaultStakingRepository( } }.cancellable() - override suspend fun getSingleYieldBalanceSync( + override suspend fun getSingleYieldBalanceSyncLegacy( userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, ): YieldBalance = withContext(dispatchers.io) { @@ -447,6 +450,20 @@ internal class DefaultStakingRepository( ?: YieldBalance.Error(integrationId, address) } + override suspend fun getSingleYieldBalanceSync( + userWalletId: UserWalletId, + cryptoCurrency: CryptoCurrency, + ): YieldBalance { + val stakingId = stakingIdFactory.createForDefault( + userWalletId = userWalletId, + currencyId = cryptoCurrency.id, + network = cryptoCurrency.network, + ) ?: error("Could not create stakingId") + + return stakingBalanceStoreV2.getSyncOrNull(userWalletId = userWalletId, stakingId = stakingId) + ?: YieldBalance.Error(integrationId = stakingId.integrationId, address = stakingId.address) + } + @Suppress("LongMethod") override suspend fun fetchMultiYieldBalance( userWalletId: UserWalletId, @@ -559,26 +576,7 @@ internal class DefaultStakingRepository( } } - override fun getMultiYieldBalanceUpdatesLegacy( - userWalletId: UserWalletId, - cryptoCurrencies: List, - ): Flow = channelFlow { - stakingBalanceStore.get( - userWalletId = userWalletId, - stakingIds = cryptoCurrencies.mapStakingId(userWalletId), - ) - .onEach { - val balances = YieldBalanceListConverter.convert(it) - send(balances) - } - .launchIn(scope = this + dispatchers.io) - - withContext(dispatchers.io) { - fetchMultiYieldBalance(userWalletId, cryptoCurrencies, refresh = false) - } - } - - override suspend fun getMultiYieldBalanceSync( + override suspend fun getMultiYieldBalanceSyncLegacy( userWalletId: UserWalletId, cryptoCurrencies: List, ): YieldBalanceList = withContext(dispatchers.io) { @@ -589,6 +587,20 @@ internal class DefaultStakingRepository( ?: YieldBalanceList.Error } + override suspend fun getMultiYieldBalanceSync( + userWalletId: UserWalletId, + cryptoCurrencies: List, + ): YieldBalanceList { + val stakingIds = cryptoCurrencies.flatMap { + stakingIdFactory.create(userWalletId = userWalletId, currencyId = it.id, network = it.network) + } + + return stakingBalanceStoreV2.getAllSyncOrNull(userWalletId) + ?.filter { it.getStakingId() in stakingIds } + ?.let { YieldBalanceListConverter.convert(value = it.toSet()) } + ?: YieldBalanceList.Error + } + override suspend fun isAnyTokenStaked(userWalletId: UserWalletId): Boolean { return withContext(dispatchers.io) { stakingBalanceStore.getSyncOrNull(userWalletId) diff --git a/data/staking/src/main/java/com/tangem/data/staking/di/StakingDataModule.kt b/data/staking/src/main/java/com/tangem/data/staking/di/StakingDataModule.kt index 43c7003ba5..67dbb87a81 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/di/StakingDataModule.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/di/StakingDataModule.kt @@ -9,7 +9,9 @@ import com.tangem.data.staking.DefaultStakingErrorResolver import com.tangem.data.staking.DefaultStakingRepository import com.tangem.data.staking.DefaultStakingTransactionHashRepository import com.tangem.data.staking.converters.error.StakeKitErrorConverter +import com.tangem.data.staking.store.YieldsBalancesStore import com.tangem.data.staking.toggles.DefaultStakingFeatureToggles +import com.tangem.data.staking.utils.StakingIdFactory import com.tangem.datasource.api.stakekit.StakeKitApi import com.tangem.datasource.api.stakekit.models.response.model.error.StakeKitErrorResponse import com.tangem.datasource.di.NetworkMoshi @@ -41,23 +43,27 @@ internal object StakingDataModule { stakeKitApi: StakeKitApi, stakingYieldsStore: StakingYieldsStore, stakingBalanceStore: StakingBalanceStore, + yieldsBalancesStore: YieldsBalancesStore, cacheRegistry: CacheRegistry, dispatchers: CoroutineDispatcherProvider, walletManagersFacade: WalletManagersFacade, getUserWalletUseCase: GetUserWalletUseCase, stakingFeatureToggles: StakingFeatureToggles, + stakingIdFactory: StakingIdFactory, @NetworkMoshi moshi: Moshi, ): StakingRepository { return DefaultStakingRepository( stakeKitApi = stakeKitApi, stakingYieldsStore = stakingYieldsStore, stakingBalanceStore = stakingBalanceStore, + stakingBalanceStoreV2 = yieldsBalancesStore, cacheRegistry = cacheRegistry, dispatchers = dispatchers, walletManagersFacade = walletManagersFacade, getUserWalletUseCase = getUserWalletUseCase, stakingFeatureToggles = stakingFeatureToggles, moshi = moshi, + stakingIdFactory = stakingIdFactory, ) } @@ -89,7 +95,7 @@ internal object StakingDataModule { @Provides @Singleton - internal fun provideStakingErrorResolver( + fun provideStakingErrorResolver( @NetworkMoshi moshi: Moshi, analyticsEventHandler: AnalyticsEventHandler, ): StakingErrorResolver { @@ -102,7 +108,7 @@ internal object StakingDataModule { @Provides @Singleton - internal fun provideFeatureToggles(featureTogglesManager: FeatureTogglesManager): StakingFeatureToggles { + fun provideFeatureToggles(featureTogglesManager: FeatureTogglesManager): StakingFeatureToggles { return DefaultStakingFeatureToggles(featureTogglesManager) } } \ No newline at end of file diff --git a/data/staking/src/main/java/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcher.kt b/data/staking/src/main/java/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcher.kt index 45cccaea38..7a5e721099 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcher.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/multi/DefaultMultiYieldBalanceFetcher.kt @@ -10,6 +10,7 @@ import com.tangem.domain.core.flow.FlowFetcher import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import javax.inject.Inject /** * Default implementation of [MultiYieldBalanceFetcher] @@ -22,7 +23,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider * [REDACTED_AUTHOR] */ -internal class DefaultMultiYieldBalanceFetcher( +internal class DefaultMultiYieldBalanceFetcher @Inject constructor( private val stakingYieldsStore: StakingYieldsStore, private val yieldsBalancesStore: YieldsBalancesStore, private val stakingIdFactory: StakingIdFactory, diff --git a/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcher.kt b/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcher.kt index 37d48ac768..a59151f771 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcher.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/single/DefaultSingleYieldBalanceFetcher.kt @@ -11,6 +11,7 @@ import com.tangem.domain.staking.fetcher.YieldBalanceFetcherParams import com.tangem.domain.staking.multi.MultiYieldBalanceFetcher import com.tangem.domain.staking.single.SingleYieldBalanceFetcher import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import javax.inject.Inject /** * Default implementation of [MultiYieldBalanceFetcher] @@ -23,7 +24,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider * [REDACTED_AUTHOR] */ -internal class DefaultSingleYieldBalanceFetcher( +internal class DefaultSingleYieldBalanceFetcher @Inject constructor( private val stakingYieldsStore: StakingYieldsStore, private val yieldsBalancesStore: YieldsBalancesStore, private val stakingIdFactory: StakingIdFactory, diff --git a/data/staking/src/main/java/com/tangem/data/staking/store/DefaultYieldsBalancesStore.kt b/data/staking/src/main/java/com/tangem/data/staking/store/DefaultYieldsBalancesStore.kt index 18fb91c240..52a301247f 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/store/DefaultYieldsBalancesStore.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/store/DefaultYieldsBalancesStore.kt @@ -58,14 +58,22 @@ internal class DefaultYieldsBalancesStore( return runtimeStore.get().mapNotNull { it[userWalletId] } } + override suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingId: StakingID): YieldBalance? { + return runtimeStore.getSyncOrNull() + ?.get(userWalletId) + ?.firstOrNull { stakingId == it.getStakingId() } + } + + override suspend fun getAllSyncOrNull(userWalletId: UserWalletId): Set? { + return runtimeStore.getSyncOrNull()?.get(userWalletId) + } + override suspend fun refresh(userWalletId: UserWalletId, stakingId: StakingID) { - updateBalancesInRuntime(userWalletId = userWalletId, stakingIds = setOf(stakingId)) { - it.copySealed(source = StatusSource.CACHE) - } + refresh(userWalletId = userWalletId, stakingIds = setOf(stakingId)) } override suspend fun refresh(userWalletId: UserWalletId, stakingIds: Set) { - updateBalancesInRuntime(userWalletId = userWalletId, stakingIds = stakingIds) { + updateInRuntime(userWalletId = userWalletId, stakingIds = stakingIds) { it.copySealed(source = StatusSource.CACHE) } } @@ -78,9 +86,12 @@ internal class DefaultYieldsBalancesStore( } override suspend fun storeError(userWalletId: UserWalletId, stakingIds: Set) { - updateBalancesInRuntime(userWalletId = userWalletId, stakingIds = stakingIds) { - it.copySealed(source = StatusSource.ONLY_CACHE) - } + updateInRuntime( + userWalletId = userWalletId, + stakingIds = stakingIds, + ifNotFound = ::createErrorYieldBalance, + update = { it.copySealed(source = StatusSource.ONLY_CACHE) }, + ) } private suspend fun storeInRuntime(userWalletId: UserWalletId, values: Set) { @@ -89,9 +100,7 @@ internal class DefaultYieldsBalancesStore( runtimeStore.update(default = emptyMap()) { saved -> saved.toMutableMap().apply { this[userWalletId] = saved[userWalletId] - ?.addOrReplace(newBalances) { old, new -> - old.integrationId == new.integrationId && old.address == new.address - } + ?.addOrReplace(newBalances) { old, new -> old.getStakingId() == new.getStakingId() } ?: newBalances } } @@ -101,33 +110,33 @@ internal class DefaultYieldsBalancesStore( persistenceStore.updateData { current -> current.toMutableMap().apply { this[userWalletId.stringValue] = current[userWalletId.stringValue] - ?.addOrReplace(items = values) { old, new -> - old.integrationId == new.integrationId && old.addresses.address == new.addresses.address - } + ?.addOrReplace(items = values) { old, new -> old.getStakingId() == new.getStakingId() } ?: values } } } - private suspend fun updateBalancesInRuntime( + private suspend fun updateInRuntime( userWalletId: UserWalletId, stakingIds: Set, + ifNotFound: (StakingID) -> YieldBalance? = { null }, update: (YieldBalance) -> YieldBalance, ) { runtimeStore.update(default = emptyMap()) { stored -> stored.toMutableMap().apply { val portfolioBalances = stored[userWalletId].orEmpty() - val balances = stakingIds.mapTo(hashSetOf()) { stakingId -> - val balance = portfolioBalances.firstOrNull { - it.integrationId == stakingId.integrationId && it.address == stakingId.address - } + val balances = stakingIds.mapNotNullTo(hashSetOf()) { stakingId -> + val balance = portfolioBalances + .firstOrNull { stakingId == it.getStakingId() } + ?: ifNotFound(stakingId) + ?: return@mapNotNullTo null - if (balance != null) update(balance) else createDefaultBalance(id = stakingId) + update(balance) } val updatedBalances = portfolioBalances.addOrReplace(items = balances) { old, new -> - old.integrationId == new.integrationId && old.address == new.address + old.getStakingId() == new.getStakingId() } put(key = userWalletId, value = updatedBalances) @@ -135,7 +144,16 @@ internal class DefaultYieldsBalancesStore( } } - private fun createDefaultBalance(id: StakingID): YieldBalance { + private fun createErrorYieldBalance(id: StakingID): YieldBalance { return YieldBalance.Error(integrationId = id.integrationId, address = id.address) } + + private fun YieldBalanceWrapperDTO.getStakingId(): StakingID? { + val integrationId = integrationId + val address = addresses.address + + if (integrationId == null || address.isBlank()) return null + + return StakingID(integrationId = integrationId, address = address) + } } \ No newline at end of file diff --git a/data/staking/src/main/java/com/tangem/data/staking/store/YieldsBalancesStore.kt b/data/staking/src/main/java/com/tangem/data/staking/store/YieldsBalancesStore.kt index 326dc6fc73..4549a72ede 100644 --- a/data/staking/src/main/java/com/tangem/data/staking/store/YieldsBalancesStore.kt +++ b/data/staking/src/main/java/com/tangem/data/staking/store/YieldsBalancesStore.kt @@ -16,6 +16,12 @@ interface YieldsBalancesStore { /** Get flow of [YieldBalance]'s set by [userWalletId] */ fun get(userWalletId: UserWalletId): Flow> + /** Get [YieldBalance] by [userWalletId] and [stakingId] synchronously or null */ + suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingId: StakingID): YieldBalance? + + /** Get all [YieldBalance] by [userWalletId] synchronously or null */ + suspend fun getAllSyncOrNull(userWalletId: UserWalletId): Set? + /** Refresh balance of [stakingId] by [userWalletId] */ suspend fun refresh(userWalletId: UserWalletId, stakingId: StakingID) diff --git a/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreUpdateMethodsTest.kt b/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreUpdateMethodsTest.kt index dc05b3db34..8e6650c1e1 100644 --- a/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreUpdateMethodsTest.kt +++ b/data/staking/src/test/kotlin/com/tangem/data/staking/store/YieldsBalancesStoreUpdateMethodsTest.kt @@ -33,14 +33,7 @@ internal class YieldsBalancesStoreUpdateMethodsTest { fun `refresh the single id if runtime store is empty`() = runTest { store.refresh(userWalletId = userWalletId, stakingId = stakingId) - val runtimeExpected = mapOf( - userWalletId to setOf( - YieldBalance.Error( - integrationId = stakingId.integrationId, - address = stakingId.address, - ), - ), - ) + val runtimeExpected = mapOf(userWalletId to emptySet()) Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) @@ -70,11 +63,7 @@ internal class YieldsBalancesStoreUpdateMethodsTest { fun `refresh the multi ids if runtime store is empty`() = runTest { store.refresh(userWalletId = userWalletId, stakingIds = stakingIds) - val runtimeExpected = mapOf( - userWalletId to stakingIds.mapTo(hashSetOf()) { - YieldBalance.Error(integrationId = it.integrationId, address = it.address) - }, - ) + val runtimeExpected = mapOf(userWalletId to emptySet()) Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected) Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap>()) diff --git a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt index d97ac0f2b9..5f5da8eeb2 100644 --- a/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt +++ b/domain/staking/src/main/java/com/tangem/domain/staking/repositories/StakingRepository.kt @@ -52,6 +52,11 @@ interface StakingRepository { fun getSingleYieldBalanceFlow(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Flow + suspend fun getSingleYieldBalanceSyncLegacy( + userWalletId: UserWalletId, + cryptoCurrency: CryptoCurrency, + ): YieldBalance + suspend fun getSingleYieldBalanceSync(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): YieldBalance suspend fun fetchMultiYieldBalance( @@ -65,10 +70,10 @@ interface StakingRepository { cryptoCurrencies: List, ): Flow - fun getMultiYieldBalanceUpdatesLegacy( + suspend fun getMultiYieldBalanceSyncLegacy( userWalletId: UserWalletId, cryptoCurrencies: List, - ): Flow + ): YieldBalanceList suspend fun getMultiYieldBalanceSync( userWalletId: UserWalletId, 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 14c18382e1..fed0750cf9 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 @@ -16,6 +16,8 @@ import com.tangem.domain.quotes.single.SingleQuoteSupplier 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.staking.single.SingleYieldBalanceProducer +import com.tangem.domain.staking.single.SingleYieldBalanceSupplier import com.tangem.domain.tokens.TokensFeatureToggles import com.tangem.domain.tokens.model.* import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations.Error @@ -46,6 +48,7 @@ abstract class BaseCurrencyStatusOperations( private val multiNetworkStatusSupplier: MultiNetworkStatusSupplier, private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier, private val singleQuoteSupplier: SingleQuoteSupplier, + private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier, private val tokensFeatureToggles: TokensFeatureToggles, ) { @@ -359,10 +362,18 @@ abstract class BaseCurrencyStatusOperations( userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, ): EitherFlow { - return stakingRepository.getSingleYieldBalanceFlow( - userWalletId = userWalletId, - cryptoCurrency = cryptoCurrency, - ).map> { it.right() } + return if (tokensFeatureToggles.isStakingLoadingRefactoringEnabled) { + singleYieldBalanceSupplier( + params = SingleYieldBalanceProducer.Params( + userWalletId = userWalletId, + currencyId = cryptoCurrency.id, + network = cryptoCurrency.network, + ), + ) + } else { + stakingRepository.getSingleYieldBalanceFlow(userWalletId = userWalletId, cryptoCurrency = cryptoCurrency) + } + .map> { it.right() } .catch { emit(Error.DataError(it).left()) } .onEmpty { emit(Error.EmptyYieldBalances.left()) } } @@ -396,10 +407,18 @@ abstract class BaseCurrencyStatusOperations( ): Either { return catch( block = { - stakingRepository.getMultiYieldBalanceSync( - userWalletId, - cryptoCurrencies, - ).right() + if (tokensFeatureToggles.isStakingLoadingRefactoringEnabled) { + stakingRepository.getMultiYieldBalanceSync( + userWalletId = userWalletId, + cryptoCurrencies = cryptoCurrencies, + ) + } else { + stakingRepository.getMultiYieldBalanceSyncLegacy( + userWalletId = userWalletId, + cryptoCurrencies = cryptoCurrencies, + ) + } + .right() }, catch = { Error.EmptyYieldBalances.left() @@ -413,10 +432,12 @@ abstract class BaseCurrencyStatusOperations( ): Either { return catch( block = { - stakingRepository.getSingleYieldBalanceSync( - userWalletId, - cryptoCurrency, - ).right() + if (tokensFeatureToggles.isStakingLoadingRefactoringEnabled) { + stakingRepository.getSingleYieldBalanceSync(userWalletId, cryptoCurrency) + } else { + stakingRepository.getSingleYieldBalanceSyncLegacy(userWalletId, cryptoCurrency) + } + .right() }, catch = { Error.EmptyYieldBalances.left() 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 919ee39358..c222b79fec 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 @@ -21,6 +21,8 @@ import com.tangem.domain.quotes.single.SingleQuoteSupplier 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.staking.single.SingleYieldBalanceProducer +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.* @@ -46,6 +48,7 @@ class CachedCurrenciesStatusesOperations( private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher, private val multiQuoteFetcher: MultiQuoteFetcher, private val singleQuoteSupplier: SingleQuoteSupplier, + private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier, private val tokensFeatureToggles: TokensFeatureToggles, ) : BaseCurrenciesStatusesOperations, BaseCurrencyStatusOperations( @@ -57,6 +60,7 @@ class CachedCurrenciesStatusesOperations( multiNetworkStatusSupplier = multiNetworkStatusSupplier, singleNetworkStatusSupplier = singleNetworkStatusSupplier, singleQuoteSupplier = singleQuoteSupplier, + singleYieldBalanceSupplier = singleYieldBalanceSupplier, tokensFeatureToggles = tokensFeatureToggles, ) { @@ -353,15 +357,19 @@ class CachedCurrenciesStatusesOperations( userWalletId: UserWalletId, cryptoCurrencies: List, ): EitherFlow { - return stakingRepository.getMultiYieldBalanceUpdates(userWalletId, cryptoCurrencies) - .map> { it.right() } - .retryWhen { cause, _ -> - emit(TokenListError.DataError(cause).left()) - // adding delay before retry to avoid spam when flow restarted - delay(RETRY_DELAY) - true - } - .distinctUntilChanged() + return if (tokensFeatureToggles.isStakingLoadingRefactoringEnabled) { + getYieldsBalancesUpdates(userWalletId, cryptoCurrencies) + } else { + stakingRepository.getMultiYieldBalanceUpdates(userWalletId, cryptoCurrencies) + .map> { it.right() } + .retryWhen { cause, _ -> + emit(TokenListError.DataError(cause).left()) + // adding delay before retry to avoid spam when flow restarted + delay(RETRY_DELAY) + true + } + .distinctUntilChanged() + } } // temporary code because token list is built using networks list @@ -423,6 +431,50 @@ class CachedCurrenciesStatusesOperations( .distinctUntilChanged() } + // temporary code because token list is built using networks list + private fun getYieldsBalancesUpdates( + userWalletId: UserWalletId, + cryptoCurrencies: List, + ): EitherFlow { + return channelFlow { + val state = MutableStateFlow(emptyList()) + + cryptoCurrencies.onEach { + launch { + singleYieldBalanceSupplier( + params = SingleYieldBalanceProducer.Params( + userWalletId = userWalletId, + currencyId = it.id, + network = it.network, + ), + ) + .onEach { balance -> + state.update { loadedBalances -> + loadedBalances.addOrReplace(balance) { + it.integrationId == balance.integrationId && it.address == balance.address + } + } + } + .launchIn(scope = this) + } + } + + state + .onEach(::send) + .launchIn(scope = this) + } + .map, Either> { balances -> + val yieldBalanceList = if (balances.isEmpty()) { + YieldBalanceList.Empty + } else { + YieldBalanceList.Data(balances) + } + + yieldBalanceList.right() + } + .distinctUntilChanged() + } + private fun isFetchingStarted(userWalletId: UserWalletId): Boolean { return fetchingState.value[userWalletId]?.let { it.isStarted() || it.isFinished() } ?: false } diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyStatusUpdatesUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyStatusUpdatesUseCaseTest.kt index 841fbf11ab..e084456c8c 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyStatusUpdatesUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyStatusUpdatesUseCaseTest.kt @@ -189,6 +189,7 @@ internal class GetPrimaryCurrencyStatusUpdatesUseCaseTest { multiQuoteFetcher = mockk(), singleQuoteSupplier = mockk(), quotesRepositoryV2 = mockk(), + singleYieldBalanceSupplier = mockk(), ), dispatchers = dispatchers, ) diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt index 04be38d010..51816af928 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockStakingRepository.kt @@ -72,6 +72,11 @@ class MockStakingRepository : StakingRepository { send(YieldBalance.Error(integrationId = null, address = null)) } + override suspend fun getSingleYieldBalanceSyncLegacy( + userWalletId: UserWalletId, + cryptoCurrency: CryptoCurrency, + ): YieldBalance = YieldBalance.Error(integrationId = null, address = null) + override suspend fun getSingleYieldBalanceSync( userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, @@ -96,10 +101,12 @@ class MockStakingRepository : StakingRepository { ) } - override fun getMultiYieldBalanceUpdatesLegacy( + override suspend fun getMultiYieldBalanceSyncLegacy( userWalletId: UserWalletId, cryptoCurrencies: List, - ): Flow = flowOf() + ): YieldBalanceList = YieldBalanceList.Data( + balances = listOf(YieldBalance.Error(integrationId = null, address = null)), + ) override suspend fun getMultiYieldBalanceSync( userWalletId: UserWalletId,