Updated on 2026-08-14
This commit is contained in:
parent
c1d4bf79cb
commit
799c132a5b
13 changed files with 252 additions and 80 deletions
|
|
@ -27,6 +27,7 @@ dependencies {
|
|||
implementation(projects.features.swap.domain.models)
|
||||
implementation(projects.domain.promo.models)
|
||||
implementation(projects.domain.promo)
|
||||
implementation(projects.domain.networks)
|
||||
|
||||
/** Project - Api */
|
||||
implementation(projects.features.send.api)
|
||||
|
|
@ -50,6 +51,7 @@ dependencies {
|
|||
/** Tests */
|
||||
testImplementation(deps.test.junit)
|
||||
testImplementation(deps.test.coroutine)
|
||||
testImplementation(deps.test.mockk)
|
||||
testImplementation(tangemDeps.blockchain) {
|
||||
exclude(module = "joda-time")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.tokens.operations
|
||||
|
||||
import arrow.core.*
|
||||
import arrow.core.raise.either
|
||||
import arrow.core.raise.recover
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
|
|
@ -9,6 +10,9 @@ import com.tangem.domain.core.lce.lceFlow
|
|||
import com.tangem.domain.core.utils.EitherFlow
|
||||
import com.tangem.domain.core.utils.lceContent
|
||||
import com.tangem.domain.core.utils.lceError
|
||||
import com.tangem.domain.networks.multi.MultiNetworkStatusFetcher
|
||||
import com.tangem.domain.networks.single.SingleNetworkStatusProducer
|
||||
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalanceList
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
|
|
@ -21,15 +25,19 @@ import com.tangem.domain.tokens.repository.NetworksRepository
|
|||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.domain.tokens.utils.extractAddress
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.extensions.addOrReplace
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
class CachedCurrenciesStatusesOperations(
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val stakingRepository: StakingRepository,
|
||||
@Suppress("UnusedPrivateMember") private val tokensFeatureToggles: TokensFeatureToggles,
|
||||
private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier,
|
||||
private val multiNetworkStatusFetcher: MultiNetworkStatusFetcher,
|
||||
private val tokensFeatureToggles: TokensFeatureToggles,
|
||||
) : BaseCurrenciesStatusesOperations,
|
||||
BaseCurrencyStatusOperations(currenciesRepository, quotesRepository, networksRepository, stakingRepository) {
|
||||
|
||||
|
|
@ -145,20 +153,26 @@ class CachedCurrenciesStatusesOperations(
|
|||
networks: Set<Network>,
|
||||
currenciesIds: Set<CryptoCurrency.ID>,
|
||||
currencies: List<CryptoCurrency>,
|
||||
): Either<Throwable, Unit> {
|
||||
return coroutineScope {
|
||||
Either.catch {
|
||||
awaitAll(
|
||||
async { networksRepository.fetchNetworkStatuses(userWalletId, networks) },
|
||||
async {
|
||||
val rawCurrenciesIds = currenciesIds.mapNotNullTo(mutableSetOf()) { it.rawCurrencyId }
|
||||
quotesRepository.fetchQuotes(rawCurrenciesIds)
|
||||
},
|
||||
async { stakingRepository.fetchMultiYieldBalance(userWalletId, currencies) },
|
||||
)
|
||||
}
|
||||
.map { }
|
||||
): Either<Throwable, Unit> = either {
|
||||
coroutineScope {
|
||||
awaitAll(
|
||||
async {
|
||||
if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
|
||||
multiNetworkStatusFetcher(
|
||||
params = MultiNetworkStatusFetcher.Params(userWalletId, networks),
|
||||
)
|
||||
} else {
|
||||
networksRepository.fetchNetworkStatuses(userWalletId, networks)
|
||||
}
|
||||
},
|
||||
async {
|
||||
val rawCurrenciesIds = currenciesIds.mapNotNullTo(mutableSetOf()) { it.rawCurrencyId }
|
||||
quotesRepository.fetchQuotes(rawCurrenciesIds)
|
||||
},
|
||||
async { stakingRepository.fetchMultiYieldBalance(userWalletId, currencies) },
|
||||
)
|
||||
}
|
||||
.map { }
|
||||
}
|
||||
|
||||
private fun createCurrenciesStatuses(
|
||||
|
|
@ -251,31 +265,44 @@ class CachedCurrenciesStatusesOperations(
|
|||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): EitherFlow<Error, Set<NetworkStatus>> {
|
||||
return networksRepository.getNetworkStatusesUpdates(userWalletId, setOf(network))
|
||||
.map<Set<NetworkStatus>, Either<Error, Set<NetworkStatus>>> { it.right() }
|
||||
.retryWhen { cause, _ ->
|
||||
emit(Error.DataError(cause).left())
|
||||
// adding delay before retry to avoid spam when flow restarted
|
||||
delay(RETRY_DELAY)
|
||||
true
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.onEmpty { emit(Error.EmptyNetworksStatuses.left()) }
|
||||
return if (tokensFeatureToggles.isNetworksLoadingRefactoringEnabled) {
|
||||
singleNetworkStatusSupplier(
|
||||
params = SingleNetworkStatusProducer.Params(userWalletId = userWalletId, network = network),
|
||||
)
|
||||
.map<NetworkStatus, Either<Error, Set<NetworkStatus>>> { setOf(it).right() }
|
||||
.distinctUntilChanged()
|
||||
.onEmpty { emit(Error.EmptyNetworksStatuses.left()) }
|
||||
} else {
|
||||
networksRepository.getNetworkStatusesUpdates(userWalletId, setOf(network))
|
||||
.map<Set<NetworkStatus>, Either<Error, Set<NetworkStatus>>> { it.right() }
|
||||
.retryWhen { cause, _ ->
|
||||
emit(Error.DataError(cause).left())
|
||||
// adding delay before retry to avoid spam when flow restarted
|
||||
delay(RETRY_DELAY)
|
||||
true
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.onEmpty { emit(Error.EmptyNetworksStatuses.left()) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNetworksStatuses(
|
||||
userWalletId: UserWalletId,
|
||||
networks: NonEmptySet<Network>,
|
||||
): EitherFlow<TokenListError, Set<NetworkStatus>> {
|
||||
return networksRepository.getNetworkStatusesUpdates(userWalletId, networks)
|
||||
.map<Set<NetworkStatus>, Either<TokenListError, Set<NetworkStatus>>> { 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.isNetworksLoadingRefactoringEnabled) {
|
||||
getNetworkStatusesUpdates(userWalletId, networks)
|
||||
} else {
|
||||
networksRepository.getNetworkStatusesUpdates(userWalletId, networks)
|
||||
.map<Set<NetworkStatus>, Either<TokenListError, Set<NetworkStatus>>> { it.right() }
|
||||
.retryWhen { cause, _ ->
|
||||
emit(TokenListError.DataError(cause).left())
|
||||
// adding delay before retry to avoid spam when flow restarted
|
||||
delay(RETRY_DELAY)
|
||||
true
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getYieldBalances(
|
||||
|
|
@ -293,6 +320,36 @@ class CachedCurrenciesStatusesOperations(
|
|||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
// temporary code because token list is built using networks list
|
||||
private fun getNetworkStatusesUpdates(
|
||||
userWalletId: UserWalletId,
|
||||
networks: NonEmptySet<Network>,
|
||||
): EitherFlow<TokenListError, Set<NetworkStatus>> {
|
||||
return channelFlow {
|
||||
val state = MutableStateFlow(emptySet<NetworkStatus>())
|
||||
|
||||
networks.onEach {
|
||||
launch {
|
||||
singleNetworkStatusSupplier(
|
||||
params = SingleNetworkStatusProducer.Params(userWalletId = userWalletId, network = it),
|
||||
)
|
||||
.onEach { status ->
|
||||
state.update { loadedStatuses ->
|
||||
loadedStatuses.addOrReplace(status) { it.network == status.network }
|
||||
}
|
||||
}
|
||||
.launchIn(scope = this)
|
||||
}
|
||||
}
|
||||
|
||||
state
|
||||
.onEach(::send)
|
||||
.launchIn(scope = this)
|
||||
}
|
||||
.map<Set<NetworkStatus>, Either<TokenListError, Set<NetworkStatus>>> { it.right() }
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal const val RETRY_DELAY = 2000L
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,17 +35,6 @@ interface NetworksRepository {
|
|||
* */
|
||||
suspend fun fetchNetworkStatuses(userWalletId: UserWalletId, networks: Set<Network>, refresh: Boolean = false)
|
||||
|
||||
/**
|
||||
* Retrieves updates of network statuses of specified blockchain networks for a specific user wallet.
|
||||
*
|
||||
* Loads remote network statuses if they have expired.
|
||||
*
|
||||
* @param userWalletId The unique identifier of the user wallet.
|
||||
* @param networks A set of network which statuses are to be retrieved.
|
||||
* @return A [Flow] emitting a set of [NetworkStatus] objects corresponding to the specified networks.
|
||||
*/
|
||||
fun getNetworkStatusesUpdatesLegacy(userWalletId: UserWalletId, networks: Set<Network>): Flow<Set<NetworkStatus>>
|
||||
|
||||
/**
|
||||
* Fetches pending transactions for given network
|
||||
*
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import com.tangem.domain.tokens.repository.MockQuotesRepository
|
|||
import com.tangem.domain.tokens.repository.MockStakingRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.mockk
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
|
@ -176,9 +177,12 @@ internal class GetPrimaryCurrencyStatusUpdatesUseCaseTest {
|
|||
quotesRepository = MockQuotesRepository(quotes),
|
||||
networksRepository = MockNetworksRepository(statuses),
|
||||
stakingRepository = MockStakingRepository(),
|
||||
|
||||
tokensFeatureToggles = object : TokensFeatureToggles {
|
||||
override val isNetworksLoadingRefactoringEnabled: Boolean = false
|
||||
},
|
||||
singleNetworkStatusSupplier = mockk(),
|
||||
multiNetworkStatusFetcher = mockk(),
|
||||
),
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -26,13 +26,6 @@ internal class MockNetworksRepository(
|
|||
/* no-op */
|
||||
}
|
||||
|
||||
override fun getNetworkStatusesUpdatesLegacy(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network>,
|
||||
): Flow<Set<NetworkStatus>> {
|
||||
return statuses.map { it.getOrElse { e -> throw e } }
|
||||
}
|
||||
|
||||
override suspend fun fetchNetworkPendingTransactions(userWalletId: UserWalletId, networks: Set<Network>) {
|
||||
// no-op
|
||||
}
|
||||
|
|
@ -42,7 +35,7 @@ internal class MockNetworksRepository(
|
|||
networks: Set<Network>,
|
||||
refresh: Boolean,
|
||||
): Set<NetworkStatus> {
|
||||
return getNetworkStatusesUpdatesLegacy(userWalletId, networks).first()
|
||||
return statuses.map { it.getOrElse { e -> throw e } }.first()
|
||||
}
|
||||
|
||||
override fun isNeedToCreateAccountWithoutReserve(network: Network) = false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue