From 18474637b9e74958c0e65a2cce4d6bec9306bd88 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 9 Sep 2025 15:29:49 +0700 Subject: [PATCH] Updated on 2026-08-14 --- .../tangem/common/ui/account/AccountIcon.kt | 132 ++++-------------- .../tangem/common/ui/account/AccountRow.kt | 1 + .../core/ui/components/account/AccountIcon.kt | 132 ++++++++++++++++++ .../components/currency/icon/ContentIcon.kt | 15 ++ .../components/currency/icon/CurrencyIcon.kt | 2 + .../currency/icon/CurrencyIconState.kt | 24 ++++ .../core/ui/components/token/TokenItem.kt | 47 ++++++- .../createedit/ui/AccountCreateEditContent.kt | 1 + 8 files changed, 249 insertions(+), 105 deletions(-) create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/account/AccountIcon.kt diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountIcon.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountIcon.kt index 8a441922f5..377c483f00 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/account/AccountIcon.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountIcon.kt @@ -1,43 +1,31 @@ package com.tangem.common.ui.account -import android.content.res.Configuration -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Icon -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.tangem.common.ui.account.AccountIconPreviewData.randomAccountIcon +import com.tangem.core.ui.components.account.AccountCharIcon +import com.tangem.core.ui.components.account.AccountIconSize +import com.tangem.core.ui.components.account.AccountResIcon import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference -import com.tangem.core.ui.extensions.stringReference -import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.res.TangemThemePreview import com.tangem.domain.models.account.CryptoPortfolioIcon import com.tangem.domain.models.account.CryptoPortfolioIcon.Color -enum class AccountIconSize { - Default, Large, Medium, Small, ExtraSmall -} - /** - * Displays an account icon that can either show a letter (derived from [name]) - * or a predefined vector resource (from [icon]). + * Displays a portfolio account icon that can either show: + * - a single character (derived from the [name]) using [AccountCharIcon], or + * - a predefined vector resource (from [icon]) using [AccountResIcon]. * - * The background color is determined by the icon's [CryptoPortfolioIconUM.color], - * and the icon size, text style, and box modifier are adapted based on the given [size]. + * The background color is taken from [CryptoPortfolioIconUM.color], + * and the icon size, text style, and container shape are adapted based on the given [size]. * - * @param name The text reference used to resolve and display the first letter - * when [icon] is set to [CryptoPortfolioIcon.Icon.Letter]. + * @param name A [TextReference] used to resolve and display the first letter + * when [icon.value] is set to [CryptoPortfolioIcon.Icon.Letter]. * @param icon The account icon definition, which can be a letter or a drawable resource. - * @param size The size of the icon, defined by [AccountIconSize]. + * @param size The icon size, defined by [AccountIconSize]. + * + * @see AccountCharIcon + * @see AccountResIcon + * @see AccountIconSize */ @Composable fun AccountIcon( @@ -46,82 +34,20 @@ fun AccountIcon( size: AccountIconSize, modifier: Modifier = Modifier, ) { - val boxModifier = modifier.selectBoxModifier(size) - val iconSize = Modifier.selectIconSize(size) - val textStyle = when (size) { - AccountIconSize.Default -> TangemTheme.typography.h3 - AccountIconSize.Large -> TangemTheme.typography.h1 - AccountIconSize.Medium -> TangemTheme.typography.subtitle1 - AccountIconSize.Small -> TangemTheme.typography.subtitle2 - AccountIconSize.ExtraSmall -> TangemTheme.typography.caption1 - } - Box( - contentAlignment = Alignment.Center, - modifier = boxModifier.background(icon.color.getUiColor()), - ) { - val icon = icon.value - val letter = name.resolveReference().firstOrNull() - when { - icon == CryptoPortfolioIcon.Icon.Letter -> Text( - text = letter?.uppercase() ?: "", - style = textStyle, - color = TangemTheme.colors.text.constantWhite, - ) - else -> Icon( - modifier = iconSize, - tint = TangemTheme.colors.text.constantWhite, - imageVector = ImageVector.vectorResource(id = icon.getResId()), - contentDescription = null, - ) - } - } -} - -private fun Modifier.selectIconSize(size: AccountIconSize): Modifier = when (size) { - AccountIconSize.Default -> this.size(20.dp) - AccountIconSize.Large -> this.size(40.dp) - AccountIconSize.Medium -> this.size(16.dp) - AccountIconSize.Small -> this.size(12.dp) - AccountIconSize.ExtraSmall -> this.size(8.dp) -} - -private fun Modifier.selectBoxModifier(size: AccountIconSize): Modifier = when (size) { - AccountIconSize.Default -> size(36.dp).clip(RoundedCornerShape(10.dp)) - AccountIconSize.Large -> size(88.dp).clip(RoundedCornerShape(24.dp)) - AccountIconSize.Medium -> size(28.dp).clip(RoundedCornerShape(8.dp)) - AccountIconSize.Small -> size(20.dp).clip(RoundedCornerShape(6.dp)) - AccountIconSize.ExtraSmall -> size(14.dp).clip(RoundedCornerShape(4.dp)) -} - -@Preview(showBackground = true) -@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) -@Composable -private fun Preview_AccountIcon() { - TangemThemePreview { - Sample() - } -} - -@Composable -private fun Sample() { - val name = stringReference("Account Name") - Row( - modifier = Modifier.background(TangemTheme.colors.background.primary), - ) { - Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) { - AccountIcon(name = name, randomAccountIcon(), size = AccountIconSize.Default) - AccountIcon(name = name, randomAccountIcon(), size = AccountIconSize.Large) - AccountIcon(name = name, randomAccountIcon(), size = AccountIconSize.Medium) - AccountIcon(name = name, randomAccountIcon(), size = AccountIconSize.Small) - AccountIcon(name = name, randomAccountIcon(), size = AccountIconSize.ExtraSmall) - } - Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) { - AccountIcon(name = name, randomAccountIcon(letter = true), size = AccountIconSize.Default) - AccountIcon(name = name, randomAccountIcon(letter = true), size = AccountIconSize.Large) - AccountIcon(name = name, randomAccountIcon(letter = true), size = AccountIconSize.Medium) - AccountIcon(name = name, randomAccountIcon(letter = true), size = AccountIconSize.Small) - AccountIcon(name = name, randomAccountIcon(letter = true), size = AccountIconSize.ExtraSmall) - } + val letter = name.resolveReference().firstOrNull() + when { + icon.value == CryptoPortfolioIcon.Icon.Letter -> AccountCharIcon( + char = letter ?: 'N', + color = icon.color.getUiColor(), + size = size, + modifier = modifier, + ) + else -> AccountResIcon( + resId = icon.value.getResId(), + color = icon.color.getUiColor(), + size = size, + modifier = modifier, + ) } } diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountRow.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountRow.kt index d02fe62c15..0b51e38465 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/account/AccountRow.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountRow.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import com.tangem.common.ui.R +import com.tangem.core.ui.components.account.AccountIconSize import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.resourceReference diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/account/AccountIcon.kt b/core/ui/src/main/java/com/tangem/core/ui/components/account/AccountIcon.kt new file mode 100644 index 0000000000..ab7844aaa5 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/account/AccountIcon.kt @@ -0,0 +1,132 @@ +package com.tangem.core.ui.components.account + +import android.content.res.Configuration +import androidx.annotation.DrawableRes +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.tangem.core.ui.R +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview + +enum class AccountIconSize { + Default, Large, Medium, Small, ExtraSmall +} + +/** + * Displays a portfolio icon using a predefined vector resource. + * + * The background color is defined by [color], and the icon tint is constant white. + * The icon size and container shape are adapted based on the provided [size]. + * + * @param resId The vector drawable resource ID to display. + * @param color The background color of the icon container. + * @param size The size of the icon, defined by [AccountIconSize]. + */ +@Composable +fun AccountResIcon(@DrawableRes resId: Int, color: Color, size: AccountIconSize, modifier: Modifier = Modifier) { + val boxModifier = modifier.selectBoxModifier(size) + val iconSize = Modifier.selectIconSize(size) + Box( + contentAlignment = Alignment.Center, + modifier = boxModifier.background(color), + ) { + Icon( + modifier = iconSize, + tint = TangemTheme.colors.text.constantWhite, + imageVector = ImageVector.vectorResource(id = resId), + contentDescription = null, + ) + } +} + +/** + * Displays a portfolio icon using a single character. + * + * The background color is defined by [color], the text is uppercased, + * and its style is chosen based on the provided [size]. + * The container shape and size also adapt to [size]. + * + * @param char The character to display inside the icon. + * @param color The background color of the icon container. + * @param size The size of the icon, defined by [AccountIconSize]. + */ +@Composable +fun AccountCharIcon(char: Char, color: Color, size: AccountIconSize, modifier: Modifier = Modifier) { + val boxModifier = modifier.selectBoxModifier(size) + val textStyle = when (size) { + AccountIconSize.Default -> TangemTheme.typography.h3 + AccountIconSize.Large -> TangemTheme.typography.h1 + AccountIconSize.Medium -> TangemTheme.typography.subtitle1 + AccountIconSize.Small -> TangemTheme.typography.subtitle2 + AccountIconSize.ExtraSmall -> TangemTheme.typography.caption1 + } + Box( + contentAlignment = Alignment.Center, + modifier = boxModifier.background(color), + ) { + Text( + text = char.uppercase(), + style = textStyle, + color = TangemTheme.colors.text.constantWhite, + ) + } +} + +private fun Modifier.selectIconSize(size: AccountIconSize): Modifier = when (size) { + AccountIconSize.Default -> this.size(20.dp) + AccountIconSize.Large -> this.size(40.dp) + AccountIconSize.Medium -> this.size(16.dp) + AccountIconSize.Small -> this.size(12.dp) + AccountIconSize.ExtraSmall -> this.size(8.dp) +} + +private fun Modifier.selectBoxModifier(size: AccountIconSize): Modifier = when (size) { + AccountIconSize.Default -> size(36.dp).clip(RoundedCornerShape(10.dp)) + AccountIconSize.Large -> size(88.dp).clip(RoundedCornerShape(24.dp)) + AccountIconSize.Medium -> size(28.dp).clip(RoundedCornerShape(8.dp)) + AccountIconSize.Small -> size(20.dp).clip(RoundedCornerShape(6.dp)) + AccountIconSize.ExtraSmall -> size(14.dp).clip(RoundedCornerShape(4.dp)) +} + +@Preview(showBackground = true) +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun Preview() { + TangemThemePreview { + Row( + modifier = Modifier.background(TangemTheme.colors.background.primary), + ) { + Sample() + } + } +} + +@Composable +private fun Sample() { + Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) { + AccountResIcon(resId = R.drawable.ic_shirt_24, color = Color.Red, size = AccountIconSize.Default) + AccountResIcon(resId = R.drawable.ic_rounded_star_24, color = Color.Blue, size = AccountIconSize.Large) + AccountResIcon(resId = R.drawable.ic_user_24, color = Color.Magenta, size = AccountIconSize.Medium) + AccountResIcon(resId = R.drawable.ic_family_24, color = Color.DarkGray, size = AccountIconSize.Small) + AccountResIcon(resId = R.drawable.ic_wallet_24, color = Color.Green, size = AccountIconSize.ExtraSmall) + } + Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) { + AccountCharIcon(char = 'D', color = Color.Red, size = AccountIconSize.Default) + AccountCharIcon(char = 'L', color = Color.Blue, size = AccountIconSize.Large) + AccountCharIcon(char = 'M', color = Color.Magenta, size = AccountIconSize.Medium) + AccountCharIcon(char = 'S', color = Color.DarkGray, size = AccountIconSize.Small) + AccountCharIcon(char = 'E', color = Color.Green, size = AccountIconSize.ExtraSmall) + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/ContentIcon.kt b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/ContentIcon.kt index 52d7c920a6..4213831216 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/ContentIcon.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/ContentIcon.kt @@ -13,6 +13,9 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource import com.tangem.core.ui.R +import com.tangem.core.ui.components.account.AccountCharIcon +import com.tangem.core.ui.components.account.AccountIconSize +import com.tangem.core.ui.components.account.AccountResIcon import com.tangem.core.ui.components.currency.DefaultCurrencyIcon import com.tangem.core.ui.res.TangemTheme @@ -58,6 +61,18 @@ internal fun ContentIcon( background = icon.background, alpha = alpha, ) + is CurrencyIconState.CryptoPortfolio.Icon -> AccountResIcon( + modifier = modifier, + resId = icon.resId, + color = icon.color, + size = AccountIconSize.Default, + ) + is CurrencyIconState.CryptoPortfolio.Letter -> AccountCharIcon( + modifier = modifier, + char = icon.char, + color = icon.color, + size = AccountIconSize.Default, + ) CurrencyIconState.Loading, CurrencyIconState.Locked, is CurrencyIconState.Empty, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIcon.kt b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIcon.kt index ff97530009..04ede1d5ba 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIcon.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIcon.kt @@ -48,6 +48,8 @@ fun CurrencyIcon( is CurrencyIconState.FiatIcon, is CurrencyIconState.CustomTokenIcon, is CurrencyIconState.TokenIcon, + is CurrencyIconState.CryptoPortfolio.Icon, + is CurrencyIconState.CryptoPortfolio.Letter, -> { ContentIconContainer( icon = state, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIconState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIconState.kt index 5ebf4ad190..2075631d63 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIconState.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIconState.kt @@ -86,6 +86,24 @@ sealed class CurrencyIconState { override val topBadgeIconResId: Int? = null } + sealed class CryptoPortfolio : CurrencyIconState() { + override val showCustomBadge: Boolean = false + override val topBadgeIconResId: Int? = null + abstract val color: Color + + data class Icon( + @DrawableRes val resId: Int, + override val color: Color, + override val isGrayscale: Boolean, + ) : CryptoPortfolio() + + data class Letter( + val char: Char, + override val color: Color, + override val isGrayscale: Boolean, + ) : CryptoPortfolio() + } + data object Loading : CurrencyIconState() { override val isGrayscale: Boolean = false override val showCustomBadge: Boolean = false @@ -125,6 +143,12 @@ sealed class CurrencyIconState { showCustomBadge = showCustomBadge, topBadgeIconResId = topBadgeIconResId, ) + is CryptoPortfolio.Icon -> copy( + isGrayscale = isGrayscale, + ) + is CryptoPortfolio.Letter -> copy( + isGrayscale = isGrayscale, + ) is Loading, is Locked, is Empty, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt index 284885331e..3c74bb41fd 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/token/TokenItem.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.composed +import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.Measurable import androidx.compose.ui.layout.Placeable @@ -309,7 +310,13 @@ private fun CustomContainer(state: TokenItemState, modifier: Modifier = Modifier }, ) - fiatAmount?.placeRelative(x = layoutWidth - fiatAmount.width, y = verticalPadding) + fiatAmount?.placeRelative( + x = layoutWidth - fiatAmount.width, + y = when (state.subtitle2State) { + null -> (layoutHeight - fiatAmount.height).div(other = 2) + else -> verticalPadding + }, + ) priceChange?.placeRelative( x = icon.width, @@ -462,7 +469,7 @@ private fun calculateLayoutHeight( return max(firstColumnHeight, secondColumnHeight).coerceAtLeast(minLayoutHeight) } -@Preview(widthDp = 360) +@Preview(widthDp = 360, showBackground = true) @Composable private fun Preview_TokenItem_InLight(@PreviewParameter(TokenItemStateProvider::class) state: TokenItemState) { TangemThemePreview(isDark = false) { @@ -589,6 +596,8 @@ private class TokenItemStateProvider : CollectionPreviewParameterProvider