From c68766a67673270f86bde42545cab3d061abc0c9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 5 Jul 2024 14:10:57 +0100 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/TokensDomainModule.kt | 12 +++++ .../repository/DefaultQuotesRepository.kt | 13 +++++ ...RefreshMultiCurrencyWalletQuotesUseCase.kt | 53 +++++++++++++++++++ .../tangem/domain/tokens/error/QuotesError.kt | 5 ++ .../tokens/repository/QuotesRepository.kt | 2 + .../tokens/repository/MockQuotesRepository.kt | 2 + .../wallet/viewmodels/WalletViewModel.kt | 18 ++++--- 7 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 domain/tokens/src/main/kotlin/com/tangem/domain/tokens/RefreshMultiCurrencyWalletQuotesUseCase.kt create mode 100644 domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/QuotesError.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 40ba3c5084..5990835735 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 @@ -370,4 +370,16 @@ internal object TokensDomainModule { networksRepository = networksRepository, ) } + + @Provides + @Singleton + fun provideRefreshMultiCurrencyWalletQuotesUseCase( + currenciesRepository: CurrenciesRepository, + quotesRepository: QuotesRepository, + ): RefreshMultiCurrencyWalletQuotesUseCase { + return RefreshMultiCurrencyWalletQuotesUseCase( + currenciesRepository = currenciesRepository, + quotesRepository = quotesRepository, + ) + } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt index 1dee0062ce..f6c7ade8b4 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt @@ -51,6 +51,19 @@ internal class DefaultQuotesRepository( .flowOn(dispatchers.io) } + override suspend fun fetchQuotes(currenciesIds: Set) { + withContext(dispatchers.io) { + val selectedAppCurrency = requireNotNull( + value = appPreferencesStore.getObjectSyncOrNull( + key = PreferencesKeys.SELECTED_APP_CURRENCY_KEY, + ), + lazyMessage = { "Unable to get selected application currency to update quotes" }, + ) + + fetchExpiredQuotes(currenciesIds, selectedAppCurrency.id, true) + } + } + override suspend fun getQuotesSync(currenciesIds: Set, refresh: Boolean): Set { return withContext(dispatchers.io) { val selectedAppCurrency = requireNotNull( diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/RefreshMultiCurrencyWalletQuotesUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/RefreshMultiCurrencyWalletQuotesUseCase.kt new file mode 100644 index 0000000000..e108b21819 --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/RefreshMultiCurrencyWalletQuotesUseCase.kt @@ -0,0 +1,53 @@ +package com.tangem.domain.tokens + +import arrow.core.Either +import arrow.core.getOrElse +import arrow.core.raise.catch +import arrow.core.raise.either +import com.tangem.domain.tokens.error.QuotesError +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.repository.CurrenciesRepository +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 RefreshMultiCurrencyWalletQuotesUseCase( + private val quotesRepository: QuotesRepository, + private val currenciesRepository: CurrenciesRepository, +) { + + suspend operator fun invoke(userWalletId: UserWalletId): Either { + return either { + val currencies = fetchCurrencies(userWalletId = userWalletId) + .getOrElse { raise(QuotesError.DataError(it)) } + + coroutineScope { + val fetchQuotes = async { + fetchQuotes( + currenciesIds = currencies.mapTo(destination = hashSetOf(), transform = CryptoCurrency::id), + ) + } + + awaitAll(fetchQuotes) + } + } + } + + private suspend fun fetchCurrencies(userWalletId: UserWalletId): Either> { + return either { + catch( + block = { currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWalletId, false) }, + catch = { raise(it) }, + ) + } + } + + private suspend fun fetchQuotes(currenciesIds: Set) { + catch( + block = { quotesRepository.fetchQuotes(currenciesIds) }, + catch = { /* Ignore error */ }, + ) + } +} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/QuotesError.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/QuotesError.kt new file mode 100644 index 0000000000..f5601eb6db --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/QuotesError.kt @@ -0,0 +1,5 @@ +package com.tangem.domain.tokens.error + +sealed class QuotesError { + data class DataError(val cause: Throwable) : QuotesError() +} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/QuotesRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/QuotesRepository.kt index 64a0d29a12..4ebd284421 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/QuotesRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/QuotesRepository.kt @@ -31,4 +31,6 @@ interface QuotesRepository { suspend fun getQuotesSync(currenciesIds: Set, refresh: Boolean): Set suspend fun getQuoteSync(currencyId: CryptoCurrency.ID): Quote? + + suspend fun fetchQuotes(currenciesIds: Set) } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockQuotesRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockQuotesRepository.kt index ae960f191a..3d45f98879 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockQuotesRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockQuotesRepository.kt @@ -25,4 +25,6 @@ internal class MockQuotesRepository( return quotes.map { it.getOrElse { e -> throw e } }.first() .first { it.rawCurrencyId == currencyId.rawCurrencyId } } + + override suspend fun fetchQuotes(currenciesIds: Set) {} } \ 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 058a853a52..713453127a 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 @@ -4,11 +4,13 @@ import androidx.compose.runtime.MutableState import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import arrow.core.getOrElse import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.settings.CanUseBiometryUseCase import com.tangem.domain.settings.IsWalletsScrollPreviewEnabled import com.tangem.domain.settings.ShouldShowSaveWalletScreenUseCase +import com.tangem.domain.tokens.RefreshMultiCurrencyWalletQuotesUseCase import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase @@ -21,7 +23,6 @@ import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContent import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent.DemonstrateWalletsScrollPreview.Direction -import com.tangem.feature.wallet.presentation.wallet.state.model.WalletPullToRefreshConfig import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState import com.tangem.feature.wallet.presentation.wallet.state.transformers.* import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender @@ -61,6 +62,7 @@ internal class WalletViewModel @Inject constructor( private val selectedWalletAnalyticsSender: SelectedWalletAnalyticsSender, private val walletDeepLinksHandler: WalletDeepLinksHandler, private val walletNameMigrationUseCase: WalletNameMigrationUseCase, + private val refreshMultiCurrencyWalletQuotesUseCase: RefreshMultiCurrencyWalletQuotesUseCase, ) : ViewModel() { val uiState: StateFlow = stateHolder.uiState @@ -178,7 +180,7 @@ internal class WalletViewModel @Inject constructor( } } - // We need to update the current wallet if the application was in the background for more than 10 seconds + // We need to update the current wallet quotes if the application was in the background for more than 10 seconds // and then returned to the foreground private fun subscribeToScreenBackgroundState() { screenLifecycleProvider.isBackgroundState @@ -186,7 +188,7 @@ internal class WalletViewModel @Inject constructor( refreshWalletJobHolder.cancel() when { isBackground -> needToRefreshTimer() - needToRefreshWallet && !isBackground -> triggerRefreshWallet() + needToRefreshWallet && !isBackground -> triggerRefreshWalletQuotes() } } .launchIn(viewModelScope) @@ -199,13 +201,15 @@ internal class WalletViewModel @Inject constructor( }.saveIn(refreshWalletJobHolder) } - private fun triggerRefreshWallet() { + private fun triggerRefreshWalletQuotes() { needToRefreshWallet = false val state = stateHolder.uiState.value val wallet = state.wallets.getOrNull(state.selectedWalletIndex) ?: return - wallet.pullToRefreshConfig.onRefresh.invoke( - WalletPullToRefreshConfig.ShowRefreshState(false), - ) + viewModelScope.launch { + refreshMultiCurrencyWalletQuotesUseCase(wallet.walletCardState.id).getOrElse { + Timber.e("Failed to refreshMultiCurrencyWalletQuotesUseCase $it") + } + }.saveIn(refreshWalletJobHolder) } private suspend fun updateWallets(action: WalletsUpdateActionResolver.Action) {