Updated on 2026-08-14

This commit is contained in:
Tangem 2023-10-02 13:21:24 +05:00
parent e1f80e7e41
commit 324f365771
14 changed files with 310 additions and 144 deletions

View file

@ -7,10 +7,7 @@ import com.tangem.common.flatMap
import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.core.ui.extensions.networkIconResId
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.tokendetails.navigation.TokenDetailsArguments
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Token.ButtonRemoveToken
@ -20,7 +17,6 @@ import com.tangem.tap.common.extensions.dispatchErrorNotification
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.converters.CryptoCurrencyConverter
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletState
@ -44,7 +40,7 @@ class MultiWalletMiddleware {
is WalletAction.MultiWallet.SelectWallet -> {
if (action.currency != null) {
val bundle = bundleOf(
TokenDetailsRouter.TOKEN_DETAILS_ARGS to createTokenDetailsArgument(action.currency),
TokenDetailsRouter.CRYPTO_CURRENCY_KEY to cryptoCurrencyConverter.convert(action.currency),
)
store.dispatch(NavigationAction.NavigateTo(screen = AppScreen.WalletDetails, bundle = bundle))
}
@ -133,23 +129,4 @@ class MultiWalletMiddleware {
store.state.globalState.tapWalletManager.loadData(updatedUserWallet, refresh = true)
}
}
private fun createTokenDetailsArgument(currency: Currency): TokenDetailsArguments {
val cryptoCurrency = cryptoCurrencyConverter.convert(currency)
return TokenDetailsArguments(
currencyId = cryptoCurrency.id,
currencyName = cryptoCurrency.name,
currencySymbol = cryptoCurrency.symbol,
iconUrl = cryptoCurrency.iconUrl,
coinType = when (cryptoCurrency) {
is CryptoCurrency.Coin -> TokenDetailsArguments.CoinType.Native
is CryptoCurrency.Token -> TokenDetailsArguments.CoinType.Token(
isCustom = cryptoCurrency.isCustom,
standardName = cryptoCurrency.network.standardType.name,
networkName = cryptoCurrency.network.name,
networkIcon = cryptoCurrency.networkIconResId,
)
},
)
}
}

View file

@ -14,7 +14,8 @@ import kotlinx.parcelize.Parcelize
* @property iconUrl Optional URL of the cryptocurrency icon. `null` if not found.
* @property isCustom Indicates whether the currency is a custom user-added currency or not.
*/
sealed class CryptoCurrency {
@Parcelize
sealed class CryptoCurrency : Parcelable {
abstract val id: ID
abstract val network: Network

View file

@ -1,5 +1,8 @@
package com.tangem.domain.tokens.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
/**
* Represents a blockchain network, identified by a unique ID, a human-readable name, and its standard type.
*
@ -13,13 +16,14 @@ package com.tangem.domain.tokens.model
* @property isTestnet Indicates whether the network is a test network or a main network.
* @property standardType The type of blockchain standard the network adheres to.
*/
@Parcelize
data class Network(
val id: ID,
val name: String,
val derivationPath: DerivationPath,
val isTestnet: Boolean,
val standardType: StandardType,
) {
) : Parcelable {
init {
require(name.isNotBlank()) { "Network name must not be blank" }
@ -31,7 +35,8 @@ data class Network(
* @property value The string representation of the network ID.
*/
@JvmInline
value class ID(val value: String) {
@Parcelize
value class ID(val value: String) : Parcelable {
init {
require(value.isNotBlank()) { "Network ID must not be blank" }
@ -44,7 +49,8 @@ data class Network(
* This class represents such paths in a generic manner, allowing for predefined card-based paths,
* custom paths, or even no derivation path at all.
*/
sealed class DerivationPath {
@Parcelize
sealed class DerivationPath : Parcelable {
/** The actual derivation path value, if any. */
abstract val value: String?
@ -67,7 +73,7 @@ data class Network(
* Represents a lack of derivation path.
*/
object None : DerivationPath() {
override val value: String? = null
override val value: String? get() = null
}
}
@ -80,27 +86,28 @@ data class Network(
*
* @property name The human-readable name of the standard type.
*/
sealed class StandardType {
@Parcelize
sealed class StandardType : Parcelable {
abstract val name: String
/** Represents the ERC20 token standard, common on the Ethereum network. */
object ERC20 : StandardType() {
override val name: String = "ERC20"
override val name: String get() = "ERC20"
}
/** Represents the TRC20 token standard, common on the TRON network. */
object TRC20 : StandardType() {
override val name: String = "TRC20"
override val name: String get() = "TRC20"
}
/** Represents the BEP20 token standard, common on the Binance Smart Chain network. */
object BEP20 : StandardType() {
override val name: String = "BEP20"
override val name: String get() = "BEP20"
}
/** Represents the BEP2 token standard, common on the Binance Chain network. */
object BEP2 : StandardType() {
override val name: String = "BEP2"
override val name: String get() = "BEP2"
}
/** Represents a network that does not adhere to a predefined standard type. */

View file

@ -1,33 +0,0 @@
package com.tangem.features.tokendetails.navigation
import android.os.Parcelable
import androidx.annotation.DrawableRes
import com.tangem.domain.tokens.model.CryptoCurrency
import kotlinx.parcelize.Parcelize
@Parcelize
data class TokenDetailsArguments(
val currencyId: CryptoCurrency.ID,
val currencyName: String,
val currencySymbol: String,
val iconUrl: String?,
val coinType: CoinType,
) : Parcelable {
@Parcelize
sealed class CoinType : Parcelable {
object Native : CoinType()
/**
* @param isCustom - Indicates whether the currency is a custom user-added currency or not.
* @param standardName - token standard. Samples: ERC20, BEP20, BEP2, TRC20 and etc.
* @param networkName - token's blockchain name. Ethereum, Tron and etc.
*/
data class Token(
val isCustom: Boolean,
val standardName: String,
val networkName: String,
@DrawableRes val networkIcon: Int,
) : CoinType()
}
}

View file

@ -7,6 +7,6 @@ interface TokenDetailsRouter {
fun getEntryFragment(): Fragment
companion object {
const val TOKEN_DETAILS_ARGS = "token_details_args"
const val CRYPTO_CURRENCY_KEY = "token_details_crypto_currency"
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.feature.tokendetails.presentation.tokendetails
import androidx.compose.ui.graphics.Color
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.extensions.TextReference
@ -32,12 +33,21 @@ internal object TokenDetailsPreviewData {
val tokenInfoBlockStateWithLongNameInMainCurrency = TokenInfoBlockState(
name = "Stellar (XLM) with long name test",
iconUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/stellar.png",
iconState = TokenInfoBlockState.IconState.CoinIcon(
url = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/stellar.png",
fallbackResId = R.drawable.img_stellar_22,
isGrayscale = false,
),
currency = TokenInfoBlockState.Currency.Native,
)
val tokenInfoBlockStateWithLongName = TokenInfoBlockState(
name = "Tether (USDT) with long name test",
iconUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/stellar.png",
iconState = TokenInfoBlockState.IconState.TokenIcon(
url = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/stellar.png",
fallbackTint = Color.Cyan,
fallbackBackground = Color.Blue,
isGrayscale = false,
),
currency = TokenInfoBlockState.Currency.Token(
standardName = "ERC20",
networkIcon = R.drawable.img_eth_22,
@ -47,7 +57,11 @@ internal object TokenDetailsPreviewData {
val tokenInfoBlockState = TokenInfoBlockState(
name = "Tether USDT",
iconUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/tether.png",
iconState = TokenInfoBlockState.IconState.CustomTokenIcon(
tint = Color.Green,
background = Color.Magenta,
isGrayscale = true,
),
currency = TokenInfoBlockState.Currency.Token(
standardName = "ERC20",
networkIcon = R.drawable.img_eth_22,

View file

@ -1,12 +1,15 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Color
internal data class TokenInfoBlockState(
val name: String,
val iconUrl: String?,
val iconState: IconState,
val currency: Currency,
) {
@Immutable
sealed class Currency {
object Native : Currency()
@ -21,4 +24,29 @@ internal data class TokenInfoBlockState(
@DrawableRes val networkIcon: Int,
) : Currency()
}
@Immutable
sealed class IconState {
abstract val isGrayscale: Boolean
data class CoinIcon(
val url: String?,
@DrawableRes val fallbackResId: Int,
override val isGrayscale: Boolean,
) : IconState()
data class TokenIcon(
val url: String?,
val fallbackTint: Color,
val fallbackBackground: Color,
override val isGrayscale: Boolean,
) : IconState()
data class CustomTokenIcon(
val tint: Color,
val background: Color,
override val isGrayscale: Boolean,
) : IconState()
}
}

View file

@ -0,0 +1,47 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
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.tokens.model.CryptoCurrency
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenInfoBlockState
import com.tangem.utils.converter.Converter
internal class TokenDetailsIconStateConverter : Converter<CryptoCurrency, TokenInfoBlockState.IconState> {
override fun convert(value: CryptoCurrency): TokenInfoBlockState.IconState {
return when (value) {
is CryptoCurrency.Coin -> getIconStateForCoin(value)
is CryptoCurrency.Token -> getIconStateForToken(value)
}
}
private fun getIconStateForCoin(coin: CryptoCurrency.Coin): TokenInfoBlockState.IconState.CoinIcon {
return TokenInfoBlockState.IconState.CoinIcon(
url = coin.iconUrl,
fallbackResId = coin.networkIconResId,
isGrayscale = coin.network.isTestnet,
)
}
private fun getIconStateForToken(token: CryptoCurrency.Token): TokenInfoBlockState.IconState {
val isGrayscale = token.network.isTestnet
val background = token.tryGetBackgroundForTokenIcon(isGrayscale)
val tint = getTintForTokenIcon(background)
return if (token.isCustom) {
TokenInfoBlockState.IconState.CustomTokenIcon(
tint = tint,
background = background,
isGrayscale = isGrayscale,
)
} else {
TokenInfoBlockState.IconState.TokenIcon(
url = token.iconUrl,
isGrayscale = isGrayscale,
fallbackTint = tint,
fallbackBackground = background,
)
}
}
}

View file

@ -3,13 +3,14 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
import com.tangem.core.ui.components.marketprice.MarketPriceBlockState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.networkIconResId
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.feature.tokendetails.presentation.tokendetails.state.*
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsPullToRefreshConfig
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.features.tokendetails.impl.R
import com.tangem.features.tokendetails.navigation.TokenDetailsArguments
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@ -17,29 +18,30 @@ import kotlinx.coroutines.flow.MutableStateFlow
internal class TokenDetailsSkeletonStateConverter(
private val clickIntents: TokenDetailsClickIntents,
) : Converter<TokenDetailsArguments, TokenDetailsState> {
) : Converter<CryptoCurrency, TokenDetailsState> {
override fun convert(value: TokenDetailsArguments): TokenDetailsState {
val coinType = value.coinType
private val iconStateConverter by lazy { TokenDetailsIconStateConverter() }
override fun convert(value: CryptoCurrency): TokenDetailsState {
return TokenDetailsState(
topAppBarConfig = TokenDetailsTopAppBarConfig(
onBackClick = clickIntents::onBackClick,
tokenDetailsAppBarMenuConfig = createMenu(),
),
tokenInfoBlockState = TokenInfoBlockState(
name = value.currencyName,
iconUrl = value.iconUrl,
currency = when (coinType) {
TokenDetailsArguments.CoinType.Native -> TokenInfoBlockState.Currency.Native
is TokenDetailsArguments.CoinType.Token -> TokenInfoBlockState.Currency.Token(
standardName = coinType.networkName,
networkName = coinType.networkName,
networkIcon = coinType.networkIcon,
name = value.name,
iconState = iconStateConverter.convert(value),
currency = when (value) {
is CryptoCurrency.Coin -> TokenInfoBlockState.Currency.Native
is CryptoCurrency.Token -> TokenInfoBlockState.Currency.Token(
standardName = value.network.standardType.name,
networkName = value.network.name,
networkIcon = value.networkIconResId,
)
},
),
tokenBalanceBlockState = TokenDetailsBalanceBlockState.Loading(actionButtons = createButtons()),
marketPriceBlockState = MarketPriceBlockState.Loading(value.currencySymbol),
marketPriceBlockState = MarketPriceBlockState.Loading(value.symbol),
notifications = persistentListOf(),
pendingTxs = persistentListOf(),
txHistoryState = TxHistoryState.Content(
@ -51,7 +53,7 @@ internal class TokenDetailsSkeletonStateConverter(
pullToRefreshConfig = createPullToRefresh(),
bottomSheetConfig = null,
isBalanceHidden = true,
isCustomToken = coinType is TokenDetailsArguments.CoinType.Token && coinType.isCustom,
isCustomToken = value.isCustom,
)
}

View file

@ -22,15 +22,14 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.component
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadedTxHistoryConverter
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
import com.tangem.features.tokendetails.navigation.TokenDetailsArguments
import kotlinx.coroutines.flow.Flow
internal class TokenDetailsStateFactory(
private val currentStateProvider: Provider<TokenDetailsState>,
private val appCurrencyProvider: Provider<AppCurrency>,
private val clickIntents: TokenDetailsClickIntents,
currencySymbolProvider: Provider<String>,
currencyDecimalsProvider: Provider<Int>,
symbol: String,
decimals: Int,
) {
private val skeletonStateConverter by lazy {
@ -45,8 +44,8 @@ internal class TokenDetailsStateFactory(
TokenDetailsLoadedBalanceConverter(
currentStateProvider = currentStateProvider,
appCurrencyProvider = appCurrencyProvider,
symbol = currencySymbolProvider(),
decimals = currencyDecimalsProvider(),
symbol = symbol,
decimals = decimals,
)
}
@ -65,8 +64,8 @@ internal class TokenDetailsStateFactory(
TokenDetailsLoadedTxHistoryConverter(
currentStateProvider = currentStateProvider,
clickIntents = clickIntents,
symbol = currencySymbolProvider(),
decimals = currencyDecimalsProvider(),
symbol = symbol,
decimals = decimals,
)
}
@ -76,7 +75,7 @@ internal class TokenDetailsStateFactory(
)
}
fun getInitialState(screenArgument: TokenDetailsArguments): TokenDetailsState {
fun getInitialState(screenArgument: CryptoCurrency): TokenDetailsState {
return skeletonStateConverter.convert(value = screenArgument)
}

View file

@ -0,0 +1,146 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import coil.compose.SubcomposeAsyncImage
import coil.request.ImageRequest
import com.tangem.core.ui.components.CircleShimmer
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenInfoBlockState
import com.tangem.features.tokendetails.impl.R
@Composable
internal fun CurrencyIcon(
icon: TokenInfoBlockState.IconState,
alpha: Float,
colorFilter: ColorFilter?,
modifier: Modifier = Modifier,
) {
when (icon) {
is TokenInfoBlockState.IconState.CoinIcon -> CoinIcon(
modifier = modifier,
url = icon.url,
fallbackResId = icon.fallbackResId,
alpha = alpha,
colorFilter = colorFilter,
)
is TokenInfoBlockState.IconState.TokenIcon -> TokenIcon(
modifier = modifier,
url = icon.url,
alpha = alpha,
colorFilter = colorFilter,
errorIcon = {
CustomTokenIcon(
modifier = modifier,
tint = icon.fallbackTint,
background = icon.fallbackBackground,
alpha = alpha,
)
},
)
is TokenInfoBlockState.IconState.CustomTokenIcon -> CustomTokenIcon(
modifier = modifier,
tint = icon.tint,
background = icon.background,
alpha = alpha,
)
}
}
@Composable
private fun CoinIcon(
url: String?,
@DrawableRes fallbackResId: Int,
alpha: Float,
colorFilter: ColorFilter?,
modifier: Modifier = Modifier,
) {
val iconData: Any = if (url.isNullOrBlank()) fallbackResId else url
DefaultCurrencyIcon(
modifier = modifier,
iconData = iconData,
errorIcon = {
Image(
painter = painterResource(id = fallbackResId),
alpha = alpha,
colorFilter = colorFilter,
contentDescription = null,
)
},
alpha = alpha,
colorFilter = colorFilter,
)
}
@Composable
private fun TokenIcon(
url: String?,
alpha: Float,
colorFilter: ColorFilter?,
errorIcon: @Composable () -> Unit,
modifier: Modifier = Modifier,
) {
if (url == null) {
errorIcon()
} else {
DefaultCurrencyIcon(
modifier = modifier,
iconData = url,
errorIcon = errorIcon,
alpha = alpha,
colorFilter = colorFilter,
)
}
}
@Composable
private fun CustomTokenIcon(tint: Color, background: Color, alpha: Float, modifier: Modifier = Modifier) {
Box(
modifier = modifier
.background(
color = background.copy(alpha = alpha),
shape = CircleShape,
),
contentAlignment = Alignment.Center,
) {
Icon(
modifier = Modifier.matchParentSize(),
painter = painterResource(id = R.drawable.ic_custom_token_44),
tint = tint.copy(alpha = alpha),
contentDescription = null,
)
}
}
@Composable
private inline fun DefaultCurrencyIcon(
iconData: Any,
alpha: Float,
colorFilter: ColorFilter?,
crossinline errorIcon: @Composable () -> Unit,
modifier: Modifier = Modifier,
) {
SubcomposeAsyncImage(
modifier = modifier,
model = ImageRequest.Builder(context = LocalContext.current)
.data(iconData)
.crossfade(enable = true)
.build(),
loading = { CircleShimmer() },
error = { errorIcon() },
alpha = alpha,
colorFilter = colorFilter,
contentDescription = null,
)
}

View file

@ -1,6 +1,5 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.Text
@ -10,19 +9,23 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import coil.compose.rememberAsyncImagePainter
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenInfoBlockState
import com.tangem.features.tokendetails.impl.R
private const val GRAY_SCALE_SATURATION = 0f
private const val GRAY_SCALE_ALPHA = 0.4f
private const val NORMAL_ALPHA = 1f
@Composable
internal fun TokenInfoBlock(state: TokenInfoBlockState, modifier: Modifier = Modifier) {
Row(modifier = modifier.fillMaxWidth()) {
@ -37,16 +40,18 @@ internal fun TokenInfoBlock(state: TokenInfoBlockState, modifier: Modifier = Mod
NetworkInfoText(state.currency)
}
val tokenIconPainter = when (LocalInspectionMode.current) {
// show drawable res in preview
true -> painterResource(id = R.drawable.img_stellar_22)
false -> rememberAsyncImagePainter(model = state.iconUrl)
val (alpha, colorFilter) = remember(state.iconState.isGrayscale) {
if (state.iconState.isGrayscale) {
GRAY_SCALE_ALPHA to GrayscaleColorFilter
} else {
NORMAL_ALPHA to null
}
}
Image(
CurrencyIcon(
modifier = Modifier.size(TangemTheme.dimens.size48),
painter = tokenIconPainter,
contentDescription = null,
icon = state.iconState,
alpha = alpha,
colorFilter = colorFilter,
)
}
}
@ -110,6 +115,9 @@ private fun extractNetwork(tokenCurrency: TokenInfoBlockState.Currency.Token): E
private data class ExtractedTokenNetworkText(val normalText: String, val boldText: String)
private val GrayscaleColorFilter: ColorFilter
get() = ColorFilter.colorMatrix(ColorMatrix().apply { setToSaturation(GRAY_SCALE_SATURATION) })
@Preview
@Composable
private fun Preview_TokenInfoBlock_LightTheme(

View file

@ -30,7 +30,6 @@ import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRout
import com.tangem.feature.tokendetails.presentation.tokendetails.analytics.TokenScreenEvent
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory
import com.tangem.features.tokendetails.navigation.TokenDetailsArguments
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.JobHolder
@ -59,15 +58,14 @@ internal class TokenDetailsViewModel @Inject constructor(
private val isBalanceHiddenUseCase: IsBalanceHiddenUseCase,
private val listenToFlipsUseCase: ListenToFlipsUseCase,
private val getCurrencyWarningsUseCase: GetCurrencyWarningsUseCase,
private val getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase,
private val walletManagersFacade: WalletManagersFacade,
private val reduxStateHolder: ReduxStateHolder,
private val analyticsEventsHandler: AnalyticsEventHandler,
savedStateHandle: SavedStateHandle,
) : ViewModel(), DefaultLifecycleObserver, TokenDetailsClickIntents {
private val screenArgument: TokenDetailsArguments = savedStateHandle[TokenDetailsRouter.TOKEN_DETAILS_ARGS]
?: error("This screen can't open without TokenDetailsArgument")
private val cryptoCurrency: CryptoCurrency = savedStateHandle[TokenDetailsRouter.CRYPTO_CURRENCY_KEY]
?: error("This screen can't open without CryptoCurrency")
var router by Delegates.notNull<InnerTokenDetailsRouter>()
@ -75,7 +73,6 @@ internal class TokenDetailsViewModel @Inject constructor(
private val refreshStateJobHolder = JobHolder()
private var cryptoCurrencyStatus: CryptoCurrencyStatus? = null
private var wallet by Delegates.notNull<UserWallet>()
private var cryptoCurrency by Delegates.notNull<CryptoCurrency>()
private val selectedAppCurrencyFlow: StateFlow<AppCurrency> = createSelectedAppCurrencyFlow()
@ -83,34 +80,25 @@ internal class TokenDetailsViewModel @Inject constructor(
currentStateProvider = Provider { uiState },
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
clickIntents = this,
currencySymbolProvider = Provider { cryptoCurrency.symbol },
currencyDecimalsProvider = Provider { cryptoCurrency.decimals },
symbol = cryptoCurrency.symbol,
decimals = cryptoCurrency.decimals,
)
var uiState: TokenDetailsState by mutableStateOf(stateFactory.getInitialState(screenArgument))
var uiState: TokenDetailsState by mutableStateOf(stateFactory.getInitialState(cryptoCurrency))
private set
override fun onCreate(owner: LifecycleOwner) {
initRequiredFields()
getWallet()
updateContent(selectedWallet = wallet)
handleBalanceHiding(owner)
}
private fun initRequiredFields() {
private fun getWallet() {
getSelectedWalletUseCase()
.fold(
ifLeft = { error("Can not get selected wallet $it") },
ifRight = { wallet = it },
)
viewModelScope.launch {
getCryptoCurrencyUseCase(userWalletId = wallet.walletId, id = screenArgument.currencyId)
.fold(
ifLeft = { error("Can not get cryptoCurrency with given ID: screenArgument.currencyId. $it") },
ifRight = {
cryptoCurrency = it
updateContent(selectedWallet = wallet)
},
)
}
}
private fun updateContent(selectedWallet: UserWallet) {

View file

@ -19,7 +19,6 @@ import androidx.navigation.navArgument
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.core.navigation.ReduxNavController
import com.tangem.core.ui.extensions.networkIconResId
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.presentation.WalletFragment
@ -27,7 +26,6 @@ import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScree
import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel
import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen
import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModel
import com.tangem.features.tokendetails.navigation.TokenDetailsArguments
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
import kotlin.properties.Delegates
@ -113,23 +111,7 @@ internal class DefaultWalletRouter(private val reduxNavController: ReduxNavContr
reduxNavController.navigate(
action = NavigationAction.NavigateTo(
screen = AppScreen.WalletDetails,
bundle = bundleOf(
TokenDetailsRouter.TOKEN_DETAILS_ARGS to TokenDetailsArguments(
currencyId = currency.id,
currencyName = currency.name,
currencySymbol = currency.symbol,
iconUrl = currency.iconUrl,
coinType = when (currency) {
is CryptoCurrency.Coin -> TokenDetailsArguments.CoinType.Native
is CryptoCurrency.Token -> TokenDetailsArguments.CoinType.Token(
isCustom = currency.isCustom,
standardName = currency.network.standardType.name,
networkName = currency.network.name,
networkIcon = currency.networkIconResId,
)
},
),
),
bundle = bundleOf(TokenDetailsRouter.CRYPTO_CURRENCY_KEY to currency),
),
)
}