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.TangemRowContainer
|
||||||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
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.LocalIsInDarkTheme
|
||||||
import com.tangem.core.ui.res.LocalWindowSize
|
import com.tangem.core.ui.res.LocalWindowSize
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
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.test.TokenElementsTestTags
|
||||||
import com.tangem.core.ui.windowsize.WindowSizeType
|
import com.tangem.core.ui.windowsize.WindowSizeType
|
||||||
import com.tangem.utils.StringsSigns.MINUS
|
import com.tangem.utils.StringsSigns.MINUS
|
||||||
|
import java.math.BigDecimal
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -88,6 +90,8 @@ fun MarketListItemContentV2(model: MarketsListItemUM, modifier: Modifier = Modif
|
||||||
.testTag(tag = TokenElementsTestTags.TOKEN_FIAT_AMOUNT),
|
.testTag(tag = TokenElementsTestTags.TOKEN_FIAT_AMOUNT),
|
||||||
price = model.price.text,
|
price = model.price.text,
|
||||||
priceChangeType = model.price.changeType,
|
priceChangeType = model.price.changeType,
|
||||||
|
priceAnnotated = model.price.annotated,
|
||||||
|
priceValue = model.price.fiatPrice,
|
||||||
)
|
)
|
||||||
|
|
||||||
TokenSubtitle(
|
TokenSubtitle(
|
||||||
|
|
@ -127,7 +131,7 @@ private fun TokenTitle(name: String, currencySymbol: String, modifier: Modifier
|
||||||
.alignByBaseline(),
|
.alignByBaseline(),
|
||||||
text = name,
|
text = name,
|
||||||
color = TangemTheme.colors2.text.neutral.primary,
|
color = TangemTheme.colors2.text.neutral.primary,
|
||||||
style = TangemTheme.typography2.bodySemibold16,
|
style = TangemTheme.typography2.bodyMedium16,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
)
|
)
|
||||||
|
|
@ -323,12 +327,16 @@ private fun Preview(@PreviewParameter(MarketChartListItemPreviewDataProvider::cl
|
||||||
price = MarketsListItemUM.Price(
|
price = MarketsListItemUM.Price(
|
||||||
text = "0.${prices[0].first}023 $",
|
text = "0.${prices[0].first}023 $",
|
||||||
changeType = prices[0].second,
|
changeType = prices[0].second,
|
||||||
|
fiatPrice = BigDecimal(123123),
|
||||||
|
annotated = stringReference("0.${prices[0].first}023 $"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
state2 = state2.copy(
|
state2 = state2.copy(
|
||||||
price = MarketsListItemUM.Price(
|
price = MarketsListItemUM.Price(
|
||||||
text = "0.${prices[1].first}023 $",
|
text = "0.${prices[1].first}023 $",
|
||||||
changeType = prices[1].second,
|
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.components.marketprice.PriceChangeType
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
import com.tangem.core.ui.extensions.resolveReference
|
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.LocalWindowSize
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.core.ui.test.MarketsTestTags
|
import com.tangem.core.ui.test.MarketsTestTags
|
||||||
import com.tangem.core.ui.windowsize.WindowSizeType
|
import com.tangem.core.ui.windowsize.WindowSizeType
|
||||||
import com.tangem.utils.StringsSigns.MINUS
|
import com.tangem.utils.StringsSigns.MINUS
|
||||||
|
import java.math.BigDecimal
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -85,6 +87,8 @@ private fun MarketsListItemContentV1(model: MarketsListItemUM, modifier: Modifie
|
||||||
modifier = Modifier.alignByBaseline(),
|
modifier = Modifier.alignByBaseline(),
|
||||||
price = model.price.text,
|
price = model.price.text,
|
||||||
priceChangeType = model.price.changeType,
|
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(
|
price = MarketsListItemUM.Price(
|
||||||
text = "0.${prices[0].first}023 $",
|
text = "0.${prices[0].first}023 $",
|
||||||
changeType = prices[0].second,
|
changeType = prices[0].second,
|
||||||
|
fiatPrice = BigDecimal(123123),
|
||||||
|
annotated = stringReference("0.${prices[0].first}023 $"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
state2 = state2.copy(
|
state2 = state2.copy(
|
||||||
price = MarketsListItemUM.Price(
|
price = MarketsListItemUM.Price(
|
||||||
text = "0.${prices[1].first}023 $",
|
text = "0.${prices[1].first}023 $",
|
||||||
changeType = prices[1].second,
|
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.components.marketprice.PriceChangeType
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class MarketsListItemUM(
|
data class MarketsListItemUM(
|
||||||
|
|
@ -40,7 +41,9 @@ data class MarketsListItemUM(
|
||||||
@Immutable
|
@Immutable
|
||||||
data class Price(
|
data class Price(
|
||||||
val text: String,
|
val text: String,
|
||||||
|
val annotated: TextReference,
|
||||||
val changeType: PriceChangeType? = null,
|
val changeType: PriceChangeType? = null,
|
||||||
|
val fiatPrice: BigDecimal,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Suppress("NullableToStringCall")
|
@Suppress("NullableToStringCall")
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||||
import com.tangem.core.ui.extensions.stringReference
|
import com.tangem.core.ui.extensions.stringReference
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
@Suppress("MagicNumber")
|
@Suppress("MagicNumber")
|
||||||
class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvider<MarketsListItemUM>(
|
class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvider<MarketsListItemUM>(
|
||||||
|
|
@ -18,7 +19,11 @@ class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvide
|
||||||
iconUrl = "",
|
iconUrl = "",
|
||||||
ratingPosition = "1",
|
ratingPosition = "1",
|
||||||
marketCap = "$6.233 B",
|
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%",
|
trendPercentText = "12.43%",
|
||||||
trendType = PriceChangeType.UP,
|
trendType = PriceChangeType.UP,
|
||||||
chartData = MarketChartRawData(
|
chartData = MarketChartRawData(
|
||||||
|
|
@ -35,7 +40,11 @@ class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvide
|
||||||
iconUrl = null,
|
iconUrl = null,
|
||||||
ratingPosition = "2",
|
ratingPosition = "2",
|
||||||
marketCap = "$6.233 B",
|
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%",
|
trendPercentText = "12.43%",
|
||||||
trendType = PriceChangeType.NEUTRAL,
|
trendType = PriceChangeType.NEUTRAL,
|
||||||
chartData = null,
|
chartData = null,
|
||||||
|
|
@ -50,7 +59,11 @@ class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvide
|
||||||
iconUrl = null,
|
iconUrl = null,
|
||||||
ratingPosition = "10",
|
ratingPosition = "10",
|
||||||
marketCap = "$6.23348172384781234 B",
|
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%",
|
trendPercentText = "12.43%",
|
||||||
trendType = PriceChangeType.DOWN,
|
trendType = PriceChangeType.DOWN,
|
||||||
chartData = MarketChartRawData(
|
chartData = MarketChartRawData(
|
||||||
|
|
@ -67,7 +80,11 @@ class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvide
|
||||||
iconUrl = null,
|
iconUrl = null,
|
||||||
ratingPosition = "10",
|
ratingPosition = "10",
|
||||||
marketCap = 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%",
|
trendPercentText = "12.43%",
|
||||||
trendType = PriceChangeType.UP,
|
trendType = PriceChangeType.UP,
|
||||||
chartData = MarketChartRawData(
|
chartData = MarketChartRawData(
|
||||||
|
|
@ -84,7 +101,11 @@ class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvide
|
||||||
iconUrl = null,
|
iconUrl = null,
|
||||||
ratingPosition = null,
|
ratingPosition = null,
|
||||||
marketCap = "$6.233 B",
|
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%",
|
trendPercentText = "12.43%",
|
||||||
trendType = PriceChangeType.UP,
|
trendType = PriceChangeType.UP,
|
||||||
chartData = MarketChartRawData(
|
chartData = MarketChartRawData(
|
||||||
|
|
@ -101,7 +122,11 @@ class MarketChartListItemPreviewDataProvider : CollectionPreviewParameterProvide
|
||||||
iconUrl = null,
|
iconUrl = null,
|
||||||
ratingPosition = null,
|
ratingPosition = null,
|
||||||
marketCap = 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%",
|
trendPercentText = "12.43%",
|
||||||
trendType = PriceChangeType.UP,
|
trendType = PriceChangeType.UP,
|
||||||
chartData = MarketChartRawData(
|
chartData = MarketChartRawData(
|
||||||
|
|
|
||||||
|
|
@ -6,25 +6,33 @@ import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
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.style.TextOverflow
|
||||||
import androidx.compose.ui.text.withStyle
|
|
||||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
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.LocalRedesignEnabled
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text view for token price.
|
* 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.
|
* @param priceChangeType Type of the price change.
|
||||||
*/
|
*/
|
||||||
@Composable
|
@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) {
|
if (LocalRedesignEnabled.current) {
|
||||||
TokenPriceTextV2(
|
TokenPriceTextV2(
|
||||||
price = price,
|
priceValue = priceValue,
|
||||||
|
priceAnnotated = priceAnnotated,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
priceChangeType = priceChangeType,
|
priceChangeType = priceChangeType,
|
||||||
)
|
)
|
||||||
|
|
@ -76,16 +84,20 @@ private fun TokenPriceTextV1(price: String, modifier: Modifier = Modifier, price
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@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 growColor = TangemTheme.colors2.text.status.accent
|
||||||
val fallColor = TangemTheme.colors2.text.status.warning
|
val fallColor = TangemTheme.colors2.text.status.warning
|
||||||
val generalColor = TangemTheme.colors2.text.neutral.primary
|
val generalColor = TangemTheme.colors2.text.neutral.primary
|
||||||
val decimalColor = TangemTheme.colors2.text.neutral.secondary
|
|
||||||
|
|
||||||
val color = remember(generalColor) { Animatable(generalColor) }
|
val color = remember(generalColor) { Animatable(generalColor) }
|
||||||
var isAnimationSkipped by remember { mutableStateOf(false) }
|
var isAnimationSkipped by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
LaunchedEffect(price) {
|
LaunchedEffect(priceValue) {
|
||||||
if (!isAnimationSkipped) {
|
if (!isAnimationSkipped) {
|
||||||
isAnimationSkipped = true
|
isAnimationSkipped = true
|
||||||
return@LaunchedEffect
|
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(
|
Text(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
text = annotatedText,
|
text = priceAnnotated.resolveAnnotatedReference(),
|
||||||
color = color.value,
|
color = color.value,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
style = TangemTheme.typography2.bodySemibold16,
|
style = TangemTheme.typography2.bodySemibold16,
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,8 @@ data class TokenMarketInfoResponse(
|
||||||
val maxSupply: BigDecimal?,
|
val maxSupply: BigDecimal?,
|
||||||
@Json(name = "fully_diluted_valuation")
|
@Json(name = "fully_diluted_valuation")
|
||||||
val fullyDilutedValuation: BigDecimal?,
|
val fullyDilutedValuation: BigDecimal?,
|
||||||
|
@Json(name = "fully_diluted_valuation_change_24h")
|
||||||
|
val fullyDilutedValuationChange24H: BigDecimal?,
|
||||||
)
|
)
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,40 @@ fun BigDecimalFiatFormat.price(): BigDecimalFormat = BigDecimalFormat { value ->
|
||||||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol)
|
.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.
|
* 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(
|
val bodyRegular15: TextStyle = TextStyle(
|
||||||
fontFamily = fontFamily,
|
fontFamily = fontFamily,
|
||||||
fontSize = 15.sp,
|
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,
|
volume24h = volume24h,
|
||||||
maxSupply = maxSupply,
|
maxSupply = maxSupply,
|
||||||
fullyDilutedValuation = fullyDilutedValuation,
|
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.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.search.model.RecentSearchToken
|
import com.tangem.domain.search.model.RecentSearchToken
|
||||||
import com.tangem.domain.search.model.SearchTextHint
|
import com.tangem.domain.search.model.SearchTextHint
|
||||||
|
import com.tangem.domain.search.model.TokenPriceChangeDirection
|
||||||
import com.tangem.utils.converter.Converter
|
import com.tangem.utils.converter.Converter
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
internal class TextHintDTOToSearchTextHintConverter : Converter<TextHintDTO, SearchTextHint> {
|
internal class TextHintDTOToSearchTextHintConverter : Converter<TextHintDTO, SearchTextHint> {
|
||||||
override fun convert(value: TextHintDTO): SearchTextHint {
|
override fun convert(value: TextHintDTO): SearchTextHint {
|
||||||
|
|
@ -24,6 +26,15 @@ internal class RecentTokenDTOToRecentSearchTokenConverter : Converter<RecentToke
|
||||||
symbol = value.symbol,
|
symbol = value.symbol,
|
||||||
imageUrl = value.imageUrl,
|
imageUrl = value.imageUrl,
|
||||||
timestamp = value.timestamp,
|
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,
|
symbol = value.symbol,
|
||||||
imageUrl = value.imageUrl,
|
imageUrl = value.imageUrl,
|
||||||
timestamp = value.timestamp,
|
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,
|
val timestamp: Long,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param appCurrencyCode currency code (e.g. "USD") the [pricePlain] was quoted in
|
||||||
|
*/
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
internal data class RecentTokenDTO(
|
internal data class RecentTokenDTO(
|
||||||
val id: String,
|
val id: String,
|
||||||
|
|
@ -21,4 +24,13 @@ internal data class RecentTokenDTO(
|
||||||
val symbol: String,
|
val symbol: String,
|
||||||
val imageUrl: String?,
|
val imageUrl: String?,
|
||||||
val timestamp: Long,
|
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 volume24h: BigDecimal?,
|
||||||
val maxSupply: BigDecimal?,
|
val maxSupply: BigDecimal?,
|
||||||
val fullyDilutedValuation: BigDecimal?,
|
val fullyDilutedValuation: BigDecimal?,
|
||||||
|
val fullyDilutedValuationChange24: BigDecimal?,
|
||||||
)
|
)
|
||||||
|
|
||||||
data class SecurityData(
|
data class SecurityData(
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
package com.tangem.domain.search.model
|
package com.tangem.domain.search.model
|
||||||
|
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property timestamp epoch milliseconds
|
* @property timestamp epoch milliseconds
|
||||||
|
* @property price fiat quote in the app currency that was active when the token was saved
|
||||||
*/
|
*/
|
||||||
data class RecentSearchToken(
|
data class RecentSearchToken(
|
||||||
val id: CryptoCurrency.RawID,
|
val id: CryptoCurrency.RawID,
|
||||||
|
|
@ -11,4 +13,13 @@ data class RecentSearchToken(
|
||||||
val symbol: String,
|
val symbol: String,
|
||||||
val imageUrl: String?,
|
val imageUrl: String?,
|
||||||
val timestamp: Long,
|
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)),
|
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.STAKING -> TextReference.Res(R.string.common_staking)
|
||||||
EarnType.YIELD -> TextReference.Res(R.string.common_yield_mode)
|
EarnType.YIELD -> TextReference.Res(R.string.common_yield_mode)
|
||||||
},
|
},
|
||||||
|
earnType = value.earnToken.type,
|
||||||
onItemClick = { onItemClick(value) },
|
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.converter.PriceAndTimePointValuesConverter
|
||||||
import com.tangem.common.ui.charts.state.sorted
|
import com.tangem.common.ui.charts.state.sorted
|
||||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
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.components.marketprice.PriceChangeType
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
import com.tangem.core.ui.extensions.wrappedList
|
import com.tangem.core.ui.extensions.wrappedList
|
||||||
|
|
@ -110,7 +111,12 @@ internal class MarketsTokenItemConverter(
|
||||||
|
|
||||||
return MarketsListItemUM.Price(
|
return MarketsListItemUM.Price(
|
||||||
text = priceText,
|
text = priceText,
|
||||||
|
annotated = tokenQuotesShort.currentPrice.toMarketsListItemPriceAnnotated(
|
||||||
|
appCurrencyCode = appCurrency.code,
|
||||||
|
appCurrencySymbol = appCurrency.symbol,
|
||||||
|
),
|
||||||
changeType = changeType,
|
changeType = changeType,
|
||||||
|
fiatPrice = tokenQuotesShort.currentPrice,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -230,6 +230,9 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
fiatCurrencySymbol = currentAppCurrency.value.symbol,
|
fiatCurrencySymbol = currentAppCurrency.value.symbol,
|
||||||
).price()
|
).price()
|
||||||
},
|
},
|
||||||
|
priceAnnotated = params.token.tokenQuotes.currentPrice.toMarketsTokenDetailsPriceAnnotated(
|
||||||
|
currentAppCurrency.value,
|
||||||
|
),
|
||||||
dateTimeText = resourceReference(R.string.common_today),
|
dateTimeText = resourceReference(R.string.common_today),
|
||||||
priceChangePercentText = params.token.tokenQuotes.h24Percent?.format { percent() },
|
priceChangePercentText = params.token.tokenQuotes.h24Percent?.format { percent() },
|
||||||
priceChangeType = params.token.tokenQuotes.h24Percent.percentChangeType(),
|
priceChangeType = params.token.tokenQuotes.h24Percent.percentChangeType(),
|
||||||
|
|
@ -505,6 +508,9 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
fiatCurrencyCode = currentAppCurrency.value.code,
|
fiatCurrencyCode = currentAppCurrency.value.code,
|
||||||
).price()
|
).price()
|
||||||
},
|
},
|
||||||
|
priceAnnotated = newInfo.quotes.currentPrice.toMarketsTokenDetailsPriceAnnotated(
|
||||||
|
currentAppCurrency.value,
|
||||||
|
),
|
||||||
priceChangePercentText = newInfo.quotes.getFormattedPercentByInterval(
|
priceChangePercentText = newInfo.quotes.getFormattedPercentByInterval(
|
||||||
interval = marketsTokenDetailsUM.selectedInterval,
|
interval = marketsTokenDetailsUM.selectedInterval,
|
||||||
),
|
),
|
||||||
|
|
@ -604,7 +610,8 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
)
|
)
|
||||||
} ?: getDefaultDateTimeString(currentState.selectedInterval)
|
} ?: getDefaultDateTimeString(currentState.selectedInterval)
|
||||||
|
|
||||||
val priceText = (price ?: currentQuotes.value.currentPrice).format {
|
val amountForPrice = price ?: currentQuotes.value.currentPrice
|
||||||
|
val priceText = amountForPrice.format {
|
||||||
fiat(
|
fiat(
|
||||||
fiatCurrencySymbol = currentAppCurrency.value.symbol,
|
fiatCurrencySymbol = currentAppCurrency.value.symbol,
|
||||||
fiatCurrencyCode = currentAppCurrency.value.code,
|
fiatCurrencyCode = currentAppCurrency.value.code,
|
||||||
|
|
@ -625,6 +632,7 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
||||||
isMarkerSet = markerTimestamp != null,
|
isMarkerSet = markerTimestamp != null,
|
||||||
dateTimeText = dateTimeText,
|
dateTimeText = dateTimeText,
|
||||||
priceText = priceText,
|
priceText = priceText,
|
||||||
|
priceAnnotated = amountForPrice.toMarketsTokenDetailsPriceAnnotated(currentAppCurrency.value),
|
||||||
priceChangePercentText = percentText,
|
priceChangePercentText = percentText,
|
||||||
priceChangeType = percent.percentChangeType(),
|
priceChangeType = percent.percentChangeType(),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,10 +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.*
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
|
||||||
import com.tangem.core.ui.extensions.stringReference
|
|
||||||
import com.tangem.core.ui.extensions.wrappedList
|
|
||||||
import com.tangem.core.ui.format.bigdecimal.compact
|
import com.tangem.core.ui.format.bigdecimal.compact
|
||||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||||
|
|
@ -188,6 +185,7 @@ internal class MetricsConverter(
|
||||||
formatArgs = wrappedList(formatted),
|
formatArgs = wrappedList(formatted),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
fullyDilutedValuationChange24 = fullyDilutedValuationChange24?.convertChange(),
|
||||||
onInfoClick = {
|
onInfoClick = {
|
||||||
onInfoClick(
|
onInfoClick(
|
||||||
InfoBottomSheetContent(
|
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")
|
@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 null
|
if (volume24h == null || marketCap == null || marketCap == BigDecimal.ZERO) return null
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
package com.tangem.features.feed.model.market.details.formatter
|
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.common.ui.charts.state.MarketChartLook
|
||||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||||
import com.tangem.core.ui.format.bigdecimal.format
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
import com.tangem.core.ui.format.bigdecimal.getFiatPriceAmountWithScale
|
import com.tangem.core.ui.format.bigdecimal.*
|
||||||
import com.tangem.core.ui.format.bigdecimal.percent
|
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.PriceChangeInterval
|
||||||
import com.tangem.domain.markets.TokenQuotes
|
import com.tangem.domain.markets.TokenQuotes
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
@ -74,3 +76,13 @@ internal fun PriceChangeType.toChartType(): MarketChartLook.Type {
|
||||||
PriceChangeType.NEUTRAL -> MarketChartLook.Type.Neutral
|
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,
|
fiatCurrencyCode = currentAppCurrency().code,
|
||||||
).price()
|
).price()
|
||||||
},
|
},
|
||||||
|
priceAnnotated = newQuotes.currentPrice.toMarketsTokenDetailsPriceAnnotated(currentAppCurrency()),
|
||||||
priceChangePercentText = newQuotes.getFormattedPercentByInterval(
|
priceChangePercentText = newQuotes.getFormattedPercentByInterval(
|
||||||
interval = stateToUpdate.selectedInterval,
|
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.converter.PriceAndTimePointValuesConverter
|
||||||
import com.tangem.common.ui.charts.state.sorted
|
import com.tangem.common.ui.charts.state.sorted
|
||||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
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.components.marketprice.PriceChangeType
|
||||||
import com.tangem.core.ui.format.bigdecimal.*
|
import com.tangem.core.ui.format.bigdecimal.*
|
||||||
import com.tangem.data.common.currency.getTokenIconUrlFromDefaultHost
|
import com.tangem.data.common.currency.getTokenIconUrlFromDefaultHost
|
||||||
|
|
@ -68,7 +69,12 @@ internal class RelatedTokenConverter(private val appCurrency: AppCurrency) :
|
||||||
|
|
||||||
return MarketsListItemUM.Price(
|
return MarketsListItemUM.Price(
|
||||||
text = priceText,
|
text = priceText,
|
||||||
|
annotated = tokenInfo.quotes.currentPrice.toMarketsListItemPriceAnnotated(
|
||||||
|
appCurrencyCode = appCurrency.code,
|
||||||
|
appCurrencySymbol = appCurrency.symbol,
|
||||||
|
),
|
||||||
changeType = null,
|
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.di.ModelScoped
|
||||||
import com.tangem.core.decompose.model.Model
|
import com.tangem.core.decompose.model.Model
|
||||||
import com.tangem.core.decompose.model.ParamsContainer
|
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.GetSelectedAppCurrencyUseCase
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
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.GetMarketsTokenListFlowUseCase
|
||||||
|
import com.tangem.domain.markets.GetTokenPriceChartUseCase
|
||||||
|
import com.tangem.domain.markets.PriceChangeInterval
|
||||||
import com.tangem.domain.models.account.AccountName
|
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.ClearSearchHistoryUseCase
|
||||||
import com.tangem.domain.search.usecase.GetSearchResultsUseCase
|
import com.tangem.domain.search.usecase.GetSearchResultsUseCase
|
||||||
|
import com.tangem.domain.search.usecase.SaveRecentSearchTokenUseCase
|
||||||
import com.tangem.domain.search.usecase.SaveSearchQueryUseCase
|
import com.tangem.domain.search.usecase.SaveSearchQueryUseCase
|
||||||
import com.tangem.features.feed.components.search.DefaultSearchComponent
|
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.MarketsListUM
|
||||||
import com.tangem.features.feed.model.market.list.state.SortByTypeUM
|
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.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.SearchStateController
|
||||||
import com.tangem.features.feed.model.search.state.transformers.*
|
import com.tangem.features.feed.model.search.state.transformers.*
|
||||||
import com.tangem.features.feed.ui.search.state.*
|
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.ImmutableList
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.awaitAll
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -45,7 +56,9 @@ internal class SearchModel @Inject constructor(
|
||||||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||||
private val getSearchResultsUseCase: GetSearchResultsUseCase,
|
private val getSearchResultsUseCase: GetSearchResultsUseCase,
|
||||||
private val saveSearchQueryUseCase: SaveSearchQueryUseCase,
|
private val saveSearchQueryUseCase: SaveSearchQueryUseCase,
|
||||||
|
private val saveRecentSearchTokenUseCase: SaveRecentSearchTokenUseCase,
|
||||||
private val clearSearchHistoryUseCase: ClearSearchHistoryUseCase,
|
private val clearSearchHistoryUseCase: ClearSearchHistoryUseCase,
|
||||||
|
private val getTokenPriceChartUseCase: GetTokenPriceChartUseCase,
|
||||||
private val stateController: SearchStateController,
|
private val stateController: SearchStateController,
|
||||||
) : Model() {
|
) : Model() {
|
||||||
|
|
||||||
|
|
@ -64,6 +77,18 @@ internal class SearchModel @Inject constructor(
|
||||||
initialValue = AppCurrency.Default,
|
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 {
|
private val searchMarketsListManager by lazy {
|
||||||
MarketsListBatchFlowManager(
|
MarketsListBatchFlowManager(
|
||||||
getMarketsTokenListFlowUseCase = getMarketsTokenListFlowUseCase,
|
getMarketsTokenListFlowUseCase = getMarketsTokenListFlowUseCase,
|
||||||
|
|
@ -108,8 +133,15 @@ internal class SearchModel @Inject constructor(
|
||||||
stateController.update(UpdateSearchBarQueryTransformer(text))
|
stateController.update(UpdateSearchBarQueryTransformer(text))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onResultMarketTokenClick() {
|
fun onResultMarketTokenClick(item: MarketsListItemUM) {
|
||||||
modelScope.launch(dispatchers.default) {
|
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)
|
saveSearchQueryUseCase(stateController.value.searchBar.query)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -263,31 +295,43 @@ internal class SearchModel @Inject constructor(
|
||||||
TextHintItemUM(text = hint.text)
|
TextHintItemUM(text = hint.text)
|
||||||
}.toImmutableList()
|
}.toImmutableList()
|
||||||
|
|
||||||
|
val appCurrency = currentAppCurrency.value
|
||||||
val recentTokens = searchResult.recentTokens.map { token ->
|
val recentTokens = searchResult.recentTokens.map { token ->
|
||||||
token.toMarketsListItemUM()
|
recentSearchTokenToMarketsListItemConverter.convert(
|
||||||
|
RecentSearchTokenWithAppCurrency(token = token, appCurrency = appCurrency),
|
||||||
|
)
|
||||||
}.toImmutableList()
|
}.toImmutableList()
|
||||||
|
|
||||||
stateController.update(UpdateHistoryTransformer(textHints, recentTokens))
|
stateController.update(UpdateHistoryTransformer(textHints, recentTokens))
|
||||||
|
|
||||||
|
loadRecentTokenCharts(recentTokens, appCurrency)
|
||||||
}
|
}
|
||||||
}.saveIn(searchResultsJob)
|
}.saveIn(searchResultsJob)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun RecentSearchToken.toMarketsListItemUM(): MarketsListItemUM {
|
private suspend fun loadRecentTokenCharts(tokens: ImmutableList<MarketsListItemUM>, appCurrency: AppCurrency) {
|
||||||
return MarketsListItemUM(
|
coroutineScope {
|
||||||
id = id,
|
tokens.map { token ->
|
||||||
name = name,
|
async(dispatchers.io) {
|
||||||
currencySymbol = symbol,
|
val chart = getTokenPriceChartUseCase(
|
||||||
iconUrl = imageUrl,
|
appCurrency = appCurrency,
|
||||||
ratingPosition = null,
|
interval = PriceChangeInterval.H24,
|
||||||
marketCap = null,
|
tokenId = token.id,
|
||||||
price = MarketsListItemUM.Price(text = ""),
|
tokenSymbol = token.currencySymbol,
|
||||||
trendPercentText = "",
|
preview = true,
|
||||||
trendType = PriceChangeType.NEUTRAL,
|
).getOrElse { return@async }
|
||||||
chartData = null,
|
|
||||||
isUnder100kMarketCap = false,
|
val chartData = priceAndTimePointValuesConverter.convert(
|
||||||
stakingRate = null,
|
MarketChartData.Data(
|
||||||
updateTimestamp = timestamp,
|
y = chart.priceY.toImmutableList(),
|
||||||
)
|
x = chart.timeStamps.map { it.toBigDecimal() }.toImmutableList(),
|
||||||
|
).sorted(),
|
||||||
|
)
|
||||||
|
|
||||||
|
stateController.update(UpdateRecentTokenChartTransformer(token.id, chartData))
|
||||||
|
}
|
||||||
|
}.awaitAll()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun AccountName.toDisplayString(): String {
|
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.decorations.roundedShapeItemDecoration
|
||||||
import com.tangem.core.ui.extensions.*
|
import com.tangem.core.ui.extensions.*
|
||||||
import com.tangem.core.ui.res.*
|
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.components.*
|
||||||
import com.tangem.features.feed.ui.earn.state.*
|
import com.tangem.features.feed.ui.earn.state.*
|
||||||
import com.tangem.features.feed.ui.feed.state.FeedListSearchBar
|
import com.tangem.features.feed.ui.feed.state.FeedListSearchBar
|
||||||
|
|
@ -641,7 +642,8 @@ private fun previewEarnListItemUM(
|
||||||
shouldShowCustomBadge = false,
|
shouldShowCustomBadge = false,
|
||||||
),
|
),
|
||||||
earnValue = stringReference("APY 6.54%"),
|
earnValue = stringReference("APY 6.54%"),
|
||||||
earnType = stringReference("Yield"),
|
earnTypeTitle = stringReference("Yield"),
|
||||||
|
earnType = EarnType.YIELD,
|
||||||
onItemClick = {},
|
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.resolveReference
|
||||||
import com.tangem.core.ui.extensions.stringReference
|
import com.tangem.core.ui.extensions.stringReference
|
||||||
import com.tangem.core.ui.res.*
|
import com.tangem.core.ui.res.*
|
||||||
|
import com.tangem.domain.models.earn.EarnType
|
||||||
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -95,7 +96,7 @@ private fun EarnListItemV1(item: EarnListItemUM, modifier: Modifier = Modifier)
|
||||||
)
|
)
|
||||||
SpacerH(2.dp)
|
SpacerH(2.dp)
|
||||||
Text(
|
Text(
|
||||||
text = item.earnType.resolveReference(),
|
text = item.earnTypeTitle.resolveReference(),
|
||||||
color = TangemTheme.colors.text.tertiary,
|
color = TangemTheme.colors.text.tertiary,
|
||||||
style = TangemTheme.typography.caption2,
|
style = TangemTheme.typography.caption2,
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
|
|
@ -147,6 +148,7 @@ private fun EarnListItemV2(item: EarnListItemUM, modifier: Modifier = Modifier)
|
||||||
ModeBlock(
|
ModeBlock(
|
||||||
modifier = Modifier.layoutId(layoutId = TangemRowLayoutId.END_BOTTOM),
|
modifier = Modifier.layoutId(layoutId = TangemRowLayoutId.END_BOTTOM),
|
||||||
earnType = item.earnType,
|
earnType = item.earnType,
|
||||||
|
earnTypeTitle = item.earnTypeTitle,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -200,19 +202,24 @@ private fun TokenTitle(name: String, symbol: String, modifier: Modifier = Modifi
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ModeBlock(earnType: TextReference, modifier: Modifier = Modifier) {
|
private fun ModeBlock(earnType: EarnType, earnTypeTitle: TextReference, modifier: Modifier = Modifier) {
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||||
) {
|
) {
|
||||||
Icon(
|
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,
|
tint = TangemTheme.colors2.markers.iconGray,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = earnType.resolveReference(),
|
text = earnTypeTitle.resolveReference(),
|
||||||
style = TangemTheme.typography2.captionSemibold12,
|
style = TangemTheme.typography2.captionSemibold12,
|
||||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||||
)
|
)
|
||||||
|
|
@ -239,7 +246,8 @@ private fun EarnListItemPreviewV1() {
|
||||||
shouldShowCustomBadge = false,
|
shouldShowCustomBadge = false,
|
||||||
),
|
),
|
||||||
earnValue = stringReference("APY 8.50%"),
|
earnValue = stringReference("APY 8.50%"),
|
||||||
earnType = stringReference("Yield"),
|
earnTypeTitle = stringReference("Yield"),
|
||||||
|
earnType = EarnType.YIELD,
|
||||||
onItemClick = {},
|
onItemClick = {},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -257,7 +265,8 @@ private fun EarnListItemPreviewV1() {
|
||||||
shouldShowCustomBadge = false,
|
shouldShowCustomBadge = false,
|
||||||
),
|
),
|
||||||
earnValue = stringReference("APY 8.50%"),
|
earnValue = stringReference("APY 8.50%"),
|
||||||
earnType = stringReference("Yield"),
|
earnTypeTitle = stringReference("Yield"),
|
||||||
|
earnType = EarnType.YIELD,
|
||||||
onItemClick = {},
|
onItemClick = {},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -286,7 +295,8 @@ private fun EarnListItemPreviewV2() {
|
||||||
shouldShowCustomBadge = false,
|
shouldShowCustomBadge = false,
|
||||||
),
|
),
|
||||||
earnValue = stringReference("APY 8.50%"),
|
earnValue = stringReference("APY 8.50%"),
|
||||||
earnType = stringReference("Yield"),
|
earnTypeTitle = stringReference("Yield"),
|
||||||
|
earnType = EarnType.YIELD,
|
||||||
onItemClick = {},
|
onItemClick = {},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -305,7 +315,8 @@ private fun EarnListItemPreviewV2() {
|
||||||
shouldShowCustomBadge = false,
|
shouldShowCustomBadge = false,
|
||||||
),
|
),
|
||||||
earnValue = stringReference("APY 8.50%"),
|
earnValue = stringReference("APY 8.50%"),
|
||||||
earnType = stringReference("Yield"),
|
earnTypeTitle = stringReference("Yield"),
|
||||||
|
earnType = EarnType.YIELD,
|
||||||
onItemClick = {},
|
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.resolveReference
|
||||||
import com.tangem.core.ui.extensions.stringReference
|
import com.tangem.core.ui.extensions.stringReference
|
||||||
import com.tangem.core.ui.res.*
|
import com.tangem.core.ui.res.*
|
||||||
|
import com.tangem.domain.models.earn.EarnType
|
||||||
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -161,7 +162,8 @@ private fun EarnListItemPreviewV1() {
|
||||||
shouldShowCustomBadge = false,
|
shouldShowCustomBadge = false,
|
||||||
),
|
),
|
||||||
earnValue = stringReference("APY 6.54%"),
|
earnValue = stringReference("APY 6.54%"),
|
||||||
earnType = stringReference("Yield"),
|
earnTypeTitle = stringReference("Yield"),
|
||||||
|
earnType = EarnType.YIELD,
|
||||||
onItemClick = {},
|
onItemClick = {},
|
||||||
),
|
),
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
|
@ -187,7 +189,8 @@ private fun EarnListItemPreviewV2() {
|
||||||
shouldShowCustomBadge = false,
|
shouldShowCustomBadge = false,
|
||||||
),
|
),
|
||||||
earnValue = stringReference("APY 6.54%"),
|
earnValue = stringReference("APY 6.54%"),
|
||||||
earnType = stringReference("Yield"),
|
earnTypeTitle = stringReference("Yield"),
|
||||||
|
earnType = EarnType.YIELD,
|
||||||
onItemClick = {},
|
onItemClick = {},
|
||||||
),
|
),
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.feed.ui.earn.state
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
|
import com.tangem.domain.models.earn.EarnType
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
|
|
@ -32,6 +33,7 @@ internal data class EarnListItemUM(
|
||||||
val tokenName: TextReference,
|
val tokenName: TextReference,
|
||||||
val currencyIconState: CurrencyIconState,
|
val currencyIconState: CurrencyIconState,
|
||||||
val earnValue: TextReference,
|
val earnValue: TextReference,
|
||||||
val earnType: TextReference,
|
val earnType: EarnType,
|
||||||
|
val earnTypeTitle: TextReference,
|
||||||
val onItemClick: () -> Unit,
|
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.extensions.stringReference
|
||||||
import com.tangem.core.ui.res.TangemColorPalette
|
import com.tangem.core.ui.res.TangemColorPalette
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
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.model.market.list.state.SortByTypeUM
|
||||||
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
|
||||||
import com.tangem.features.feed.ui.earn.state.EarnListUM
|
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.components.articles.ArticleConfigUM
|
||||||
import com.tangem.features.feed.ui.feed.state.*
|
import com.tangem.features.feed.ui.feed.state.*
|
||||||
import kotlinx.collections.immutable.*
|
import kotlinx.collections.immutable.*
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
@Suppress("MagicNumber")
|
@Suppress("MagicNumber")
|
||||||
internal object FeedListPreviewDataProvider {
|
internal object FeedListPreviewDataProvider {
|
||||||
|
|
@ -246,7 +248,11 @@ internal object FeedListPreviewDataProvider {
|
||||||
iconUrl = null,
|
iconUrl = null,
|
||||||
ratingPosition = rating,
|
ratingPosition = rating,
|
||||||
marketCap = marketCap,
|
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,
|
trendPercentText = percent,
|
||||||
trendType = trendType,
|
trendType = trendType,
|
||||||
chartData = MarketChartRawData(
|
chartData = MarketChartRawData(
|
||||||
|
|
@ -273,7 +279,8 @@ internal object FeedListPreviewDataProvider {
|
||||||
shouldShowCustomBadge = false,
|
shouldShowCustomBadge = false,
|
||||||
),
|
),
|
||||||
earnValue = stringReference("APY 6.54%"),
|
earnValue = stringReference("APY 6.54%"),
|
||||||
earnType = stringReference("Yield"),
|
earnTypeTitle = stringReference("Yield"),
|
||||||
|
earnType = EarnType.YIELD,
|
||||||
onItemClick = {},
|
onItemClick = {},
|
||||||
)
|
)
|
||||||
}.toPersistentList()
|
}.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.EventEffect
|
||||||
import com.tangem.core.ui.event.StateEvent
|
import com.tangem.core.ui.event.StateEvent
|
||||||
import com.tangem.core.ui.extensions.TextReference
|
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.resolveReference
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||||
|
|
@ -182,6 +183,7 @@ private fun Header(state: MarketsTokenDetailsUM, modifier: Modifier = Modifier)
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
TokenPriceText(
|
TokenPriceText(
|
||||||
price = state.priceText,
|
price = state.priceText,
|
||||||
|
priceAnnotated = state.priceAnnotated,
|
||||||
triggerPriceChange = state.triggerPriceChange,
|
triggerPriceChange = state.triggerPriceChange,
|
||||||
)
|
)
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4)) {
|
||||||
|
|
@ -212,6 +214,28 @@ private fun Header(state: MarketsTokenDetailsUM, modifier: Modifier = Modifier)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun TokenPriceText(
|
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,
|
price: String,
|
||||||
triggerPriceChange: StateEvent<PriceChangeType>,
|
triggerPriceChange: StateEvent<PriceChangeType>,
|
||||||
modifier: Modifier = Modifier,
|
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
|
@Composable
|
||||||
private fun IntervalSelector(
|
private fun IntervalSelector(
|
||||||
trendInterval: PriceChangeInterval,
|
trendInterval: PriceChangeInterval,
|
||||||
|
|
|
||||||
|
|
@ -156,12 +156,42 @@ internal fun FDVCard(item: InfoPointUMV2.FullyDilutedValuation) {
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.heightIn(120.dp)
|
.heightIn(120.dp)
|
||||||
.fillMaxWidth(),
|
.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 = {
|
content = {
|
||||||
InformationTextBlock(
|
Column {
|
||||||
text = resourceReference(R.string.markets_token_details_fully_diluted_valuation),
|
if (item.fullyDilutedValuationChange24 != null) {
|
||||||
onInfoClick = item.onInfoClick,
|
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(
|
FDVCard(
|
||||||
item = InfoPointUMV2.FullyDilutedValuation(
|
item = InfoPointUMV2.FullyDilutedValuation(
|
||||||
value = stringReference("$ 1.5 T"),
|
value = stringReference("$ 1.5 T"),
|
||||||
|
fullyDilutedValuationChange24 = stringReference("$ 2.44 M in total"),
|
||||||
onInfoClick = {},
|
onInfoClick = {},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ internal object MarketsTokenDetailsPreview {
|
||||||
onScroll = {},
|
onScroll = {},
|
||||||
),
|
),
|
||||||
onShareClick = {},
|
onShareClick = {},
|
||||||
|
priceAnnotated = stringReference("$0.00000000324"),
|
||||||
)
|
)
|
||||||
|
|
||||||
val contentState = MarketsTokenDetailsUM(
|
val contentState = MarketsTokenDetailsUM(
|
||||||
|
|
@ -144,5 +145,6 @@ internal object MarketsTokenDetailsPreview {
|
||||||
onScroll = {},
|
onScroll = {},
|
||||||
),
|
),
|
||||||
onShareClick = {},
|
onShareClick = {},
|
||||||
|
priceAnnotated = stringReference("$0.00000000324"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -14,6 +14,7 @@ import java.math.BigDecimal
|
||||||
internal data class MarketsTokenDetailsUM(
|
internal data class MarketsTokenDetailsUM(
|
||||||
val tokenName: String,
|
val tokenName: String,
|
||||||
val priceText: String,
|
val priceText: String,
|
||||||
|
val priceAnnotated: TextReference,
|
||||||
val iconUrl: String?,
|
val iconUrl: String?,
|
||||||
val dateTimeText: TextReference,
|
val dateTimeText: TextReference,
|
||||||
val priceChangePercentText: String?,
|
val priceChangePercentText: String?,
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ internal sealed interface InfoPointUMV2 {
|
||||||
@Immutable
|
@Immutable
|
||||||
data class FullyDilutedValuation(
|
data class FullyDilutedValuation(
|
||||||
val value: TextReference?,
|
val value: TextReference?,
|
||||||
|
val fullyDilutedValuationChange24: TextReference?,
|
||||||
val onInfoClick: () -> Unit,
|
val onInfoClick: () -> Unit,
|
||||||
) : InfoPointUMV2
|
) : InfoPointUMV2
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.tangem.common.ui.markets.MarketsListItem
|
import com.tangem.common.ui.markets.MarketsListItem
|
||||||
import com.tangem.common.ui.markets.MarketsListItemPlaceholder
|
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.R
|
||||||
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.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()) {
|
if (results.userAssets.isNotEmpty()) {
|
||||||
item(key = "header_portfolio") {
|
item(key = "header_portfolio") {
|
||||||
SectionHeader(title = stringResourceSafe(R.string.markets_search_portfolio_header))
|
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(
|
private fun LazyListScope.marketSearchResultItems(
|
||||||
market: MarketSearchResultUM.Content,
|
market: MarketSearchResultUM.Content,
|
||||||
hasUserAssetsSection: Boolean,
|
hasUserAssetsSection: Boolean,
|
||||||
onResultMarketTokenClick: () -> Unit,
|
onResultMarketTokenClick: (MarketsListItemUM) -> Unit,
|
||||||
) {
|
) {
|
||||||
if (hasUserAssetsSection) {
|
if (hasUserAssetsSection) {
|
||||||
item(key = "spacer_between_sections") {
|
item(key = "spacer_between_sections") {
|
||||||
|
|
@ -185,7 +189,7 @@ private fun LazyListScope.marketSearchResultItems(
|
||||||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||||
),
|
),
|
||||||
model = token,
|
model = token,
|
||||||
onClick = onResultMarketTokenClick,
|
onClick = { onResultMarketTokenClick(token) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (market.shouldShowUnder100kNotification) {
|
if (market.shouldShowUnder100kNotification) {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import com.tangem.features.feed.ui.search.state.*
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
/** Labeled UI state for [SearchContent] previews. */
|
/** Labeled UI state for [SearchContent] previews. */
|
||||||
internal data class SearchContentPreviewScenario(
|
internal data class SearchContentPreviewScenario(
|
||||||
|
|
@ -192,7 +193,11 @@ internal object SearchContentPreviewFixtures {
|
||||||
iconUrl = null,
|
iconUrl = null,
|
||||||
ratingPosition = rating,
|
ratingPosition = rating,
|
||||||
marketCap = marketCap,
|
marketCap = marketCap,
|
||||||
price = MarketsListItemUM.Price(text = priceText),
|
price = MarketsListItemUM.Price(
|
||||||
|
text = priceText,
|
||||||
|
annotated = stringReference(priceText),
|
||||||
|
fiatPrice = BigDecimal(123123),
|
||||||
|
),
|
||||||
trendPercentText = trendText,
|
trendPercentText = trendText,
|
||||||
trendType = trend,
|
trendType = trend,
|
||||||
chartData = chart,
|
chartData = chart,
|
||||||
|
|
@ -384,7 +389,7 @@ private val SearchContentPreviewCallbacks = SearchCallbacks(
|
||||||
onLoadMore = {},
|
onLoadMore = {},
|
||||||
onClearHintsClick = {},
|
onClearHintsClick = {},
|
||||||
onTextHintClick = { _ -> },
|
onTextHintClick = { _ -> },
|
||||||
onResultMarketTokenClick = {},
|
onResultMarketTokenClick = { _ -> },
|
||||||
)
|
)
|
||||||
|
|
||||||
/** All [SearchContentPreviewScenario] values for the Preview Parameter dropdown in Android Studio. */
|
/** All [SearchContentPreviewScenario] values for the Preview Parameter dropdown in Android Studio. */
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
package com.tangem.features.feed.ui.search.state
|
package com.tangem.features.feed.ui.search.state
|
||||||
|
|
||||||
|
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
||||||
|
|
||||||
internal data class SearchCallbacks(
|
internal data class SearchCallbacks(
|
||||||
val onLoadMore: () -> Unit,
|
val onLoadMore: () -> Unit,
|
||||||
val onClearHintsClick: () -> Unit,
|
val onClearHintsClick: () -> Unit,
|
||||||
val onTextHintClick: (hint: String) -> Unit,
|
val onTextHintClick: (hint: String) -> Unit,
|
||||||
val onResultMarketTokenClick: () -> Unit,
|
val onResultMarketTokenClick: (MarketsListItemUM) -> Unit,
|
||||||
)
|
)
|
||||||
|
|
@ -81,6 +81,7 @@ dependencies {
|
||||||
|
|
||||||
/* Common */
|
/* Common */
|
||||||
implementation(projects.common.ui)
|
implementation(projects.common.ui)
|
||||||
|
implementation(projects.common.uiMarkets)
|
||||||
implementation(projects.common.uiCharts)
|
implementation(projects.common.uiCharts)
|
||||||
implementation(projects.common.routing)
|
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.MarketChartData
|
||||||
import com.tangem.common.ui.charts.state.converter.PriceAndTimePointValuesConverter
|
import com.tangem.common.ui.charts.state.converter.PriceAndTimePointValuesConverter
|
||||||
import com.tangem.common.ui.charts.state.sorted
|
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.di.ModelScoped
|
||||||
import com.tangem.core.decompose.model.Model
|
import com.tangem.core.decompose.model.Model
|
||||||
import com.tangem.core.decompose.model.ParamsContainer
|
import com.tangem.core.decompose.model.ParamsContainer
|
||||||
|
|
@ -62,6 +63,8 @@ internal class TokenMarketBlockModel @Inject constructor(
|
||||||
TokenMarketBlockUM(
|
TokenMarketBlockUM(
|
||||||
currencySymbol = params.cryptoCurrency.symbol,
|
currencySymbol = params.cryptoCurrency.symbol,
|
||||||
currentPrice = null,
|
currentPrice = null,
|
||||||
|
currentPriceValue = null,
|
||||||
|
priceAnnotated = null,
|
||||||
h24Percent = null,
|
h24Percent = null,
|
||||||
priceChangeType = PriceChangeType.NEUTRAL,
|
priceChangeType = PriceChangeType.NEUTRAL,
|
||||||
chartData = null,
|
chartData = null,
|
||||||
|
|
@ -86,15 +89,21 @@ internal class TokenMarketBlockModel @Inject constructor(
|
||||||
h24Percent = res.priceChange,
|
h24Percent = res.priceChange,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val appCurrency = currentAppCurrency.value
|
||||||
state.value = state.value.copy(
|
state.value = state.value.copy(
|
||||||
currentPrice = res.fiatRate.format {
|
currentPrice = res.fiatRate.format {
|
||||||
fiat(
|
fiat(
|
||||||
// TODO get currency from quotes use case [REDACTED_TASK_KEY]
|
// 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]
|
// TODO get currency from quotes use case [REDACTED_TASK_KEY]
|
||||||
fiatCurrencySymbol = currentAppCurrency.value.symbol,
|
fiatCurrencySymbol = appCurrency.symbol,
|
||||||
).price()
|
).price()
|
||||||
},
|
},
|
||||||
|
currentPriceValue = res.fiatRate,
|
||||||
|
priceAnnotated = res.fiatRate.toMarketsListItemPriceAnnotated(
|
||||||
|
appCurrencyCode = appCurrency.code,
|
||||||
|
appCurrencySymbol = appCurrency.symbol,
|
||||||
|
),
|
||||||
h24Percent = res.priceChange.format { percent() },
|
h24Percent = res.priceChange.format { percent() },
|
||||||
priceChangeType = PriceChangeType.fromBigDecimal(res.priceChange),
|
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.block.BlockCard
|
||||||
import com.tangem.core.ui.components.marketprice.PriceChangeInPercent
|
import com.tangem.core.ui.components.marketprice.PriceChangeInPercent
|
||||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
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.extensions.stringResourceSafe
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.core.ui.res.TangemThemePreview
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
|
|
@ -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.model.formatter.toChartType
|
||||||
import com.tangem.features.markets.token.block.impl.ui.state.TokenMarketBlockUM
|
import com.tangem.features.markets.token.block.impl.ui.state.TokenMarketBlockUM
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
import java.math.BigDecimal
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -47,6 +50,8 @@ internal fun TokenMarketBlockLegacy(state: TokenMarketBlockUM, modifier: Modifie
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
symbol = state.currencySymbol,
|
symbol = state.currencySymbol,
|
||||||
priceText = state.currentPrice,
|
priceText = state.currentPrice,
|
||||||
|
priceValue = state.currentPriceValue,
|
||||||
|
priceAnnotated = state.priceAnnotated,
|
||||||
percentText = state.h24Percent,
|
percentText = state.h24Percent,
|
||||||
type = state.priceChangeType,
|
type = state.priceChangeType,
|
||||||
)
|
)
|
||||||
|
|
@ -61,11 +66,14 @@ internal fun TokenMarketBlockLegacy(state: TokenMarketBlockUM, modifier: Modifie
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("LongParameterList")
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun LeftSide(
|
private fun LeftSide(
|
||||||
symbol: String,
|
symbol: String,
|
||||||
priceText: String?,
|
priceText: String?,
|
||||||
|
priceValue: BigDecimal?,
|
||||||
|
priceAnnotated: TextReference?,
|
||||||
percentText: String?,
|
percentText: String?,
|
||||||
type: PriceChangeType,
|
type: PriceChangeType,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
|
@ -88,6 +96,8 @@ private fun LeftSide(
|
||||||
modifier = Modifier.alignByBaseline(),
|
modifier = Modifier.alignByBaseline(),
|
||||||
price = priceText,
|
price = priceText,
|
||||||
priceChangeType = type,
|
priceChangeType = type,
|
||||||
|
priceValue = priceValue ?: BigDecimal.ZERO,
|
||||||
|
priceAnnotated = priceAnnotated ?: TextReference.EMPTY,
|
||||||
)
|
)
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||||
|
|
@ -181,6 +191,8 @@ private fun Preview() {
|
||||||
priceChangeType = PriceChangeType.UP,
|
priceChangeType = PriceChangeType.UP,
|
||||||
chartData = data,
|
chartData = data,
|
||||||
onClick = {},
|
onClick = {},
|
||||||
|
currentPriceValue = BigDecimal(1234),
|
||||||
|
priceAnnotated = stringReference("0,5$"),
|
||||||
)
|
)
|
||||||
|
|
||||||
TangemThemePreview {
|
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.common.ui.charts.state.MarketChartRawData
|
||||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||||
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
internal data class TokenMarketBlockUM(
|
internal data class TokenMarketBlockUM(
|
||||||
val currencySymbol: String,
|
val currencySymbol: String,
|
||||||
val currentPrice: String?,
|
val currentPrice: String?,
|
||||||
|
val currentPriceValue: BigDecimal?,
|
||||||
|
val priceAnnotated: TextReference?,
|
||||||
val h24Percent: String?,
|
val h24Percent: String?,
|
||||||
val priceChangeType: PriceChangeType,
|
val priceChangeType: PriceChangeType,
|
||||||
val chartData: MarketChartRawData?,
|
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.converter.PriceAndTimePointValuesConverter
|
||||||
import com.tangem.common.ui.charts.state.sorted
|
import com.tangem.common.ui.charts.state.sorted
|
||||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
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.R
|
||||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
|
|
@ -119,7 +120,12 @@ internal class SwapMarketsTokenItemConverter(
|
||||||
|
|
||||||
return MarketsListItemUM.Price(
|
return MarketsListItemUM.Price(
|
||||||
text = priceText,
|
text = priceText,
|
||||||
|
annotated = tokenQuotesShort.currentPrice.toMarketsListItemPriceAnnotated(
|
||||||
|
appCurrencyCode = appCurrency.code,
|
||||||
|
appCurrencySymbol = appCurrency.symbol,
|
||||||
|
),
|
||||||
changeType = changeType,
|
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.converter.PriceAndTimePointValuesConverter
|
||||||
import com.tangem.common.ui.charts.state.sorted
|
import com.tangem.common.ui.charts.state.sorted
|
||||||
import com.tangem.common.ui.markets.models.MarketsListItemUM
|
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.components.marketprice.PriceChangeType
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
import com.tangem.core.ui.extensions.wrappedList
|
import com.tangem.core.ui.extensions.wrappedList
|
||||||
|
|
@ -119,7 +120,12 @@ internal class SwapMarketsTokenItemConverter(
|
||||||
|
|
||||||
return MarketsListItemUM.Price(
|
return MarketsListItemUM.Price(
|
||||||
text = priceText,
|
text = priceText,
|
||||||
|
annotated = tokenQuotesShort.currentPrice.toMarketsListItemPriceAnnotated(
|
||||||
|
appCurrencyCode = appCurrency.code,
|
||||||
|
appCurrencySymbol = appCurrency.symbol,
|
||||||
|
),
|
||||||
changeType = changeType,
|
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.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
import java.math.BigDecimal
|
||||||
|
|
||||||
internal object SwapSelectTokenPreviewProvider {
|
internal object SwapSelectTokenPreviewProvider {
|
||||||
|
|
||||||
|
|
@ -205,7 +206,11 @@ internal object SwapSelectTokenPreviewProvider {
|
||||||
iconUrl = iconUrl,
|
iconUrl = iconUrl,
|
||||||
ratingPosition = ratingPosition,
|
ratingPosition = ratingPosition,
|
||||||
marketCap = marketCap,
|
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%",
|
trendPercentText = "12.43%",
|
||||||
trendType = trendType,
|
trendType = trendType,
|
||||||
chartData = chartData,
|
chartData = chartData,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue