Updated on 2026-08-14

This commit is contained in:
Tangem 2023-09-27 22:27:46 +08:00
parent c6fc0d49ba
commit 31b1b2182b
11 changed files with 110 additions and 96 deletions

View file

@ -130,7 +130,7 @@ private fun Price(price: String, modifier: Modifier = Modifier) {
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun PriceChangeInPercent(config: PriceChangeConfig) {
private fun PriceChangeInPercent(config: PriceChangeState.Content) {
AnimatedContent(targetState = config.type, label = "Update price change") { type ->
Row(
verticalAlignment = Alignment.CenterVertically,
@ -139,13 +139,13 @@ private fun PriceChangeInPercent(config: PriceChangeConfig) {
Icon(
painter = painterResource(
id = when (type) {
PriceChangeConfig.Type.UP -> R.drawable.ic_arrow_up_8
PriceChangeConfig.Type.DOWN -> R.drawable.ic_arrow_down_8
PriceChangeType.UP -> R.drawable.ic_arrow_up_8
PriceChangeType.DOWN -> R.drawable.ic_arrow_down_8
},
),
tint = when (type) {
PriceChangeConfig.Type.UP -> TangemTheme.colors.icon.accent
PriceChangeConfig.Type.DOWN -> TangemTheme.colors.icon.warning
PriceChangeType.UP -> TangemTheme.colors.icon.accent
PriceChangeType.DOWN -> TangemTheme.colors.icon.warning
},
contentDescription = null,
)
@ -153,8 +153,8 @@ private fun PriceChangeInPercent(config: PriceChangeConfig) {
Text(
text = config.valueInPercent,
color = when (type) {
PriceChangeConfig.Type.UP -> TangemTheme.colors.text.accent
PriceChangeConfig.Type.DOWN -> TangemTheme.colors.text.warning
PriceChangeType.UP -> TangemTheme.colors.text.accent
PriceChangeType.DOWN -> TangemTheme.colors.text.warning
},
style = TangemTheme.typography.body2,
)
@ -215,17 +215,17 @@ private class WalletMarketPriceBlockStateProvider : CollectionPreviewParameterPr
MarketPriceBlockState.Content(
currencyName = "BTC",
price = "98900 $",
priceChangeConfig = PriceChangeConfig(
priceChangeConfig = PriceChangeState.Content(
valueInPercent = "5.16%",
type = PriceChangeConfig.Type.DOWN,
type = PriceChangeType.DOWN,
),
),
MarketPriceBlockState.Content(
currencyName = "BTC",
price = "98900 $",
priceChangeConfig = PriceChangeConfig(
priceChangeConfig = PriceChangeState.Content(
valueInPercent = "10.89%",
type = PriceChangeConfig.Type.UP,
type = PriceChangeType.UP,
),
),
MarketPriceBlockState.Loading(currencyName = "BTC"),

View file

@ -14,6 +14,6 @@ sealed interface MarketPriceBlockState {
data class Content(
override val currencyName: String,
val price: String,
val priceChangeConfig: PriceChangeConfig,
val priceChangeConfig: PriceChangeState.Content,
) : MarketPriceBlockState
}

View file

@ -1,9 +0,0 @@
package com.tangem.core.ui.components.marketprice
data class PriceChangeConfig(val valueInPercent: String, val type: Type) {
/** Price changing type */
enum class Type {
UP, DOWN
}
}

View file

@ -0,0 +1,13 @@
package com.tangem.core.ui.components.marketprice
sealed class PriceChangeState {
data class Content(val valueInPercent: String, val type: PriceChangeType) : PriceChangeState()
object Unknown : PriceChangeState()
}
/** Price changing type */
enum class PriceChangeType {
UP, DOWN
}

View file

@ -3,7 +3,8 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
import arrow.core.Either
import com.tangem.common.Provider
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.components.marketprice.PriceChangeConfig
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.error.CurrencyStatusError
@ -88,7 +89,7 @@ internal class TokenDetailsLoadedBalanceConverter(
-> MarketPriceBlockState.Content(
currencyName = currencyName,
price = formatPrice(status, appCurrencyProvider()),
priceChangeConfig = PriceChangeConfig(
priceChangeConfig = PriceChangeState.Content(
valueInPercent = formatPriceChange(status),
type = getPriceChangeType(status),
),
@ -103,14 +104,10 @@ internal class TokenDetailsLoadedBalanceConverter(
}
}
private fun getPriceChangeType(status: CryptoCurrencyStatus.Status): PriceChangeConfig.Type {
val priceChange = status.priceChange ?: return PriceChangeConfig.Type.DOWN
private fun getPriceChangeType(status: CryptoCurrencyStatus.Status): PriceChangeType {
val priceChange = status.priceChange ?: return PriceChangeType.DOWN
return if (priceChange > BigDecimal.ZERO) {
PriceChangeConfig.Type.UP
} else {
PriceChangeConfig.Type.DOWN
}
return if (priceChange > BigDecimal.ZERO) PriceChangeType.UP else PriceChangeType.DOWN
}
private fun formatPriceChange(status: CryptoCurrencyStatus.Status): String {

View file

@ -4,7 +4,8 @@ import androidx.paging.PagingData
import com.tangem.core.ui.R
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.components.marketprice.PriceChangeConfig
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.components.transactions.state.TransactionState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.event.consumedEvent
@ -129,10 +130,7 @@ internal object WalletPreviewData {
hasPending = true,
tokenOptions = TokenOptionsState(
fiatAmount = "321 $",
config = PriceChangeConfig(
valueInPercent = "2%",
type = PriceChangeConfig.Type.UP,
),
priceChangeState = PriceChangeState.Unknown,
isBalanceHidden = false,
),
onItemClick = {},
@ -155,9 +153,9 @@ internal object WalletPreviewData {
amount = "5,412 MATIC",
hasPending = false,
tokenOptions = TokenOptionsState(
config = PriceChangeConfig(
priceChangeState = PriceChangeState.Content(
valueInPercent = "2%",
type = PriceChangeConfig.Type.UP,
type = PriceChangeType.UP,
),
fiatAmount = "321 $",
isBalanceHidden = false,
@ -417,9 +415,9 @@ internal object WalletPreviewData {
marketPriceBlockState = MarketPriceBlockState.Content(
currencyName = "BTC",
price = "98900.12$",
priceChangeConfig = PriceChangeConfig(
priceChangeConfig = PriceChangeState.Content(
valueInPercent = "5.16%",
type = PriceChangeConfig.Type.UP,
type = PriceChangeType.UP,
),
),
txHistoryState = TxHistoryState.Content(

View file

@ -11,6 +11,8 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.constraintlayout.compose.*
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.extensions.rememberHapticFeedback
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
@ -203,16 +205,18 @@ private class TokenConfigProvider : CollectionPreviewParameterProvider<TokenItem
WalletPreviewData.tokenItemVisibleState.copy(amount = "5,41221467146712416241274127841274174213421 MATIC"),
WalletPreviewData.tokenItemVisibleState.copy(
tokenOptions = WalletPreviewData.tokenItemVisibleState.tokenOptions.copy(
config = WalletPreviewData.tokenItemVisibleState.tokenOptions.config.copy(
priceChangeState = PriceChangeState.Content(
valueInPercent = "31231231231231231231223123123123212312312312.00%",
type = PriceChangeType.UP,
),
),
),
WalletPreviewData.tokenItemVisibleState.copy(
amount = "5,41221467146712416241274127841274174213421 MATIC",
tokenOptions = WalletPreviewData.tokenItemVisibleState.tokenOptions.copy(
config = WalletPreviewData.tokenItemVisibleState.tokenOptions.config.copy(
priceChangeState = PriceChangeState.Content(
valueInPercent = "31231231231231231231223123123123212312312312.00%",
type = PriceChangeType.UP,
),
),
),

View file

@ -2,10 +2,7 @@ package com.tangem.feature.wallet.presentation.common.component.token
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -16,7 +13,8 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.SpacerW4
import com.tangem.core.ui.components.marketprice.PriceChangeConfig
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemTypography
import com.tangem.feature.wallet.impl.R
@ -28,7 +26,7 @@ internal fun TokenPriceChange(state: TokenItemState, modifier: Modifier = Modifi
AnimatedContent(targetState = state, label = "Update the price change", modifier = modifier) { animatedState ->
when (animatedState) {
is TokenItemState.Content -> {
PriceChangeBlock(config = animatedState.tokenOptions.config)
PriceChangeBlock(state = animatedState.tokenOptions.priceChangeState)
}
is TokenItemState.Loading -> {
RectangleShimmer(modifier = Modifier.placeholderSize(), radius = TangemTheme.dimens.radius4)
@ -45,54 +43,70 @@ internal fun TokenPriceChange(state: TokenItemState, modifier: Modifier = Modifi
}
@Composable
private fun PriceChangeBlock(config: PriceChangeConfig) {
private fun PriceChangeBlock(state: PriceChangeState) {
Row(horizontalArrangement = Arrangement.End) {
PriceChangeIcon(
type = config.type,
type = (state as? PriceChangeState.Content)?.type,
modifier = Modifier.align(Alignment.CenterVertically),
)
SpacerW4()
PriceChangeText(config = config, modifier = Modifier.align(Alignment.CenterVertically))
PriceChangeText(state = state, modifier = Modifier.align(Alignment.CenterVertically))
}
}
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun PriceChangeIcon(type: PriceChangeConfig.Type, modifier: Modifier = Modifier) {
private fun PriceChangeIcon(type: PriceChangeType?, modifier: Modifier = Modifier) {
AnimatedContent(
targetState = type,
label = "Update the price change's arrow",
modifier = modifier,
) { animatedType ->
animatedType ?: return@AnimatedContent
Icon(
painter = painterResource(
id = when (animatedType) {
PriceChangeConfig.Type.UP -> R.drawable.ic_arrow_up_8
PriceChangeConfig.Type.DOWN -> R.drawable.ic_arrow_down_8
PriceChangeType.UP -> R.drawable.ic_arrow_up_8
PriceChangeType.DOWN -> R.drawable.ic_arrow_down_8
},
),
tint = when (animatedType) {
PriceChangeConfig.Type.UP -> TangemTheme.colors.icon.accent
PriceChangeConfig.Type.DOWN -> TangemTheme.colors.icon.warning
PriceChangeType.UP -> TangemTheme.colors.icon.accent
PriceChangeType.DOWN -> TangemTheme.colors.icon.warning
},
contentDescription = null,
)
}
}
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun PriceChangeText(config: PriceChangeConfig, modifier: Modifier = Modifier) {
Text(
text = config.valueInPercent,
private fun PriceChangeText(state: PriceChangeState, modifier: Modifier = Modifier) {
AnimatedContent(
targetState = state,
label = "Update the price change's text",
modifier = modifier,
color = when (config.type) {
PriceChangeConfig.Type.UP -> TangemTheme.colors.text.accent
PriceChangeConfig.Type.DOWN -> TangemTheme.colors.text.warning
},
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = TangemTypography.body2,
)
) { animatedState ->
Text(
text = when (animatedState) {
is PriceChangeState.Content -> animatedState.valueInPercent
is PriceChangeState.Unknown -> TokenItemState.UNKNOWN_AMOUNT_SIGN
},
color = when (animatedState) {
is PriceChangeState.Content -> {
when (animatedState.type) {
PriceChangeType.UP -> TangemTheme.colors.text.accent
PriceChangeType.DOWN -> TangemTheme.colors.text.warning
}
}
PriceChangeState.Unknown -> TangemTheme.colors.text.primary1
},
overflow = TextOverflow.Ellipsis,
maxLines = 1,
style = TangemTypography.body2,
)
}
}
private fun Modifier.placeholderSize(): Modifier = composed {

View file

@ -3,7 +3,7 @@ package com.tangem.feature.wallet.presentation.common.state
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Color
import com.tangem.core.ui.components.marketprice.PriceChangeConfig
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.extensions.TextReference
/** Token item state */
@ -166,8 +166,12 @@ internal sealed interface TokenItemState {
/** Token options state */
@Immutable
data class TokenOptionsState(
val config: PriceChangeConfig,
val priceChangeState: PriceChangeState,
val fiatAmount: String,
val isBalanceHidden: Boolean,
)
companion object {
const val UNKNOWN_AMOUNT_SIGN = ""
}
}

View file

@ -3,7 +3,8 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory
import arrow.core.Either
import com.tangem.common.Provider
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.components.marketprice.PriceChangeConfig
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.error.CurrencyStatusError
@ -59,7 +60,7 @@ internal class WalletSingleCurrencyLoadedBalanceConverter(
-> MarketPriceBlockState.Content(
currencyName = currencyName,
price = formatPrice(status, appCurrencyProvider()),
priceChangeConfig = PriceChangeConfig(
priceChangeConfig = PriceChangeState.Content(
valueInPercent = formatPriceChange(status),
type = getPriceChangeType(status),
),
@ -128,14 +129,10 @@ internal class WalletSingleCurrencyLoadedBalanceConverter(
)
}
private fun getPriceChangeType(status: CryptoCurrencyStatus.Status): PriceChangeConfig.Type {
val priceChange = status.priceChange ?: return PriceChangeConfig.Type.DOWN
private fun getPriceChangeType(status: CryptoCurrencyStatus.Status): PriceChangeType {
val priceChange = status.priceChange ?: return PriceChangeType.DOWN
return if (priceChange > BigDecimal.ZERO) {
PriceChangeConfig.Type.UP
} else {
PriceChangeConfig.Type.DOWN
}
return if (priceChange > BigDecimal.ZERO) PriceChangeType.UP else PriceChangeType.DOWN
}
private fun formatPriceChange(status: CryptoCurrencyStatus.Status): String {

View file

@ -1,7 +1,8 @@
package com.tangem.feature.wallet.presentation.wallet.utils
import com.tangem.common.Provider
import com.tangem.core.ui.components.marketprice.PriceChangeConfig
import com.tangem.core.ui.components.marketprice.PriceChangeState
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
@ -44,7 +45,7 @@ internal class CryptoCurrencyStatusToTokenItemConverter(
hasPending = value.hasCurrentNetworkTransactions,
tokenOptions = TokenItemState.TokenOptionsState(
fiatAmount = getFormattedFiatAmount(),
config = getPriceChangeConfig(),
priceChangeState = getPriceChangeConfig(),
isBalanceHidden = isBalanceHiddenProvider(),
),
onItemClick = { clickIntents.onTokenItemClick(currency) },
@ -53,13 +54,13 @@ internal class CryptoCurrencyStatusToTokenItemConverter(
}
private fun CryptoCurrencyStatus.getFormattedAmount(): String {
val amount = value.amount ?: return UNKNOWN_AMOUNT_SIGN
val amount = value.amount ?: return TokenItemState.UNKNOWN_AMOUNT_SIGN
return BigDecimalFormatter.formatCryptoAmount(amount, currency.symbol, currency.decimals)
}
private fun CryptoCurrencyStatus.getFormattedFiatAmount(): String {
val fiatAmount = value.fiatAmount ?: return UNKNOWN_AMOUNT_SIGN
val fiatAmount = value.fiatAmount ?: return TokenItemState.UNKNOWN_AMOUNT_SIGN
val appCurrency = appCurrencyProvider()
return BigDecimalFormatter.formatFiatAmount(fiatAmount, appCurrency.code, appCurrency.symbol)
@ -80,25 +81,20 @@ internal class CryptoCurrencyStatusToTokenItemConverter(
onItemLongClick = { clickIntents.onTokenItemLongClick(cryptoCurrencyStatus = this) },
)
private fun CryptoCurrencyStatus.getPriceChangeConfig(): PriceChangeConfig {
private fun CryptoCurrencyStatus.getPriceChangeConfig(): PriceChangeState {
val priceChange = value.priceChange
?: return PriceChangeConfig(UNKNOWN_AMOUNT_SIGN, PriceChangeConfig.Type.DOWN)
return PriceChangeConfig(
valueInPercent = BigDecimalFormatter.formatPercent(priceChange, useAbsoluteValue = true),
type = priceChange.getPriceChangeType(),
)
}
private fun BigDecimal?.getPriceChangeType(): PriceChangeConfig.Type {
return when {
this == null -> PriceChangeConfig.Type.DOWN
this < BigDecimal.ZERO -> PriceChangeConfig.Type.DOWN
else -> PriceChangeConfig.Type.UP
return if (priceChange != null) {
PriceChangeState.Content(
valueInPercent = BigDecimalFormatter.formatPercent(percent = priceChange, useAbsoluteValue = true),
type = priceChange.getPriceChangeType(),
)
} else {
PriceChangeState.Unknown
}
}
private companion object {
const val UNKNOWN_AMOUNT_SIGN = ""
private fun BigDecimal.getPriceChangeType(): PriceChangeType {
return if (this > BigDecimal.ZERO) PriceChangeType.UP else PriceChangeType.DOWN
}
}