Updated on 2026-08-14
This commit is contained in:
parent
cdc3f78320
commit
ae42c238d3
13 changed files with 276 additions and 177 deletions
|
|
@ -30,6 +30,7 @@ dependencies {
|
|||
/** Project - Domain */
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.wallets.models)
|
||||
implementation(projects.domain.staking.models)
|
||||
implementation(projects.domain.appCurrency.models)
|
||||
implementation(projects.domain.transaction.models)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,193 @@
|
|||
package com.tangem.common.ui.tokens
|
||||
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.components.marketprice.utils.PriceChangeConverter
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.extensions.isZero
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Token item state converter from [CryptoCurrencyStatus] to [TokenItemState]
|
||||
*
|
||||
* @property appCurrency app currency
|
||||
* @property titleStateProvider title state provider
|
||||
* @property subtitleStateProvider subtitle state provider
|
||||
* @property onItemClick callback is invoked when item is clicked
|
||||
* @property onItemLongClick callback is invoked when item is long clicked
|
||||
*/
|
||||
class TokenItemStateConverter(
|
||||
private val appCurrency: AppCurrency,
|
||||
private val titleStateProvider: (CryptoCurrencyStatus) -> TokenItemState.TitleState = Companion::createTitleState,
|
||||
private val subtitleStateProvider: (CryptoCurrencyStatus) -> TokenItemState.SubtitleState? = {
|
||||
createSubtitleState(it, appCurrency)
|
||||
},
|
||||
private val onItemClick: (CryptoCurrencyStatus) -> Unit,
|
||||
private val onItemLongClick: ((CryptoCurrencyStatus) -> Unit)? = null,
|
||||
) : Converter<CryptoCurrencyStatus, TokenItemState> {
|
||||
|
||||
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
|
||||
|
||||
override fun convert(value: CryptoCurrencyStatus): TokenItemState {
|
||||
return when (value.value) {
|
||||
is CryptoCurrencyStatus.Loading -> value.mapToLoadingState()
|
||||
is CryptoCurrencyStatus.Loaded,
|
||||
is CryptoCurrencyStatus.Custom,
|
||||
is CryptoCurrencyStatus.NoQuote,
|
||||
is CryptoCurrencyStatus.NoAccount,
|
||||
-> value.mapToTokenItemState()
|
||||
is CryptoCurrencyStatus.MissedDerivation -> value.mapToNoAddressTokenItemState()
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
is CryptoCurrencyStatus.NoAmount,
|
||||
-> value.mapToUnreachableTokenItemState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.mapToLoadingState(): TokenItemState.Loading {
|
||||
return TokenItemState.Loading(
|
||||
id = currency.id.value,
|
||||
iconState = iconStateConverter.convert(value = this),
|
||||
titleState = titleStateProvider(this) as TokenItemState.TitleState.Content,
|
||||
subtitleState = requireNotNull(subtitleStateProvider(this)),
|
||||
)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.mapToTokenItemState(): TokenItemState.Content {
|
||||
return TokenItemState.Content(
|
||||
id = currency.id.value,
|
||||
iconState = iconStateConverter.convert(value = this),
|
||||
titleState = titleStateProvider(this),
|
||||
subtitleState = requireNotNull(subtitleStateProvider(this)),
|
||||
fiatAmountState = TokenItemState.FiatAmountState.Content(
|
||||
text = getFormattedFiatAmount(),
|
||||
hasStaked = !getStakedBalance().isZero(),
|
||||
),
|
||||
cryptoAmountState = TokenItemState.CryptoAmountState.Content(text = getFormattedAmount()),
|
||||
onItemClick = { onItemClick(this) },
|
||||
onItemLongClick = onItemLongClick?.let {
|
||||
{ it(this) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getFormattedAmount(): String {
|
||||
val amount = value.amount?.plus(getStakedBalance()) ?: return DASH_SIGN
|
||||
|
||||
return BigDecimalFormatter.formatCryptoAmount(amount, currency.symbol, currency.decimals)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getFormattedFiatAmount(): String {
|
||||
val fiatYieldBalance = value.fiatRate?.times(getStakedBalance()).orZero()
|
||||
val fiatAmount = value.fiatAmount?.plus(fiatYieldBalance) ?: return DASH_SIGN
|
||||
|
||||
return BigDecimalFormatter.formatFiatAmount(fiatAmount, appCurrency.code, appCurrency.symbol)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getStakedBalance() =
|
||||
(value.yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance().orZero()
|
||||
|
||||
private fun CryptoCurrencyStatus.mapToUnreachableTokenItemState(): TokenItemState.Unreachable {
|
||||
return TokenItemState.Unreachable(
|
||||
id = currency.id.value,
|
||||
iconState = iconStateConverter.convert(value = this),
|
||||
titleState = titleStateProvider(this),
|
||||
subtitleState = subtitleStateProvider(this),
|
||||
onItemClick = { onItemClick(this) },
|
||||
onItemLongClick = onItemLongClick?.let {
|
||||
{ it(this) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.mapToNoAddressTokenItemState(): TokenItemState.NoAddress {
|
||||
return TokenItemState.NoAddress(
|
||||
id = currency.id.value,
|
||||
iconState = iconStateConverter.convert(this),
|
||||
titleState = titleStateProvider(this),
|
||||
subtitleState = subtitleStateProvider(this),
|
||||
onItemLongClick = onItemLongClick?.let {
|
||||
{ it(this) }
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
fun createTitleState(currencyStatus: CryptoCurrencyStatus): TokenItemState.TitleState {
|
||||
return when (val value = currencyStatus.value) {
|
||||
is CryptoCurrencyStatus.Loading,
|
||||
is CryptoCurrencyStatus.MissedDerivation,
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
is CryptoCurrencyStatus.NoAmount,
|
||||
-> {
|
||||
TokenItemState.TitleState.Content(text = currencyStatus.currency.name)
|
||||
}
|
||||
is CryptoCurrencyStatus.Loaded,
|
||||
is CryptoCurrencyStatus.Custom,
|
||||
is CryptoCurrencyStatus.NoQuote,
|
||||
is CryptoCurrencyStatus.NoAccount,
|
||||
-> {
|
||||
TokenItemState.TitleState.Content(
|
||||
text = currencyStatus.currency.name,
|
||||
hasPending = value.hasCurrentNetworkTransactions,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createSubtitleState(
|
||||
currencyStatus: CryptoCurrencyStatus,
|
||||
appCurrency: AppCurrency,
|
||||
): TokenItemState.SubtitleState? {
|
||||
return when (currencyStatus.value) {
|
||||
is CryptoCurrencyStatus.Loading -> TokenItemState.SubtitleState.Loading
|
||||
is CryptoCurrencyStatus.Loaded,
|
||||
is CryptoCurrencyStatus.Custom,
|
||||
is CryptoCurrencyStatus.NoQuote,
|
||||
is CryptoCurrencyStatus.NoAccount,
|
||||
-> currencyStatus.getCryptoPriceState(appCurrency)
|
||||
is CryptoCurrencyStatus.MissedDerivation,
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
is CryptoCurrencyStatus.NoAmount,
|
||||
-> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getCryptoPriceState(appCurrency: AppCurrency): TokenItemState.SubtitleState {
|
||||
val fiatRate = value.fiatRate
|
||||
val priceChange = value.priceChange
|
||||
|
||||
return if (fiatRate != null && priceChange != null) {
|
||||
TokenItemState.SubtitleState.CryptoPriceContent(
|
||||
price = fiatRate.getFormattedCryptoPrice(appCurrency),
|
||||
priceChangePercent = BigDecimalFormatter.formatPercent(
|
||||
percent = priceChange,
|
||||
useAbsoluteValue = true,
|
||||
),
|
||||
type = priceChange.getPriceChangeType(),
|
||||
)
|
||||
} else {
|
||||
TokenItemState.SubtitleState.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
private fun BigDecimal.getFormattedCryptoPrice(appCurrency: AppCurrency): String {
|
||||
return BigDecimalFormatter.formatFiatAmountUncapped(
|
||||
fiatAmount = this,
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
}
|
||||
|
||||
private fun BigDecimal.getPriceChangeType(): PriceChangeType {
|
||||
return PriceChangeConverter.fromBigDecimal(value = this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.core.ui.components.token
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -79,9 +78,7 @@ fun TokenItem(
|
|||
|
||||
CustomContainer(
|
||||
state = state,
|
||||
modifier = modifier
|
||||
.tokenClickable(state = state)
|
||||
.background(color = TangemTheme.colors.background.primary),
|
||||
modifier = modifier.tokenClickable(state = state),
|
||||
) {
|
||||
CurrencyIcon(
|
||||
state = state.iconState,
|
||||
|
|
@ -130,17 +127,22 @@ fun TokenItem(
|
|||
@OptIn(ExperimentalFoundationApi::class)
|
||||
private fun Modifier.tokenClickable(state: TokenItemState): Modifier = composed {
|
||||
when (state) {
|
||||
is TokenItemState.Content -> {
|
||||
val onLongClick = rememberHapticFeedback(state = state, onAction = state.onItemLongClick)
|
||||
combinedClickable(onClick = state.onItemClick, onLongClick = onLongClick)
|
||||
}
|
||||
is TokenItemState.Unreachable -> {
|
||||
val onLongClick = rememberHapticFeedback(state = state, onAction = state.onItemLongClick)
|
||||
combinedClickable(onClick = state.onItemClick, onLongClick = onLongClick)
|
||||
}
|
||||
is TokenItemState.NoAddress -> {
|
||||
val onLongClick = rememberHapticFeedback(state = state, onAction = state.onItemLongClick)
|
||||
combinedClickable(onClick = {}, onLongClick = onLongClick)
|
||||
is TokenItemState.Content,
|
||||
is TokenItemState.NoAddress,
|
||||
is TokenItemState.Unreachable,
|
||||
-> {
|
||||
val onClick = state.onItemClick
|
||||
val onLongClick = state.onItemLongClick?.let { rememberHapticFeedback(state = state, onAction = it) }
|
||||
|
||||
when {
|
||||
onClick == null && onLongClick == null -> this
|
||||
onClick == null && onLongClick != null -> combinedClickable(onClick = {}, onLongClick = onLongClick)
|
||||
onClick != null && onLongClick == null -> combinedClickable(onClick = onClick)
|
||||
onClick != null && onLongClick != null -> {
|
||||
combinedClickable(onClick = onClick, onLongClick = onLongClick)
|
||||
}
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
is TokenItemState.Draggable,
|
||||
is TokenItemState.Loading,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,12 @@ sealed class TokenItemState {
|
|||
/** Token crypto amount state (under [fiatAmountState] and in one row with [subtitleState]) */
|
||||
abstract val cryptoAmountState: CryptoAmountState?
|
||||
|
||||
/** Callback which will be called when an item is clicked */
|
||||
abstract val onItemClick: (() -> Unit)?
|
||||
|
||||
/** Callback which will be called when an item is long clicked */
|
||||
abstract val onItemLongClick: (() -> Unit)?
|
||||
|
||||
/**
|
||||
* Loading token state
|
||||
*
|
||||
|
|
@ -42,6 +48,8 @@ sealed class TokenItemState {
|
|||
) : TokenItemState() {
|
||||
override val fiatAmountState: FiatAmountState = FiatAmountState.Loading
|
||||
override val cryptoAmountState: CryptoAmountState = CryptoAmountState.Loading
|
||||
override val onItemClick: (() -> Unit)? = null
|
||||
override val onItemLongClick: (() -> Unit)? = null
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -55,6 +63,8 @@ sealed class TokenItemState {
|
|||
override val subtitleState: SubtitleState = SubtitleState.Locked
|
||||
override val fiatAmountState: FiatAmountState = FiatAmountState.Locked
|
||||
override val cryptoAmountState: CryptoAmountState = CryptoAmountState.Locked
|
||||
override val onItemClick: (() -> Unit)? = null
|
||||
override val onItemLongClick: (() -> Unit)? = null
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -76,8 +86,8 @@ sealed class TokenItemState {
|
|||
override val subtitleState: SubtitleState,
|
||||
override val fiatAmountState: FiatAmountState,
|
||||
override val cryptoAmountState: CryptoAmountState.Content,
|
||||
val onItemClick: () -> Unit,
|
||||
val onItemLongClick: () -> Unit,
|
||||
override val onItemClick: (() -> Unit)?,
|
||||
override val onItemLongClick: (() -> Unit)?,
|
||||
) : TokenItemState()
|
||||
|
||||
/**
|
||||
|
|
@ -96,6 +106,8 @@ sealed class TokenItemState {
|
|||
) : TokenItemState() {
|
||||
override val subtitleState: SubtitleState? = null
|
||||
override val fiatAmountState: FiatAmountState? = null
|
||||
override val onItemClick: (() -> Unit)? = null
|
||||
override val onItemLongClick: (() -> Unit)? = null
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -113,8 +125,8 @@ sealed class TokenItemState {
|
|||
override val iconState: CurrencyIconState,
|
||||
override val titleState: TitleState,
|
||||
override val subtitleState: SubtitleState? = null,
|
||||
val onItemClick: () -> Unit,
|
||||
val onItemLongClick: () -> Unit,
|
||||
override val onItemClick: (() -> Unit)?,
|
||||
override val onItemLongClick: (() -> Unit)?,
|
||||
) : TokenItemState() {
|
||||
override val fiatAmountState: FiatAmountState? = null
|
||||
override val cryptoAmountState: CryptoAmountState? = null
|
||||
|
|
@ -134,10 +146,11 @@ sealed class TokenItemState {
|
|||
override val iconState: CurrencyIconState,
|
||||
override val titleState: TitleState,
|
||||
override val subtitleState: SubtitleState? = null,
|
||||
val onItemLongClick: () -> Unit,
|
||||
override val onItemLongClick: (() -> Unit)?,
|
||||
) : TokenItemState() {
|
||||
override val fiatAmountState: FiatAmountState? = null
|
||||
override val cryptoAmountState: CryptoAmountState? = null
|
||||
override val onItemClick: (() -> Unit)? = null
|
||||
}
|
||||
|
||||
@Immutable
|
||||
|
|
|
|||
|
|
@ -7,4 +7,6 @@ import java.math.BigDecimal
|
|||
*
|
||||
* If `BigDecimal?` is `null`, returns `BigDecimal.ZERO`
|
||||
*/
|
||||
fun BigDecimal?.orZero(): BigDecimal = this ?: BigDecimal.ZERO
|
||||
fun BigDecimal?.orZero(): BigDecimal = this ?: BigDecimal.ZERO
|
||||
|
||||
fun BigDecimal.isZero(): Boolean = this.compareTo(BigDecimal.ZERO) == 0
|
||||
|
|
@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.*
|
|||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
|
@ -74,10 +75,12 @@ internal fun MyPortfolio(state: MyPortfolioUM, modifier: Modifier = Modifier) {
|
|||
private fun TokenList(state: MyPortfolioUM.Tokens, modifier: Modifier = Modifier) {
|
||||
Column(modifier) {
|
||||
state.tokens.fastForEachIndexed { index, token ->
|
||||
PortfolioItem(
|
||||
state = token,
|
||||
lastInList = index == state.tokens.size - 1,
|
||||
)
|
||||
key(token.tokenItemState.id) {
|
||||
PortfolioItem(
|
||||
state = token,
|
||||
lastInList = index == state.tokens.size - 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.features.markets.portfolio.impl.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.*
|
||||
|
|
@ -20,16 +21,22 @@ import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState
|
|||
@Composable
|
||||
internal fun PortfolioItem(state: PortfolioTokenUM, lastInList: Boolean, modifier: Modifier = Modifier) {
|
||||
Column(modifier) {
|
||||
TokenItem(state = state.tokenItemState, isBalanceHidden = state.isBalanceHidden)
|
||||
TokenItem(
|
||||
state = state.tokenItemState,
|
||||
isBalanceHidden = state.isBalanceHidden,
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.action),
|
||||
)
|
||||
|
||||
PortfolioQuickActions(
|
||||
modifier = Modifier.padding(
|
||||
bottom = if (lastInList) {
|
||||
TangemTheme.dimens.spacing12
|
||||
} else {
|
||||
TangemTheme.dimens.spacing24
|
||||
},
|
||||
),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing12)
|
||||
.padding(
|
||||
bottom = if (lastInList) {
|
||||
TangemTheme.dimens.spacing12
|
||||
} else {
|
||||
TangemTheme.dimens.spacing24
|
||||
},
|
||||
),
|
||||
isVisible = state.isQuickActionsShown,
|
||||
onActionClick = state.onQuickActionClick,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import android.content.res.Configuration
|
|||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Button
|
||||
|
|
@ -67,7 +69,7 @@ private fun AnimatedVisibilityScope.LineSeparator(modifier: Modifier = Modifier)
|
|||
val strokeWidth = TangemTheme.dimens.size1
|
||||
val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr
|
||||
val verticalPadding = TangemTheme.dimens.spacing2
|
||||
val startPadding = TangemTheme.dimens.spacing28
|
||||
val startPadding = TangemTheme.dimens.spacing18
|
||||
|
||||
val height = TangemTheme.dimens.size16 + verticalPadding * 2
|
||||
|
||||
|
|
@ -118,10 +120,7 @@ private fun AnimatedVisibilityScope.QuickActionItem(
|
|||
hapticManager.perform(TangemHapticEffect.View.SegmentTick)
|
||||
onClick()
|
||||
}
|
||||
.padding(
|
||||
vertical = TangemTheme.dimens.spacing2,
|
||||
horizontal = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
.padding(horizontal = TangemTheme.dimens.spacing2, vertical = TangemTheme.dimens.spacing3),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing18),
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -93,6 +93,9 @@ dependencies {
|
|||
implementation(projects.features.pushNotifications.api)
|
||||
implementation(projects.features.markets.api)
|
||||
|
||||
/** Common modules */
|
||||
implementation(projects.common.ui)
|
||||
|
||||
/** Test libraries */
|
||||
implementation(deps.test.junit)
|
||||
implementation(deps.test.truth)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.AppBarDefaults
|
||||
import androidx.compose.material3.FabPosition
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -181,7 +180,7 @@ private fun LazyItemScope.DraggableItem(
|
|||
reorderableTokenListState = reorderableState,
|
||||
)
|
||||
is DraggableItem.Token -> TokenItem(
|
||||
modifier = itemModifier,
|
||||
modifier = itemModifier.background(color = TangemTheme.colors.background.primary),
|
||||
state = item.tokenItemState,
|
||||
reorderableTokenListState = reorderableState,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
|
|
@ -212,7 +211,7 @@ private fun TopBar(
|
|||
}
|
||||
}
|
||||
val elevation by animateDpAsState(
|
||||
targetValue = if (isElevationEnabled) AppBarDefaults.TopAppBarElevation else TangemTheme.dimens.elevation0,
|
||||
targetValue = if (isElevationEnabled) TangemTheme.dimens.elevation4 else TangemTheme.dimens.elevation0,
|
||||
label = "top_bar_shadow_elevation",
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,130 +0,0 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
|
||||
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.components.marketprice.utils.PriceChangeConverter
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.StringsSigns.DASH_SIGN
|
||||
import com.tangem.utils.converter.Converter
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class TokenItemStateConverter(
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) : Converter<CryptoCurrencyStatus, TokenItemState> {
|
||||
|
||||
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
|
||||
|
||||
override fun convert(value: CryptoCurrencyStatus): TokenItemState {
|
||||
return when (value.value) {
|
||||
is CryptoCurrencyStatus.Loading -> value.mapToLoadingState()
|
||||
is CryptoCurrencyStatus.Loaded,
|
||||
is CryptoCurrencyStatus.Custom,
|
||||
is CryptoCurrencyStatus.NoQuote,
|
||||
is CryptoCurrencyStatus.NoAccount,
|
||||
-> value.mapToTokenItemState()
|
||||
is CryptoCurrencyStatus.MissedDerivation -> value.mapToNoAddressTokenItemState()
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
is CryptoCurrencyStatus.NoAmount,
|
||||
-> value.mapToUnreachableTokenItemState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.mapToLoadingState(): TokenItemState.Loading {
|
||||
return TokenItemState.Loading(
|
||||
id = currency.id.value,
|
||||
iconState = iconStateConverter.convert(value = this),
|
||||
titleState = TokenItemState.TitleState.Content(text = currency.name),
|
||||
)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.mapToTokenItemState(): TokenItemState.Content {
|
||||
return TokenItemState.Content(
|
||||
id = currency.id.value,
|
||||
iconState = iconStateConverter.convert(value = this),
|
||||
titleState = TokenItemState.TitleState.Content(
|
||||
text = currency.name,
|
||||
hasPending = value.hasCurrentNetworkTransactions,
|
||||
),
|
||||
fiatAmountState = TokenItemState.FiatAmountState.Content(
|
||||
text = getFormattedFiatAmount(),
|
||||
hasStaked = !getStakedBalance().isZero(),
|
||||
),
|
||||
cryptoAmountState = TokenItemState.CryptoAmountState.Content(text = getFormattedAmount()),
|
||||
subtitleState = getCryptoPriceState(),
|
||||
onItemClick = { clickIntents.onTokenItemClick(this) },
|
||||
onItemLongClick = { clickIntents.onTokenItemLongClick(cryptoCurrencyStatus = this) },
|
||||
)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getFormattedAmount(): String {
|
||||
val amount = value.amount?.plus(getStakedBalance()) ?: return DASH_SIGN
|
||||
|
||||
return BigDecimalFormatter.formatCryptoAmount(amount, currency.symbol, currency.decimals)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getFormattedFiatAmount(): String {
|
||||
val fiatYieldBalance = value.fiatRate?.times(getStakedBalance()).orZero()
|
||||
val fiatAmount = value.fiatAmount?.plus(fiatYieldBalance) ?: return DASH_SIGN
|
||||
val appCurrency = appCurrencyProvider()
|
||||
|
||||
return BigDecimalFormatter.formatFiatAmount(fiatAmount, appCurrency.code, appCurrency.symbol)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getStakedBalance() =
|
||||
(value.yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance().orZero()
|
||||
|
||||
private fun CryptoCurrencyStatus.mapToUnreachableTokenItemState() = TokenItemState.Unreachable(
|
||||
id = currency.id.value,
|
||||
iconState = iconStateConverter.convert(value = this),
|
||||
titleState = TokenItemState.TitleState.Content(text = currency.name),
|
||||
onItemClick = { clickIntents.onTokenItemClick(this) },
|
||||
onItemLongClick = { clickIntents.onTokenItemLongClick(cryptoCurrencyStatus = this) },
|
||||
)
|
||||
|
||||
private fun CryptoCurrencyStatus.mapToNoAddressTokenItemState() = TokenItemState.NoAddress(
|
||||
id = currency.id.value,
|
||||
iconState = iconStateConverter.convert(this),
|
||||
titleState = TokenItemState.TitleState.Content(text = currency.name),
|
||||
onItemLongClick = { clickIntents.onTokenItemLongClick(cryptoCurrencyStatus = this) },
|
||||
)
|
||||
|
||||
private fun CryptoCurrencyStatus.getCryptoPriceState(): TokenItemState.SubtitleState {
|
||||
val fiatRate = value.fiatRate
|
||||
val priceChange = value.priceChange
|
||||
|
||||
return if (fiatRate != null && priceChange != null) {
|
||||
TokenItemState.SubtitleState.CryptoPriceContent(
|
||||
price = fiatRate.getFormattedCryptoPrice(),
|
||||
priceChangePercent = BigDecimalFormatter.formatPercent(
|
||||
percent = priceChange,
|
||||
useAbsoluteValue = true,
|
||||
),
|
||||
type = priceChange.getPriceChangeType(),
|
||||
)
|
||||
} else {
|
||||
TokenItemState.SubtitleState.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
private fun BigDecimal.getFormattedCryptoPrice(): String {
|
||||
val appCurrency = appCurrencyProvider()
|
||||
return BigDecimalFormatter.formatFiatAmountUncapped(
|
||||
fiatAmount = this,
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
}
|
||||
|
||||
private fun BigDecimal.getPriceChangeType(): PriceChangeType {
|
||||
return PriceChangeConverter.fromBigDecimal(value = this)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
|
||||
|
||||
import com.tangem.common.ui.tokens.TokenItemStateConverter
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
|
|
@ -11,7 +12,6 @@ import com.tangem.domain.wallets.models.UserWallet
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState.TokensListItemState
|
||||
import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import kotlinx.collections.immutable.PersistentList
|
||||
import kotlinx.collections.immutable.mutate
|
||||
|
|
@ -19,15 +19,16 @@ import kotlinx.collections.immutable.persistentListOf
|
|||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState.OrganizeTokensButtonConfig as WalletOrganizeTokensButtonConfig
|
||||
|
||||
internal class TokenListStateConverter(
|
||||
appCurrency: AppCurrency,
|
||||
private val tokenList: TokenList,
|
||||
private val selectedWallet: UserWallet,
|
||||
private val appCurrency: AppCurrency,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) : Converter<WalletTokensListState, WalletTokensListState> {
|
||||
|
||||
private val tokenStatusConverter = TokenItemStateConverter(
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
clickIntents = clickIntents,
|
||||
appCurrency = appCurrency,
|
||||
onItemClick = clickIntents::onTokenItemClick,
|
||||
onItemLongClick = clickIntents::onTokenItemLongClick,
|
||||
)
|
||||
|
||||
override fun convert(value: WalletTokensListState): WalletTokensListState {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.core.ui.components.token.TokenItem
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.wallet.presentation.common.component.NetworkGroupItem
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState.TokensListItemState
|
||||
|
||||
|
|
@ -26,7 +28,11 @@ internal fun MultiCurrencyContentItem(
|
|||
NetworkGroupItem(networkName = state.name.resolveReference(), modifier = modifier)
|
||||
}
|
||||
is TokensListItemState.Token -> {
|
||||
TokenItem(state = state.state, isBalanceHidden = isBalanceHidden, modifier = modifier)
|
||||
TokenItem(
|
||||
state = state.state,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = modifier.background(color = TangemTheme.colors.background.primary),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue