Updated on 2026-08-14
This commit is contained in:
parent
d894698a9b
commit
c744af9a6a
17 changed files with 402 additions and 109 deletions
|
|
@ -908,9 +908,11 @@
|
||||||
<item quantity="one">%d asset</item>
|
<item quantity="one">%d asset</item>
|
||||||
<item quantity="other">%d assets</item>
|
<item quantity="other">%d assets</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
<string name="market_chart_bubble_no_amount">No amount
on tokens</string>
|
||||||
<string name="market_chart_bubble_no_data">No data</string>
|
<string name="market_chart_bubble_no_data">No data</string>
|
||||||
<string name="market_chart_bubble_total_value">Total value</string>
|
<string name="market_chart_bubble_total_value">Total value</string>
|
||||||
<string name="market_chart_can_not_load_data">Can’t load data</string>
|
<string name="market_chart_can_not_load_data">Can’t load data</string>
|
||||||
|
<string name="market_chart_no_amount">You don’t have any tokens with amount</string>
|
||||||
<string name="market_chart_top_holding">Top holding %s</string>
|
<string name="market_chart_top_holding">Top holding %s</string>
|
||||||
<string name="markets_about_coin_header">About coin</string>
|
<string name="markets_about_coin_header">About coin</string>
|
||||||
<string name="markets_add_to_my_portfolio_description">To buy, exchange, or receive this asset, add it to your portfolio</string>
|
<string name="markets_add_to_my_portfolio_description">To buy, exchange, or receive this asset, add it to your portfolio</string>
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,14 @@ internal class DefaultForYouComponent @AssistedInject constructor(
|
||||||
childFactory = { config, componentContext ->
|
childFactory = { config, componentContext ->
|
||||||
when (config) {
|
when (config) {
|
||||||
ForYouBottomSheetConfig.AddToPortfolio -> portfolioSelectorChild(componentContext)
|
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(
|
private fun manageFundsChild(
|
||||||
config: ForYouBottomSheetConfig.ManageFunds,
|
launchMode: ManageFundsComponent.LaunchMode,
|
||||||
componentContext: ComponentContext,
|
componentContext: ComponentContext,
|
||||||
): ComposableBottomSheetComponent = manageFundsComponentFactory.create(
|
): ComposableBottomSheetComponent = manageFundsComponentFactory.create(
|
||||||
context = childByContext(componentContext),
|
context = childByContext(componentContext),
|
||||||
params = ManageFundsComponent.Params(
|
params = ManageFundsComponent.Params(
|
||||||
launchMode = ManageFundsComponent.LaunchMode.FilteredByRawId(config.rawCurrencyId),
|
launchMode = launchMode,
|
||||||
onDismiss = { model.bottomSheetNavigation.dismiss() },
|
onDismiss = { model.bottomSheetNavigation.dismiss() },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.layout.boundsInWindow
|
import androidx.compose.ui.layout.boundsInWindow
|
||||||
import androidx.compose.ui.layout.onGloballyPositioned
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
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.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
|
@ -51,13 +52,16 @@ internal fun MarketChart(marketChart: MarketChartUM, modifier: Modifier = Modifi
|
||||||
Column(modifier = Modifier.fillMaxWidth()) {
|
Column(modifier = Modifier.fillMaxWidth()) {
|
||||||
DonutChartBlock(marketChart.donutChart, cardBoundsInWindow)
|
DonutChartBlock(marketChart.donutChart, cardBoundsInWindow)
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
if (marketChart is MarketChartUM.Loaded) {
|
when (marketChart) {
|
||||||
TopHoldingBlock(
|
is MarketChartUM.Loaded -> {
|
||||||
assetCount = marketChart.assetCount,
|
TopHoldingBlock(
|
||||||
topHoldingPercent = marketChart.topHoldingPercent,
|
assetCount = marketChart.assetCount,
|
||||||
)
|
topHoldingPercent = marketChart.topHoldingPercent,
|
||||||
} else {
|
)
|
||||||
CantLoadDataBlock()
|
}
|
||||||
|
is MarketChartUM.NoData -> {
|
||||||
|
CantLoadDataBlock(text = marketChart.title)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
@ -113,36 +117,40 @@ private fun ColumnScope.DonutChartBlock(donutChartUM: DonutChartUM, cardBoundsIn
|
||||||
},
|
},
|
||||||
segments = segments,
|
segments = segments,
|
||||||
) {
|
) {
|
||||||
if (donutChartUM is DonutChartUM.Loaded) {
|
when (donutChartUM) {
|
||||||
Text(
|
is DonutChartUM.Loaded -> {
|
||||||
text = donutChartUM.totalAmount,
|
Text(
|
||||||
color = TangemTheme.colors3.text.primary,
|
text = donutChartUM.totalAmount,
|
||||||
style = TangemTheme.typography3.body.medium,
|
color = TangemTheme.colors3.text.primary,
|
||||||
maxLines = 1,
|
style = TangemTheme.typography3.body.medium,
|
||||||
autoSize = TextAutoSize.StepBased(
|
maxLines = 1,
|
||||||
minFontSize = 8.sp,
|
autoSize = TextAutoSize.StepBased(
|
||||||
maxFontSize = TangemTheme.typography3.body.medium.fontSize,
|
minFontSize = 8.sp,
|
||||||
),
|
maxFontSize = TangemTheme.typography3.body.medium.fontSize,
|
||||||
)
|
),
|
||||||
Text(
|
)
|
||||||
text = stringResourceSafe(R.string.market_chart_bubble_total_value),
|
Text(
|
||||||
color = TangemTheme.colors3.text.secondary,
|
text = stringResourceSafe(R.string.market_chart_bubble_total_value),
|
||||||
style = TangemTheme.typography3.caption.medium,
|
color = TangemTheme.colors3.text.secondary,
|
||||||
maxLines = 1,
|
style = TangemTheme.typography3.caption.medium,
|
||||||
autoSize = TextAutoSize.StepBased(
|
maxLines = 1,
|
||||||
maxFontSize = TangemTheme.typography3.caption.medium.fontSize,
|
autoSize = TextAutoSize.StepBased(
|
||||||
),
|
maxFontSize = TangemTheme.typography3.caption.medium.fontSize,
|
||||||
)
|
),
|
||||||
} else {
|
)
|
||||||
Text(
|
}
|
||||||
text = stringResourceSafe(R.string.market_chart_bubble_no_data),
|
is DonutChartUM.NoData -> {
|
||||||
color = TangemTheme.colors3.text.secondary,
|
Text(
|
||||||
style = TangemTheme.typography3.body.medium,
|
text = donutChartUM.title.resolveReference(),
|
||||||
maxLines = 1,
|
color = TangemTheme.colors3.text.secondary,
|
||||||
autoSize = TextAutoSize.StepBased(
|
style = TangemTheme.typography3.body.medium,
|
||||||
maxFontSize = TangemTheme.typography3.caption.medium.fontSize,
|
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
|
@Composable
|
||||||
private fun ColumnScope.CantLoadDataBlock() {
|
private fun ColumnScope.CantLoadDataBlock(text: TextReference) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
text = stringResourceSafe(R.string.market_chart_can_not_load_data),
|
text = text.resolveReference(),
|
||||||
color = TangemTheme.colors3.text.secondary,
|
color = TangemTheme.colors3.text.secondary,
|
||||||
style = TangemTheme.typography3.heading.small,
|
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")
|
@Suppress("MagicNumber")
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,11 @@ internal sealed class MarketChartUM(
|
||||||
val assetCount: Int = donutChart.donutSegmentList.size
|
val assetCount: Int = donutChart.donutSegmentList.size
|
||||||
}
|
}
|
||||||
|
|
||||||
data object NoData : MarketChartUM(
|
data class NoData(
|
||||||
donutChart = DonutChartUM.NoData,
|
val title: TextReference,
|
||||||
|
private val donutText: TextReference,
|
||||||
|
) : MarketChartUM(
|
||||||
|
donutChart = DonutChartUM.NoData(title = donutText),
|
||||||
aiInsight = AiInsightUM.Hide,
|
aiInsight = AiInsightUM.Hide,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +39,9 @@ internal sealed class DonutChartUM(
|
||||||
override val donutSegmentList: ImmutableList<DonutSegmentUM>,
|
override val donutSegmentList: ImmutableList<DonutSegmentUM>,
|
||||||
) : DonutChartUM(donutSegmentList = donutSegmentList)
|
) : DonutChartUM(donutSegmentList = donutSegmentList)
|
||||||
|
|
||||||
data object NoData : DonutChartUM(donutSegmentList = persistentListOf())
|
data class NoData(
|
||||||
|
val title: TextReference,
|
||||||
|
) : DonutChartUM(donutSegmentList = persistentListOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.tangem.features.foryou.impl.entity
|
package com.tangem.features.foryou.impl.entity
|
||||||
|
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
|
||||||
internal sealed interface ForYouBottomSheetConfig {
|
internal sealed interface ForYouBottomSheetConfig {
|
||||||
|
|
||||||
|
|
@ -9,4 +10,8 @@ internal sealed interface ForYouBottomSheetConfig {
|
||||||
data class ManageFunds(
|
data class ManageFunds(
|
||||||
val rawCurrencyId: CryptoCurrency.RawID,
|
val rawCurrencyId: CryptoCurrency.RawID,
|
||||||
) : ForYouBottomSheetConfig
|
) : ForYouBottomSheetConfig
|
||||||
|
|
||||||
|
data class AddFunds(
|
||||||
|
val userWalletId: UserWalletId,
|
||||||
|
) : ForYouBottomSheetConfig
|
||||||
}
|
}
|
||||||
|
|
@ -31,6 +31,7 @@ internal sealed interface PortfolioReviewUM {
|
||||||
data class Content(
|
data class Content(
|
||||||
override val tokenList: ImmutableList<ForYouTokenListItemUM>,
|
override val tokenList: ImmutableList<ForYouTokenListItemUM>,
|
||||||
override val marketChartUM: MarketChartUM,
|
override val marketChartUM: MarketChartUM,
|
||||||
|
val onAddFundsClick: (() -> Unit)?,
|
||||||
) : PortfolioReviewUM
|
) : PortfolioReviewUM
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.row.token.TangemTokenRowUM
|
||||||
import com.tangem.core.ui.ds.tabs.TangemSegmentUM
|
import com.tangem.core.ui.ds.tabs.TangemSegmentUM
|
||||||
import com.tangem.core.ui.ds.tabs.TangemSegmentedPickerUM
|
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.supplier.MultiAccountStatusListSupplier
|
||||||
import com.tangem.domain.account.status.usecase.IsAccountsModeEnabledUseCase
|
import com.tangem.domain.account.status.usecase.IsAccountsModeEnabledUseCase
|
||||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
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.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||||
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
|
import com.tangem.features.commonfeatures.api.addtoportfolio.AddToPortfolioManager
|
||||||
import com.tangem.features.foryou.ForYouComponent
|
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.components.state.MarketChartUM
|
||||||
import com.tangem.features.foryou.impl.entity.*
|
import com.tangem.features.foryou.impl.entity.*
|
||||||
import com.tangem.features.foryou.impl.model.converter.TOP_EARN_TOKENS_BATCH_SIZE
|
import com.tangem.features.foryou.impl.model.converter.TOP_EARN_TOKENS_BATCH_SIZE
|
||||||
|
|
@ -106,7 +108,10 @@ internal class ForYouModel @Inject constructor(
|
||||||
),
|
),
|
||||||
onPeriodClick = ::onPeriodClick,
|
onPeriodClick = ::onPeriodClick,
|
||||||
portfolioReviewUM = PortfolioReviewUM.Loading(
|
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<ForYouTokenListItemUM> {
|
tokenList = buildList<ForYouTokenListItemUM> {
|
||||||
repeat(4) { index ->
|
repeat(4) { index ->
|
||||||
add(
|
add(
|
||||||
|
|
@ -154,6 +159,7 @@ internal class ForYouModel @Inject constructor(
|
||||||
expandedAssetIds = expandedPortfolioReview,
|
expandedAssetIds = expandedPortfolioReview,
|
||||||
expandClick = ::onExpandPortfolioReviewClick,
|
expandClick = ::onExpandPortfolioReviewClick,
|
||||||
onTokenClick = ::onPortfolioReviewTokenClick,
|
onTokenClick = ::onPortfolioReviewTokenClick,
|
||||||
|
onAddFundsClick = ::onAddFundsClick,
|
||||||
).convert(accountStatusList)
|
).convert(accountStatusList)
|
||||||
|
|
||||||
val earnOpportunitiesUM = ForYouEarnOpportunitiesConverter(
|
val earnOpportunitiesUM = ForYouEarnOpportunitiesConverter(
|
||||||
|
|
@ -363,4 +369,8 @@ internal class ForYouModel @Inject constructor(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun onAddFundsClick(userWalletId: UserWalletId) {
|
||||||
|
bottomSheetNavigation.activate(ForYouBottomSheetConfig.AddFunds(userWalletId))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -15,8 +15,10 @@ import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.features.foryou.impl.R
|
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.ForYouTokenListItemUM
|
||||||
import com.tangem.features.foryou.impl.entity.PortfolioReviewUM
|
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.forYouGroupKey
|
||||||
import com.tangem.features.foryou.impl.model.converter.forYouPlaceholderBadge
|
import com.tangem.features.foryou.impl.model.converter.forYouPlaceholderBadge
|
||||||
import com.tangem.features.foryou.impl.model.converter.toForYouPercent
|
import com.tangem.features.foryou.impl.model.converter.toForYouPercent
|
||||||
|
|
@ -42,6 +44,7 @@ internal class ForYouPortfolioReviewConverter(
|
||||||
private val expandedAssetIds: Set<String>,
|
private val expandedAssetIds: Set<String>,
|
||||||
private val expandClick: (assetId: String) -> Unit,
|
private val expandClick: (assetId: String) -> Unit,
|
||||||
private val onTokenClick: (UserWalletId, CryptoCurrency) -> Unit,
|
private val onTokenClick: (UserWalletId, CryptoCurrency) -> Unit,
|
||||||
|
private val onAddFundsClick: (UserWalletId) -> Unit,
|
||||||
) : Converter<AccountStatusList?, PortfolioReviewUM> {
|
) : Converter<AccountStatusList?, PortfolioReviewUM> {
|
||||||
|
|
||||||
private val iconConverter = CryptoCurrencyToIconStateConverter()
|
private val iconConverter = CryptoCurrencyToIconStateConverter()
|
||||||
|
|
@ -51,6 +54,26 @@ internal class ForYouPortfolioReviewConverter(
|
||||||
val loadedBalance = value?.totalFiatBalance as? TotalFiatBalance.Loaded
|
val loadedBalance = value?.totalFiatBalance as? TotalFiatBalance.Loaded
|
||||||
val totalFiatBalance = loadedBalance?.amount.orZero()
|
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
|
// 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
|
// 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
|
// 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(
|
return PortfolioReviewUM.Content(
|
||||||
tokenList = tokenList,
|
tokenList = tokenList,
|
||||||
marketChartUM = marketChartUM,
|
marketChartUM = marketChartUM,
|
||||||
|
onAddFundsClick = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,7 +151,7 @@ internal class ForYouPortfolioReviewConverter(
|
||||||
networkCount = networkGroups.size,
|
networkCount = networkGroups.size,
|
||||||
totalFiatBalance = totalFiatBalance,
|
totalFiatBalance = totalFiatBalance,
|
||||||
),
|
),
|
||||||
tokenList = networkGroups.map(rowConverter::convertNetworkGroup).toPersistentList(),
|
tokenList = networkGroups.map(rowConverter::convert).toPersistentList(),
|
||||||
isExpanded = assetId in expandedAssetIds,
|
isExpanded = assetId in expandedAssetIds,
|
||||||
isExpandable = true,
|
isExpandable = true,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package com.tangem.features.foryou.impl.model.converter.portfolioReview
|
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.extensions.stringReference
|
||||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||||
import com.tangem.core.ui.format.bigdecimal.format
|
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.appcurrency.model.AppCurrency
|
||||||
import com.tangem.domain.models.TotalFiatBalance
|
import com.tangem.domain.models.TotalFiatBalance
|
||||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
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.AiInsightUM
|
||||||
import com.tangem.features.foryou.impl.components.state.DonutChartUM
|
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.DonutSegmentColor
|
||||||
|
|
@ -54,7 +56,10 @@ internal class ForYouPortfolioReviewMarketChartConverter(
|
||||||
TotalFiatBalance.Loading,
|
TotalFiatBalance.Loading,
|
||||||
TotalFiatBalance.Failed,
|
TotalFiatBalance.Failed,
|
||||||
null,
|
null,
|
||||||
-> MarketChartUM.NoData
|
-> MarketChartUM.NoData(
|
||||||
|
title = resourceReference(R.string.market_chart_can_not_load_data),
|
||||||
|
donutText = resourceReference(R.string.market_chart_bubble_no_data),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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.forYouPlaceholderBadge
|
||||||
import com.tangem.features.foryou.impl.model.converter.toForYouPercent
|
import com.tangem.features.foryou.impl.model.converter.toForYouPercent
|
||||||
import com.tangem.utils.StringsSigns
|
import com.tangem.utils.StringsSigns
|
||||||
|
import com.tangem.utils.converter.Converter
|
||||||
import com.tangem.utils.extensions.orZero
|
import com.tangem.utils.extensions.orZero
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
|
@ -47,21 +48,20 @@ internal class ForYouPortfolioReviewTokenRowConverter(
|
||||||
private val userWalletId: UserWalletId?,
|
private val userWalletId: UserWalletId?,
|
||||||
private val totalFiatBalance: BigDecimal,
|
private val totalFiatBalance: BigDecimal,
|
||||||
private val onTokenClick: (UserWalletId, CryptoCurrency) -> Unit,
|
private val onTokenClick: (UserWalletId, CryptoCurrency) -> Unit,
|
||||||
) {
|
) : Converter<List<CryptoCurrencyStatus>, TangemTokenRowUM> {
|
||||||
|
|
||||||
private val iconConverter = CryptoCurrencyToIconStateConverter()
|
private val iconConverter = CryptoCurrencyToIconStateConverter()
|
||||||
|
|
||||||
/** Builds one row for all [statuses] of a single asset on the same network. */
|
override fun convert(value: List<CryptoCurrencyStatus>): TangemTokenRowUM {
|
||||||
fun convertNetworkGroup(statuses: List<CryptoCurrencyStatus>): TangemTokenRowUM {
|
val representative = value.first()
|
||||||
val representative = statuses.first()
|
if (value.all { it.value is CryptoCurrencyStatus.Loading }) {
|
||||||
if (statuses.all { it.value is CryptoCurrencyStatus.Loading }) {
|
|
||||||
return TangemTokenRowUM.Loading(id = representative.currency.id.value)
|
return TangemTokenRowUM.Loading(id = representative.currency.id.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
val currency = representative.currency
|
val currency = representative.currency
|
||||||
val cryptoAmount = statuses.sumOf { it.value.amount.orZero() }
|
val cryptoAmount = value.sumOf { it.value.amount.orZero() }
|
||||||
val fiatAmount = statuses.sumOf { it.value.fiatAmount.orZero() }
|
val fiatAmount = value.sumOf { it.value.fiatAmount.orZero() }
|
||||||
val state = statuses.classify()
|
val state = value.classify()
|
||||||
|
|
||||||
return TangemTokenRowUM.Content(
|
return TangemTokenRowUM.Content(
|
||||||
id = currency.id.value,
|
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
|
* 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 /
|
* 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
|
* of its holdings — analogous to how `AccountCryptoPortfolioItemStateConverter` reflects a
|
||||||
* `TotalFiatBalance`'s status on the account row.
|
* `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. */
|
/** Bottom-end: percentage share for resolved states, no-address / unreachable treatment otherwise. */
|
||||||
private fun toRowBottomEnd(state: RowState, fiatAmount: BigDecimal): TangemTokenRowUM.EndContentUM = when (state) {
|
private fun toRowBottomEnd(state: RowState, fiatAmount: BigDecimal): TangemTokenRowUM.EndContentUM = when (state) {
|
||||||
is RowState.Normal -> TangemTokenRowUM.EndContentUM.Content(
|
is RowState.Normal -> TangemTokenRowUM.EndContentUM.Content(
|
||||||
text = stringReference(fiatAmount.toForYouPercent(totalFiatBalance).format { percent() }),
|
text = stringReference(fiatAmount.toForYouPercent(totalFiatBalance).orZero().format { percent() }),
|
||||||
isFlickering = state.isFlickering,
|
isFlickering = state.isFlickering,
|
||||||
)
|
)
|
||||||
RowState.NoAddress -> attentionEndContent(R.string.common_no_address)
|
RowState.NoAddress -> attentionEndContent(R.string.common_no_address)
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,11 @@ internal fun ForYouContent(
|
||||||
.drawBehind { drawRect(background.value) },
|
.drawBehind { drawRect(background.value) },
|
||||||
) {
|
) {
|
||||||
promoBannersBlockComponent.ContentWithPadding(
|
promoBannersBlockComponent.ContentWithPadding(
|
||||||
modifier = Modifier.padding(top = 12.dp),
|
modifier = Modifier
|
||||||
|
.padding(top = 12.dp)
|
||||||
|
.conditional(forYouUM.notifications.isEmpty()) {
|
||||||
|
padding(bottom = 48.dp)
|
||||||
|
},
|
||||||
walletId = null,
|
walletId = null,
|
||||||
horizontalItemPadding = 16.dp,
|
horizontalItemPadding = 16.dp,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,13 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.tangem.core.ui.components.SpacerH
|
import com.tangem.core.ui.components.SpacerH
|
||||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
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.TangemSegmentUM
|
||||||
import com.tangem.core.ui.ds.tabs.TangemSegmentedPicker
|
import com.tangem.core.ui.ds.tabs.TangemSegmentedPicker
|
||||||
import com.tangem.core.ui.ds.tabs.TangemSegmentedPickerUM
|
import com.tangem.core.ui.ds.tabs.TangemSegmentedPickerUM
|
||||||
import com.tangem.core.ui.ds2.badge.TangemBadge
|
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.ds2.shimmers.TangemShimmer
|
||||||
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
import com.tangem.core.ui.extensions.stringReference
|
import com.tangem.core.ui.extensions.stringReference
|
||||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
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.core.ui.res.generated.icons.ic_chevron_down_16
|
||||||
import com.tangem.features.foryou.impl.R
|
import com.tangem.features.foryou.impl.R
|
||||||
import com.tangem.features.foryou.impl.components.MarketChart
|
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.entity.PortfolioReviewUM
|
||||||
import com.tangem.features.foryou.impl.ui.components.ForYouPortfolioTokenList
|
import com.tangem.features.foryou.impl.ui.components.ForYouPortfolioTokenList
|
||||||
import com.tangem.features.foryou.impl.ui.preview.ForYouPortfolioReviewPreviewData
|
import com.tangem.features.foryou.impl.ui.preview.ForYouPortfolioReviewPreviewData
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ForYouPortfolioReview(
|
internal fun ForYouPortfolioReview(
|
||||||
|
|
@ -84,6 +82,18 @@ internal fun ForYouPortfolioReview(
|
||||||
}
|
}
|
||||||
|
|
||||||
ForYouPortfolioTokenList(tokenList = portfolioReviewUM.tokenList)
|
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<Po
|
||||||
override val values: Sequence<PortfolioReviewUM>
|
override val values: Sequence<PortfolioReviewUM>
|
||||||
get() = sequenceOf(
|
get() = sequenceOf(
|
||||||
ForYouPortfolioReviewPreviewData.reviewContent,
|
ForYouPortfolioReviewPreviewData.reviewContent,
|
||||||
PortfolioReviewUM.Loading(
|
ForYouPortfolioReviewPreviewData.loadingState,
|
||||||
marketChartUM = MarketChartUM.NoData,
|
ForYouPortfolioReviewPreviewData.zeroPortfolioState,
|
||||||
tokenList = buildList {
|
|
||||||
repeat(4) { index ->
|
|
||||||
add(
|
|
||||||
ForYouTokenListItemUM(
|
|
||||||
tokenRowUM = TangemTokenRowUM.Loading(
|
|
||||||
id = index.toString(),
|
|
||||||
),
|
|
||||||
tokenList = persistentListOf(),
|
|
||||||
isExpanded = false,
|
|
||||||
isExpandable = false,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}.toPersistentList(),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
@ -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.badge.TangemBadgeUM
|
||||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||||
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
|
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.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.DonutChartUM
|
||||||
import com.tangem.features.foryou.impl.components.state.DonutSegmentColor
|
import com.tangem.features.foryou.impl.components.state.DonutSegmentColor
|
||||||
import com.tangem.features.foryou.impl.components.state.DonutSegmentUM
|
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.features.foryou.impl.entity.PortfolioReviewUM
|
||||||
import com.tangem.utils.StringsSigns.DOT
|
import com.tangem.utils.StringsSigns.DOT
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
internal object ForYouPortfolioReviewPreviewData {
|
internal object ForYouPortfolioReviewPreviewData {
|
||||||
|
|
@ -151,5 +154,70 @@ internal object ForYouPortfolioReviewPreviewData {
|
||||||
isExpandable = false,
|
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 = { },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -108,7 +108,7 @@ internal class ForYouModelTest {
|
||||||
val loading = model.uiState.value.portfolioReviewUM as PortfolioReviewUM.Loading
|
val loading = model.uiState.value.portfolioReviewUM as PortfolioReviewUM.Loading
|
||||||
assertThat(loading.tokenList).hasSize(4)
|
assertThat(loading.tokenList).hasSize(4)
|
||||||
assertThat(loading.tokenList.all { it.tokenRowUM is TangemTokenRowUM.Loading }).isTrue()
|
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
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -341,7 +341,7 @@ internal class ForYouPortfolioReviewConverterTest {
|
||||||
val result = createConverter().convert(statusList) as PortfolioReviewUM.Content
|
val result = createConverter().convert(statusList) as PortfolioReviewUM.Content
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
assertThat(result.marketChartUM).isEqualTo(MarketChartUM.NoData)
|
assertThat(result.marketChartUM).isInstanceOf(MarketChartUM.NoData::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -350,7 +350,155 @@ internal class ForYouPortfolioReviewConverterTest {
|
||||||
val result = createConverter().convert(null) as PortfolioReviewUM.Content
|
val result = createConverter().convert(null) as PortfolioReviewUM.Content
|
||||||
|
|
||||||
// Assert
|
// 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<String> = emptySet(),
|
expandedAssetIds: Set<String> = emptySet(),
|
||||||
expandClick: (String) -> Unit = {},
|
expandClick: (String) -> Unit = {},
|
||||||
onTokenClick: (UserWalletId, CryptoCurrency) -> Unit = { _, _ -> },
|
onTokenClick: (UserWalletId, CryptoCurrency) -> Unit = { _, _ -> },
|
||||||
|
onAddFundsClick: (UserWalletId) -> Unit = {},
|
||||||
): ForYouPortfolioReviewConverter = ForYouPortfolioReviewConverter(
|
): ForYouPortfolioReviewConverter = ForYouPortfolioReviewConverter(
|
||||||
appCurrency = appCurrency,
|
appCurrency = appCurrency,
|
||||||
expandedAssetIds = expandedAssetIds,
|
expandedAssetIds = expandedAssetIds,
|
||||||
expandClick = expandClick,
|
expandClick = expandClick,
|
||||||
onTokenClick = onTokenClick,
|
onTokenClick = onTokenClick,
|
||||||
|
onAddFundsClick = onAddFundsClick,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun accountStatusList(
|
private fun accountStatusList(
|
||||||
|
|
|
||||||
|
|
@ -25,24 +25,24 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
private val appCurrency: AppCurrency = AppCurrency.Default
|
private val appCurrency: AppCurrency = AppCurrency.Default
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
inner class ConvertNetworkGroup {
|
inner class Convert {
|
||||||
|
|
||||||
@Test
|
@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
|
// Arrange
|
||||||
val currency = createCurrency(id = "coin-eth", symbol = "ETH")
|
val currency = createCurrency(id = "coin-eth", symbol = "ETH")
|
||||||
val statuses = listOf(createStatus(currency, CryptoCurrencyStatus.Loading))
|
val statuses = listOf(createStatus(currency, CryptoCurrencyStatus.Loading))
|
||||||
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val result = converter.convertNetworkGroup(statuses)
|
val result = converter.convert(statuses)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
assertThat(result).isEqualTo(TangemTokenRowUM.Loading(id = "coin-eth"))
|
assertThat(result).isEqualTo(TangemTokenRowUM.Loading(id = "coin-eth"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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
|
// Arrange
|
||||||
val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum")
|
val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum")
|
||||||
val statuses = listOf(
|
val statuses = listOf(
|
||||||
|
|
@ -51,7 +51,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val result = converter.convertNetworkGroup(statuses) as TangemTokenRowUM.Content
|
val result = converter.convert(statuses) as TangemTokenRowUM.Content
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
assertThat(result.id).isEqualTo("coin-eth")
|
assertThat(result.id).isEqualTo("coin-eth")
|
||||||
|
|
@ -62,7 +62,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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
|
// 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 currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum")
|
||||||
val statuses = listOf(
|
val statuses = listOf(
|
||||||
|
|
@ -72,7 +72,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val result = converter.convertNetworkGroup(statuses) as TangemTokenRowUM.Content
|
val result = converter.convert(statuses) as TangemTokenRowUM.Content
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content
|
val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content
|
||||||
|
|
@ -80,7 +80,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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
|
// Arrange — not *all* statuses are Loading, so it should not collapse to a Loading row
|
||||||
val currency = createCurrency(id = "coin-eth", symbol = "ETH")
|
val currency = createCurrency(id = "coin-eth", symbol = "ETH")
|
||||||
val statuses = listOf(
|
val statuses = listOf(
|
||||||
|
|
@ -90,14 +90,14 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val result = converter.convertNetworkGroup(statuses)
|
val result = converter.convert(statuses)
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
assertThat(result).isInstanceOf(TangemTokenRowUM.Content::class.java)
|
assertThat(result).isInstanceOf(TangemTokenRowUM.Content::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `GIVEN loaded status from cache WHEN convertNetworkGroup THEN content flickers`() {
|
fun `GIVEN loaded status from cache WHEN convert THEN content flickers`() {
|
||||||
// Arrange
|
// Arrange
|
||||||
val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum")
|
val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum")
|
||||||
val statuses = listOf(
|
val statuses = listOf(
|
||||||
|
|
@ -109,7 +109,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val result = converter.convertNetworkGroup(statuses) as TangemTokenRowUM.Content
|
val result = converter.convert(statuses) as TangemTokenRowUM.Content
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content
|
val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content
|
||||||
|
|
@ -120,7 +120,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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
|
// Arrange
|
||||||
val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum")
|
val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum")
|
||||||
val statuses = listOf(
|
val statuses = listOf(
|
||||||
|
|
@ -136,7 +136,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val result = converter.convertNetworkGroup(statuses) as TangemTokenRowUM.Content
|
val result = converter.convert(statuses) as TangemTokenRowUM.Content
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content
|
val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content
|
||||||
|
|
@ -145,14 +145,14 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `GIVEN missed derivation status WHEN convertNetworkGroup THEN no-address treatment`() {
|
fun `GIVEN missed derivation status WHEN convert THEN no-address treatment`() {
|
||||||
// Arrange
|
// Arrange
|
||||||
val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum")
|
val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum")
|
||||||
val statuses = listOf(createStatus(currency, missedDerivationValue()))
|
val statuses = listOf(createStatus(currency, missedDerivationValue()))
|
||||||
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
||||||
|
|
||||||
// Act
|
// 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
|
// Assert — top-end is a dash, bottom-end carries the attention "no address" icon
|
||||||
val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content
|
val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content
|
||||||
|
|
@ -162,14 +162,14 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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
|
// Arrange
|
||||||
val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum")
|
val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum")
|
||||||
val statuses = listOf(createStatus(currency, unreachableValue()))
|
val statuses = listOf(createStatus(currency, unreachableValue()))
|
||||||
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
||||||
|
|
||||||
// Act
|
// 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
|
// Assert — top-end is a bare dash, the attention "unreachable" icon lives on the bottom end
|
||||||
val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content
|
val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content
|
||||||
|
|
@ -179,7 +179,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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
|
// Arrange — one account resolved, another unreachable: the row must surface the error state
|
||||||
val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum")
|
val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum")
|
||||||
val statuses = listOf(
|
val statuses = listOf(
|
||||||
|
|
@ -189,7 +189,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
||||||
|
|
||||||
// Act
|
// 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
|
// Assert — the unreachable treatment (attention icon on the bottom end) wins over the loaded amount
|
||||||
val bottomEnd = result.bottomEndContentUM as TangemTokenRowUM.EndContentUM.Content
|
val bottomEnd = result.bottomEndContentUM as TangemTokenRowUM.EndContentUM.Content
|
||||||
|
|
@ -212,7 +212,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val result = converter.convertNetworkGroup(statuses) as TangemTokenRowUM.Content
|
val result = converter.convert(statuses) as TangemTokenRowUM.Content
|
||||||
result.onItemClick?.invoke()
|
result.onItemClick?.invoke()
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -234,7 +234,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
val result = converter.convertNetworkGroup(statuses) as TangemTokenRowUM.Content
|
val result = converter.convert(statuses) as TangemTokenRowUM.Content
|
||||||
result.onItemClick?.invoke()
|
result.onItemClick?.invoke()
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -242,7 +242,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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
|
// Arrange — missed derivation is the most severe terminal state and dominates
|
||||||
val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum")
|
val currency = createCurrency(id = "coin-eth", symbol = "ETH", networkName = "Ethereum")
|
||||||
val statuses = listOf(
|
val statuses = listOf(
|
||||||
|
|
@ -252,7 +252,7 @@ internal class ForYouPortfolioReviewTokenRowConverterTest {
|
||||||
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
val converter = createConverter(totalFiatBalance = BigDecimal("1000"))
|
||||||
|
|
||||||
// Act
|
// 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
|
// Assert — top-end is a dash (no-address treatment), not an unreachable label
|
||||||
val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content
|
val topEnd = result.topEndContentUM as TangemTokenRowUM.EndContentUM.Content
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,8 @@ internal class SetPortfolioReviewTransformerTest {
|
||||||
|
|
||||||
private fun contentPortfolioReview(): PortfolioReviewUM.Content = PortfolioReviewUM.Content(
|
private fun contentPortfolioReview(): PortfolioReviewUM.Content = PortfolioReviewUM.Content(
|
||||||
tokenList = persistentListOf(),
|
tokenList = persistentListOf(),
|
||||||
marketChartUM = MarketChartUM.NoData,
|
marketChartUM = noDataChart(),
|
||||||
|
onAddFundsClick = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun contentEarnOpportunities(): EarnOpportunitiesUM.Content = EarnOpportunitiesUM.Content(
|
private fun contentEarnOpportunities(): EarnOpportunitiesUM.Content = EarnOpportunitiesUM.Content(
|
||||||
|
|
@ -148,6 +149,11 @@ internal class SetPortfolioReviewTransformerTest {
|
||||||
onAllEarnTokensClick = {},
|
onAllEarnTokensClick = {},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun noDataChart(): MarketChartUM.NoData = MarketChartUM.NoData(
|
||||||
|
title = stringReference("No data"),
|
||||||
|
donutText = stringReference("No data"),
|
||||||
|
)
|
||||||
|
|
||||||
private fun accountStatusList(totalFiatBalance: TotalFiatBalance): AccountStatusList = mockk {
|
private fun accountStatusList(totalFiatBalance: TotalFiatBalance): AccountStatusList = mockk {
|
||||||
every { this@mockk.totalFiatBalance } returns totalFiatBalance
|
every { this@mockk.totalFiatBalance } returns totalFiatBalance
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +164,7 @@ internal class SetPortfolioReviewTransformerTest {
|
||||||
private fun loadingState(): ForYouUM = ForYouUM(
|
private fun loadingState(): ForYouUM = ForYouUM(
|
||||||
portfolioReviewUM = PortfolioReviewUM.Loading(
|
portfolioReviewUM = PortfolioReviewUM.Loading(
|
||||||
tokenList = persistentListOf(),
|
tokenList = persistentListOf(),
|
||||||
marketChartUM = MarketChartUM.NoData,
|
marketChartUM = noDataChart(),
|
||||||
),
|
),
|
||||||
earnOpportunities = EarnOpportunitiesUM.Loading(tokenList = persistentListOf()),
|
earnOpportunities = EarnOpportunitiesUM.Loading(tokenList = persistentListOf()),
|
||||||
notifications = persistentListOf(),
|
notifications = persistentListOf(),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue