From ba1b0e1f84250cc3890f90aaf20099b5ad14f08f Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 16 Mar 2026 14:26:00 +0100 Subject: [PATCH] Updated on 2026-08-14 --- core/res/src/main/res/values/strings.xml | 4 + .../information/InformationBlockContent.kt | 2 +- .../ui/components/items/DecriptionItem.kt | 149 ++++++++++- .../core/ui/ds/tabs/TangemSegmentedPicker.kt | 4 + .../market/detailed/components/InfoPoint.kt | 157 +++++++++++- .../detailed/components/InsightsBlock.kt | 234 ++++++++++++++++-- .../detailed/components/ListedOnBlock.kt | 6 +- .../detailed/components/SecurityScoreBlock.kt | 10 +- .../components/TokenMarketDetailsBody.kt | 138 +++++++++-- 9 files changed, 652 insertions(+), 52 deletions(-) diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 3d4d8d7a48..f60477cf1c 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -801,6 +801,10 @@ Holders The change in the number of token holders within a specific timeframe Holders + D + M + W + Y Insights Links Liquidity diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlockContent.kt b/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlockContent.kt index a39c6b7783..ea9b54bea5 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlockContent.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/block/information/InformationBlockContent.kt @@ -40,7 +40,7 @@ inline fun InformationBlockContentScope.ListItems( } @Composable -inline fun InformationBlockContentScope.GridItems( +inline fun GridItems( items: ImmutableList, modifier: Modifier = Modifier, verticalAlignment: Alignment.Vertical = Alignment.Top, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/items/DecriptionItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/items/DecriptionItem.kt index 25e496e785..2841c1c57e 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/items/DecriptionItem.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/items/DecriptionItem.kt @@ -1,24 +1,30 @@ package com.tangem.core.ui.components.items import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import com.tangem.core.ui.R import com.tangem.core.ui.components.TextShimmer import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.LocalRedesignEnabled import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.core.ui.utils.PreviewShimmerContainer import com.tangem.utils.StringsSigns @@ -29,6 +35,32 @@ fun DescriptionItem( onReadMoreClick: () -> Unit, modifier: Modifier = Modifier, textStyle: TextStyle = TangemTheme.typography.body2, +) { + if (LocalRedesignEnabled.current) { + DescriptionItemV2( + description = description, + hasFullDescription = hasFullDescription, + onReadMoreClick = onReadMoreClick, + modifier = modifier, + ) + } else { + DescriptionItemV1( + description = description, + hasFullDescription = hasFullDescription, + onReadMoreClick = onReadMoreClick, + modifier = modifier, + textStyle = textStyle, + ) + } +} + +@Composable +private fun DescriptionItemV1( + description: TextReference, + hasFullDescription: Boolean, + onReadMoreClick: () -> Unit, + modifier: Modifier = Modifier, + textStyle: TextStyle = TangemTheme.typography.body2, ) { if (hasFullDescription) { val text = buildAnnotatedString { @@ -66,7 +98,58 @@ fun DescriptionItem( } @Composable -public fun DescriptionPlaceholder(modifier: Modifier = Modifier) { +private fun DescriptionItemV2( + description: TextReference, + hasFullDescription: Boolean, + onReadMoreClick: () -> Unit, + modifier: Modifier = Modifier, +) { + if (hasFullDescription) { + val text = buildAnnotatedString { + withStyle(SpanStyle(color = TangemTheme.colors2.text.neutral.tertiary)) { + append(description.resolveReference()) + } + withStyle(SpanStyle(color = TangemTheme.colors2.text.neutral.primary)) { + append( + " " + stringResourceSafe(R.string.common_read_more).replace( + ' ', + StringsSigns.NON_BREAKING_SPACE, + ), + ) + } + } + + Text( + modifier = modifier + .clickable( + interactionSource = null, + indication = null, + onClick = onReadMoreClick, + ), + text = text, + style = TangemTheme.typography2.bodyRegular15, + ) + } else { + Text( + modifier = modifier, + text = description.resolveReference(), + style = TangemTheme.typography2.bodyRegular15, + color = TangemTheme.colors2.text.neutral.tertiary, + ) + } +} + +@Composable +fun DescriptionPlaceholder(modifier: Modifier = Modifier) { + if (LocalRedesignEnabled.current) { + DescriptionPlaceholderV2(modifier) + } else { + DescriptionPlaceholderV1(modifier) + } +} + +@Composable +private fun DescriptionPlaceholderV1(modifier: Modifier = Modifier) { Column( modifier = modifier, ) { @@ -88,9 +171,33 @@ public fun DescriptionPlaceholder(modifier: Modifier = Modifier) { } } +@Composable +private fun DescriptionPlaceholderV2(modifier: Modifier = Modifier) { + Column( + modifier = modifier.padding(horizontal = 6.dp), + verticalArrangement = Arrangement.spacedBy(6.dp), + ) { + TextShimmer( + modifier = Modifier.fillMaxWidth(), + style = TangemTheme.typography2.bodyRegular15, + radius = TangemTheme.dimens2.x25, + ) + TextShimmer( + modifier = Modifier.fillMaxWidth(), + style = TangemTheme.typography2.bodyRegular15, + radius = TangemTheme.dimens2.x25, + ) + TextShimmer( + modifier = Modifier.fillMaxWidth(fraction = 0.8f), + style = TangemTheme.typography2.bodyRegular15, + radius = TangemTheme.dimens2.x25, + ) + } +} + @Preview @Composable -private fun ContentPreview() { +private fun ContentPreviewV1() { TangemThemePreview { DescriptionItem( description = stringReference( @@ -105,15 +212,49 @@ private fun ContentPreview() { @Preview @Composable -private fun PreviewPlaceholder() { +private fun ContentPreviewV2() { + CompositionLocalProvider(LocalRedesignEnabled provides true) { + TangemThemePreviewRedesign { + DescriptionItem( + description = stringReference( + "XRP (XRP) is a cryptocurrency launched in January 2009, where the first " + + "genesis block was mined on 9th January 2009", + ), + hasFullDescription = true, + onReadMoreClick = {}, + ) + } + } +} + +@Preview +@Composable +private fun PreviewPlaceholderV1() { TangemThemePreview { PreviewShimmerContainer( actualContent = { - ContentPreview() + ContentPreviewV1() }, shimmerContent = { DescriptionPlaceholder() }, ) } +} + +@Preview +@Composable +private fun PreviewPlaceholderV2() { + CompositionLocalProvider(LocalRedesignEnabled provides true) { + TangemThemePreviewRedesign { + PreviewShimmerContainer( + actualContent = { + ContentPreviewV2() + }, + shimmerContent = { + DescriptionPlaceholder() + }, + ) + } + } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/tabs/TangemSegmentedPicker.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/tabs/TangemSegmentedPicker.kt index fc0a59340b..3f98cea45a 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ds/tabs/TangemSegmentedPicker.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ds/tabs/TangemSegmentedPicker.kt @@ -79,6 +79,7 @@ fun TangemSegmentedPicker( hasSeparator: Boolean = false, isFixed: Boolean = false, isAltSurface: Boolean = false, + minSegmentWidth: Dp = Dp.Unspecified, onClick: (TangemSegmentUM) -> Unit, ) { if (items.isEmpty() || items.size == 1) return @@ -123,6 +124,7 @@ fun TangemSegmentedPicker( item = item, index = index, isFixed = isFixed, + minSegmentWidth = minSegmentWidth, selectedIndex = selectedIndex, onClick = { onClick(item) }, modifier = Modifier @@ -176,9 +178,11 @@ private fun RowScope.Segment( selectedIndex: MutableState, onClick: () -> Unit, modifier: Modifier = Modifier, + minSegmentWidth: Dp = Dp.Unspecified, ) { Box( modifier = modifier + .defaultMinSize(minWidth = minSegmentWidth) .conditional(isFixed) { weight(1f) } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/InfoPoint.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/InfoPoint.kt index a3d85081b4..4baddf7c1a 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/InfoPoint.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/InfoPoint.kt @@ -2,17 +2,11 @@ package com.tangem.features.feed.ui.market.detailed.components import android.content.res.Configuration import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.requiredHeight -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.* import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector @@ -21,18 +15,30 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.tangem.core.ui.R +import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.components.SpacerW4 import com.tangem.core.ui.components.TextShimmer import com.tangem.core.ui.components.text.TooltipText import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.res.LocalRedesignEnabled import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.core.ui.utils.PreviewShimmerContainer import com.tangem.features.feed.ui.market.detailed.state.InfoPointUM @Composable internal fun InfoPoint(infoPointUM: InfoPointUM, modifier: Modifier = Modifier) { + if (LocalRedesignEnabled.current) { + InfoPointV2(infoPointUM, modifier) + } else { + InfoPointV1(infoPointUM, modifier) + } +} + +@Composable +private fun InfoPointV1(infoPointUM: InfoPointUM, modifier: Modifier = Modifier) { Column( modifier = modifier.padding(vertical = TangemTheme.dimens.spacing8), horizontalAlignment = Alignment.Start, @@ -81,8 +87,68 @@ internal fun InfoPoint(infoPointUM: InfoPointUM, modifier: Modifier = Modifier) } } +@Composable +private fun InfoPointV2(infoPointUM: InfoPointUM, modifier: Modifier = Modifier) { + Column( + modifier = modifier.padding(vertical = TangemTheme.dimens.spacing8), + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + Row { + Text( + text = infoPointUM.value, + style = TangemTheme.typography2.headingSemibold20, + color = TangemTheme.colors2.text.neutral.primary, + ) + if (infoPointUM.change != null) { + SpacerW4() + Icon( + modifier = Modifier + .size(TangemTheme.dimens2.x2) + .align(Alignment.CenterVertically), + imageVector = ImageVector.vectorResource( + id = when (infoPointUM.change) { + InfoPointUM.ChangeType.UP -> R.drawable.ic_arrow_up_8 + InfoPointUM.ChangeType.DOWN -> R.drawable.ic_arrow_down_8 + }, + ), + tint = when (infoPointUM.change) { + InfoPointUM.ChangeType.UP -> TangemTheme.colors2.markers.iconGreen + InfoPointUM.ChangeType.DOWN -> TangemTheme.colors2.markers.iconRed + }, + contentDescription = null, + ) + } + } + if (infoPointUM.onInfoClick != null) { + InformationTextBlock( + text = infoPointUM.title, + onInfoClick = infoPointUM.onInfoClick, + informationTextBlockIconPosition = InformationTextBlockIconPosition.START, + ) + } else { + Text( + text = infoPointUM.title.resolveReference(), + style = TangemTheme.typography2.captionSemibold12, + color = TangemTheme.colors2.text.neutral.tertiary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } + } +} + @Composable internal fun InfoPointShimmer(modifier: Modifier = Modifier, withTooltip: Boolean = false) { + if (LocalRedesignEnabled.current) { + InfoPointShimmerV2(modifier) + } else { + InfoPointShimmerV1(modifier, withTooltip) + } +} + +@Composable +private fun InfoPointShimmerV1(modifier: Modifier = Modifier, withTooltip: Boolean = false) { Column( modifier = modifier.padding(vertical = TangemTheme.dimens.spacing8), horizontalAlignment = Alignment.Start, @@ -115,10 +181,34 @@ internal fun InfoPointShimmer(modifier: Modifier = Modifier, withTooltip: Boolea } } +@Composable +private fun InfoPointShimmerV2(modifier: Modifier = Modifier) { + Column( + modifier = modifier.padding(vertical = TangemTheme.dimens2.x6), + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1), + ) { + RectangleShimmer( + modifier = Modifier + .fillMaxWidth() + .height(24.dp) + .padding(end = 10.dp), + radius = TangemTheme.dimens2.x25, + ) + + RectangleShimmer( + modifier = Modifier + .width(68.dp) + .height(16.dp), + radius = TangemTheme.dimens2.x25, + ) + } +} + @Preview @Preview("Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable -private fun ContentPreview() { +private fun ContentPreviewV1() { TangemThemePreview { Column( modifier = Modifier @@ -158,6 +248,53 @@ private fun ContentPreview() { } } +@Preview +@Preview("Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun ContentPreviewV2() { + CompositionLocalProvider( + LocalRedesignEnabled provides true, + ) { + TangemThemePreviewRedesign { + Column( + modifier = Modifier + .width(150.dp) + .background(TangemTheme.colors.background.tertiary), + ) { + InfoPoint( + infoPointUM = InfoPointUM( + title = stringReference("Market Cap"), + value = "$1,000,000,000", + ), + ) + InfoPoint( + infoPointUM = InfoPointUM( + title = stringReference("Market Cap"), + value = "$1,000,000,000", + onInfoClick = { }, + ), + ) + InfoPoint( + infoPointUM = InfoPointUM( + title = stringReference("Market Cap"), + value = "$1,000,000", + change = InfoPointUM.ChangeType.UP, + onInfoClick = { }, + ), + ) + InfoPoint( + infoPointUM = InfoPointUM( + title = stringReference("Market Cap"), + value = "$1,000,000", + change = InfoPointUM.ChangeType.DOWN, + onInfoClick = { }, + ), + ) + } + } + } +} + @Preview @Preview("Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable @@ -186,7 +323,7 @@ private fun PreviewShimmer() { } }, actualContent = { - ContentPreview() + ContentPreviewV1() }, ) } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/InsightsBlock.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/InsightsBlock.kt index 69a803858b..30fd5eda07 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/InsightsBlock.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/InsightsBlock.kt @@ -1,37 +1,33 @@ package com.tangem.features.feed.ui.market.detailed.components import android.content.res.Configuration -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.IntrinsicSize -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.* import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.tangem.core.ui.components.RectangleShimmer +import com.tangem.core.ui.components.SpacerW import com.tangem.core.ui.components.block.information.GridItems import com.tangem.core.ui.components.block.information.InformationBlock import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons import com.tangem.core.ui.components.text.TooltipText +import com.tangem.core.ui.ds.tabs.TangemSegmentUM +import com.tangem.core.ui.ds.tabs.TangemSegmentedPicker import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.LocalRedesignEnabled import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.core.ui.utils.PreviewShimmerContainer import com.tangem.domain.markets.PriceChangeInterval import com.tangem.features.feed.impl.R +import com.tangem.features.feed.ui.components.TokenMarketInformationBlock import com.tangem.features.feed.ui.market.detailed.getText import com.tangem.features.feed.ui.market.detailed.state.InfoPointUM import com.tangem.features.feed.ui.market.detailed.state.InsightsUM @@ -40,6 +36,15 @@ import kotlinx.collections.immutable.toImmutableList @Composable internal fun InsightsBlock(state: InsightsUM, modifier: Modifier = Modifier) { + if (LocalRedesignEnabled.current) { + InsightsBlockV2(state, modifier) + } else { + InsightsBlockV1(state, modifier) + } +} + +@Composable +private fun InsightsBlockV1(state: InsightsUM, modifier: Modifier = Modifier) { var currentInterval by remember { mutableStateOf(PriceChangeInterval.H24) } InformationBlock( @@ -104,8 +109,84 @@ internal fun InsightsBlock(state: InsightsUM, modifier: Modifier = Modifier) { ) } +@Composable +private fun InsightsBlockV2(state: InsightsUM, modifier: Modifier = Modifier) { + val segmentItems = persistentListOf( + TangemSegmentUM( + id = PriceChangeInterval.H24.name, + title = resourceReference(R.string.markets_token_details_insight_day_timeline), + ), + TangemSegmentUM( + id = PriceChangeInterval.WEEK.name, + title = resourceReference(R.string.markets_token_details_insight_week_timeline), + ), + TangemSegmentUM( + id = PriceChangeInterval.MONTH.name, + title = resourceReference(R.string.markets_token_details_insight_month_timeline), + ), + ) + var currentInterval by remember { mutableStateOf(segmentItems.first()) } + + TokenMarketInformationBlock( + modifier = modifier, + title = { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text( + text = stringResourceSafe(R.string.markets_token_details_insights), + style = TangemTheme.typography2.headingSemibold20, + color = TangemTheme.colors2.text.neutral.primary, + ) + TangemSegmentedPicker( + items = segmentItems, + initialSelectedItem = segmentItems.first(), + hasSeparator = true, + isFixed = false, + isAltSurface = true, + minSegmentWidth = 48.dp, + onClick = { segment -> + currentInterval = segment + state.onIntervalChanged(PriceChangeInterval.valueOf(segment.id)) + }, + ) + } + }, + content = { + val infoPoints = when (currentInterval.id) { + PriceChangeInterval.H24.name -> state.h24Info + PriceChangeInterval.WEEK.name -> state.weekInfo + PriceChangeInterval.MONTH.name -> state.monthInfo + else -> state.h24Info + } + + GridItems( + modifier = Modifier.padding(top = 24.dp), + items = infoPoints, + itemContent = { infoPointUM -> + InfoPoint( + modifier = Modifier.align(Alignment.CenterStart), + infoPointUM = infoPointUM, + ) + }, + ) + }, + ) +} + @Composable internal fun InsightsBlockPlaceholder(modifier: Modifier = Modifier) { + if (LocalRedesignEnabled.current) { + InsightsBlockPlaceholderV2(modifier) + } else { + InsightsBlockPlaceholderV1(modifier) + } +} + +@Composable +internal fun InsightsBlockPlaceholderV1(modifier: Modifier = Modifier) { val subtitle2dp = with(LocalDensity.current) { TangemTheme.typography.subtitle2.lineHeight.toDp() } val caption1dp = with(LocalDensity.current) { TangemTheme.typography.caption1.lineHeight.toDp() } val headerHeight = maxOf(subtitle2dp, caption1dp) + TangemTheme.dimens.spacing4 @@ -134,10 +215,47 @@ internal fun InsightsBlockPlaceholder(modifier: Modifier = Modifier) { ) } +@Composable +internal fun InsightsBlockPlaceholderV2(modifier: Modifier = Modifier) { + TokenMarketInformationBlock( + modifier = modifier, + title = { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + RectangleShimmer( + modifier = Modifier + .height(24.dp) + .width(120.dp), + radius = TangemTheme.dimens2.x25, + ) + + SpacerW(62.dp) + + RectangleShimmer( + modifier = Modifier + .height(36.dp) + .weight(1f), + radius = TangemTheme.dimens2.x25, + ) + } + }, + content = { + GridItems( + items = List(size = 4) { it }.toImmutableList(), + horizontalArragement = Arrangement.spacedBy(10.dp), + itemContent = { + InfoPointShimmer(modifier = Modifier.fillMaxWidth()) + }, + ) + }, + ) +} + @Preview @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable -private fun ContentPreview() { +private fun ContentPreviewV1() { TangemThemePreview { InsightsBlock( state = InsightsUM( @@ -205,11 +323,95 @@ private fun ContentPreview() { @Preview @Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable -private fun PreviewPlaceholder() { +private fun ContentPreviewV2() { + CompositionLocalProvider(LocalRedesignEnabled provides true) { + TangemThemePreviewRedesign { + InsightsBlock( + state = InsightsUM( + h24Info = persistentListOf( + InfoPointUM( + title = resourceReference(R.string.markets_token_details_experienced_buyers), + value = "1 000 000 000", + ), + InfoPointUM( + title = resourceReference(R.string.markets_token_details_buy_pressure), + value = "1 000 000 000", + ), + InfoPointUM( + title = resourceReference(R.string.markets_token_details_holders), + value = "1 000 000 000", + ), + InfoPointUM( + title = resourceReference(R.string.markets_token_details_liquidity), + value = "1 000 000 000", + ), + ), + weekInfo = persistentListOf( + InfoPointUM( + title = resourceReference(R.string.markets_token_details_experienced_buyers), + value = "1 000 000", + ), + InfoPointUM( + title = resourceReference(R.string.markets_token_details_buy_pressure), + value = "1 000 000", + ), + InfoPointUM( + title = resourceReference(R.string.markets_token_details_holders), + value = "1 000 000", + ), + InfoPointUM( + title = resourceReference(R.string.markets_token_details_liquidity), + value = "1 000 000", + ), + ), + monthInfo = persistentListOf( + InfoPointUM( + title = resourceReference(R.string.markets_token_details_experienced_buyers), + value = "1 000", + ), + InfoPointUM( + title = resourceReference(R.string.markets_token_details_buy_pressure), + value = "1 000", + ), + InfoPointUM( + title = resourceReference(R.string.markets_token_details_holders), + value = "1 000", + ), + InfoPointUM( + title = resourceReference(R.string.markets_token_details_liquidity), + value = "1 000", + ), + ), + onInfoClick = {}, + onIntervalChanged = {}, + ), + ) + } + } +} + +@Preview +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun PreviewPlaceholderV1() { TangemThemePreview { PreviewShimmerContainer( - actualContent = { ContentPreview() }, + actualContent = { ContentPreviewV1() }, shimmerContent = { InsightsBlockPlaceholder() }, ) } +} + +@Preview +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun PreviewPlaceholderV2() { + TangemThemePreviewRedesign { + CompositionLocalProvider(LocalRedesignEnabled provides true) { + PreviewShimmerContainer( + actualContent = { ContentPreviewV2() }, + shimmerContent = { InsightsBlockPlaceholder() }, + ) + } + } } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/ListedOnBlock.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/ListedOnBlock.kt index 3305077d9f..e7e041f634 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/ListedOnBlock.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/ListedOnBlock.kt @@ -156,17 +156,17 @@ internal fun ListedOnBlockPlaceholderV1(modifier: Modifier = Modifier) { @Composable internal fun ListedOnBlockPlaceholderV2(modifier: Modifier = Modifier) { TokenMarketInformationBlock( - modifier = modifier, + modifier = modifier.fillMaxWidth(), title = { Column(verticalArrangement = Arrangement.spacedBy(4.dp)) { TextShimmer( style = TangemTheme.typography2.headingSemibold20, - modifier = Modifier.fillMaxWidth(fraction = 0.5f), + modifier = Modifier.width(120.dp), radius = TangemTheme.dimens2.x25, ) TextShimmer( style = TangemTheme.typography2.captionSemibold13, - modifier = Modifier.fillMaxWidth(fraction = 0.5f), + modifier = Modifier.width(66.dp), radius = TangemTheme.dimens2.x25, ) } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/SecurityScoreBlock.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/SecurityScoreBlock.kt index ec4c86a487..70929c2ab5 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/SecurityScoreBlock.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/SecurityScoreBlock.kt @@ -129,7 +129,7 @@ private fun SecurityScoreBlockPlaceholderV2(modifier: Modifier = Modifier) { TangemRowContainer(modifier = modifier) { TextShimmer( modifier = Modifier - .width(74.dp) + .width(114.dp) .layoutId(layoutId = TangemRowLayoutId.START_TOP), style = TangemTheme.typography2.headingBold28, radius = TangemTheme.dimens2.x25, @@ -137,7 +137,8 @@ private fun SecurityScoreBlockPlaceholderV2(modifier: Modifier = Modifier) { TextShimmer( modifier = Modifier - .width(96.dp) + .width(74.dp) + .padding(top = 8.dp) .layoutId(layoutId = TangemRowLayoutId.START_BOTTOM), style = TangemTheme.typography2.captionSemibold12, radius = TangemTheme.dimens2.x25, @@ -145,7 +146,7 @@ private fun SecurityScoreBlockPlaceholderV2(modifier: Modifier = Modifier) { TextShimmer( modifier = Modifier - .width(120.dp) + .width(116.dp) .layoutId(layoutId = TangemRowLayoutId.END_TOP), style = TangemTheme.typography2.headingBold28, radius = TangemTheme.dimens2.x25, @@ -153,7 +154,8 @@ private fun SecurityScoreBlockPlaceholderV2(modifier: Modifier = Modifier) { TextShimmer( modifier = Modifier - .width(72.dp) + .width(96.dp) + .padding(top = 8.dp) .layoutId(layoutId = TangemRowLayoutId.END_BOTTOM), style = TangemTheme.typography2.captionSemibold12, radius = TangemTheme.dimens2.x25, diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/TokenMarketDetailsBody.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/TokenMarketDetailsBody.kt index 4abff26fa9..47d19a1751 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/TokenMarketDetailsBody.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/market/detailed/components/TokenMarketDetailsBody.kt @@ -26,6 +26,26 @@ internal fun LazyListScope.tokenMarketDetailsBody( state: MarketsTokenDetailsUM.Body, portfolioBlock: @Composable ((Modifier) -> Unit)?, relatedNews: RelatedNews, +) { + if (isRedesignEnabled) { + tokenMarketDetailsBodyV2( + state = state, + relatedNews = relatedNews, + ) + } else { + tokenMarketDetailsBodyV1( + state = state, + portfolioBlock = portfolioBlock, + relatedNews = relatedNews, + ) + } +} + +@Suppress("CanBeNonNullable") +private fun LazyListScope.tokenMarketDetailsBodyV1( + state: MarketsTokenDetailsUM.Body, + portfolioBlock: @Composable ((Modifier) -> Unit)?, + relatedNews: RelatedNews, ) { when (state) { MarketsTokenDetailsUM.Body.Loading -> { @@ -41,7 +61,7 @@ internal fun LazyListScope.tokenMarketDetailsBody( aboutCoinHeader() - loadingInfoBlocks(isRedesignEnabled) + loadingInfoBlocks(false) } is MarketsTokenDetailsUM.Body.Content -> { if (state.description != null) { @@ -60,9 +80,35 @@ internal fun LazyListScope.tokenMarketDetailsBody( aboutCoinHeader() - infoBlocksList( + infoBlocksListV1(state = state.infoBlocks) + } + is MarketsTokenDetailsUM.Body.Error -> { + error(state) + } + MarketsTokenDetailsUM.Body.Nothing -> { + // Do nothing + } + } +} + +@Suppress("CanBeNonNullable") +private fun LazyListScope.tokenMarketDetailsBodyV2(state: MarketsTokenDetailsUM.Body, relatedNews: RelatedNews) { + when (state) { + MarketsTokenDetailsUM.Body.Loading -> { + item("description-loading") { + DescriptionPlaceholder(modifier = Modifier.blockPaddings()) + } + + loadingInfoBlocks(true) + } + is MarketsTokenDetailsUM.Body.Content -> { + if (state.description != null) { + description(state.description) + } + + infoBlocksListV2( state = state.infoBlocks, - isRedesignEnabled = isRedesignEnabled, + relatedNews = relatedNews, ) } is MarketsTokenDetailsUM.Body.Error -> { @@ -116,7 +162,7 @@ private fun LazyListScope.description(description: MarketsTokenDetailsUM.Descrip } } -internal fun LazyListScope.infoBlocksList(state: MarketsTokenDetailsUM.InformationBlocks, isRedesignEnabled: Boolean) { +internal fun LazyListScope.infoBlocksListV1(state: MarketsTokenDetailsUM.InformationBlocks) { if (state.insights != null) { item("insights") { InsightsBlock( @@ -126,7 +172,7 @@ internal fun LazyListScope.infoBlocksList(state: MarketsTokenDetailsUM.Informati } } - if (state.securityScore != null && !isRedesignEnabled) { + if (state.securityScore != null) { item("securityScore") { SecurityScoreBlock( modifier = Modifier.blockPaddings(), @@ -160,7 +206,47 @@ internal fun LazyListScope.infoBlocksList(state: MarketsTokenDetailsUM.Informati ) } - if (state.securityScore != null && isRedesignEnabled) { + if (state.links != null) { + item("links") { + LinksBlock( + modifier = Modifier.blockPaddings(), + state = state.links, + ) + } + } +} + +internal fun LazyListScope.infoBlocksListV2(state: MarketsTokenDetailsUM.InformationBlocks, relatedNews: RelatedNews) { + if (state.metrics != null) { + item("metrics") { + MetricsBlock( + modifier = Modifier.blockPaddings(), + state = state.metrics, + ) + } + } + + if (state.insights != null) { + item("insights") { + InsightsBlock( + modifier = Modifier.blockPaddings(), + state = state.insights, + ) + } + } + + item(key = "listedOn") { + ListedOnBlock( + state = state.listedOn, + modifier = Modifier.blockPaddings(), + ) + } + + if (relatedNews.articles.isNotEmpty()) { + relatedNews(relatedNews) + } + + if (state.securityScore != null) { item("securityScore") { SecurityScoreBlock( modifier = Modifier.blockPaddings(), @@ -180,16 +266,22 @@ internal fun LazyListScope.infoBlocksList(state: MarketsTokenDetailsUM.Informati } private fun LazyListScope.loadingInfoBlocks(isRedesignEnabled: Boolean) { + if (isRedesignEnabled) { + loadingInfoBlocksV2() + } else { + loadingInfoBlocksV1() + } +} + +private fun LazyListScope.loadingInfoBlocksV1() { item("insights-loading") { InsightsBlockPlaceholder( modifier = Modifier.blockPaddings(), ) } - if (!isRedesignEnabled) { - item("securityScore-loading") { - SecurityScoreBlockPlaceholder(modifier = Modifier.blockPaddings()) - } + item("securityScore-loading") { + SecurityScoreBlockPlaceholder(modifier = Modifier.blockPaddings()) } item("metrics-loading") { @@ -204,10 +296,28 @@ private fun LazyListScope.loadingInfoBlocks(isRedesignEnabled: Boolean) { ListedOnBlockPlaceholder(modifier = Modifier.blockPaddings()) } - if (isRedesignEnabled) { - item("securityScore-loading") { - SecurityScoreBlockPlaceholder(modifier = Modifier.blockPaddings()) - } + item("links-loading") { + LinksBlockPlaceholder(modifier = Modifier.blockPaddings()) + } +} + +private fun LazyListScope.loadingInfoBlocksV2() { + item("metrics-loading") { + MetricsBlockPlaceholder(modifier = Modifier.blockPaddings()) + } + + item("insights-loading") { + InsightsBlockPlaceholder( + modifier = Modifier.blockPaddings(), + ) + } + + item(key = "listedOn-loading") { + ListedOnBlockPlaceholder(modifier = Modifier.blockPaddings()) + } + + item("securityScore-loading") { + SecurityScoreBlockPlaceholder(modifier = Modifier.blockPaddings()) } item("links-loading") {