Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-01 16:49:41 +05:00
parent 02f927fd48
commit 33f03e8653
11 changed files with 8 additions and 695 deletions

View file

@ -1,14 +1,11 @@
package com.tangem.feature.tokendetails.presentation.tokendetails package com.tangem.feature.tokendetails.presentation.tokendetails
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.paging.PagingData
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.components.marketprice.PriceChangeState import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.components.marketprice.PriceChangeType import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.components.transactions.state.TransactionState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringReference
@ -18,7 +15,6 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.*
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton
import com.tangem.features.tokendetails.impl.R import com.tangem.features.tokendetails.impl.R
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.MutableStateFlow
import java.math.BigDecimal import java.math.BigDecimal
@Suppress("LargeClass") @Suppress("LargeClass")
@ -168,130 +164,6 @@ internal object TokenDetailsPreviewData {
onRefresh = {}, onRefresh = {},
) )
private val txHistoryItems = listOf(
TxHistoryState.TxHistoryItemState.Title(onExploreClick = {}),
TxHistoryState.TxHistoryItemState.GroupTitle(
title = "Today",
itemKey = "Today",
),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Content(
txHash = "1",
amount = "-0.500913 XLM",
time = "8:41",
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.OUTGOING,
onClick = {},
iconRes = R.drawable.ic_arrow_up_24,
title = stringReference(value = "Sending"),
subtitle = stringReference(value = "to: 33BddS...ga2B"),
timestamp = 0,
),
),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Content(
txHash = "2",
amount = "+0.500913 XLM",
time = "8:41",
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.INCOMING,
onClick = {},
iconRes = R.drawable.ic_arrow_down_24,
title = stringReference(value = "Receiving"),
subtitle = stringReference(value = "from: 33BddS...ga2B"),
timestamp = 0,
),
),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Content(
txHash = "3",
amount = "+0.500913 XLM",
time = "8:41",
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.INCOMING,
onClick = {},
iconRes = R.drawable.ic_doc_24,
title = stringReference(value = "Approving"),
subtitle = stringReference(value = "from: 33BddS...ga2B"),
timestamp = 0,
),
),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Content(
txHash = "4",
amount = "+0.500913 XLM",
time = "8:41",
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.INCOMING,
onClick = {},
iconRes = R.drawable.ic_exchange_vertical_24,
title = stringReference(value = "Swapping"),
subtitle = stringReference(value = "contract: 33BddS...ga2B"),
timestamp = 0,
),
),
TxHistoryState.TxHistoryItemState.GroupTitle(
title = "Yesterday",
itemKey = "Yesterday",
),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Content(
txHash = "5",
amount = "-0.500913 XLM",
time = "8:41",
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.OUTGOING,
onClick = {},
iconRes = R.drawable.ic_arrow_up_24,
title = stringReference(value = "Sending"),
subtitle = stringReference(value = "to: 33BddS...ga2B"),
timestamp = 0,
),
),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Content(
txHash = "6",
amount = "+0.500913 XLM",
time = "8:41",
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.INCOMING,
onClick = {},
iconRes = R.drawable.ic_arrow_down_24,
title = stringReference(value = "Receiving"),
subtitle = stringReference(value = "from: 33BddS...ga2B"),
timestamp = 0,
),
),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Content(
txHash = "7",
amount = "+0.500913 XLM",
time = "8:41",
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.INCOMING,
onClick = {},
iconRes = R.drawable.ic_doc_24,
title = stringReference(value = "Approving"),
subtitle = stringReference(value = "from: 33BddS...ga2B"),
timestamp = 0,
),
),
TxHistoryState.TxHistoryItemState.Transaction(
state = TransactionState.Content(
txHash = "8",
amount = "+0.500913 XLM",
time = "8:41",
status = TransactionState.Content.Status.Confirmed,
direction = TransactionState.Content.Direction.INCOMING,
onClick = {},
iconRes = R.drawable.ic_exchange_vertical_24,
title = stringReference(value = "Swapping"),
subtitle = stringReference(value = "contract: 33BddS...ga2B"),
timestamp = 0,
),
),
)
val tokenDetailsState_1 = TokenDetailsState( val tokenDetailsState_1 = TokenDetailsState(
topAppBarConfig = tokenDetailsTopAppBarConfig, topAppBarConfig = tokenDetailsTopAppBarConfig,
tokenInfoBlockState = tokenInfoBlockState, tokenInfoBlockState = tokenInfoBlockState,
@ -299,13 +171,7 @@ internal object TokenDetailsPreviewData {
marketPriceBlockState = marketPriceLoading, marketPriceBlockState = marketPriceLoading,
stakingBlocksState = stakingLoadingBlock, stakingBlocksState = stakingLoadingBlock,
notifications = persistentListOf(), notifications = persistentListOf(),
txHistoryState = TxHistoryState.Content(
contentItems = MutableStateFlow(
value = TxHistoryState.getDefaultLoadingTransactions {},
),
),
dialogConfig = null, dialogConfig = null,
pendingTxs = persistentListOf(),
expressTxs = persistentListOf(), expressTxs = persistentListOf(),
expressTxsToDisplay = persistentListOf(), expressTxsToDisplay = persistentListOf(),
pullToRefreshConfig = pullToRefreshConfig, pullToRefreshConfig = pullToRefreshConfig,
@ -330,12 +196,7 @@ internal object TokenDetailsPreviewData {
), ),
stakingBlocksState = stakingAvailableBlock, stakingBlocksState = stakingAvailableBlock,
notifications = persistentListOf(), notifications = persistentListOf(),
txHistoryState = TxHistoryState.NotSupported(
onExploreClick = {},
pendingTransactions = persistentListOf(),
),
dialogConfig = null, dialogConfig = null,
pendingTxs = persistentListOf(),
expressTxs = persistentListOf(), expressTxs = persistentListOf(),
expressTxsToDisplay = persistentListOf(), expressTxsToDisplay = persistentListOf(),
pullToRefreshConfig = pullToRefreshConfig, pullToRefreshConfig = pullToRefreshConfig,
@ -344,12 +205,5 @@ internal object TokenDetailsPreviewData {
isMarketPriceAvailable = true, isMarketPriceAvailable = true,
) )
val tokenDetailsState_3 = tokenDetailsState_2.copy( val tokenDetailsState_3 = tokenDetailsState_2.copy(stakingBlocksState = stakingBalanceBlock)
txHistoryState = TxHistoryState.Content(
contentItems = MutableStateFlow(
value = PagingData.from(txHistoryItems),
),
),
stakingBlocksState = stakingBalanceBlock,
)
} }

View file

@ -1,7 +1,6 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.model package com.tangem.feature.tokendetails.presentation.tokendetails.model
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.paging.cachedIn
import arrow.core.getOrElse import arrow.core.getOrElse
import arrow.core.merge import arrow.core.merge
import com.arkivanov.decompose.router.slot.SlotNavigation import com.arkivanov.decompose.router.slot.SlotNavigation
@ -24,7 +23,6 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.share.ShareManager import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.ui.clipboard.ClipboardManager import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringReference
@ -69,8 +67,6 @@ import com.tangem.domain.transaction.error.OpenTrustlineError
import com.tangem.domain.transaction.error.SendTransactionError import com.tangem.domain.transaction.error.SendTransactionError
import com.tangem.domain.transaction.usecase.* import com.tangem.domain.transaction.usecase.*
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase
import com.tangem.domain.wallets.usecase.GetExtendedPublicKeyForCurrencyUseCase import com.tangem.domain.wallets.usecase.GetExtendedPublicKeyForCurrencyUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
@ -107,8 +103,6 @@ internal class TokenDetailsModel @Inject constructor(
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase, private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase,
private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase,
private val getExploreUrlUseCase: GetExploreUrlUseCase, private val getExploreUrlUseCase: GetExploreUrlUseCase,
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase, private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase,
private val removeCurrencyUseCase: RemoveCurrencyUseCase, private val removeCurrencyUseCase: RemoveCurrencyUseCase,
@ -177,8 +171,6 @@ internal class TokenDetailsModel @Inject constructor(
networkHasDerivationUseCase = networkHasDerivationUseCase, networkHasDerivationUseCase = networkHasDerivationUseCase,
getUserWalletUseCase = getUserWalletUseCase, getUserWalletUseCase = getUserWalletUseCase,
userWalletId = userWalletId, userWalletId = userWalletId,
symbol = cryptoCurrency.symbol,
decimals = cryptoCurrency.decimals,
) )
private val expressStatusFactory by lazy(mode = LazyThreadSafetyMode.NONE) { private val expressStatusFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
@ -251,7 +243,6 @@ internal class TokenDetailsModel @Inject constructor(
private fun updateContent() { private fun updateContent() {
subscribeOnCurrencyStatusUpdates() subscribeOnCurrencyStatusUpdates()
subscribeOnExpressTransactionsUpdates() subscribeOnExpressTransactionsUpdates()
updateTxHistory(refresh = false, showItemsLoading = true, initialUpdating = true)
} }
private fun handleBalanceHiding() { private fun handleBalanceHiding() {
@ -382,39 +373,8 @@ internal class TokenDetailsModel @Inject constructor(
} }
} }
/** private fun updateTxHistory() {
* @param refresh - invalidate cache and get data from remote modelScope.launch { txHistoryContentUpdateEmitter.triggerUpdate() }
* @param showItemsLoading - show loading items placeholder.
*/
private fun updateTxHistory(refresh: Boolean, showItemsLoading: Boolean, initialUpdating: Boolean = false) {
modelScope.launch {
if (!initialUpdating) {
txHistoryContentUpdateEmitter.triggerUpdate()
} else {
val txHistoryItemsCountEither = txHistoryItemsCountUseCase(
userWalletId = userWalletId,
currency = cryptoCurrency,
)
// if countEither is left, handling error state run inside getLoadingTxHistoryState
if (showItemsLoading || txHistoryItemsCountEither.isLeft()) {
internalUiState.value = stateFactory.getLoadingTxHistoryState(
itemsCountEither = txHistoryItemsCountEither,
pendingTransactions = internalUiState.value.pendingTxs,
)
}
txHistoryItemsCountEither.onRight {
val maybeTxHistory = txHistoryItemsUseCase(
userWalletId = userWalletId,
currency = cryptoCurrency,
refresh = refresh,
).map { it.cachedIn(modelScope) }
internalUiState.value = stateFactory.getLoadedTxHistoryState(maybeTxHistory)
}
}
}
} }
private fun subscribeOnUpdateStakingInfo(cryptoCurrencyStatus: CryptoCurrencyStatus) { private fun subscribeOnUpdateStakingInfo(cryptoCurrencyStatus: CryptoCurrencyStatus) {
@ -552,8 +512,7 @@ internal class TokenDetailsModel @Inject constructor(
override fun onReloadClick() { override fun onReloadClick() {
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonReload(cryptoCurrency.symbol)) analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonReload(cryptoCurrency.symbol))
internalUiState.value = stateFactory.getLoadingTxHistoryState() updateTxHistory()
updateTxHistory(refresh = true, showItemsLoading = true)
} }
override fun onSendClick(unavailabilityReason: ScenarioUnavailabilityReason) { override fun onSendClick(unavailabilityReason: ScenarioUnavailabilityReason) {
@ -800,10 +759,7 @@ internal class TokenDetailsModel @Inject constructor(
listOf( listOf(
async { fetchCurrencyStatusUseCase(userWalletId = userWalletId, id = cryptoCurrency.id) }, async { fetchCurrencyStatusUseCase(userWalletId = userWalletId, id = cryptoCurrency.id) },
async { async {
updateTxHistory( updateTxHistory()
refresh = true,
showItemsLoading = internalUiState.value.txHistoryState !is TxHistoryState.Content,
)
subscribeOnExpressTransactionsUpdates() subscribeOnExpressTransactionsUpdates()
}, },
).awaitAll() ).awaitAll()

View file

@ -2,10 +2,8 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state
import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
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.core.ui.components.containers.pullToRefresh.PullToRefreshConfig import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsDialogConfig import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsDialogConfig
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@ -18,10 +16,8 @@ internal data class TokenDetailsState(
val marketPriceBlockState: MarketPriceBlockState, val marketPriceBlockState: MarketPriceBlockState,
val stakingBlocksState: StakingBlockUM?, val stakingBlocksState: StakingBlockUM?,
val notifications: ImmutableList<TokenDetailsNotification>, val notifications: ImmutableList<TokenDetailsNotification>,
val pendingTxs: PersistentList<TransactionState>,
val expressTxsToDisplay: PersistentList<ExpressTransactionStateUM>, val expressTxsToDisplay: PersistentList<ExpressTransactionStateUM>,
val expressTxs: PersistentList<ExpressTransactionStateUM>, val expressTxs: PersistentList<ExpressTransactionStateUM>,
val txHistoryState: TxHistoryState,
val dialogConfig: TokenDetailsDialogConfig?, val dialogConfig: TokenDetailsDialogConfig?,
val pullToRefreshConfig: PullToRefreshConfig, val pullToRefreshConfig: PullToRefreshConfig,
val bottomSheetConfig: TangemBottomSheetConfig?, val bottomSheetConfig: TangemBottomSheetConfig?,

View file

@ -5,7 +5,6 @@ import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.components.marketprice.PriceChangeState import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.components.marketprice.PriceChangeType import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.components.marketprice.utils.PriceChangeConverter import com.tangem.core.ui.components.marketprice.utils.PriceChangeConverter
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.format.bigdecimal.* import com.tangem.core.ui.format.bigdecimal.*
import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.StatusSource import com.tangem.domain.models.StatusSource
@ -18,29 +17,20 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.BalanceTy
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockState 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.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsTxHistoryTransactionStateConverter
import com.tangem.feature.tokendetails.presentation.tokendetails.state.utils.getBalance import com.tangem.feature.tokendetails.presentation.tokendetails.state.utils.getBalance
import com.tangem.utils.Provider import com.tangem.utils.Provider
import com.tangem.utils.StringsSigns.DASH_SIGN import com.tangem.utils.StringsSigns.DASH_SIGN
import com.tangem.utils.converter.Converter import com.tangem.utils.converter.Converter
import com.tangem.utils.isNullOrZero import com.tangem.utils.isNullOrZero
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
import java.math.BigDecimal import java.math.BigDecimal
@Suppress("LongParameterList")
internal class TokenDetailsLoadedBalanceConverter( internal class TokenDetailsLoadedBalanceConverter(
private val currentStateProvider: Provider<TokenDetailsState>, private val currentStateProvider: Provider<TokenDetailsState>,
private val appCurrencyProvider: Provider<AppCurrency>, private val appCurrencyProvider: Provider<AppCurrency>,
private val symbol: String,
private val decimals: Int,
private val clickIntents: TokenDetailsClickIntents, private val clickIntents: TokenDetailsClickIntents,
) : Converter<Either<CurrencyStatusError, CryptoCurrencyStatus>, TokenDetailsState> { ) : Converter<Either<CurrencyStatusError, CryptoCurrencyStatus>, TokenDetailsState> {
private val txHistoryItemConverter by lazy {
TokenDetailsTxHistoryTransactionStateConverter(symbol, decimals, clickIntents)
}
override fun convert(value: Either<CurrencyStatusError, CryptoCurrencyStatus>): TokenDetailsState { override fun convert(value: Either<CurrencyStatusError, CryptoCurrencyStatus>): TokenDetailsState {
return value.fold( return value.fold(
ifLeft = { convertError() }, ifLeft = { convertError() },
@ -64,7 +54,6 @@ internal class TokenDetailsLoadedBalanceConverter(
private fun convert(status: CryptoCurrencyStatus): TokenDetailsState { private fun convert(status: CryptoCurrencyStatus): TokenDetailsState {
val state = currentStateProvider() val state = currentStateProvider()
val currencyName = state.marketPriceBlockState.currencySymbol val currencyName = state.marketPriceBlockState.currencySymbol
val pendingTxs = status.value.pendingTransactions.map(txHistoryItemConverter::convert).toPersistentList()
return state.copy( return state.copy(
tokenBalanceBlockState = getBalanceState( tokenBalanceBlockState = getBalanceState(
@ -73,12 +62,6 @@ internal class TokenDetailsLoadedBalanceConverter(
), ),
stakingBlocksState = state.stakingBlocksState, stakingBlocksState = state.stakingBlocksState,
marketPriceBlockState = getMarketPriceState(status = status.value, currencySymbol = currencyName), marketPriceBlockState = getMarketPriceState(status = status.value, currencySymbol = currencyName),
pendingTxs = pendingTxs,
txHistoryState = if (state.txHistoryState is TxHistoryState.NotSupported) {
state.txHistoryState.copy(pendingTransactions = pendingTxs)
} else {
state.txHistoryState
},
) )
} }

View file

@ -4,7 +4,6 @@ import arrow.core.getOrElse
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.networkIconResId import com.tangem.core.ui.extensions.networkIconResId
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
@ -24,7 +23,6 @@ import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.MutableStateFlow
internal class TokenDetailsSkeletonStateConverter( internal class TokenDetailsSkeletonStateConverter(
private val clickIntents: TokenDetailsClickIntents, private val clickIntents: TokenDetailsClickIntents,
@ -64,14 +62,8 @@ internal class TokenDetailsSkeletonStateConverter(
marketPriceBlockState = MarketPriceBlockState.Loading(value.symbol), marketPriceBlockState = MarketPriceBlockState.Loading(value.symbol),
stakingBlocksState = StakingBlockUM.Loading(iconState).takeIf { isSupportedInMobileApp }, stakingBlocksState = StakingBlockUM.Loading(iconState).takeIf { isSupportedInMobileApp },
notifications = persistentListOf(), notifications = persistentListOf(),
pendingTxs = persistentListOf(),
expressTxs = persistentListOf(), expressTxs = persistentListOf(),
expressTxsToDisplay = persistentListOf(), expressTxsToDisplay = persistentListOf(),
txHistoryState = TxHistoryState.Content(
contentItems = MutableStateFlow(
value = TxHistoryState.getDefaultLoadingTransactions(clickIntents::onExploreClick),
),
),
dialogConfig = null, dialogConfig = null,
pullToRefreshConfig = createPullToRefresh(), pullToRefreshConfig = createPullToRefresh(),
bottomSheetConfig = null, bottomSheetConfig = null,

View file

@ -1,14 +1,11 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
import androidx.paging.PagingData
import arrow.core.Either import arrow.core.Either
import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheetConfig import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheetConfig
import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
import com.tangem.common.ui.tokens.getUnavailabilityReasonText import com.tangem.common.ui.tokens.getUnavailabilityReasonText
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
import com.tangem.core.ui.components.transactions.state.TransactionState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
@ -18,7 +15,6 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.network.Network import com.tangem.domain.models.network.Network
import com.tangem.domain.models.network.NetworkAddress import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.models.network.TxInfo
import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.staking.model.StakingAvailability import com.tangem.domain.staking.model.StakingAvailability
@ -27,8 +23,6 @@ import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.domain.tokens.model.TokenActionsState import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
import com.tangem.domain.txhistory.models.TxHistoryListError
import com.tangem.domain.txhistory.models.TxHistoryStateError
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase import com.tangem.domain.wallets.usecase.NetworkHasDerivationUseCase
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
@ -36,14 +30,9 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBala
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsAppBarMenuConfig import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsAppBarMenuConfig
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsDialogConfig import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsDialogConfig
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadedTxHistoryConverter
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter.TokenDetailsLoadingTxHistoryModel
import com.tangem.features.tokendetails.impl.R import com.tangem.features.tokendetails.impl.R
import com.tangem.utils.Provider import com.tangem.utils.Provider
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@Suppress("TooManyFunctions", "LargeClass", "LongParameterList") @Suppress("TooManyFunctions", "LargeClass", "LongParameterList")
internal class TokenDetailsStateFactory( internal class TokenDetailsStateFactory(
@ -54,8 +43,6 @@ internal class TokenDetailsStateFactory(
private val networkHasDerivationUseCase: NetworkHasDerivationUseCase, private val networkHasDerivationUseCase: NetworkHasDerivationUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase, private val getUserWalletUseCase: GetUserWalletUseCase,
private val userWalletId: UserWalletId, private val userWalletId: UserWalletId,
symbol: String,
decimals: Int,
) { ) {
private val skeletonStateConverter by lazy { private val skeletonStateConverter by lazy {
@ -75,8 +62,6 @@ internal class TokenDetailsStateFactory(
TokenDetailsLoadedBalanceConverter( TokenDetailsLoadedBalanceConverter(
currentStateProvider = currentStateProvider, currentStateProvider = currentStateProvider,
appCurrencyProvider = appCurrencyProvider, appCurrencyProvider = appCurrencyProvider,
symbol = symbol,
decimals = decimals,
clickIntents = clickIntents, clickIntents = clickIntents,
) )
} }
@ -88,22 +73,6 @@ internal class TokenDetailsStateFactory(
) )
} }
private val loadingTransactionsStateConverter by lazy {
TokenDetailsLoadingTxHistoryConverter(
currentStateProvider = currentStateProvider,
clickIntents = clickIntents,
)
}
private val loadedTxHistoryConverter by lazy {
TokenDetailsLoadedTxHistoryConverter(
currentStateProvider = currentStateProvider,
clickIntents = clickIntents,
symbol = symbol,
decimals = decimals,
)
}
private val refreshStateConverter by lazy { private val refreshStateConverter by lazy {
TokenDetailsRefreshStateConverter( TokenDetailsRefreshStateConverter(
currentStateProvider = currentStateProvider, currentStateProvider = currentStateProvider,
@ -147,36 +116,6 @@ internal class TokenDetailsStateFactory(
return tokenDetailsButtonsConverter.convert(actions) return tokenDetailsButtonsConverter.convert(actions)
} }
fun getLoadingTxHistoryState(): TokenDetailsState {
return currentStateProvider().copy(
txHistoryState = TxHistoryState.Content(
contentItems = MutableStateFlow(
value = TxHistoryState.getDefaultLoadingTransactions(clickIntents::onExploreClick),
),
),
)
}
fun getLoadingTxHistoryState(
itemsCountEither: Either<TxHistoryStateError, Int>,
pendingTransactions: List<TransactionState>,
): TokenDetailsState {
return loadingTransactionsStateConverter.convert(
value = TokenDetailsLoadingTxHistoryModel(
historyLoadingState = itemsCountEither,
pendingTransactions = pendingTransactions,
),
)
}
fun getLoadedTxHistoryState(
txHistoryEither: Either<TxHistoryListError, Flow<PagingData<TxInfo>>>,
): TokenDetailsState {
return currentStateProvider().copy(
txHistoryState = loadedTxHistoryConverter.convert(txHistoryEither),
)
}
fun getStateWithClosedDialog(): TokenDetailsState { fun getStateWithClosedDialog(): TokenDetailsState {
val state = currentStateProvider() val state = currentStateProvider()
return state.copy(dialogConfig = state.dialogConfig?.copy(isShow = false)) return state.copy(dialogConfig = state.dialogConfig?.copy(isShow = false))

View file

@ -1,48 +0,0 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory
import androidx.paging.PagingData
import arrow.core.Either
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.domain.models.network.TxInfo
import com.tangem.domain.txhistory.models.TxHistoryListError
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
import kotlinx.coroutines.flow.Flow
internal class TokenDetailsLoadedTxHistoryConverter(
private val currentStateProvider: Provider<TokenDetailsState>,
private val clickIntents: TokenDetailsClickIntents,
symbol: String,
decimals: Int,
) : Converter<Either<TxHistoryListError, Flow<PagingData<TxInfo>>>, TxHistoryState> {
private val txHistoryItemFlowConverter by lazy {
TokenDetailsTxHistoryItemFlowConverter(
currentStateProvider = currentStateProvider,
symbol = symbol,
decimals = decimals,
clickIntents = clickIntents,
)
}
override fun convert(value: Either<TxHistoryListError, Flow<PagingData<TxInfo>>>): TxHistoryState {
return value.fold(ifLeft = ::convertError, ifRight = ::convert)
}
private fun convertError(error: TxHistoryListError): TxHistoryState {
return when (error) {
is TxHistoryListError.DataError -> {
TxHistoryState.Error(
onReloadClick = clickIntents::onReloadClick,
onExploreClick = clickIntents::onExploreClick,
)
}
}
}
private fun convert(items: Flow<PagingData<TxInfo>>): TxHistoryState {
return txHistoryItemFlowConverter.convert(value = items)
}
}

View file

@ -1,81 +0,0 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory
import androidx.paging.PagingData
import arrow.core.Either
import com.tangem.core.ui.components.transactions.state.TransactionState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.domain.txhistory.models.TxHistoryStateError
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter.TokenDetailsLoadingTxHistoryModel
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
internal class TokenDetailsLoadingTxHistoryConverter(
private val currentStateProvider: Provider<TokenDetailsState>,
private val clickIntents: TokenDetailsClickIntents,
) : Converter<TokenDetailsLoadingTxHistoryModel, TokenDetailsState> {
override fun convert(value: TokenDetailsLoadingTxHistoryModel): TokenDetailsState {
return value.historyLoadingState.fold(
ifLeft = { convertError(error = it, pendingTransactions = value.pendingTransactions) },
ifRight = ::convert,
)
}
private fun convertError(
error: TxHistoryStateError,
pendingTransactions: List<TransactionState>,
): TokenDetailsState {
return currentStateProvider().copy(
txHistoryState = when (error) {
is TxHistoryStateError.EmptyTxHistories -> TxHistoryState.Empty(clickIntents::onExploreClick)
is TxHistoryStateError.DataError -> TxHistoryState.Error(
onReloadClick = clickIntents::onReloadClick,
onExploreClick = clickIntents::onExploreClick,
)
is TxHistoryStateError.TxHistoryNotImplemented -> {
TxHistoryState.NotSupported(
pendingTransactions = pendingTransactions.toImmutableList(),
onExploreClick = clickIntents::onExploreClick,
)
}
},
)
}
private fun convert(value: Int): TokenDetailsState {
val state = currentStateProvider()
return if (state.txHistoryState is TxHistoryState.Content) {
state.txHistoryState.contentItems.update {
PagingData.from(data = createLoadingItems(value))
}
state
} else {
val txHistoryContent = TxHistoryState.Content(
contentItems = MutableStateFlow(
value = PagingData.from(data = createLoadingItems(value)),
),
)
state.copy(txHistoryState = txHistoryContent)
}
}
private fun createLoadingItems(size: Int): List<TxHistoryState.TxHistoryItemState> {
return buildList {
add(TxHistoryState.TxHistoryItemState.Title(onExploreClick = clickIntents::onExploreClick))
(1..size).forEach {
add(TxHistoryState.TxHistoryItemState.Transaction(state = TransactionState.Loading(it.toString())))
}
}
}
data class TokenDetailsLoadingTxHistoryModel(
val historyLoadingState: Either<TxHistoryStateError, Int>,
val pendingTransactions: List<TransactionState>,
)
}

View file

@ -1,103 +0,0 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory
import androidx.paging.*
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.state.TxHistoryState.TxHistoryItemState
import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
import com.tangem.domain.models.network.TxInfo
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import java.util.UUID
internal class TokenDetailsTxHistoryItemFlowConverter(
private val currentStateProvider: Provider<TokenDetailsState>,
private val symbol: String,
private val decimals: Int,
private val clickIntents: TokenDetailsClickIntents,
) : Converter<Flow<PagingData<TxInfo>>, TxHistoryState> {
private val txHistoryItemConverter by lazy {
TokenDetailsTxHistoryTransactionStateConverter(
symbol = symbol,
decimals = decimals,
clickIntents = clickIntents,
)
}
override fun convert(value: Flow<PagingData<TxInfo>>): TxHistoryState {
val state = currentStateProvider()
val txHistoryContent = if (state.txHistoryState is TxHistoryState.Content) {
state.txHistoryState
} else {
TxHistoryState.Content(contentItems = MutableStateFlow(PagingData.empty()))
}
// FIXME: TxHistoryRepository should send loading transactions
// [REDACTED_JIRA]
value
.onEach { txHistoryStatePagingData ->
txHistoryContent.contentItems.update {
txHistoryStatePagingData
.map<TxInfo, TxHistoryItemState> { item ->
// [createTransactionState] returns timestamp without formatting
TxHistoryItemState.Transaction(state = createTransactionState(item))
}
.insertHeaderItem(
terminalSeparatorType = TerminalSeparatorType.SOURCE_COMPLETE,
item = TxHistoryItemState.Title(clickIntents::onExploreClick),
)
.insertGroupTitle()
}
}
.launchIn(CoroutineScope(Dispatchers.IO))
return txHistoryContent
}
private fun createTransactionState(item: TxInfo): TransactionState {
return txHistoryItemConverter.convert(value = item)
}
private fun PagingData<TxHistoryItemState>.insertGroupTitle(): PagingData<TxHistoryItemState> {
return insertSeparators(terminalSeparatorType = TerminalSeparatorType.SOURCE_COMPLETE) { before, after ->
// Use raw timestamp to get date
// If [afterDate] is the first transaction in the flow, add the group title
val afterDate = after.getTimestamp()?.toDateFormatWithTodayYesterday() ?: return@insertSeparators null
if (before is TxHistoryItemState.Title) {
return@insertSeparators TxHistoryItemState.GroupTitle(
title = afterDate,
itemKey = UUID.randomUUID().toString(),
)
}
/*
* If [beforeDate] is not equals to [afterDate], then [afterDate] is first transaction in
* the new group
*/
val beforeDate = before.getTimestamp()?.toDateFormatWithTodayYesterday() ?: return@insertSeparators null
return@insertSeparators if (beforeDate != afterDate) {
TxHistoryItemState.GroupTitle(
title = afterDate,
itemKey = UUID.randomUUID().toString(),
)
} else {
null
}
}
}
private fun TxHistoryItemState?.getTimestamp(): Long? {
return if (this is TxHistoryItemState.Transaction && this.state is TransactionState.Content) {
val txContent = this.state as TransactionState.Content
txContent.timestamp
} else {
null
}
}
}

View file

@ -1,136 +0,0 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory
import com.tangem.common.extensions.isZero
import com.tangem.core.ui.components.transactions.state.TransactionState
import com.tangem.core.ui.components.transactions.state.TransactionState.Content.Direction
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.utils.toTimeFormat
import com.tangem.domain.models.network.TxInfo
import com.tangem.domain.models.network.TxInfo.*
import com.tangem.feature.tokendetails.presentation.tokendetails.model.TokenDetailsClickIntents
import com.tangem.features.tokendetails.impl.R
import com.tangem.utils.StringsSigns.MINUS
import com.tangem.utils.StringsSigns.PLUS
import com.tangem.utils.converter.Converter
import com.tangem.utils.toBriefAddressFormat
internal class TokenDetailsTxHistoryTransactionStateConverter(
private val symbol: String,
private val decimals: Int,
private val clickIntents: TokenDetailsClickIntents,
) : Converter<TxInfo, TransactionState> {
override fun convert(value: TxInfo): TransactionState {
return createTransactionStateItem(item = value)
}
@Suppress("LongMethod")
private fun createTransactionStateItem(item: TxInfo): TransactionState {
return TransactionState.Content(
txHash = item.txHash,
amount = item.getAmount(),
time = item.timestampInMillis.toTimeFormat(),
status = item.status.tiUiStatus(),
direction = item.extractDirection(),
iconRes = item.extractIcon(),
title = item.extractTitle(),
subtitle = item.extractSubtitle(),
timestamp = item.timestampInMillis,
onClick = { clickIntents.onTransactionClick(item.txHash) },
)
}
private fun TxInfo.extractIcon(): Int = if (status == TransactionStatus.Failed) {
R.drawable.ic_close_24
} else {
when (type) {
is TransactionType.Approve -> R.drawable.ic_doc_24
is TransactionType.Staking.Stake,
is TransactionType.Staking.Vote,
is TransactionType.Staking.Restake,
-> R.drawable.ic_transaction_history_staking_24
is TransactionType.Staking.ClaimRewards,
-> R.drawable.ic_transaction_history_claim_rewards_24
is TransactionType.Staking.Unstake,
is TransactionType.Staking.Withdraw,
-> R.drawable.ic_transaction_history_unstaking_24
is TransactionType.Operation,
is TransactionType.Swap,
is TransactionType.Transfer,
is TransactionType.UnknownOperation,
-> if (isOutgoing) R.drawable.ic_arrow_up_24 else R.drawable.ic_arrow_down_24
}
}
private fun TxInfo.extractTitle(): TextReference = when (val type = type) {
is TransactionType.Approve -> resourceReference(R.string.common_approval)
is TransactionType.Operation -> stringReference(type.name)
is TransactionType.Swap -> resourceReference(R.string.common_swap)
is TransactionType.Transfer -> resourceReference(R.string.common_transfer)
is TransactionType.UnknownOperation -> resourceReference(R.string.transaction_history_operation)
is TransactionType.Staking.Stake -> resourceReference(R.string.common_stake)
is TransactionType.Staking.Unstake -> resourceReference(R.string.common_unstake)
is TransactionType.Staking.Vote -> resourceReference(R.string.staking_vote)
is TransactionType.Staking.ClaimRewards -> resourceReference(R.string.common_claim_rewards)
is TransactionType.Staking.Withdraw -> resourceReference(R.string.staking_withdraw)
is TransactionType.Staking.Restake -> resourceReference(R.string.staking_restake)
}
private fun TxInfo.extractSubtitle(): TextReference = when (val interactionAddress = interactionAddressType) {
is InteractionAddressType.Contract -> resourceReference(
id = R.string.transaction_history_contract_address,
formatArgs = wrappedList(interactionAddress.address.toBriefAddressFormat()),
)
is InteractionAddressType.Multiple -> resourceReference(
id = if (isOutgoing) {
R.string.transaction_history_transaction_to_address
} else {
R.string.transaction_history_transaction_from_address
},
formatArgs = wrappedList(resourceReference(R.string.transaction_history_multiple_addresses)),
)
is InteractionAddressType.User -> resourceReference(
id = if (isOutgoing) {
R.string.transaction_history_transaction_to_address
} else {
R.string.transaction_history_transaction_from_address
},
formatArgs = wrappedList(interactionAddress.address.toBriefAddressFormat()),
)
is InteractionAddressType.Validator -> resourceReference(
id = R.string.transaction_history_transaction_validator,
formatArgs = wrappedList(interactionAddress.address.toBriefAddressFormat()),
)
null -> {
TextReference.EMPTY
}
}
private fun TxInfo.extractDirection() = if (isOutgoing) Direction.OUTGOING else Direction.INCOMING
private fun TransactionStatus.tiUiStatus() = when (this) {
TransactionStatus.Confirmed -> TransactionState.Content.Status.Confirmed
TransactionStatus.Failed -> TransactionState.Content.Status.Failed
TransactionStatus.Unconfirmed -> TransactionState.Content.Status.Unconfirmed
}
private fun TxInfo.getAmount(): String {
if (type is TransactionType.Staking.Vote ||
type == TransactionType.Staking.ClaimRewards ||
type == TransactionType.Staking.Withdraw
) {
return ""
}
val prefix = when {
status == TransactionStatus.Failed -> ""
this.amount.isZero() -> ""
else -> if (isOutgoing) MINUS else PLUS
}
return prefix + amount.format { crypto(symbol = symbol, decimals = decimals) }
}
}

View file

@ -6,7 +6,7 @@ import androidx.compose.foundation.lazy.*
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScaffoldDefaults import androidx.compose.material3.ScaffoldDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.State import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
@ -14,8 +14,6 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheet import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheet
import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheetConfig import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheetConfig
import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheet import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheet
@ -26,8 +24,6 @@ import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefres
import com.tangem.core.ui.components.marketprice.MarketPriceBlock import com.tangem.core.ui.components.marketprice.MarketPriceBlock
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.components.notifications.Notification import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.components.transactions.txHistoryItems
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.TokenDetailsScreenTestTags import com.tangem.core.ui.test.TokenDetailsScreenTestTags
@ -46,7 +42,6 @@ import com.tangem.features.txhistory.component.TxHistoryComponent
import com.tangem.features.txhistory.entity.TxHistoryUM import com.tangem.features.txhistory.entity.TxHistoryUM
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlin.reflect.KProperty
// TODO: Split to blocks [REDACTED_JIRA] // TODO: Split to blocks [REDACTED_JIRA]
@Suppress("LongMethod") @Suppress("LongMethod")
@ -63,11 +58,6 @@ internal fun TokenDetailsScreen(
contentWindowInsets = ScaffoldDefaults.contentWindowInsets.exclude(WindowInsets.navigationBars), contentWindowInsets = ScaffoldDefaults.contentWindowInsets.exclude(WindowInsets.navigationBars),
containerColor = TangemTheme.colors.background.secondary, containerColor = TangemTheme.colors.background.secondary,
) { scaffoldPaddings -> ) { scaffoldPaddings ->
val txHistoryItems = if (state.txHistoryState is TxHistoryState.Content) {
state.txHistoryState.contentItems.collectAsLazyPagingItems()
} else {
null
}
val listState = rememberLazyListState() val listState = rememberLazyListState()
val txHistoryComponentState by txHistoryComponent.txHistoryState.collectAsStateWithLifecycle() val txHistoryComponentState by txHistoryComponent.txHistoryState.collectAsStateWithLifecycle()
val betweenItemsPadding = TangemTheme.dimens.spacing12 val betweenItemsPadding = TangemTheme.dimens.spacing12
@ -160,14 +150,7 @@ internal fun TokenDetailsScreen(
modifier = itemModifier, modifier = itemModifier,
) )
txHistoryItems( with(txHistoryComponent) { txHistoryContent(listState = listState, state = txHistoryComponentState) }
listState = listState,
txHistoryComponent = txHistoryComponent,
txHistoryComponentState = txHistoryComponentState,
txHistoryState = state.txHistoryState,
txHistoryItems = txHistoryItems,
isBalanceHidden = state.isBalanceHidden,
)
} }
} }
@ -189,28 +172,6 @@ internal fun TokenDetailsScreen(
} }
} }
@Suppress("LongParameterList")
private fun LazyListScope.txHistoryItems(
listState: LazyListState,
txHistoryComponent: TxHistoryComponent,
txHistoryComponentState: TxHistoryUM?,
txHistoryState: TxHistoryState,
txHistoryItems: LazyPagingItems<TxHistoryState.TxHistoryItemState>?,
isBalanceHidden: Boolean,
) {
if (txHistoryComponentState != null) {
with(txHistoryComponent) { txHistoryContent(listState = listState, state = txHistoryComponentState) }
} else {
txHistoryItems(
state = txHistoryState,
isBalanceHidden = isBalanceHidden,
txHistoryItems = txHistoryItems,
)
}
}
private inline operator fun <T> State<T>?.getValue(thisObj: Any?, property: KProperty<*>): T? = this?.value
// region Preview // region Preview
@Preview(showBackground = true, widthDp = 360) @Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) @Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)