Updated on 2026-08-14

This commit is contained in:
Tangem 2025-08-20 12:45:49 +02:00
parent 39a190f056
commit f0de027151
23 changed files with 1050 additions and 2 deletions

View file

@ -13,6 +13,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.CircleShimmer
import com.tangem.core.ui.res.TangemTheme
@ -28,11 +29,16 @@ import com.tangem.core.ui.utils.getGreyScaleColorFilter
* @param shouldDisplayNetwork specifies whether to display network badge
*/
@Composable
fun CurrencyIcon(state: CurrencyIconState, modifier: Modifier = Modifier, shouldDisplayNetwork: Boolean = true) {
fun CurrencyIcon(
state: CurrencyIconState,
modifier: Modifier = Modifier,
shouldDisplayNetwork: Boolean = true,
iconSize: Dp = 36.dp,
) {
BaseContainer(modifier = modifier) {
val iconModifier = Modifier
.align(Alignment.Center)
.size(TangemTheme.dimens.size36)
.size(iconSize)
when (state) {
is CurrencyIconState.Loading -> LoadingIcon(modifier = iconModifier)

View file

@ -0,0 +1,103 @@
package com.tangem.core.ui.components.currency.icon
import androidx.annotation.DrawableRes
import androidx.compose.ui.graphics.Color
import com.tangem.core.ui.R
import com.tangem.core.ui.extensions.getTintForTokenIcon
import com.tangem.core.ui.extensions.networkIconResId
import com.tangem.core.ui.extensions.tryGetBackgroundForTokenIcon
import com.tangem.domain.models.currency.CryptoCurrency
object CurrencyIconStateBuilder {
fun build(
cryptoCurrency: CryptoCurrency,
isGrayscale: Boolean = false,
showCustomBadge: Boolean = true,
): CurrencyIconState = when (cryptoCurrency) {
is CryptoCurrency.Coin -> fromCoin(cryptoCurrency, isGrayscale, showCustomBadge)
is CryptoCurrency.Token -> fromToken(cryptoCurrency, isGrayscale, showCustomBadge)
}
private fun createCoinIcon(
url: String? = null,
@DrawableRes fallbackResId: Int = R.drawable.ic_empty_64,
isGrayscale: Boolean = false,
showCustomBadge: Boolean = false,
): CurrencyIconState.CoinIcon = CurrencyIconState.CoinIcon(
url = url,
fallbackResId = fallbackResId,
isGrayscale = isGrayscale,
showCustomBadge = showCustomBadge,
)
private fun createTokenIcon(
url: String? = null,
@DrawableRes topBadgeIconResId: Int? = null,
isGrayscale: Boolean = false,
showCustomBadge: Boolean = false,
fallbackTint: Color = Color.Black,
fallbackBackground: Color = Color.White,
): CurrencyIconState.TokenIcon = CurrencyIconState.TokenIcon(
url = url,
topBadgeIconResId = topBadgeIconResId,
isGrayscale = isGrayscale,
fallbackTint = fallbackTint,
fallbackBackground = fallbackBackground,
showCustomBadge = showCustomBadge,
)
private fun createCustomTokenIcon(
tint: Color,
background: Color,
@DrawableRes topBadgeIconResId: Int,
isGrayscale: Boolean = false,
showCustomBadge: Boolean = true,
): CurrencyIconState.CustomTokenIcon = CurrencyIconState.CustomTokenIcon(
tint = tint,
background = background,
topBadgeIconResId = topBadgeIconResId,
isGrayscale = isGrayscale,
showCustomBadge = showCustomBadge,
)
private fun fromCoin(
coin: CryptoCurrency.Coin,
isGrayscale: Boolean = false,
showCustomBadge: Boolean = true,
): CurrencyIconState.CoinIcon = createCoinIcon(
url = coin.iconUrl,
fallbackResId = coin.networkIconResId,
isGrayscale = isGrayscale || coin.network.isTestnet,
showCustomBadge = coin.isCustom && showCustomBadge,
)
private fun fromToken(
token: CryptoCurrency.Token,
isGrayscale: Boolean = false,
showCustomBadge: Boolean = true,
): CurrencyIconState {
val grayScale = isGrayscale || token.network.isTestnet
val background = token.tryGetBackgroundForTokenIcon(grayScale)
val tint = getTintForTokenIcon(background)
return if (token.isCustom && token.iconUrl == null) {
createCustomTokenIcon(
tint = tint,
background = background,
topBadgeIconResId = token.networkIconResId,
isGrayscale = grayScale,
showCustomBadge = showCustomBadge,
)
} else {
createTokenIcon(
url = token.iconUrl,
topBadgeIconResId = token.networkIconResId,
isGrayscale = grayScale,
fallbackTint = tint,
fallbackBackground = background,
showCustomBadge = token.isCustom && showCustomBadge,
)
}
}
}

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M21.099,15C21.099,13.56 21.098,12.571 20.998,11.828C20.901,11.111 20.729,10.759 20.485,10.514C20.241,10.27 19.889,10.098 19.172,10.002C18.429,9.902 17.439,9.9 16,9.9H15C13.56,9.9 12.571,9.902 11.828,10.002C11.111,10.098 10.759,10.27 10.514,10.514C10.27,10.759 10.098,11.111 10.002,11.828C9.902,12.571 9.9,13.56 9.9,15V16C9.9,17.44 9.902,18.429 10.002,19.172C10.098,19.889 10.27,20.241 10.514,20.485C10.759,20.729 11.111,20.902 11.828,20.998C12.571,21.098 13.56,21.1 15,21.1H16C17.439,21.1 18.429,21.098 19.172,20.998C19.889,20.902 20.241,20.729 20.485,20.485C20.729,20.241 20.901,19.889 20.998,19.172C21.098,18.429 21.099,17.44 21.099,16V15ZM22.9,16C22.9,17.389 22.902,18.521 22.782,19.412C22.659,20.329 22.392,21.123 21.757,21.758C21.123,22.392 20.328,22.659 19.412,22.782C18.521,22.902 17.389,22.9 16,22.9H15C13.611,22.9 12.479,22.902 11.588,22.782C10.671,22.659 9.876,22.392 9.242,21.758C8.608,21.123 8.341,20.329 8.217,19.412C8.098,18.521 8.099,17.389 8.099,16V15C8.099,13.611 8.098,12.479 8.217,11.588C8.341,10.671 8.608,9.876 9.242,9.242C9.876,8.608 10.671,8.341 11.588,8.218C12.479,8.098 13.611,8.099 15,8.099H16C17.389,8.099 18.521,8.098 19.412,8.218C20.328,8.341 21.123,8.608 21.757,9.242C22.392,9.876 22.659,10.671 22.782,11.588C22.902,12.479 22.9,13.611 22.9,15V16Z"
android:fillColor="#919191"/>
<path
android:pathData="M2.099,9.999V8.999L2.099,8.945C2.099,7.581 2.099,6.466 2.217,5.587C2.34,4.67 2.607,3.876 3.241,3.241C3.876,2.607 4.67,2.34 5.587,2.217C6.466,2.099 7.581,2.099 8.945,2.099L8.999,2.099H9.999L10.054,2.099C11.417,2.099 12.532,2.099 13.411,2.217C14.328,2.34 15.123,2.607 15.757,3.241C15.978,3.463 16.16,3.709 16.307,3.983C16.542,4.42 16.378,4.966 15.941,5.201C15.503,5.437 14.957,5.273 14.722,4.835C14.656,4.713 14.579,4.608 14.484,4.514C14.24,4.27 13.888,4.098 13.171,4.001C12.428,3.901 11.439,3.9 9.999,3.9H8.999C7.559,3.9 6.57,3.901 5.827,4.001C5.11,4.098 4.758,4.27 4.514,4.514C4.269,4.758 4.097,5.11 4.001,5.827C3.901,6.57 3.899,7.559 3.899,8.999V9.999C3.899,11.439 3.901,12.428 4.001,13.171C4.097,13.888 4.269,14.24 4.514,14.484C4.692,14.663 4.923,14.797 5.292,14.895C5.772,15.022 6.059,15.515 5.932,15.995C5.812,16.446 5.371,16.726 4.921,16.654L4.831,16.635L4.606,16.569C4.09,16.404 3.633,16.149 3.241,15.757C2.607,15.123 2.34,14.328 2.217,13.411C2.099,12.532 2.099,11.417 2.099,10.054L2.099,9.999Z"
android:fillColor="#919191"/>
</vector>

View file

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="36dp"
android:height="37dp"
android:viewportWidth="36"
android:viewportHeight="37">
<path
android:pathData="M0,18.658C0,8.717 8.059,0.658 18,0.658C27.941,0.658 36,8.717 36,18.658C36,28.599 27.941,36.658 18,36.658C8.059,36.658 0,28.599 0,18.658Z">
<aapt:attr name="android:fillColor">
<gradient
android:startX="7.08"
android:startY="6.638"
android:endX="38.587"
android:endY="33.361"
android:type="linear">
<item android:offset="0" android:color="#FF8BA0FF"/>
<item android:offset="0.524" android:color="#FF65B1FF"/>
<item android:offset="1" android:color="#FF91D0FF"/>
</gradient>
</aapt:attr>
</path>
<group>
<clip-path
android:pathData="M9.314,8.766h17.372v19.784h-17.372z"/>
<path
android:pathData="M17.55,9.187L12.219,17.958C12.177,18.027 12.08,18.035 12.028,17.973C11.559,17.416 9.811,15.045 11.974,12.885C13.948,10.913 16.463,9.507 17.395,9.021C17.501,8.966 17.612,9.085 17.55,9.187Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M17.255,28.285C17.362,28.36 17.493,28.233 17.422,28.124C16.231,26.313 12.273,20.287 11.726,19.383C11.187,18.491 10.126,17.008 10.038,15.739C10.029,15.613 9.854,15.587 9.809,15.706C9.738,15.898 9.663,16.127 9.592,16.389C8.702,19.695 9.995,23.203 12.802,25.168L17.255,28.285Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M17.988,28.129L23.319,19.358C23.36,19.289 23.458,19.281 23.509,19.343C23.979,19.9 25.727,22.271 23.564,24.431C21.589,26.403 19.075,27.809 18.143,28.295C18.037,28.35 17.926,28.231 17.988,28.129Z"
android:fillColor="#ffffff"/>
<path
android:pathData="M18.288,9.03C18.182,8.955 18.051,9.082 18.122,9.191C19.313,11.002 23.271,17.028 23.818,17.932C24.357,18.824 25.418,20.307 25.506,21.576C25.515,21.702 25.69,21.728 25.734,21.609C25.805,21.417 25.881,21.188 25.951,20.926C26.841,17.62 25.549,14.112 22.742,12.147L18.288,9.03Z"
android:fillColor="#ffffff"/>
</group>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M4.816,11.215C3.377,11.215 2.667,10.515 2.667,9.036V4.846C2.667,3.367 3.377,2.667 4.816,2.667H9.066C10.505,2.667 11.215,3.367 11.215,4.846V9.036C11.215,10.515 10.505,11.215 9.066,11.215H4.816ZM14.934,11.215C13.485,11.215 12.785,10.515 12.785,9.036V4.846C12.785,3.367 13.485,2.667 14.934,2.667H19.184C20.624,2.667 21.333,3.367 21.333,4.846V9.036C21.333,10.515 20.624,11.215 19.184,11.215H14.934ZM4.776,9.635H9.096C9.455,9.635 9.635,9.455 9.635,9.086V4.796C9.635,4.426 9.455,4.246 9.096,4.246H4.776C4.416,4.246 4.246,4.426 4.246,4.796V9.086C4.246,9.455 4.416,9.635 4.776,9.635ZM14.894,9.635H19.224C19.584,9.635 19.754,9.455 19.754,9.086V4.796C19.754,4.426 19.584,4.246 19.224,4.246H14.894C14.535,4.246 14.355,4.426 14.355,4.796V9.086C14.355,9.455 14.535,9.635 14.894,9.635ZM6.046,8.046C5.886,8.046 5.836,7.976 5.836,7.796V6.076C5.836,5.906 5.886,5.836 6.046,5.836H7.826C7.976,5.836 8.046,5.906 8.046,6.076V7.796C8.046,7.976 7.976,8.046 7.826,8.046H6.046ZM16.204,8.046C16.054,8.046 16.004,7.976 16.004,7.796V6.076C16.004,5.906 16.054,5.836 16.204,5.836H17.984C18.144,5.836 18.204,5.906 18.204,6.076V7.796C18.204,7.976 18.144,8.046 17.984,8.046H16.204ZM4.816,21.333C3.377,21.333 2.667,20.633 2.667,19.144V14.965C2.667,13.485 3.377,12.785 4.816,12.785H9.066C10.505,12.785 11.215,13.485 11.215,14.965V19.144C11.215,20.633 10.505,21.333 9.066,21.333H4.816ZM13.475,15.454C13.325,15.454 13.275,15.384 13.275,15.204V13.475C13.275,13.315 13.325,13.245 13.475,13.245H15.254C15.414,13.245 15.474,13.315 15.474,13.475V15.204C15.474,15.384 15.414,15.454 15.254,15.454H13.475ZM18.834,15.454C18.674,15.454 18.624,15.384 18.624,15.204V13.475C18.624,13.315 18.674,13.245 18.834,13.245H20.614C20.764,13.245 20.823,13.315 20.823,13.475V15.204C20.823,15.384 20.764,15.454 20.614,15.454H18.834ZM4.776,19.754H9.096C9.455,19.754 9.635,19.574 9.635,19.204V14.915C9.635,14.535 9.455,14.365 9.096,14.365H4.776C4.416,14.365 4.246,14.535 4.246,14.915V19.204C4.246,19.574 4.416,19.754 4.776,19.754ZM16.174,18.124C16.024,18.124 15.974,18.064 15.974,17.884V16.154C15.974,15.984 16.024,15.924 16.174,15.924H17.964C18.114,15.924 18.174,15.984 18.174,16.154V17.884C18.174,18.064 18.114,18.124 17.964,18.124H16.174ZM6.046,18.154C5.886,18.154 5.836,18.094 5.836,17.914V16.184C5.836,16.014 5.886,15.954 6.046,15.954H7.826C7.976,15.954 8.046,16.014 8.046,16.184V17.914C8.046,18.094 7.976,18.154 7.826,18.154H6.046ZM13.475,20.803C13.325,20.803 13.275,20.733 13.275,20.553V18.834C13.275,18.664 13.325,18.594 13.475,18.594H15.254C15.414,18.594 15.474,18.664 15.474,18.834V20.553C15.474,20.733 15.414,20.803 15.254,20.803H13.475ZM18.834,20.803C18.674,20.803 18.624,20.733 18.624,20.553V18.834C18.624,18.664 18.674,18.594 18.834,18.594H20.614C20.764,18.594 20.823,18.664 20.823,18.834V20.553C20.823,20.733 20.764,20.803 20.614,20.803H18.834Z"
android:fillColor="#919191"/>
</vector>