Updated on 2026-08-14

This commit is contained in:
Tangem 2024-10-28 17:27:49 +04:00
parent e6bb75a1fd
commit 47f5ac82df
8 changed files with 34 additions and 194 deletions

View file

@ -67,17 +67,6 @@ internal object TokensDomainModule {
)
}
@Provides
@Singleton
fun provideGetCardTokensListUseCase(
currenciesRepository: CurrenciesRepository,
quotesRepository: QuotesRepository,
networksRepository: NetworksRepository,
stakingRepository: StakingRepository,
): GetNodlTokenListUseCase {
return GetNodlTokenListUseCase(currenciesRepository, quotesRepository, networksRepository, stakingRepository)
}
@Provides
@Singleton
fun provideRemoveCurrencyUseCase(

View file

@ -38,7 +38,6 @@ import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import kotlinx.coroutines.withContext
import timber.log.Timber
@ -207,8 +206,9 @@ internal class DefaultCurrenciesRepository(
if (userWallet.isMultiCurrency) {
getMultiCurrencyWalletCurrenciesUpdates(userWalletId).collect(::send)
} else {
val currency = getSingleCurrencyWalletPrimaryCurrency(userWalletId)
send(listOf(currency))
val currencies = getSingleCurrencyWalletWithCardCurrencies(userWalletId)
send(currencies)
}
}
}
@ -349,26 +349,34 @@ internal class DefaultCurrenciesRepository(
override fun isTokensGrouped(userWalletId: UserWalletId): Flow<Boolean> {
return channelFlow {
ensureIsCorrectUserWallet(userWalletId, isMultiCurrencyWalletExpected = true)
val userWallet = getUserWallet(userWalletId)
launch(dispatchers.io) {
if (userWallet.isMultiCurrency) {
getSavedUserTokensResponse(userWalletId)
.map { it.group == UserTokensResponse.GroupType.NETWORK }
.collect(::send)
.map { response -> response.group == UserTokensResponse.GroupType.NETWORK }
.distinctUntilChanged()
.onEach { isGrouped -> send(isGrouped) }
.launchIn(scope = this + dispatchers.io)
} else {
send(element = false)
}
}.cancellable()
}
}
override fun isTokensSortedByBalance(userWalletId: UserWalletId): Flow<Boolean> {
return channelFlow {
ensureIsCorrectUserWallet(userWalletId, isMultiCurrencyWalletExpected = true)
val userWallet = getUserWallet(userWalletId)
launch(dispatchers.io) {
if (userWallet.isMultiCurrency) {
getSavedUserTokensResponse(userWalletId)
.map { it.sort == UserTokensResponse.SortType.BALANCE }
.collect(::send)
.map { response -> response.sort == UserTokensResponse.SortType.BALANCE }
.distinctUntilChanged()
.onEach { isSorted -> send(isSorted) }
.launchIn(scope = this + dispatchers.io)
} else {
send(element = false)
}
}.cancellable()
}
}
override fun isSendBlockedByPendingTransactions(

View file

@ -1,79 +0,0 @@
package com.tangem.domain.tokens
import arrow.core.left
import com.tangem.domain.core.utils.EitherFlow
import com.tangem.domain.staking.repositories.StakingRepository
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.error.mapper.mapToTokenListError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
import com.tangem.domain.tokens.operations.TokenListOperations
import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.tokens.repository.QuotesRepository
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.transformLatest
/**
* Use case for getting a list of tokens for NODL card
*
* @property currenciesRepository currencies repository
* @property quotesRepository quotes repository
* @property networksRepository networks repository
* @property stakingRepository staking repository
*/
class GetNodlTokenListUseCase(
private val currenciesRepository: CurrenciesRepository,
private val quotesRepository: QuotesRepository,
private val networksRepository: NetworksRepository,
private val stakingRepository: StakingRepository,
) {
@OptIn(ExperimentalCoroutinesApi::class)
operator fun invoke(userWalletId: UserWalletId): EitherFlow<TokenListError, TokenList> {
return getTokensStatuses(userWalletId).transformLatest { maybeTokens ->
maybeTokens.fold(
ifLeft = { error ->
emit(error.left())
},
ifRight = { tokens ->
emitAll(createTokenList(userWalletId, tokens))
},
)
}
}
private fun getTokensStatuses(userWalletId: UserWalletId): EitherFlow<TokenListError, List<CryptoCurrencyStatus>> {
val operations = CurrenciesStatusesOperations(
userWalletId = userWalletId,
currenciesRepository = currenciesRepository,
quotesRepository = quotesRepository,
networksRepository = networksRepository,
stakingRepository = stakingRepository,
)
return operations.getCardCurrenciesStatusesFlow()
.map { maybeCurrenciesStatuses ->
maybeCurrenciesStatuses.mapLeft(CurrenciesStatusesOperations.Error::mapToTokenListError)
}
}
private fun createTokenList(
userWalletId: UserWalletId,
tokens: List<CryptoCurrencyStatus>,
): EitherFlow<TokenListError, TokenList> {
val operations = TokenListOperations(
userWalletId = userWalletId,
tokens = tokens,
currenciesRepository = currenciesRepository,
)
return operations.getTokenListForSingleCurrencyFlow().map { maybeTokenList ->
maybeTokenList.mapLeft(TokenListOperations.Error::mapToTokenListError)
}
}
}

View file

@ -120,50 +120,6 @@ internal class CurrenciesStatusesOperations(
return createCurrencyStatus(currency, quotes, networkStatus, yieldBalances)
}
fun getCardCurrenciesStatusesFlow(): Flow<Either<Error, List<CryptoCurrencyStatus>>> {
return flow {
val nonEmptyCurrencies = recover(
block = { getCurrenciesFromCard(userWalletId) },
recover = {
emit(it.left())
return@flow
},
).toNonEmptyListOrNull()
if (nonEmptyCurrencies == null) {
val emptyCurrenciesStatuses = emptyList<CryptoCurrencyStatus>()
emit(emptyCurrenciesStatuses.right())
return@flow
}
val maybeLoadingCurrenciesStatuses = createCurrenciesStatuses(
currencies = nonEmptyCurrencies,
maybeNetworkStatuses = null,
maybeQuotes = null,
maybeYieldBalances = null,
)
emit(maybeLoadingCurrenciesStatuses)
val (networks, currenciesIds) = getIds(nonEmptyCurrencies)
val currenciesFlow = combine(
getQuotes(currenciesIds),
getNetworksStatuses(networks),
) { maybeQuotes, maybeNetworksStatuses ->
createCurrenciesStatuses(
currencies = nonEmptyCurrencies,
maybeQuotes = maybeQuotes,
maybeNetworkStatuses = maybeNetworksStatuses,
maybeYieldBalances = null,
)
}
emitAll(currenciesFlow)
}
}
suspend fun getCurrencyStatusFlow(currencyId: CryptoCurrency.ID): Flow<Either<Error, CryptoCurrencyStatus>> {
val currency = recover(
block = { getMultiCurrencyWalletCurrency(currencyId) },
@ -377,12 +333,6 @@ internal class CurrenciesStatusesOperations(
)
}
private suspend fun Raise<Error>.getCurrenciesFromCard(userWalletId: UserWalletId): List<CryptoCurrency> {
return catch({ currenciesRepository.getSingleCurrencyWalletWithCardCurrencies(userWalletId) }) {
raise(Error.DataError(it))
}
}
private fun getQuotes(tokensIds: NonEmptySet<CryptoCurrency.ID>): Flow<Either<Error, Set<Quote>>> {
return quotesRepository.getQuotesUpdates(tokensIds)
.map<Set<Quote>, Either<Error, Set<Quote>>> { quotes ->

View file

@ -29,16 +29,6 @@ internal class TokenListOperations(
}
}
fun getTokenListForSingleCurrencyFlow(): Flow<Either<Error, TokenList>> {
return flow {
emit(
either {
createTokenList()
},
)
}
}
private fun Raise<Error>.createTokenList(isGrouped: Boolean, isSortedByBalance: Boolean): TokenList {
val nonEmptyCurrencies = tokens.toNonEmptyListOrNull() ?: return TokenList.Empty
@ -54,22 +44,6 @@ internal class TokenListOperations(
)
}
private fun Raise<Error>.createTokenList(): TokenList {
val nonEmptyCurrencies = tokens.toNonEmptyListOrNull()
?: return TokenList.Empty
val isAnyTokenLoading = nonEmptyCurrencies.any { it.value is CryptoCurrencyStatus.Loading }
val fiatBalanceOperations = TokenListFiatBalanceOperations(nonEmptyCurrencies, isAnyTokenLoading)
return createTokenList(
currencies = nonEmptyCurrencies,
fiatBalance = fiatBalanceOperations.calculateFiatBalance(),
isAnyTokenLoading = isAnyTokenLoading,
isGrouped = false,
isSortedByBalance = false,
)
}
private fun Raise<Error>.createTokenList(
currencies: NonEmptyList<CryptoCurrencyStatus>,
fiatBalance: TotalFiatBalance,

View file

@ -1,12 +1,12 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.tokens.GetNodlTokenListUseCase
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletWarningsSubscriber
@ -23,7 +23,7 @@ internal class SingleWalletWithTokenContentLoader(
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val walletWithFundsChecker: WalletWithFundsChecker,
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
private val getNodlTokenListUseCase: GetNodlTokenListUseCase,
private val tokenListStore: MultiWalletTokenListStore,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
) : WalletContentLoader(id = userWallet.walletId) {
@ -36,7 +36,7 @@ internal class SingleWalletWithTokenContentLoader(
clickIntents = clickIntents,
tokenListAnalyticsSender = tokenListAnalyticsSender,
walletWithFundsChecker = walletWithFundsChecker,
getNodlTokenListUseCase = getNodlTokenListUseCase,
tokenListStore = tokenListStore,
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
),

View file

@ -1,12 +1,12 @@
package com.tangem.feature.wallet.presentation.wallet.loaders.implementors
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.tokens.GetNodlTokenListUseCase
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
@ -19,7 +19,7 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
private val tokenListAnalyticsSender: TokenListAnalyticsSender,
private val walletWithFundsChecker: WalletWithFundsChecker,
private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory,
private val getNodlTokenListUseCase: GetNodlTokenListUseCase,
private val tokenListStore: MultiWalletTokenListStore,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender,
private val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
@ -33,7 +33,7 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
tokenListAnalyticsSender = tokenListAnalyticsSender,
walletWithFundsChecker = walletWithFundsChecker,
getMultiWalletWarningsFactory = getMultiWalletWarningsFactory,
getNodlTokenListUseCase = getNodlTokenListUseCase,
tokenListStore = tokenListStore,
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
walletWarningsAnalyticsSender = walletWarningsAnalyticsSender,
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,

View file

@ -2,23 +2,21 @@ package com.tangem.feature.wallet.presentation.wallet.subscribers
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.core.lce.LceFlow
import com.tangem.domain.core.utils.toLce
import com.tangem.domain.tokens.GetNodlTokenListUseCase
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
import com.tangem.feature.wallet.presentation.wallet.domain.MultiWalletTokenListStore
import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.map
@Suppress("LongParameterList")
internal class SingleWalletWithTokenListSubscriber(
private val userWallet: UserWallet,
private val getNodlTokenListUseCase: GetNodlTokenListUseCase,
private val tokenListStore: MultiWalletTokenListStore,
stateHolder: WalletStateController,
clickIntents: WalletClickIntents,
tokenListAnalyticsSender: TokenListAnalyticsSender,
@ -35,9 +33,9 @@ internal class SingleWalletWithTokenListSubscriber(
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
) {
override fun tokenListFlow(coroutineScope: CoroutineScope): LceFlow<TokenListError, TokenList> =
getNodlTokenListUseCase(
userWallet.walletId,
)
.map { it.toLce() }
override fun tokenListFlow(coroutineScope: CoroutineScope): LceFlow<TokenListError, TokenList> {
tokenListStore.addIfNot(userWallet.walletId, coroutineScope)
return tokenListStore.getOrThrow(userWallet.walletId)
}
}