Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-10 13:28:12 +02:00
parent 602e20a1d2
commit c83a46cc1f
17 changed files with 1116 additions and 143 deletions

View file

@ -1,6 +1,8 @@
package com.tangem.features.feed.model.search
import arrow.core.getOrElse
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.charts.state.MarketChartData
import com.tangem.common.ui.charts.state.converter.PriceAndTimePointValuesConverter
import com.tangem.common.ui.charts.state.sorted
@ -19,6 +21,7 @@ import com.tangem.domain.markets.toSerializableParam
import com.tangem.domain.search.usecase.ClearSearchHistoryUseCase
import com.tangem.domain.search.usecase.GetSearchResultsUseCase
import com.tangem.domain.search.usecase.SaveRecentSearchTokenUseCase
import com.tangem.domain.search.model.UserAssetSearchEntry
import com.tangem.domain.search.usecase.SaveSearchQueryUseCase
import com.tangem.features.feed.components.search.DefaultSearchComponent
import com.tangem.features.feed.model.market.list.state.MarketsListUM
@ -58,6 +61,7 @@ internal class SearchModel @Inject constructor(
private val saveRecentSearchTokenUseCase: SaveRecentSearchTokenUseCase,
private val clearSearchHistoryUseCase: ClearSearchHistoryUseCase,
private val getTokenPriceChartUseCase: GetTokenPriceChartUseCase,
private val appRouter: AppRouter,
private val stateController: SearchStateController,
) : Model() {
@ -195,6 +199,15 @@ internal class SearchModel @Inject constructor(
stateController.update(UpdateSearchBarQueryTransformer(""))
}
private fun onSingleUserAssetClick(entry: UserAssetSearchEntry) {
appRouter.push(
AppRoute.CurrencyDetails(
userWalletId = entry.userWalletId,
currency = entry.currencyStatus.currency,
),
)
}
private fun subscribeToQueryChanges() {
stateController.uiState
.map { it.searchBar.query.trim() }
@ -231,6 +244,7 @@ internal class SearchModel @Inject constructor(
val converter = UserAssetSearchItemConverter(
appCurrency = appCurrency,
isBalanceHidden = balanceHidden,
onSingleClick = ::onSingleUserAssetClick,
)
searchResult.userAssets
.map(converter::convert)

View file

@ -1,23 +1,34 @@
package com.tangem.features.feed.model.search.converter
import com.tangem.common.ui.account.toUM
import com.tangem.common.ui.markets.toMarketsListItemPriceAnnotated
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.extensions.networkIconResId
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.percent
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.search.model.UserAssetSearchEntry
import com.tangem.domain.search.model.UserAssetSearchItem
import com.tangem.features.feed.ui.search.state.BalanceDisplayState
import com.tangem.features.feed.ui.search.state.UserAssetItemUM
import com.tangem.utils.StringsSigns
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.orZero
import kotlinx.collections.immutable.toImmutableList
import java.math.BigDecimal
internal class UserAssetSearchItemConverter(
private val appCurrency: AppCurrency,
private val isBalanceHidden: Boolean,
private val onSingleClick: (UserAssetSearchEntry) -> Unit,
) : Converter<UserAssetSearchItem, UserAssetItemUM> {
override fun convert(value: UserAssetSearchItem): UserAssetItemUM {
@ -39,17 +50,56 @@ internal class UserAssetSearchItemConverter(
tokenName = currency.name,
tokenSymbol = currency.symbol,
fiatRate = value.fiatRate?.format { fiat(appCurrency.code, appCurrency.symbol) },
cryptoBalance = formatCryptoAmount(value.amount, currency.symbol, currency.decimals),
fiatBalance = formatFiatAmount(value.fiatAmount),
priceChangeState = when (value) {
is CryptoCurrencyStatus.Loading,
is CryptoCurrencyStatus.Unreachable,
is CryptoCurrencyStatus.MissedDerivation,
is CryptoCurrencyStatus.NoAmount,
-> PriceChangeState.Unknown
else -> PriceChangeState.Content(
type = PriceChangeType.fromBigDecimal(value.priceChange.orZero()),
valueInPercent = value.priceChange.format { percent() },
)
},
balanceState = convertSingleBalanceState(value, currency.symbol, currency.decimals),
isBalanceHidden = isBalanceHidden,
onClick = {},
onClick = { onSingleClick(entry) },
)
}
private fun convertGrouped(item: UserAssetSearchItem.Grouped): UserAssetItemUM.Grouped {
val totalFiat = item.entries.sumOf { it.currencyStatus.value.fiatAmount ?: BigDecimal.ZERO }
val totalCrypto = item.entries.sumOf { it.currencyStatus.value.amount ?: BigDecimal.ZERO }
private fun convertSingleBalanceState(
value: CryptoCurrencyStatus.Value,
symbol: String,
decimals: Int,
): BalanceDisplayState {
return when {
value is CryptoCurrencyStatus.Loading && value.amount != null ->
BalanceDisplayState.Flickering(
cryptoBalance = stringReference(formatCryptoAmount(value.amount, symbol, decimals)),
fiatBalance = value.fiatAmount?.toMarketsListItemPriceAnnotated(
appCurrencyCode = appCurrency.code, appCurrencySymbol = appCurrency.symbol,
) ?: stringReference(StringsSigns.DASH_SIGN),
)
value is CryptoCurrencyStatus.Loading -> BalanceDisplayState.Loading
value is CryptoCurrencyStatus.Unreachable -> BalanceDisplayState.Unreachable
value.isError && value.amount != null ->
BalanceDisplayState.Stale(
cryptoBalance = stringReference(formatCryptoAmount(value.amount, symbol, decimals)),
fiatBalance = value.fiatAmount?.toMarketsListItemPriceAnnotated(
appCurrencyCode = appCurrency.code, appCurrencySymbol = appCurrency.symbol,
) ?: stringReference(StringsSigns.DASH_SIGN),
)
value.isError -> BalanceDisplayState.Unreachable
else -> BalanceDisplayState.Loaded(
cryptoBalance = stringReference(formatCryptoAmount(value.amount, symbol, decimals)),
fiatBalance = value.fiatAmount?.toMarketsListItemPriceAnnotated(
appCurrencyCode = appCurrency.code, appCurrencySymbol = appCurrency.symbol,
) ?: stringReference(StringsSigns.DASH_SIGN),
)
}
}
private fun convertGrouped(item: UserAssetSearchItem.Grouped): UserAssetItemUM.Grouped {
val firstCurrency = item.entries.first().currencyStatus.currency
val children = item.entries.map { entry ->
UserAssetItemUM.GroupedChild(
@ -57,36 +107,78 @@ internal class UserAssetSearchItemConverter(
accountName = entry.accountName.toUM(),
accountIcon = entry.accountIcon.value,
accountColor = entry.accountIcon.color,
cryptoBalance = formatCryptoAmount(
entry.currencyStatus.value.amount,
entry.currencyStatus.currency.symbol,
entry.currencyStatus.currency.decimals,
),
fiatBalance = formatFiatAmount(entry.currencyStatus.value.fiatAmount),
currencyStatus = entry.currencyStatus,
)
}.toImmutableList()
val entryCurrencyStatus = item.entries.first().currencyStatus
return UserAssetItemUM.Grouped(
id = "grouped_${item.tokenName}_${item.tokenSymbol}",
icon = TangemIconUM.Currency(
currencyIconState = CryptoCurrencyToIconStateConverter().convert(item.entries.first().currencyStatus),
currencyIconState = CurrencyIconState.CoinIcon(
url = entryCurrencyStatus.currency.iconUrl,
fallbackResId = entryCurrencyStatus.currency.networkIconResId,
isGrayscale = entryCurrencyStatus.currency.network.isTestnet || entryCurrencyStatus.value.isError,
shouldShowCustomBadge = entryCurrencyStatus.currency.isCustom,
),
),
tokenName = item.tokenName,
tokenSymbol = item.tokenSymbol,
tokensCount = item.entries.size,
totalCryptoBalance = formatCryptoAmount(totalCrypto, firstCurrency.symbol, firstCurrency.decimals),
totalFiatBalance = formatFiatAmount(totalFiat),
balanceState = convertGroupedBalanceState(item.entries, firstCurrency.symbol, firstCurrency.decimals),
isBalanceHidden = isBalanceHidden,
children = children,
onClick = {},
)
}
private fun convertGroupedBalanceState(
entries: List<UserAssetSearchEntry>,
symbol: String,
decimals: Int,
): BalanceDisplayState {
val hasAnyLoading = entries.any { it.currencyStatus.value is CryptoCurrencyStatus.Loading }
val hasAnyError = entries.any { it.currencyStatus.value.isError }
val hasAnyAmount = entries.any { it.currencyStatus.value.amount != null }
val balance = when {
hasAnyLoading && !hasAnyAmount -> BalanceDisplayState.Loading
hasAnyLoading && hasAnyAmount -> computeGroupBalanceFlickering(entries, symbol, decimals)
hasAnyError && entries.size == 1 && !hasAnyAmount -> BalanceDisplayState.Unreachable
hasAnyError && entries.size > 1 -> computeGroupBalance(entries, symbol, decimals)
else -> computeGroupBalance(entries, symbol, decimals)
}
return balance
}
private fun computeGroupBalance(
entries: List<UserAssetSearchEntry>,
symbol: String,
decimals: Int,
): BalanceDisplayState.Loaded {
val totalFiat = entries.sumOf { it.currencyStatus.value.fiatAmount ?: BigDecimal.ZERO }
val totalCrypto = entries.sumOf { it.currencyStatus.value.amount ?: BigDecimal.ZERO }
return BalanceDisplayState.Loaded(
cryptoBalance = stringReference(formatCryptoAmount(totalCrypto, symbol, decimals)),
fiatBalance = totalFiat.toMarketsListItemPriceAnnotated(appCurrency.code, appCurrency.symbol),
)
}
private fun computeGroupBalanceFlickering(
entries: List<UserAssetSearchEntry>,
symbol: String,
decimals: Int,
): BalanceDisplayState.Flickering {
val totalFiat = entries.sumOf { it.currencyStatus.value.fiatAmount ?: BigDecimal.ZERO }
val totalCrypto = entries.sumOf { it.currencyStatus.value.amount ?: BigDecimal.ZERO }
return BalanceDisplayState.Flickering(
cryptoBalance = stringReference(formatCryptoAmount(totalCrypto, symbol, decimals)),
fiatBalance = totalFiat.toMarketsListItemPriceAnnotated(appCurrency.code, appCurrency.symbol),
)
}
private fun formatCryptoAmount(amount: BigDecimal?, symbol: String, decimals: Int): String {
return amount?.format { crypto(symbol, decimals) } ?: StringsSigns.DASH_SIGN
}
private fun formatFiatAmount(fiatAmount: BigDecimal?): String {
return fiatAmount?.format { fiat(appCurrency.code, appCurrency.symbol) } ?: StringsSigns.DASH_SIGN
}
}

View file

@ -0,0 +1,99 @@
package com.tangem.features.feed.ui.components
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.ds.image.TangemIcon
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
private const val MIN_STACKED_ICON_COUNT = 1
private const val MAX_STACKED_ICON_COUNT = 3
private const val FIRST_BACK_LAYER_HORIZONTAL_OFFSET_MULTIPLIER = 1
private const val SECOND_BACK_LAYER_HORIZONTAL_OFFSET_MULTIPLIER = 2
private const val MIN_STACKED_COUNT_FOR_FIRST_BACK_LAYER = 2
private const val FIRST_BACK_LAYER_ICON_ALPHA = 0.4f
private const val SECOND_BACK_LAYER_ICON_ALPHA = 0.2f
@Composable
fun LayeringIcons(
tangemIconUM: TangemIconUM,
modifier: Modifier = Modifier,
count: Int = MIN_STACKED_ICON_COUNT,
layerHorizontalShift: Dp = TangemTheme.dimens2.x1,
iconSize: Dp = TangemTheme.dimens2.x10,
) {
require(count > 0)
val stackTrailingWidth = layerHorizontalShift * SECOND_BACK_LAYER_HORIZONTAL_OFFSET_MULTIPLIER
Box(
modifier = modifier.size(
width = iconSize + stackTrailingWidth,
height = iconSize,
),
) {
val baseIconModifier = Modifier
.align(Alignment.TopStart)
.size(iconSize)
if (count >= MAX_STACKED_ICON_COUNT) {
TangemIcon(
tangemIconUM = tangemIconUM,
modifier = baseIconModifier
.offset(x = layerHorizontalShift * SECOND_BACK_LAYER_HORIZONTAL_OFFSET_MULTIPLIER)
.alpha(SECOND_BACK_LAYER_ICON_ALPHA),
)
}
if (count >= MIN_STACKED_COUNT_FOR_FIRST_BACK_LAYER) {
TangemIcon(
tangemIconUM = tangemIconUM,
modifier = baseIconModifier
.offset(x = layerHorizontalShift * FIRST_BACK_LAYER_HORIZONTAL_OFFSET_MULTIPLIER)
.alpha(FIRST_BACK_LAYER_ICON_ALPHA),
)
}
TangemIcon(
tangemIconUM = tangemIconUM,
modifier = baseIconModifier,
)
}
}
@Preview(showBackground = true, widthDp = 360)
@Composable
private fun LayeringIconsPreview() {
val previewCurrencyIcon = TangemIconUM.Currency(
currencyIconState = CurrencyIconState.TokenIcon(
url = null,
topBadgeIconResId = null,
fallbackTint = TangemColorPalette.Black,
fallbackBackground = TangemColorPalette.Meadow,
isGrayscale = false,
shouldShowCustomBadge = false,
),
)
TangemThemePreview {
Column(horizontalAlignment = Alignment.End) {
LayeringIcons(count = MIN_STACKED_ICON_COUNT, tangemIconUM = previewCurrencyIcon)
LayeringIcons(
count = MAX_STACKED_ICON_COUNT,
tangemIconUM = previewCurrencyIcon,
)
LayeringIcons(
count = MIN_STACKED_COUNT_FOR_FIRST_BACK_LAYER,
tangemIconUM = previewCurrencyIcon,
)
}
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.features.feed.ui.search
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@ -8,9 +9,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
@ -27,16 +27,20 @@ import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerW
import com.tangem.core.ui.components.list.InfiniteListHandler
import com.tangem.core.ui.ds.button.*
import com.tangem.core.ui.ds.image.TangemIcon
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.extensions.clickableSingle
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.LocalMainBottomSheetColor
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.feed.ui.search.components.GroupedUserAssetItem
import com.tangem.features.feed.ui.search.components.SingleUserAssetItem
import com.tangem.features.feed.ui.search.state.*
import kotlinx.collections.immutable.ImmutableList
private const val PLACEHOLDER_COUNT = 10
private const val LOAD_MORE_THRESHOLD = 5
private const val USER_ASSETS_LIMIT = 3
@Composable
internal fun SearchContent(
@ -58,6 +62,14 @@ internal fun SearchContent(
lazyListState.scrollToItem(0)
}
var isUserAssetsExpanded by rememberSaveable { mutableStateOf(false) }
val shouldShowUserAssetsPortfolio = content is SearchContentUM.Results && content.userAssets.isNotEmpty()
LaunchedEffect(shouldShowUserAssetsPortfolio) {
if (!shouldShowUserAssetsPortfolio) {
isUserAssetsExpanded = false
}
}
LazyColumn(
state = lazyListState,
modifier = modifier
@ -80,6 +92,8 @@ internal fun SearchContent(
)
is SearchContentUM.Results -> searchResultsItems(
results = content,
isUserAssetsExpanded = isUserAssetsExpanded,
onUserAssetsExpandedChange = { isUserAssetsExpanded = it },
onResultMarketTokenClick = searchCallbacks.onResultMarketTokenClick,
)
}
@ -147,18 +161,19 @@ private fun LazyListScope.searchHistoryItems(
private fun LazyListScope.searchResultsItems(
results: SearchContentUM.Results,
isUserAssetsExpanded: Boolean,
onUserAssetsExpandedChange: (Boolean) -> Unit,
onResultMarketTokenClick: (MarketsListItemUM) -> Unit,
) {
if (results.userAssets.isNotEmpty()) {
item(key = "header_portfolio") {
SectionHeader(title = stringResourceSafe(R.string.markets_search_portfolio_header))
}
items(
items = results.userAssets,
key = { it.id },
) { asset ->
UserAssetItem(asset)
}
userAssetsPortfolioItems(
assets = results.userAssets,
expanded = isUserAssetsExpanded,
onExpandedChange = onUserAssetsExpandedChange,
)
}
when (val market = results.marketTokens) {
@ -175,6 +190,42 @@ private fun LazyListScope.searchResultsItems(
}
}
private fun LazyListScope.userAssetsPortfolioItems(
assets: ImmutableList<UserAssetItemUM>,
expanded: Boolean,
onExpandedChange: (Boolean) -> Unit,
) {
val shouldShowToggle = assets.size > USER_ASSETS_LIMIT
val visibleCount = if (shouldShowToggle && !expanded) USER_ASSETS_LIMIT else assets.size
items(
count = visibleCount,
key = { index -> "user_asset_${assets[index].id}" },
) { index ->
Column(
modifier = Modifier.animateItem(
fadeInSpec = tween(durationMillis = 300),
fadeOutSpec = tween(durationMillis = 250),
),
) {
UserAssetItem(assets[index])
SpacerH(TangemTheme.dimens2.x2)
}
}
if (shouldShowToggle) {
item(key = "user_assets_show_toggle") {
ShowAllUserAssetsButton(
modifier = Modifier.animateItem(
fadeInSpec = tween(durationMillis = 300),
fadeOutSpec = tween(durationMillis = 250),
),
isExpanded = expanded,
onClick = { onExpandedChange(!expanded) },
)
}
}
}
private fun LazyListScope.marketSearchResultItems(
market: MarketSearchResultUM.Content,
hasUserAssetsSection: Boolean,
@ -234,7 +285,7 @@ private fun LazyListScope.marketSearchResultNotFoundItem() {
) {
Text(
text = stringResourceSafe(R.string.common_no_results),
style = TangemTheme.typography2.bodyRegular14,
style = TangemTheme.typography2.subheadlineMedium14,
color = TangemTheme.colors2.text.neutral.tertiary,
)
}
@ -278,106 +329,34 @@ private fun TextHintItem(hint: TextHintItemUM, onHintClick: () -> Unit) {
@Composable
private fun UserAssetItem(asset: UserAssetItemUM) {
when (asset) {
is UserAssetItemUM.Single -> SingleUserAssetItem(asset)
is UserAssetItemUM.Grouped -> GroupedUserAssetItem(asset)
is UserAssetItemUM.Single -> SingleUserAssetItem(item = asset)
is UserAssetItemUM.Grouped -> GroupedUserAssetItem(item = asset)
}
}
@Composable
private fun SingleUserAssetItem(asset: UserAssetItemUM.Single) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = asset.onClick)
.padding(horizontal = 12.dp, vertical = 14.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
private fun ShowAllUserAssetsButton(isExpanded: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
Box(
modifier = modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
TangemIcon(
modifier = Modifier.size(40.dp),
tangemIconUM = asset.icon,
TangemButton(
buttonUM = TangemButtonUM(
text = if (isExpanded) {
resourceReference(R.string.feed_search_show_less_user_assets)
} else {
resourceReference(R.string.feed_search_show_all_user_assets)
},
tangemIconUM = TangemIconUM.Icon(
iconRes = if (isExpanded) R.drawable.ic_chewron_up_20 else R.drawable.ic_chewron_down_20,
),
iconPosition = TangemButtonIconPosition.End,
onClick = onClick,
type = TangemButtonType.Secondary,
size = TangemButtonSize.X7,
shape = TangemButtonShape.Rounded,
),
)
Column(modifier = Modifier.weight(1f)) {
Text(
text = asset.tokenName,
style = TangemTheme.typography2.bodySemibold16,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
)
Text(
text = asset.tokenSymbol,
style = TangemTheme.typography2.captionRegular13,
color = TangemTheme.colors2.text.neutral.tertiary,
maxLines = 1,
)
}
if (!asset.isBalanceHidden) {
Column(horizontalAlignment = Alignment.End) {
Text(
text = asset.fiatBalance,
style = TangemTheme.typography2.bodySemibold16,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
)
Text(
text = asset.cryptoBalance,
style = TangemTheme.typography2.captionRegular13,
color = TangemTheme.colors2.text.neutral.tertiary,
maxLines = 1,
)
}
}
}
}
// TODO [REDACTED_JIRA] update ui item to Portfolio block item
@Composable
private fun GroupedUserAssetItem(asset: UserAssetItemUM.Grouped) {
Column(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = asset.onClick)
.padding(horizontal = 12.dp, vertical = 14.dp),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
TangemIcon(
modifier = Modifier.size(40.dp),
tangemIconUM = asset.icon,
)
Column(modifier = Modifier.weight(1f)) {
Text(
text = asset.tokenName,
style = TangemTheme.typography2.bodySemibold16,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
)
Text(
text = "${asset.tokenSymbol} · ${asset.tokensCount}",
style = TangemTheme.typography2.captionRegular13,
color = TangemTheme.colors2.text.neutral.tertiary,
maxLines = 1,
)
}
if (!asset.isBalanceHidden) {
Column(horizontalAlignment = Alignment.End) {
Text(
text = asset.totalFiatBalance,
style = TangemTheme.typography2.bodySemibold16,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
)
Text(
text = asset.totalCryptoBalance,
style = TangemTheme.typography2.captionRegular13,
color = TangemTheme.colors2.text.neutral.tertiary,
maxLines = 1,
)
}
}
}
}
}
@ -397,7 +376,7 @@ private fun ShowTokensUnder100kItem(onShowTokensClick: () -> Unit, modifier: Mod
) {
Text(
text = stringResourceSafe(R.string.markets_search_see_tokens_under_100k),
style = TangemTheme.typography2.bodyRegular14,
style = TangemTheme.typography2.subheadlineMedium14,
color = TangemTheme.colors2.text.neutral.secondary,
)
TangemButton(

View file

@ -0,0 +1,175 @@
package com.tangem.features.feed.ui.search.components
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.SpacerW
import com.tangem.core.ui.components.flicker
import com.tangem.core.ui.extensions.resolveAnnotatedReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.feed.ui.search.state.BalanceDisplayState
import com.tangem.utils.StringsSigns
@Composable
internal fun BalanceColumn(
balanceState: BalanceDisplayState,
isBalanceHidden: Boolean,
modifier: Modifier = Modifier,
) {
if (isBalanceHidden) {
HiddenBalance(modifier)
return
}
when (balanceState) {
is BalanceDisplayState.Loading -> LoadingBalanceColumn(modifier)
is BalanceDisplayState.Flickering -> FlickeringBalanceColumn(balanceState, modifier)
is BalanceDisplayState.Stale -> StaleBalanceColumn(balanceState, modifier)
is BalanceDisplayState.Unreachable -> UnreachableBalanceColumn(modifier)
is BalanceDisplayState.Loaded -> LoadedBalanceColumn(balanceState, modifier)
}
}
@Composable
private fun BalanceColumnLayout(modifier: Modifier = Modifier, content: @Composable ColumnScope.() -> Unit) {
Column(
modifier = modifier,
horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.spacedBy(4.dp),
content = content,
)
}
@Composable
private fun LoadingBalanceColumn(modifier: Modifier = Modifier) {
BalanceColumnLayout(modifier) {
RectangleShimmer(modifier = Modifier.size(width = 108.dp, height = 20.dp), radius = 20.dp)
RectangleShimmer(modifier = Modifier.size(width = 64.dp, height = 16.dp), radius = 20.dp)
}
}
@Composable
private fun FlickeringBalanceColumn(state: BalanceDisplayState.Flickering, modifier: Modifier = Modifier) {
val flickerModifier = Modifier.flicker(isFlickering = true)
BalanceColumnLayout(modifier) {
Text(
modifier = flickerModifier,
text = state.fiatBalance.resolveAnnotatedReference(),
style = TangemTheme.typography2.bodyMedium16,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
modifier = flickerModifier,
text = state.cryptoBalance.resolveReference(),
style = TangemTheme.typography2.captionMedium12,
color = TangemTheme.colors2.text.neutral.secondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
@Composable
private fun StaleBalanceColumn(state: BalanceDisplayState.Stale, modifier: Modifier = Modifier) {
BalanceColumnLayout(modifier) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
modifier = Modifier.size(16.dp),
painter = painterResource(id = R.drawable.ic_error_sync_24),
contentDescription = null,
tint = TangemTheme.colors2.markers.iconGray,
)
SpacerW(TangemTheme.dimens2.x0_5)
Text(
text = state.fiatBalance.resolveAnnotatedReference(),
style = TangemTheme.typography2.bodyMedium16,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
Text(
text = state.cryptoBalance.resolveReference(),
style = TangemTheme.typography2.captionMedium12,
color = TangemTheme.colors2.text.neutral.secondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
@Composable
private fun UnreachableBalanceColumn(modifier: Modifier = Modifier) {
BalanceColumnLayout(modifier) {
Text(
text = StringsSigns.DASH_SIGN,
style = TangemTheme.typography2.bodyMedium16,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
text = stringResourceSafe(R.string.common_unreachable),
style = TangemTheme.typography2.captionMedium12,
color = TangemTheme.colors2.text.status.attention,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
SpacerW(TangemTheme.dimens2.x0_5)
Icon(
modifier = Modifier.size(16.dp),
painter = painterResource(id = R.drawable.ic_alert_triange_24),
contentDescription = null,
tint = TangemTheme.colors2.graphic.status.attention,
)
}
}
}
@Composable
private fun LoadedBalanceColumn(state: BalanceDisplayState.Loaded, modifier: Modifier = Modifier) {
BalanceColumnLayout(modifier) {
Text(
text = state.fiatBalance.resolveAnnotatedReference(),
style = TangemTheme.typography2.bodySemibold16,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
)
Text(
text = state.cryptoBalance.resolveReference(),
style = TangemTheme.typography2.captionMedium12,
color = TangemTheme.colors2.text.neutral.secondary,
maxLines = 1,
)
}
}
@Composable
private fun HiddenBalance(modifier: Modifier = Modifier) {
BalanceColumnLayout(modifier) {
Text(
text = StringsSigns.THREE_STARS,
style = TangemTheme.typography2.bodySemibold16,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
)
Text(
text = StringsSigns.THREE_STARS,
style = TangemTheme.typography2.captionMedium12,
color = TangemTheme.colors2.text.neutral.secondary,
maxLines = 1,
)
}
}

View file

@ -0,0 +1,84 @@
package com.tangem.features.feed.ui.search.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.text.style.TextOverflow
import com.tangem.core.ui.ds.button.*
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.row.TangemRowContainer
import com.tangem.core.ui.ds.row.TangemRowLayoutId
import com.tangem.core.ui.extensions.pluralStringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.feed.impl.R
import com.tangem.features.feed.ui.components.LayeringIcons
import com.tangem.features.feed.ui.search.state.UserAssetItemUM
@Composable
internal fun GroupedUserAssetItem(item: UserAssetItemUM.Grouped, modifier: Modifier = Modifier) {
Box(
modifier = modifier.background(
color = TangemTheme.colors2.surface.level3,
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
),
) {
TangemRowContainer(
modifier = Modifier.clickable(onClick = item.onClick),
content = {
LayeringIcons(
modifier = Modifier
.layoutId(layoutId = TangemRowLayoutId.HEAD)
.padding(end = TangemTheme.dimens2.x1),
tangemIconUM = item.icon,
count = item.tokensCount,
)
Text(
modifier = Modifier.layoutId(TangemRowLayoutId.START_TOP),
text = item.tokenName,
style = TangemTheme.typography2.bodyMedium16,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.MiddleEllipsis,
)
Text(
modifier = Modifier.layoutId(TangemRowLayoutId.START_BOTTOM),
text = pluralStringResourceSafe(R.plurals.common_tokens_count, item.tokensCount, item.tokensCount),
style = TangemTheme.typography2.captionMedium12,
color = TangemTheme.colors2.text.neutral.secondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
BalanceColumn(
modifier = Modifier.layoutId(layoutId = TangemRowLayoutId.END_TOP),
balanceState = item.balanceState,
isBalanceHidden = item.isBalanceHidden,
)
TangemButton(
modifier = Modifier
.padding(start = TangemTheme.dimens2.x2)
.layoutId(TangemRowLayoutId.TAIL),
buttonUM = TangemButtonUM(
type = TangemButtonType.Secondary,
tangemIconUM = TangemIconUM.Icon(
iconRes = R.drawable.ic_chevron_24,
tintReference = { TangemTheme.colors2.graphic.neutral.primary },
),
shape = TangemButtonShape.Rounded,
size = TangemButtonSize.X10,
onClick = item.onClick,
),
)
},
)
}
}

View file

@ -0,0 +1,117 @@
package com.tangem.features.feed.ui.search.components
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.marketprice.PriceChangeInPercent
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.ds.image.TangemIcon
import com.tangem.core.ui.ds.row.TangemRowContainer
import com.tangem.core.ui.ds.row.TangemRowLayoutId
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.feed.ui.search.state.BalanceDisplayState
import com.tangem.features.feed.ui.search.state.UserAssetItemUM
@Composable
fun SingleUserAssetItem(item: UserAssetItemUM.Single, modifier: Modifier = Modifier) {
Box(
modifier = modifier.background(
color = TangemTheme.colors2.surface.level3,
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
),
) {
TangemRowContainer(
modifier = Modifier.clickable(onClick = item.onClick),
content = {
TangemIcon(
modifier = Modifier
.layoutId(layoutId = TangemRowLayoutId.HEAD)
.size(40.dp)
.padding(end = TangemTheme.dimens2.x1),
tangemIconUM = item.icon,
)
Text(
modifier = Modifier.layoutId(TangemRowLayoutId.START_TOP),
text = item.tokenName,
style = TangemTheme.typography2.bodyMedium16,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.MiddleEllipsis,
)
PriceBlock(
modifier = Modifier.layoutId(TangemRowLayoutId.START_BOTTOM),
priceChangeState = item.priceChangeState,
fiatRate = item.fiatRate,
balanceState = item.balanceState,
)
BalanceColumn(
modifier = Modifier.layoutId(layoutId = TangemRowLayoutId.END_TOP),
balanceState = item.balanceState,
isBalanceHidden = item.isBalanceHidden,
)
},
)
}
}
@Composable
private fun PriceBlock(
priceChangeState: PriceChangeState,
fiatRate: String?,
balanceState: BalanceDisplayState,
modifier: Modifier = Modifier,
) {
val isDisabled = remember(balanceState) {
balanceState is BalanceDisplayState.Unreachable
}
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1),
) {
if (fiatRate != null) {
Text(
text = fiatRate,
style = TangemTheme.typography2.captionMedium12,
color = if (isDisabled) {
TangemTheme.colors2.text.status.disabled
} else {
TangemTheme.colors2.text.neutral.secondary
},
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
AnimatedContent(
targetState = priceChangeState,
contentKey = { it::class },
) { animatedState ->
when (animatedState) {
is PriceChangeState.Content -> {
PriceChangeInPercent(
valueInPercent = animatedState.valueInPercent,
type = animatedState.type,
textStyle = TangemTheme.typography2.captionMedium12,
isDisabled = isDisabled,
)
}
PriceChangeState.Unknown -> Unit
}
}
}
}

View file

@ -12,6 +12,7 @@ import androidx.compose.ui.unit.dp
import com.tangem.common.ui.charts.state.MarketChartRawData
import com.tangem.common.ui.markets.models.MarketsListItemUM
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.extensions.stringReference
@ -217,8 +218,14 @@ internal object SearchContentPreviewFixtures {
tokenName = name,
tokenSymbol = symbol,
fiatRate = "$98,765.43",
cryptoBalance = "1.234 $symbol",
fiatBalance = "$121,876.50",
priceChangeState = PriceChangeState.Content(
type = PriceChangeType.UP,
valueInPercent = "+2.34%",
),
balanceState = BalanceDisplayState.Loaded(
cryptoBalance = stringReference("1.234 $symbol"),
fiatBalance = stringReference("$121,876.50"),
),
isBalanceHidden = false,
onClick = {},
)

View file

@ -0,0 +1,238 @@
package com.tangem.features.feed.ui.search.preview
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.features.feed.ui.search.components.GroupedUserAssetItem
import com.tangem.features.feed.ui.search.components.SingleUserAssetItem
import com.tangem.features.feed.ui.search.state.BalanceDisplayState
import com.tangem.features.feed.ui.search.state.UserAssetItemUM
import kotlinx.collections.immutable.persistentListOf
/** Labeled UI state for [SingleUserAssetItem] previews (dropdown label in Studio). */
internal data class SingleUserAssetItemPreviewScenario(
val title: String,
val item: UserAssetItemUM.Single,
)
/** Labeled UI state for [GroupedUserAssetItem] previews. */
internal data class GroupedUserAssetItemPreviewScenario(
val title: String,
val item: UserAssetItemUM.Grouped,
)
@Suppress("StringLiteralDuplication")
internal object UserAssetItemPreviewFixtures {
private val sampleIcon = TangemIconUM.Currency(
CurrencyIconState.CoinIcon(
url = null,
fallbackResId = com.tangem.core.ui.R.drawable.ic_ethereumpow_22,
isGrayscale = false,
shouldShowCustomBadge = false,
),
)
private val cryptoRef = stringReference("1.234 ETH")
private val fiatRef = stringReference("$121,876.50")
private val samplePriceChange = PriceChangeState.Content(
valueInPercent = "+2.34%",
type = PriceChangeType.UP,
)
private fun balanceLoaded() = BalanceDisplayState.Loaded(
cryptoBalance = cryptoRef,
fiatBalance = fiatRef,
)
private fun balanceFlickering() = BalanceDisplayState.Flickering(
cryptoBalance = cryptoRef,
fiatBalance = fiatRef,
)
private fun balanceStale() = BalanceDisplayState.Stale(
cryptoBalance = cryptoRef,
fiatBalance = fiatRef,
)
private fun balanceLoading() = BalanceDisplayState.Loading
private fun balanceUnreachable() = BalanceDisplayState.Unreachable
fun allSingleScenarios(): List<SingleUserAssetItemPreviewScenario> = listOf(
SingleUserAssetItemPreviewScenario(
title = "Hidden (balanceState ignored)",
item = single(
balanceState = balanceLoaded(),
isBalanceHidden = true,
),
),
SingleUserAssetItemPreviewScenario(
title = "Balance Loaded",
item = single(balanceState = balanceLoaded(), isBalanceHidden = false),
),
SingleUserAssetItemPreviewScenario(
title = "Balance Flickering",
item = single(balanceState = balanceFlickering(), isBalanceHidden = false),
),
SingleUserAssetItemPreviewScenario(
title = "Balance Stale",
item = single(balanceState = balanceStale(), isBalanceHidden = false),
),
SingleUserAssetItemPreviewScenario(
title = "Balance Loading",
item = single(balanceState = balanceLoading(), isBalanceHidden = false),
),
SingleUserAssetItemPreviewScenario(
title = "Balance Unreachable",
item = single(balanceState = balanceUnreachable(), isBalanceHidden = false),
),
)
fun allGroupedScenarios(): List<GroupedUserAssetItemPreviewScenario> = listOf(
GroupedUserAssetItemPreviewScenario(
title = "Hidden (balanceState ignored)",
item = grouped(
balanceState = balanceLoaded(),
isBalanceHidden = true,
),
),
GroupedUserAssetItemPreviewScenario(
title = "Balance Loaded",
item = grouped(balanceState = balanceLoaded(), isBalanceHidden = false),
),
GroupedUserAssetItemPreviewScenario(
title = "Balance Flickering",
item = grouped(balanceState = balanceFlickering(), isBalanceHidden = false),
),
GroupedUserAssetItemPreviewScenario(
title = "Balance Stale",
item = grouped(balanceState = balanceStale(), isBalanceHidden = false),
),
GroupedUserAssetItemPreviewScenario(
title = "Balance Loading",
item = grouped(balanceState = balanceLoading(), isBalanceHidden = false),
),
GroupedUserAssetItemPreviewScenario(
title = "Balance Unreachable",
item = grouped(balanceState = balanceUnreachable(), isBalanceHidden = false),
),
)
private fun single(balanceState: BalanceDisplayState, isBalanceHidden: Boolean): UserAssetItemUM.Single =
UserAssetItemUM.Single(
id = "single_preview",
icon = sampleIcon,
tokenName = "Ethereum",
tokenSymbol = "ETH",
fiatRate = "$98,765.43",
priceChangeState = samplePriceChange,
balanceState = balanceState,
isBalanceHidden = isBalanceHidden,
onClick = {},
)
private fun grouped(balanceState: BalanceDisplayState, isBalanceHidden: Boolean): UserAssetItemUM.Grouped =
UserAssetItemUM.Grouped(
id = "grouped_preview",
icon = sampleIcon,
tokenName = "Ethereum",
tokenSymbol = "ETH",
tokensCount = 3,
balanceState = balanceState,
isBalanceHidden = isBalanceHidden,
children = persistentListOf(),
onClick = {},
)
}
internal class SingleUserAssetItemPreviewParameterProvider :
PreviewParameterProvider<SingleUserAssetItemPreviewScenario> {
override val values: Sequence<SingleUserAssetItemPreviewScenario>
get() = UserAssetItemPreviewFixtures.allSingleScenarios().asSequence()
}
internal class GroupedUserAssetItemPreviewParameterProvider :
PreviewParameterProvider<GroupedUserAssetItemPreviewScenario> {
override val values: Sequence<GroupedUserAssetItemPreviewScenario>
get() = UserAssetItemPreviewFixtures.allGroupedScenarios().asSequence()
}
@Composable
private fun SingleUserAssetItemPreviewHost(
scenario: SingleUserAssetItemPreviewScenario,
modifier: Modifier = Modifier,
) {
Box(
modifier = modifier
.fillMaxWidth()
.background(TangemTheme.colors2.surface.level1)
.padding(8.dp),
) {
SingleUserAssetItem(item = scenario.item)
}
}
@Composable
private fun GroupedUserAssetItemPreviewHost(
scenario: GroupedUserAssetItemPreviewScenario,
modifier: Modifier = Modifier,
) {
Box(
modifier = modifier
.fillMaxWidth()
.background(TangemTheme.colors2.surface.level1)
.padding(8.dp),
) {
GroupedUserAssetItem(item = scenario.item)
}
}
@Composable
@Preview(name = "Single all balance states (parameter)", showBackground = true, widthDp = 360)
@Preview(
name = "Single all balance states (night)",
showBackground = true,
widthDp = 360,
uiMode = Configuration.UI_MODE_NIGHT_YES,
)
private fun SingleUserAssetItemPreview_AllBalanceStates(
@PreviewParameter(SingleUserAssetItemPreviewParameterProvider::class) scenario: SingleUserAssetItemPreviewScenario,
) {
TangemThemePreviewRedesign {
SingleUserAssetItemPreviewHost(scenario = scenario)
}
}
@Composable
@Preview(name = "Grouped all balance states (parameter)", showBackground = true, widthDp = 360)
@Preview(
name = "Grouped all balance states (night)",
showBackground = true,
widthDp = 360,
uiMode = Configuration.UI_MODE_NIGHT_YES,
)
private fun GroupedUserAssetItemPreview_AllBalanceStates(
@PreviewParameter(GroupedUserAssetItemPreviewParameterProvider::class)
scenario: GroupedUserAssetItemPreviewScenario,
) {
TangemThemePreviewRedesign {
GroupedUserAssetItemPreviewHost(scenario = scenario)
}
}

View file

@ -4,8 +4,11 @@ import androidx.compose.runtime.Immutable
import com.tangem.common.ui.account.AccountNameUM
import com.tangem.common.ui.markets.models.MarketsListItemUM
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.models.account.CryptoPortfolioIcon
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import kotlinx.collections.immutable.ImmutableList
data class SearchUM(
@ -45,6 +48,28 @@ sealed interface MarketSearchResultUM {
data class TextHintItemUM(val text: String)
@Immutable
sealed interface BalanceDisplayState {
data class Loaded(
val cryptoBalance: TextReference,
val fiatBalance: TextReference,
) : BalanceDisplayState
data class Flickering(
val cryptoBalance: TextReference,
val fiatBalance: TextReference,
) : BalanceDisplayState
data class Stale(
val cryptoBalance: TextReference,
val fiatBalance: TextReference,
) : BalanceDisplayState
data object Loading : BalanceDisplayState
data object Unreachable : BalanceDisplayState
}
@Immutable
sealed interface UserAssetItemUM {
val id: String
@ -59,8 +84,8 @@ sealed interface UserAssetItemUM {
override val tokenName: String,
override val tokenSymbol: String,
val fiatRate: String?,
val cryptoBalance: String,
val fiatBalance: String,
val priceChangeState: PriceChangeState,
val balanceState: BalanceDisplayState,
val isBalanceHidden: Boolean,
override val onClick: () -> Unit,
) : UserAssetItemUM
@ -71,8 +96,7 @@ sealed interface UserAssetItemUM {
override val tokenName: String,
override val tokenSymbol: String,
val tokensCount: Int,
val totalCryptoBalance: String,
val totalFiatBalance: String,
val balanceState: BalanceDisplayState,
val isBalanceHidden: Boolean,
val children: ImmutableList<GroupedChild>,
override val onClick: () -> Unit,
@ -83,7 +107,6 @@ sealed interface UserAssetItemUM {
val accountName: AccountNameUM,
val accountIcon: CryptoPortfolioIcon.Icon,
val accountColor: CryptoPortfolioIcon.Color,
val cryptoBalance: String,
val fiatBalance: String,
val currencyStatus: CryptoCurrencyStatus,
)
}

View file

@ -27,11 +27,7 @@ internal fun contentFeedEntryStackAnimation(): StackAnimation<
ComposableModularBottomSheetContentComponent,
> =
stackAnimation { to, from, _ ->
val isSearchToTokenList =
(to.configuration as? FeedEntryChildFactory.Child.TokenList)?.params?.shouldAlwaysShowSearchBar == true
val isFromSearchTokenList =
(from.configuration as? FeedEntryChildFactory.Child.TokenList)?.params?.shouldAlwaysShowSearchBar == true
if (isSearchToTokenList || isFromSearchTokenList) {
if (to.configuration.usesFadeStackTransition() || from.configuration.usesFadeStackTransition()) {
fade()
} else {
slide()