From a3016f459b4f81af6ae005abbdef0e603cf3ec61 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 10 Jul 2025 16:21:35 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../state/helpers/StakingBalanceUpdater.kt | 6 +- .../swap/DefaultSwapTransactionRepository.kt | 51 ++++---- .../swap/domain/SwapTransactionRepository.kt | 2 +- .../tokendetails/model/TokenDetailsModel.kt | 113 +++++++++--------- .../factory/express/ExchangeStatusFactory.kt | 2 +- .../factory/express/ExpressStatusFactory.kt | 8 +- 6 files changed, 89 insertions(+), 93 deletions(-) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt index e6c32aa82c..7b11e36afb 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/helpers/StakingBalanceUpdater.kt @@ -64,7 +64,11 @@ internal class StakingBalanceUpdater @AssistedInject constructor( coroutineScope { listOf( async { - fetchCurrencyStatus() + /* + * It is important to use NonCancellable here to ensure the update is not interrupted midway. + * For example, this can happen if the user enters and immediately leaves the screen. + */ + withContext(NonCancellable) { fetchCurrencyStatus() } }, async { updateStakingActions() diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt index 5633a2c403..97d35ceba4 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapTransactionRepository.kt @@ -6,7 +6,7 @@ import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.datasource.local.preferences.utils.getObjectList import com.tangem.datasource.local.preferences.utils.getObjectListSync -import com.tangem.datasource.local.preferences.utils.getObjectMapSync +import com.tangem.datasource.local.preferences.utils.getObjectMap import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId @@ -17,9 +17,8 @@ import com.tangem.feature.swap.domain.models.domain.* import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.extensions.addOrReplace import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.withContext internal class DefaultSwapTransactionRepository( private val appPreferencesStore: AppPreferencesStore, @@ -82,35 +81,35 @@ internal class DefaultSwapTransactionRepository( } } - override suspend fun getTransactions( + override fun getTransactions( userWallet: UserWallet, cryptoCurrencyId: CryptoCurrency.ID, ): Flow?> { - return withContext(dispatchers.io) { - val txStatuses = appPreferencesStore.getObjectMapSync( - key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY, - ) - appPreferencesStore.getObjectList( + return combine( + flow = appPreferencesStore.getObjectList( key = PreferencesKeys.SWAP_TRANSACTIONS_KEY, - ).map { savedTransactions -> - val currencyTxs = savedTransactions - ?.filter { - it.userWalletId == userWallet.walletId.stringValue && - ( - it.toCryptoCurrencyId == cryptoCurrencyId.value || - it.fromCryptoCurrencyId == cryptoCurrencyId.value - ) - } + ), + flow2 = appPreferencesStore.getObjectMap( + key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY, + ), + ) { savedTransactions, txStatuses -> + val currencyTxs = savedTransactions?.filter { + it.userWalletId == userWallet.walletId.stringValue && + ( + it.toCryptoCurrencyId == cryptoCurrencyId.value || + it.fromCryptoCurrencyId == cryptoCurrencyId.value + ) + } - currencyTxs?.mapNotNull { - converter.convertBack( - value = it, - scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY] - txStatuses = txStatuses, - ) - } - }.flowOn(dispatchers.io) + currencyTxs?.mapNotNull { + converter.convertBack( + value = it, + scanResponse = userWallet.requireColdWallet().scanResponse, // TODO [REDACTED_TASK_KEY] + txStatuses = txStatuses, + ) + } } + .flowOn(dispatchers.default) } override suspend fun removeTransaction( diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapTransactionRepository.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapTransactionRepository.kt index 6948208c03..c52ee66212 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapTransactionRepository.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapTransactionRepository.kt @@ -17,7 +17,7 @@ interface SwapTransactionRepository { transaction: SavedSwapTransactionModel, ) - suspend fun getTransactions( + fun getTransactions( userWallet: UserWallet, cryptoCurrencyId: CryptoCurrency.ID, ): Flow?> diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt index ae48c2de2d..46dba2b2a9 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt @@ -206,15 +206,15 @@ internal class TokenDetailsModel @Inject constructor( analyticsEventsHandler.send(TokenScreenAnalyticsEvent.Bought(currency.symbol)) } + fun onResume() { + subscribeOnExpressTransactionsUpdates() + } + fun onPause() { expressTxStatusTaskScheduler.cancelTask() expressTxJobHolder.cancel() } - fun onResume() { - subscribeOnExpressTransactionsUpdates() - } - override fun onDestroy() { expressTxStatusTaskScheduler.cancelTask() expressTxJobHolder.cancel() @@ -305,65 +305,60 @@ internal class TokenDetailsModel @Inject constructor( } private fun subscribeOnCurrencyStatusUpdates() { - modelScope.launch(dispatchers.main) { - getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet( - userWalletId = userWalletId, - currencyId = cryptoCurrency.id, - isSingleWalletWithTokens = userWallet is UserWallet.Cold && - userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(), - ) - .distinctUntilChanged() - .onEach { maybeCurrencyStatus -> - internalUiState.value = stateFactory.getCurrencyLoadedBalanceState(maybeCurrencyStatus) - maybeCurrencyStatus.onRight { status -> - cryptoCurrencyStatus = status - updateButtons(currencyStatus = status) - updateWarnings(status) - subscribeOnUpdateStakingInfo(status) - } - currencyStatusAnalyticsSender.send(maybeCurrencyStatus) + getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet( + userWalletId = userWalletId, + currencyId = cryptoCurrency.id, + isSingleWalletWithTokens = userWallet is UserWallet.Cold && + userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(), + ) + .distinctUntilChanged() + .onEach { maybeCurrencyStatus -> + internalUiState.value = stateFactory.getCurrencyLoadedBalanceState(maybeCurrencyStatus) + maybeCurrencyStatus.onRight { status -> + cryptoCurrencyStatus = status + updateButtons(currencyStatus = status) + updateWarnings(status) + subscribeOnUpdateStakingInfo(status) } - .flowOn(dispatchers.main) - .launchIn(modelScope) - .saveIn(marketPriceJobHolder) - } + currencyStatusAnalyticsSender.send(maybeCurrencyStatus) + } + .flowOn(dispatchers.main) + .launchIn(modelScope) + .saveIn(marketPriceJobHolder) } private fun subscribeOnExpressTransactionsUpdates() { - modelScope.launch(dispatchers.main) { - expressTxStatusTaskScheduler.cancelTask() - expressStatusFactory - .getExpressStatuses() - .distinctUntilChanged() - .onEach { expressTxs -> - internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs( - expressTxs, - ::updateNetworkToSwapBalance, - ) - expressTxStatusTaskScheduler.scheduleTask( - modelScope, - PeriodicTask( - isDelayFirst = false, - delay = EXPRESS_STATUS_UPDATE_DELAY, - task = { - runCatching { - expressStatusFactory.getUpdatedExpressStatuses(internalUiState.value.expressTxs) - } - }, - onSuccess = { updatedTxs -> - internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs( - updatedTxs, - ::updateNetworkToSwapBalance, - ) - }, - onError = { /* no-op */ }, - ), - ) - } - .flowOn(dispatchers.main) - .launchIn(modelScope) - .saveIn(expressTxJobHolder) - } + expressTxStatusTaskScheduler.cancelTask() + expressStatusFactory.getExpressStatuses() + .distinctUntilChanged() + .onEach { expressTxs -> + internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs( + expressTxs = expressTxs, + updateBalance = ::updateNetworkToSwapBalance, + ) + expressTxStatusTaskScheduler.scheduleTask( + scope = modelScope, + task = PeriodicTask( + isDelayFirst = false, + delay = EXPRESS_STATUS_UPDATE_DELAY, + task = { + runCatching { + expressStatusFactory.getUpdatedExpressStatuses(internalUiState.value.expressTxs) + } + }, + onSuccess = { updatedTxs -> + internalUiState.value = expressStatusFactory.getStateWithUpdatedExpressTxs( + updatedTxs, + ::updateNetworkToSwapBalance, + ) + }, + onError = { /* no-op */ }, + ), + ) + } + .flowOn(dispatchers.main) + .launchIn(modelScope) + .saveIn(expressTxJobHolder) } private fun updateNetworkToSwapBalance(toCryptoCurrency: CryptoCurrency) { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt index c12a0e8048..1e567bfc6a 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExchangeStatusFactory.kt @@ -52,7 +52,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor( ) } - suspend operator fun invoke(): Flow> { + operator fun invoke(): Flow> { return swapTransactionRepository.getTransactions( userWallet = userWallet, cryptoCurrencyId = cryptoCurrency.id, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt index 4e9ee06a08..2378fa4b52 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/express/ExpressStatusFactory.kt @@ -65,14 +65,12 @@ internal class ExpressStatusFactory @AssistedInject constructor( ) } - suspend fun getExpressStatuses(): Flow> = combine( + fun getExpressStatuses(): Flow> = combine( flow = exchangeStatusFactory(), flow2 = onrampStatusFactory(), ) { maybeExchange, maybeOnramp -> - persistentListOf( - maybeOnramp, - maybeExchange, - ).flatten() + persistentListOf(maybeOnramp, maybeExchange) + .flatten() .sortedByDescending { it.info.timestamp } .toPersistentList() }