diff --git a/common/ui/build.gradle.kts b/common/ui/build.gradle.kts index 2d6b9e12d6..d7b55fb311 100644 --- a/common/ui/build.gradle.kts +++ b/common/ui/build.gradle.kts @@ -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) diff --git a/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt new file mode 100644 index 0000000000..3b3d14103d --- /dev/null +++ b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt @@ -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 { + + 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) + } + } +} \ No newline at end of file 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 027db6c7d6..436f507958 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 @@ -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, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/token/state/TokenItemState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/token/state/TokenItemState.kt index 9c014e8067..eb10290ab4 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/token/state/TokenItemState.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/token/state/TokenItemState.kt @@ -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 diff --git a/core/utils/src/main/java/com/tangem/utils/extensions/BigDecimalExt.kt b/core/utils/src/main/java/com/tangem/utils/extensions/BigDecimalExt.kt index d09f3f6a6d..30820e1536 100644 --- a/core/utils/src/main/java/com/tangem/utils/extensions/BigDecimalExt.kt +++ b/core/utils/src/main/java/com/tangem/utils/extensions/BigDecimalExt.kt @@ -7,4 +7,6 @@ import java.math.BigDecimal * * If `BigDecimal?` is `null`, returns `BigDecimal.ZERO` */ -fun BigDecimal?.orZero(): BigDecimal = this ?: BigDecimal.ZERO \ No newline at end of file +fun BigDecimal?.orZero(): BigDecimal = this ?: BigDecimal.ZERO + +fun BigDecimal.isZero(): Boolean = this.compareTo(BigDecimal.ZERO) == 0 \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/MyPortfolio.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/MyPortfolio.kt index 30cbcb453f..2acf18d893 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/MyPortfolio.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/MyPortfolio.kt @@ -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, + ) + } } } } diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioItem.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioItem.kt index 035d1dbb91..2b781bd2a7 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioItem.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioItem.kt @@ -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, ) diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioQuickActions.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioQuickActions.kt index 1af8cde8ce..f498942443 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioQuickActions.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/PortfolioQuickActions.kt @@ -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), ) { diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index 4cdbb44b40..75b2af5fb3 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -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) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt index 437fd6b480..0654725a22 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt @@ -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", ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenItemStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenItemStateConverter.kt deleted file mode 100644 index e9752df81b..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenItemStateConverter.kt +++ /dev/null @@ -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, - private val clickIntents: WalletClickIntents, -) : Converter { - - 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) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt index ccb6834bde..4ae09b322f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt @@ -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 { private val tokenStatusConverter = TokenItemStateConverter( - appCurrencyProvider = Provider { appCurrency }, - clickIntents = clickIntents, + appCurrency = appCurrency, + onItemClick = clickIntents::onTokenItemClick, + onItemLongClick = clickIntents::onTokenItemLongClick, ) override fun convert(value: WalletTokensListState): WalletTokensListState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt index 87d84a3a64..755447b863 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt @@ -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), + ) } } } \ No newline at end of file