Updated on 2026-08-14

This commit is contained in:
Tangem 2023-08-16 14:39:38 +08:00
parent 4ad8c5bfc1
commit 33909502ed
12 changed files with 338 additions and 193 deletions

View file

@ -20,12 +20,19 @@ sealed interface TextReference {
* Text resource id
*
* @property id resource id
* @property formatArgs arguments
*
* Impossible to use [kotlinx.collections.immutable.ImmutableList] because [Any] is unstable.
* @property formatArgs arguments. Impossible to use [kotlinx.collections.immutable.ImmutableList] because [Any] is
* unstable.
*/
data class Res(@StringRes val id: Int, val formatArgs: WrappedList<Any> = WrappedList(emptyList())) : TextReference
/**
* Plural resource id
*
* @property id resource id
* @property count count
* @property formatArgs arguments. Impossible to use [kotlinx.collections.immutable.ImmutableList] because [Any] is
* unstable.
*/
data class PluralRes(@PluralsRes val id: Int, val count: Int, val formatArgs: WrappedList<Any>) : TextReference
/**
@ -34,9 +41,16 @@ sealed interface TextReference {
* @property value value
*/
data class Str(val value: String) : TextReference
/**
* Combined reference. It concatenates all [refs].
*
* @see [TextReference.plus] method
*/
data class Combined(val refs: WrappedList<TextReference>) : TextReference
}
/** Get text */
/** Resolve [TextReference] as [String] */
@Composable
@ReadOnlyComposable
fun TextReference.resolveReference(): String {
@ -44,5 +58,23 @@ fun TextReference.resolveReference(): String {
is TextReference.Res -> stringResource(id, *formatArgs.toTypedArray())
is TextReference.PluralRes -> pluralStringResource(id, count, *formatArgs.toTypedArray())
is TextReference.Str -> value
is TextReference.Combined -> {
buildString {
refs.forEach {
append(it.resolveReference())
}
}
}
}
}
/** Concatenate [this] reference with [ref] */
operator fun TextReference.plus(ref: TextReference): TextReference {
return when (this) {
is TextReference.Combined -> copy(refs = (refs.data + ref).toWrappedList())
is TextReference.PluralRes,
is TextReference.Res,
is TextReference.Str,
-> TextReference.Combined(refs = wrappedList(this, ref))
}
}

View file

@ -7,4 +7,8 @@ import androidx.compose.runtime.Immutable
*/
@JvmInline
@Immutable
value class WrappedList<T>(val data: List<T>) : List<T> by data
value class WrappedList<T>(val data: List<T>) : List<T> by data
fun <T> List<T>.toWrappedList(): WrappedList<T> = WrappedList(data = this)
fun <T> wrappedList(vararg elements: T): WrappedList<T> = WrappedList(data = listOf(*elements))

View file

@ -12,22 +12,14 @@ object BigDecimalFormatter {
private const val TEMP_CURRENCY_CODE = "USD"
fun formatCryptoAmount(
cryptoAmount: BigDecimal,
cryptoCurrency: String,
decimals: Int,
locale: Locale = Locale.getDefault(),
): String {
val formatterCurrency = getCurrency(cryptoCurrency)
val formatter = NumberFormat.getCurrencyInstance(locale).apply {
currency = formatterCurrency
fun formatCryptoAmount(cryptoAmount: BigDecimal, cryptoCurrency: String, decimals: Int): String {
val formatter = NumberFormat.getNumberInstance().apply {
maximumFractionDigits = decimals.coerceAtMost(maximumValue = 8)
minimumFractionDigits = 2
roundingMode = RoundingMode.DOWN
}
return formatter.format(cryptoAmount)
.replace(formatterCurrency.getSymbol(locale), cryptoCurrency)
return formatter.format(cryptoAmount) + "\u2009$cryptoCurrency"
}
fun formatFiatAmount(

View file

@ -28,10 +28,10 @@ internal object WalletPreviewData {
val walletCardContentState by lazy {
WalletCardState.Content(
id = UserWalletId("123"),
id = UserWalletId(stringValue = "123"),
title = "Wallet 1",
balance = "8923,05 $",
additionalInfo = "3 cards • Seed enabled",
additionalInfo = TextReference.Str("3 cards • Seed phrase"),
imageResId = R.drawable.ill_businessman_3d,
onClick = null,
)
@ -41,7 +41,6 @@ internal object WalletPreviewData {
WalletCardState.Loading(
id = UserWalletId("321"),
title = "Wallet 1",
additionalInfo = "3 cards • Seed enabled",
imageResId = R.drawable.ill_businessman_3d,
onClick = null,
)
@ -51,7 +50,6 @@ internal object WalletPreviewData {
WalletCardState.HiddenContent(
id = UserWalletId("42"),
title = "Wallet 1",
additionalInfo = "3 cards • Seed enabled",
imageResId = R.drawable.ill_businessman_3d,
onClick = null,
)
@ -61,7 +59,6 @@ internal object WalletPreviewData {
WalletCardState.Error(
id = UserWalletId("24"),
title = "Wallet 1",
additionalInfo = "3 cards • Seed enabled",
imageResId = R.drawable.ill_businessman_3d,
onClick = null,
)

View file

@ -1,7 +1,11 @@
package com.tangem.feature.wallet.presentation.wallet.domain
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.plus
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.domain.common.CardTypesResolver
import com.tangem.utils.toFormattedCurrencyString
import com.tangem.feature.wallet.impl.R
import java.math.BigDecimal
/**
@ -9,35 +13,69 @@ import java.math.BigDecimal
*
[REDACTED_AUTHOR]
*/
// TODO: Finalize strings [REDACTED_JIRA]
internal object WalletAdditionalInfoFactory {
private val DIVIDER_RES by lazy { TextReference.Str(value = "") }
/**
* Get additional info
*
* @param cardTypesResolver card types resolver
* @param cardTypesResolver card type resolver
* @param isLocked check if wallet is locked
* @param currencyAmount amount of currency
*/
fun resolve(cardTypesResolver: CardTypesResolver, isLocked: Boolean, currencyAmount: BigDecimal? = null): String {
fun resolve(
cardTypesResolver: CardTypesResolver,
isLocked: Boolean,
currencyAmount: BigDecimal? = null,
): TextReference {
return if (cardTypesResolver.isMultiwalletAllowed()) {
val backupInfo = "${cardTypesResolver.getBackupCardsCount()} cards"
when {
cardTypesResolver.isWallet2() && !isLocked -> "$backupInfo • Seed phrase"
cardTypesResolver.isTangemWallet() && !isLocked -> backupInfo
isLocked -> "$backupInfo • Locked"
else -> ""
}
resolveMultiCurrencyInfo(cardTypesResolver, isLocked)
} else {
if (isLocked) {
"Locked"
} else {
val blockchain = cardTypesResolver.getBlockchain()
currencyAmount?.toFormattedCurrencyString(
decimals = blockchain.decimals(),
currency = blockchain.currency,
).orEmpty()
resolveSingleCurrencyInfo(cardTypesResolver, isLocked, currencyAmount)
}
}
private fun resolveMultiCurrencyInfo(cardTypeResolver: CardTypesResolver, isLocked: Boolean): TextReference {
val backupCardsCount = cardTypeResolver.getBackupCardsCount()
val backupInfoRes = TextReference.PluralRes(
id = R.plurals.card_label_card_count,
count = backupCardsCount,
formatArgs = wrappedList(backupCardsCount),
)
return when {
cardTypeResolver.isWallet2() && !isLocked -> {
backupInfoRes + DIVIDER_RES + TextReference.Res(id = R.string.common_seed_phrase)
}
cardTypeResolver.isTangemWallet() && !isLocked -> {
backupInfoRes
}
isLocked -> {
backupInfoRes + TextReference.Res(R.string.common_locked)
}
else -> error("It isn't exist additional info for this case")
}
}
private fun resolveSingleCurrencyInfo(
cardTypeResolver: CardTypesResolver,
isLocked: Boolean,
currencyAmount: BigDecimal?,
): TextReference {
return if (isLocked) {
TextReference.Res(R.string.common_locked)
} else {
val blockchain = cardTypeResolver.getBlockchain()
val amount = currencyAmount?.let {
BigDecimalFormatter.formatCryptoAmount(
cryptoAmount = it,
cryptoCurrency = blockchain.currency,
decimals = blockchain.decimals(),
)
}
TextReference.Str(value = amount.orEmpty())
}
}
}

View file

@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.components
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.wallets.models.UserWalletId
/** Wallet card state */
@ -14,9 +15,6 @@ internal sealed interface WalletCardState {
/** Title */
val title: String
/** Additional wallet information */
val additionalInfo: String
/** Wallet image resource id */
@get:DrawableRes
val imageResId: Int?
@ -24,38 +22,43 @@ internal sealed interface WalletCardState {
/** Lambda be invoked when card is clicked */
val onClick: (() -> Unit)?
/** Additional text availability */
sealed interface AdditionalTextAvailability {
/** Additional wallet information */
val additionalInfo: TextReference
}
/**
* Wallet card content state
*
* @property id wallet id
* @property title wallet name
* @property additionalInfo wallet additional info
* @property imageResId wallet image resource id
* @property onClick lambda be invoked when wallet card is clicked
* @property additionalInfo wallet additional info
* @property balance wallet balance
*/
data class Content(
override val id: UserWalletId,
override val title: String,
override val additionalInfo: String,
override val imageResId: Int?,
override val onClick: (() -> Unit)? = null,
override val additionalInfo: TextReference,
val balance: String,
) : WalletCardState
) : WalletCardState, AdditionalTextAvailability
/**
* Wallet card loading state
*
* @property id wallet id
* @property title wallet name
* @property additionalInfo wallet additional info
* @property imageResId wallet image resource id
* @property onClick lambda be invoked when wallet card is clicked
* @property id wallet id
* @property title wallet name
* @property imageResId wallet image resource id
* @property onClick lambda be invoked when wallet card is clicked
*/
data class Loading(
override val id: UserWalletId,
override val title: String,
override val additionalInfo: String,
override val imageResId: Int?,
override val onClick: (() -> Unit)? = null,
) : WalletCardState
@ -63,34 +66,40 @@ internal sealed interface WalletCardState {
/**
* Wallet card hidden content state
*
* @property id wallet id
* @property title wallet name
* @property additionalInfo wallet additional info
* @property imageResId wallet image resource id
* @property onClick lambda be invoked when wallet card is clicked
* @property id wallet id
* @property title wallet name
* @property imageResId wallet image resource id
* @property onClick lambda be invoked when wallet card is clicked
*/
data class HiddenContent(
override val id: UserWalletId,
override val title: String,
override val additionalInfo: String,
override val imageResId: Int?,
override val onClick: (() -> Unit)?,
) : WalletCardState
) : WalletCardState, AdditionalTextAvailability {
override val additionalInfo: TextReference = HIDDEN_BALANCE_TEXT
}
/**
* Wallet card error state
*
* @property id wallet id
* @property title wallet name
* @property additionalInfo wallet additional info
* @property imageResId wallet image resource id
* @property onClick lambda be invoked when wallet card is clicked
* @property additionalInfo wallet additional info
*/
data class Error(
override val id: UserWalletId,
override val title: String,
override val additionalInfo: String,
override val imageResId: Int?,
override val onClick: (() -> Unit)?,
) : WalletCardState
override val additionalInfo: TextReference = EMPTY_BALANCE_TEXT,
) : WalletCardState, AdditionalTextAvailability
companion object {
val HIDDEN_BALANCE_TEXT by lazy { TextReference.Str(value = "•••") }
val EMPTY_BALANCE_TEXT by lazy { TextReference.Str(value = "") }
}
}

View file

@ -48,14 +48,14 @@ internal class WalletRefreshStateConverter(
private fun WalletSingleCurrencyState.Content.getRefreshState(): WalletSingleCurrencyState.Content {
return copy(
// TODO: [REDACTED_JIRA]
walletsListConfig = getWalletsListConfig(additionalInfo = ""),
walletsListConfig = getWalletsListConfig(),
pullToRefreshConfig = getPullToRefreshConfig(),
txHistoryState = getTxHistoryState(),
marketPriceBlockState = MarketPriceBlockState.Loading(currencyName = marketPriceBlockState.currencyName),
)
}
private fun WalletState.ContentState.getWalletsListConfig(additionalInfo: String? = null): WalletsListConfig {
private fun WalletState.ContentState.getWalletsListConfig(): WalletsListConfig {
val selectedWallet = walletsListConfig.wallets[walletsListConfig.selectedWalletIndex]
return walletsListConfig.copy(
@ -66,7 +66,6 @@ internal class WalletRefreshStateConverter(
element = WalletCardState.Loading(
id = selectedWallet.id,
title = selectedWallet.title,
additionalInfo = additionalInfo ?: selectedWallet.additionalInfo,
imageResId = selectedWallet.imageResId,
),
),

View file

@ -94,14 +94,13 @@ internal class WalletSingleCurrencyLoadedBalanceConverter(
),
imageResId = selectedWallet.imageResId,
onClick = selectedWallet.onClick,
balance = formatFiatAmount(status, appCurrencyProvider()),
balance = formatFiatAmount(status = status, appCurrency = appCurrencyProvider()),
)
}
is CryptoCurrencyStatus.Loading -> {
WalletCardState.Loading(
id = selectedWallet.id,
title = selectedWallet.title,
additionalInfo = selectedWallet.additionalInfo,
imageResId = selectedWallet.imageResId,
onClick = selectedWallet.onClick,
)
@ -114,7 +113,6 @@ internal class WalletSingleCurrencyLoadedBalanceConverter(
WalletCardState.Error(
id = selectedWallet.id,
title = selectedWallet.title,
additionalInfo = selectedWallet.additionalInfo,
imageResId = selectedWallet.imageResId,
onClick = selectedWallet.onClick,
)

View file

@ -5,7 +5,6 @@ import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver
import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState
import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState
@ -106,10 +105,6 @@ internal class WalletSkeletonStateConverter(
return WalletCardState.Loading(
id = wallet.walletId,
title = wallet.name,
additionalInfo = WalletAdditionalInfoFactory.resolve(
cardTypesResolver = cardTypeResolver,
isLocked = wallet.isLocked,
),
imageResId = WalletImageResolver.resolve(cardTypesResolver = cardTypeResolver),
)
}

View file

@ -9,13 +9,13 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.core.ui.components.transactions.state.TransactionState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistoryItemState
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.isToday
import com.tangem.utils.extensions.isYesterday
import com.tangem.utils.toBriefAddressFormat
import com.tangem.utils.toFormattedCurrencyString
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import org.joda.time.DateTime
@ -133,7 +133,11 @@ internal class WalletTxHistoryItemFlowConverter(
}
private fun BigDecimal.toCryptoCurrencyFormat(blockchain: Blockchain): String {
return toFormattedCurrencyString(currency = blockchain.currency, decimals = blockchain.decimals())
return BigDecimalFormatter.formatCryptoAmount(
cryptoAmount = this,
cryptoCurrency = blockchain.currency,
decimals = blockchain.decimals(),
)
}
private fun PagingData<TxHistoryItemState>.insertGroupTitle(): PagingData<TxHistoryItemState> {

View file

@ -18,18 +18,17 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.ConstraintLayoutScope
import androidx.constraintlayout.compose.Dimension
import com.tangem.core.ui.components.FontSizeRange
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.ResizableText
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.common.WalletPreviewData
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState
private const val DOTS = "•••"
/**
* Wallet card
*
@ -40,61 +39,87 @@ private const val DOTS = "•••"
*/
@Composable
internal fun WalletCard(state: WalletCardState, modifier: Modifier = Modifier) {
@Suppress("DestructuringDeclarationWithTooManyEntries")
CardContainer(onClick = state.onClick, modifier = modifier) {
val (title, balance, additionalText, image) = createRefs()
val contentVerticalMargin = TangemTheme.dimens.spacing12
Title(
state = state,
modifier = Modifier.constrainAs(title) {
start.linkTo(parent.start)
top.linkTo(anchor = parent.top, margin = contentVerticalMargin)
end.linkTo(image.start)
width = Dimension.fillToConstraints
},
)
val betweenContentMargin = TangemTheme.dimens.spacing8
Balance(
state = state,
modifier = Modifier.constrainAs(balance) {
start.linkTo(parent.start)
top.linkTo(anchor = title.bottom, margin = betweenContentMargin)
bottom.linkTo(anchor = additionalText.top, margin = betweenContentMargin)
},
)
AdditionalInfo(
state = state,
modifier = Modifier.constrainAs(additionalText) {
start.linkTo(parent.start)
bottom.linkTo(anchor = parent.bottom, margin = contentVerticalMargin)
},
)
val imageWidth = TangemTheme.dimens.size120
Image(
id = state.imageResId,
modifier = Modifier.constrainAs(image) {
centerVerticallyTo(parent)
top.linkTo(parent.top)
end.linkTo(parent.end)
height = Dimension.fillToConstraints
width = Dimension.value(imageWidth)
},
)
}
}
@Composable
private fun CardContainer(
onClick: (() -> Unit)?,
modifier: Modifier = Modifier,
content: @Composable ConstraintLayoutScope.() -> Unit,
) {
Surface(
modifier = modifier.defaultMinSize(minHeight = TangemTheme.dimens.size108),
shape = TangemTheme.shapes.roundedCornersXMedium,
color = TangemTheme.colors.background.primary,
onClick = state.onClick ?: {},
enabled = state.onClick != null,
onClick = onClick ?: {},
enabled = onClick != null,
) {
ConstraintLayout(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = TangemTheme.dimens.spacing14),
) {
val (balanceBlock, imageItem) = createRefs()
Column(
modifier = Modifier.constrainAs(balanceBlock) {
centerVerticallyTo(parent)
start.linkTo(parent.start)
end.linkTo(imageItem.start)
width = Dimension.fillToConstraints
},
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
) {
Title(state)
Balance(state)
AdditionalInfo(description = state.additionalInfo)
}
val imageWidth = TangemTheme.dimens.size120
WalletImage(
id = state.imageResId,
modifier = Modifier.constrainAs(imageItem) {
centerVerticallyTo(parent)
top.linkTo(parent.top)
end.linkTo(parent.end)
height = Dimension.fillToConstraints
width = Dimension.value(imageWidth)
},
)
content()
}
}
}
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun Title(state: WalletCardState) {
AnimatedContent(targetState = state, label = "Update the title") {
when (it) {
is WalletCardState.HiddenContent -> {
Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4)) {
Text(
text = it.title,
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.body2,
maxLines = 1,
)
private fun Title(state: WalletCardState, modifier: Modifier = Modifier) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
) {
TitleText(title = state.title)
AnimatedVisibility(visible = state is WalletCardState.HiddenContent, label = "Update the hidden icon") {
when (state) {
is WalletCardState.HiddenContent -> {
Icon(
modifier = Modifier.size(size = TangemTheme.dimens.size20),
painter = painterResource(id = R.drawable.ic_eye_off_24),
@ -102,16 +127,63 @@ private fun Title(state: WalletCardState) {
tint = TangemTheme.colors.icon.informative,
)
}
is WalletCardState.Content,
is WalletCardState.Error,
is WalletCardState.Loading,
-> Unit
}
is WalletCardState.Content,
is WalletCardState.Error,
is WalletCardState.Loading,
-> {
}
}
}
@Composable
private fun TitleText(title: String) {
Text(
text = title,
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.body2,
maxLines = 1,
)
}
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun Balance(state: WalletCardState, modifier: Modifier = Modifier) {
AnimatedContent(
targetState = state,
label = "Update the balance",
modifier = modifier,
) { walletCardState ->
when (walletCardState) {
is WalletCardState.Content -> {
ResizableText(
text = walletCardState.balance,
fontSizeRange = FontSizeRange(min = 16.sp, max = TangemTheme.typography.h2.fontSize),
modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size32),
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.h2,
)
}
is WalletCardState.Loading -> {
RectangleShimmer(
modifier = Modifier.size(
width = TangemTheme.dimens.size102,
height = TangemTheme.dimens.size32,
),
)
}
is WalletCardState.HiddenContent -> {
Text(
text = it.title,
color = TangemTheme.colors.text.tertiary,
style = TangemTheme.typography.body2,
maxLines = 1,
text = WalletCardState.HIDDEN_BALANCE_TEXT.resolveReference(),
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.h2,
)
}
is WalletCardState.Error -> {
Text(
text = WalletCardState.EMPTY_BALANCE_TEXT.resolveReference(),
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.h2,
)
}
}
@ -120,55 +192,34 @@ private fun Title(state: WalletCardState) {
@OptIn(ExperimentalAnimationApi::class)
@Composable
private fun Balance(state: WalletCardState) {
AnimatedContent(targetState = state, label = "Update the balance") {
when (it) {
is WalletCardState.Content -> {
ResizableText(
text = it.balance,
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.h2,
fontSizeRange = FontSizeRange(min = 16.sp, max = TangemTheme.typography.h2.fontSize),
modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size32),
private fun AdditionalInfo(state: WalletCardState, modifier: Modifier = Modifier) {
AnimatedContent(
targetState = state,
label = "Update the additional text",
modifier = modifier,
) { walletCardState ->
when (walletCardState) {
is WalletCardState.AdditionalTextAvailability -> {
Text(
text = walletCardState.additionalInfo.resolveReference(),
color = TangemTheme.colors.text.disabled,
style = TangemTheme.typography.caption,
)
}
is WalletCardState.Loading -> {
RectangleShimmer(
modifier = Modifier.size(
width = TangemTheme.dimens.size102,
height = TangemTheme.dimens.size24,
width = TangemTheme.dimens.size84,
height = TangemTheme.dimens.size16,
),
)
}
is WalletCardState.HiddenContent -> {
Text(
text = DOTS,
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.h2,
)
}
is WalletCardState.Error -> {
Text(
text = BigDecimalFormatter.EMPTY_BALANCE_SIGN,
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography.h2,
)
}
}
}
}
@Composable
private fun AdditionalInfo(description: String) {
Text(
text = description,
color = TangemTheme.colors.text.disabled,
style = TangemTheme.typography.caption,
)
}
@Composable
private fun WalletImage(@DrawableRes id: Int?, modifier: Modifier = Modifier) {
private fun Image(@DrawableRes id: Int?, modifier: Modifier = Modifier) {
AnimatedVisibility(visible = id != null, modifier = modifier) {
Image(
painter = painterResource(id = requireNotNull(id)),
@ -180,11 +231,14 @@ private fun WalletImage(@DrawableRes id: Int?, modifier: Modifier = Modifier) {
// region Preview
@Preview(widthDp = 360, heightDp = 360)
@Preview
@Composable
private fun Preview_WalletCard_LightTheme(@PreviewParameter(WalletCardStateProvider::class) state: WalletCardState) {
private fun Preview_WalletCard_LightTheme(
@PreviewParameter(WalletCardStateProvider::class)
state: WalletCardState,
) {
TangemTheme(isDark = false) {
WalletCard(state = state, modifier = Modifier.fillMaxWidth())
WalletCard(state = state)
}
}

View file

@ -4,7 +4,7 @@ import com.tangem.common.Provider
import com.tangem.core.ui.utils.BigDecimalFormatter.formatFiatAmount
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.tokens.model.TokenList.FiatBalance
import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory
import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState
import com.tangem.utils.converter.Converter
@ -15,36 +15,59 @@ internal class FiatBalanceToWalletCardConverter(
private val appCurrencyProvider: Provider<AppCurrency>,
private val isLockedState: Boolean,
private val isWalletContentHidden: Boolean,
) : Converter<TokenList.FiatBalance, WalletCardState> {
) : Converter<FiatBalance, WalletCardState> {
override fun convert(value: TokenList.FiatBalance): WalletCardState {
val additionalInfo = WalletAdditionalInfoFactory.resolve(
cardTypesResolver = cardTypeResolverProvider(),
isLocked = isLockedState,
)
override fun convert(value: FiatBalance): WalletCardState {
return when (value) {
is TokenList.FiatBalance.Loading -> with(currentState) {
WalletCardState.Loading(id, title, additionalInfo, imageResId, onClick)
}
is TokenList.FiatBalance.Failed -> with(currentState) {
WalletCardState.Error(id, title, additionalInfo, imageResId, onClick)
}
is TokenList.FiatBalance.Loaded -> with(currentState) {
if (isWalletContentHidden) {
WalletCardState.HiddenContent(id, title, additionalInfo, imageResId, onClick)
} else {
val appCurrency = appCurrencyProvider()
is FiatBalance.Loading -> currentState.toLoadingWalletCardState()
is FiatBalance.Failed -> currentState.toErrorWalletCardState()
is FiatBalance.Loaded -> value.convertToWalletCardState()
}
}
WalletCardState.Content(
id = id,
title = title,
additionalInfo = additionalInfo,
imageResId = imageResId,
onClick = onClick,
balance = formatFiatAmount(value.amount, appCurrency.code, appCurrency.symbol),
)
}
}
private fun WalletCardState.toLoadingWalletCardState(): WalletCardState {
return WalletCardState.Loading(id, title, imageResId, onClick)
}
private fun WalletCardState.toErrorWalletCardState(): WalletCardState {
return WalletCardState.Error(
id = id,
title = title,
imageResId = imageResId,
onClick = onClick,
additionalInfo = WalletAdditionalInfoFactory.resolve(
cardTypesResolver = cardTypeResolverProvider(),
isLocked = isLockedState,
),
)
}
private fun FiatBalance.Loaded.convertToWalletCardState(): WalletCardState {
return if (isWalletContentHidden) {
WalletCardState.HiddenContent(
id = currentState.id,
title = currentState.title,
imageResId = currentState.imageResId,
onClick = currentState.onClick,
)
} else {
val appCurrency = appCurrencyProvider()
WalletCardState.Content(
id = currentState.id,
title = currentState.title,
additionalInfo = WalletAdditionalInfoFactory.resolve(
cardTypesResolver = cardTypeResolverProvider(),
isLocked = isLockedState,
),
imageResId = currentState.imageResId,
onClick = currentState.onClick,
balance = formatFiatAmount(
fiatAmount = this.amount,
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
),
)
}
}
}