Updated on 2026-08-14
This commit is contained in:
parent
ef2cb2ce43
commit
a0bf0bbdad
10 changed files with 98 additions and 30 deletions
|
|
@ -35,6 +35,8 @@ object PreferencesKeys {
|
|||
|
||||
val SWAP_TRANSACTIONS_KEY by lazy { stringPreferencesKey(name = "swapTransactions") }
|
||||
|
||||
val SWAP_TRANSACTIONS_STATUSES_KEY by lazy { stringPreferencesKey(name = "swapTransactionsStatuses") }
|
||||
|
||||
val WALLETS_SCROLL_PREVIEW_KEY by lazy { booleanPreferencesKey(name = "walletsScrollPreview") }
|
||||
|
||||
val SENT_ONE_TIME_EVENTS_KEY by lazy { stringPreferencesKey(name = "sentOneTimeEvents") }
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ 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.getObjectMap
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.swap.domain.SwapTransactionRepository
|
||||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatusModel
|
||||
import com.tangem.feature.swap.domain.models.domain.SavedLastSwappedCryptoCurrency
|
||||
import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionListModel
|
||||
import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionModel
|
||||
|
|
@ -24,11 +26,11 @@ class DefaultSwapTransactionRepository(
|
|||
toCryptoCurrencyId: CryptoCurrency.ID,
|
||||
transaction: SavedSwapTransactionModel,
|
||||
) {
|
||||
transaction.status?.let { storeTransactionState(transaction.txId, it) }
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
val savedTransactions: List<SavedSwapTransactionListModel>? = mutablePreferences.getObjectList(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
|
||||
)
|
||||
|
||||
val tokenTransactions = savedTransactions
|
||||
?.firstOrNull {
|
||||
it.checkId(
|
||||
|
|
@ -62,14 +64,17 @@ class DefaultSwapTransactionRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getTransactions(
|
||||
override suspend fun getTransactions(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyId: CryptoCurrency.ID,
|
||||
): Flow<List<SavedSwapTransactionListModel>?> {
|
||||
val txStatuses = appPreferencesStore.getObjectMap<ExchangeStatusModel>(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
|
||||
)
|
||||
return appPreferencesStore.getObjectList<SavedSwapTransactionListModel>(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
|
||||
).map { savedTransactions ->
|
||||
savedTransactions
|
||||
val currencyTxs = savedTransactions
|
||||
?.filter {
|
||||
it.userWalletId == userWalletId.stringValue &&
|
||||
(
|
||||
|
|
@ -77,6 +82,14 @@ class DefaultSwapTransactionRepository(
|
|||
it.fromCryptoCurrencyId == cryptoCurrencyId.value
|
||||
)
|
||||
}
|
||||
|
||||
currencyTxs?.map { currencyTx ->
|
||||
currencyTx.copy(
|
||||
transactions = currencyTx.transactions.map { tx ->
|
||||
tx.copy(status = txStatuses[tx.txId])
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -86,6 +99,7 @@ class DefaultSwapTransactionRepository(
|
|||
toCryptoCurrencyId: CryptoCurrency.ID,
|
||||
txId: String,
|
||||
) {
|
||||
clearTransactionsStatuses(txId = txId)
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
val savedList: List<SavedSwapTransactionListModel>? = mutablePreferences.getObjectList(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_KEY,
|
||||
|
|
@ -130,6 +144,22 @@ class DefaultSwapTransactionRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun storeTransactionState(txId: String, status: ExchangeStatusModel) {
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
val savedMap = mutablePreferences.getObjectMap<ExchangeStatusModel>(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
|
||||
)
|
||||
|
||||
val updatesMap = savedMap?.toMutableMap() ?: mutableMapOf()
|
||||
updatesMap[txId] = status
|
||||
|
||||
mutablePreferences.setObjectMap(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
|
||||
value = updatesMap,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getLastSwappedCryptoCurrencyId(userWalletId: UserWalletId): String? {
|
||||
val lastSwappedCurrencies = appPreferencesStore.getObjectListSync<SavedLastSwappedCryptoCurrency>(
|
||||
key = PreferencesKeys.LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY,
|
||||
|
|
@ -194,4 +224,22 @@ class DefaultSwapTransactionRepository(
|
|||
},
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun clearTransactionsStatuses(txId: String) {
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
val savedList = mutablePreferences.getObjectMap<ExchangeStatusModel>(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
|
||||
)
|
||||
val editedList = savedList?.filterNot { it.key == txId }
|
||||
|
||||
if (editedList.isNullOrEmpty()) {
|
||||
mutablePreferences.remove(key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY)
|
||||
} else {
|
||||
mutablePreferences.setObjectMap(
|
||||
key = PreferencesKeys.SWAP_TRANSACTIONS_STATUSES_KEY,
|
||||
value = editedList,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ sealed class TxState {
|
|||
val toAmountValue: BigDecimal? = null,
|
||||
val txAddress: String,
|
||||
val txExternalUrl: String? = null,
|
||||
val txUrl: String? = null,
|
||||
val timestamp: Long,
|
||||
) : TxState()
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
private val dispatcher: CoroutineDispatcherProvider,
|
||||
private val swapTransactionRepository: SwapTransactionRepository,
|
||||
private val initialToCurrencyResolver: InitialToCurrencyResolver,
|
||||
private val blockchainInteractor: BlockchainInteractor,
|
||||
) : SwapInteractor {
|
||||
|
||||
private val estimateFeeUseCase by lazy(LazyThreadSafetyMode.NONE) {
|
||||
|
|
@ -539,6 +540,10 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
},
|
||||
ifRight = {
|
||||
val timestamp = System.currentTimeMillis()
|
||||
val txUrl = blockchainInteractor.getExplorerTransactionLink(
|
||||
networkId = currencyToSend.currency.network.backendId,
|
||||
txAddress = exchangeData.transaction.txTo,
|
||||
)
|
||||
storeSwapTransaction(
|
||||
currencyToSend = currencyToSend,
|
||||
currencyToGet = currencyToGet,
|
||||
|
|
@ -546,6 +551,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
swapProvider = swapProvider,
|
||||
swapDataModel = exchangeData,
|
||||
timestamp = timestamp,
|
||||
txUrl = txUrl,
|
||||
)
|
||||
storeLastCryptoCurrencyId(currencyToGet.currency)
|
||||
TxState.TxSent(
|
||||
|
|
@ -564,6 +570,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
derivationPath,
|
||||
).orEmpty(),
|
||||
txExternalUrl = externalUrl,
|
||||
txUrl = txUrl,
|
||||
timestamp = timestamp,
|
||||
)
|
||||
},
|
||||
|
|
@ -597,6 +604,7 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
swapProvider: SwapProvider,
|
||||
swapDataModel: SwapDataModel,
|
||||
timestamp: Long,
|
||||
txUrl: String,
|
||||
) {
|
||||
swapTransactionRepository.storeTransaction(
|
||||
userWalletId = UserWalletId(userWalletManager.getWalletId()),
|
||||
|
|
@ -608,6 +616,12 @@ internal class SwapInteractorImpl @Inject constructor(
|
|||
timestamp = timestamp,
|
||||
fromCryptoAmount = amount.value,
|
||||
toCryptoAmount = swapDataModel.toTokenAmount.value,
|
||||
status = ExchangeStatusModel(
|
||||
providerId = swapProvider.providerId,
|
||||
status = ExchangeStatus.New,
|
||||
txId = swapDataModel.transaction.txId,
|
||||
txUrl = txUrl,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.swap.domain
|
|||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.swap.domain.models.domain.ExchangeStatusModel
|
||||
import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionListModel
|
||||
import com.tangem.feature.swap.domain.models.domain.SavedSwapTransactionModel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -15,7 +16,7 @@ interface SwapTransactionRepository {
|
|||
transaction: SavedSwapTransactionModel,
|
||||
)
|
||||
|
||||
fun getTransactions(
|
||||
suspend fun getTransactions(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyId: CryptoCurrency.ID,
|
||||
): Flow<List<SavedSwapTransactionListModel>?>
|
||||
|
|
@ -27,6 +28,8 @@ interface SwapTransactionRepository {
|
|||
txId: String,
|
||||
)
|
||||
|
||||
suspend fun storeTransactionState(txId: String, status: ExchangeStatusModel)
|
||||
|
||||
suspend fun storeLastSwappedCryptoCurrencyId(userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID)
|
||||
|
||||
suspend fun getLastSwappedCryptoCurrencyId(userWalletId: UserWalletId): String?
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ class SwapDomainModule {
|
|||
walletManagersFacade: WalletManagersFacade,
|
||||
coroutineDispatcherProvider: CoroutineDispatcherProvider,
|
||||
initialToCurrencyResolver: InitialToCurrencyResolver,
|
||||
blockchainInteractor: BlockchainInteractor,
|
||||
): SwapInteractor {
|
||||
return SwapInteractorImpl(
|
||||
transactionManager = transactionManager,
|
||||
|
|
@ -56,6 +57,7 @@ class SwapDomainModule {
|
|||
dispatcher = coroutineDispatcherProvider,
|
||||
swapTransactionRepository = swapTransactionRepository,
|
||||
initialToCurrencyResolver = initialToCurrencyResolver,
|
||||
blockchainInteractor = blockchainInteractor,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -717,7 +717,6 @@ internal class StateBuilder(
|
|||
fun createSuccessState(
|
||||
uiState: SwapStateHolder,
|
||||
txState: TxState.TxSent,
|
||||
txUrl: String,
|
||||
dataState: SwapProcessDataState,
|
||||
onExploreClick: () -> Unit,
|
||||
onStatusClick: () -> Unit,
|
||||
|
|
@ -735,7 +734,7 @@ internal class StateBuilder(
|
|||
return uiState.copy(
|
||||
successState = SwapSuccessStateHolder(
|
||||
timestamp = txState.timestamp,
|
||||
txUrl = txUrl,
|
||||
txUrl = txState.txUrl.orEmpty(),
|
||||
providerName = stringReference(providerState.name),
|
||||
providerType = stringReference(providerState.type),
|
||||
showStatusButton = providerState.type == ExchangeProviderType.CEX.name,
|
||||
|
|
|
|||
|
|
@ -447,19 +447,14 @@ internal class SwapViewModel @Inject constructor(
|
|||
}.onSuccess {
|
||||
when (it) {
|
||||
is TxState.TxSent -> {
|
||||
val url = blockchainInteractor.getExplorerTransactionLink(
|
||||
networkId = fromCurrency.currency.network.backendId,
|
||||
txAddress = it.txAddress,
|
||||
)
|
||||
uiState = stateBuilder.createSuccessState(
|
||||
uiState = uiState,
|
||||
txState = it,
|
||||
dataState = dataState,
|
||||
txUrl = url,
|
||||
onExploreClick = {
|
||||
val txHash = it.txAddress
|
||||
if (txHash.isNotEmpty()) {
|
||||
swapRouter.openUrl(url)
|
||||
val txUrl = it.txUrl
|
||||
if (!txUrl.isNullOrBlank()) {
|
||||
swapRouter.openUrl(txUrl)
|
||||
}
|
||||
analyticsEventHandler.send(
|
||||
event = SwapEvents.ButtonExplore(initialCryptoCurrency.symbol),
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ internal class ExchangeStatusFactory(
|
|||
)
|
||||
}
|
||||
|
||||
operator fun invoke() = combine(
|
||||
suspend operator fun invoke() = combine(
|
||||
flow = swapTransactionRepository.getTransactions(userWalletId, cryptoCurrency.id),
|
||||
flow2 = getWalletCryptoCurrencies().conflate(),
|
||||
) { savedTransactions, cryptoCurrenciesStatusList ->
|
||||
|
|
@ -95,9 +95,10 @@ internal class ExchangeStatusFactory(
|
|||
return swapRepository.getExchangeStatus(txId)
|
||||
.fold(
|
||||
ifLeft = { null },
|
||||
ifRight = {
|
||||
sendStatusUpdateAnalytics(it)
|
||||
it
|
||||
ifRight = { statusModel ->
|
||||
sendStatusUpdateAnalytics(statusModel)
|
||||
swapTransactionRepository.storeTransactionState(txId, statusModel)
|
||||
statusModel
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
.distinctUntilChanged()
|
||||
.filterNot { it.isEmpty() }
|
||||
.onEach { swapTxs ->
|
||||
updateSwapTx(swapTxs)
|
||||
swapTxStatusTaskScheduler.scheduleTask(
|
||||
viewModelScope,
|
||||
PeriodicTask(
|
||||
|
|
@ -231,18 +232,8 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
exchangeStatusFactory.updateSwapTxStatuses(swapTxs)
|
||||
}
|
||||
},
|
||||
onSuccess = { updatedTxs ->
|
||||
val config = uiState.bottomSheetConfig
|
||||
val exchangeBottomSheet = config?.content as? ExchangeStatusBottomSheetConfig
|
||||
val currentTx = updatedTxs.firstOrNull { it.txId == exchangeBottomSheet?.value?.txId }
|
||||
uiState = uiState.copy(
|
||||
swapTxs = updatedTxs,
|
||||
bottomSheetConfig = currentTx?.let(
|
||||
stateFactory::updateStateWithExchangeStatusBottomSheet,
|
||||
) ?: config,
|
||||
)
|
||||
},
|
||||
onError = {},
|
||||
onSuccess = ::updateSwapTx,
|
||||
onError = { /* no-op */ },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -252,6 +243,18 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateSwapTx(swapTxs: PersistentList<SwapTransactionsState>) {
|
||||
val config = uiState.bottomSheetConfig
|
||||
val exchangeBottomSheet = config?.content as? ExchangeStatusBottomSheetConfig
|
||||
val currentTx = swapTxs.firstOrNull { it.txId == exchangeBottomSheet?.value?.txId }
|
||||
uiState = uiState.copy(
|
||||
swapTxs = swapTxs,
|
||||
bottomSheetConfig = currentTx?.let(
|
||||
stateFactory::updateStateWithExchangeStatusBottomSheet,
|
||||
) ?: config,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param refresh - invalidate cache and get data from remote
|
||||
* @param showItemsLoading - show loading items placeholder.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue