Updated on 2026-08-14

This commit is contained in:
Tangem 2025-02-04 15:24:26 +04:00
parent b712e42f44
commit 9315578317
7 changed files with 130 additions and 36 deletions

View file

@ -1,5 +1,6 @@
package com.tangem.common.ui.tokens
import com.tangem.common.ui.R
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.PriceChangeType
@ -17,6 +18,7 @@ import com.tangem.utils.StringsSigns.DASH_SIGN
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.isZero
import com.tangem.utils.extensions.orZero
import kotlinx.collections.immutable.toImmutableList
import java.math.BigDecimal
/**
@ -219,7 +221,14 @@ class TokenItemStateConverter(
-> {
TokenItemState.FiatAmountState.Content(
text = status.getFormattedFiatAmount(appCurrency = appCurrency, includeStaking = true),
hasStaked = !status.getStakedBalance().isZero(),
icons = buildList {
if (!status.getStakedBalance().isZero()) {
TokenItemState.FiatAmountState.Content.IconUM(
iconRes = R.drawable.ic_staking_24,
useAccentColor = true,
).let(::add)
}
}.toImmutableList(),
)
}
is CryptoCurrencyStatus.Unreachable,

View file

@ -23,12 +23,14 @@ import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.components.token.internal.*
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.rememberHapticFeedback
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import kotlinx.collections.immutable.persistentListOf
import org.burnoutcrew.reorderable.ReorderableLazyListState
import java.util.UUID
import kotlin.math.max
@ -462,11 +464,18 @@ private class TokenItemStateProvider : CollectionPreviewParameterProvider<TokenI
text = stringReference(value = "PolygonPolygonPolygonPolygonPolygonPolygon"),
hasPending = true,
),
fiatAmountState = TokenItemState.FiatAmountState.Content(
fiatAmountState = FiatAmountState.Content(
text = "3213123123321312312312312312 $",
hasStaked = true,
icons = persistentListOf(
FiatAmountState.Content.IconUM(R.drawable.ic_error_sync_24, useAccentColor = false),
stakingIcon,
),
isFlickering = true,
),
subtitle2State = TokenItemState.Subtitle2State.TextContent(
text = "5,4123123213123123123123123123 MATIC",
isFlickering = true,
),
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "5,4123123213123123123123123123 MATIC"),
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
price = "312 USD",
priceChangePercent = "42.0%",
@ -511,7 +520,10 @@ private class TokenItemStateProvider : CollectionPreviewParameterProvider<TokenI
id = UUID.randomUUID().toString(),
iconState = tokenIconState,
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Polygon")),
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "321 $", hasStaked = false),
fiatAmountState = FiatAmountState.Content(
text = "321 $",
icons = persistentListOf(stakingIcon),
),
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "5,412 MATIC"),
subtitleState = TokenItemState.SubtitleState.CryptoPriceContent(
price = "312 USD",
@ -525,7 +537,10 @@ private class TokenItemStateProvider : CollectionPreviewParameterProvider<TokenI
id = UUID.randomUUID().toString(),
iconState = tokenIconState,
titleState = TokenItemState.TitleState.Content(text = stringReference(value = "Polygon")),
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "321 $", hasStaked = false),
fiatAmountState = FiatAmountState.Content(
text = "321 $",
icons = persistentListOf(stakingIcon),
),
subtitle2State = TokenItemState.Subtitle2State.LabelContent(
auditLabelUM = AuditLabelUM(
text = TextReference.Str(value = "Trusted"),
@ -565,6 +580,8 @@ private class TokenItemStateProvider : CollectionPreviewParameterProvider<TokenI
companion object {
val stakingIcon = FiatAmountState.Content.IconUM(R.drawable.ic_staking_24, useAccentColor = true)
val coinIconState
get() = CurrencyIconState.CoinIcon(
url = null,
@ -599,7 +616,10 @@ private class TokenItemStateProvider : CollectionPreviewParameterProvider<TokenI
text = stringReference(value = "Polygon"),
hasPending = true,
),
fiatAmountState = TokenItemState.FiatAmountState.Content(text = "321 $", hasStaked = true),
fiatAmountState = FiatAmountState.Content(
text = "321 $",
icons = persistentListOf(stakingIcon),
),
subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "5,412 MATIC"),
subtitleState = TokenItemState.SubtitleState.Unknown,
onItemClick = {},

View file

@ -10,6 +10,7 @@ import androidx.compose.ui.text.style.TextOverflow
import com.tangem.core.ui.R
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.audits.AuditLabel
import com.tangem.core.ui.components.flicker
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.extensions.orMaskWithStars
import com.tangem.core.ui.extensions.stringResourceSafe
@ -25,15 +26,19 @@ internal fun TokenCryptoAmount(
when (state) {
is TokenCryptoAmountState.TextContent -> {
CryptoAmountText(
amount = state.text.orMaskWithStars(isBalanceHidden),
modifier = modifier,
amount = state.text.orMaskWithStars(isBalanceHidden),
isFlickering = state.isFlickering,
)
}
is TokenItemState.Subtitle2State.LabelContent -> {
AuditLabel(state = state.auditLabelUM, modifier = modifier)
}
is TokenCryptoAmountState.Unreachable -> {
CryptoAmountText(amount = stringResourceSafe(id = R.string.common_unreachable), modifier = modifier)
CryptoAmountText(
amount = stringResourceSafe(id = R.string.common_unreachable),
modifier = modifier,
)
}
is TokenCryptoAmountState.Loading -> {
RectangleShimmer(modifier = modifier.placeholderSize(), radius = TangemTheme.dimens.radius4)
@ -46,10 +51,10 @@ internal fun TokenCryptoAmount(
}
@Composable
private fun CryptoAmountText(amount: String, modifier: Modifier = Modifier) {
private fun CryptoAmountText(amount: String, modifier: Modifier = Modifier, isFlickering: Boolean = false) {
Text(
modifier = modifier.flicker(isFlickering),
text = amount,
modifier = modifier,
color = TangemTheme.colors.text.tertiary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,

View file

@ -1,6 +1,7 @@
package com.tangem.core.ui.components.token.internal
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@ -14,9 +15,10 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextOverflow
import com.tangem.core.ui.R
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.components.flicker
import com.tangem.core.ui.extensions.orMaskWithStars
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState as TokenFiatAmountState
@ -26,12 +28,13 @@ internal fun TokenFiatAmount(state: TokenFiatAmountState?, isBalanceHidden: Bool
when (state) {
is TokenFiatAmountState.Content -> {
ContentFiatAmount(
text = state.text.orMaskWithStars(isBalanceHidden),
hasStaked = state.hasStaked,
modifier = modifier,
text = state.text.orMaskWithStars(isBalanceHidden),
icons = state.icons,
isAmountFlickering = state.isFlickering,
)
}
is TokenItemState.FiatAmountState.TextContent -> {
is TokenFiatAmountState.TextContent -> {
FiatAmountText(
text = state.text.orMaskWithStars(isBalanceHidden),
modifier = modifier,
@ -39,7 +42,7 @@ internal fun TokenFiatAmount(state: TokenFiatAmountState?, isBalanceHidden: Bool
)
}
is TokenFiatAmountState.Loading -> {
RectangleShimmer(modifier = modifier.placeholderSize(), radius = TangemTheme.dimens.radius4)
RectangleShimmer(modifier = modifier.placeholderSize(), radius = 4.dp)
}
is TokenFiatAmountState.Locked -> {
LockedRectangle(modifier = modifier.placeholderSize())
@ -49,33 +52,53 @@ internal fun TokenFiatAmount(state: TokenFiatAmountState?, isBalanceHidden: Bool
}
@Composable
private fun ContentFiatAmount(text: String, hasStaked: Boolean, modifier: Modifier = Modifier) {
private fun ContentFiatAmount(
text: String,
icons: List<TokenFiatAmountState.Content.IconUM>,
isAmountFlickering: Boolean,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier,
verticalAlignment = Alignment.CenterVertically,
) {
AnimatedVisibility(
visible = hasStaked,
visible = icons.isNotEmpty(),
) {
Icon(
painter = rememberVectorPainter(image = ImageVector.vectorResource(R.drawable.ic_staking_24)),
contentDescription = null,
tint = TangemTheme.colors.icon.accent,
modifier = Modifier
.padding(horizontal = TangemTheme.dimens.spacing4)
.size(TangemTheme.dimens.size12),
)
Row(
modifier = Modifier.padding(horizontal = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
icons.fastForEach { icon ->
Icon(
modifier = Modifier.size(12.dp),
painter = rememberVectorPainter(image = ImageVector.vectorResource(icon.iconRes)),
tint = if (icon.useAccentColor) {
TangemTheme.colors.icon.accent
} else {
TangemTheme.colors.icon.inactive
},
contentDescription = null,
)
}
}
}
FiatAmountText(text = text)
FiatAmountText(text = text, isFlickering = isAmountFlickering)
}
}
@Composable
private fun FiatAmountText(text: String, modifier: Modifier = Modifier, isAvailable: Boolean = true) {
private fun FiatAmountText(
text: String,
modifier: Modifier = Modifier,
isAvailable: Boolean = true,
isFlickering: Boolean = false,
) {
Text(
modifier = modifier.flicker(isFlickering),
text = text,
modifier = modifier,
color = if (isAvailable) TangemTheme.colors.text.primary1 else TangemTheme.colors.text.tertiary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
@ -85,6 +108,6 @@ private fun FiatAmountText(text: String, modifier: Modifier = Modifier, isAvaila
private fun Modifier.placeholderSize(): Modifier = composed {
return@composed this
.padding(vertical = TangemTheme.dimens.spacing4)
.size(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12)
.padding(vertical = 4.dp)
.size(width = 40.dp, height = 12.dp)
}

View file

@ -5,6 +5,8 @@ import com.tangem.core.ui.components.audits.AuditLabelUM
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.extensions.TextReference
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
/** TokenItem component state */
@Immutable
@ -192,10 +194,18 @@ sealed class TokenItemState {
@Immutable
sealed class FiatAmountState {
data class Content(
val text: String,
val hasStaked: Boolean = false,
) : FiatAmountState()
val isFlickering: Boolean = false,
val icons: ImmutableList<IconUM> = persistentListOf(),
) : FiatAmountState() {
data class IconUM(
val iconRes: Int,
val useAccentColor: Boolean,
)
}
data class TextContent(val text: String, val isAvailable: Boolean = true) : FiatAmountState()
@ -207,7 +217,10 @@ sealed class TokenItemState {
@Immutable
sealed class Subtitle2State {
data class TextContent(val text: String) : Subtitle2State()
data class TextContent(
val text: String,
val isFlickering: Boolean = false,
) : Subtitle2State()
data class LabelContent(val auditLabelUM: AuditLabelUM) : Subtitle2State()

View file

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M12,4C15.64,4,18.67,6.59,19.35,10.04C21.95,10.22,24,12.36,24,15C24,15.66,23.87,16.31,23.62,16.91C23.37,17.52,23,18.07,22.54,18.54C22.07,19,21.52,19.37,20.91,19.62C20.31,19.87,19.66,20,19,20H15.5V18H19C19.8,18,20.56,17.68,21.12,17.12C21.68,16.56,22,15.8,22,15C22,13.45,20.78,12.14,19.22,12.04L17.69,11.93L17.39,10.43C16.88,7.86,14.62,6,12,6C9.94,6,8.08,7.14,7.13,8.97L6.63,9.92L5.56,10.03C3.53,10.24,2,11.95,2,14C2,15.06,2.42,16.08,3.17,16.83C3.92,17.58,4.94,18,6,18H8.5V20H6C4.41,20,2.88,19.37,1.76,18.24C0.63,17.12,0,15.59,0,14C0,10.91,2.34,8.36,5.35,8.04C6.6,5.64,9.11,4,12,4Z" />
<path
android:fillColor="#000"
android:pathData="M11,19V14H13V19Z" />
<path
android:fillColor="#000"
android:pathData="M13,21V23H11V21Z" />
</vector>

View file

@ -16,9 +16,11 @@ import com.tangem.core.ui.haptic.TangemHapticEffect
import com.tangem.core.ui.res.LocalHapticManager
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.markets.impl.R
import com.tangem.features.markets.portfolio.impl.ui.preview.PreviewMyPortfolioUMProvider
import com.tangem.features.markets.portfolio.impl.ui.state.PortfolioTokenUM
import com.tangem.utils.StringsSigns.DASH_SIGN
import kotlinx.collections.immutable.persistentListOf
import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState as TokenFiatAmountState
@Composable
@ -96,7 +98,14 @@ private class PortfolioTokenUMProvider : CollectionPreviewParameterProvider<Port
collection = listOf(
tokenUM.copy(
tokenItemState = (tokenUM.tokenItemState as TokenItemState.Content).copy(
fiatAmountState = contentFiatAmount.copy(hasStaked = true),
fiatAmountState = contentFiatAmount.copy(
icons = persistentListOf(
TokenItemState.FiatAmountState.Content.IconUM(
iconRes = R.drawable.ic_staking_24,
useAccentColor = true,
),
),
),
),
),
tokenUM.copy(