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 ba41f1dda4..026bf203bb 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 @@ -122,6 +122,16 @@ internal object TokensDomainModule { return FetchCurrencyStatusUseCase(currenciesRepository, networksRepository, quotesRepository) } + @Provides + @ViewModelScoped + fun provideFetchCardTokenListUseCase( + currenciesRepository: CurrenciesRepository, + quotesRepository: QuotesRepository, + networksRepository: NetworksRepository, + ): FetchCardTokenListUseCase { + return FetchCardTokenListUseCase(currenciesRepository, networksRepository, quotesRepository) + } + @Provides @ViewModelScoped fun provideGetCryptoCurrencyUseCase(currenciesRepository: CurrenciesRepository): GetCryptoCurrencyUseCase { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchCardTokenListUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchCardTokenListUseCase.kt new file mode 100644 index 0000000000..7448714472 --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/FetchCardTokenListUseCase.kt @@ -0,0 +1,72 @@ +package com.tangem.domain.tokens + +import arrow.core.Either +import arrow.core.raise.Raise +import arrow.core.raise.catch +import arrow.core.raise.either +import com.tangem.domain.tokens.error.TokenListError +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.Network +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.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope + +class FetchCardTokenListUseCase( + private val currenciesRepository: CurrenciesRepository, + private val networksRepository: NetworksRepository, + private val quotesRepository: QuotesRepository, +) { + + suspend operator fun invoke(userWalletId: UserWalletId, refresh: Boolean = false): Either { + return either { + val currencies = fetchCurrencies(userWalletId = userWalletId) + + coroutineScope { + val fetchStatuses = async { + fetchNetworksStatuses( + userWalletId = userWalletId, + networks = currencies.mapTo(destination = hashSetOf(), transform = CryptoCurrency::network), + refresh = refresh, + ) + } + val fetchQuotes = async { + fetchQuotes( + currenciesIds = currencies.mapTo(destination = hashSetOf(), transform = CryptoCurrency::id), + refresh = refresh, + ) + } + + awaitAll(fetchStatuses, fetchQuotes) + } + } + } + + private suspend fun Raise.fetchCurrencies(userWalletId: UserWalletId): List { + return catch( + block = { currenciesRepository.getSingleCurrencyWalletWithCardCurrencies(userWalletId = userWalletId) }, + catch = { raise(TokenListError.DataError(it)) }, + ) + } + + private suspend fun Raise.fetchNetworksStatuses( + userWalletId: UserWalletId, + networks: Set, + refresh: Boolean, + ) { + catch( + block = { networksRepository.getNetworkStatusesSync(userWalletId, networks, refresh) }, + catch = { raise(TokenListError.DataError(it)) }, + ) + } + + private suspend fun fetchQuotes(currenciesIds: Set, refresh: Boolean) { + catch( + block = { quotesRepository.getQuotesSync(currenciesIds, refresh) }, + catch = { /* Ignore error */ }, + ) + } +} \ No newline at end of file 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 f0b0798819..23054b94df 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 @@ -97,6 +97,7 @@ internal class WalletViewModel @Inject constructor( private val getTokenListUseCase: GetTokenListUseCase, private val getCardTokensListUseCase: GetCardTokensListUseCase, private val fetchTokenListUseCase: FetchTokenListUseCase, + private val fetchCardTokenListUseCase: FetchCardTokenListUseCase, private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase, private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase, private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase, @@ -544,6 +545,24 @@ internal class WalletViewModel @Inject constructor( } } + private fun refreshMultiCurrencyContent(walletIndex: Int) { + uiState = stateFactory.getRefreshingState() + + viewModelScope.launch(dispatchers.main) { + val wallet = getWallet(walletIndex) + + val maybeFetchResult = if (isSingleWalletWithTokens(wallet)) { + fetchCardTokenListUseCase(userWalletId = wallet.walletId, refresh = true) + } else { + fetchTokenListUseCase(userWalletId = wallet.walletId, refresh = true) + } + + maybeFetchResult.onLeft { uiState = stateFactory.getStateByTokenListError(it) } + + uiState = stateFactory.getRefreshedState() + }.saveIn(refreshContentJobHolder) + } + override fun onOrganizeTokensClick() { analyticsEventsHandler.send(PortfolioEvent.OrganizeTokens) @@ -702,6 +721,27 @@ internal class WalletViewModel @Inject constructor( refreshSingleCurrencyContent(selectedWalletIndex) } + private fun refreshSingleCurrencyContent(walletIndex: Int) { + uiState = stateFactory.getRefreshingState() + val wallet = getWallet(walletIndex) + + viewModelScope.launch(dispatchers.main) { + val result = fetchCurrencyStatusUseCase(wallet.walletId, refresh = true) + + uiState = stateFactory.getRefreshedState() + uiState = result.fold(stateFactory::getStateByCurrencyStatusError) { uiState } + + singleWalletCryptoCurrencyStatus?.let { + val singleCurrencyState = uiState as WalletSingleCurrencyState + if (singleCurrencyState.txHistoryState !is TxHistoryState.Content) { + // show loading indicator while refreshing in non content state + uiState = stateFactory.getLoadingTxHistoryState(1.right()) + } + updateTxHistory(wallet.walletId, it.currency, refresh = true) + } + }.saveIn(refreshContentJobHolder) + } + override fun onExploreClick() { viewModelScope.launch(dispatchers.io) { val wallet = getWallet( @@ -924,9 +964,7 @@ internal class WalletViewModel @Inject constructor( uiState = stateFactory.getLockedState() } wallet.isMultiCurrency -> getMultiCurrencyContent(wallet, index) - isSingleWalletWithTokens(wallet) -> { - getSingleCurrencyWithTokenContent(index) - } + isSingleWalletWithTokens(wallet) -> getSingleCurrencyWithTokenContent(index) !wallet.isMultiCurrency -> getSingleCurrencyContent(index) } } @@ -1137,44 +1175,6 @@ internal class WalletViewModel @Inject constructor( .saveIn(notificationsJobHolder) } - private fun refreshMultiCurrencyContent(walletIndex: Int) { - uiState = stateFactory.getRefreshingState() - val wallet = getWallet(walletIndex) - - viewModelScope.launch(dispatchers.io) { - 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() - }.saveIn(refreshContentJobHolder) - } - - private fun refreshSingleCurrencyContent(walletIndex: Int) { - uiState = stateFactory.getRefreshingState() - val wallet = getWallet(walletIndex) - - viewModelScope.launch(dispatchers.io) { - val result = fetchCurrencyStatusUseCase(wallet.walletId, refresh = true) - - uiState = stateFactory.getRefreshedState() - uiState = result.fold(stateFactory::getStateByCurrencyStatusError) { uiState } - - singleWalletCryptoCurrencyStatus?.let { - val singleCurrencyState = uiState as WalletSingleCurrencyState - if (singleCurrencyState.txHistoryState !is TxHistoryState.Content) { - // show loading indicator while refreshing in non content state - uiState = stateFactory.getLoadingTxHistoryState(1.right()) - } - updateTxHistory(wallet.walletId, it.currency, refresh = true) - } - }.saveIn(refreshContentJobHolder) - } - private fun createSelectedAppCurrencyFlow(): StateFlow { return getSelectedAppCurrencyUseCase() .map { maybeAppCurrency ->