diff --git a/data/tokens/build.gradle.kts b/data/tokens/build.gradle.kts index a45be27508..37aab60a23 100644 --- a/data/tokens/build.gradle.kts +++ b/data/tokens/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { implementation(projects.domain.models) implementation(projects.domain.tokens) implementation(projects.domain.tokens.models) + implementation(projects.domain.txhistory.models) implementation(projects.domain.wallets.models) /** Project - Data */ diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt index 0127ed7330..faed2bda5c 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt @@ -2,9 +2,9 @@ package com.tangem.data.tokens.utils import com.tangem.domain.tokens.model.NetworkAddress import com.tangem.domain.tokens.model.NetworkStatus -import com.tangem.domain.tokens.model.PendingTransaction import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network +import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.walletmanager.model.CryptoCurrencyAmount import com.tangem.domain.walletmanager.model.CryptoCurrencyTransaction import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult @@ -31,7 +31,6 @@ internal class NetworkStatusFactory { address = getNetworkAddress(result.defaultAddress, result.addresses), amounts = formatAmounts(result.currenciesAmounts, currencies), pendingTransactions = formatTransactions( - networksAddresses = result.addresses, transactions = result.currentTransactions, currencies = currencies, ), @@ -67,10 +66,9 @@ internal class NetworkStatusFactory { } private fun formatTransactions( - networksAddresses: Set, transactions: Set, currencies: Set, - ): Map> { + ): Map> { if (transactions.isEmpty()) return emptyMap() return currencies @@ -87,48 +85,13 @@ internal class NetworkStatusFactory { } } - currency.id to createCurrentTransactions(networksAddresses, currencyTransactions) + currency.id to createCurrentTransactions(currencyTransactions) } .toMap() } - private fun createCurrentTransactions( - networksAddresses: Set, - transactions: Set, - ): Set { - return transactions.mapNotNullTo(hashSetOf()) { createCurrentTransaction(networksAddresses, it) } - } - - private fun createCurrentTransaction( - networksAddresses: Set, - transaction: CryptoCurrencyTransaction, - ): PendingTransaction? { - val direction = when { - transaction.toAddress in networksAddresses -> PendingTransaction.Direction.Incoming( - fromAddress = transaction.fromAddress, - ) - transaction.fromAddress in networksAddresses -> PendingTransaction.Direction.Outgoing( - toAddress = transaction.toAddress, - ) - else -> { - Timber.e( - """ - Unable to find transaction direction - |- To address: ${transaction.toAddress} - |- From address: ${transaction.fromAddress} - |- Network addresses: $networksAddresses - """.trimIndent(), - ) - - return null - } - } - - return PendingTransaction( - amount = transaction.amount, - direction = direction, - sentAt = transaction.sentAt, - ) + private fun createCurrentTransactions(transactions: Set): Set { + return transactions.mapTo(hashSetOf()) { it.txHistoryItem } } private fun getNetworkAddress(defaultAddress: String, availableAddresses: Set): NetworkAddress { diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/model/CryptoCurrencyTransaction.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/model/CryptoCurrencyTransaction.kt index c0b69b941f..94c3bba81b 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/model/CryptoCurrencyTransaction.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/model/CryptoCurrencyTransaction.kt @@ -1,28 +1,17 @@ package com.tangem.domain.walletmanager.model -import org.joda.time.DateTime -import java.math.BigDecimal +import com.tangem.domain.txhistory.models.TxHistoryItem +// TODO: [REDACTED_JIRA] move to txhistory module sealed class CryptoCurrencyTransaction { - abstract val amount: BigDecimal - abstract val fromAddress: String? - abstract val toAddress: String? - abstract val sentAt: DateTime + abstract val txHistoryItem: TxHistoryItem - data class Coin( - override val amount: BigDecimal, - override val fromAddress: String?, - override val toAddress: String?, - override val sentAt: DateTime, - ) : CryptoCurrencyTransaction() + data class Coin(override val txHistoryItem: TxHistoryItem) : CryptoCurrencyTransaction() data class Token( val tokenId: String?, val tokenContractAddress: String, - override val amount: BigDecimal, - override val fromAddress: String?, - override val toAddress: String?, - override val sentAt: DateTime, + override val txHistoryItem: TxHistoryItem, ) : CryptoCurrencyTransaction() } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/UpdateWalletManagerResultFactory.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/UpdateWalletManagerResultFactory.kt index 4d644e4b7c..effa52cfdb 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/UpdateWalletManagerResultFactory.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/UpdateWalletManagerResultFactory.kt @@ -3,37 +3,37 @@ package com.tangem.domain.walletmanager.utils import com.tangem.blockchain.common.* import com.tangem.blockchain.common.address.Address import com.tangem.domain.common.extensions.amountToCreateAccount +import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.walletmanager.model.CryptoCurrencyAmount import com.tangem.domain.walletmanager.model.CryptoCurrencyTransaction import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult -import org.joda.time.DateTime -import org.joda.time.DateTimeZone -import org.joda.time.Instant import timber.log.Timber import java.math.BigDecimal -import java.util.Calendar +import java.util.concurrent.TimeUnit internal class UpdateWalletManagerResultFactory { fun getResult(walletManager: WalletManager): UpdateWalletManagerResult.Verified { val wallet = walletManager.wallet + val addresses = getAvailableAddresses(wallet.addresses) return UpdateWalletManagerResult.Verified( defaultAddress = wallet.address, - addresses = getAvailableAddresses(wallet.addresses), + addresses = addresses, currenciesAmounts = getTokensAmounts(wallet.amounts.values.toSet()), - currentTransactions = getCurrentTransactions(wallet.recentTransactions.toSet()), + currentTransactions = getCurrentTransactions(addresses, wallet.recentTransactions.toSet()), ) } fun getDemoResult(walletManager: WalletManager, demoAmount: Amount): UpdateWalletManagerResult.Verified { val wallet = walletManager.wallet + val addresses = getAvailableAddresses(wallet.addresses) return UpdateWalletManagerResult.Verified( defaultAddress = wallet.address, - addresses = getAvailableAddresses(wallet.addresses), + addresses = addresses, currenciesAmounts = getDemoTokensAmounts(demoAmount, walletManager.cardTokens), - currentTransactions = getCurrentTransactions(wallet.recentTransactions.toSet()), + currentTransactions = getCurrentTransactions(addresses, wallet.recentTransactions.toSet()), ) } @@ -70,12 +70,15 @@ internal class UpdateWalletManagerResultFactory { } } - private fun getCurrentTransactions(recentTransactions: Set): Set { + private fun getCurrentTransactions( + walletAddresses: Set, + recentTransactions: Set, + ): Set { val unconfirmedTransactions = recentTransactions.filter { it.status == TransactionStatus.Unconfirmed } - return unconfirmedTransactions.mapNotNullTo(hashSetOf(), ::createCurrencyTransaction) + return unconfirmedTransactions.mapNotNullTo(hashSetOf()) { createCurrencyTransaction(walletAddresses, it) } } private fun createCurrencyAmount(amount: Amount): CryptoCurrencyAmount? { @@ -92,31 +95,78 @@ internal class UpdateWalletManagerResultFactory { } } - private fun createCurrencyTransaction(data: TransactionData): CryptoCurrencyTransaction? { - val fromAddress = takeAddressIfNotUnknown(data.sourceAddress) - val toAddress = takeAddressIfNotUnknown(data.destinationAddress) - val amount = getTransactionAmountValue(data.amount) ?: return null - val sentAt = getTransactionSentTime(data.date) ?: return null - + private fun createCurrencyTransaction( + walletAddresses: Set, + data: TransactionData, + ): CryptoCurrencyTransaction? { return when (val type = data.amount.type) { - is AmountType.Coin -> CryptoCurrencyTransaction.Coin( - amount = amount, - fromAddress = fromAddress, - toAddress = toAddress, - sentAt = sentAt, - ) - is AmountType.Token -> CryptoCurrencyTransaction.Token( - tokenId = type.token.id, - tokenContractAddress = type.token.contractAddress, - amount = amount, - fromAddress = fromAddress, - toAddress = toAddress, - sentAt = sentAt, - ) + is AmountType.Coin -> { + val txHistoryItem = createTxHistoryItem(walletAddresses, data) ?: return null + CryptoCurrencyTransaction.Coin(txHistoryItem) + } + is AmountType.Token -> { + val txHistoryItem = createTxHistoryItem(walletAddresses, data) ?: return null + CryptoCurrencyTransaction.Token( + tokenId = type.token.id, + tokenContractAddress = type.token.contractAddress, + txHistoryItem = txHistoryItem, + ) + } is AmountType.Reserve -> null } } + private fun createTxHistoryItem(walletAddresses: Set, data: TransactionData): TxHistoryItem? { + val direction = extractDirection(walletAddresses, data) ?: run { + Timber.w("Can not determine address for $data") + return null + } + val hash = data.hash ?: return null + val millis = data.date?.timeInMillis ?: return null + val amount = getTransactionAmountValue(data.amount) ?: return null + + return TxHistoryItem( + txHash = hash, + timestampInMillis = TimeUnit.SECONDS.toMillis(millis), + direction = direction, + status = when (data.status) { + TransactionStatus.Confirmed -> TxHistoryItem.TxStatus.Confirmed + TransactionStatus.Unconfirmed -> TxHistoryItem.TxStatus.Unconfirmed + }, + type = TxHistoryItem.TransactionType.Transfer, + amount = amount, + ) + } + + private fun extractDirection( + walletAddresses: Set, + data: TransactionData, + ): TxHistoryItem.TransactionDirection? { + val fromAddress = data.sourceAddress + val toAddress = data.destinationAddress + + return when { + toAddress in walletAddresses -> { + TxHistoryItem.TransactionDirection.Incoming(TxHistoryItem.Address.Single(fromAddress)) + } + fromAddress in walletAddresses -> { + TxHistoryItem.TransactionDirection.Outgoing(TxHistoryItem.Address.Single(toAddress)) + } + else -> { + Timber.e( + """ + Unable to find transaction direction + |- To address: ${data.destinationAddress} + |- From address: ${data.sourceAddress} + |- Network addresses: $walletAddresses + """.trimIndent(), + ) + + return null + } + } + } + private fun getAvailableAddresses(addresses: Set
): Set { return addresses.mapTo(hashSetOf()) { it.value } } @@ -140,24 +190,4 @@ internal class UpdateWalletManagerResultFactory { return value } - - private fun getTransactionSentTime(date: Calendar?): DateTime? { - if (date == null) { - Timber.e("Transaction date must not be null") - return null - } - - val instant = Instant.ofEpochMilli(date.timeInMillis) - val timeZone = DateTimeZone.forTimeZone(date.timeZone) - - return instant.toDateTime(timeZone) - } - - private fun takeAddressIfNotUnknown(address: String): String? { - return address.takeIf { it.isNotBlank() && it != UNKNOWN_TRANSACTION_ADDRESS } - } - - private companion object { - const val UNKNOWN_TRANSACTION_ADDRESS = "unknown" - } } \ No newline at end of file diff --git a/domain/tokens/build.gradle.kts b/domain/tokens/build.gradle.kts index 17599c040d..c146658684 100644 --- a/domain/tokens/build.gradle.kts +++ b/domain/tokens/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { implementation(projects.domain.models) implementation(projects.domain.legacy) implementation(projects.domain.tokens.models) + implementation(projects.domain.txhistory.models) implementation(projects.domain.wallets.models) implementation(projects.domain.appCurrency.models) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt index b71f6496b5..1ccd0e1e61 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt @@ -1,6 +1,7 @@ package com.tangem.domain.tokens.model import com.tangem.domain.tokens.models.CryptoCurrency +import com.tangem.domain.txhistory.models.TxHistoryItem import java.math.BigDecimal /** @@ -39,7 +40,7 @@ data class CryptoCurrencyStatus( open val hasCurrentNetworkTransactions: Boolean = false /** The pending cryptocurrency transactions. */ - open val pendingTransactions: Set = emptySet() + open val pendingTransactions: Set = emptySet() /** The network address */ open val networkAddress: NetworkAddress? = null @@ -74,7 +75,7 @@ data class CryptoCurrencyStatus( override val fiatRate: BigDecimal, override val priceChange: BigDecimal, override val hasCurrentNetworkTransactions: Boolean, - override val pendingTransactions: Set, + override val pendingTransactions: Set, override val networkAddress: NetworkAddress?, ) : Status() @@ -95,7 +96,7 @@ data class CryptoCurrencyStatus( override val fiatRate: BigDecimal?, override val priceChange: BigDecimal?, override val hasCurrentNetworkTransactions: Boolean, - override val pendingTransactions: Set, + override val pendingTransactions: Set, override val networkAddress: NetworkAddress?, ) : Status() @@ -110,7 +111,7 @@ data class CryptoCurrencyStatus( data class NoQuote( override val amount: BigDecimal, override val hasCurrentNetworkTransactions: Boolean, - override val pendingTransactions: Set, + override val pendingTransactions: Set, override val networkAddress: NetworkAddress?, ) : Status() } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkStatus.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkStatus.kt index e9d5ec7405..87be054645 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkStatus.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkStatus.kt @@ -2,6 +2,7 @@ package com.tangem.domain.tokens.model import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network +import com.tangem.domain.txhistory.models.TxHistoryItem import java.math.BigDecimal /** @@ -44,7 +45,7 @@ data class NetworkStatus( data class Verified( val address: NetworkAddress, val amounts: Map, - val pendingTransactions: Map>, + val pendingTransactions: Map>, ) : Status() /** diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt index 1d46a74430..360e77ad20 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt @@ -82,5 +82,6 @@ internal object TokenDetailsPreviewData { ), ), dialogConfig = null, + pendingTxs = persistentListOf(), ) } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsState.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsState.kt index c6e26d31c5..7aece5e270 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsState.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsState.kt @@ -1,14 +1,17 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state import com.tangem.core.ui.components.marketprice.MarketPriceBlockState +import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsDialogConfig +import kotlinx.collections.immutable.PersistentList internal data class TokenDetailsState( val topAppBarConfig: TokenDetailsTopAppBarConfig, val tokenInfoBlockState: TokenInfoBlockState, val tokenBalanceBlockState: TokenDetailsBalanceBlockState, val marketPriceBlockState: MarketPriceBlockState, + val pendingTxs: PersistentList, val txHistoryState: TxHistoryState, val dialogConfig: TokenDetailsDialogConfig?, ) \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt index 19c2d0c8b4..1d09bb6fae 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt @@ -10,14 +10,22 @@ import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockState import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState +import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsTxHistoryToTransactionStateConverter import com.tangem.utils.converter.Converter +import kotlinx.collections.immutable.toPersistentList import java.math.BigDecimal internal class TokenDetailsLoadedBalanceConverter( private val currentStateProvider: Provider, private val appCurrencyProvider: Provider, + private val symbol: String, + private val decimals: Int, ) : Converter, TokenDetailsState> { + private val txHistoryItemConverter by lazy { + TokenDetailsTxHistoryToTransactionStateConverter(symbol, decimals) + } + override fun convert(value: Either): TokenDetailsState { return value.fold(ifLeft = { convertError() }, ifRight = ::convert) } @@ -33,6 +41,7 @@ internal class TokenDetailsLoadedBalanceConverter( return state.copy( tokenBalanceBlockState = getBalanceState(state.tokenBalanceBlockState, status), marketPriceBlockState = getMarketPriceState(status = status.value, currencyName = currencyName), + pendingTxs = status.value.pendingTransactions.map(txHistoryItemConverter::convert).toPersistentList(), ) } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt index 29e967a070..9d666add03 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsSkeletonStateConverter.kt @@ -42,6 +42,7 @@ internal class TokenDetailsSkeletonStateConverter( actionButtons = createButtons(), ), marketPriceBlockState = MarketPriceBlockState.Loading(value.cryptoCurrency.name), + pendingTxs = persistentListOf(), txHistoryState = TxHistoryState.Content( contentItems = MutableStateFlow( value = TxHistoryState.getDefaultLoadingTransactions(clickIntents::onExploreClick), diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt index 6db391a63d..eb3ee645d1 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt @@ -34,6 +34,8 @@ internal class TokenDetailsStateFactory( TokenDetailsLoadedBalanceConverter( currentStateProvider = currentStateProvider, appCurrencyProvider = appCurrencyProvider, + symbol = symbol, + decimals = decimals, ) } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryToTransactionStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryToTransactionStateConverter.kt new file mode 100644 index 0000000000..e0e714eea4 --- /dev/null +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryToTransactionStateConverter.kt @@ -0,0 +1,94 @@ +package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory + +import com.tangem.core.ui.components.transactions.state.TransactionState +import com.tangem.core.ui.extensions.TextReference +import com.tangem.domain.txhistory.models.TxHistoryItem +import com.tangem.features.tokendetails.impl.R +import com.tangem.utils.converter.Converter +import com.tangem.utils.toBriefAddressFormat +import com.tangem.utils.toFormattedCurrencyString +import org.joda.time.DateTime +import org.joda.time.DateTimeZone +import org.joda.time.format.DateTimeFormatterBuilder +import java.math.BigDecimal +import java.util.Locale + +internal class TokenDetailsTxHistoryToTransactionStateConverter( + private val symbol: String, + private val decimals: Int, +) : Converter { + + /** Example, 13:35 */ + private val timeFormatter by lazy { + DateTimeFormatterBuilder() + .appendHourOfDay(1) + .appendLiteral(':') + .appendMinuteOfHour(2) + .toFormatter() + .withLocale(Locale.getDefault()) + } + + override fun convert(value: TxHistoryItem): TransactionState { + return when (value.type) { + TxHistoryItem.TransactionType.Transfer -> { + when (val direction = value.direction) { + is TxHistoryItem.TransactionDirection.Incoming -> { + createIncomingTransferTransaction(value, direction) + } + is TxHistoryItem.TransactionDirection.Outgoing -> { + createOutgoingTransferTransaction(value, direction) + } + } + } + } + } + + private fun createIncomingTransferTransaction( + item: TxHistoryItem, + direction: TxHistoryItem.TransactionDirection.Incoming, + ): TransactionState { + return when (item.status) { + TxHistoryItem.TxStatus.Confirmed -> TransactionState.Receive( + txHash = item.txHash, + address = direction.extractAddress(), + amount = item.amount.toCryptoCurrencyFormat(), + timestamp = timeFormatter.print(DateTime(item.timestampInMillis, DateTimeZone.getDefault())), + ) + TxHistoryItem.TxStatus.Unconfirmed -> TransactionState.Receiving( + txHash = item.txHash, + address = direction.extractAddress(), + amount = item.amount.toCryptoCurrencyFormat(), + timestamp = timeFormatter.print(DateTime(item.timestampInMillis, DateTimeZone.getDefault())), + ) + } + } + + private fun createOutgoingTransferTransaction( + item: TxHistoryItem, + direction: TxHistoryItem.TransactionDirection.Outgoing, + ): TransactionState { + return when (item.status) { + TxHistoryItem.TxStatus.Confirmed -> TransactionState.Send( + txHash = item.txHash, + address = direction.extractAddress(), + amount = item.amount.toCryptoCurrencyFormat(), + timestamp = timeFormatter.print(DateTime(item.timestampInMillis, DateTimeZone.getDefault())), + ) + TxHistoryItem.TxStatus.Unconfirmed -> TransactionState.Sending( + txHash = item.txHash, + address = direction.extractAddress(), + amount = item.amount.toCryptoCurrencyFormat(), + timestamp = timeFormatter.print(DateTime(item.timestampInMillis, DateTimeZone.getDefault())), + ) + } + } + + private fun BigDecimal.toCryptoCurrencyFormat(): String { + return toFormattedCurrencyString(currency = symbol, decimals = decimals) + } + + private fun TxHistoryItem.TransactionDirection.extractAddress(): TextReference = when (val addr = address) { + TxHistoryItem.Address.Multiple -> TextReference.Res(R.string.transaction_history_multiple_addresses) + is TxHistoryItem.Address.Single -> TextReference.Str(addr.rawAddress.toBriefAddressFormat()) + } +} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt index 7ce9bb2274..9dbb266f47 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt @@ -1,15 +1,23 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.ui +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.util.fastForEach import androidx.paging.compose.collectAsLazyPagingItems import com.tangem.core.ui.components.marketprice.MarketPriceBlock import com.tangem.core.ui.components.marketprice.MarketPriceBlockState +import com.tangem.core.ui.components.transactions.Transaction +import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.components.transactions.txHistoryItems import com.tangem.core.ui.res.TangemTheme @@ -19,6 +27,7 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.T import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsDialogs import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsTopAppBar import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenInfoBlock +import kotlinx.collections.immutable.PersistentList @Composable internal fun TokenDetailsScreen(state: TokenDetailsState) { @@ -55,6 +64,14 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) { contentType = MarketPriceBlockState::class.java, content = { MarketPriceBlock(modifier = itemModifier, state = state.marketPriceBlockState) }, ) + if (state.txHistoryState is TxHistoryState.NotSupported && state.pendingTxs.isNotEmpty()) { + item { + PendingTxsBlock( + pendingTxs = state.pendingTxs, + modifier = itemModifier, + ) + } + } txHistoryItems(state = state.txHistoryState, txHistoryItems = txHistoryItems) } @@ -62,6 +79,19 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) { } } +@Composable +private fun PendingTxsBlock(pendingTxs: PersistentList, modifier: Modifier = Modifier) { + Column( + modifier = modifier + .clip(shape = TangemTheme.shapes.roundedCornersXMedium) + .background(color = TangemTheme.colors.background.primary), + verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing8), + horizontalAlignment = Alignment.Start, + ) { + pendingTxs.fastForEach { Transaction(state = it) } + } +} + @Preview @Composable private fun Preview_TokenDetailsScreen_LightTheme() {