Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-22 18:30:36 +03:00
parent 5bc59f3789
commit d6bf0992d7
13 changed files with 209 additions and 85 deletions

View file

@ -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,
)
}

View file

@ -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<CryptoCurrency>,
): Flow<YieldBalanceList> = 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<CryptoCurrency>,
): YieldBalanceList = withContext(dispatchers.io) {
@ -589,6 +587,20 @@ internal class DefaultStakingRepository(
?: YieldBalanceList.Error
}
override suspend fun getMultiYieldBalanceSync(
userWalletId: UserWalletId,
cryptoCurrencies: List<CryptoCurrency>,
): 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)

View file

@ -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)
}
}

View file

@ -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,

View file

@ -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,

View file

@ -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<YieldBalance>? {
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<StakingID>) {
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<StakingID>) {
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<YieldBalanceWrapperDTO>) {
@ -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<StakingID>,
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)
}
}

View file

@ -16,6 +16,12 @@ interface YieldsBalancesStore {
/** Get flow of [YieldBalance]'s set by [userWalletId] */
fun get(userWalletId: UserWalletId): Flow<Set<YieldBalance>>
/** 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<YieldBalance>?
/** Refresh balance of [stakingId] by [userWalletId] */
suspend fun refresh(userWalletId: UserWalletId, stakingId: StakingID)

View file

@ -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<YieldBalance>())
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected)
Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap<String, Set<YieldBalanceWrapperDTO>>())
@ -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<YieldBalance>())
Truth.assertThat(runtimeStore.getSyncOrNull()).isEqualTo(runtimeExpected)
Truth.assertThat(persistenceStore.data.firstOrNull()).isEqualTo(emptyMap<String, Set<YieldBalanceWrapperDTO>>())

View file

@ -52,6 +52,11 @@ interface StakingRepository {
fun getSingleYieldBalanceFlow(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Flow<YieldBalance>
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<CryptoCurrency>,
): Flow<YieldBalanceList>
fun getMultiYieldBalanceUpdatesLegacy(
suspend fun getMultiYieldBalanceSyncLegacy(
userWalletId: UserWalletId,
cryptoCurrencies: List<CryptoCurrency>,
): Flow<YieldBalanceList>
): YieldBalanceList
suspend fun getMultiYieldBalanceSync(
userWalletId: UserWalletId,

View file

@ -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<Error, YieldBalance> {
return stakingRepository.getSingleYieldBalanceFlow(
userWalletId = userWalletId,
cryptoCurrency = cryptoCurrency,
).map<YieldBalance, Either<Error, YieldBalance>> { 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<YieldBalance, Either<Error, YieldBalance>> { it.right() }
.catch { emit(Error.DataError(it).left()) }
.onEmpty { emit(Error.EmptyYieldBalances.left()) }
}
@ -396,10 +407,18 @@ abstract class BaseCurrencyStatusOperations(
): Either<Error.EmptyYieldBalances, YieldBalanceList> {
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<Error.EmptyYieldBalances, YieldBalance> {
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()

View file

@ -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<CryptoCurrency>,
): EitherFlow<TokenListError, YieldBalanceList> {
return stakingRepository.getMultiYieldBalanceUpdates(userWalletId, cryptoCurrencies)
.map<YieldBalanceList, Either<TokenListError, YieldBalanceList>> { 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<YieldBalanceList, Either<TokenListError, YieldBalanceList>> { 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<CryptoCurrency>,
): EitherFlow<TokenListError, YieldBalanceList> {
return channelFlow {
val state = MutableStateFlow(emptyList<YieldBalance>())
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<List<YieldBalance>, Either<TokenListError, YieldBalanceList>> { 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
}

View file

@ -189,6 +189,7 @@ internal class GetPrimaryCurrencyStatusUpdatesUseCaseTest {
multiQuoteFetcher = mockk(),
singleQuoteSupplier = mockk(),
quotesRepositoryV2 = mockk(),
singleYieldBalanceSupplier = mockk(),
),
dispatchers = dispatchers,
)

View file

@ -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<CryptoCurrency>,
): Flow<YieldBalanceList> = flowOf()
): YieldBalanceList = YieldBalanceList.Data(
balances = listOf(YieldBalance.Error(integrationId = null, address = null)),
)
override suspend fun getMultiYieldBalanceSync(
userWalletId: UserWalletId,