Updated on 2026-08-14

This commit is contained in:
Tangem 2025-02-04 16:44:18 +04:00
parent 9315578317
commit 3113cfe26f
6 changed files with 259 additions and 132 deletions

View file

@ -3,7 +3,6 @@ package com.tangem.common.ui.userwallet
import android.content.res.Configuration
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.CardColors
import androidx.compose.material3.Icon
@ -15,26 +14,32 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import coil.compose.SubcomposeAsyncImage
import coil.request.ImageRequest
import com.tangem.common.ui.R
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.ui.coil.RotationTransformation
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.TextShimmer
import com.tangem.core.ui.components.block.BlockCard
import com.tangem.core.ui.components.block.TangemBlockCardColors
import com.tangem.core.ui.components.flicker
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.common.ui.R
import com.tangem.core.ui.coil.RotationTransformation
import com.tangem.core.ui.components.block.TangemBlockCardColors
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.collections.immutable.persistentListOf
import com.tangem.utils.StringsSigns.DASH_SIGN
import com.tangem.utils.StringsSigns.DOT
import com.tangem.utils.StringsSigns.THREE_STARS
@Composable
fun UserWalletItem(
@ -61,6 +66,7 @@ fun UserWalletItem(
modifier = Modifier.weight(1f),
name = state.name,
information = state.information,
balance = state.balance,
)
when (state.endIcon) {
@ -85,7 +91,12 @@ fun UserWalletItem(
}
@Composable
private fun NameAndInfo(name: TextReference, information: TextReference, modifier: Modifier = Modifier) {
private fun NameAndInfo(
name: TextReference,
information: TextReference,
balance: UserWalletItemUM.Balance,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier.heightIn(min = TangemTheme.dimens.size40),
horizontalAlignment = Alignment.Start,
@ -99,17 +110,37 @@ private fun NameAndInfo(name: TextReference, information: TextReference, modifie
overflow = TextOverflow.Ellipsis,
)
AnimatedContent(
targetState = information.resolveReference(),
label = "User wallet information",
) { information ->
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = information,
text = information.resolveReference() + " $DOT ",
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
AnimatedContent(
targetState = balance,
label = "Balance content",
) { balance ->
val (balanceValue, isFlickering) = getBalanceValueAndFlickerState(balance)
if (balanceValue == null) {
TextShimmer(
style = TangemTheme.typography.caption2,
text = "aaaaa",
)
} else {
Text(
modifier = Modifier.flicker(isFlickering),
text = balanceValue,
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
maxLines = 1,
)
}
}
}
}
}
@ -150,16 +181,39 @@ private fun CardImage(imageUrl: String, modifier: Modifier = Modifier) {
)
}
@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun Preview() {
fun getBalanceValueAndFlickerState(balance: UserWalletItemUM.Balance): Pair<String?, Boolean> {
return when (balance) {
is UserWalletItemUM.Balance.Failed -> DASH_SIGN to false
is UserWalletItemUM.Balance.Hidden -> THREE_STARS to false
is UserWalletItemUM.Balance.Loading -> null to false
is UserWalletItemUM.Balance.Locked -> stringResource(R.string.common_locked) to false
is UserWalletItemUM.Balance.Loaded -> balance.value to balance.isFlickering
}
}
// region Preview
@Composable
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun Preview_UserWalletItem(
@PreviewParameter(UserWalletItemUMPreviewProvider::class) params: UserWalletItemUM,
) {
TangemThemePreview {
val list = persistentListOf(
UserWalletItem(
state = params,
)
}
}
private class UserWalletItemUMPreviewProvider : PreviewParameterProvider<UserWalletItemUM> {
override val values: Sequence<UserWalletItemUM>
get() = sequenceOf(
UserWalletItemUM(
id = UserWalletId("user_wallet_1".encodeToByteArray()),
name = stringReference("My Wallet"),
information = getInformation(3, "4 496,75 $"),
information = getInformation(cardCount = 1),
balance = UserWalletItemUM.Balance.Locked,
imageUrl = "",
isEnabled = true,
onClick = {},
@ -167,7 +221,8 @@ private fun Preview() {
UserWalletItemUM(
id = UserWalletId("user_wallet_2".encodeToByteArray()),
name = stringReference("Old wallet"),
information = getInformation(3, "4 496,75 $"),
information = getInformation(cardCount = 2),
balance = UserWalletItemUM.Balance.Hidden,
imageUrl = "",
isEnabled = true,
onClick = {},
@ -176,7 +231,44 @@ private fun Preview() {
UserWalletItemUM(
id = UserWalletId("user_wallet_3".encodeToByteArray()),
name = stringReference("Multi Card"),
information = getInformation(3, "4 496,75 $"),
information = getInformation(cardCount = 3),
balance = UserWalletItemUM.Balance.Failed,
imageUrl = "",
isEnabled = false,
endIcon = UserWalletItemUM.EndIcon.Checkmark,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_3".encodeToByteArray()),
name = stringReference("Multi Card"),
information = getInformation(cardCount = 3),
balance = UserWalletItemUM.Balance.Loading,
imageUrl = "",
isEnabled = false,
endIcon = UserWalletItemUM.EndIcon.Checkmark,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_3".encodeToByteArray()),
name = stringReference("Multi Card"),
information = getInformation(cardCount = 3),
balance = UserWalletItemUM.Balance.Loaded(
value = "1.2345 BTC",
isFlickering = false,
),
imageUrl = "",
isEnabled = false,
endIcon = UserWalletItemUM.EndIcon.Checkmark,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_3".encodeToByteArray()),
name = stringReference("Multi Card"),
information = getInformation(cardCount = 3),
balance = UserWalletItemUM.Balance.Loaded(
value = "1.2345 BTC",
isFlickering = true,
),
imageUrl = "",
isEnabled = false,
endIcon = UserWalletItemUM.EndIcon.Checkmark,
@ -184,30 +276,12 @@ private fun Preview() {
),
)
Column(
Modifier
.background(TangemTheme.colors.background.tertiary)
.padding(TangemTheme.dimens.spacing12),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
) {
list.fastForEach { userWalletItemUM ->
UserWalletItem(
modifier = Modifier.fillMaxWidth(),
state = userWalletItemUM,
)
}
}
private fun getInformation(cardCount: Int): TextReference {
return TextReference.PluralRes(
id = R.plurals.card_label_card_count,
count = cardCount,
formatArgs = wrappedList(cardCount),
)
}
}
private fun getInformation(cardCount: Int, totalBalance: String): TextReference {
val t1 = TextReference.PluralRes(
id = R.plurals.card_label_card_count,
count = cardCount,
formatArgs = wrappedList(cardCount),
)
val divider = stringReference(value = "")
val t2 = stringReference(totalBalance)
return TextReference.Combined(wrappedList(t1, divider, t2))
}
// endregion Preview

View file

@ -2,14 +2,16 @@ package com.tangem.common.ui.userwallet.converter
import com.tangem.common.ui.R
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.common.util.getCardsCount
import com.tangem.domain.tokens.model.TotalFiatBalance
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.utils.StringsSigns.DOT
import com.tangem.utils.converter.Converter
/**
@ -37,12 +39,8 @@ class UserWalletItemUMConverter(
UserWalletItemUM(
id = walletId,
name = stringReference(name),
information = getInfo(
appCurrency = appCurrency,
balance = balance,
isBalanceHidden = isBalanceHidden,
isLoading = isLoading,
),
information = getInfo(userWallet = this),
balance = getBalanceInfo(userWallet = this),
imageUrl = artworkUrl,
isEnabled = !isLocked,
endIcon = endIcon,
@ -51,53 +49,40 @@ class UserWalletItemUMConverter(
}
}
private fun UserWallet.getInfo(
appCurrency: AppCurrency?,
balance: TotalFiatBalance?,
isBalanceHidden: Boolean,
isLoading: Boolean,
): TextReference {
val dividerRef = stringReference(value = " $DOT ")
val cardCount = getCardsCount() ?: 1
val cardCountRef = TextReference.PluralRes(
private fun getInfo(userWallet: UserWallet): TextReference {
val cardCount = userWallet.getCardsCount() ?: 1
return TextReference.PluralRes(
id = R.plurals.card_label_card_count,
count = cardCount,
formatArgs = wrappedList(cardCount),
)
return when {
isBalanceHidden -> combinedReference(cardCountRef, dividerRef, TextReference.STARS)
isLocked -> combinedReference(cardCountRef, dividerRef, resourceReference(R.string.common_locked))
isLoading -> cardCountRef
else -> getBalanceInfo(balance, appCurrency, cardCountRef, dividerRef)
}
}
private fun getBalanceInfo(
balance: TotalFiatBalance?,
appCurrency: AppCurrency?,
cardCountRef: TextReference,
dividerRef: TextReference,
): TextReference {
val amount = when (balance) {
is TotalFiatBalance.Loaded -> balance.amount
is TotalFiatBalance.Failed,
is TotalFiatBalance.Loading,
null,
-> null
}
private fun getBalanceInfo(userWallet: UserWallet): UserWalletItemUM.Balance {
return when {
isBalanceHidden -> UserWalletItemUM.Balance.Hidden
userWallet.isLocked -> UserWalletItemUM.Balance.Locked
balance == null -> UserWalletItemUM.Balance.Loading
else -> {
when (balance) {
is TotalFiatBalance.Loading -> UserWalletItemUM.Balance.Loading
is TotalFiatBalance.Failed -> UserWalletItemUM.Balance.Failed
is TotalFiatBalance.Loaded -> {
if (appCurrency != null) {
val formattedAmount = balance.amount.format {
fiat(appCurrency.code, appCurrency.symbol)
}
return if (amount != null && appCurrency != null) {
val formattedAmount = BigDecimalFormatter.formatFiatAmount(
fiatAmount = amount,
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
)
val amountRef = stringReference(formattedAmount)
combinedReference(cardCountRef, dividerRef, amountRef)
} else {
combinedReference(cardCountRef, dividerRef, stringReference(BigDecimalFormatter.EMPTY_BALANCE_SIGN))
UserWalletItemUM.Balance.Loaded(
value = formattedAmount,
isFlickering = isLoading,
)
} else {
UserWalletItemUM.Balance.Failed
}
}
}
}
}
}
}

View file

@ -9,6 +9,7 @@ data class UserWalletItemUM(
val id: UserWalletId,
val name: TextReference,
val information: TextReference,
val balance: Balance,
val imageUrl: String,
val isEnabled: Boolean,
val endIcon: EndIcon = EndIcon.None,
@ -19,4 +20,20 @@ data class UserWalletItemUM(
Arrow,
Checkmark,
}
sealed class Balance {
data object Hidden : Balance()
data object Locked : Balance()
data object Failed : Balance()
data object Loading : Balance()
data class Loaded(
val value: String,
val isFlickering: Boolean,
) : Balance()
}
}

View file

@ -1,6 +1,9 @@
package com.tangem.features.details.component.preview
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.ui.extensions.TextReference
@ -13,55 +16,75 @@ import com.tangem.features.details.entity.UserWalletListUM
import com.tangem.features.details.impl.R
import com.tangem.features.details.ui.UserWalletListBlock
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.delay
internal class PreviewUserWalletListComponent : UserWalletListComponent {
private val previewState = UserWalletListUM(
userWallets = persistentListOf(
UserWalletItemUM(
id = UserWalletId("user_wallet_1".encodeToByteArray()),
name = stringReference("My Wallet"),
information = getInformation(3, "4 496,75 $"),
imageUrl = "",
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_2".encodeToByteArray()),
name = stringReference("Old wallet"),
information = getInformation(3, "4 496,75 $"),
imageUrl = "",
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_3".encodeToByteArray()),
name = stringReference("Multi Card"),
information = getInformation(3, "4 496,75 $"),
imageUrl = "",
isEnabled = false,
onClick = {},
private val previewState = mutableStateOf(
UserWalletListUM(
userWallets = persistentListOf(
UserWalletItemUM(
id = UserWalletId("user_wallet_1".encodeToByteArray()),
name = stringReference("My Wallet"),
information = getInformation(cardCount = 1),
balance = UserWalletItemUM.Balance.Loading,
imageUrl = "",
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_2".encodeToByteArray()),
name = stringReference("Old wallet"),
information = getInformation(cardCount = 2),
balance = UserWalletItemUM.Balance.Loading,
imageUrl = "",
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_3".encodeToByteArray()),
name = stringReference("Multi Card"),
information = getInformation(cardCount = 3),
balance = UserWalletItemUM.Balance.Loading,
imageUrl = "",
isEnabled = false,
onClick = {},
),
),
addNewWalletText = resourceReference(R.string.user_wallet_list_add_button),
isWalletSavingInProgress = false,
onAddNewWalletClick = {},
),
addNewWalletText = resourceReference(R.string.user_wallet_list_add_button),
isWalletSavingInProgress = false,
onAddNewWalletClick = {},
)
@Composable
override fun Content(modifier: Modifier) {
UserWalletListBlock(state = previewState, modifier = modifier)
val state by previewState
UserWalletListBlock(state = state, modifier = modifier)
LaunchedEffect(key1 = this) {
delay(timeMillis = 2_000)
previewState.value = state.copy(
userWallets = state.userWallets.map {
it.copy(
balance = UserWalletItemUM.Balance.Loaded(
value = "1.000 BTC",
isFlickering = true,
),
)
}.toImmutableList(),
)
}
}
private fun getInformation(cardCount: Int, totalBalance: String): TextReference {
val t1 = TextReference.PluralRes(
private fun getInformation(cardCount: Int): TextReference {
return TextReference.PluralRes(
id = R.plurals.card_label_card_count,
count = cardCount,
formatArgs = wrappedList(cardCount),
)
val divider = stringReference(value = "")
val t2 = stringReference(totalBalance)
return TextReference.Combined(wrappedList(t1, divider, t2))
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.features.details.ui
import android.content.res.Configuration
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.CircularProgressIndicator
@ -10,11 +11,17 @@ import androidx.compose.runtime.key
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.tangem.common.ui.userwallet.UserWalletItem
import com.tangem.core.ui.components.block.BlockCard
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.details.component.UserWalletListComponent
import com.tangem.features.details.component.preview.PreviewUserWalletListComponent
import com.tangem.features.details.entity.UserWalletListUM
import com.tangem.features.details.impl.R
@ -85,4 +92,24 @@ private fun AddWalletButton(
)
}
}
}
}
// region Preview
@Composable
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun Preview_UserWalletListBlock(
@PreviewParameter(UserWalletListComponentPreviewProvider::class) component: UserWalletListComponent,
) {
TangemThemePreview {
component.Content(Modifier)
}
}
private class UserWalletListComponentPreviewProvider : PreviewParameterProvider<UserWalletListComponent> {
override val values: Sequence<UserWalletListComponent>
get() = sequenceOf(
PreviewUserWalletListComponent(),
)
}
// endregion Preview

View file

@ -25,7 +25,8 @@ internal class PreviewAddToPortfolioBSContentProvider : PreviewParameterProvider
val userWallet = UserWalletItemUM(
id = UserWalletId("1"),
name = stringReference("Wallet 1"),
information = stringReference("3 cards, 10,123$"),
information = stringReference("3 cards"),
balance = UserWalletItemUM.Balance.Loading,
imageUrl = "",
isEnabled = true,
endIcon = UserWalletItemUM.EndIcon.Arrow,