Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-10 16:21:35 +04:00
parent 68071d5e6a
commit a3016f459b
6 changed files with 89 additions and 93 deletions

View file

@ -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()

View file

@ -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<List<SavedSwapTransactionListModel>?> {
return withContext(dispatchers.io) {
val txStatuses = appPreferencesStore.getObjectMapSync<ExchangeStatusModel>(
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
)
appPreferencesStore.getObjectList<SavedSwapTransactionListModelInner>(
return combine(
flow = appPreferencesStore.getObjectList<SavedSwapTransactionListModelInner>(
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<ExchangeStatusModel>(
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(

View file

@ -17,7 +17,7 @@ interface SwapTransactionRepository {
transaction: SavedSwapTransactionModel,
)
suspend fun getTransactions(
fun getTransactions(
userWallet: UserWallet,
cryptoCurrencyId: CryptoCurrency.ID,
): Flow<List<SavedSwapTransactionListModel>?>

View file

@ -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) {

View file

@ -52,7 +52,7 @@ internal class ExchangeStatusFactory @AssistedInject constructor(
)
}
suspend operator fun invoke(): Flow<PersistentList<ExchangeUM>> {
operator fun invoke(): Flow<PersistentList<ExchangeUM>> {
return swapTransactionRepository.getTransactions(
userWallet = userWallet,
cryptoCurrencyId = cryptoCurrency.id,

View file

@ -65,14 +65,12 @@ internal class ExpressStatusFactory @AssistedInject constructor(
)
}
suspend fun getExpressStatuses(): Flow<PersistentList<ExpressTransactionStateUM>> = combine(
fun getExpressStatuses(): Flow<PersistentList<ExpressTransactionStateUM>> = combine(
flow = exchangeStatusFactory(),
flow2 = onrampStatusFactory(),
) { maybeExchange, maybeOnramp ->
persistentListOf(
maybeOnramp,
maybeExchange,
).flatten()
persistentListOf(maybeOnramp, maybeExchange)
.flatten()
.sortedByDescending { it.info.timestamp }
.toPersistentList()
}