From 98915e2507f6ece3575df6eaf8920eaf5781cf2e Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 8 Oct 2023 21:10:03 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/TokensDomainModule.kt | 11 +++ .../repository/DefaultCurrenciesRepository.kt | 24 ++++++ .../utils/CardCryptoCurrenciesFactory.kt | 24 ++++++ .../domain/tokens/GetCardTokensListUseCase.kt | 70 +++++++++++++++ .../tokens/GetCurrencyStatusUpdatesUseCase.kt | 19 ++++- .../tokens/GetCurrencyWarningsUseCase.kt | 17 +++- .../tokens/GetNetworkCoinStatusUseCase.kt | 11 ++- .../CurrenciesStatusesOperations.kt | 85 +++++++++++++++++++ .../tokens/operations/TokenListOperations.kt | 26 ++++++ .../tokens/repository/CurrenciesRepository.kt | 25 ++++++ .../repository/MockCurrenciesRepository.kt | 11 +++ .../viewmodels/TokenDetailsViewModel.kt | 43 ++++++---- .../wallet/state/WalletMultiCurrencyState.kt | 1 + .../state/factory/TokenListWithWallet.kt | 9 ++ .../WalletLoadedTokensListConverter.kt | 4 +- .../factory/WalletSkeletonStateConverter.kt | 4 +- .../state/factory/WalletStateFactory.kt | 5 +- .../presentation/wallet/ui/WalletScreen.kt | 2 +- .../utils/TokenListToContentItemsConverter.kt | 35 +++++--- .../utils/TokenListToWalletStateConverter.kt | 11 ++- .../wallet/viewmodels/WalletViewModel.kt | 56 +++++++++++- 21 files changed, 448 insertions(+), 45 deletions(-) create mode 100644 domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCardTokensListUseCase.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/TokenListWithWallet.kt 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 330be53972..ba41f1dda4 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 @@ -47,6 +47,17 @@ internal object TokensDomainModule { return GetTokenListUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers) } + @Provides + @ViewModelScoped + fun provideGetCardTokensListUseCase( + currenciesRepository: CurrenciesRepository, + quotesRepository: QuotesRepository, + networksRepository: NetworksRepository, + dispatchers: CoroutineDispatcherProvider, + ): GetCardTokensListUseCase { + return GetCardTokensListUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers) + } + @Provides @ViewModelScoped fun provideRemoveCurrencyUseCase( diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt index 910b4f7b61..01f5379e8f 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt @@ -26,6 +26,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import timber.log.Timber +@Suppress("LargeClass") internal class DefaultCurrenciesRepository( private val tangemTechApi: TangemTechApi, private val userTokensStore: UserTokensStore, @@ -152,6 +153,29 @@ internal class DefaultCurrenciesRepository( } } + override suspend fun getSingleCurrencyWalletWithCardCurrencies(userWalletId: UserWalletId): List { + return withContext(dispatchers.io) { + val userWallet = getUserWallet(userWalletId) + ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = false) + + cardCurrenciesFactory.createCurrenciesForSingleCurrencyCardWithToken(userWallet.scanResponse) + } + } + + override suspend fun getSingleCurrencyWalletWithCardCurrency( + userWalletId: UserWalletId, + id: CryptoCurrency.ID, + ): CryptoCurrency { + return withContext(dispatchers.io) { + val userWallet = getUserWallet(userWalletId) + ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = false) + + val currency = cardCurrenciesFactory.createCurrenciesForSingleCurrencyCardWithToken(userWallet.scanResponse) + .find { it.id == id } + requireNotNull(currency) { "Unable to find currency with provided ID: $id" } + } + } + override fun getMultiCurrencyWalletCurrenciesUpdates(userWalletId: UserWalletId): Flow> { return channelFlow { val userWallet = getUserWallet(userWalletId) diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCryptoCurrenciesFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCryptoCurrenciesFactory.kt index 11e573b876..a2993452d9 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCryptoCurrenciesFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCryptoCurrenciesFactory.kt @@ -58,4 +58,28 @@ internal class CardCryptoCurrenciesFactory(private val demoConfig: DemoConfig) { return primaryToken ?: coin } + + fun createCurrenciesForSingleCurrencyCardWithToken(scanResponse: ScanResponse): List { + val cardDerivationStyleProvider = scanResponse.derivationStyleProvider + val resolver = scanResponse.cardTypesResolver + val blockchain = resolver.getBlockchain() + + val coin = cryptoCurrencyFactory.createCoin( + blockchain = blockchain, + extraDerivationPath = null, + derivationStyleProvider = cardDerivationStyleProvider, + ) + requireNotNull(coin) { "Coin for the single currency card cannot be null" } + + val primaryToken = resolver.getPrimaryToken()?.let { token -> + cryptoCurrencyFactory.createToken( + sdkToken = token, + blockchain = blockchain, + extraDerivationPath = null, + derivationStyleProvider = cardDerivationStyleProvider, + ) + } + + return listOfNotNull(coin, primaryToken) + } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCardTokensListUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCardTokensListUseCase.kt new file mode 100644 index 0000000000..efe0126403 --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCardTokensListUseCase.kt @@ -0,0 +1,70 @@ +package com.tangem.domain.tokens + +import arrow.core.Either +import arrow.core.left +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 com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.* + +class GetCardTokensListUseCase( + internal val currenciesRepository: CurrenciesRepository, + internal val quotesRepository: QuotesRepository, + internal val networksRepository: NetworksRepository, + internal val dispatchers: CoroutineDispatcherProvider, +) { + + @OptIn(ExperimentalCoroutinesApi::class) + operator fun invoke(userWalletId: UserWalletId): Flow> { + return getTokensStatuses(userWalletId).transformLatest { maybeTokens -> + maybeTokens.fold( + ifLeft = { error -> + emit(error.left()) + }, + ifRight = { tokens -> + emitAll(createTokenList(userWalletId, tokens)) + }, + ) + } + } + + private fun getTokensStatuses( + userWalletId: UserWalletId, + ): Flow>> { + val operations = CurrenciesStatusesOperations( + userWalletId = userWalletId, + currenciesRepository = currenciesRepository, + quotesRepository = quotesRepository, + networksRepository = networksRepository, + ) + + return operations.getCardCurrenciesStatusesFlow() + .map { maybeCurrenciesStatuses -> + maybeCurrenciesStatuses.mapLeft(CurrenciesStatusesOperations.Error::mapToTokenListError) + } + } + + private fun createTokenList( + userWalletId: UserWalletId, + tokens: List, + ): Flow> { + val operations = TokenListOperations( + userWalletId = userWalletId, + tokens = tokens, + currenciesRepository = currenciesRepository, + ) + + return operations.getTokenListForSingleCurrencyFlow().map { maybeTokenList -> + maybeTokenList.mapLeft(TokenListOperations.Error::mapToTokenListError) + } + } +} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyStatusUpdatesUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyStatusUpdatesUseCase.kt index f575854cc6..97f340256f 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyStatusUpdatesUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyStatusUpdatesUseCase.kt @@ -34,15 +34,24 @@ class GetCurrencyStatusUpdatesUseCase( * @param userWalletId The unique identifier of the user's wallet. * @param currencyId The unique identifier of the cryptocurrency. * @param derivationPath currency derivation path. + * @param isSingleWalletWithTokens Indicates whether the user wallet contains only one token on card (old cards) * @return A [Flow] emitting either a [CurrencyStatusError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation. */ operator fun invoke( userWalletId: UserWalletId, currencyId: CryptoCurrency.ID, derivationPath: Network.DerivationPath, + isSingleWalletWithTokens: Boolean, ): Flow> { return flow { - emitAll(getCurrency(userWalletId, currencyId, derivationPath)) + emitAll( + getCurrency( + userWalletId, + currencyId, + derivationPath, + isSingleWalletWithTokens, + ), + ) }.flowOn(dispatchers.io) } @@ -50,6 +59,7 @@ class GetCurrencyStatusUpdatesUseCase( userWalletId: UserWalletId, currencyId: CryptoCurrency.ID, derivationPath: Network.DerivationPath, + isSingleWalletWithTokens: Boolean, ): Flow> { val operations = CurrenciesStatusesOperations( currenciesRepository = currenciesRepository, @@ -58,7 +68,12 @@ class GetCurrencyStatusUpdatesUseCase( userWalletId = userWalletId, ) - return operations.getCurrencyStatusFlow(currencyId, derivationPath).map { maybeCurrency -> + val currencyFlow = if (isSingleWalletWithTokens) { + operations.getCurrencyStatusSingleWalletWithTokensFlow(currencyId) + } else { + operations.getCurrencyStatusFlow(currencyId, derivationPath) + } + return currencyFlow.map { maybeCurrency -> maybeCurrency.mapLeft(CurrenciesStatusesOperations.Error::mapToCurrencyError) } } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt index 994eda5490..b79ffa3f49 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt @@ -25,6 +25,7 @@ class GetCurrencyWarningsUseCase( userWalletId: UserWalletId, currency: CryptoCurrency, derivationPath: Network.DerivationPath, + isSingleWalletWithTokens: Boolean, ): Flow> { return combine( getFeeWarningFlow( @@ -32,6 +33,7 @@ class GetCurrencyWarningsUseCase( networkId = currency.network.id, currencyId = currency.id, derivationPath = derivationPath, + isSingleWalletWithTokens = isSingleWalletWithTokens, ), flowOf(walletManagersFacade.getRentInfo(userWalletId, currency.network)), flowOf(walletManagersFacade.getExistentialDeposit(userWalletId, currency.network)), @@ -54,6 +56,7 @@ class GetCurrencyWarningsUseCase( networkId: Network.ID, currencyId: CryptoCurrency.ID, derivationPath: Network.DerivationPath, + isSingleWalletWithTokens: Boolean, ): Flow { val operations = CurrenciesStatusesOperations( currenciesRepository = currenciesRepository, @@ -62,9 +65,19 @@ class GetCurrencyWarningsUseCase( userWalletId = userWalletId, ) + val currencyFlow = if (isSingleWalletWithTokens) { + operations.getCurrencyStatusSingleWalletWithTokensFlow(currencyId) + } else { + operations.getCurrencyStatusFlow(currencyId, derivationPath) + } + val networkFlow = if (isSingleWalletWithTokens) { + operations.getNetworkCoinForSingleWalletWithTokenFlow(networkId) + } else { + operations.getNetworkCoinFlow(networkId, derivationPath) + } return combine( - operations.getCurrencyStatusFlow(currencyId, derivationPath).map { it.getOrNull() }, - operations.getNetworkCoinFlow(networkId, derivationPath).map { it.getOrNull() }, + currencyFlow.map { it.getOrNull() }, + networkFlow.map { it.getOrNull() }, ) { tokenStatus, coinStatus -> when { tokenStatus != null && coinStatus != null -> { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkCoinStatusUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkCoinStatusUseCase.kt index 44ca1e09cc..f33b882c8e 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkCoinStatusUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetNetworkCoinStatusUseCase.kt @@ -24,6 +24,7 @@ class GetNetworkCoinStatusUseCase( userWalletId: UserWalletId, networkId: Network.ID, derivationPath: Network.DerivationPath, + isSingleWalletWithTokens: Boolean, ): Flow> { return flow { emitAll( @@ -31,6 +32,7 @@ class GetNetworkCoinStatusUseCase( userWalletId = userWalletId, networkId = networkId, derivationPath = derivationPath, + isSingleWalletWithTokens = isSingleWalletWithTokens, ), ) } @@ -41,6 +43,7 @@ class GetNetworkCoinStatusUseCase( userWalletId: UserWalletId, networkId: Network.ID, derivationPath: Network.DerivationPath, + isSingleWalletWithTokens: Boolean, ): Flow> { val operations = CurrenciesStatusesOperations( currenciesRepository = currenciesRepository, @@ -48,8 +51,12 @@ class GetNetworkCoinStatusUseCase( networksRepository = networksRepository, userWalletId = userWalletId, ) - - return operations.getNetworkCoinFlow(networkId, derivationPath).map { maybeCurrency -> + val networkFlow = if (isSingleWalletWithTokens) { + operations.getNetworkCoinForSingleWalletWithTokenFlow(networkId) + } else { + operations.getNetworkCoinFlow(networkId, derivationPath) + } + return networkFlow.map { maybeCurrency -> maybeCurrency.mapLeft(CurrenciesStatusesOperations.Error::mapToCurrencyError) } } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index 7037cb1458..6af9729e8b 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -67,6 +67,44 @@ internal class CurrenciesStatusesOperations( } } + fun getCardCurrenciesStatusesFlow(): Flow>> { + return flow { + val nonEmptyCurrencies = recover( + block = { getCurrenciesFromCard(userWalletId) }, + recover = { + emit(it.left()) + return@flow + }, + ).toNonEmptyListOrNull() + + if (nonEmptyCurrencies == null) { + val emptyCurrenciesStatuses = emptyList() + + emit(emptyCurrenciesStatuses.right()) + return@flow + } + + val maybeLoadingCurrenciesStatuses = createCurrenciesStatuses( + currencies = nonEmptyCurrencies, + maybeNetworkStatuses = null, + maybeQuotes = null, + ) + + emit(maybeLoadingCurrenciesStatuses) + + val (networks, currenciesIds) = getIds(nonEmptyCurrencies) + + val currenciesFlow = combine( + getQuotes(currenciesIds), + getNetworksStatuses(networks), + ) { maybeQuotes, maybeNetworksStatuses -> + createCurrenciesStatuses(nonEmptyCurrencies, maybeQuotes, maybeNetworksStatuses) + } + + emitAll(currenciesFlow) + } + } + suspend fun getCurrencyStatusFlow( currencyId: CryptoCurrency.ID, derivationPath: Network.DerivationPath, @@ -79,6 +117,17 @@ internal class CurrenciesStatusesOperations( return getCurrencyStatusFlow(currency) } + suspend fun getCurrencyStatusSingleWalletWithTokensFlow( + currencyId: CryptoCurrency.ID, + ): Flow> { + val currency = recover( + block = { getSingleCurrencyWalletWithCardTokensCurrency(currencyId) }, + recover = { return flowOf(it.left()) }, + ) + + return getCurrencyStatusFlow(currency) + } + suspend fun getNetworkCoinFlow( networkId: Network.ID, derivationPath: Network.DerivationPath, @@ -91,6 +140,18 @@ internal class CurrenciesStatusesOperations( return getCurrencyStatusFlow(currency) } + suspend fun getNetworkCoinForSingleWalletWithTokenFlow( + networkId: Network.ID, + ): Flow,> { + val currency = recover( + block = { getNetworkCoinForSingleWalletWithToken(networkId) }, + recover = { return flowOf(it.left()) }, + ) + + return getCurrencyStatusFlow(currency) + } + suspend fun getPrimaryCurrencyStatusFlow(): Flow> { val currency = recover( block = { getPrimaryCurrency() }, @@ -199,6 +260,14 @@ internal class CurrenciesStatusesOperations( .bind() } + private suspend fun Raise.getSingleCurrencyWalletWithCardTokensCurrency( + currencyId: CryptoCurrency.ID, + ): CryptoCurrency { + return Either.catch { currenciesRepository.getSingleCurrencyWalletWithCardCurrency(userWalletId, currencyId) } + .mapLeft { Error.DataError(it) } + .bind() + } + private suspend fun Raise.getNetworkCoin( networkId: Network.ID, derivationPath: Network.DerivationPath, @@ -208,6 +277,16 @@ internal class CurrenciesStatusesOperations( .bind() } + private suspend fun Raise.getNetworkCoinForSingleWalletWithToken(networkId: Network.ID): CryptoCurrency { + return Either.catch { + currenciesRepository.getSingleCurrencyWalletWithCardCurrencies(userWalletId) + .find { it.network.id == networkId && it is CryptoCurrency.Coin } + ?: raise(Error.DataError(IllegalStateException("Coin with network $networkId not found for this card"))) + } + .mapLeft { Error.DataError(it) } + .bind() + } + private suspend fun Raise.getPrimaryCurrency(): CryptoCurrency { return catch( block = { currenciesRepository.getSingleCurrencyWalletPrimaryCurrency(userWalletId) }, @@ -215,6 +294,12 @@ internal class CurrenciesStatusesOperations( ) } + private suspend fun Raise.getCurrenciesFromCard(userWalletId: UserWalletId): List { + return catch({ currenciesRepository.getSingleCurrencyWalletWithCardCurrencies(userWalletId) }) { + raise(Error.DataError(it)) + } + } + private fun getQuotes(tokensIds: NonEmptySet): Flow>> { return quotesRepository.getQuotesUpdates(tokensIds) .map, Either>> { quotes -> diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt index 1a03353ee5..1c77b372ce 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt @@ -39,6 +39,16 @@ internal class TokenListOperations( } } + fun getTokenListForSingleCurrencyFlow(): Flow> { + return flow { + emit( + either { + createTokenList() + }, + ) + } + } + private fun Raise.createTokenList(isGrouped: Boolean, isSortedByBalance: Boolean): TokenList { val nonEmptyCurrencies = tokens.toNonEmptyListOrNull() ?: return TokenList.Empty @@ -55,6 +65,22 @@ internal class TokenListOperations( ) } + private fun Raise.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.createTokenList( currencies: NonEmptyList, fiatBalance: TokenList.FiatBalance, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt index 2973c698f6..de719ff8c0 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt @@ -68,6 +68,31 @@ interface CurrenciesRepository { */ suspend fun getSingleCurrencyWalletPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency + /** + * Retrieves the cryptocurrencies for a specific single-currency user wallet with tokens on the card. + * + * @param userWalletId The unique identifier of the user wallet. + * @return The primary cryptocurrency associated with the user wallet. + * @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If multi-currency user wallet + * ID provided. + */ + suspend fun getSingleCurrencyWalletWithCardCurrencies(userWalletId: UserWalletId): List + + /** + * Retrieves the cryptocurrency for a specific single-currency user old wallet + * that stores token on card + * + * @param userWalletId The unique identifier of the user wallet. + * @param id The unique identifier of the cryptocurrency to be retrieved. + * @return The cryptocurrency associated with the user wallet and ID. + * @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If single-currency user wallet + * ID provided. + */ + suspend fun getSingleCurrencyWalletWithCardCurrency( + userWalletId: UserWalletId, + id: CryptoCurrency.ID, + ): CryptoCurrency + /** * Retrieves updates of the list of cryptocurrencies within a multi-currency wallet. * diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt index c50ee469db..c3ae45ed54 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt @@ -60,6 +60,17 @@ internal class MockCurrenciesRepository( return token.getOrElse { e -> throw e } } + override suspend fun getSingleCurrencyWalletWithCardCurrencies(userWalletId: UserWalletId): List { + return tokens.first().getOrElse { e -> throw e } + } + + override suspend fun getSingleCurrencyWalletWithCardCurrency( + userWalletId: UserWalletId, + id: CryptoCurrency.ID, + ): CryptoCurrency { + return token.getOrElse { e -> throw e } + } + override fun getMultiCurrencyWalletCurrenciesUpdates(userWalletId: UserWalletId): Flow> { return tokens.map { it.getOrElse { e -> throw e } } } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 4d5a4ad91a..493e8fa532 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -24,6 +24,7 @@ import com.tangem.domain.tokens.models.analytics.TokenReceiveAnalyticsEvent import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase @@ -133,10 +134,12 @@ internal class TokenDetailsViewModel @Inject constructor( private fun updateWarnings() { viewModelScope.launch(dispatchers.io) { + val wallet = getUserWalletUseCase(userWalletId).getOrElse { return@launch } getCurrencyWarningsUseCase.invoke( userWalletId = userWalletId, currency = cryptoCurrency, derivationPath = cryptoCurrency.network.derivationPath, + isSingleWalletWithTokens = isSingleWalletWithTokens(wallet), ) .distinctUntilChanged() .onEach { uiState = stateFactory.getStateWithNotifications(it) } @@ -145,22 +148,30 @@ internal class TokenDetailsViewModel @Inject constructor( } private fun updateMarketPrice() { - getCurrencyStatusUpdatesUseCase( - userWalletId = userWalletId, - currencyId = cryptoCurrency.id, - derivationPath = cryptoCurrency.network.derivationPath, - ) - .distinctUntilChanged() - .onEach { either -> - uiState = stateFactory.getCurrencyLoadedBalanceState(either) - either.onRight { status -> - cryptoCurrencyStatus = status - updateButtons(userWalletId = userWalletId, currencyStatus = status) + viewModelScope.launch(dispatchers.io) { + val wallet = getUserWalletUseCase(userWalletId).getOrElse { return@launch } + getCurrencyStatusUpdatesUseCase( + userWalletId = userWalletId, + currencyId = cryptoCurrency.id, + derivationPath = cryptoCurrency.network.derivationPath, + isSingleWalletWithTokens = isSingleWalletWithTokens(wallet), + ) + .distinctUntilChanged() + .onEach { either -> + uiState = stateFactory.getCurrencyLoadedBalanceState(either) + either.onRight { status -> + cryptoCurrencyStatus = status + updateButtons(userWalletId = userWalletId, currencyStatus = status) + } } - } - .flowOn(dispatchers.io) - .launchIn(viewModelScope) - .saveIn(marketPriceJobHolder) + .flowOn(dispatchers.io) + .launchIn(viewModelScope) + .saveIn(marketPriceJobHolder) + } + } + + private fun isSingleWalletWithTokens(userWallet: UserWallet): Boolean { + return userWallet.scanResponse.walletData?.token != null && !userWallet.isMultiCurrency } /** @@ -251,10 +262,12 @@ internal class TokenDetailsViewModel @Inject constructor( private fun sendToken(status: CryptoCurrencyStatus) { viewModelScope.launch(dispatchers.io) { + val wallet = getUserWalletUseCase(userWalletId).getOrElse { return@launch } val maybeCoinStatus = getNetworkCoinStatusUseCase( userWalletId = userWalletId, networkId = status.currency.network.id, derivationPath = status.currency.network.derivationPath, + isSingleWalletWithTokens = isSingleWalletWithTokens(wallet), ).firstOrNull() maybeCoinStatus?.onRight { coinStatus -> diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt index 0a71550228..1e8fcbcc9e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt @@ -27,6 +27,7 @@ internal sealed class WalletMultiCurrencyState : WalletState.ContentState() { override val tokensListState: WalletTokensListState, override val event: StateEvent = consumedEvent(), override val isBalanceHidden: Boolean, + val isManageTokensAvailable: Boolean = true, val tokenActionsBottomSheet: ActionsBottomSheetConfig?, val onManageTokensClick: () -> Unit, ) : WalletMultiCurrencyState() diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/TokenListWithWallet.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/TokenListWithWallet.kt new file mode 100644 index 0000000000..bd9060c2ce --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/TokenListWithWallet.kt @@ -0,0 +1,9 @@ +package com.tangem.feature.wallet.presentation.wallet.state.factory + +import com.tangem.domain.tokens.model.TokenList +import com.tangem.domain.wallets.models.UserWallet + +data class TokenListWithWallet( + val tokenList: TokenList, + val wallet: UserWallet, +) \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt index 3cd576ca7d..f2d12adf65 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt @@ -31,7 +31,7 @@ internal class WalletLoadedTokensListConverter( appCurrencyProvider: Provider, currentWalletProvider: Provider, clickIntents: WalletClickIntents, -) : Converter, WalletState> { +) : Converter, WalletState> { private val tokenListStateConverter = TokenListToWalletStateConverter( currentStateProvider = currentStateProvider, @@ -40,7 +40,7 @@ internal class WalletLoadedTokensListConverter( clickIntents = clickIntents, ) - override fun convert(value: Either): WalletState { + override fun convert(value: Either): WalletState { return value.fold( ifLeft = tokenListErrorConverter::convert, ifRight = tokenListStateConverter::convert, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt index dbdd8d5ea9..ead7b287fc 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt @@ -38,7 +38,9 @@ internal class WalletSkeletonStateConverter( override fun convert(value: SkeletonModel): WalletState.ContentState { val selectedWallet = value.wallets[value.selectedWalletIndex] - return if (selectedWallet.isMultiCurrency) { + val isSingleWalletWithToken = !selectedWallet.isMultiCurrency && + selectedWallet.scanResponse.walletData?.token != null + return if (selectedWallet.isMultiCurrency || isSingleWalletWithToken) { createMultiCurrencyState(value = value) } else { createSingleCurrencyState(value = value, currencyName = selectedWallet.getPrimaryCurrencyName()) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt index d8ea0d3ff9..961c4078dd 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt @@ -13,7 +13,6 @@ import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenActionsState -import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.domain.txhistory.models.TxHistoryStateError @@ -160,8 +159,8 @@ internal class WalletStateFactory( ) } - fun getStateByTokensList(maybeTokenList: Either): WalletState { - return loadedTokensListConverter.convert(maybeTokenList) + fun getStateByTokensList(maybeTokenListWithWallet: Either): WalletState { + return loadedTokensListConverter.convert(maybeTokenListWithWallet) } fun getStateByTokenListError(error: TokenListError): WalletState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index 449909bf02..7cef25b9ea 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -217,7 +217,7 @@ private fun BaseScaffold( topBar = { WalletTopBar(config = state.topBarConfig) }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, floatingActionButton = { - if (state is WalletMultiCurrencyState.Content) { + if (state is WalletMultiCurrencyState.Content && state.isManageTokensAvailable) { ManageTokensButton(onManageTokensClick = state.onManageTokensClick) } }, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt index 45442e30c8..13626e2988 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt @@ -9,6 +9,7 @@ import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState.OrganizeTokensButtonState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState.TokensListItemState +import com.tangem.feature.wallet.presentation.wallet.state.factory.TokenListWithWallet import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.PersistentList @@ -18,23 +19,25 @@ import kotlinx.collections.immutable.persistentListOf internal class TokenListToContentItemsConverter( appCurrencyProvider: Provider, private val clickIntents: WalletClickIntents, -) : Converter { +) : Converter { private val tokenStatusConverter = CryptoCurrencyStatusToTokenItemConverter( appCurrencyProvider = appCurrencyProvider, clickIntents = clickIntents, ) - override fun convert(value: TokenList): WalletTokensListState { - return when (value) { + override fun convert(value: TokenListWithWallet): WalletTokensListState { + val isSingleCurrencyWalletWithToken = !value.wallet.isMultiCurrency && + value.wallet.scanResponse.walletData?.token != null + return when (val tokenList = value.tokenList) { is TokenList.Empty -> WalletTokensListState.Empty is TokenList.GroupedByNetwork -> WalletTokensListState.Content( - items = value.mapToMultiCurrencyItems(), - organizeTokensButton = value.mapToOrganizeTokensButtonState(), + items = tokenList.mapToMultiCurrencyItems(), + organizeTokensButton = tokenList.mapToOrganizeTokensButtonState(isSingleCurrencyWalletWithToken), ) is TokenList.Ungrouped -> WalletTokensListState.Content( - items = value.mapToMultiCurrencyItems(), - organizeTokensButton = value.mapToOrganizeTokensButtonState(), + items = tokenList.mapToMultiCurrencyItems(), + organizeTokensButton = tokenList.mapToOrganizeTokensButtonState(isSingleCurrencyWalletWithToken), ) } } @@ -51,17 +54,23 @@ internal class TokenListToContentItemsConverter( } } - private fun TokenList.GroupedByNetwork.mapToOrganizeTokensButtonState(): OrganizeTokensButtonState { + private fun TokenList.GroupedByNetwork.mapToOrganizeTokensButtonState( + isSingleCurrencyWithTokenWallet: Boolean, + ): OrganizeTokensButtonState { return getOrganizeTokensButtonState( isLoading = totalFiatBalance is TokenList.FiatBalance.Loading, currenciesSize = groups.flatMap(NetworkGroup::currencies).size, + isSingleCurrencyWithTokenWallet = isSingleCurrencyWithTokenWallet, ) } - private fun TokenList.Ungrouped.mapToOrganizeTokensButtonState(): OrganizeTokensButtonState { + private fun TokenList.Ungrouped.mapToOrganizeTokensButtonState( + isSingleCurrencyWithTokenWallet: Boolean, + ): OrganizeTokensButtonState { return getOrganizeTokensButtonState( isLoading = totalFiatBalance is TokenList.FiatBalance.Loading, currenciesSize = currencies.size, + isSingleCurrencyWithTokenWallet = isSingleCurrencyWithTokenWallet, ) } @@ -88,8 +97,12 @@ internal class TokenListToContentItemsConverter( return this } - private fun getOrganizeTokensButtonState(isLoading: Boolean, currenciesSize: Int): OrganizeTokensButtonState { - return if (currenciesSize > 1) { + private fun getOrganizeTokensButtonState( + isLoading: Boolean, + currenciesSize: Int, + isSingleCurrencyWithTokenWallet: Boolean, + ): OrganizeTokensButtonState { + return if (currenciesSize > 1 && !isSingleCurrencyWithTokenWallet) { OrganizeTokensButtonState.Visible( isEnabled = !isLoading, onClick = clickIntents::onOrganizeTokensClick, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt index 896f278745..2228f63598 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt @@ -8,6 +8,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencySt import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig +import com.tangem.feature.wallet.presentation.wallet.state.factory.TokenListWithWallet import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.toPersistentList @@ -18,19 +19,23 @@ internal class TokenListToWalletStateConverter( private val currentWalletProvider: Provider, private val appCurrencyProvider: Provider, clickIntents: WalletClickIntents, -) : Converter { +) : Converter { private val tokenListToContentConverter = TokenListToContentItemsConverter( appCurrencyProvider = appCurrencyProvider, clickIntents = clickIntents, ) - override fun convert(value: TokenList): WalletState { + override fun convert(value: TokenListWithWallet): WalletState { + val tokenList = value.tokenList + val isSingleCurrencyWalletWithToken = !value.wallet.isMultiCurrency && + value.wallet.scanResponse.walletData?.token != null return when (val state = currentStateProvider()) { is WalletMultiCurrencyState.Content -> { state.copy( - walletsListConfig = state.updateSelectedWallet(fiatBalance = value.totalFiatBalance), + walletsListConfig = state.updateSelectedWallet(fiatBalance = tokenList.totalFiatBalance), tokensListState = tokenListToContentConverter.convert(value = value), + isManageTokensAvailable = !isSingleCurrencyWalletWithToken, ) } is WalletMultiCurrencyState.Locked, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 8c97e1e814..b27abb5602 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -62,6 +62,7 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.PortfolioEvent import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent import com.tangem.feature.wallet.presentation.wallet.state.* import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.factory.TokenListWithWallet import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletStateFactory import com.tangem.operations.derivation.ExtendedPublicKeysMap import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -92,6 +93,7 @@ internal class WalletViewModel @Inject constructor( private val updateWalletUseCase: UpdateWalletUseCase, private val deleteWalletUseCase: DeleteWalletUseCase, private val getTokenListUseCase: GetTokenListUseCase, + private val getCardTokensListUseCase: GetCardTokensListUseCase, private val fetchTokenListUseCase: FetchTokenListUseCase, private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase, private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase, @@ -592,10 +594,14 @@ internal class WalletViewModel @Inject constructor( viewModelScope.launch(dispatchers.io) { val userWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex) + val isSingleWalletWithTokens = !userWallet.isMultiCurrency && + userWallet.scanResponse.walletData?.token != null + getNetworkCoinStatusUseCase( userWalletId = userWallet.walletId, networkId = cryptoCurrencyStatus.currency.network.id, derivationPath = cryptoCurrencyStatus.currency.network.derivationPath, + isSingleWalletWithTokens = isSingleWalletWithTokens, ) .take(count = 1) .collectLatest { @@ -916,6 +922,9 @@ internal class WalletViewModel @Inject constructor( uiState = stateFactory.getLockedState() } wallet.isMultiCurrency -> getMultiCurrencyContent(wallet, index) + isSingleWalletWithTokens(wallet) -> { + getSingleCurrencyWithTokenContent(index) + } !wallet.isMultiCurrency -> getSingleCurrencyContent(index) } } @@ -933,7 +942,7 @@ internal class WalletViewModel @Inject constructor( tokenListFlow .distinctUntilChanged() .onEach { maybeTokenList -> - uiState = stateFactory.getStateByTokensList(maybeTokenList) + uiState = stateFactory.getStateByTokensList(maybeTokenList.getTokenListWithWallet(wallet)) maybeTokenList.onRight { checkMultiWalletWithFunds(it) } @@ -978,6 +987,10 @@ internal class WalletViewModel @Inject constructor( .saveIn(updateWcJobHolder) } + private fun isSingleWalletWithTokens(userWallet: UserWallet): Boolean { + return userWallet.scanResponse.walletData?.token != null && !userWallet.isMultiCurrency + } + private fun List.isAllCurrenciesLoaded(): Boolean { return !this.any { it.value is CryptoCurrencyStatus.Loading } } @@ -1008,6 +1021,14 @@ internal class WalletViewModel @Inject constructor( } } + private fun Either.getTokenListWithWallet( + userWallet: UserWallet, + ): Either { + return this.map { + TokenListWithWallet(it, userWallet) + } + } + private fun getSingleCurrencyContent(index: Int) { val wallet = getWallet(index) getPrimaryCurrencyStatusUpdatesUseCase(wallet.walletId) @@ -1032,6 +1053,30 @@ internal class WalletViewModel @Inject constructor( .saveIn(marketPriceJobHolder) } + private fun getSingleCurrencyWithTokenContent(walletIndex: Int) { + val state = requireNotNull(uiState as? WalletMultiCurrencyState) { + "Impossible to get a token list updates if state isn't WalletMultiCurrencyState" + } + + val wallet = getWallet(walletIndex) + + getCardTokensListUseCase(userWalletId = state.walletsListConfig.wallets[walletIndex].id) + .distinctUntilChanged() + .onEach { maybeTokenList -> + uiState = stateFactory.getStateByTokensList(maybeTokenList.getTokenListWithWallet(wallet)) + + maybeTokenList.onRight { checkMultiWalletWithFunds(it) } + + updateNotifications( + index = walletIndex, + tokenList = maybeTokenList.fold(ifLeft = { null }, ifRight = { it }), + ) + } + .flowOn(dispatchers.io) + .launchIn(viewModelScope) + .saveIn(tokensJobHolder) + } + private fun updateTxHistory(userWalletId: UserWalletId, currency: CryptoCurrency, refresh: Boolean) { viewModelScope.launch(dispatchers.io) { val txHistoryItemsCountEither = txHistoryItemsCountUseCase( @@ -1095,10 +1140,15 @@ internal class WalletViewModel @Inject constructor( val wallet = getWallet(walletIndex) viewModelScope.launch(dispatchers.io) { - val result = fetchTokenListUseCase(wallet.walletId, refresh = true) + if (isSingleWalletWithTokens(wallet)) { + // TODO add refresh for nodl cards ([REDACTED_JIRA]) + delay(timeMillis = 1000) + } else { + val result = fetchTokenListUseCase(wallet.walletId, refresh = true) + uiState = result.fold(stateFactory::getStateByTokenListError) { uiState } + } uiState = stateFactory.getRefreshedState() - uiState = result.fold(stateFactory::getStateByTokenListError) { uiState } }.saveIn(refreshContentJobHolder) }