Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-26 11:03:16 +01:00
parent 88f7205293
commit f9f2ac60d0
8 changed files with 221 additions and 116 deletions

View file

@ -104,6 +104,8 @@ data class TokenMarketInfoResponse(
data class Metrics( data class Metrics(
@Json(name = "market_rating") @Json(name = "market_rating")
val marketRating: Int?, val marketRating: Int?,
@Json(name = "market_rating_change_24h")
val marketRatingChange24h: Int?,
@Json(name = "circulating_supply") @Json(name = "circulating_supply")
val circulatingSupply: BigDecimal?, val circulatingSupply: BigDecimal?,
@Json(name = "market_cap") @Json(name = "market_cap")

View file

@ -834,6 +834,7 @@
<string name="markets_token_details_max_supply_description">The maximum number of coins or tokens that can ever exist for a particular cryptocurrency</string> <string name="markets_token_details_max_supply_description">The maximum number of coins or tokens that can ever exist for a particular cryptocurrency</string>
<string name="markets_token_details_max_supply_full">Max supply</string> <string name="markets_token_details_max_supply_full">Max supply</string>
<string name="markets_token_details_metrics">Metrics</string> <string name="markets_token_details_metrics">Metrics</string>
<string name="markets_token_details_metrics_no_limited">No limited</string>
<string name="markets_token_details_official_links">Official links</string> <string name="markets_token_details_official_links">Official links</string>
<string name="markets_token_details_price_performance">Price performance</string> <string name="markets_token_details_price_performance">Price performance</string>
<string name="markets_token_details_repository">Repository</string> <string name="markets_token_details_repository">Repository</string>

View file

@ -114,6 +114,18 @@ class TangemTypography2 internal constructor(
), ),
) )
val headingSemibold22: TextStyle = TextStyle(
fontFamily = fontFamily,
fontSize = 22.sp,
fontWeight = FontWeight.SemiBold,
letterSpacing = TextUnit(value = 0.38f, type = TextUnitType.Sp),
lineHeight = TextUnit(value = 28f, type = TextUnitType.Sp),
lineHeightStyle = LineHeightStyle(
alignment = LineHeightStyle.Alignment.Center,
trim = LineHeightStyle.Trim.None,
),
)
val headingRegular20: TextStyle = TextStyle( val headingRegular20: TextStyle = TextStyle(
fontFamily = fontFamily, fontFamily = fontFamily,
fontSize = 20.sp, fontSize = 20.sp,

View file

@ -105,6 +105,7 @@ internal class TokenMarketInfoConverter(
private fun TokenMarketInfoResponse.Metrics.convert(): TokenMarketInfo.Metrics { private fun TokenMarketInfoResponse.Metrics.convert(): TokenMarketInfo.Metrics {
return TokenMarketInfo.Metrics( return TokenMarketInfo.Metrics(
marketRating = marketRating, marketRating = marketRating,
marketRatingChange24h = marketRatingChange24h,
circulatingSupply = circulatingSupply, circulatingSupply = circulatingSupply,
marketCap = marketCap, marketCap = marketCap,
volume24h = volume24h, volume24h = volume24h,

View file

@ -49,6 +49,7 @@ data class TokenMarketInfo(
data class Metrics( data class Metrics(
val marketRating: Int?, val marketRating: Int?,
val marketRatingChange24h: Int?,
val circulatingSupply: BigDecimal?, val circulatingSupply: BigDecimal?,
val marketCap: BigDecimal?, val marketCap: BigDecimal?,
val volume24h: BigDecimal?, val volume24h: BigDecimal?,

View file

@ -1,6 +1,7 @@
package com.tangem.features.feed.model.market.details.converter package com.tangem.features.feed.model.market.details.converter
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import com.tangem.core.ui.extensions.combinedReference
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
import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.extensions.wrappedList
@ -37,7 +38,7 @@ internal class MetricsConverter(
metrics = persistentListOf( metrics = persistentListOf(
InfoPointUM( InfoPointUM(
title = resourceReference(R.string.markets_token_details_market_capitalization), title = resourceReference(R.string.markets_token_details_market_capitalization),
value = marketCap.formatAmount(), value = marketCap.formatAmount() ?: StringsSigns.DASH_SIGN,
onInfoClick = { onInfoClick = {
onInfoClick( onInfoClick(
InfoBottomSheetContent( InfoBottomSheetContent(
@ -65,7 +66,7 @@ internal class MetricsConverter(
), ),
InfoPointUM( InfoPointUM(
title = resourceReference(R.string.markets_token_details_trading_volume), title = resourceReference(R.string.markets_token_details_trading_volume),
value = volume24h.formatAmount(), value = volume24h.formatAmount() ?: StringsSigns.DASH_SIGN,
onInfoClick = { onInfoClick = {
onInfoClick( onInfoClick(
InfoBottomSheetContent( InfoBottomSheetContent(
@ -79,7 +80,7 @@ internal class MetricsConverter(
), ),
InfoPointUM( InfoPointUM(
title = resourceReference(R.string.markets_token_details_fully_diluted_valuation), title = resourceReference(R.string.markets_token_details_fully_diluted_valuation),
value = fullyDilutedValuation.formatAmount(), value = fullyDilutedValuation.formatAmount() ?: StringsSigns.DASH_SIGN,
onInfoClick = { onInfoClick = {
onInfoClick( onInfoClick(
InfoBottomSheetContent( InfoBottomSheetContent(
@ -95,7 +96,7 @@ internal class MetricsConverter(
), ),
InfoPointUM( InfoPointUM(
title = resourceReference(R.string.markets_token_details_circulating_supply), title = resourceReference(R.string.markets_token_details_circulating_supply),
value = circulatingSupply.formatAmount(crypto = true), value = circulatingSupply.formatAmount(crypto = true) ?: StringsSigns.DASH_SIGN,
onInfoClick = { onInfoClick = {
onInfoClick( onInfoClick(
InfoBottomSheetContent( InfoBottomSheetContent(
@ -109,7 +110,7 @@ internal class MetricsConverter(
), ),
InfoPointUM( InfoPointUM(
title = resourceReference(R.string.markets_token_details_max_supply), title = resourceReference(R.string.markets_token_details_max_supply),
value = maxSupply.formatMaxSupply(), value = maxSupply.formatMaxSupply() ?: StringsSigns.DASH_SIGN,
onInfoClick = { onInfoClick = {
onInfoClick( onInfoClick(
InfoBottomSheetContent( InfoBottomSheetContent(
@ -135,7 +136,7 @@ internal class MetricsConverter(
val liquidity = getLiquidity(value.volume24h, value.marketCap) val liquidity = getLiquidity(value.volume24h, value.marketCap)
persistentListOf( persistentListOf(
InfoPointUMV2.MarketCap( InfoPointUMV2.MarketCap(
capitalizationValue = stringReference(marketCap.formatAmount()), capitalizationValue = marketCap.formatAmount()?.let(::stringReference),
onInfoClick = { onInfoClick = {
onInfoClick( onInfoClick(
InfoBottomSheetContent( InfoBottomSheetContent(
@ -150,7 +151,7 @@ internal class MetricsConverter(
}, },
), ),
InfoPointUMV2.TradingVolume( InfoPointUMV2.TradingVolume(
tradingValue = stringReference(volume24h.formatAmount()), tradingValue = volume24h.formatAmount()?.let(::stringReference),
liquidity = liquidity, liquidity = liquidity,
trendingVolumeLiquidityType = getTrendingVolumeLiquidityType(liquidity), trendingVolumeLiquidityType = getTrendingVolumeLiquidityType(liquidity),
onInfoClick = { onInfoClick = {
@ -165,7 +166,9 @@ internal class MetricsConverter(
}, },
), ),
InfoPointUMV2.MarketPosition( InfoPointUMV2.MarketPosition(
position = marketRating?.toString() ?: StringsSigns.DASH_SIGN, position = marketRating?.let {
stringReference(it.toString())
},
rangeValue = getMarketRatingRangeValue(marketRating), rangeValue = getMarketRatingRangeValue(marketRating),
marketRatingType = getMarketRatingType(marketRating), marketRatingType = getMarketRatingType(marketRating),
onInfoClick = { onInfoClick = {
@ -176,12 +179,15 @@ internal class MetricsConverter(
), ),
) )
}, },
marketRatingChange24H = getMarketRatingChange(marketRatingChange24h),
), ),
InfoPointUMV2.FullyDilutedValuation( InfoPointUMV2.FullyDilutedValuation(
value = resourceReference( value = fullyDilutedValuation?.formatAmount()?.let { formatted ->
R.string.markets_token_details_valuation_value_in_total, resourceReference(
wrappedList(fullyDilutedValuation.formatAmount()), id = R.string.markets_token_details_valuation_value_in_total,
), formatArgs = wrappedList(formatted),
)
},
onInfoClick = { onInfoClick = {
onInfoClick( onInfoClick(
InfoBottomSheetContent( InfoBottomSheetContent(
@ -196,20 +202,29 @@ internal class MetricsConverter(
}, },
), ),
InfoPointUMV2.CirculatingSupply( InfoPointUMV2.CirculatingSupply(
currentValue = stringReference(circulatingSupply.formatAmount(crypto = true)), currentValue = circulatingSupply?.formatAmount(true)?.let(::stringReference),
maxValue = maxSupply?.let { supply -> maxValue = when (maxSupply) {
if (supply > BigDecimal.ZERO) { null -> null
stringReference(supply.formatMaxSupply()) BigDecimal.ZERO -> {
} else { resourceReference(
null R.string
.markets_token_details_metrics_no_limited,
)
} }
else -> maxSupply.formatAmount(true)?.let(::stringReference)
}, },
fillValue = getCirculatingSupplyFillValue(circulatingSupply, maxSupply), fillValue = getCirculatingSupplyFillValue(circulatingSupply, maxSupply),
onInfoClick = { onInfoClick = {
onInfoClick( onInfoClick(
InfoBottomSheetContent( InfoBottomSheetContent(
title = resourceReference(R.string.markets_token_details_max_supply_full), title = resourceReference(
body = resourceReference(R.string.markets_token_details_total_supply_description), R.string.markets_token_details_max_supply_and_circulation_full,
),
body = combinedReference(
resourceReference(R.string.markets_token_details_circulating_supply_description),
stringReference("\n\n"),
resourceReference(R.string.markets_token_details_total_supply_description),
),
), ),
) )
}, },
@ -234,7 +249,7 @@ internal class MetricsConverter(
) )
} }
private fun BigDecimal?.formatMaxSupply(): String { private fun BigDecimal?.formatMaxSupply(): String? {
when (this) { when (this) {
null -> return StringsSigns.DASH_SIGN null -> return StringsSigns.DASH_SIGN
BigDecimal.ZERO -> return StringsSigns.INFINITY_SIGN BigDecimal.ZERO -> return StringsSigns.INFINITY_SIGN
@ -243,8 +258,8 @@ internal class MetricsConverter(
return this.formatAmount(crypto = true) return this.formatAmount(crypto = true)
} }
private fun BigDecimal?.formatAmount(crypto: Boolean = false): String { private fun BigDecimal?.formatAmount(crypto: Boolean = false): String? {
if (this == null) return StringsSigns.DASH_SIGN if (this == null) return null
return if (crypto) { return if (crypto) {
format { format {
@ -266,9 +281,8 @@ internal class MetricsConverter(
} }
@Suppress("MagicNumber") @Suppress("MagicNumber")
private fun getLiquidity(volume24h: BigDecimal?, marketCap: BigDecimal?): Float { private fun getLiquidity(volume24h: BigDecimal?, marketCap: BigDecimal?): Float? {
if (volume24h == null || marketCap == null || marketCap == BigDecimal.ZERO) return 0f if (volume24h == null || marketCap == null || marketCap == BigDecimal.ZERO) return null
val ratio = volume24h val ratio = volume24h
.divide(marketCap, 6, RoundingMode.HALF_UP) .divide(marketCap, 6, RoundingMode.HALF_UP)
.toFloat() .toFloat()
@ -277,8 +291,9 @@ internal class MetricsConverter(
} }
@Suppress("MagicNumber") @Suppress("MagicNumber")
private fun getTrendingVolumeLiquidityType(liquidity: Float): TrendingVolumeLiquidityType { private fun getTrendingVolumeLiquidityType(liquidity: Float?): TrendingVolumeLiquidityType {
return when { return when {
liquidity == null -> TrendingVolumeLiquidityType.UNKNOWN
liquidity >= 0.5f -> TrendingVolumeLiquidityType.HIGH liquidity >= 0.5f -> TrendingVolumeLiquidityType.HIGH
liquidity in 0.2f..<0.5f -> TrendingVolumeLiquidityType.MEDIUM liquidity in 0.2f..<0.5f -> TrendingVolumeLiquidityType.MEDIUM
else -> TrendingVolumeLiquidityType.LOW else -> TrendingVolumeLiquidityType.LOW
@ -296,8 +311,8 @@ internal class MetricsConverter(
} }
@Suppress("MagicNumber") @Suppress("MagicNumber")
private fun getMarketRatingRangeValue(marketRating: Int?): Float { private fun getMarketRatingRangeValue(marketRating: Int?): Float? {
if (marketRating == null) return 0f if (marketRating == null) return null
return when { return when {
marketRating <= 20 -> { marketRating <= 20 -> {
@ -352,4 +367,12 @@ internal class MetricsConverter(
return ratio.coerceIn(0f, 1f) return ratio.coerceIn(0f, 1f)
} }
private fun getMarketRatingChange(change: Int?): MarketRatingChange24H {
return when {
change == null || change == 0 -> MarketRatingChange24H.NoChanges
change < 0 -> MarketRatingChange24H.Down(-change)
else -> MarketRatingChange24H.Up(change)
}
}
} }

View file

@ -21,14 +21,12 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.tangem.core.ui.components.SpacerH import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.SpacerW
import com.tangem.core.ui.components.progressbar.TangemLinearProgressIndicator import com.tangem.core.ui.components.progressbar.TangemLinearProgressIndicator
import com.tangem.core.ui.ds.progress.TangemLinearProgressIndicatorWithDot import com.tangem.core.ui.ds.progress.TangemLinearProgressIndicatorWithDot
import com.tangem.core.ui.ds.row.TangemRowContainer import com.tangem.core.ui.ds.row.TangemRowContainer
import com.tangem.core.ui.ds.row.TangemRowLayoutId import com.tangem.core.ui.ds.row.TangemRowLayoutId
import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.LocalIsInDarkTheme import com.tangem.core.ui.res.LocalIsInDarkTheme
import com.tangem.core.ui.res.LocalRedesignEnabled import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
@ -36,6 +34,7 @@ import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.features.feed.impl.R import com.tangem.features.feed.impl.R
import com.tangem.features.feed.ui.components.MetricsCard import com.tangem.features.feed.ui.components.MetricsCard
import com.tangem.features.feed.ui.market.detailed.state.InfoPointUMV2 import com.tangem.features.feed.ui.market.detailed.state.InfoPointUMV2
import com.tangem.features.feed.ui.market.detailed.state.MarketRatingChange24H
import com.tangem.features.feed.ui.market.detailed.state.MarketRatingType import com.tangem.features.feed.ui.market.detailed.state.MarketRatingType
import com.tangem.features.feed.ui.market.detailed.state.TrendingVolumeLiquidityType import com.tangem.features.feed.ui.market.detailed.state.TrendingVolumeLiquidityType
@ -45,15 +44,7 @@ internal fun MarketCapCard(item: InfoPointUMV2.MarketCap) {
modifier = Modifier modifier = Modifier
.heightIn(120.dp) .heightIn(120.dp)
.fillMaxWidth(), .fillMaxWidth(),
title = { title = { MetricValueText(value = item.capitalizationValue) },
Text(
text = item.capitalizationValue.resolveReference(),
style = TangemTheme.typography2.headingSemibold20,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
},
content = { content = {
InformationTextBlock( InformationTextBlock(
text = resourceReference(R.string.markets_token_details_market_capitalization), text = resourceReference(R.string.markets_token_details_market_capitalization),
@ -69,25 +60,22 @@ internal fun TradingVolumeCard(item: InfoPointUMV2.TradingVolume) {
TrendingVolumeLiquidityType.HIGH -> TangemTheme.colors2.markers.backgroundSolidGreen TrendingVolumeLiquidityType.HIGH -> TangemTheme.colors2.markers.backgroundSolidGreen
TrendingVolumeLiquidityType.MEDIUM -> TangemTheme.colors2.graphic.status.attention TrendingVolumeLiquidityType.MEDIUM -> TangemTheme.colors2.graphic.status.attention
TrendingVolumeLiquidityType.LOW -> TangemTheme.colors2.graphic.status.warning TrendingVolumeLiquidityType.LOW -> TangemTheme.colors2.graphic.status.warning
TrendingVolumeLiquidityType.UNKNOWN -> TangemTheme.colors2.surface.level3
} }
val valueColor = metricValueColor(hasData = item.tradingValue != null)
MetricsCard( MetricsCard(
modifier = Modifier modifier = Modifier
.heightIn(120.dp) .heightIn(120.dp)
.fillMaxWidth(), .fillMaxWidth(),
title = { title = {
Row { Row {
Text( MetricValueText(item.tradingValue)
text = item.tradingValue.resolveReference(),
style = TangemTheme.typography2.headingSemibold20,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text( Text(
modifier = Modifier.padding(TangemTheme.dimens2.x1), modifier = Modifier.padding(TangemTheme.dimens2.x1),
text = stringResourceSafe(R.string.markets_token_details_trading_interval), text = stringResourceSafe(R.string.markets_token_details_trading_interval),
style = TangemTheme.typography2.captionSemibold11, style = TangemTheme.typography2.captionSemibold11,
color = TangemTheme.colors2.text.neutral.primary, color = valueColor,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
) )
@ -95,14 +83,17 @@ internal fun TradingVolumeCard(item: InfoPointUMV2.TradingVolume) {
}, },
content = { content = {
Column(modifier = Modifier.fillMaxWidth()) { Column(modifier = Modifier.fillMaxWidth()) {
TangemLinearProgressIndicator( if (item.liquidity != null) {
modifier = Modifier TangemLinearProgressIndicator(
.fillMaxWidth() modifier = Modifier
.height(6.dp), .fillMaxWidth()
progress = { item.liquidity }, .height(6.dp),
color = tradingColor, progress = { item.liquidity },
backgroundColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant.copy(alpha = .1f), color = tradingColor,
) backgroundColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant
.copy(alpha = .1f),
)
}
SpacerH(12.dp) SpacerH(12.dp)
InformationTextBlock( InformationTextBlock(
text = resourceReference(R.string.markets_token_details_trading_volume), text = resourceReference(R.string.markets_token_details_trading_volume),
@ -127,37 +118,25 @@ internal fun MarketPositionCard(item: InfoPointUMV2.MarketPosition) {
.fillMaxWidth(), .fillMaxWidth(),
title = { title = {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
Icon( MarketPositionValue(position = item.position, ratingColor = ratingColor)
imageVector = ImageVector.vectorResource(R.drawable.ic_big_laurel_left_20), if (item.position != null) {
tint = ratingColor, SpacerW(6.dp)
contentDescription = null, RatingChangeIndicator(change = item.marketRatingChange24H)
) }
Text(
textAlign = TextAlign.Center,
text = item.position,
color = ratingColor,
style = TangemTheme.typography2.headingSemibold20.copy(letterSpacing = 0.sp),
maxLines = 1,
)
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_big_laurel_right_20),
tint = ratingColor,
contentDescription = null,
)
} }
}, },
content = { content = {
Column(modifier = Modifier.fillMaxWidth()) { Column(modifier = Modifier.fillMaxWidth()) {
TangemLinearProgressIndicatorWithDot( if (item.rangeValue != null) {
modifier = Modifier TangemLinearProgressIndicatorWithDot(
.fillMaxWidth() modifier = Modifier
.height(6.dp), .fillMaxWidth()
progress = { item.rangeValue }, .height(6.dp),
dotColor = TangemTheme.colors2.fill.neutral.primaryInvertedConstant, progress = { item.rangeValue },
backgroundColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant.copy(alpha = .1f), dotColor = TangemTheme.colors2.fill.neutral.primaryInvertedConstant,
) backgroundColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant.copy(alpha = .1f),
)
}
SpacerH(12.dp) SpacerH(12.dp)
InformationTextBlock( InformationTextBlock(
text = resourceReference(R.string.markets_token_details_market_rating), text = resourceReference(R.string.markets_token_details_market_rating),
@ -177,15 +156,7 @@ internal fun FDVCard(item: InfoPointUMV2.FullyDilutedValuation) {
modifier = Modifier modifier = Modifier
.heightIn(120.dp) .heightIn(120.dp)
.fillMaxWidth(), .fillMaxWidth(),
title = { title = { MetricValueText(value = item.value) },
Text(
text = item.value.resolveReference(),
style = TangemTheme.typography2.headingSemibold20,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
},
content = { content = {
InformationTextBlock( InformationTextBlock(
text = resourceReference(R.string.markets_token_details_fully_diluted_valuation), text = resourceReference(R.string.markets_token_details_fully_diluted_valuation),
@ -212,15 +183,11 @@ internal fun CirculatingSupplyCard(item: InfoPointUMV2.CirculatingSupply) {
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
) )
Text( MetricValueText(
value = item.currentValue,
modifier = Modifier modifier = Modifier
.padding(top = 12.dp) .padding(top = 12.dp)
.layoutId(TangemRowLayoutId.START_BOTTOM), .layoutId(TangemRowLayoutId.START_BOTTOM),
text = item.currentValue.resolveReference(),
style = TangemTheme.typography2.headingSemibold20,
color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
) )
Text( Text(
@ -238,7 +205,7 @@ internal fun CirculatingSupplyCard(item: InfoPointUMV2.CirculatingSupply) {
.padding(top = 12.dp) .padding(top = 12.dp)
.layoutId(TangemRowLayoutId.END_BOTTOM), .layoutId(TangemRowLayoutId.END_BOTTOM),
text = item.maxValue.resolveReference(), text = item.maxValue.resolveReference(),
style = TangemTheme.typography2.headingSemibold20, style = TangemTheme.typography2.headingSemibold22,
color = TangemTheme.colors2.text.neutral.primary, color = TangemTheme.colors2.text.neutral.primary,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
@ -253,7 +220,8 @@ internal fun CirculatingSupplyCard(item: InfoPointUMV2.CirculatingSupply) {
.fillMaxWidth() .fillMaxWidth()
.height(6.dp), .height(6.dp),
color = TangemTheme.colors2.graphic.status.accent, color = TangemTheme.colors2.graphic.status.accent,
trackColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant.copy(alpha = .1f), trackColor = TangemTheme.colors2.graphic.neutral.primaryInvertedConstant
.copy(alpha = .1f),
progress = { item.fillValue }, progress = { item.fillValue },
strokeCap = StrokeCap.Round, strokeCap = StrokeCap.Round,
drawStopIndicator = {}, drawStopIndicator = {},
@ -265,6 +233,87 @@ internal fun CirculatingSupplyCard(item: InfoPointUMV2.CirculatingSupply) {
) )
} }
// region Private helpers
@Composable
private fun MetricValueText(value: TextReference?, modifier: Modifier = Modifier) {
Text(
modifier = modifier,
text = value?.resolveReference() ?: stringResourceSafe(R.string.token_market_metrics_no_data),
style = TangemTheme.typography2.headingSemibold22,
color = metricValueColor(hasData = value != null),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
@Composable
private fun metricValueColor(hasData: Boolean): Color {
return if (hasData) TangemTheme.colors2.text.neutral.primary else TangemTheme.colors2.text.neutral.tertiary
}
@Composable
private fun MarketPositionValue(position: TextReference?, ratingColor: Color) {
if (position != null) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_big_laurel_left_20),
tint = ratingColor,
contentDescription = null,
)
Text(
textAlign = TextAlign.Center,
text = position.resolveReference(),
color = ratingColor,
style = TangemTheme.typography2.headingSemibold22.copy(letterSpacing = 0.sp),
maxLines = 1,
)
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_big_laurel_right_20),
tint = ratingColor,
contentDescription = null,
)
} else {
MetricValueText(value = null)
}
}
@Composable
private fun RatingChangeIndicator(change: MarketRatingChange24H) {
when (change) {
is MarketRatingChange24H.Up -> RatingChangeContent(
iconRes = R.drawable.ic_arrow_up_8,
iconTint = TangemTheme.colors2.markers.iconGreen,
changeValue = change.changeValue.toString(),
textColor = TangemTheme.colors2.text.status.positive,
)
is MarketRatingChange24H.Down -> RatingChangeContent(
iconRes = R.drawable.ic_arrow_down_8,
iconTint = TangemTheme.colors2.markers.iconRed,
changeValue = change.changeValue.toString(),
textColor = TangemTheme.colors2.text.status.warning,
)
MarketRatingChange24H.NoChanges -> Unit
}
}
@Composable
private fun RatingChangeContent(iconRes: Int, iconTint: Color, changeValue: String, textColor: Color) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
modifier = Modifier.size(TangemTheme.dimens2.x3),
imageVector = ImageVector.vectorResource(id = iconRes),
tint = iconTint,
contentDescription = null,
)
SpacerW(2.dp)
Text(
text = changeValue,
style = TangemTheme.typography2.captionSemibold12,
color = textColor,
)
}
}
@Composable @Composable
private fun MarketRatingType.baseColor(): Color { private fun MarketRatingType.baseColor(): Color {
val isDarkTheme = LocalIsInDarkTheme.current val isDarkTheme = LocalIsInDarkTheme.current
@ -295,6 +344,8 @@ private fun mapRatingToCardColor(marketRatingType: MarketRatingType): Color {
} }
} }
// endregion
private const val GOLD_PLACE_COLOR_NIGHT = 0xFFFBEE76 private const val GOLD_PLACE_COLOR_NIGHT = 0xFFFBEE76
private const val GOLD_PLACE_COLOR_LIGHT = 0xFFD9B900 private const val GOLD_PLACE_COLOR_LIGHT = 0xFFD9B900
private const val SILVER_PLACE_COLOR_NIGHT = 0xFFAABEF7 private const val SILVER_PLACE_COLOR_NIGHT = 0xFFAABEF7
@ -353,17 +404,19 @@ private fun MetricsCardsPreview() {
MarketPositionCard( MarketPositionCard(
item = InfoPointUMV2.MarketPosition( item = InfoPointUMV2.MarketPosition(
position = "1", position = stringReference("1"),
rangeValue = 0.02f, rangeValue = 0.02f,
marketRatingType = MarketRatingType.GOLD, marketRatingType = MarketRatingType.GOLD,
onInfoClick = {}, onInfoClick = {},
marketRatingChange24H = MarketRatingChange24H.NoChanges,
), ),
) )
MarketPositionCard( MarketPositionCard(
item = InfoPointUMV2.MarketPosition( item = InfoPointUMV2.MarketPosition(
position = "2", position = stringReference("2"),
rangeValue = 0.05f, rangeValue = 0.05f,
marketRatingChange24H = MarketRatingChange24H.Up(1),
marketRatingType = MarketRatingType.SILVER, marketRatingType = MarketRatingType.SILVER,
onInfoClick = {}, onInfoClick = {},
), ),
@ -371,19 +424,21 @@ private fun MetricsCardsPreview() {
MarketPositionCard( MarketPositionCard(
item = InfoPointUMV2.MarketPosition( item = InfoPointUMV2.MarketPosition(
position = "3", position = null,
rangeValue = 0.08f, rangeValue = null,
marketRatingType = MarketRatingType.BRONZE, marketRatingType = MarketRatingType.OTHER,
onInfoClick = {}, onInfoClick = {},
marketRatingChange24H = MarketRatingChange24H.NoChanges,
), ),
) )
MarketPositionCard( MarketPositionCard(
item = InfoPointUMV2.MarketPosition( item = InfoPointUMV2.MarketPosition(
position = "42", position = stringReference("42"),
rangeValue = 0.42f, rangeValue = 0.42f,
marketRatingType = MarketRatingType.OTHER, marketRatingType = MarketRatingType.OTHER,
onInfoClick = {}, onInfoClick = {},
marketRatingChange24H = MarketRatingChange24H.Down(15),
), ),
) )

View file

@ -14,35 +14,36 @@ internal sealed interface InfoPointUMV2 {
@Immutable @Immutable
data class MarketCap( data class MarketCap(
val capitalizationValue: TextReference, val capitalizationValue: TextReference?,
val onInfoClick: () -> Unit, val onInfoClick: () -> Unit,
) : InfoPointUMV2 ) : InfoPointUMV2
@Immutable @Immutable
data class TradingVolume( data class TradingVolume(
val tradingValue: TextReference, val tradingValue: TextReference?,
val liquidity: Float, val liquidity: Float?,
val trendingVolumeLiquidityType: TrendingVolumeLiquidityType, val trendingVolumeLiquidityType: TrendingVolumeLiquidityType,
val onInfoClick: () -> Unit, val onInfoClick: () -> Unit,
) : InfoPointUMV2 ) : InfoPointUMV2
@Immutable @Immutable
data class MarketPosition( data class MarketPosition(
val position: String, val position: TextReference?,
val rangeValue: Float, val rangeValue: Float?,
val marketRatingChange24H: MarketRatingChange24H,
val marketRatingType: MarketRatingType, val marketRatingType: MarketRatingType,
val onInfoClick: () -> Unit, val onInfoClick: () -> Unit,
) : InfoPointUMV2 ) : InfoPointUMV2
@Immutable @Immutable
data class FullyDilutedValuation( data class FullyDilutedValuation(
val value: TextReference, val value: TextReference?,
val onInfoClick: () -> Unit, val onInfoClick: () -> Unit,
) : InfoPointUMV2 ) : InfoPointUMV2
@Immutable @Immutable
data class CirculatingSupply( data class CirculatingSupply(
val currentValue: TextReference, val currentValue: TextReference?,
val maxValue: TextReference?, val maxValue: TextReference?,
val fillValue: Float?, val fillValue: Float?,
val onInfoClick: () -> Unit, val onInfoClick: () -> Unit,
@ -60,9 +61,18 @@ internal data class MetricsV2UM(
} }
internal enum class TrendingVolumeLiquidityType { internal enum class TrendingVolumeLiquidityType {
HIGH, MEDIUM, LOW, HIGH, MEDIUM, LOW, UNKNOWN
} }
internal enum class MarketRatingType { internal enum class MarketRatingType {
GOLD, SILVER, BRONZE, OTHER GOLD, SILVER, BRONZE, OTHER
}
internal sealed interface MarketRatingChange24H {
data class Up(val changeValue: Int) : MarketRatingChange24H
data class Down(val changeValue: Int) : MarketRatingChange24H
data object NoChanges : MarketRatingChange24H
} }