Updated on 2026-08-14
This commit is contained in:
parent
f70d183814
commit
e4f1557d72
6 changed files with 164 additions and 52 deletions
|
|
@ -2,8 +2,10 @@ package com.tangem.data.txhistory.repository.paging
|
|||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.datasource.local.txhistory.TxHistoryItemsStore
|
||||
import com.tangem.domain.common.extensions.fromNetworkId
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.txhistory.models.PaginationWrapper
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
|
|
@ -20,6 +22,8 @@ internal class TxHistoryPagingSource(
|
|||
|
||||
private val storeKey = TxHistoryItemsStore.Key(sourceParams.userWalletId, sourceParams.currency)
|
||||
|
||||
override val keyReuseSupported: Boolean get() = true
|
||||
|
||||
override fun getRefreshKey(state: PagingState<Int, TxHistoryItem>): Int? {
|
||||
return state.anchorPosition?.let { anchorPosition ->
|
||||
val anchorPage = state.closestPageToPosition(anchorPosition)
|
||||
|
|
@ -70,14 +74,54 @@ internal class TxHistoryPagingSource(
|
|||
}
|
||||
|
||||
private suspend fun fetch(pageToLoad: Int, pageSize: Int) {
|
||||
val wrappedItems = walletManagersFacade.getTxHistoryItems(
|
||||
var wrappedItems = walletManagersFacade.getTxHistoryItems(
|
||||
userWalletId = sourceParams.userWalletId,
|
||||
currency = sourceParams.currency,
|
||||
page = pageToLoad,
|
||||
pageSize = pageSize,
|
||||
)
|
||||
|
||||
txHistoryItemsStore.store(storeKey, wrappedItems)
|
||||
if (pageToLoad == 1) {
|
||||
wrappedItems = wrappedItems.addRecentTransactions()
|
||||
}
|
||||
|
||||
txHistoryItemsStore.store(key = storeKey, value = wrappedItems)
|
||||
}
|
||||
|
||||
private suspend fun PaginationWrapper<TxHistoryItem>.addRecentTransactions(): PaginationWrapper<TxHistoryItem> {
|
||||
val recentTxHistoryItems = Blockchain.fromNetworkId(sourceParams.currency.network.backendId)?.let {
|
||||
walletManagersFacade.getRecentTransactions(
|
||||
userWalletId = sourceParams.userWalletId,
|
||||
blockchain = it,
|
||||
derivationPath = sourceParams.currency.network.derivationPath.value,
|
||||
)
|
||||
.filterUnconfirmedTransaction()
|
||||
.filterIfApiKnowsAboutTx(apiItems = items)
|
||||
} ?: emptyList()
|
||||
|
||||
return if (recentTxHistoryItems.isEmpty()) {
|
||||
Timber.d("Nothing to add to TxHistory")
|
||||
this
|
||||
} else {
|
||||
Timber.d(
|
||||
"Recent transactions were added to TxHistory: %s",
|
||||
recentTxHistoryItems.joinToString(
|
||||
prefix = "[",
|
||||
postfix = "]",
|
||||
transform = TxHistoryItem::txHash,
|
||||
),
|
||||
)
|
||||
|
||||
return copy(items = recentTxHistoryItems + items)
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<TxHistoryItem>.filterUnconfirmedTransaction(): List<TxHistoryItem> {
|
||||
return filter { it.status == TxHistoryItem.TransactionStatus.Unconfirmed }
|
||||
}
|
||||
|
||||
private fun List<TxHistoryItem>.filterIfApiKnowsAboutTx(apiItems: List<TxHistoryItem>): List<TxHistoryItem> {
|
||||
return filter { item -> apiItems.none { it.txHash == item.txHash } }
|
||||
}
|
||||
|
||||
private fun getTxHistoryPageKey(page: Int): String {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import timber.log.Timber
|
|||
import java.math.BigDecimal
|
||||
import java.util.EnumSet
|
||||
|
||||
@Suppress("LargeClass")
|
||||
@Suppress("LargeClass", "TooManyFunctions")
|
||||
// FIXME: Move to its own module and make internal
|
||||
@Deprecated("Inject the WalletManagerFacade interface using DI instead")
|
||||
class DefaultWalletManagersFacade(
|
||||
|
|
@ -481,6 +481,29 @@ class DefaultWalletManagersFacade(
|
|||
return (walletManager as TransactionSender).send(txData, signer)
|
||||
}
|
||||
|
||||
override suspend fun getRecentTransactions(
|
||||
userWalletId: UserWalletId,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: String?,
|
||||
): List<TxHistoryItem> {
|
||||
val walletManager = getOrCreateWalletManager(
|
||||
userWalletId = userWalletId,
|
||||
blockchain = blockchain,
|
||||
derivationPath = derivationPath,
|
||||
)
|
||||
|
||||
if (walletManager == null) {
|
||||
Timber.e("Unable to get a wallet manager for blockchain: $blockchain")
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val transactionDataConverter = TransactionDataToTxHistoryItemConverter(
|
||||
walletAddresses = SdkAddressToAddressConverter.convertList(walletManager.wallet.addresses).toSet(),
|
||||
)
|
||||
|
||||
return walletManager.wallet.recentTransactions.mapNotNull(transactionDataConverter::convert)
|
||||
}
|
||||
|
||||
private fun updateWalletManagerTokensIfNeeded(walletManager: WalletManager, tokens: Set<CryptoCurrency.Token>) {
|
||||
if (tokens.isEmpty()) return
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import java.util.EnumSet
|
|||
/**
|
||||
* A facade for managing wallets.
|
||||
*/
|
||||
@Suppress("TooManyFunctions")
|
||||
interface WalletManagersFacade {
|
||||
|
||||
/**
|
||||
|
|
@ -218,4 +219,11 @@ interface WalletManagersFacade {
|
|||
userWalletId: UserWalletId,
|
||||
network: Network,
|
||||
): SimpleResult
|
||||
|
||||
/** Get recent transactions of [userWalletId] for [blockchain] with [derivationPath] */
|
||||
suspend fun getRecentTransactions(
|
||||
userWalletId: UserWalletId,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: String?,
|
||||
): List<TxHistoryItem>
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.domain.walletmanager.model.Address
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.blockchain.common.address.Address as SdkAddress
|
||||
|
||||
/**
|
||||
* Convert [SdkAddress] to [Address]
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal object SdkAddressToAddressConverter : Converter<SdkAddress, Address> {
|
||||
|
||||
override fun convert(value: SdkAddress): Address {
|
||||
return Address(
|
||||
value = value.value,
|
||||
type = when (value.type) {
|
||||
AddressType.Default -> Address.Type.Primary
|
||||
AddressType.Legacy -> Address.Type.Secondary
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.TransactionStatus
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.walletmanager.model.Address
|
||||
import com.tangem.utils.converter.Converter
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Convert [TransactionData] to [TxHistoryItem]
|
||||
*
|
||||
* @property walletAddresses wallet addresses
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class TransactionDataToTxHistoryItemConverter(
|
||||
private val walletAddresses: Set<Address>,
|
||||
) : Converter<TransactionData, TxHistoryItem?> {
|
||||
|
||||
override fun convert(value: TransactionData): TxHistoryItem? {
|
||||
val hash = value.hash ?: return null
|
||||
val millis = value.date?.timeInMillis ?: return null
|
||||
val amount = getTransactionAmountValue(value.amount) ?: return null
|
||||
val isOutgoing = value.sourceAddress in walletAddresses.map(Address::value)
|
||||
|
||||
return TxHistoryItem(
|
||||
txHash = hash,
|
||||
timestampInMillis = millis,
|
||||
isOutgoing = isOutgoing,
|
||||
destinationType = TxHistoryItem.DestinationType.Single(
|
||||
addressType = TxHistoryItem.AddressType.User(value.destinationAddress),
|
||||
),
|
||||
sourceType = TxHistoryItem.SourceType.Single(value.sourceAddress),
|
||||
interactionAddressType = TxHistoryItem.InteractionAddressType.User(
|
||||
address = if (isOutgoing) value.destinationAddress else value.sourceAddress,
|
||||
),
|
||||
status = when (value.status) {
|
||||
TransactionStatus.Confirmed -> TxHistoryItem.TransactionStatus.Confirmed
|
||||
TransactionStatus.Unconfirmed -> TxHistoryItem.TransactionStatus.Unconfirmed
|
||||
},
|
||||
type = TxHistoryItem.TransactionType.Transfer,
|
||||
amount = amount,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getTransactionAmountValue(amount: Amount): BigDecimal? {
|
||||
val value = amount.value
|
||||
|
||||
if (value == null) {
|
||||
Timber.w("Transaction amount must not be null: ${amount.currencySymbol}")
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
package com.tangem.domain.walletmanager.utils
|
||||
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.domain.common.extensions.amountToCreateAccount
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.walletmanager.model.Address
|
||||
import com.tangem.domain.walletmanager.model.CryptoCurrencyAmount
|
||||
import com.tangem.domain.walletmanager.model.CryptoCurrencyTransaction
|
||||
|
|
@ -111,13 +109,14 @@ internal class UpdateWalletManagerResultFactory {
|
|||
walletAddresses: Set<Address>,
|
||||
data: TransactionData,
|
||||
): CryptoCurrencyTransaction? {
|
||||
val txHistoryItemConverter = TransactionDataToTxHistoryItemConverter(walletAddresses)
|
||||
return when (val type = data.amount.type) {
|
||||
is AmountType.Coin -> {
|
||||
val txHistoryItem = createTxHistoryItem(walletAddresses, data) ?: return null
|
||||
val txHistoryItem = txHistoryItemConverter.convert(data) ?: return null
|
||||
CryptoCurrencyTransaction.Coin(txHistoryItem)
|
||||
}
|
||||
is AmountType.Token -> {
|
||||
val txHistoryItem = createTxHistoryItem(walletAddresses, data) ?: return null
|
||||
val txHistoryItem = txHistoryItemConverter.convert(data) ?: return null
|
||||
CryptoCurrencyTransaction.Token(
|
||||
tokenId = type.token.id,
|
||||
tokenContractAddress = type.token.contractAddress,
|
||||
|
|
@ -128,42 +127,8 @@ internal class UpdateWalletManagerResultFactory {
|
|||
}
|
||||
}
|
||||
|
||||
private fun createTxHistoryItem(walletAddresses: Set<Address>, data: TransactionData): TxHistoryItem? {
|
||||
val hash = data.hash ?: return null
|
||||
val millis = data.date?.timeInMillis ?: return null
|
||||
val amount = getTransactionAmountValue(data.amount) ?: return null
|
||||
val isOutgoing = data.sourceAddress in walletAddresses.map { it.value }
|
||||
|
||||
return TxHistoryItem(
|
||||
txHash = hash,
|
||||
timestampInMillis = millis,
|
||||
isOutgoing = isOutgoing,
|
||||
destinationType = TxHistoryItem.DestinationType.Single(
|
||||
TxHistoryItem.AddressType.User(data.destinationAddress),
|
||||
),
|
||||
sourceType = TxHistoryItem.SourceType.Single(data.sourceAddress),
|
||||
interactionAddressType = TxHistoryItem.InteractionAddressType.User(
|
||||
if (isOutgoing) data.destinationAddress else data.sourceAddress,
|
||||
),
|
||||
status = when (data.status) {
|
||||
TransactionStatus.Confirmed -> TxHistoryItem.TransactionStatus.Confirmed
|
||||
TransactionStatus.Unconfirmed -> TxHistoryItem.TransactionStatus.Unconfirmed
|
||||
},
|
||||
type = TxHistoryItem.TransactionType.Transfer,
|
||||
amount = amount,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getAvailableAddresses(addresses: Set<SdkAddress>): Set<Address> {
|
||||
return addresses.mapTo(hashSetOf()) { sdkAddress ->
|
||||
Address(
|
||||
value = sdkAddress.value,
|
||||
type = when (sdkAddress.type) {
|
||||
AddressType.Default -> Address.Type.Primary
|
||||
AddressType.Legacy -> Address.Type.Secondary
|
||||
},
|
||||
)
|
||||
}
|
||||
return SdkAddressToAddressConverter.convertList(addresses).toSet()
|
||||
}
|
||||
|
||||
private fun getCurrencyAmountValue(amount: Amount): BigDecimal? {
|
||||
|
|
@ -175,14 +140,4 @@ internal class UpdateWalletManagerResultFactory {
|
|||
|
||||
return value
|
||||
}
|
||||
|
||||
private fun getTransactionAmountValue(amount: Amount): BigDecimal? {
|
||||
val value = amount.value
|
||||
|
||||
if (value == null) {
|
||||
Timber.w("Transaction amount must not be null: ${amount.currencySymbol}")
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue