Updated on 2026-08-14
This commit is contained in:
parent
2e857d5e3e
commit
45fa120411
49 changed files with 669 additions and 92 deletions
|
|
@ -39,6 +39,7 @@ 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.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.LocalIsInDarkTheme
|
||||
import com.tangem.core.ui.res.LocalWindowSize
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -47,6 +48,7 @@ import com.tangem.core.ui.test.MarketsTestTags
|
|||
import com.tangem.core.ui.test.TokenElementsTestTags
|
||||
import com.tangem.core.ui.windowsize.WindowSizeType
|
||||
import com.tangem.utils.StringsSigns.MINUS
|
||||
import java.math.BigDecimal
|
||||
import kotlin.random.Random
|
||||
|
||||
@Composable
|
||||
|
|
@ -88,6 +90,8 @@ fun MarketListItemContentV2(model: MarketsListItemUM, modifier: Modifier = Modif
|
|||
.testTag(tag = TokenElementsTestTags.TOKEN_FIAT_AMOUNT),
|
||||
price = model.price.text,
|
||||
priceChangeType = model.price.changeType,
|
||||
priceAnnotated = model.price.annotated,
|
||||
priceValue = model.price.fiatPrice,
|
||||
)
|
||||
|
||||
TokenSubtitle(
|
||||
|
|
@ -127,7 +131,7 @@ private fun TokenTitle(name: String, currencySymbol: String, modifier: Modifier
|
|||
.alignByBaseline(),
|
||||
text = name,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
style = TangemTheme.typography2.bodyMedium16,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
|
@ -323,12 +327,16 @@ private fun Preview(@PreviewParameter(MarketChartListItemPreviewDataProvider::cl
|
|||
price = MarketsListItemUM.Price(
|
||||
text = "0.${prices[0].first}023 $",
|
||||
changeType = prices[0].second,
|
||||
fiatPrice = BigDecimal(123123),
|
||||
annotated = stringReference("0.${prices[0].first}023 $"),
|
||||
),
|
||||
)
|
||||
state2 = state2.copy(
|
||||
price = MarketsListItemUM.Price(
|
||||
text = "0.${prices[1].first}023 $",
|
||||
changeType = prices[1].second,
|
||||
fiatPrice = BigDecimal(123123),
|
||||
annotated = stringReference("0.${prices[0].first}023 $"),
|
||||
),
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.common.ui.markets
|
||||
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.formatStyled
|
||||
import com.tangem.core.ui.format.bigdecimal.price
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Fiat amount for markets list rows: integer part in primary style, fractional part (from locale separator) in secondary.
|
||||
*/
|
||||
fun BigDecimal.toMarketsListItemPriceAnnotated(appCurrencyCode: String, appCurrencySymbol: String): TextReference {
|
||||
return formatStyled {
|
||||
fiat(
|
||||
fiatCurrencyCode = appCurrencyCode,
|
||||
fiatCurrencySymbol = appCurrencySymbol,
|
||||
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.neutral.secondary) },
|
||||
).price()
|
||||
}
|
||||
}
|
||||
|
|
@ -29,12 +29,14 @@ import com.tangem.core.ui.components.marketprice.PriceChangeInPercent
|
|||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.LocalWindowSize
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.test.MarketsTestTags
|
||||
import com.tangem.core.ui.windowsize.WindowSizeType
|
||||
import com.tangem.utils.StringsSigns.MINUS
|
||||
import java.math.BigDecimal
|
||||
import kotlin.random.Random
|
||||
|
||||
@Composable
|
||||
|
|
@ -85,6 +87,8 @@ private fun MarketsListItemContentV1(model: MarketsListItemUM, modifier: Modifie
|
|||
modifier = Modifier.alignByBaseline(),
|
||||
price = model.price.text,
|
||||
priceChangeType = model.price.changeType,
|
||||
priceAnnotated = model.price.annotated,
|
||||
priceValue = model.price.fiatPrice,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -306,12 +310,16 @@ private fun Preview(@PreviewParameter(MarketChartListItemPreviewDataProvider::cl
|
|||
price = MarketsListItemUM.Price(
|
||||
text = "0.${prices[0].first}023 $",
|
||||
changeType = prices[0].second,
|
||||
fiatPrice = BigDecimal(123123),
|
||||
annotated = stringReference("0.${prices[0].first}023 $"),
|
||||
),
|
||||
)
|
||||
state2 = state2.copy(
|
||||
price = MarketsListItemUM.Price(
|
||||
text = "0.${prices[1].first}023 $",
|
||||
changeType = prices[1].second,
|
||||
fiatPrice = BigDecimal(123123),
|
||||
annotated = stringReference("0.${prices[0].first}023 $"),
|
||||
),
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.common.ui.charts.state.MarketChartRawData
|
|||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Immutable
|
||||
data class MarketsListItemUM(
|
||||
|
|
@ -40,7 +41,9 @@ data class MarketsListItemUM(
|
|||
@Immutable
|
||||
data class Price(
|
||||
val text: String,
|
||||
val annotated: TextReference,
|
||||
val changeType: PriceChangeType? = null,
|
||||
val fiatPrice: BigDecimal,
|
||||
)
|
||||
|
||||
@Suppress("NullableToStringCall")
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.core.ui.components.marketprice.PriceChangeType
|
|||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvider<MarketsListItemUM>(
|
||||
|
|
@ -18,7 +19,11 @@ class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvide
|
|||
iconUrl = "",
|
||||
ratingPosition = "1",
|
||||
marketCap = "$6.233 B",
|
||||
price = MarketsListItemUM.Price(text = "31 285.72$"),
|
||||
price = MarketsListItemUM.Price(
|
||||
text = "31 285.72$",
|
||||
fiatPrice = BigDecimal(123123),
|
||||
annotated = stringReference("31 285.72$"),
|
||||
),
|
||||
trendPercentText = "12.43%",
|
||||
trendType = PriceChangeType.UP,
|
||||
chartData = MarketChartRawData(
|
||||
|
|
@ -35,7 +40,11 @@ class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvide
|
|||
iconUrl = null,
|
||||
ratingPosition = "2",
|
||||
marketCap = "$6.233 B",
|
||||
price = MarketsListItemUM.Price(text = "31 285.72$"),
|
||||
price = MarketsListItemUM.Price(
|
||||
text = "31 285.72$",
|
||||
fiatPrice = BigDecimal(123123),
|
||||
annotated = stringReference("31 285.72$"),
|
||||
),
|
||||
trendPercentText = "12.43%",
|
||||
trendType = PriceChangeType.NEUTRAL,
|
||||
chartData = null,
|
||||
|
|
@ -50,7 +59,11 @@ class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvide
|
|||
iconUrl = null,
|
||||
ratingPosition = "10",
|
||||
marketCap = "$6.23348172384781234 B",
|
||||
price = MarketsListItemUM.Price(text = "31 285.72$"),
|
||||
price = MarketsListItemUM.Price(
|
||||
text = "31 285.72$",
|
||||
fiatPrice = BigDecimal(123123),
|
||||
annotated = stringReference("31 285.72$"),
|
||||
),
|
||||
trendPercentText = "12.43%",
|
||||
trendType = PriceChangeType.DOWN,
|
||||
chartData = MarketChartRawData(
|
||||
|
|
@ -67,7 +80,11 @@ class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvide
|
|||
iconUrl = null,
|
||||
ratingPosition = "10",
|
||||
marketCap = null,
|
||||
price = MarketsListItemUM.Price(text = "31 285.72$"),
|
||||
price = MarketsListItemUM.Price(
|
||||
text = "31 285.72$",
|
||||
fiatPrice = BigDecimal(123123),
|
||||
annotated = stringReference("31 285.72$"),
|
||||
),
|
||||
trendPercentText = "12.43%",
|
||||
trendType = PriceChangeType.UP,
|
||||
chartData = MarketChartRawData(
|
||||
|
|
@ -84,7 +101,11 @@ class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvide
|
|||
iconUrl = null,
|
||||
ratingPosition = null,
|
||||
marketCap = "$6.233 B",
|
||||
price = MarketsListItemUM.Price(text = "31 285.72$"),
|
||||
price = MarketsListItemUM.Price(
|
||||
text = "31 285.72$",
|
||||
fiatPrice = BigDecimal(123123),
|
||||
annotated = stringReference("31 285.72$"),
|
||||
),
|
||||
trendPercentText = "12.43%",
|
||||
trendType = PriceChangeType.UP,
|
||||
chartData = MarketChartRawData(
|
||||
|
|
@ -101,7 +122,11 @@ class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvide
|
|||
iconUrl = null,
|
||||
ratingPosition = null,
|
||||
marketCap = null,
|
||||
price = MarketsListItemUM.Price(text = "31 285.72$"),
|
||||
price = MarketsListItemUM.Price(
|
||||
text = "31 285.72$",
|
||||
fiatPrice = BigDecimal(123123),
|
||||
annotated = stringReference("31 285.72$"),
|
||||
),
|
||||
trendPercentText = "12.43%",
|
||||
trendType = PriceChangeType.UP,
|
||||
chartData = MarketChartRawData(
|
||||
|
|
|
|||
|
|
@ -6,25 +6,33 @@ import androidx.compose.animation.core.tween
|
|||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveAnnotatedReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Text view for token price.
|
||||
*
|
||||
* @param price Price of the token.
|
||||
* @param price Plain price string (used by V1 and as fallback for V2 when [priceAnnotated] is null).
|
||||
* @param priceAnnotated Optional styled price (locale-aware decimal separator + secondary fractional color).
|
||||
* @param priceChangeType Type of the price change.
|
||||
*/
|
||||
@Composable
|
||||
fun TokenPriceText(price: String, modifier: Modifier = Modifier, priceChangeType: PriceChangeType? = null) {
|
||||
fun TokenPriceText(
|
||||
priceValue: BigDecimal,
|
||||
price: String,
|
||||
priceAnnotated: TextReference,
|
||||
modifier: Modifier = Modifier,
|
||||
priceChangeType: PriceChangeType? = null,
|
||||
) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
TokenPriceTextV2(
|
||||
price = price,
|
||||
priceValue = priceValue,
|
||||
priceAnnotated = priceAnnotated,
|
||||
modifier = modifier,
|
||||
priceChangeType = priceChangeType,
|
||||
)
|
||||
|
|
@ -76,16 +84,20 @@ private fun TokenPriceTextV1(price: String, modifier: Modifier = Modifier, price
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenPriceTextV2(price: String, modifier: Modifier = Modifier, priceChangeType: PriceChangeType? = null) {
|
||||
private fun TokenPriceTextV2(
|
||||
priceValue: BigDecimal,
|
||||
priceAnnotated: TextReference,
|
||||
modifier: Modifier = Modifier,
|
||||
priceChangeType: PriceChangeType? = null,
|
||||
) {
|
||||
val growColor = TangemTheme.colors2.text.status.accent
|
||||
val fallColor = TangemTheme.colors2.text.status.warning
|
||||
val generalColor = TangemTheme.colors2.text.neutral.primary
|
||||
val decimalColor = TangemTheme.colors2.text.neutral.secondary
|
||||
|
||||
val color = remember(generalColor) { Animatable(generalColor) }
|
||||
var isAnimationSkipped by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(price) {
|
||||
LaunchedEffect(priceValue) {
|
||||
if (!isAnimationSkipped) {
|
||||
isAnimationSkipped = true
|
||||
return@LaunchedEffect
|
||||
|
|
@ -103,27 +115,9 @@ private fun TokenPriceTextV2(price: String, modifier: Modifier = Modifier, price
|
|||
}
|
||||
}
|
||||
|
||||
val annotatedText = remember(price) {
|
||||
buildAnnotatedString {
|
||||
val dotIndex = price.indexOf(".")
|
||||
|
||||
if (dotIndex == -1) {
|
||||
append(price)
|
||||
} else {
|
||||
append(price.take(dotIndex))
|
||||
|
||||
withStyle(
|
||||
style = SpanStyle(color = decimalColor),
|
||||
) {
|
||||
append(price.substring(dotIndex))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = modifier,
|
||||
text = annotatedText,
|
||||
text = priceAnnotated.resolveAnnotatedReference(),
|
||||
color = color.value,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
|
|
|
|||
|
|
@ -116,6 +116,8 @@ data class TokenMarketInfoResponse(
|
|||
val maxSupply: BigDecimal?,
|
||||
@Json(name = "fully_diluted_valuation")
|
||||
val fullyDilutedValuation: BigDecimal?,
|
||||
@Json(name = "fully_diluted_valuation_change_24h")
|
||||
val fullyDilutedValuationChange24H: BigDecimal?,
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
|
|
|
|||
|
|
@ -172,6 +172,40 @@ fun BigDecimalFiatFormat.price(): BigDecimalFormat = BigDecimalFormat { value ->
|
|||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol)
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats fiat price with precision calculated based on the value.
|
||||
* Styled version — fractional part uses [spanStyleReference].
|
||||
* @see getFiatPriceAmountWithScale
|
||||
*/
|
||||
fun BigDecimalFiatFormatStyled.price() = price(spanStyleReference)
|
||||
|
||||
private fun BigDecimalFiatFormatStyled.price(spanStyleReference: SpanStyleReference) = BigDecimalFormatStyled { value ->
|
||||
val formatterCurrency = getJavaCurrencyByCode(fiatCurrencyCode)
|
||||
|
||||
val (priceAmount, finalScale) = getFiatPriceAmountWithScale(value = value)
|
||||
|
||||
val formatter = NumberFormat.getCurrencyInstance(locale).apply {
|
||||
currency = formatterCurrency
|
||||
maximumFractionDigits = finalScale
|
||||
minimumFractionDigits = FIAT_MARKET_DEFAULT_DIGITS
|
||||
roundingMode = RoundingMode.HALF_UP
|
||||
}
|
||||
|
||||
val decimalSeparator = (formatter as? DecimalFormat)?.decimalFormatSymbols?.decimalSeparator
|
||||
val formattedAmount = formatter.format(priceAmount)
|
||||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol)
|
||||
|
||||
val separatorIndex = decimalSeparator?.let { formattedAmount.indexOf(it) } ?: formattedAmount.length
|
||||
|
||||
val wholePart = formattedAmount.take(separatorIndex)
|
||||
val fractionalPart = formattedAmount.drop(separatorIndex)
|
||||
|
||||
combinedReference(
|
||||
stringReference(wholePart),
|
||||
styledStringReference(fractionalPart, spanStyleReference),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats fiat amount with an exact number of fractional digits.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -198,6 +198,18 @@ class TangemTypography2 internal constructor(
|
|||
),
|
||||
)
|
||||
|
||||
val bodyMedium16: TextStyle = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
letterSpacing = TextUnit(value = -0.32f, type = TextUnitType.Sp),
|
||||
lineHeight = TextUnit(value = 20f, type = TextUnitType.Sp),
|
||||
lineHeightStyle = LineHeightStyle(
|
||||
alignment = LineHeightStyle.Alignment.Center,
|
||||
trim = LineHeightStyle.Trim.None,
|
||||
),
|
||||
)
|
||||
|
||||
val bodyRegular15: TextStyle = TextStyle(
|
||||
fontFamily = fontFamily,
|
||||
fontSize = 15.sp,
|
||||
|
|
|
|||
9
core/ui/src/main/res/drawable/ic_yield_mode_16.xml
Normal file
9
core/ui/src/main/res/drawable/ic_yield_mode_16.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="M4,8.667C4.368,8.667 4.667,8.965 4.667,9.333V12C4.667,12.368 4.368,12.667 4,12.667C3.632,12.667 3.333,12.368 3.333,12V9.333C3.333,8.965 3.632,8.667 4,8.667ZM8,6C8.368,6 8.667,6.298 8.667,6.667V12C8.667,12.368 8.368,12.667 8,12.667C7.632,12.667 7.333,12.368 7.333,12V6.667C7.333,6.298 7.632,6 8,6ZM12,3.333C12.368,3.333 12.667,3.632 12.667,4V12C12.667,12.368 12.368,12.667 12,12.667C11.632,12.667 11.333,12.368 11.333,12V4C11.333,3.632 11.632,3.333 12,3.333Z"
|
||||
android:fillColor="#656565"/>
|
||||
</vector>
|
||||
|
|
@ -111,6 +111,7 @@ internal class TokenMarketInfoConverter(
|
|||
volume24h = volume24h,
|
||||
maxSupply = maxSupply,
|
||||
fullyDilutedValuation = fullyDilutedValuation,
|
||||
fullyDilutedValuationChange24 = fullyDilutedValuationChange24H,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import com.tangem.data.search.model.TextHintDTO
|
|||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.search.model.RecentSearchToken
|
||||
import com.tangem.domain.search.model.SearchTextHint
|
||||
import com.tangem.domain.search.model.TokenPriceChangeDirection
|
||||
import com.tangem.utils.converter.Converter
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class TextHintDTOToSearchTextHintConverter : Converter<TextHintDTO, SearchTextHint> {
|
||||
override fun convert(value: TextHintDTO): SearchTextHint {
|
||||
|
|
@ -24,6 +26,15 @@ internal class RecentTokenDTOToRecentSearchTokenConverter : Converter<RecentToke
|
|||
symbol = value.symbol,
|
||||
imageUrl = value.imageUrl,
|
||||
timestamp = value.timestamp,
|
||||
appCurrencyCode = value.appCurrencyCode,
|
||||
appCurrencySymbol = value.appCurrencySymbol,
|
||||
price = value.pricePlain.toBigDecimalOrNull() ?: BigDecimal.ZERO,
|
||||
priceChangePercent = value.priceChangePercent,
|
||||
priceChangeDirection = value.priceChangeDirection.toTokenPriceChangeDirection(),
|
||||
marketCap = value.marketCap,
|
||||
ratingPosition = value.ratingPosition,
|
||||
isUnder100kMarketCap = value.isUnder100kMarketCap,
|
||||
stakingRateText = value.stakingRateText,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -36,6 +47,20 @@ internal class RecentSearchTokenToRecentTokenDTOConverter : Converter<RecentSear
|
|||
symbol = value.symbol,
|
||||
imageUrl = value.imageUrl,
|
||||
timestamp = value.timestamp,
|
||||
pricePlain = value.price.toPlainString(),
|
||||
appCurrencyCode = value.appCurrencyCode,
|
||||
appCurrencySymbol = value.appCurrencySymbol,
|
||||
priceChangePercent = value.priceChangePercent,
|
||||
priceChangeDirection = value.priceChangeDirection.name,
|
||||
marketCap = value.marketCap,
|
||||
ratingPosition = value.ratingPosition,
|
||||
isUnder100kMarketCap = value.isUnder100kMarketCap,
|
||||
stakingRateText = value.stakingRateText,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.toTokenPriceChangeDirection(): TokenPriceChangeDirection {
|
||||
return runCatching { TokenPriceChangeDirection.valueOf(this) }
|
||||
.getOrDefault(TokenPriceChangeDirection.NEUTRAL)
|
||||
}
|
||||
|
|
@ -14,6 +14,9 @@ internal data class TextHintDTO(
|
|||
val timestamp: Long,
|
||||
)
|
||||
|
||||
/**
|
||||
* @param appCurrencyCode currency code (e.g. "USD") the [pricePlain] was quoted in
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class RecentTokenDTO(
|
||||
val id: String,
|
||||
|
|
@ -21,4 +24,13 @@ internal data class RecentTokenDTO(
|
|||
val symbol: String,
|
||||
val imageUrl: String?,
|
||||
val timestamp: Long,
|
||||
val pricePlain: String,
|
||||
val appCurrencyCode: String,
|
||||
val appCurrencySymbol: String,
|
||||
val priceChangePercent: String,
|
||||
val priceChangeDirection: String,
|
||||
val marketCap: String?,
|
||||
val ratingPosition: String?,
|
||||
val isUnder100kMarketCap: Boolean,
|
||||
val stakingRateText: String?,
|
||||
)
|
||||
|
|
@ -55,6 +55,7 @@ data class TokenMarketInfo(
|
|||
val volume24h: BigDecimal?,
|
||||
val maxSupply: BigDecimal?,
|
||||
val fullyDilutedValuation: BigDecimal?,
|
||||
val fullyDilutedValuationChange24: BigDecimal?,
|
||||
)
|
||||
|
||||
data class SecurityData(
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
package com.tangem.domain.search.model
|
||||
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* @property timestamp epoch milliseconds
|
||||
* @property price fiat quote in the app currency that was active when the token was saved
|
||||
*/
|
||||
data class RecentSearchToken(
|
||||
val id: CryptoCurrency.RawID,
|
||||
|
|
@ -11,4 +13,13 @@ data class RecentSearchToken(
|
|||
val symbol: String,
|
||||
val imageUrl: String?,
|
||||
val timestamp: Long,
|
||||
val appCurrencyCode: String,
|
||||
val appCurrencySymbol: String,
|
||||
val price: BigDecimal,
|
||||
val priceChangePercent: String,
|
||||
val priceChangeDirection: TokenPriceChangeDirection,
|
||||
val marketCap: String?,
|
||||
val ratingPosition: String?,
|
||||
val isUnder100kMarketCap: Boolean,
|
||||
val stakingRateText: String?,
|
||||
)
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.domain.search.model
|
||||
|
||||
enum class TokenPriceChangeDirection {
|
||||
UP,
|
||||
DOWN,
|
||||
NEUTRAL,
|
||||
}
|
||||
|
|
@ -35,10 +35,11 @@ internal class EarnTokenWithCurrencyToListItemUMConverter(
|
|||
formatArgs = wrappedList(convertPercent(value.earnToken.apy)),
|
||||
)
|
||||
},
|
||||
earnType = when (value.earnToken.type) {
|
||||
earnTypeTitle = when (value.earnToken.type) {
|
||||
EarnType.STAKING -> TextReference.Res(R.string.common_staking)
|
||||
EarnType.YIELD -> TextReference.Res(R.string.common_yield_mode)
|
||||
},
|
||||
earnType = value.earnToken.type,
|
||||
onItemClick = { onItemClick(value) },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.common.ui.charts.state.MarketChartRawData
|
|||
import com.tangem.common.ui.charts.state.converter.PriceAndTimePointValuesConverter
|
||||
import com.tangem.common.ui.charts.state.sorted
|
||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||
import com.tangem.common.ui.markets.toMarketsListItemPriceAnnotated
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
|
|
@ -110,7 +111,12 @@ internal class MarketsTokenItemConverter(
|
|||
|
||||
return MarketsListItemUM.Price(
|
||||
text = priceText,
|
||||
annotated = tokenQuotesShort.currentPrice.toMarketsListItemPriceAnnotated(
|
||||
appCurrencyCode = appCurrency.code,
|
||||
appCurrencySymbol = appCurrency.symbol,
|
||||
),
|
||||
changeType = changeType,
|
||||
fiatPrice = tokenQuotesShort.currentPrice,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -230,6 +230,9 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
fiatCurrencySymbol = currentAppCurrency.value.symbol,
|
||||
).price()
|
||||
},
|
||||
priceAnnotated = params.token.tokenQuotes.currentPrice.toMarketsTokenDetailsPriceAnnotated(
|
||||
currentAppCurrency.value,
|
||||
),
|
||||
dateTimeText = resourceReference(R.string.common_today),
|
||||
priceChangePercentText = params.token.tokenQuotes.h24Percent?.format { percent() },
|
||||
priceChangeType = params.token.tokenQuotes.h24Percent.percentChangeType(),
|
||||
|
|
@ -505,6 +508,9 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
fiatCurrencyCode = currentAppCurrency.value.code,
|
||||
).price()
|
||||
},
|
||||
priceAnnotated = newInfo.quotes.currentPrice.toMarketsTokenDetailsPriceAnnotated(
|
||||
currentAppCurrency.value,
|
||||
),
|
||||
priceChangePercentText = newInfo.quotes.getFormattedPercentByInterval(
|
||||
interval = marketsTokenDetailsUM.selectedInterval,
|
||||
),
|
||||
|
|
@ -604,7 +610,8 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
)
|
||||
} ?: getDefaultDateTimeString(currentState.selectedInterval)
|
||||
|
||||
val priceText = (price ?: currentQuotes.value.currentPrice).format {
|
||||
val amountForPrice = price ?: currentQuotes.value.currentPrice
|
||||
val priceText = amountForPrice.format {
|
||||
fiat(
|
||||
fiatCurrencySymbol = currentAppCurrency.value.symbol,
|
||||
fiatCurrencyCode = currentAppCurrency.value.code,
|
||||
|
|
@ -625,6 +632,7 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
isMarkerSet = markerTimestamp != null,
|
||||
dateTimeText = dateTimeText,
|
||||
priceText = priceText,
|
||||
priceAnnotated = amountForPrice.toMarketsTokenDetailsPriceAnnotated(currentAppCurrency.value),
|
||||
priceChangePercentText = percentText,
|
||||
priceChangeType = percent.percentChangeType(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
package com.tangem.features.feed.model.market.details.converter
|
||||
|
||||
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.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.format.bigdecimal.compact
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
|
|
@ -188,6 +185,7 @@ internal class MetricsConverter(
|
|||
formatArgs = wrappedList(formatted),
|
||||
)
|
||||
},
|
||||
fullyDilutedValuationChange24 = fullyDilutedValuationChange24?.convertChange(),
|
||||
onInfoClick = {
|
||||
onInfoClick(
|
||||
InfoBottomSheetContent(
|
||||
|
|
@ -280,6 +278,17 @@ internal class MetricsConverter(
|
|||
}
|
||||
}
|
||||
|
||||
private fun BigDecimal?.convertChange(): TextReference? {
|
||||
if (this == null) return null
|
||||
val amount = this.abs().formatAmount()
|
||||
val value = when {
|
||||
this > BigDecimal.ZERO -> StringsSigns.PLUS + amount
|
||||
this < BigDecimal.ZERO -> StringsSigns.MINUS + amount
|
||||
else -> amount
|
||||
}
|
||||
return value?.let(::stringReference)
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun getLiquidity(volume24h: BigDecimal?, marketCap: BigDecimal?): Float? {
|
||||
if (volume24h == null || marketCap == null || marketCap == BigDecimal.ZERO) return null
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
package com.tangem.features.feed.model.market.details.formatter
|
||||
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import com.tangem.common.ui.charts.state.MarketChartLook
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.format.bigdecimal.getFiatPriceAmountWithScale
|
||||
import com.tangem.core.ui.format.bigdecimal.percent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.format.bigdecimal.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import com.tangem.domain.markets.TokenQuotes
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -74,3 +76,13 @@ internal fun PriceChangeType.toChartType(): MarketChartLook.Type {
|
|||
PriceChangeType.NEUTRAL -> MarketChartLook.Type.Neutral
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BigDecimal.toMarketsTokenDetailsPriceAnnotated(appCurrency: AppCurrency): TextReference {
|
||||
return formatStyled {
|
||||
fiat(
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.neutral.tertiary) },
|
||||
).price()
|
||||
}
|
||||
}
|
||||
|
|
@ -66,6 +66,7 @@ internal class QuotesStateUpdater(
|
|||
fiatCurrencyCode = currentAppCurrency().code,
|
||||
).price()
|
||||
},
|
||||
priceAnnotated = newQuotes.currentPrice.toMarketsTokenDetailsPriceAnnotated(currentAppCurrency()),
|
||||
priceChangePercentText = newQuotes.getFormattedPercentByInterval(
|
||||
interval = stateToUpdate.selectedInterval,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.common.ui.charts.state.MarketChartRawData
|
|||
import com.tangem.common.ui.charts.state.converter.PriceAndTimePointValuesConverter
|
||||
import com.tangem.common.ui.charts.state.sorted
|
||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||
import com.tangem.common.ui.markets.toMarketsListItemPriceAnnotated
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.format.bigdecimal.*
|
||||
import com.tangem.data.common.currency.getTokenIconUrlFromDefaultHost
|
||||
|
|
@ -68,7 +69,12 @@ internal class RelatedTokenConverter(private val appCurrency: AppCurrency) :
|
|||
|
||||
return MarketsListItemUM.Price(
|
||||
text = priceText,
|
||||
annotated = tokenInfo.quotes.currentPrice.toMarketsListItemPriceAnnotated(
|
||||
appCurrencyCode = appCurrency.code,
|
||||
appCurrencySymbol = appCurrency.symbol,
|
||||
),
|
||||
changeType = null,
|
||||
fiatPrice = tokenInfo.quotes.currentPrice,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,19 +5,27 @@ import com.tangem.common.ui.markets.models.MarketsListItemUM
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
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
|
||||
import com.tangem.domain.markets.GetMarketsTokenListFlowUseCase
|
||||
import com.tangem.domain.markets.GetTokenPriceChartUseCase
|
||||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import com.tangem.domain.models.account.AccountName
|
||||
import com.tangem.domain.search.model.RecentSearchToken
|
||||
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.usecase.SaveSearchQueryUseCase
|
||||
import com.tangem.features.feed.components.search.DefaultSearchComponent
|
||||
import com.tangem.features.feed.model.market.list.state.MarketsListUM
|
||||
import com.tangem.features.feed.model.market.list.state.SortByTypeUM
|
||||
import com.tangem.features.feed.model.market.list.statemanager.MarketsListBatchFlowManager
|
||||
import com.tangem.features.feed.model.search.converter.MarketsListItemUMToRecentSearchTokenConverter
|
||||
import com.tangem.features.feed.model.search.converter.MarketsListItemUMWithAppCurrency
|
||||
import com.tangem.features.feed.model.search.converter.RecentSearchTokenToMarketsListItemUMConverter
|
||||
import com.tangem.features.feed.model.search.converter.RecentSearchTokenWithAppCurrency
|
||||
import com.tangem.features.feed.model.search.state.SearchStateController
|
||||
import com.tangem.features.feed.model.search.state.transformers.*
|
||||
import com.tangem.features.feed.ui.search.state.*
|
||||
|
|
@ -28,6 +36,9 @@ import com.tangem.utils.coroutines.saveIn
|
|||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -45,7 +56,9 @@ internal class SearchModel @Inject constructor(
|
|||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getSearchResultsUseCase: GetSearchResultsUseCase,
|
||||
private val saveSearchQueryUseCase: SaveSearchQueryUseCase,
|
||||
private val saveRecentSearchTokenUseCase: SaveRecentSearchTokenUseCase,
|
||||
private val clearSearchHistoryUseCase: ClearSearchHistoryUseCase,
|
||||
private val getTokenPriceChartUseCase: GetTokenPriceChartUseCase,
|
||||
private val stateController: SearchStateController,
|
||||
) : Model() {
|
||||
|
||||
|
|
@ -64,6 +77,18 @@ internal class SearchModel @Inject constructor(
|
|||
initialValue = AppCurrency.Default,
|
||||
)
|
||||
|
||||
private val marketsListItemToRecentSearchTokenConverter by lazy {
|
||||
MarketsListItemUMToRecentSearchTokenConverter()
|
||||
}
|
||||
|
||||
private val recentSearchTokenToMarketsListItemConverter by lazy {
|
||||
RecentSearchTokenToMarketsListItemUMConverter()
|
||||
}
|
||||
|
||||
private val priceAndTimePointValuesConverter by lazy {
|
||||
PriceAndTimePointValuesConverter(shouldFormatAxis = false)
|
||||
}
|
||||
|
||||
private val searchMarketsListManager by lazy {
|
||||
MarketsListBatchFlowManager(
|
||||
getMarketsTokenListFlowUseCase = getMarketsTokenListFlowUseCase,
|
||||
|
|
@ -108,8 +133,15 @@ internal class SearchModel @Inject constructor(
|
|||
stateController.update(UpdateSearchBarQueryTransformer(text))
|
||||
}
|
||||
|
||||
fun onResultMarketTokenClick() {
|
||||
fun onResultMarketTokenClick(item: MarketsListItemUM) {
|
||||
modelScope.launch(dispatchers.default) {
|
||||
val appCurrency = currentAppCurrency.value
|
||||
val input = MarketsListItemUMWithAppCurrency(
|
||||
item = item,
|
||||
appCurrencyCode = appCurrency.code,
|
||||
appCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
saveRecentSearchTokenUseCase(marketsListItemToRecentSearchTokenConverter.convert(input))
|
||||
saveSearchQueryUseCase(stateController.value.searchBar.query)
|
||||
}
|
||||
}
|
||||
|
|
@ -263,31 +295,43 @@ internal class SearchModel @Inject constructor(
|
|||
TextHintItemUM(text = hint.text)
|
||||
}.toImmutableList()
|
||||
|
||||
val appCurrency = currentAppCurrency.value
|
||||
val recentTokens = searchResult.recentTokens.map { token ->
|
||||
token.toMarketsListItemUM()
|
||||
recentSearchTokenToMarketsListItemConverter.convert(
|
||||
RecentSearchTokenWithAppCurrency(token = token, appCurrency = appCurrency),
|
||||
)
|
||||
}.toImmutableList()
|
||||
|
||||
stateController.update(UpdateHistoryTransformer(textHints, recentTokens))
|
||||
|
||||
loadRecentTokenCharts(recentTokens, appCurrency)
|
||||
}
|
||||
}.saveIn(searchResultsJob)
|
||||
}
|
||||
|
||||
private fun RecentSearchToken.toMarketsListItemUM(): MarketsListItemUM {
|
||||
return MarketsListItemUM(
|
||||
id = id,
|
||||
name = name,
|
||||
currencySymbol = symbol,
|
||||
iconUrl = imageUrl,
|
||||
ratingPosition = null,
|
||||
marketCap = null,
|
||||
price = MarketsListItemUM.Price(text = ""),
|
||||
trendPercentText = "",
|
||||
trendType = PriceChangeType.NEUTRAL,
|
||||
chartData = null,
|
||||
isUnder100kMarketCap = false,
|
||||
stakingRate = null,
|
||||
updateTimestamp = timestamp,
|
||||
private suspend fun loadRecentTokenCharts(tokens: ImmutableList<MarketsListItemUM>, appCurrency: AppCurrency) {
|
||||
coroutineScope {
|
||||
tokens.map { token ->
|
||||
async(dispatchers.io) {
|
||||
val chart = getTokenPriceChartUseCase(
|
||||
appCurrency = appCurrency,
|
||||
interval = PriceChangeInterval.H24,
|
||||
tokenId = token.id,
|
||||
tokenSymbol = token.currencySymbol,
|
||||
preview = true,
|
||||
).getOrElse { return@async }
|
||||
|
||||
val chartData = priceAndTimePointValuesConverter.convert(
|
||||
MarketChartData.Data(
|
||||
y = chart.priceY.toImmutableList(),
|
||||
x = chart.timeStamps.map { it.toBigDecimal() }.toImmutableList(),
|
||||
).sorted(),
|
||||
)
|
||||
|
||||
stateController.update(UpdateRecentTokenChartTransformer(token.id, chartData))
|
||||
}
|
||||
}.awaitAll()
|
||||
}
|
||||
}
|
||||
|
||||
private fun AccountName.toDisplayString(): String {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
package com.tangem.features.feed.model.search.converter
|
||||
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.search.model.RecentSearchToken
|
||||
import com.tangem.domain.search.model.TokenPriceChangeDirection
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class MarketsListItemUMToRecentSearchTokenConverter :
|
||||
Converter<MarketsListItemUMWithAppCurrency, RecentSearchToken> {
|
||||
|
||||
override fun convert(value: MarketsListItemUMWithAppCurrency): RecentSearchToken {
|
||||
val item = value.item
|
||||
val direction = when (item.trendType) {
|
||||
PriceChangeType.UP -> TokenPriceChangeDirection.UP
|
||||
PriceChangeType.DOWN -> TokenPriceChangeDirection.DOWN
|
||||
PriceChangeType.NEUTRAL -> TokenPriceChangeDirection.NEUTRAL
|
||||
}
|
||||
val stakingText = when (val rate = item.stakingRate) {
|
||||
is TextReference.Str -> rate.value
|
||||
is TextReference.StyledStr -> rate.value
|
||||
else -> null
|
||||
}
|
||||
return RecentSearchToken(
|
||||
id = item.id,
|
||||
name = item.name,
|
||||
symbol = item.currencySymbol,
|
||||
imageUrl = item.iconUrl,
|
||||
timestamp = item.updateTimestamp ?: System.currentTimeMillis(),
|
||||
appCurrencyCode = value.appCurrencyCode,
|
||||
appCurrencySymbol = value.appCurrencySymbol,
|
||||
price = item.price.fiatPrice,
|
||||
priceChangePercent = item.trendPercentText,
|
||||
priceChangeDirection = direction,
|
||||
marketCap = item.marketCap,
|
||||
ratingPosition = item.ratingPosition,
|
||||
isUnder100kMarketCap = item.isUnder100kMarketCap,
|
||||
stakingRateText = stakingText,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.features.feed.model.search.converter
|
||||
|
||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||
|
||||
internal data class MarketsListItemUMWithAppCurrency(
|
||||
val item: MarketsListItemUM,
|
||||
val appCurrencyCode: String,
|
||||
val appCurrencySymbol: String,
|
||||
)
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package com.tangem.features.feed.model.search.converter
|
||||
|
||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||
import com.tangem.common.ui.markets.toMarketsListItemPriceAnnotated
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.format.bigdecimal.price
|
||||
import com.tangem.domain.search.model.TokenPriceChangeDirection
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class RecentSearchTokenToMarketsListItemUMConverter :
|
||||
Converter<RecentSearchTokenWithAppCurrency, MarketsListItemUM> {
|
||||
|
||||
override fun convert(value: RecentSearchTokenWithAppCurrency): MarketsListItemUM {
|
||||
val token = value.token
|
||||
val currencyCode = token.appCurrencyCode.ifEmpty { value.appCurrency.code }
|
||||
val currencySymbol = token.appCurrencySymbol.ifEmpty { value.appCurrency.symbol }
|
||||
val trendType = when (token.priceChangeDirection) {
|
||||
TokenPriceChangeDirection.UP -> PriceChangeType.UP
|
||||
TokenPriceChangeDirection.DOWN -> PriceChangeType.DOWN
|
||||
TokenPriceChangeDirection.NEUTRAL -> PriceChangeType.NEUTRAL
|
||||
}
|
||||
val priceText = token.price.format {
|
||||
fiat(
|
||||
fiatCurrencyCode = currencyCode,
|
||||
fiatCurrencySymbol = currencySymbol,
|
||||
).price()
|
||||
}
|
||||
return MarketsListItemUM(
|
||||
id = token.id,
|
||||
name = token.name,
|
||||
currencySymbol = token.symbol,
|
||||
iconUrl = token.imageUrl,
|
||||
ratingPosition = token.ratingPosition,
|
||||
marketCap = token.marketCap,
|
||||
price = MarketsListItemUM.Price(
|
||||
text = priceText,
|
||||
annotated = token.price.toMarketsListItemPriceAnnotated(
|
||||
appCurrencyCode = currencyCode,
|
||||
appCurrencySymbol = currencySymbol,
|
||||
),
|
||||
changeType = null,
|
||||
fiatPrice = token.price,
|
||||
),
|
||||
trendPercentText = token.priceChangePercent,
|
||||
trendType = trendType,
|
||||
chartData = null,
|
||||
isUnder100kMarketCap = token.isUnder100kMarketCap,
|
||||
stakingRate = token.stakingRateText?.let { TextReference.Str(it) },
|
||||
updateTimestamp = token.timestamp,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.features.feed.model.search.converter
|
||||
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.search.model.RecentSearchToken
|
||||
|
||||
internal data class RecentSearchTokenWithAppCurrency(
|
||||
val token: RecentSearchToken,
|
||||
val appCurrency: AppCurrency,
|
||||
)
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.features.feed.model.search.state.transformers
|
||||
|
||||
import com.tangem.common.ui.charts.state.MarketChartRawData
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.feed.ui.search.state.SearchContentUM
|
||||
import com.tangem.features.feed.ui.search.state.SearchUM
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
internal class UpdateRecentTokenChartTransformer(
|
||||
private val tokenId: CryptoCurrency.RawID,
|
||||
private val chartData: MarketChartRawData,
|
||||
) : SearchUMTransformer {
|
||||
|
||||
override fun transform(prevState: SearchUM): SearchUM {
|
||||
val content = prevState.content
|
||||
if (content !is SearchContentUM.History) return prevState
|
||||
|
||||
val updatedTokens = content.recentTokens.map { token ->
|
||||
if (token.id == tokenId) {
|
||||
token.copy(chartData = chartData)
|
||||
} else {
|
||||
token
|
||||
}
|
||||
}.toImmutableList()
|
||||
|
||||
return prevState.copy(
|
||||
content = content.copy(recentTokens = updatedTokens),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ import com.tangem.core.ui.components.list.InfiniteListHandler
|
|||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.*
|
||||
import com.tangem.domain.models.earn.EarnType
|
||||
import com.tangem.features.feed.ui.earn.components.*
|
||||
import com.tangem.features.feed.ui.earn.state.*
|
||||
import com.tangem.features.feed.ui.feed.state.FeedListSearchBar
|
||||
|
|
@ -641,7 +642,8 @@ private fun previewEarnListItemUM(
|
|||
shouldShowCustomBadge = false,
|
||||
),
|
||||
earnValue = stringReference("APY 6.54%"),
|
||||
earnType = stringReference("Yield"),
|
||||
earnTypeTitle = stringReference("Yield"),
|
||||
earnType = EarnType.YIELD,
|
||||
onItemClick = {},
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.*
|
||||
import com.tangem.domain.models.earn.EarnType
|
||||
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
||||
|
||||
@Composable
|
||||
|
|
@ -95,7 +96,7 @@ private fun EarnListItemV1(item: EarnListItemUM, modifier: Modifier = Modifier)
|
|||
)
|
||||
SpacerH(2.dp)
|
||||
Text(
|
||||
text = item.earnType.resolveReference(),
|
||||
text = item.earnTypeTitle.resolveReference(),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.caption2,
|
||||
maxLines = 1,
|
||||
|
|
@ -147,6 +148,7 @@ private fun EarnListItemV2(item: EarnListItemUM, modifier: Modifier = Modifier)
|
|||
ModeBlock(
|
||||
modifier = Modifier.layoutId(layoutId = TangemRowLayoutId.END_BOTTOM),
|
||||
earnType = item.earnType,
|
||||
earnTypeTitle = item.earnTypeTitle,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
@ -200,19 +202,24 @@ private fun TokenTitle(name: String, symbol: String, modifier: Modifier = Modifi
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun ModeBlock(earnType: TextReference, modifier: Modifier = Modifier) {
|
||||
private fun ModeBlock(earnType: EarnType, earnTypeTitle: TextReference, modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_staking_new_16),
|
||||
imageVector = ImageVector.vectorResource(
|
||||
id = when (earnType) {
|
||||
EarnType.STAKING -> R.drawable.ic_staking_new_16
|
||||
EarnType.YIELD -> R.drawable.ic_yield_mode_16
|
||||
},
|
||||
),
|
||||
tint = TangemTheme.colors2.markers.iconGray,
|
||||
contentDescription = null,
|
||||
)
|
||||
Text(
|
||||
text = earnType.resolveReference(),
|
||||
text = earnTypeTitle.resolveReference(),
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
)
|
||||
|
|
@ -239,7 +246,8 @@ private fun EarnListItemPreviewV1() {
|
|||
shouldShowCustomBadge = false,
|
||||
),
|
||||
earnValue = stringReference("APY 8.50%"),
|
||||
earnType = stringReference("Yield"),
|
||||
earnTypeTitle = stringReference("Yield"),
|
||||
earnType = EarnType.YIELD,
|
||||
onItemClick = {},
|
||||
),
|
||||
)
|
||||
|
|
@ -257,7 +265,8 @@ private fun EarnListItemPreviewV1() {
|
|||
shouldShowCustomBadge = false,
|
||||
),
|
||||
earnValue = stringReference("APY 8.50%"),
|
||||
earnType = stringReference("Yield"),
|
||||
earnTypeTitle = stringReference("Yield"),
|
||||
earnType = EarnType.YIELD,
|
||||
onItemClick = {},
|
||||
),
|
||||
)
|
||||
|
|
@ -286,7 +295,8 @@ private fun EarnListItemPreviewV2() {
|
|||
shouldShowCustomBadge = false,
|
||||
),
|
||||
earnValue = stringReference("APY 8.50%"),
|
||||
earnType = stringReference("Yield"),
|
||||
earnTypeTitle = stringReference("Yield"),
|
||||
earnType = EarnType.YIELD,
|
||||
onItemClick = {},
|
||||
),
|
||||
)
|
||||
|
|
@ -305,7 +315,8 @@ private fun EarnListItemPreviewV2() {
|
|||
shouldShowCustomBadge = false,
|
||||
),
|
||||
earnValue = stringReference("APY 8.50%"),
|
||||
earnType = stringReference("Yield"),
|
||||
earnTypeTitle = stringReference("Yield"),
|
||||
earnType = EarnType.YIELD,
|
||||
onItemClick = {},
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import com.tangem.core.ui.ds.image.TangemIconUM
|
|||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.*
|
||||
import com.tangem.domain.models.earn.EarnType
|
||||
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
||||
|
||||
@Composable
|
||||
|
|
@ -161,7 +162,8 @@ private fun EarnListItemPreviewV1() {
|
|||
shouldShowCustomBadge = false,
|
||||
),
|
||||
earnValue = stringReference("APY 6.54%"),
|
||||
earnType = stringReference("Yield"),
|
||||
earnTypeTitle = stringReference("Yield"),
|
||||
earnType = EarnType.YIELD,
|
||||
onItemClick = {},
|
||||
),
|
||||
onClick = {},
|
||||
|
|
@ -187,7 +189,8 @@ private fun EarnListItemPreviewV2() {
|
|||
shouldShowCustomBadge = false,
|
||||
),
|
||||
earnValue = stringReference("APY 6.54%"),
|
||||
earnType = stringReference("Yield"),
|
||||
earnTypeTitle = stringReference("Yield"),
|
||||
earnType = EarnType.YIELD,
|
||||
onItemClick = {},
|
||||
),
|
||||
onClick = {},
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.feed.ui.earn.state
|
|||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.models.earn.EarnType
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Immutable
|
||||
|
|
@ -32,6 +33,7 @@ internal data class EarnListItemUM(
|
|||
val tokenName: TextReference,
|
||||
val currencyIconState: CurrencyIconState,
|
||||
val earnValue: TextReference,
|
||||
val earnType: TextReference,
|
||||
val earnType: EarnType,
|
||||
val earnTypeTitle: TextReference,
|
||||
val onItemClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -11,12 +11,14 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.earn.EarnType
|
||||
import com.tangem.features.feed.model.market.list.state.SortByTypeUM
|
||||
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
||||
import com.tangem.features.feed.ui.earn.state.EarnListUM
|
||||
import com.tangem.features.feed.ui.feed.components.articles.ArticleConfigUM
|
||||
import com.tangem.features.feed.ui.feed.state.*
|
||||
import kotlinx.collections.immutable.*
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
internal object FeedListPreviewDataProvider {
|
||||
|
|
@ -246,7 +248,11 @@ internal object FeedListPreviewDataProvider {
|
|||
iconUrl = null,
|
||||
ratingPosition = rating,
|
||||
marketCap = marketCap,
|
||||
price = MarketsListItemUM.Price(text = "31 285.72$"),
|
||||
price = MarketsListItemUM.Price(
|
||||
text = "31 285.72$",
|
||||
fiatPrice = BigDecimal(123123),
|
||||
annotated = stringReference("31 285.72$"),
|
||||
),
|
||||
trendPercentText = percent,
|
||||
trendType = trendType,
|
||||
chartData = MarketChartRawData(
|
||||
|
|
@ -273,7 +279,8 @@ internal object FeedListPreviewDataProvider {
|
|||
shouldShowCustomBadge = false,
|
||||
),
|
||||
earnValue = stringReference("APY 6.54%"),
|
||||
earnType = stringReference("Yield"),
|
||||
earnTypeTitle = stringReference("Yield"),
|
||||
earnType = EarnType.YIELD,
|
||||
onItemClick = {},
|
||||
)
|
||||
}.toPersistentList()
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import com.tangem.core.ui.ds.tabs.TangemSegmentedPicker
|
|||
import com.tangem.core.ui.event.EventEffect
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveAnnotatedReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
|
|
@ -182,6 +183,7 @@ private fun Header(state: MarketsTokenDetailsUM, modifier: Modifier = Modifier)
|
|||
Column(modifier = Modifier.weight(1f)) {
|
||||
TokenPriceText(
|
||||
price = state.priceText,
|
||||
priceAnnotated = state.priceAnnotated,
|
||||
triggerPriceChange = state.triggerPriceChange,
|
||||
)
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4)) {
|
||||
|
|
@ -212,6 +214,28 @@ private fun Header(state: MarketsTokenDetailsUM, modifier: Modifier = Modifier)
|
|||
|
||||
@Composable
|
||||
private fun TokenPriceText(
|
||||
price: String,
|
||||
triggerPriceChange: StateEvent<PriceChangeType>,
|
||||
priceAnnotated: TextReference,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
TokenPriceTextV2(
|
||||
priceAnnotated = priceAnnotated,
|
||||
triggerPriceChange = triggerPriceChange,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
TokenPriceTextV1(
|
||||
price = price,
|
||||
triggerPriceChange = triggerPriceChange,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenPriceTextV1(
|
||||
price: String,
|
||||
triggerPriceChange: StateEvent<PriceChangeType>,
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -244,6 +268,40 @@ private fun TokenPriceText(
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenPriceTextV2(
|
||||
priceAnnotated: TextReference,
|
||||
triggerPriceChange: StateEvent<PriceChangeType>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val growColor = TangemTheme.colors2.graphic.status.accent
|
||||
val fallColor = TangemTheme.colors2.graphic.status.warning
|
||||
val generalColor = TangemTheme.colors2.text.neutral.primary
|
||||
|
||||
val color = remember(generalColor) { Animatable(generalColor) }
|
||||
|
||||
EventEffect(triggerPriceChange) { priceChangeType ->
|
||||
val nextColor = when (priceChangeType) {
|
||||
PriceChangeType.UP,
|
||||
-> growColor
|
||||
PriceChangeType.DOWN -> fallColor
|
||||
PriceChangeType.NEUTRAL -> return@EventEffect
|
||||
}
|
||||
|
||||
color.animateTo(nextColor, snap())
|
||||
color.animateTo(generalColor, tween(durationMillis = 500))
|
||||
}
|
||||
|
||||
Text(
|
||||
text = priceAnnotated.resolveAnnotatedReference(),
|
||||
modifier = modifier,
|
||||
color = color.value,
|
||||
autoSize = TextAutoSize.StepBased(maxFontSize = TangemTheme.typography2.headingBold34.fontSize),
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography2.headingBold34,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun IntervalSelector(
|
||||
trendInterval: PriceChangeInterval,
|
||||
|
|
|
|||
|
|
@ -156,12 +156,42 @@ internal fun FDVCard(item: InfoPointUMV2.FullyDilutedValuation) {
|
|||
modifier = Modifier
|
||||
.heightIn(120.dp)
|
||||
.fillMaxWidth(),
|
||||
title = { MetricValueText(value = item.value) },
|
||||
title = {
|
||||
if (item.fullyDilutedValuationChange24 != null) {
|
||||
Row {
|
||||
MetricValueText(value = item.fullyDilutedValuationChange24)
|
||||
Text(
|
||||
modifier = Modifier.padding(TangemTheme.dimens2.x1),
|
||||
text = stringResourceSafe(R.string.markets_token_details_trading_interval),
|
||||
style = TangemTheme.typography2.captionSemibold11,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
MetricValueText(value = item.value)
|
||||
}
|
||||
},
|
||||
content = {
|
||||
Column {
|
||||
if (item.fullyDilutedValuationChange24 != null) {
|
||||
Text(
|
||||
text = item.value?.resolveReference()
|
||||
?: stringResourceSafe(R.string.token_market_metrics_no_data),
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
SpacerH(4.dp)
|
||||
}
|
||||
|
||||
InformationTextBlock(
|
||||
text = resourceReference(R.string.markets_token_details_fully_diluted_valuation),
|
||||
onInfoClick = item.onInfoClick,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -445,6 +475,7 @@ private fun MetricsCardsPreview() {
|
|||
FDVCard(
|
||||
item = InfoPointUMV2.FullyDilutedValuation(
|
||||
value = stringReference("$ 1.5 T"),
|
||||
fullyDilutedValuationChange24 = stringReference("$ 2.44 M in total"),
|
||||
onInfoClick = {},
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ internal object MarketsTokenDetailsPreview {
|
|||
onScroll = {},
|
||||
),
|
||||
onShareClick = {},
|
||||
priceAnnotated = stringReference("$0.00000000324"),
|
||||
)
|
||||
|
||||
val contentState = MarketsTokenDetailsUM(
|
||||
|
|
@ -144,5 +145,6 @@ internal object MarketsTokenDetailsPreview {
|
|||
onScroll = {},
|
||||
),
|
||||
onShareClick = {},
|
||||
priceAnnotated = stringReference("$0.00000000324"),
|
||||
)
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ import java.math.BigDecimal
|
|||
internal data class MarketsTokenDetailsUM(
|
||||
val tokenName: String,
|
||||
val priceText: String,
|
||||
val priceAnnotated: TextReference,
|
||||
val iconUrl: String?,
|
||||
val dateTimeText: TextReference,
|
||||
val priceChangePercentText: String?,
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ internal sealed interface InfoPointUMV2 {
|
|||
@Immutable
|
||||
data class FullyDilutedValuation(
|
||||
val value: TextReference?,
|
||||
val fullyDilutedValuationChange24: TextReference?,
|
||||
val onInfoClick: () -> Unit,
|
||||
) : InfoPointUMV2
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.markets.MarketsListItem
|
||||
import com.tangem.common.ui.markets.MarketsListItemPlaceholder
|
||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.SpacerW
|
||||
|
|
@ -133,7 +134,10 @@ private fun LazyListScope.searchHistoryItems(
|
|||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.searchResultsItems(results: SearchContentUM.Results, onResultMarketTokenClick: () -> Unit) {
|
||||
private fun LazyListScope.searchResultsItems(
|
||||
results: SearchContentUM.Results,
|
||||
onResultMarketTokenClick: (MarketsListItemUM) -> Unit,
|
||||
) {
|
||||
if (results.userAssets.isNotEmpty()) {
|
||||
item(key = "header_portfolio") {
|
||||
SectionHeader(title = stringResourceSafe(R.string.markets_search_portfolio_header))
|
||||
|
|
@ -163,7 +167,7 @@ private fun LazyListScope.searchResultsItems(results: SearchContentUM.Results, o
|
|||
private fun LazyListScope.marketSearchResultItems(
|
||||
market: MarketSearchResultUM.Content,
|
||||
hasUserAssetsSection: Boolean,
|
||||
onResultMarketTokenClick: () -> Unit,
|
||||
onResultMarketTokenClick: (MarketsListItemUM) -> Unit,
|
||||
) {
|
||||
if (hasUserAssetsSection) {
|
||||
item(key = "spacer_between_sections") {
|
||||
|
|
@ -185,7 +189,7 @@ private fun LazyListScope.marketSearchResultItems(
|
|||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||
),
|
||||
model = token,
|
||||
onClick = onResultMarketTokenClick,
|
||||
onClick = { onResultMarketTokenClick(token) },
|
||||
)
|
||||
}
|
||||
if (market.shouldShowUnder100kNotification) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import com.tangem.features.feed.ui.search.state.*
|
|||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.math.BigDecimal
|
||||
|
||||
/** Labeled UI state for [SearchContent] previews. */
|
||||
internal data class SearchContentPreviewScenario(
|
||||
|
|
@ -192,7 +193,11 @@ internal object SearchContentPreviewFixtures {
|
|||
iconUrl = null,
|
||||
ratingPosition = rating,
|
||||
marketCap = marketCap,
|
||||
price = MarketsListItemUM.Price(text = priceText),
|
||||
price = MarketsListItemUM.Price(
|
||||
text = priceText,
|
||||
annotated = stringReference(priceText),
|
||||
fiatPrice = BigDecimal(123123),
|
||||
),
|
||||
trendPercentText = trendText,
|
||||
trendType = trend,
|
||||
chartData = chart,
|
||||
|
|
@ -384,7 +389,7 @@ private val SearchContentPreviewCallbacks = SearchCallbacks(
|
|||
onLoadMore = {},
|
||||
onClearHintsClick = {},
|
||||
onTextHintClick = { _ -> },
|
||||
onResultMarketTokenClick = {},
|
||||
onResultMarketTokenClick = { _ -> },
|
||||
)
|
||||
|
||||
/** All [SearchContentPreviewScenario] values for the Preview Parameter dropdown in Android Studio. */
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tangem.features.feed.ui.search.state
|
||||
|
||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||
|
||||
internal data class SearchCallbacks(
|
||||
val onLoadMore: () -> Unit,
|
||||
val onClearHintsClick: () -> Unit,
|
||||
val onTextHintClick: (hint: String) -> Unit,
|
||||
val onResultMarketTokenClick: () -> Unit,
|
||||
val onResultMarketTokenClick: (MarketsListItemUM) -> Unit,
|
||||
)
|
||||
|
|
@ -81,6 +81,7 @@ dependencies {
|
|||
|
||||
/* Common */
|
||||
implementation(projects.common.ui)
|
||||
implementation(projects.common.uiMarkets)
|
||||
implementation(projects.common.uiCharts)
|
||||
implementation(projects.common.routing)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.common.routing.AppRoute
|
|||
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
|
||||
import com.tangem.common.ui.markets.toMarketsListItemPriceAnnotated
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
|
|
@ -62,6 +63,8 @@ internal class TokenMarketBlockModel @Inject constructor(
|
|||
TokenMarketBlockUM(
|
||||
currencySymbol = params.cryptoCurrency.symbol,
|
||||
currentPrice = null,
|
||||
currentPriceValue = null,
|
||||
priceAnnotated = null,
|
||||
h24Percent = null,
|
||||
priceChangeType = PriceChangeType.NEUTRAL,
|
||||
chartData = null,
|
||||
|
|
@ -86,15 +89,21 @@ internal class TokenMarketBlockModel @Inject constructor(
|
|||
h24Percent = res.priceChange,
|
||||
)
|
||||
|
||||
val appCurrency = currentAppCurrency.value
|
||||
state.value = state.value.copy(
|
||||
currentPrice = res.fiatRate.format {
|
||||
fiat(
|
||||
// TODO get currency from quotes use case [REDACTED_TASK_KEY]
|
||||
fiatCurrencyCode = currentAppCurrency.value.code,
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
// TODO get currency from quotes use case [REDACTED_TASK_KEY]
|
||||
fiatCurrencySymbol = currentAppCurrency.value.symbol,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
).price()
|
||||
},
|
||||
currentPriceValue = res.fiatRate,
|
||||
priceAnnotated = res.fiatRate.toMarketsListItemPriceAnnotated(
|
||||
appCurrencyCode = appCurrency.code,
|
||||
appCurrencySymbol = appCurrency.symbol,
|
||||
),
|
||||
h24Percent = res.priceChange.format { percent() },
|
||||
priceChangeType = PriceChangeType.fromBigDecimal(res.priceChange),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import com.tangem.core.ui.components.TextShimmer
|
|||
import com.tangem.core.ui.components.block.BlockCard
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeInPercent
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
|
@ -27,6 +29,7 @@ import com.tangem.features.markets.impl.R
|
|||
import com.tangem.features.markets.token.block.impl.model.formatter.toChartType
|
||||
import com.tangem.features.markets.token.block.impl.ui.state.TokenMarketBlockUM
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import java.math.BigDecimal
|
||||
import kotlin.random.Random
|
||||
|
||||
@Composable
|
||||
|
|
@ -47,6 +50,8 @@ internal fun TokenMarketBlockLegacy(state: TokenMarketBlockUM, modifier: Modifie
|
|||
modifier = Modifier.weight(1f),
|
||||
symbol = state.currencySymbol,
|
||||
priceText = state.currentPrice,
|
||||
priceValue = state.currentPriceValue,
|
||||
priceAnnotated = state.priceAnnotated,
|
||||
percentText = state.h24Percent,
|
||||
type = state.priceChangeType,
|
||||
)
|
||||
|
|
@ -61,11 +66,14 @@ internal fun TokenMarketBlockLegacy(state: TokenMarketBlockUM, modifier: Modifie
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun LeftSide(
|
||||
symbol: String,
|
||||
priceText: String?,
|
||||
priceValue: BigDecimal?,
|
||||
priceAnnotated: TextReference?,
|
||||
percentText: String?,
|
||||
type: PriceChangeType,
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -88,6 +96,8 @@ private fun LeftSide(
|
|||
modifier = Modifier.alignByBaseline(),
|
||||
price = priceText,
|
||||
priceChangeType = type,
|
||||
priceValue = priceValue ?: BigDecimal.ZERO,
|
||||
priceAnnotated = priceAnnotated ?: TextReference.EMPTY,
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
|
|
@ -181,6 +191,8 @@ private fun Preview() {
|
|||
priceChangeType = PriceChangeType.UP,
|
||||
chartData = data,
|
||||
onClick = {},
|
||||
currentPriceValue = BigDecimal(1234),
|
||||
priceAnnotated = stringReference("0,5$"),
|
||||
)
|
||||
|
||||
TangemThemePreview {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,14 @@ package com.tangem.features.markets.token.block.impl.ui.state
|
|||
|
||||
import com.tangem.common.ui.charts.state.MarketChartRawData
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal data class TokenMarketBlockUM(
|
||||
val currencySymbol: String,
|
||||
val currentPrice: String?,
|
||||
val currentPriceValue: BigDecimal?,
|
||||
val priceAnnotated: TextReference?,
|
||||
val h24Percent: String?,
|
||||
val priceChangeType: PriceChangeType,
|
||||
val chartData: MarketChartRawData?,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.common.ui.charts.state.MarketChartRawData
|
|||
import com.tangem.common.ui.charts.state.converter.PriceAndTimePointValuesConverter
|
||||
import com.tangem.common.ui.charts.state.sorted
|
||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||
import com.tangem.common.ui.markets.toMarketsListItemPriceAnnotated
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
|
@ -119,7 +120,12 @@ internal class SwapMarketsTokenItemConverter(
|
|||
|
||||
return MarketsListItemUM.Price(
|
||||
text = priceText,
|
||||
annotated = tokenQuotesShort.currentPrice.toMarketsListItemPriceAnnotated(
|
||||
appCurrencyCode = appCurrency.code,
|
||||
appCurrencySymbol = appCurrency.symbol,
|
||||
),
|
||||
changeType = changeType,
|
||||
fiatPrice = tokenQuotesShort.currentPrice,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.common.ui.charts.state.MarketChartRawData
|
|||
import com.tangem.common.ui.charts.state.converter.PriceAndTimePointValuesConverter
|
||||
import com.tangem.common.ui.charts.state.sorted
|
||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||
import com.tangem.common.ui.markets.toMarketsListItemPriceAnnotated
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
|
|
@ -119,7 +120,12 @@ internal class SwapMarketsTokenItemConverter(
|
|||
|
||||
return MarketsListItemUM.Price(
|
||||
text = priceText,
|
||||
annotated = tokenQuotesShort.currentPrice.toMarketsListItemPriceAnnotated(
|
||||
appCurrencyCode = appCurrency.code,
|
||||
appCurrencySymbol = appCurrency.symbol,
|
||||
),
|
||||
changeType = changeType,
|
||||
fiatPrice = tokenQuotesShort.currentPrice,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import com.tangem.feature.swap.models.market.state.SwapMarketState
|
|||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal object SwapSelectTokenPreviewProvider {
|
||||
|
||||
|
|
@ -205,7 +206,11 @@ internal object SwapSelectTokenPreviewProvider {
|
|||
iconUrl = iconUrl,
|
||||
ratingPosition = ratingPosition,
|
||||
marketCap = marketCap,
|
||||
price = MarketsListItemUM.Price(text = "31 285.72$"),
|
||||
price = MarketsListItemUM.Price(
|
||||
text = "31 285.72$",
|
||||
annotated = stringReference("31 285.72$"),
|
||||
fiatPrice = BigDecimal("123123"),
|
||||
),
|
||||
trendPercentText = "12.43%",
|
||||
trendType = trendType,
|
||||
chartData = chartData,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue