Updated on 2026-08-14
This commit is contained in:
parent
4ad8c5bfc1
commit
33909502ed
12 changed files with 338 additions and 193 deletions
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 = "—") }
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue