From 33909502ed79022ee38ea93e20b19db9d825bbc9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 16 Aug 2023 14:39:38 +0800 Subject: [PATCH] Updated on 2026-08-14 --- .../core/ui/extensions/TextReference.kt | 40 ++- .../tangem/core/ui/extensions/WrappedList.kt | 6 +- .../core/ui/utils/BigDecimalFormatter.kt | 14 +- .../presentation/common/WalletPreviewData.kt | 7 +- .../domain/WalletAdditionalInfoFactory.kt | 76 ++++-- .../state/components/WalletCardState.kt | 53 ++-- .../factory/WalletRefreshStateConverter.kt | 5 +- ...letSingleCurrencyLoadedBalanceConverter.kt | 4 +- .../factory/WalletSkeletonStateConverter.kt | 5 - .../WalletTxHistoryItemFlowConverter.kt | 8 +- .../wallet/ui/components/common/WalletCard.kt | 234 +++++++++++------- .../utils/FiatBalanceToWalletCardConverter.kt | 79 +++--- 12 files changed, 338 insertions(+), 193 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt index 92df3daeb9..bc8107d5f5 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/TextReference.kt @@ -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 = 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) : 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 } -/** 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)) } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/WrappedList.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/WrappedList.kt index 6f529c9574..47be5e543d 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/WrappedList.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/WrappedList.kt @@ -7,4 +7,8 @@ import androidx.compose.runtime.Immutable */ @JvmInline @Immutable -value class WrappedList(val data: List) : List by data \ No newline at end of file +value class WrappedList(val data: List) : List by data + +fun List.toWrappedList(): WrappedList = WrappedList(data = this) + +fun wrappedList(vararg elements: T): WrappedList = WrappedList(data = listOf(*elements)) \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/BigDecimalFormatter.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/BigDecimalFormatter.kt index 2a78274eb5..db128105fb 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/utils/BigDecimalFormatter.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/BigDecimalFormatter.kt @@ -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( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index 62c0b8ed22..080d54bbc4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -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, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletAdditionalInfoFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletAdditionalInfoFactory.kt index affbdde057..699a8bea7d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletAdditionalInfoFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletAdditionalInfoFactory.kt @@ -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()) } } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt index 2da80de62f..01134378a5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt @@ -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 = "—") } + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRefreshStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRefreshStateConverter.kt index 0fe9e475c9..f442b7edd2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRefreshStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRefreshStateConverter.kt @@ -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, ), ), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt index 7e84f3f652..a1f491073b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt @@ -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, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt index f452ed705f..db8c705554 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt @@ -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), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt index b3fbee3db3..8feafcee7c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt @@ -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.insertGroupTitle(): PagingData { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt index ccff84d714..57d1ca7765 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt @@ -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) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt index 8d48f9b5ec..52542675be 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt @@ -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, private val isLockedState: Boolean, private val isWalletContentHidden: Boolean, -) : Converter { +) : Converter { - 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, + ), + ) } } } \ No newline at end of file