diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 2d1d56553e..eadaa40d6e 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -908,9 +908,11 @@ %d asset %d assets + No amount 
on tokens No data Total value Can’t load data + You don’t have any tokens with amount Top holding %s About coin To buy, exchange, or receive this asset, add it to your portfolio diff --git a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/DefaultForYouComponent.kt b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/DefaultForYouComponent.kt index 2c33e64590..9a244d74e3 100644 --- a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/DefaultForYouComponent.kt +++ b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/DefaultForYouComponent.kt @@ -69,7 +69,14 @@ internal class DefaultForYouComponent @AssistedInject constructor( childFactory = { config, componentContext -> when (config) { ForYouBottomSheetConfig.AddToPortfolio -> portfolioSelectorChild(componentContext) - is ForYouBottomSheetConfig.ManageFunds -> manageFundsChild(config, componentContext) + is ForYouBottomSheetConfig.ManageFunds -> manageFundsChild( + componentContext = componentContext, + launchMode = ManageFundsComponent.LaunchMode.FilteredByRawId(config.rawCurrencyId), + ) + is ForYouBottomSheetConfig.AddFunds -> manageFundsChild( + componentContext = componentContext, + launchMode = ManageFundsComponent.LaunchMode.ChooseToken(config.userWalletId), + ) } }, ) @@ -129,12 +136,12 @@ internal class DefaultForYouComponent @AssistedInject constructor( ) private fun manageFundsChild( - config: ForYouBottomSheetConfig.ManageFunds, + launchMode: ManageFundsComponent.LaunchMode, componentContext: ComponentContext, ): ComposableBottomSheetComponent = manageFundsComponentFactory.create( context = childByContext(componentContext), params = ManageFundsComponent.Params( - launchMode = ManageFundsComponent.LaunchMode.FilteredByRawId(config.rawCurrencyId), + launchMode = launchMode, onDismiss = { model.bottomSheetNavigation.dismiss() }, ), ) diff --git a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/components/MarketChart.kt b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/components/MarketChart.kt index 793cb72e7b..27a7491559 100644 --- a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/components/MarketChart.kt +++ b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/components/MarketChart.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider @@ -51,13 +52,16 @@ internal fun MarketChart(marketChart: MarketChartUM, modifier: Modifier = Modifi Column(modifier = Modifier.fillMaxWidth()) { DonutChartBlock(marketChart.donutChart, cardBoundsInWindow) Spacer(modifier = Modifier.height(16.dp)) - if (marketChart is MarketChartUM.Loaded) { - TopHoldingBlock( - assetCount = marketChart.assetCount, - topHoldingPercent = marketChart.topHoldingPercent, - ) - } else { - CantLoadDataBlock() + when (marketChart) { + is MarketChartUM.Loaded -> { + TopHoldingBlock( + assetCount = marketChart.assetCount, + topHoldingPercent = marketChart.topHoldingPercent, + ) + } + is MarketChartUM.NoData -> { + CantLoadDataBlock(text = marketChart.title) + } } Spacer(modifier = Modifier.height(16.dp)) @@ -113,36 +117,40 @@ private fun ColumnScope.DonutChartBlock(donutChartUM: DonutChartUM, cardBoundsIn }, segments = segments, ) { - if (donutChartUM is DonutChartUM.Loaded) { - Text( - text = donutChartUM.totalAmount, - color = TangemTheme.colors3.text.primary, - style = TangemTheme.typography3.body.medium, - maxLines = 1, - autoSize = TextAutoSize.StepBased( - minFontSize = 8.sp, - maxFontSize = TangemTheme.typography3.body.medium.fontSize, - ), - ) - Text( - text = stringResourceSafe(R.string.market_chart_bubble_total_value), - color = TangemTheme.colors3.text.secondary, - style = TangemTheme.typography3.caption.medium, - maxLines = 1, - autoSize = TextAutoSize.StepBased( - maxFontSize = TangemTheme.typography3.caption.medium.fontSize, - ), - ) - } else { - Text( - text = stringResourceSafe(R.string.market_chart_bubble_no_data), - color = TangemTheme.colors3.text.secondary, - style = TangemTheme.typography3.body.medium, - maxLines = 1, - autoSize = TextAutoSize.StepBased( - maxFontSize = TangemTheme.typography3.caption.medium.fontSize, - ), - ) + when (donutChartUM) { + is DonutChartUM.Loaded -> { + Text( + text = donutChartUM.totalAmount, + color = TangemTheme.colors3.text.primary, + style = TangemTheme.typography3.body.medium, + maxLines = 1, + autoSize = TextAutoSize.StepBased( + minFontSize = 8.sp, + maxFontSize = TangemTheme.typography3.body.medium.fontSize, + ), + ) + Text( + text = stringResourceSafe(R.string.market_chart_bubble_total_value), + color = TangemTheme.colors3.text.secondary, + style = TangemTheme.typography3.caption.medium, + maxLines = 1, + autoSize = TextAutoSize.StepBased( + maxFontSize = TangemTheme.typography3.caption.medium.fontSize, + ), + ) + } + is DonutChartUM.NoData -> { + Text( + text = donutChartUM.title.resolveReference(), + color = TangemTheme.colors3.text.secondary, + style = TangemTheme.typography3.body.medium, + maxLines = 2, + textAlign = TextAlign.Center, + autoSize = TextAutoSize.StepBased( + maxFontSize = TangemTheme.typography3.caption.medium.fontSize, + ), + ) + } } } @@ -233,10 +241,10 @@ private fun ColumnScope.TopHoldingBlock(assetCount: Int, topHoldingPercent: Text } @Composable -private fun ColumnScope.CantLoadDataBlock() { +private fun ColumnScope.CantLoadDataBlock(text: TextReference) { Text( modifier = Modifier.padding(horizontal = 16.dp), - text = stringResourceSafe(R.string.market_chart_can_not_load_data), + text = text.resolveReference(), color = TangemTheme.colors3.text.secondary, style = TangemTheme.typography3.heading.small, ) @@ -306,7 +314,10 @@ private fun previewMarketChartState(scenario: MarketChartPreviewScenario): Marke ), ), ) - MarketChartPreviewScenario.NO_DATA -> MarketChartUM.NoData + MarketChartPreviewScenario.NO_DATA -> MarketChartUM.NoData( + title = resourceReference(R.string.market_chart_can_not_load_data), + donutText = resourceReference(R.string.market_chart_bubble_no_data), + ) } @Suppress("MagicNumber") diff --git a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/components/state/MarketChartUM.kt b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/components/state/MarketChartUM.kt index 7440dda859..e6f7d4367b 100644 --- a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/components/state/MarketChartUM.kt +++ b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/components/state/MarketChartUM.kt @@ -21,8 +21,11 @@ internal sealed class MarketChartUM( val assetCount: Int = donutChart.donutSegmentList.size } - data object NoData : MarketChartUM( - donutChart = DonutChartUM.NoData, + data class NoData( + val title: TextReference, + private val donutText: TextReference, + ) : MarketChartUM( + donutChart = DonutChartUM.NoData(title = donutText), aiInsight = AiInsightUM.Hide, ) } @@ -36,7 +39,9 @@ internal sealed class DonutChartUM( override val donutSegmentList: ImmutableList, ) : DonutChartUM(donutSegmentList = donutSegmentList) - data object NoData : DonutChartUM(donutSegmentList = persistentListOf()) + data class NoData( + val title: TextReference, + ) : DonutChartUM(donutSegmentList = persistentListOf()) } @Immutable diff --git a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/entity/ForYouBottomSheetConfig.kt b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/entity/ForYouBottomSheetConfig.kt index 1b798d9b26..d90245eba6 100644 --- a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/entity/ForYouBottomSheetConfig.kt +++ b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/entity/ForYouBottomSheetConfig.kt @@ -1,6 +1,7 @@ package com.tangem.features.foryou.impl.entity import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.wallet.UserWalletId internal sealed interface ForYouBottomSheetConfig { @@ -9,4 +10,8 @@ internal sealed interface ForYouBottomSheetConfig { data class ManageFunds( val rawCurrencyId: CryptoCurrency.RawID, ) : ForYouBottomSheetConfig + + data class AddFunds( + val userWalletId: UserWalletId, + ) : ForYouBottomSheetConfig } \ No newline at end of file diff --git a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/entity/ForYouUM.kt b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/entity/ForYouUM.kt index de0075f610..ed743bdb3d 100644 --- a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/entity/ForYouUM.kt +++ b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/entity/ForYouUM.kt @@ -31,6 +31,7 @@ internal sealed interface PortfolioReviewUM { data class Content( override val tokenList: ImmutableList, override val marketChartUM: MarketChartUM, + val onAddFundsClick: (() -> Unit)?, ) : PortfolioReviewUM } diff --git a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/model/ForYouModel.kt b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/model/ForYouModel.kt index 9c920731d9..08132681be 100644 --- a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/model/ForYouModel.kt +++ b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/model/ForYouModel.kt @@ -16,6 +16,7 @@ import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.ui.ds.row.token.TangemTokenRowUM import com.tangem.core.ui.ds.tabs.TangemSegmentUM import com.tangem.core.ui.ds.tabs.TangemSegmentedPickerUM +import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.account.status.supplier.MultiAccountStatusListSupplier import com.tangem.domain.account.status.usecase.IsAccountsModeEnabledUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase @@ -32,6 +33,7 @@ import com.tangem.domain.staking.usecase.StakingAvailabilityListUseCase import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager import com.tangem.features.foryou.ForYouComponent +import com.tangem.features.foryou.impl.R import com.tangem.features.foryou.impl.components.state.MarketChartUM import com.tangem.features.foryou.impl.entity.* import com.tangem.features.foryou.impl.model.converter.TOP_EARN_TOKENS_BATCH_SIZE @@ -106,7 +108,10 @@ internal class ForYouModel @Inject constructor( ), onPeriodClick = ::onPeriodClick, portfolioReviewUM = PortfolioReviewUM.Loading( - marketChartUM = MarketChartUM.NoData, + marketChartUM = MarketChartUM.NoData( + title = resourceReference(R.string.market_chart_can_not_load_data), + donutText = resourceReference(R.string.market_chart_bubble_no_data), + ), tokenList = buildList { repeat(4) { index -> add( @@ -154,6 +159,7 @@ internal class ForYouModel @Inject constructor( expandedAssetIds = expandedPortfolioReview, expandClick = ::onExpandPortfolioReviewClick, onTokenClick = ::onPortfolioReviewTokenClick, + onAddFundsClick = ::onAddFundsClick, ).convert(accountStatusList) val earnOpportunitiesUM = ForYouEarnOpportunitiesConverter( @@ -363,4 +369,8 @@ internal class ForYouModel @Inject constructor( }, ) } + + private fun onAddFundsClick(userWalletId: UserWalletId) { + bottomSheetNavigation.activate(ForYouBottomSheetConfig.AddFunds(userWalletId)) + } } \ No newline at end of file diff --git a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewConverter.kt b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewConverter.kt index 6fd651133f..514e95dcc3 100644 --- a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewConverter.kt +++ b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewConverter.kt @@ -15,8 +15,10 @@ import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWalletId import com.tangem.features.foryou.impl.R +import com.tangem.features.foryou.impl.components.state.MarketChartUM import com.tangem.features.foryou.impl.entity.ForYouTokenListItemUM import com.tangem.features.foryou.impl.entity.PortfolioReviewUM +import com.tangem.features.foryou.impl.model.converter.FOR_YOU_TOP_EARN_TOKENS_COUNT import com.tangem.features.foryou.impl.model.converter.forYouGroupKey import com.tangem.features.foryou.impl.model.converter.forYouPlaceholderBadge import com.tangem.features.foryou.impl.model.converter.toForYouPercent @@ -42,6 +44,7 @@ internal class ForYouPortfolioReviewConverter( private val expandedAssetIds: Set, private val expandClick: (assetId: String) -> Unit, private val onTokenClick: (UserWalletId, CryptoCurrency) -> Unit, + private val onAddFundsClick: (UserWalletId) -> Unit, ) : Converter { private val iconConverter = CryptoCurrencyToIconStateConverter() @@ -51,6 +54,26 @@ internal class ForYouPortfolioReviewConverter( val loadedBalance = value?.totalFiatBalance as? TotalFiatBalance.Loaded val totalFiatBalance = loadedBalance?.amount.orZero() + if (currencies.all { it.value.fiatAmount?.isZero() == true }) { + return PortfolioReviewUM.Content( + tokenList = currencies.take(FOR_YOU_TOP_EARN_TOKENS_COUNT) + .groupBy { it.forYouGroupKey() } + .map { (assetId, currencies) -> + createListItem( + userWalletId = value?.userWalletId, + assetId = assetId, + currencies = currencies, + totalFiatBalance = totalFiatBalance, + ) + }.toPersistentList(), + marketChartUM = MarketChartUM.NoData( + title = resourceReference(R.string.market_chart_no_amount), + donutText = resourceReference(R.string.market_chart_bubble_no_amount), + ), + onAddFundsClick = { value?.userWalletId?.let(onAddFundsClick) }, + ) + } + // Drop only assets we positively know are empty — a resolved, priced zero fiat balance. Currencies // whose fiat we couldn't determine (unreachable / no-address / no-quote / still-loading — i.e. any // non-content status, which all carry a null fiatAmount) are kept so the converter can still render @@ -95,6 +118,7 @@ internal class ForYouPortfolioReviewConverter( return PortfolioReviewUM.Content( tokenList = tokenList, marketChartUM = marketChartUM, + onAddFundsClick = null, ) } @@ -127,7 +151,7 @@ internal class ForYouPortfolioReviewConverter( networkCount = networkGroups.size, totalFiatBalance = totalFiatBalance, ), - tokenList = networkGroups.map(rowConverter::convertNetworkGroup).toPersistentList(), + tokenList = networkGroups.map(rowConverter::convert).toPersistentList(), isExpanded = assetId in expandedAssetIds, isExpandable = true, ) diff --git a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewMarketChartConverter.kt b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewMarketChartConverter.kt index c727d3de6e..b784871933 100644 --- a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewMarketChartConverter.kt +++ b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewMarketChartConverter.kt @@ -1,5 +1,6 @@ package com.tangem.features.foryou.impl.model.converter.portfolioReview +import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format @@ -7,6 +8,7 @@ import com.tangem.core.ui.format.bigdecimal.percent import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.features.foryou.impl.R import com.tangem.features.foryou.impl.components.state.AiInsightUM import com.tangem.features.foryou.impl.components.state.DonutChartUM import com.tangem.features.foryou.impl.components.state.DonutSegmentColor @@ -54,7 +56,10 @@ internal class ForYouPortfolioReviewMarketChartConverter( TotalFiatBalance.Loading, TotalFiatBalance.Failed, null, - -> MarketChartUM.NoData + -> MarketChartUM.NoData( + title = resourceReference(R.string.market_chart_can_not_load_data), + donutText = resourceReference(R.string.market_chart_bubble_no_data), + ) } } } \ No newline at end of file diff --git a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewTokenRowConverter.kt b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewTokenRowConverter.kt index 41a34e5d3a..c1b705b473 100644 --- a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewTokenRowConverter.kt +++ b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewTokenRowConverter.kt @@ -20,6 +20,7 @@ import com.tangem.domain.models.wallet.UserWalletId import com.tangem.features.foryou.impl.model.converter.forYouPlaceholderBadge import com.tangem.features.foryou.impl.model.converter.toForYouPercent import com.tangem.utils.StringsSigns +import com.tangem.utils.converter.Converter import com.tangem.utils.extensions.orZero import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -47,21 +48,20 @@ internal class ForYouPortfolioReviewTokenRowConverter( private val userWalletId: UserWalletId?, private val totalFiatBalance: BigDecimal, private val onTokenClick: (UserWalletId, CryptoCurrency) -> Unit, -) { +) : Converter, TangemTokenRowUM> { private val iconConverter = CryptoCurrencyToIconStateConverter() - /** Builds one row for all [statuses] of a single asset on the same network. */ - fun convertNetworkGroup(statuses: List): TangemTokenRowUM { - val representative = statuses.first() - if (statuses.all { it.value is CryptoCurrencyStatus.Loading }) { + override fun convert(value: List): TangemTokenRowUM { + val representative = value.first() + if (value.all { it.value is CryptoCurrencyStatus.Loading }) { return TangemTokenRowUM.Loading(id = representative.currency.id.value) } val currency = representative.currency - val cryptoAmount = statuses.sumOf { it.value.amount.orZero() } - val fiatAmount = statuses.sumOf { it.value.fiatAmount.orZero() } - val state = statuses.classify() + val cryptoAmount = value.sumOf { it.value.amount.orZero() } + val fiatAmount = value.sumOf { it.value.fiatAmount.orZero() } + val state = value.classify() return TangemTokenRowUM.Content( id = currency.id.value, @@ -78,7 +78,7 @@ internal class ForYouPortfolioReviewTokenRowConverter( /** * Maps the aggregate status of [statuses] onto a row's top/bottom end content, rendering the given * pre-summed [fiatAmount]. Reflects the same cache-flicker / could-not-refresh / no-address / - * unreachable treatment as [convertNetworkGroup], so the asset-level row surfaces the combined status + * unreachable treatment as [convert], so the asset-level row surfaces the combined status * of its holdings — analogous to how `AccountCryptoPortfolioItemStateConverter` reflects a * `TotalFiatBalance`'s status on the account row. * @@ -158,7 +158,7 @@ internal class ForYouPortfolioReviewTokenRowConverter( /** Bottom-end: percentage share for resolved states, no-address / unreachable treatment otherwise. */ private fun toRowBottomEnd(state: RowState, fiatAmount: BigDecimal): TangemTokenRowUM.EndContentUM = when (state) { is RowState.Normal -> TangemTokenRowUM.EndContentUM.Content( - text = stringReference(fiatAmount.toForYouPercent(totalFiatBalance).format { percent() }), + text = stringReference(fiatAmount.toForYouPercent(totalFiatBalance).orZero().format { percent() }), isFlickering = state.isFlickering, ) RowState.NoAddress -> attentionEndContent(R.string.common_no_address) diff --git a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/ui/ForYouContent.kt b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/ui/ForYouContent.kt index 63a964717d..cc4c2af243 100644 --- a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/ui/ForYouContent.kt +++ b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/ui/ForYouContent.kt @@ -58,7 +58,11 @@ internal fun ForYouContent( .drawBehind { drawRect(background.value) }, ) { promoBannersBlockComponent.ContentWithPadding( - modifier = Modifier.padding(top = 12.dp), + modifier = Modifier + .padding(top = 12.dp) + .conditional(forYouUM.notifications.isEmpty()) { + padding(bottom = 48.dp) + }, walletId = null, horizontalItemPadding = 16.dp, ) diff --git a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/ui/ForYouPortfolioReview.kt b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/ui/ForYouPortfolioReview.kt index e6b07656e9..9624f1f01e 100644 --- a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/ui/ForYouPortfolioReview.kt +++ b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/ui/ForYouPortfolioReview.kt @@ -13,12 +13,13 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import com.tangem.core.ui.components.SpacerH import com.tangem.core.ui.ds.image.TangemIconUM -import com.tangem.core.ui.ds.row.token.TangemTokenRowUM import com.tangem.core.ui.ds.tabs.TangemSegmentUM import com.tangem.core.ui.ds.tabs.TangemSegmentedPicker import com.tangem.core.ui.ds.tabs.TangemSegmentedPickerUM import com.tangem.core.ui.ds2.badge.TangemBadge +import com.tangem.core.ui.ds2.button.TangemButton import com.tangem.core.ui.ds2.shimmers.TangemShimmer +import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemTheme @@ -27,13 +28,10 @@ import com.tangem.core.ui.res.generated.icons.Icons import com.tangem.core.ui.res.generated.icons.ic_chevron_down_16 import com.tangem.features.foryou.impl.R import com.tangem.features.foryou.impl.components.MarketChart -import com.tangem.features.foryou.impl.components.state.MarketChartUM -import com.tangem.features.foryou.impl.entity.ForYouTokenListItemUM import com.tangem.features.foryou.impl.entity.PortfolioReviewUM import com.tangem.features.foryou.impl.ui.components.ForYouPortfolioTokenList import com.tangem.features.foryou.impl.ui.preview.ForYouPortfolioReviewPreviewData import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toPersistentList @Composable internal fun ForYouPortfolioReview( @@ -84,6 +82,18 @@ internal fun ForYouPortfolioReview( } ForYouPortfolioTokenList(tokenList = portfolioReviewUM.tokenList) + + if (portfolioReviewUM is PortfolioReviewUM.Content && portfolioReviewUM.onAddFundsClick != null) { + TangemButton( + text = resourceReference(R.string.common_add_funds), + onClick = portfolioReviewUM.onAddFundsClick, + variant = TangemButton.Variant.Secondary, + size = TangemButton.Size.X9, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp), + ) + } } } @@ -117,23 +127,8 @@ private class ForYouPortfolioReviewPreviewProvider : PreviewParameterProvider get() = sequenceOf( ForYouPortfolioReviewPreviewData.reviewContent, - PortfolioReviewUM.Loading( - marketChartUM = MarketChartUM.NoData, - tokenList = buildList { - repeat(4) { index -> - add( - ForYouTokenListItemUM( - tokenRowUM = TangemTokenRowUM.Loading( - id = index.toString(), - ), - tokenList = persistentListOf(), - isExpanded = false, - isExpandable = false, - ), - ) - } - }.toPersistentList(), - ), + ForYouPortfolioReviewPreviewData.loadingState, + ForYouPortfolioReviewPreviewData.zeroPortfolioState, ) } // endregion \ No newline at end of file diff --git a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/ui/preview/ForYouPortfolioReviewPreviewData.kt b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/ui/preview/ForYouPortfolioReviewPreviewData.kt index 75aea81cde..69a5b25e33 100644 --- a/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/ui/preview/ForYouPortfolioReviewPreviewData.kt +++ b/features/for-you/impl/src/main/java/com/tangem/features/foryou/impl/ui/preview/ForYouPortfolioReviewPreviewData.kt @@ -7,7 +7,9 @@ import com.tangem.core.ui.ds.badge.TangemBadgeType import com.tangem.core.ui.ds.badge.TangemBadgeUM import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds.row.token.TangemTokenRowUM +import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference +import com.tangem.features.foryou.impl.R import com.tangem.features.foryou.impl.components.state.DonutChartUM import com.tangem.features.foryou.impl.components.state.DonutSegmentColor import com.tangem.features.foryou.impl.components.state.DonutSegmentUM @@ -16,6 +18,7 @@ import com.tangem.features.foryou.impl.entity.ForYouTokenListItemUM import com.tangem.features.foryou.impl.entity.PortfolioReviewUM import com.tangem.utils.StringsSigns.DOT import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList import java.math.BigDecimal internal object ForYouPortfolioReviewPreviewData { @@ -151,5 +154,70 @@ internal object ForYouPortfolioReviewPreviewData { isExpandable = false, ), ), + onAddFundsClick = null, + ) + + val loadingState = PortfolioReviewUM.Loading( + marketChartUM = MarketChartUM.NoData( + title = resourceReference(R.string.market_chart_can_not_load_data), + donutText = resourceReference(R.string.market_chart_bubble_no_data), + ), + tokenList = buildList { + repeat(5) { index -> + add( + ForYouTokenListItemUM( + tokenRowUM = TangemTokenRowUM.Loading( + id = index.toString(), + ), + tokenList = persistentListOf(), + isExpanded = false, + isExpandable = false, + ), + ) + } + }.toPersistentList(), + ) + + val zeroPortfolioState = PortfolioReviewUM.Content( + marketChartUM = MarketChartUM.NoData( + title = stringReference("You don’t have any tokens with amount"), + donutText = stringReference("No amount on tokens"), + ), + tokenList = buildList { + repeat(5) { index -> + add( + ForYouTokenListItemUM( + tokenRowUM = TangemTokenRowUM.Content( + id = "token_$index", + headIconUM = TangemIconUM.Currency(CurrencyIconState.Loading), + titleUM = TangemTokenRowUM.TitleUM.Content( + text = stringReference("Token $index"), + badge = TangemBadgeUM( + text = stringReference("Positive"), + size = TangemBadgeSize.X4, + type = TangemBadgeType.Tinted, + color = TangemBadgeColor.Green, + ), + ), + subtitleUM = TangemTokenRowUM.SubtitleUM.Content( + text = stringReference("Some network"), + ), + topEndContentUM = TangemTokenRowUM.EndContentUM.Content( + text = stringReference("\$0"), + ), + bottomEndContentUM = TangemTokenRowUM.EndContentUM.Content( + text = stringReference("0.00%"), + ), + onItemClick = {}, + onItemLongClick = { _, _ -> }, + ), + tokenList = persistentListOf(), + isExpanded = false, + isExpandable = false, + ), + ) + } + }.toPersistentList(), + onAddFundsClick = { }, ) } \ No newline at end of file diff --git a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/ForYouModelTest.kt b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/ForYouModelTest.kt index b021e7f4b2..c43fd9a2a7 100644 --- a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/ForYouModelTest.kt +++ b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/ForYouModelTest.kt @@ -108,7 +108,7 @@ internal class ForYouModelTest { val loading = model.uiState.value.portfolioReviewUM as PortfolioReviewUM.Loading assertThat(loading.tokenList).hasSize(4) assertThat(loading.tokenList.all { it.tokenRowUM is TangemTokenRowUM.Loading }).isTrue() - assertThat(loading.marketChartUM).isEqualTo(MarketChartUM.NoData) + assertThat(loading.marketChartUM).isInstanceOf(MarketChartUM.NoData::class.java) } @Test diff --git a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewConverterTest.kt b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewConverterTest.kt index 02e1a1dabc..1d5a83b0d9 100644 --- a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewConverterTest.kt +++ b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewConverterTest.kt @@ -341,7 +341,7 @@ internal class ForYouPortfolioReviewConverterTest { val result = createConverter().convert(statusList) as PortfolioReviewUM.Content // Assert - assertThat(result.marketChartUM).isEqualTo(MarketChartUM.NoData) + assertThat(result.marketChartUM).isInstanceOf(MarketChartUM.NoData::class.java) } @Test @@ -350,7 +350,155 @@ internal class ForYouPortfolioReviewConverterTest { val result = createConverter().convert(null) as PortfolioReviewUM.Content // Assert - assertThat(result.marketChartUM).isEqualTo(MarketChartUM.NoData) + assertThat(result.marketChartUM).isInstanceOf(MarketChartUM.NoData::class.java) + } + } + + @Nested + inner class ZeroBalancePortfolio { + + @Test + fun `GIVEN all currencies have zero fiat WHEN convert THEN market chart is the no-amount NoData`() { + // Arrange + val statuses = listOf( + createStatus( + createCoin(rawCurrencyId = "btc", symbol = "BTC", networkId = "bitcoin"), + loadedValue(BigDecimal.ZERO, BigDecimal.ZERO), + ), + createStatus( + createCoin(rawCurrencyId = "eth", symbol = "ETH", networkId = "ethereum"), + loadedValue(BigDecimal.ZERO, BigDecimal.ZERO), + ), + ) + + // Act + val result = convert(statuses, totalFiatBalance = BigDecimal.ZERO) + + // Assert — the zero-balance treatment, not the generic can-not-load-data chart + assertThat(result.marketChartUM).isEqualTo( + MarketChartUM.NoData( + title = resourceReference(R.string.market_chart_no_amount), + donutText = resourceReference(R.string.market_chart_bubble_no_amount), + ), + ) + } + + @Test + fun `GIVEN all currencies have zero fiat WHEN add funds clicked THEN callback receives the wallet id`() { + // Arrange + val statuses = listOf( + createStatus( + createCoin(rawCurrencyId = "btc", symbol = "BTC", networkId = "bitcoin"), + loadedValue(BigDecimal.ZERO, BigDecimal.ZERO), + ), + ) + var addFundsWalletId: UserWalletId? = null + val converter = createConverter(onAddFundsClick = { addFundsWalletId = it }) + + // Act + val result = converter.convert( + accountStatusList(statuses, BigDecimal.ZERO), + ) as PortfolioReviewUM.Content + result.onAddFundsClick?.invoke() + + // Assert + assertThat(result.onAddFundsClick).isNotNull() + assertThat(addFundsWalletId).isEqualTo(UserWalletId("01")) + } + + @Test + fun `GIVEN a non-zero balance WHEN convert THEN add funds action is absent`() { + // Arrange + val statuses = listOf( + createStatus( + createCoin(rawCurrencyId = "btc", symbol = "BTC", networkId = "bitcoin"), + loadedValue(BigDecimal.ONE, BigDecimal("100")), + ), + ) + + // Act + val result = convert(statuses, totalFiatBalance = BigDecimal("100")) + + // Assert + assertThat(result.onAddFundsClick).isNull() + } + + @Test + fun `GIVEN null account status list WHEN add funds clicked THEN callback is not invoked`() { + // Arrange — without a wallet there is nowhere to add funds, so the click must be a no-op + var clicked = false + val converter = createConverter(onAddFundsClick = { clicked = true }) + + // Act + val result = converter.convert(null) as PortfolioReviewUM.Content + result.onAddFundsClick?.invoke() + + // Assert + assertThat(clicked).isFalse() + } + + @Test + fun `GIVEN more than five zero-fiat currencies WHEN convert THEN list is capped with no Other row`() { + // Arrange — 7 distinct zero-balance assets; the zero-balance branch shows the first 5 + // as-is instead of ranking and collapsing the excess into an "Other" row + val statuses = (1..7).map { index -> + createStatus( + createCoin(rawCurrencyId = "asset-$index", symbol = "A$index", networkId = "net-$index"), + loadedValue(BigDecimal.ZERO, BigDecimal.ZERO), + ) + } + + // Act + val result = convert(statuses, totalFiatBalance = BigDecimal.ZERO) + + // Assert + assertThat(result.tokenList.map { it.tokenRowUM.id }) + .containsExactly("asset-1", "asset-2", "asset-3", "asset-4", "asset-5") + .inOrder() + } + + @Test + fun `GIVEN zero-fiat asset on several networks WHEN convert THEN grouped into one expandable item`() { + // Arrange — the same asset (shared rawCurrencyId) with zero balances on two networks + val onEth = createToken(rawCurrencyId = "usdc", symbol = "USDC", networkId = "ethereum") + val onSol = createToken(rawCurrencyId = "usdc", symbol = "USDC", networkId = "solana") + val statuses = listOf( + createStatus(onEth, loadedValue(BigDecimal.ZERO, BigDecimal.ZERO)), + createStatus(onSol, loadedValue(BigDecimal.ZERO, BigDecimal.ZERO)), + ) + + // Act + val result = convert(statuses, totalFiatBalance = BigDecimal.ZERO) + + // Assert + val item = result.tokenList.single() + assertThat(item.tokenRowUM.id).isEqualTo("usdc") + assertThat(item.tokenList).hasSize(2) + assertThat(item.isExpandable).isTrue() + } + + @Test + fun `GIVEN zero and null fiat currencies mixed WHEN convert THEN zero-balance treatment is not applied`() { + // Arrange — an unreachable holding has an *unknown* balance, not a resolved zero, so the + // portfolio must not collapse into the add-funds empty state + val statuses = listOf( + createStatus( + createCoin(rawCurrencyId = "eth", symbol = "ETH", networkId = "ethereum"), + loadedValue(BigDecimal.ZERO, BigDecimal.ZERO), + ), + createStatus( + createCoin(rawCurrencyId = "btc", symbol = "BTC", networkId = "bitcoin"), + unreachableValue(), + ), + ) + + // Act + val result = convert(statuses, totalFiatBalance = BigDecimal.ZERO) + + // Assert — falls through to the ranked branch: no add-funds action, the resolved zero is + // dropped, the unknown-balance holding stays visible + assertThat(result.onAddFundsClick).isNull() + assertThat(result.tokenList.map { it.tokenRowUM.id }).containsExactly("btc") } } @@ -364,11 +512,13 @@ internal class ForYouPortfolioReviewConverterTest { expandedAssetIds: Set = emptySet(), expandClick: (String) -> Unit = {}, onTokenClick: (UserWalletId, CryptoCurrency) -> Unit = { _, _ -> }, + onAddFundsClick: (UserWalletId) -> Unit = {}, ): ForYouPortfolioReviewConverter = ForYouPortfolioReviewConverter( appCurrency = appCurrency, expandedAssetIds = expandedAssetIds, expandClick = expandClick, onTokenClick = onTokenClick, + onAddFundsClick = onAddFundsClick, ) private fun accountStatusList( diff --git a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewTokenRowConverterTest.kt b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewTokenRowConverterTest.kt index 15447c10cb..db070a0d17 100644 --- a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewTokenRowConverterTest.kt +++ b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/converter/portfolioReview/ForYouPortfolioReviewTokenRowConverterTest.kt @@ -25,24 +25,24 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { private val appCurrency: AppCurrency = AppCurrency.Default @Nested - inner class ConvertNetworkGroup { + inner class Convert { @Test - fun `GIVEN all statuses Loading WHEN convertNetworkGroup THEN row is Loading with representative id`() { + fun `GIVEN all statuses Loading WHEN convert THEN row is Loading with representative id`() { // Arrange val currency = createCurrency(id = "coin-eth", symbol = "ETH") val statuses = listOf(createStatus(currency, CryptoCurrencyStatus.Loading)) val converter = createConverter(totalFiatBalance = BigDecimal("1000")) // Act - val result = converter.convertNetworkGroup(statuses) + val result = converter.convert(statuses) // Assert assertThat(result).isEqualTo(TangemTokenRowUM.Loading(id = "coin-eth")) } @Test - fun `GIVEN single loaded status WHEN convertNetworkGroup THEN row is Content with its amounts`() { + fun `GIVEN single loaded status WHEN convert THEN row is Content with its amounts`() { // Arrange val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum") val statuses = listOf( @@ -51,7 +51,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { val converter = createConverter(totalFiatBalance = BigDecimal("1000")) // Act - val result = converter.convertNetworkGroup(statuses) as TangemTokenRowUM.Content + val result = converter.convert(statuses) as TangemTokenRowUM.Content // Assert assertThat(result.id).isEqualTo("coin-eth") @@ -62,7 +62,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { } @Test - fun `GIVEN several statuses of the same asset on one network WHEN convertNetworkGroup THEN amounts are summed`() { + fun `GIVEN several statuses of the same asset on one network WHEN convert THEN amounts are summed`() { // Arrange — same asset held in two accounts on the same network aggregates into one row val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum") val statuses = listOf( @@ -72,7 +72,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { val converter = createConverter(totalFiatBalance = BigDecimal("1000")) // Act - val result = converter.convertNetworkGroup(statuses) as TangemTokenRowUM.Content + val result = converter.convert(statuses) as TangemTokenRowUM.Content // Assert val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content @@ -80,7 +80,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { } @Test - fun `GIVEN mixed Loading and Loaded statuses WHEN convertNetworkGroup THEN row is Content`() { + fun `GIVEN mixed Loading and Loaded statuses WHEN convert THEN row is Content`() { // Arrange — not *all* statuses are Loading, so it should not collapse to a Loading row val currency = createCurrency(id = "coin-eth", symbol = "ETH") val statuses = listOf( @@ -90,14 +90,14 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { val converter = createConverter(totalFiatBalance = BigDecimal("1000")) // Act - val result = converter.convertNetworkGroup(statuses) + val result = converter.convert(statuses) // Assert assertThat(result).isInstanceOf(TangemTokenRowUM.Content::class.java) } @Test - fun `GIVEN loaded status from cache WHEN convertNetworkGroup THEN content flickers`() { + fun `GIVEN loaded status from cache WHEN convert THEN content flickers`() { // Arrange val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum") val statuses = listOf( @@ -109,7 +109,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { val converter = createConverter(totalFiatBalance = BigDecimal("1000")) // Act - val result = converter.convertNetworkGroup(statuses) as TangemTokenRowUM.Content + val result = converter.convert(statuses) as TangemTokenRowUM.Content // Assert val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content @@ -120,7 +120,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { } @Test - fun `GIVEN loaded status only-cache WHEN convertNetworkGroup THEN error-sync start icon shown`() { + fun `GIVEN loaded status only-cache WHEN convert THEN error-sync start icon shown`() { // Arrange val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum") val statuses = listOf( @@ -136,7 +136,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { val converter = createConverter(totalFiatBalance = BigDecimal("1000")) // Act - val result = converter.convertNetworkGroup(statuses) as TangemTokenRowUM.Content + val result = converter.convert(statuses) as TangemTokenRowUM.Content // Assert val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content @@ -145,14 +145,14 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { } @Test - fun `GIVEN missed derivation status WHEN convertNetworkGroup THEN no-address treatment`() { + fun `GIVEN missed derivation status WHEN convert THEN no-address treatment`() { // Arrange val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum") val statuses = listOf(createStatus(currency, missedDerivationValue())) val converter = createConverter(totalFiatBalance = BigDecimal("1000")) // Act - val result = converter.convertNetworkGroup(statuses) as TangemTokenRowUM.Content + val result = converter.convert(statuses) as TangemTokenRowUM.Content // Assert — top-end is a dash, bottom-end carries the attention "no address" icon val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content @@ -162,14 +162,14 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { } @Test - fun `GIVEN unreachable status WHEN convertNetworkGroup THEN dash on top and attention icon on bottom`() { + fun `GIVEN unreachable status WHEN convert THEN dash on top and attention icon on bottom`() { // Arrange val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum") val statuses = listOf(createStatus(currency, unreachableValue())) val converter = createConverter(totalFiatBalance = BigDecimal("1000")) // Act - val result = converter.convertNetworkGroup(statuses) as TangemTokenRowUM.Content + val result = converter.convert(statuses) as TangemTokenRowUM.Content // Assert — top-end is a bare dash, the attention "unreachable" icon lives on the bottom end val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content @@ -179,7 +179,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { } @Test - fun `GIVEN mixed Loaded and Unreachable WHEN convertNetworkGroup THEN collapses to unreachable`() { + fun `GIVEN mixed Loaded and Unreachable WHEN convert THEN collapses to unreachable`() { // Arrange — one account resolved, another unreachable: the row must surface the error state val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum") val statuses = listOf( @@ -189,7 +189,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { val converter = createConverter(totalFiatBalance = BigDecimal("1000")) // Act - val result = converter.convertNetworkGroup(statuses) as TangemTokenRowUM.Content + val result = converter.convert(statuses) as TangemTokenRowUM.Content // Assert — the unreachable treatment (attention icon on the bottom end) wins over the loaded amount val bottomEnd = result.bottomEndContentUM as TangemTokenRowUM.EndContentUM.Content @@ -212,7 +212,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { ) // Act - val result = converter.convertNetworkGroup(statuses) as TangemTokenRowUM.Content + val result = converter.convert(statuses) as TangemTokenRowUM.Content result.onItemClick?.invoke() // Assert @@ -234,7 +234,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { ) // Act - val result = converter.convertNetworkGroup(statuses) as TangemTokenRowUM.Content + val result = converter.convert(statuses) as TangemTokenRowUM.Content result.onItemClick?.invoke() // Assert @@ -242,7 +242,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { } @Test - fun `GIVEN mixed MissedDerivation and Unreachable WHEN convertNetworkGroup THEN missed-derivation wins`() { + fun `GIVEN mixed MissedDerivation and Unreachable WHEN convert THEN missed-derivation wins`() { // Arrange — missed derivation is the most severe terminal state and dominates val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum") val statuses = listOf( @@ -252,7 +252,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest { val converter = createConverter(totalFiatBalance = BigDecimal("1000")) // Act - val result = converter.convertNetworkGroup(statuses) as TangemTokenRowUM.Content + val result = converter.convert(statuses) as TangemTokenRowUM.Content // Assert — top-end is a dash (no-address treatment), not an unreachable label val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content diff --git a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/transformer/SetPortfolioReviewTransformerTest.kt b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/transformer/SetPortfolioReviewTransformerTest.kt index c8c458b12a..bfef65a043 100644 --- a/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/transformer/SetPortfolioReviewTransformerTest.kt +++ b/features/for-you/impl/src/test/kotlin/com/tangem/features/foryou/impl/model/transformer/SetPortfolioReviewTransformerTest.kt @@ -137,7 +137,8 @@ internal class SetPortfolioReviewTransformerTest { private fun contentPortfolioReview(): PortfolioReviewUM.Content = PortfolioReviewUM.Content( tokenList = persistentListOf(), - marketChartUM = MarketChartUM.NoData, + marketChartUM = noDataChart(), + onAddFundsClick = null, ) private fun contentEarnOpportunities(): EarnOpportunitiesUM.Content = EarnOpportunitiesUM.Content( @@ -148,6 +149,11 @@ internal class SetPortfolioReviewTransformerTest { onAllEarnTokensClick = {}, ) + private fun noDataChart(): MarketChartUM.NoData = MarketChartUM.NoData( + title = stringReference("No data"), + donutText = stringReference("No data"), + ) + private fun accountStatusList(totalFiatBalance: TotalFiatBalance): AccountStatusList = mockk { every { this@mockk.totalFiatBalance } returns totalFiatBalance } @@ -158,7 +164,7 @@ internal class SetPortfolioReviewTransformerTest { private fun loadingState(): ForYouUM = ForYouUM( portfolioReviewUM = PortfolioReviewUM.Loading( tokenList = persistentListOf(), - marketChartUM = MarketChartUM.NoData, + marketChartUM = noDataChart(), ), earnOpportunities = EarnOpportunitiesUM.Loading(tokenList = persistentListOf()), notifications = persistentListOf(),