Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-20 19:03:25 +04:00
parent 91942930d6
commit 6b75b48f1e
61 changed files with 193 additions and 436 deletions

View file

@ -33,6 +33,7 @@ dependencies {
implementation(projects.core.utils)
implementation(projects.core.res)
implementation(projects.libs.crypto)
implementation(projects.libs.blockchainSdk)
/** Project - Domain */
implementation(projects.domain.appCurrency.models)

View file

@ -6,7 +6,7 @@ import com.tangem.common.ui.amountScreen.converters.field.AmountFieldConverter
import com.tangem.common.ui.amountScreen.models.AmountParameters
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.combinedReference
import com.tangem.core.ui.extensions.orMaskWithStars

View file

@ -0,0 +1,104 @@
package com.tangem.common.ui.components.currency.icon
import androidx.annotation.DrawableRes
import androidx.compose.ui.graphics.Color
import com.tangem.common.ui.extensions.networkIconResId
import com.tangem.core.ui.R
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.extensions.getTintForTokenIcon
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,
shouldShowCustomBadge = 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,
shouldShowCustomBadge = 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,
shouldShowCustomBadge = 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 isGrayscaleOrTestnet = isGrayscale || token.network.isTestnet
val background = token.tryGetBackgroundForTokenIcon(isGrayscaleOrTestnet)
val tint = getTintForTokenIcon(background)
return if (token.isCustom && token.iconUrl == null) {
createCustomTokenIcon(
tint = tint,
background = background,
topBadgeIconResId = token.networkIconResId,
isGrayscale = isGrayscaleOrTestnet,
showCustomBadge = showCustomBadge,
)
} else {
createTokenIcon(
url = token.iconUrl,
topBadgeIconResId = token.networkIconResId,
isGrayscale = isGrayscaleOrTestnet,
fallbackTint = tint,
fallbackBackground = background,
showCustomBadge = token.isCustom && showCustomBadge,
)
}
}
}

View file

@ -0,0 +1,98 @@
package com.tangem.common.ui.components.currency.icon.converter
import com.tangem.common.ui.extensions.networkIconResId
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.extensions.getTintForTokenIcon
import com.tangem.core.ui.extensions.tryGetBackgroundForTokenIcon
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.utils.converter.Converter
/**
* Converts [CryptoCurrencyStatus] to [CurrencyIconState]
*
* @property isAvailable flag that indicates if the currency is available (affects on icon's grayscale)
*/
class CryptoCurrencyToIconStateConverter(
private val isAvailable: Boolean = true,
) : Converter<CryptoCurrencyStatus, CurrencyIconState> {
override fun convert(value: CryptoCurrencyStatus): CurrencyIconState {
return when (val currency = value.currency) {
is CryptoCurrency.Coin -> getIconStateForCoin(currency, value.value.isError)
is CryptoCurrency.Token -> getIconStateForToken(currency, value.value.isError)
}
}
fun convertCustom(
value: CryptoCurrencyStatus,
forceGrayscale: Boolean,
showCustomTokenBadge: Boolean,
): CurrencyIconState {
return when (val currency = value.currency) {
is CryptoCurrency.Coin -> getIconStateForCoin(
coin = currency,
isUnreachable = value.value.isError,
forceGrayscale = forceGrayscale,
showCustomBadge = showCustomTokenBadge,
)
is CryptoCurrency.Token -> getIconStateForToken(
token = currency,
isErrorStatus = value.value.isError,
forceGrayscale = forceGrayscale,
showCustomBadge = showCustomTokenBadge,
)
}
}
fun convert(currency: CryptoCurrency): CurrencyIconState {
return when (currency) {
is CryptoCurrency.Coin -> getIconStateForCoin(currency, isUnreachable = false)
is CryptoCurrency.Token -> getIconStateForToken(currency, isErrorStatus = false)
}
}
private fun getIconStateForCoin(
coin: CryptoCurrency.Coin,
isUnreachable: Boolean,
showCustomBadge: Boolean = true,
forceGrayscale: Boolean = false,
): CurrencyIconState.CoinIcon {
return CurrencyIconState.CoinIcon(
url = coin.iconUrl,
fallbackResId = coin.networkIconResId,
isGrayscale = forceGrayscale || coin.network.isTestnet || isUnreachable || !isAvailable,
shouldShowCustomBadge = coin.isCustom && showCustomBadge,
)
}
private fun getIconStateForToken(
token: CryptoCurrency.Token,
isErrorStatus: Boolean,
showCustomBadge: Boolean = true,
forceGrayscale: Boolean = false,
): CurrencyIconState {
val isGrayscale = forceGrayscale || token.network.isTestnet || isErrorStatus || !isAvailable
val background = token.tryGetBackgroundForTokenIcon(isGrayscale)
val tint = getTintForTokenIcon(background)
return if (token.isCustom && token.iconUrl == null) {
CurrencyIconState.CustomTokenIcon(
tint = tint,
background = background,
topBadgeIconResId = token.networkIconResId,
isGrayscale = isGrayscale,
shouldShowCustomBadge = showCustomBadge,
)
} else {
CurrencyIconState.TokenIcon(
url = token.iconUrl,
topBadgeIconResId = token.networkIconResId,
isGrayscale = isGrayscale,
fallbackTint = tint,
fallbackBackground = background,
shouldShowCustomBadge = token.isCustom && showCustomBadge,
)
}
}
}

View file

@ -0,0 +1,62 @@
package com.tangem.common.ui.extensions
import androidx.annotation.DrawableRes
import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
/**
* Retrieves the active icon drawable resource for the network of a [CryptoCurrency].
*/
@get:DrawableRes
val CryptoCurrency.networkIconResId: Int
get() = network.iconResId
/**
* Retrieves the greyed-out icon drawable resource for the network of a [CryptoCurrency].
*/
@get:DrawableRes
val CryptoCurrency.networkGreyedOutIconResId: Int
get() = network.greyedOutIconResId
/**
* Retrieves the active icon drawable resource for this [Network].
*/
@get:DrawableRes
val Network.iconResId: Int
get() = id.iconResId
/**
* Retrieves the greyed-out icon drawable resource for this [Network].
*/
@get:DrawableRes
val Network.greyedOutIconResId: Int
get() = id.greyedOutIconResId
/**
* Retrieves the active icon drawable resource for this [Network.ID].
*/
@get:DrawableRes
val Network.ID.iconResId: Int
get() = rawId.iconResId
/**
* Retrieves the greyed-out icon drawable resource for this [Network.ID].
*/
@get:DrawableRes
val Network.ID.greyedOutIconResId: Int
get() = rawId.greyedOutIconResId
/**
* Retrieves the active icon drawable resource for this [Network.RawID].
*/
@get:DrawableRes
val Network.RawID.iconResId: Int
get() = getActiveIconRes(toBlockchain())
/**
* Retrieves the greyed-out icon drawable resource for this [Network.RawID].
*/
@get:DrawableRes
val Network.RawID.greyedOutIconResId: Int
get() = getGreyedOutIconRes(toBlockchain())

View file

@ -4,7 +4,7 @@ import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.common.ui.R
import com.tangem.common.ui.amountScreen.models.AmountFieldModel
import com.tangem.common.ui.amountScreen.utils.getFiatString
import com.tangem.core.ui.extensions.networkIconResId
import com.tangem.common.ui.extensions.networkIconResId
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.uncapped

View file

@ -4,7 +4,7 @@ import com.tangem.common.getTotalCryptoAmount
import com.tangem.common.getTotalFiatAmount
import com.tangem.common.ui.R
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.components.icons.IconTint
import com.tangem.core.ui.components.marketprice.PriceChangeType
import com.tangem.core.ui.components.marketprice.utils.PriceChangeConverter