diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index f40c33cb35..b03f0df753 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -851,6 +851,7 @@
Related tokens
Related news
Stay in the loop
+ Trending score
NFC is not available on your device
About NFT
NFT asset
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Fade.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Fade.kt
index d2e0edcec9..ea58b6415a 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/Fade.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/Fade.kt
@@ -81,6 +81,7 @@ fun BottomFadeWithBlur(backgroundColor: Color, modifier: Modifier = Modifier) {
),
),
),
+ backgroundColor = Color.Transparent,
),
) {
progressive =
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/chip/Chip.kt b/core/ui/src/main/java/com/tangem/core/ui/components/chip/Chip.kt
index b0c25bc839..fa8da4d095 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/chip/Chip.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/chip/Chip.kt
@@ -66,7 +66,7 @@ fun Chip(state: ChipUM, modifier: Modifier = Modifier) {
@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
-private fun ChipPreviewV() {
+private fun ChipPreview() {
TangemThemePreview {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/pager/PagerIndicator.kt b/core/ui/src/main/java/com/tangem/core/ui/components/pager/PagerIndicator.kt
index 39cdb7d9ea..1f87b768f3 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/pager/PagerIndicator.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/pager/PagerIndicator.kt
@@ -1,7 +1,6 @@
package com.tangem.core.ui.components.pager
import androidx.compose.animation.animateColorAsState
-import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
@@ -22,132 +21,147 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
+import com.tangem.core.ui.components.haze.hazeEffectTangem
+import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.launch
+import com.tangem.core.ui.res.TangemThemePreviewRedesign
+import dev.chrisbanes.haze.HazeStyle
import kotlin.math.abs
-import kotlin.math.min
import kotlin.math.roundToInt
-private const val ANIMATION_DURATION = 300
-private const val MAX_VISIBLE_DOTS = 5
+internal const val ANIMATION_DURATION = 300
+internal const val MAX_VISIBLE_DOTS = 5
+internal val SPACING = 4.dp
+internal val HINT_DOT_SIZE = DpSize(6.dp, 6.dp)
private const val MIN_HIDDEN_FOR_SMALL_DOT = 2
private const val MIN_DISTANCE_FOR_SMALL_DOT = 3
private const val MIN_DISTANCE_FOR_HINT_DOT = 2
-
-private val SPACING = 4.dp
private val BACKGROUND_SIZE = DpSize(92.dp, 32.dp)
-
private val CURRENT_DOT_SIZE = DpSize(16.dp, 8.dp)
private val NORMAL_DOT_SIZE = DpSize(8.dp, 8.dp)
-private val HINT_DOT_SIZE = DpSize(6.dp, 6.dp)
private val SMALL_DOT_SIZE = DpSize(4.dp, 4.dp)
-@Suppress("LongMethod", "CyclomaticComplexMethod")
@Composable
fun PagerIndicator(pagerState: PagerState, modifier: Modifier = Modifier) {
- val totalPages = pagerState.pageCount
- val currentIndex = pagerState.currentPage
+ if (LocalRedesignEnabled.current) {
+ PagerIndicatorV2(pagerState, modifier)
+ } else {
+ PagerIndicatorV1(pagerState, modifier)
+ }
+}
+@Composable
+private fun PagerIndicatorV1(pagerState: PagerState, modifier: Modifier = Modifier) {
+ val colors = PagerIndicatorColors(
+ active = TangemTheme.colors.control.key,
+ inactive = TangemTheme.colors.text.tertiary,
+ overlay = TangemTheme.colors.overlay.secondary,
+ )
+ PagerIndicatorContent(
+ pagerState = pagerState,
+ colors = colors,
+ modifier = modifier,
+ )
+}
+
+@Composable
+private fun PagerIndicatorV2(pagerState: PagerState, modifier: Modifier = Modifier) {
+ val colors = PagerIndicatorColors(
+ active = TangemTheme.colors2.graphic.neutral.primary,
+ inactive = TangemTheme.colors2.graphic.neutral.tertiary,
+ overlay = TangemTheme.colors2.tabs.backgroundSecondary.copy(alpha = .1f),
+ )
+ PagerIndicatorContent(
+ pagerState = pagerState,
+ colors = colors,
+ modifier = modifier,
+ boxModifier = Modifier.hazeEffectTangem(style = HazeStyle(blurRadius = 22.dp, tint = null)),
+ )
+}
+
+@Composable
+private fun rememberPagerIndicatorAnimationState(pagerState: PagerState): PagerIndicatorAnimationState {
+ val density = LocalDensity.current
+ return remember(pagerState.pageCount, density) {
+ PagerIndicatorAnimationState(pagerState.pageCount, pagerState.currentPage, density)
+ }
+}
+
+@Suppress("LongParameterList")
+private fun calculateDotAlpha(
+ isSliding: Boolean,
+ slideDirection: Int,
+ index: Int,
+ displayLower: Int,
+ displayUpper: Int,
+ fadeProgress: Float,
+): Float {
+ return when {
+ !isSliding -> 1f
+ slideDirection > 0 && index == displayLower -> 1f - fadeProgress
+ slideDirection > 0 && index == displayUpper - 1 -> fadeProgress
+ slideDirection < 0 && index == displayUpper - 1 -> 1f - fadeProgress
+ slideDirection < 0 && index == displayLower -> fadeProgress
+ else -> 1f
+ }
+}
+
+@Composable
+private fun PagerIndicatorContent(
+ pagerState: PagerState,
+ colors: PagerIndicatorColors,
+ modifier: Modifier = Modifier,
+ boxModifier: Modifier = Modifier,
+) {
+ val totalPages = pagerState.pageCount
if (totalPages == 0) return
- val indicatorColor = TangemTheme.colors.control.key
- val overlayColor = TangemTheme.colors.overlay.secondary
- val inactiveIndicatorColor = TangemTheme.colors.text.tertiary
-
- val density = LocalDensity.current
-
- val (targetLower, targetUpper) = getWindowBounds(totalPages, currentIndex)
-
- var displayLower by remember { mutableIntStateOf(targetLower) }
- var displayUpper by remember { mutableIntStateOf(targetUpper) }
- var prevTargetLower by remember { mutableIntStateOf(targetLower) }
-
- val slideOffset = remember { Animatable(0f) }
- var isSliding by remember { mutableStateOf(false) }
- var slideDirection by remember { mutableIntStateOf(0) }
- val fadeProgress = remember { Animatable(0f) }
- var fadeJob by remember { mutableStateOf(null) }
+ val animState = rememberPagerIndicatorAnimationState(pagerState)
+ val (targetLower, targetUpper) = getWindowBounds(pagerState.pageCount, pagerState.currentPage)
LaunchedEffect(targetLower) {
- if (targetLower != prevTargetLower && totalPages > MAX_VISIBLE_DOTS) {
- fadeJob?.cancel()
- slideOffset.stop()
- fadeProgress.stop()
-
- val dir = if (targetLower > prevTargetLower) 1 else -1
- val edgeDotSize = with(density) { (HINT_DOT_SIZE.width + SPACING).toPx() }
- val halfEdge = edgeDotSize / 2
-
- isSliding = true
- slideDirection = dir
- fadeProgress.snapTo(0f)
-
- if (dir > 0) {
- displayLower = prevTargetLower
- displayUpper = targetUpper
- slideOffset.snapTo(halfEdge)
- } else {
- displayLower = targetLower
- displayUpper = prevTargetLower + MAX_VISIBLE_DOTS
- slideOffset.snapTo(-halfEdge)
- }
-
- prevTargetLower = targetLower
-
- fadeJob = launch {
- fadeProgress.animateTo(1f, tween(ANIMATION_DURATION))
- }
- slideOffset.animateTo(
- if (dir > 0) -halfEdge else halfEdge,
- tween(ANIMATION_DURATION),
- )
-
- displayLower = targetLower
- displayUpper = targetUpper
- slideOffset.snapTo(0f)
- isSliding = false
- slideDirection = 0
- }
+ animState.onBoundsChange(this, targetLower, targetUpper)
}
- val visibleIndices = (displayLower until displayUpper).toList()
+
+ val visibleIndices = (animState.displayLower until animState.displayUpper).toList()
Box(
modifier = modifier
.width(BACKGROUND_SIZE.width)
.height(BACKGROUND_SIZE.height)
.background(
- color = overlayColor,
+ color = colors.overlay,
shape = CircleShape,
)
- .clip(CircleShape),
+ .clip(CircleShape)
+ .then(boxModifier),
contentAlignment = Alignment.Center,
) {
Row(
modifier = Modifier.offset {
- IntOffset(slideOffset.value.roundToInt(), 0)
+ IntOffset(animState.slideOffset.value.roundToInt(), 0)
},
horizontalArrangement = Arrangement.spacedBy(SPACING),
verticalAlignment = Alignment.CenterVertically,
) {
visibleIndices.forEach { index ->
- val dotAlpha = when {
- !isSliding -> 1f
- slideDirection > 0 && index == displayLower -> 1f - fadeProgress.value
- slideDirection > 0 && index == displayUpper - 1 -> fadeProgress.value
- slideDirection < 0 && index == displayUpper - 1 -> 1f - fadeProgress.value
- slideDirection < 0 && index == displayLower -> fadeProgress.value
- else -> 1f
- }
+ val dotAlpha = calculateDotAlpha(
+ isSliding = animState.isSliding,
+ slideDirection = animState.slideDirection,
+ index = index,
+ displayLower = animState.displayLower,
+ displayUpper = animState.displayUpper,
+ fadeProgress = animState.fadeProgress.value,
+ )
key(index) {
Dot(
index = index,
- currentIndex = currentIndex,
+ currentIndex = pagerState.currentPage,
totalPages = totalPages,
- activeColor = indicatorColor,
- inactiveColor = inactiveIndicatorColor,
+ activeColor = colors.active,
+ inactiveColor = colors.inactive,
modifier = Modifier.graphicsLayer { alpha = dotAlpha },
)
}
@@ -156,19 +170,6 @@ fun PagerIndicator(pagerState: PagerState, modifier: Modifier = Modifier) {
}
}
-private fun getWindowBounds(totalPages: Int, currentIndex: Int): Pair {
- if (totalPages <= MAX_VISIBLE_DOTS) {
- return 0 to totalPages
- }
- val lowerBound = when {
- currentIndex <= 1 -> 0
- currentIndex >= totalPages - 2 -> totalPages - MAX_VISIBLE_DOTS
- else -> currentIndex - 2
- }
- val upperBound = min(lowerBound + MAX_VISIBLE_DOTS, totalPages)
- return lowerBound to upperBound
-}
-
private fun getDotSize(index: Int, currentIndex: Int, totalPages: Int): DpSize {
if (index == currentIndex) {
return CURRENT_DOT_SIZE
@@ -180,6 +181,46 @@ private fun getDotSize(index: Int, currentIndex: Int, totalPages: Int): DpSize {
return params.calculateSize()
}
+@Composable
+private fun Dot(
+ index: Int,
+ currentIndex: Int,
+ totalPages: Int,
+ activeColor: Color,
+ inactiveColor: Color,
+ modifier: Modifier = Modifier,
+) {
+ val isActive = index == currentIndex
+ val size = getDotSize(index, currentIndex, totalPages)
+
+ val animSpec = tween(ANIMATION_DURATION)
+ val colorSpec = tween(ANIMATION_DURATION)
+
+ val animatedWidth by animateDpAsState(size.width, animSpec, label = "w$index")
+ val animatedHeight by animateDpAsState(size.height, animSpec, label = "h$index")
+ val animatedColor by animateColorAsState(
+ targetValue = if (isActive) activeColor else inactiveColor,
+ animationSpec = colorSpec,
+ label = "c$index",
+ )
+
+ val shape = RoundedCornerShape(animatedHeight / 2)
+
+ Box(
+ modifier = modifier
+ .width(animatedWidth)
+ .height(animatedHeight)
+ .background(animatedColor, shape),
+ )
+}
+
+@Immutable
+private data class PagerIndicatorColors(
+ val active: Color,
+ val inactive: Color,
+ val overlay: Color,
+)
+
private class DotSizeParams private constructor(
val posInWindow: Int,
val currentPosInWindow: Int,
@@ -248,43 +289,27 @@ private class DotSizeParams private constructor(
}
}
+@Preview(showBackground = true)
@Composable
-private fun Dot(
- index: Int,
- currentIndex: Int,
- totalPages: Int,
- activeColor: Color,
- inactiveColor: Color,
- modifier: Modifier = Modifier,
-) {
- val isActive = index == currentIndex
- val size = getDotSize(index, currentIndex, totalPages)
-
- val animSpec = tween(ANIMATION_DURATION)
- val colorSpec = tween(ANIMATION_DURATION)
-
- val animatedWidth by animateDpAsState(size.width, animSpec, label = "w$index")
- val animatedHeight by animateDpAsState(size.height, animSpec, label = "h$index")
- val animatedColor by animateColorAsState(
- targetValue = if (isActive) activeColor else inactiveColor,
- animationSpec = colorSpec,
- label = "c$index",
- )
-
- val shape = RoundedCornerShape(animatedHeight / 2)
-
- Box(
- modifier = modifier
- .width(animatedWidth)
- .height(animatedHeight)
- .background(animatedColor, shape),
- )
+private fun PagerIndicatorPreviewV1() {
+ TangemThemePreview {
+ Column(
+ Modifier
+ .background(TangemTheme.colors.background.primary)
+ .padding(20.dp),
+ verticalArrangement = Arrangement.spacedBy(10.dp),
+ ) {
+ listOf(0, 1, 2, 3, 4).forEach { page ->
+ PagerIndicator(rememberPagerState(page) { 5 })
+ }
+ }
+ }
}
@Preview(showBackground = true)
@Composable
-private fun PagerIndicatorPreview() {
- TangemThemePreview {
+private fun PagerIndicatorPreviewV2() {
+ TangemThemePreviewRedesign {
Column(
Modifier
.background(TangemTheme.colors.background.primary)
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/pager/PagerIndicatorAnimationState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/pager/PagerIndicatorAnimationState.kt
new file mode 100644
index 0000000000..1968908ace
--- /dev/null
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/pager/PagerIndicatorAnimationState.kt
@@ -0,0 +1,95 @@
+package com.tangem.core.ui.components.pager
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.tween
+import androidx.compose.runtime.*
+import androidx.compose.ui.unit.Density
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlin.math.min
+
+@Stable
+internal class PagerIndicatorAnimationState(
+ private val totalPages: Int,
+ initialCurrentPage: Int,
+ private val density: Density,
+) {
+ var displayLower by mutableIntStateOf(0)
+ private set
+ var displayUpper by mutableIntStateOf(0)
+ private set
+
+ val slideOffset = Animatable(0f)
+ var isSliding by mutableStateOf(false)
+ private set
+ var slideDirection by mutableIntStateOf(0)
+ private set
+ val fadeProgress = Animatable(0f)
+ private var fadeJob by mutableStateOf(null)
+
+ private var prevTargetLower by mutableIntStateOf(0)
+
+ init {
+ val (lower, upper) = getWindowBounds(totalPages, initialCurrentPage)
+ displayLower = lower
+ displayUpper = upper
+ prevTargetLower = lower
+ }
+
+ suspend fun onBoundsChange(scope: CoroutineScope, targetLower: Int, targetUpper: Int) {
+ if (targetLower == prevTargetLower || totalPages <= MAX_VISIBLE_DOTS) {
+ return
+ }
+
+ fadeJob?.cancel()
+ slideOffset.stop()
+ fadeProgress.stop()
+
+ val dir = if (targetLower > prevTargetLower) 1 else -1
+ val edgeDotSize = with(density) { (HINT_DOT_SIZE.width + SPACING).toPx() }
+ val halfEdge = edgeDotSize / 2
+
+ isSliding = true
+ slideDirection = dir
+ fadeProgress.snapTo(0f)
+
+ if (dir > 0) {
+ displayUpper = targetUpper
+ slideOffset.snapTo(halfEdge)
+ } else {
+ displayLower = targetLower
+ displayUpper = prevTargetLower + MAX_VISIBLE_DOTS
+ slideOffset.snapTo(-halfEdge)
+ }
+
+ prevTargetLower = targetLower
+
+ fadeJob = scope.launch {
+ fadeProgress.animateTo(1f, tween(ANIMATION_DURATION))
+ }
+ slideOffset.animateTo(
+ if (dir > 0) -halfEdge else halfEdge,
+ tween(ANIMATION_DURATION),
+ )
+
+ displayLower = targetLower
+ displayUpper = targetUpper
+ slideOffset.snapTo(0f)
+ isSliding = false
+ slideDirection = 0
+ }
+}
+
+internal fun getWindowBounds(totalPages: Int, currentIndex: Int): Pair {
+ if (totalPages <= MAX_VISIBLE_DOTS) {
+ return 0 to totalPages
+ }
+ val lowerBound = when {
+ currentIndex <= 1 -> 0
+ currentIndex >= totalPages - 2 -> totalPages - MAX_VISIBLE_DOTS
+ else -> currentIndex - 2
+ }
+ val upperBound = min(lowerBound + MAX_VISIBLE_DOTS, totalPages)
+ return lowerBound to upperBound
+}
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/tangem/core/ui/ds/button/TangemButtonInternal.kt b/core/ui/src/main/java/com/tangem/core/ui/ds/button/TangemButtonInternal.kt
index 53166d0416..b9426982dd 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/ds/button/TangemButtonInternal.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/ds/button/TangemButtonInternal.kt
@@ -80,7 +80,7 @@ internal fun TangemButtonInternal(
visible = iconRes != null && iconPosition == TangemButtonIconPosition.Start,
modifier = Modifier.size(size = size.toContentSize()),
) {
- val wrappedIconRes = remember(this) { requireNotNull(iconRes) }
+ val wrappedIconRes = remember(this, iconRes) { requireNotNull(iconRes) }
TangemButtonIcon(iconRes = wrappedIconRes, state = state, iconColor = contentColor, size = size)
}
diff --git a/core/ui/src/main/res/drawable/ic_like_20.xml b/core/ui/src/main/res/drawable/ic_like_20.xml
new file mode 100644
index 0000000000..b44b0f8818
--- /dev/null
+++ b/core/ui/src/main/res/drawable/ic_like_20.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/converter/ShortArticleToArticleConfigUMConverter.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/converter/ShortArticleToArticleConfigUMConverter.kt
index 6a6b8580cc..204c185109 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/converter/ShortArticleToArticleConfigUMConverter.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/converter/ShortArticleToArticleConfigUMConverter.kt
@@ -1,12 +1,12 @@
package com.tangem.features.feed.model.converter
-import com.tangem.features.feed.ui.feed.components.articles.ArticleConfigUM
import com.tangem.core.ui.components.label.entity.LabelLeadingContentUM
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.data.common.currency.getTokenIconUrlFromDefaultHost
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.news.ShortArticle
+import com.tangem.features.feed.ui.feed.components.articles.ArticleConfigUM
import com.tangem.features.feed.ui.utils.mapFormattedDate
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
@@ -16,7 +16,7 @@ import kotlinx.collections.immutable.toPersistentList
import kotlinx.collections.immutable.toPersistentSet
internal class ShortArticleToArticleConfigUMConverter(
- private val isTrending: Provider,
+ private val isTrending: Provider?,
) : Converter, ImmutableList> {
override fun convert(value: List): ImmutableList {
@@ -25,7 +25,7 @@ internal class ShortArticleToArticleConfigUMConverter(
id = shortArticle.id,
title = shortArticle.title,
score = shortArticle.score,
- isTrending = isTrending(),
+ isTrending = isTrending?.invoke() ?: shortArticle.isTrending,
tags = buildArticleTags(shortArticle),
createdAt = mapFormattedDate(shortArticle.createdAt),
isViewed = shortArticle.viewed,
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/details/converter/NewsDetailsConverter.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/details/converter/NewsDetailsConverter.kt
index ee57aec32a..a966da27f2 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/details/converter/NewsDetailsConverter.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/details/converter/NewsDetailsConverter.kt
@@ -40,6 +40,7 @@ internal class NewsDetailsConverter(
newsUrl = value.newsUrl,
relatedTokens = value.relatedTokens.toImmutableList(),
isLiked = value.isLiked,
+ isTrending = value.isTrending,
)
}
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/list/statemanager/NewsListBatchFlowManager.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/list/statemanager/NewsListBatchFlowManager.kt
index eb6341a0c7..ca5f6d7382 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/list/statemanager/NewsListBatchFlowManager.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/news/list/statemanager/NewsListBatchFlowManager.kt
@@ -1,12 +1,12 @@
package com.tangem.features.feed.model.news.list.statemanager
-import com.tangem.features.feed.ui.feed.components.articles.ArticleConfigUM
import com.tangem.domain.models.news.ShortArticle
import com.tangem.domain.news.model.NewsListBatchingContext
import com.tangem.domain.news.model.NewsListConfig
import com.tangem.domain.news.usecase.GetNewsListBatchFlowUseCase
import com.tangem.features.feed.model.converter.ShortArticleToArticleConfigUMConverter
import com.tangem.features.feed.model.converter.distinctBatchesContent
+import com.tangem.features.feed.ui.feed.components.articles.ArticleConfigUM
import com.tangem.pagination.Batch
import com.tangem.pagination.BatchAction
import com.tangem.pagination.PaginationStatus
@@ -30,7 +30,7 @@ internal open class NewsListBatchFlowManager(
) {
private val actionsFlow = MutableSharedFlow>()
private val converter by lazy {
- ShortArticleToArticleConfigUMConverter(isTrending = Provider { false })
+ ShortArticleToArticleConfigUMConverter(null)
}
private val batchFlow = getNewsListBatchFlowUseCase(
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/NewsSlider.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/NewsSlider.kt
index 1bc690a184..87a5053d61 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/NewsSlider.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/NewsSlider.kt
@@ -10,12 +10,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onFirstVisible
import androidx.compose.ui.unit.dp
-import com.tangem.core.ui.components.HorizontalFadeWithBlur
import com.tangem.core.ui.components.block.TangemBlockCardColors
-import com.tangem.core.ui.components.haze.hazeSourceTangem
-import com.tangem.core.ui.extensions.conditionalCompose
import com.tangem.core.ui.res.LocalMainBottomSheetColor
-import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.feed.ui.feed.components.articles.ArticleCard
import com.tangem.features.feed.ui.feed.components.articles.ShowMoreArticlesCard
@@ -25,72 +21,50 @@ import com.tangem.features.feed.ui.feed.state.NewsSliderConfig
@Composable
internal fun NewsSlider(newsSliderConfig: NewsSliderConfig) {
val background = LocalMainBottomSheetColor.current.value
- val isRedesignEnabled = LocalRedesignEnabled.current
- Box(
- modifier = Modifier.fillMaxWidth(),
+ LazyRow(
+ modifier = Modifier.background(color = background),
+ verticalAlignment = Alignment.CenterVertically,
+ contentPadding = PaddingValues(horizontal = 16.dp),
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
+ state = rememberLazyListState(),
) {
- LazyRow(
- modifier = Modifier
- .conditionalCompose(
- condition = isRedesignEnabled,
- modifier = {
- hazeSourceTangem(zIndex = -1f)
- },
+ itemsIndexed(
+ items = newsSliderConfig.content,
+ key = { index, _ -> index },
+ contentType = { _, _ -> "article" },
+ ) { index, article ->
+ val articleModifier = if (index == FOURTH_ITEM_INDEX) {
+ Modifier.onFirstVisible(
+ minFractionVisible = 0.5f,
+ callback = newsSliderConfig.callbacks.onSliderScroll,
)
- .background(color = background),
- verticalAlignment = Alignment.CenterVertically,
- contentPadding = PaddingValues(horizontal = 16.dp),
- horizontalArrangement = Arrangement.spacedBy(12.dp),
- state = rememberLazyListState(),
- ) {
- itemsIndexed(
- items = newsSliderConfig.content,
- key = { index, _ -> index },
- contentType = { _, _ -> "article" },
- ) { index, article ->
- val articleModifier = if (index == FOURTH_ITEM_INDEX) {
- Modifier.onFirstVisible(
- minFractionVisible = 0.5f,
- callback = newsSliderConfig.callbacks.onSliderScroll,
- )
- } else {
- Modifier
- }
- ArticleCard(
- articleConfigUM = article,
- onArticleClick = { newsSliderConfig.callbacks.onArticleClick(article.id) },
- modifier = articleModifier
+ } else {
+ Modifier
+ }
+ ArticleCard(
+ articleConfigUM = article,
+ onArticleClick = { newsSliderConfig.callbacks.onArticleClick(article.id) },
+ modifier = articleModifier
+ .width(228.dp)
+ .heightIn(min = 172.dp)
+ .fillMaxHeight(),
+ colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
+ )
+ }
+
+ if (newsSliderConfig.shouldShowSeeAllNewsItem) {
+ item(contentType = "show_more") {
+ ShowMoreArticlesCard(
+ modifier = Modifier
.width(228.dp)
.heightIn(min = 172.dp)
- .fillMaxHeight(),
- colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
+ .onFirstVisible(
+ minFractionVisible = 0.5f,
+ callback = newsSliderConfig.callbacks.onSliderEndReached,
+ ),
+ onClick = newsSliderConfig.callbacks.onOpenAllNews,
)
}
-
- if (newsSliderConfig.shouldShowSeeAllNewsItem) {
- item(contentType = "show_more") {
- ShowMoreArticlesCard(
- modifier = Modifier
- .width(228.dp)
- .heightIn(min = 172.dp)
- .onFirstVisible(
- minFractionVisible = 0.5f,
- callback = newsSliderConfig.callbacks.onSliderEndReached,
- ),
- onClick = newsSliderConfig.callbacks.onOpenAllNews,
- )
- }
- }
- }
- if (isRedesignEnabled) {
- HorizontalFadeWithBlur(
- modifier = Modifier
- .align(Alignment.TopEnd)
- .heightIn(min = 172.dp)
- .fillMaxHeight()
- .width(100.dp),
- backgroundColor = background,
- )
}
}
}
\ No newline at end of file
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/articles/ArticleHeader.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/articles/ArticleHeader.kt
index fcb608cf65..fadece2c4e 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/articles/ArticleHeader.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/components/articles/ArticleHeader.kt
@@ -1,23 +1,69 @@
package com.tangem.features.feed.ui.feed.components.articles
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.ExperimentalLayoutApi
-import androidx.compose.foundation.layout.FlowRow
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.*
+import androidx.compose.material3.Icon
import androidx.compose.material3.Text
+import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.vectorResource
+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.SpacerW
import com.tangem.core.ui.components.label.Label
+import com.tangem.core.ui.components.label.entity.LabelLeadingContentUM
import com.tangem.core.ui.components.label.entity.LabelUM
+import com.tangem.core.ui.ds.badge.TangemBadge
+import com.tangem.core.ui.ds.badge.TangemBadgeColor
+import com.tangem.core.ui.ds.badge.TangemBadgeIconPosition
+import com.tangem.core.ui.ds.badge.TangemBadgeShape
+import com.tangem.core.ui.ds.badge.TangemBadgeSize
+import com.tangem.core.ui.ds.badge.TangemBadgeType
+import com.tangem.core.ui.ds.image.TangemIconUM
+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 kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.persistentListOf
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun ArticleHeader(
+ isTrending: Boolean,
+ title: String,
+ createdAt: String,
+ score: Float,
+ tags: ImmutableList,
+ modifier: Modifier = Modifier,
+) {
+ if (LocalRedesignEnabled.current) {
+ ArticleHeaderV2(
+ isTrending = isTrending,
+ title = title,
+ createdAt = createdAt,
+ score = score,
+ tags = tags,
+ modifier = modifier,
+ )
+ } else {
+ ArticleHeaderV1(
+ title = title,
+ createdAt = createdAt,
+ score = score,
+ tags = tags,
+ modifier = modifier,
+ )
+ }
+}
+
+@OptIn(ExperimentalLayoutApi::class)
+@Composable
+private fun ArticleHeaderV1(
title: String,
createdAt: String,
score: Float,
@@ -52,4 +98,161 @@ fun ArticleHeader(
}
}
}
+}
+
+@OptIn(ExperimentalLayoutApi::class)
+@Composable
+private fun ArticleHeaderV2(
+ isTrending: Boolean,
+ title: String,
+ createdAt: String,
+ score: Float,
+ tags: ImmutableList,
+ modifier: Modifier = Modifier,
+) {
+ Column(modifier = modifier) {
+ Row(
+ modifier = Modifier
+ .heightIn(min = 66.dp)
+ .padding(top = 16.dp),
+ verticalAlignment = Alignment.Bottom,
+ ) {
+ DateBlock(
+ modifier = Modifier.weight(1f),
+ createdAt = createdAt,
+ )
+ SpacerW(30.dp)
+ VerticalDivider(
+ modifier = Modifier
+ .height(46.dp)
+ .padding(bottom = 4.dp),
+ color = TangemTheme.colors2.border.neutral.primary,
+ )
+ SpacerW(30.dp)
+ ScoreBlock(
+ modifier = Modifier.weight(1f),
+ score = score,
+ isTrending = isTrending,
+ )
+ }
+
+ Text(
+ modifier = Modifier.padding(vertical = 36.dp),
+ text = title,
+ style = TangemTheme.typography2.headingBold34,
+ color = TangemTheme.colors2.text.neutral.primary,
+ )
+
+ if (tags.isNotEmpty()) {
+ Spacer(modifier = Modifier.height(20.dp))
+ FlowRow(
+ horizontalArrangement = Arrangement.spacedBy(4.dp),
+ verticalArrangement = Arrangement.spacedBy(4.dp),
+ ) {
+ tags.forEach { tag ->
+ TangemBadge(
+ text = tag.text,
+ tangemIconUM = when (val content = tag.leadingContent) {
+ LabelLeadingContentUM.None -> null
+ is LabelLeadingContentUM.Token -> TangemIconUM.Url(content.iconUrl)
+ },
+ shape = TangemBadgeShape.Rounded,
+ size = TangemBadgeSize.X9,
+ type = TangemBadgeType.Tinted,
+ color = TangemBadgeColor.Gray,
+ iconPosition = when (tag.leadingContent) {
+ LabelLeadingContentUM.None -> TangemBadgeIconPosition.None
+ is LabelLeadingContentUM.Token -> TangemBadgeIconPosition.Start
+ },
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun ScoreBlock(score: Float, isTrending: Boolean, modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.spacedBy(10.dp),
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(10.dp),
+ ) {
+ Icon(
+ modifier = Modifier.size(20.dp),
+ imageVector = ImageVector.vectorResource(R.drawable.ic_wrapped_circle_star_16),
+ tint = if (isTrending) {
+ TangemTheme.colors2.fill.status.attention
+ } else {
+ TangemTheme.colors2.graphic.neutral.primary
+ },
+ contentDescription = null,
+ )
+ Text(
+ text = score.toString(),
+ style = TangemTheme.typography2.bodyRegular16,
+ color = if (isTrending) {
+ TangemTheme.colors2.text.status.attention
+ } else {
+ TangemTheme.colors2.text.neutral.primary
+ },
+ )
+ }
+ Text(
+ text = stringResourceSafe(R.string.news_trending_score),
+ style = TangemTheme.typography2.captionSemibold13,
+ color = TangemTheme.colors2.text.neutral.tertiary,
+ )
+ }
+}
+
+@Composable
+private fun DateBlock(createdAt: String, modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.spacedBy(10.dp),
+ ) {
+ Icon(
+ modifier = Modifier.size(20.dp),
+ imageVector = ImageVector.vectorResource(R.drawable.ic_calendar_20),
+ tint = TangemTheme.colors2.fill.neutral.primary,
+ contentDescription = null,
+ )
+ Text(
+ text = createdAt,
+ style = TangemTheme.typography2.captionSemibold13,
+ color = TangemTheme.colors2.text.neutral.tertiary,
+ )
+ }
+}
+
+@Preview(showBackground = true, widthDp = 360)
+@Composable
+private fun ArticleHeaderPreviewV1() {
+ TangemThemePreview {
+ ArticleHeader(
+ title = "Something going good!",
+ createdAt = "1 hour ago",
+ score = 5.5f,
+ tags = persistentListOf(),
+ isTrending = true,
+ )
+ }
+}
+
+@Preview(showBackground = true, widthDp = 360)
+@Composable
+private fun ArticleHeaderPreviewV2() {
+ TangemThemePreviewRedesign {
+ ArticleHeader(
+ title = "Something going good!",
+ createdAt = "1 hour ago",
+ score = 5.5f,
+ tags = persistentListOf(),
+ isTrending = true,
+ )
+ }
}
\ No newline at end of file
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/NewsDetailsContent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/NewsDetailsContent.kt
index fd67175055..518b25e6a6 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/NewsDetailsContent.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/NewsDetailsContent.kt
@@ -3,53 +3,25 @@ package com.tangem.features.feed.ui.news.details
import android.content.res.Configuration
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.background
-import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.LazyRow
-import androidx.compose.foundation.lazy.items
-import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material3.Icon
-import androidx.compose.material3.Text
-import androidx.compose.material3.VerticalDivider
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.snapshotFlow
+import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.hapticfeedback.HapticFeedbackType
-import androidx.compose.ui.layout.ContentScale
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalHapticFeedback
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import coil.compose.SubcomposeAsyncImage
-import coil.request.CachePolicy
-import coil.request.ImageRequest
-import com.tangem.features.feed.ui.feed.components.articles.ArticleHeader
-import com.tangem.core.ui.R
-import com.tangem.core.ui.components.*
-import com.tangem.core.ui.components.buttons.common.TangemButtonSize
+import com.tangem.core.ui.components.UnableToLoadData
+import com.tangem.core.ui.components.haze.hazeSourceTangem
import com.tangem.core.ui.components.pager.PagerIndicator
-import com.tangem.core.ui.extensions.resolveReference
-import com.tangem.core.ui.extensions.stringResourceSafe
-import com.tangem.core.ui.res.LocalMainBottomSheetColor
-import com.tangem.core.ui.res.TangemTheme
-import com.tangem.core.ui.res.TangemThemePreview
+import com.tangem.core.ui.extensions.conditionalCompose
+import com.tangem.core.ui.res.*
+import com.tangem.features.feed.ui.news.details.components.ArticleDetail
import com.tangem.features.feed.ui.news.details.components.NewsDetailsPlaceholder
-import com.tangem.features.feed.ui.news.details.components.RelatedTokensBlock
-import com.tangem.features.feed.ui.news.details.state.*
+import com.tangem.features.feed.ui.news.details.state.ArticlesStateUM
+import com.tangem.features.feed.ui.news.details.state.MockArticlesFactory
+import com.tangem.features.feed.ui.news.details.state.NewsDetailsUM
@Composable
internal fun NewsDetailsContent(state: NewsDetailsUM, modifier: Modifier = Modifier) {
@@ -86,6 +58,7 @@ internal fun NewsDetailsContent(state: NewsDetailsUM, modifier: Modifier = Modif
@Composable
private fun Content(state: NewsDetailsUM, background: Color) {
+ val isRedesignEnabled = LocalRedesignEnabled.current
val pagerState = rememberPagerState(
initialPage = state.selectedArticleIndex,
pageCount = { state.articles.size },
@@ -109,6 +82,12 @@ private fun Content(state: NewsDetailsUM, background: Color) {
Column(
modifier = Modifier
.fillMaxSize()
+ .conditionalCompose(
+ condition = isRedesignEnabled,
+ modifier = {
+ hazeSourceTangem(zIndex = 1f)
+ },
+ )
.background(background),
) {
Box(modifier = Modifier.fillMaxSize()) {
@@ -138,242 +117,6 @@ private fun Content(state: NewsDetailsUM, background: Color) {
}
}
-@Suppress("LongMethod")
-@Composable
-private fun ArticleDetail(
- article: ArticleUM,
- onLikeClick: () -> Unit,
- relatedTokensUM: RelatedTokensUM,
- modifier: Modifier = Modifier,
-) {
- val hapticFeedback = LocalHapticFeedback.current
- val density = LocalDensity.current
- val background = LocalMainBottomSheetColor.current.value
- val pagerHeight = 32.dp
- val contentPadding = pagerHeight + 56.dp + with(density) {
- WindowInsets.navigationBars.getBottom(this).div(this.density)
- }.dp
-
- Box(modifier = modifier) {
- LazyColumn(
- modifier = Modifier.fillMaxSize(),
- contentPadding = PaddingValues(bottom = contentPadding),
- ) {
- item("content") {
- ArticleHeader(
- title = article.title,
- createdAt = article.createdAt.resolveReference(),
- score = article.score,
- tags = article.tags,
- modifier = Modifier
- .padding(top = 16.dp)
- .padding(horizontal = 16.dp),
- )
-
- if (article.shortContent.isNotEmpty()) {
- QuickRecap(
- content = article.shortContent,
- modifier = Modifier
- .padding(top = 32.dp)
- .padding(horizontal = 16.dp),
- )
- }
-
- Text(
- text = article.content,
- style = TangemTheme.typography.body1,
- color = TangemTheme.colors.text.primary1,
- modifier = Modifier
- .padding(top = 16.dp)
- .padding(horizontal = 16.dp),
- )
-
- SpacerH(24.dp)
-
- SecondaryButtonIconStart(
- modifier = Modifier.padding(horizontal = 16.dp),
- iconResId = if (article.isLiked) {
- R.drawable.ic_heart_filled_20
- } else {
- R.drawable.ic_heart_20
- },
- iconTint = Color.Unspecified,
- text = stringResourceSafe(R.string.news_like),
- size = TangemButtonSize.RoundedAction,
- onClick = {
- hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
- onLikeClick()
- },
- )
-
- RelatedTokensBlock(
- relatedTokensUM = relatedTokensUM,
- onItemClick = when (relatedTokensUM) {
- is RelatedTokensUM.Content -> relatedTokensUM.onTokenClick
- else -> null
- },
- modifier = Modifier.padding(horizontal = 16.dp),
- )
-
- if (article.relatedArticles.isNotEmpty()) {
- SpacerH(24.dp)
- Row(
- modifier = Modifier.padding(horizontal = 16.dp),
- horizontalArrangement = Arrangement.spacedBy(8.dp),
- ) {
- Text(
- text = stringResourceSafe(R.string.news_sources),
- style = TangemTheme.typography.h3,
- color = TangemTheme.colors.text.primary1,
- )
- Text(
- text = "${article.relatedArticles.size}",
- style = TangemTheme.typography.h3,
- color = TangemTheme.colors.text.tertiary,
- )
- }
- }
- }
-
- if (article.relatedArticles.isNotEmpty()) {
- item("relatedArticles") {
- LazyRow(
- modifier = Modifier.padding(vertical = 12.dp),
- state = rememberLazyListState(),
- contentPadding = PaddingValues(horizontal = 16.dp),
- horizontalArrangement = Arrangement.spacedBy(12.dp),
- ) {
- items(
- items = article.relatedArticles,
- key = RelatedArticleUM::id,
- ) { article ->
- RelatedNewsItem(
- relatedArticle = article,
- modifier = Modifier.fillParentMaxHeight(),
- )
- }
- }
- }
- }
- }
- BottomFade(
- modifier = Modifier
- .align(Alignment.BottomCenter),
- backgroundColor = background,
- )
- }
-}
-
-@Composable
-private fun QuickRecap(content: String, modifier: Modifier = Modifier) {
- Box(
- modifier = modifier.height(IntrinsicSize.Min),
- ) {
- VerticalDivider(
- modifier = Modifier.fillMaxHeight(),
- thickness = 2.dp,
- color = TangemTheme.colors.stroke.primary,
- )
- Column(
- modifier = Modifier
- .padding(start = 16.dp),
- ) {
- Row(
- verticalAlignment = Alignment.CenterVertically,
- ) {
- Icon(
- painter = painterResource(id = R.drawable.ic_quick_recap_16),
- contentDescription = null,
- tint = TangemTheme.colors.icon.accent,
- modifier = Modifier.size(20.dp),
- )
- Spacer(modifier = Modifier.width(8.dp))
- Text(
- text = stringResourceSafe(R.string.news_quick_recap),
- style = TangemTheme.typography.subtitle2,
- color = TangemTheme.colors.text.accent,
- )
- }
- Spacer(modifier = Modifier.height(12.dp))
- Text(
- text = content,
- style = TangemTheme.typography.body1,
- color = TangemTheme.colors.text.primary1,
- )
- }
- }
-}
-
-@Composable
-private fun RelatedNewsItem(relatedArticle: RelatedArticleUM, modifier: Modifier = Modifier) {
- Column(
- modifier = modifier
- .sizeIn(maxWidth = 256.dp, minHeight = 132.dp)
- .background(color = TangemTheme.colors.background.action, shape = RoundedCornerShape(12.dp))
- .clickable(onClick = relatedArticle.onClick)
- .padding(12.dp),
- ) {
- Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
- Column(modifier = Modifier.weight(1f)) {
- Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(bottom = 4.dp)) {
- Icon(
- painter = painterResource(id = R.drawable.ic_explore_16),
- contentDescription = null,
- tint = TangemTheme.colors.icon.informative,
- modifier = Modifier.size(16.dp),
- )
- SpacerW(4.dp)
- Text(
- text = relatedArticle.media.name,
- style = TangemTheme.typography.caption1,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- color = TangemTheme.colors.text.tertiary,
- )
- }
- if (relatedArticle.title.isNotEmpty()) {
- SpacerH(4.dp)
- Text(
- text = relatedArticle.title,
- style = TangemTheme.typography.subtitle2,
- color = TangemTheme.colors.text.primary1,
- maxLines = 3,
- overflow = TextOverflow.Ellipsis,
- )
- }
- }
- if (relatedArticle.imageUrl != null) {
- SubcomposeAsyncImage(
- modifier = Modifier
- .size(40.dp)
- .clip(RoundedCornerShape(4.dp)),
- contentScale = ContentScale.Crop,
- model = ImageRequest.Builder(context = LocalContext.current)
- .data(relatedArticle.imageUrl)
- .crossfade(enable = false)
- .allowHardware(true)
- .memoryCachePolicy(CachePolicy.DISABLED)
- .build(),
- loading = {
- RectangleShimmer(
- modifier = Modifier.size(40.dp),
- radius = 4.dp,
- )
- },
- error = {},
- contentDescription = relatedArticle.media.name,
- )
- }
- }
- SpacerHMax()
- Text(
- text = relatedArticle.publishedAt.resolveReference(),
- style = TangemTheme.typography.caption2,
- color = TangemTheme.colors.text.tertiary,
- )
- }
-}
-
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
@@ -396,4 +139,28 @@ private fun PreviewNewsDetailsContent() {
)
}
}
+}
+
+@Preview(showBackground = true, widthDp = 360)
+@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
+@Composable
+private fun PreviewNewsDetailsContentV2() {
+ TangemThemePreviewRedesign {
+ val background = TangemTheme.colors.background.tertiary
+ CompositionLocalProvider(
+ LocalMainBottomSheetColor provides remember { mutableStateOf(background) },
+ ) {
+ NewsDetailsContent(
+ state = NewsDetailsUM(
+ articlesStateUM = ArticlesStateUM.Content,
+ articles = MockArticlesFactory.createMockArticles(),
+ selectedArticleIndex = 0,
+ onShareClick = {},
+ onLikeClick = {},
+ onBackClick = {},
+ onArticleIndexChanged = {},
+ ),
+ )
+ }
+ }
}
\ No newline at end of file
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/components/ArticleDetail.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/components/ArticleDetail.kt
new file mode 100644
index 0000000000..12a7153f8d
--- /dev/null
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/components/ArticleDetail.kt
@@ -0,0 +1,324 @@
+package com.tangem.features.feed.ui.news.details.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.hapticfeedback.HapticFeedbackType
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalHapticFeedback
+import androidx.compose.ui.unit.dp
+import com.tangem.core.ui.R
+import com.tangem.core.ui.components.BottomFade
+import com.tangem.core.ui.components.BottomFadeWithBlur
+import com.tangem.core.ui.components.SecondaryButtonIconStart
+import com.tangem.core.ui.components.SpacerH
+import com.tangem.core.ui.components.buttons.common.TangemButtonSize
+import com.tangem.core.ui.components.haze.hazeSourceTangem
+import com.tangem.core.ui.ds.button.SecondaryTangemButton
+import com.tangem.core.ui.ds.button.TangemButtonShape
+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.LocalMainBottomSheetColor
+import com.tangem.core.ui.res.LocalRedesignEnabled
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.features.feed.ui.feed.components.articles.ArticleHeader
+import com.tangem.features.feed.ui.news.details.state.ArticleUM
+import com.tangem.features.feed.ui.news.details.state.RelatedArticleUM
+import com.tangem.features.feed.ui.news.details.state.RelatedTokensUM
+
+@Composable
+internal fun ArticleDetail(
+ article: ArticleUM,
+ onLikeClick: () -> Unit,
+ relatedTokensUM: RelatedTokensUM,
+ modifier: Modifier = Modifier,
+) {
+ if (LocalRedesignEnabled.current) {
+ ArticleDetailV2(
+ article = article,
+ onLikeClick = onLikeClick,
+ relatedTokensUM = relatedTokensUM,
+ modifier = modifier,
+ )
+ } else {
+ ArticleDetailV1(
+ article = article,
+ onLikeClick = onLikeClick,
+ relatedTokensUM = relatedTokensUM,
+ modifier = modifier,
+ )
+ }
+}
+
+@Suppress("LongMethod")
+@Composable
+private fun ArticleDetailV1(
+ article: ArticleUM,
+ onLikeClick: () -> Unit,
+ relatedTokensUM: RelatedTokensUM,
+ modifier: Modifier = Modifier,
+) {
+ val hapticFeedback = LocalHapticFeedback.current
+ val density = LocalDensity.current
+ val background = LocalMainBottomSheetColor.current.value
+ val pagerHeight = 32.dp
+ val contentPadding = pagerHeight + 56.dp + with(density) {
+ WindowInsets.navigationBars.getBottom(this).div(this.density)
+ }.dp
+
+ Box(modifier = modifier) {
+ LazyColumn(
+ modifier = Modifier.fillMaxSize(),
+ contentPadding = PaddingValues(bottom = contentPadding),
+ ) {
+ item("content") {
+ ArticleHeader(
+ title = article.title,
+ createdAt = article.createdAt.resolveReference(),
+ score = article.score,
+ tags = article.tags,
+ isTrending = article.isTrending,
+ modifier = Modifier
+ .padding(top = 16.dp)
+ .padding(horizontal = 16.dp),
+ )
+
+ if (article.shortContent.isNotEmpty()) {
+ QuickRecap(
+ content = article.shortContent,
+ modifier = Modifier
+ .padding(top = 32.dp)
+ .padding(horizontal = 16.dp),
+ )
+ }
+
+ Text(
+ text = article.content,
+ style = TangemTheme.typography.body1,
+ color = TangemTheme.colors.text.primary1,
+ modifier = Modifier
+ .padding(top = 16.dp)
+ .padding(horizontal = 16.dp),
+ )
+
+ SpacerH(24.dp)
+
+ SecondaryButtonIconStart(
+ modifier = Modifier.padding(horizontal = 16.dp),
+ iconResId = if (article.isLiked) {
+ R.drawable.ic_heart_filled_20
+ } else {
+ R.drawable.ic_heart_20
+ },
+ iconTint = Color.Unspecified,
+ text = stringResourceSafe(R.string.news_like),
+ size = TangemButtonSize.RoundedAction,
+ onClick = {
+ hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
+ onLikeClick()
+ },
+ )
+
+ RelatedTokensBlock(
+ relatedTokensUM = relatedTokensUM,
+ onItemClick = when (relatedTokensUM) {
+ is RelatedTokensUM.Content -> relatedTokensUM.onTokenClick
+ else -> null
+ },
+ modifier = Modifier.padding(horizontal = 16.dp),
+ )
+
+ if (article.relatedArticles.isNotEmpty()) {
+ SpacerH(24.dp)
+ Row(
+ modifier = Modifier.padding(horizontal = 16.dp),
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ ) {
+ Text(
+ text = stringResourceSafe(R.string.news_sources),
+ style = TangemTheme.typography.h3,
+ color = TangemTheme.colors.text.primary1,
+ )
+ Text(
+ text = "${article.relatedArticles.size}",
+ style = TangemTheme.typography.h3,
+ color = TangemTheme.colors.text.tertiary,
+ )
+ }
+ }
+ }
+
+ if (article.relatedArticles.isNotEmpty()) {
+ item("relatedArticles") {
+ LazyRow(
+ modifier = Modifier.padding(vertical = 12.dp),
+ state = rememberLazyListState(),
+ contentPadding = PaddingValues(horizontal = 16.dp),
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ items(
+ items = article.relatedArticles,
+ key = RelatedArticleUM::id,
+ ) { article ->
+ RelatedNewsItem(
+ relatedArticle = article,
+ modifier = Modifier.fillParentMaxHeight(),
+ )
+ }
+ }
+ }
+ }
+ }
+ BottomFade(
+ modifier = Modifier
+ .align(Alignment.BottomCenter),
+ backgroundColor = background,
+ )
+ }
+}
+
+@Suppress("LongMethod")
+@Composable
+internal fun ArticleDetailV2(
+ article: ArticleUM,
+ onLikeClick: () -> Unit,
+ relatedTokensUM: RelatedTokensUM,
+ modifier: Modifier = Modifier,
+) {
+ val hapticFeedback = LocalHapticFeedback.current
+ val density = LocalDensity.current
+ val background = LocalMainBottomSheetColor.current.value
+ val pagerHeight = 32.dp
+ val contentPadding = pagerHeight + 56.dp + with(density) {
+ WindowInsets.navigationBars.getBottom(this).div(this.density)
+ }.dp
+
+ Box(modifier = modifier) {
+ LazyColumn(
+ modifier = Modifier
+ .fillMaxSize()
+ .hazeSourceTangem(zIndex = 0f)
+ .background(background),
+ contentPadding = PaddingValues(bottom = contentPadding),
+ ) {
+ item("content") {
+ ArticleHeader(
+ title = article.title,
+ createdAt = article.createdAt.resolveReference(),
+ score = article.score,
+ tags = article.tags,
+ isTrending = article.isTrending,
+ modifier = Modifier
+ .padding(top = 16.dp)
+ .padding(horizontal = 16.dp),
+ )
+
+ if (article.shortContent.isNotEmpty()) {
+ QuickRecap(
+ content = article.shortContent,
+ modifier = Modifier
+ .padding(top = 32.dp)
+ .padding(horizontal = 16.dp),
+ )
+ }
+
+ Text(
+ text = article.content,
+ style = TangemTheme.typography2.bodyRegular16,
+ color = TangemTheme.colors2.text.neutral.primary,
+ modifier = Modifier
+ .padding(top = 12.dp)
+ .padding(horizontal = 16.dp),
+ )
+
+ SpacerH(24.dp)
+
+ HorizontalDivider(
+ modifier = Modifier.padding(horizontal = 24.dp),
+ color = TangemTheme.colors2.border.neutral.primary,
+ )
+
+ SpacerH(20.dp)
+
+ SecondaryTangemButton(
+ modifier = Modifier.padding(horizontal = 24.dp),
+ text = resourceReference(R.string.news_like),
+ size = com.tangem.core.ui.ds.button.TangemButtonSize.X9,
+ iconRes = if (article.isLiked) {
+ R.drawable.ic_like_20
+ } else {
+ R.drawable.ic_heart_20
+ },
+ onClick = {
+ hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
+ onLikeClick()
+ },
+ shape = TangemButtonShape.Rounded,
+ )
+
+ RelatedTokensBlock(
+ relatedTokensUM = relatedTokensUM,
+ onItemClick = when (relatedTokensUM) {
+ is RelatedTokensUM.Content -> relatedTokensUM.onTokenClick
+ else -> null
+ },
+ modifier = Modifier.padding(horizontal = 16.dp),
+ )
+
+ if (article.relatedArticles.isNotEmpty()) {
+ SpacerH(24.dp)
+ Row(
+ modifier = Modifier.padding(horizontal = 16.dp),
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ ) {
+ Text(
+ text = stringResourceSafe(R.string.news_sources),
+ style = TangemTheme.typography2.headingSemibold20,
+ color = TangemTheme.colors2.text.neutral.primary,
+ )
+ }
+ }
+ }
+
+ if (article.relatedArticles.isNotEmpty()) {
+ item("relatedArticles") {
+ LazyRow(
+ modifier = Modifier
+ .padding(vertical = 12.dp),
+ state = rememberLazyListState(),
+ contentPadding = PaddingValues(horizontal = 16.dp),
+ horizontalArrangement = Arrangement.spacedBy(12.dp),
+ ) {
+ items(
+ items = article.relatedArticles,
+ key = RelatedArticleUM::id,
+ ) { article ->
+ RelatedNewsItem(
+ relatedArticle = article,
+ modifier = Modifier.fillParentMaxHeight(),
+ )
+ }
+ }
+ }
+ }
+ }
+
+ BottomFadeWithBlur(
+ modifier = Modifier
+ .align(Alignment.BottomCenter)
+ .height(80.dp)
+ .fillMaxWidth(),
+ backgroundColor = background,
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/components/QuickRecap.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/components/QuickRecap.kt
index 725bb4866b..6cc0ec1434 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/components/QuickRecap.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/components/QuickRecap.kt
@@ -17,6 +17,7 @@ import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextOverflow
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.SpacerH
@@ -24,6 +25,7 @@ import com.tangem.core.ui.components.SpacerW
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.TangemThemePreviewRedesign
@Composable
internal fun QuickRecap(content: String, modifier: Modifier = Modifier) {
@@ -105,20 +107,36 @@ private fun QuickRecapV2(content: String, modifier: Modifier = Modifier) {
Box {
VerticalDivider(
- modifier = Modifier.fillMaxHeight(),
+ modifier = Modifier
+ .fillMaxHeight()
+ .padding(start = 10.dp),
thickness = 2.dp,
color = Color(QUICK_RECAP_DIVIDER_COLOR),
)
Text(
- modifier = Modifier.padding(start = 16.dp),
+ modifier = Modifier.padding(start = 20.dp),
text = content,
- style = TangemTheme.typography.body1,
- color = TangemTheme.colors.text.primary1,
+ style = TangemTheme.typography2.bodyRegular16,
+ color = TangemTheme.colors2.text.neutral.primary,
)
}
}
}
+@Preview(showBackground = true, widthDp = 360)
+@Composable
+private fun QuickRecapPreview() {
+ TangemThemePreviewRedesign {
+ QuickRecapV2(
+ content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut" +
+ " labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris " +
+ "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit " +
+ "esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt " +
+ "in culpa qui officia deserunt mollit anim id est laborum.",
+ )
+ }
+}
+
private const val QUICK_RECAP_DIVIDER_COLOR = 0xFFA99FFF
private const val LINEAR_GRADIENT_FIRST_PART = 0xFFA3A0FF
private const val LINEAR_GRADIENT_SECOND_PART = 0xFFF79DFF
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/components/RelatedNewsItem.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/components/RelatedNewsItem.kt
new file mode 100644
index 0000000000..a3810b1bda
--- /dev/null
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/components/RelatedNewsItem.kt
@@ -0,0 +1,188 @@
+package com.tangem.features.feed.ui.news.details.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import coil.compose.SubcomposeAsyncImage
+import coil.request.CachePolicy
+import coil.request.ImageRequest
+import com.tangem.core.ui.R
+import com.tangem.core.ui.components.RectangleShimmer
+import com.tangem.core.ui.components.SpacerH
+import com.tangem.core.ui.components.SpacerHMax
+import com.tangem.core.ui.components.SpacerW
+import com.tangem.core.ui.extensions.resolveReference
+import com.tangem.core.ui.res.LocalRedesignEnabled
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.features.feed.ui.news.details.state.RelatedArticleUM
+
+@Composable
+internal fun RelatedNewsItem(relatedArticle: RelatedArticleUM, modifier: Modifier = Modifier) {
+ if (LocalRedesignEnabled.current) {
+ RelatedNewsItemV2(relatedArticle, modifier)
+ } else {
+ RelatedNewsItemV1(relatedArticle, modifier)
+ }
+}
+
+@Composable
+private fun RelatedNewsItemV1(relatedArticle: RelatedArticleUM, modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier
+ .sizeIn(maxWidth = 256.dp, minHeight = 132.dp)
+ .background(color = TangemTheme.colors.background.action, shape = RoundedCornerShape(12.dp))
+ .clickable(onClick = relatedArticle.onClick)
+ .padding(12.dp),
+ ) {
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ Column(modifier = Modifier.weight(1f)) {
+ Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(bottom = 4.dp)) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_explore_16),
+ contentDescription = null,
+ tint = TangemTheme.colors.icon.informative,
+ modifier = Modifier.size(16.dp),
+ )
+ SpacerW(4.dp)
+ Text(
+ text = relatedArticle.media.name,
+ style = TangemTheme.typography.caption1,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ color = TangemTheme.colors.text.tertiary,
+ )
+ }
+ if (relatedArticle.title.isNotEmpty()) {
+ SpacerH(4.dp)
+ Text(
+ text = relatedArticle.title,
+ style = TangemTheme.typography.subtitle2,
+ color = TangemTheme.colors.text.primary1,
+ maxLines = 3,
+ overflow = TextOverflow.Ellipsis,
+ )
+ }
+ }
+ if (relatedArticle.imageUrl != null) {
+ SubcomposeAsyncImage(
+ modifier = Modifier
+ .size(40.dp)
+ .clip(RoundedCornerShape(4.dp)),
+ contentScale = ContentScale.Crop,
+ model = ImageRequest.Builder(context = LocalContext.current)
+ .data(relatedArticle.imageUrl)
+ .crossfade(enable = false)
+ .allowHardware(true)
+ .memoryCachePolicy(CachePolicy.DISABLED)
+ .build(),
+ loading = {
+ RectangleShimmer(
+ modifier = Modifier.size(40.dp),
+ radius = 4.dp,
+ )
+ },
+ error = {},
+ contentDescription = relatedArticle.media.name,
+ )
+ }
+ }
+ SpacerHMax()
+ Text(
+ text = relatedArticle.publishedAt.resolveReference(),
+ style = TangemTheme.typography.caption2,
+ color = TangemTheme.colors.text.tertiary,
+ )
+ }
+}
+
+@Suppress("LongMethod")
+@Composable
+private fun RelatedNewsItemV2(relatedArticle: RelatedArticleUM, modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier
+ .sizeIn(maxWidth = 228.dp, minHeight = 160.dp)
+ .background(
+ color = TangemTheme.colors2.surface.level3,
+ shape = RoundedCornerShape(TangemTheme.dimens2.x5),
+ )
+ .clickable(onClick = relatedArticle.onClick)
+ .border(
+ width = 1.dp,
+ color = TangemTheme.colors2.border.neutral.primary,
+ shape = RoundedCornerShape(TangemTheme.dimens2.x5),
+ )
+ .padding(16.dp),
+ ) {
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ Column(modifier = Modifier.weight(1f)) {
+ Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(bottom = 4.dp)) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_explore_16),
+ contentDescription = null,
+ tint = TangemTheme.colors2.markers.iconGray,
+ modifier = Modifier.size(16.dp),
+ )
+ SpacerW(2.dp)
+ Text(
+ text = relatedArticle.media.name,
+ style = TangemTheme.typography2.captionSemibold12,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ color = TangemTheme.colors2.text.neutral.secondary,
+ )
+ }
+ if (relatedArticle.title.isNotEmpty()) {
+ SpacerH(8.dp)
+ Text(
+ text = relatedArticle.title,
+ style = TangemTheme.typography2.bodyRegular16,
+ color = TangemTheme.colors2.text.neutral.primary,
+ maxLines = 3,
+ overflow = TextOverflow.Ellipsis,
+ )
+ }
+ }
+ if (relatedArticle.imageUrl != null) {
+ SubcomposeAsyncImage(
+ modifier = Modifier
+ .size(44.dp)
+ .clip(RoundedCornerShape(12.dp)),
+ contentScale = ContentScale.Crop,
+ model = ImageRequest.Builder(context = LocalContext.current)
+ .data(relatedArticle.imageUrl)
+ .crossfade(enable = false)
+ .allowHardware(true)
+ .memoryCachePolicy(CachePolicy.DISABLED)
+ .build(),
+ loading = {
+ RectangleShimmer(
+ modifier = Modifier.size(44.dp),
+ radius = 12.dp,
+ )
+ },
+ error = {},
+ contentDescription = relatedArticle.media.name,
+ )
+ }
+ }
+ SpacerHMax()
+ Text(
+ text = relatedArticle.publishedAt.resolveReference(),
+ style = TangemTheme.typography2.captionSemibold12,
+ color = TangemTheme.colors2.text.neutral.secondary,
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/components/RelatedTokensBlock.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/components/RelatedTokensBlock.kt
index d43ebbb5a9..ce6a92843e 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/components/RelatedTokensBlock.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/components/RelatedTokensBlock.kt
@@ -2,6 +2,7 @@ package com.tangem.features.feed.ui.news.details.components
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.remember
@@ -16,6 +17,7 @@ import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.block.BlockCard
import com.tangem.core.ui.components.block.TangemBlockCardColors
import com.tangem.core.ui.extensions.stringResourceSafe
+import com.tangem.core.ui.res.LocalRedesignEnabled
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.feed.model.news.details.NewsDetailsModel.Companion.RELATED_TOKEN_MAX_COUNT
import com.tangem.features.feed.ui.news.details.state.RelatedTokensUM
@@ -38,11 +40,20 @@ internal fun RelatedTokensBlock(
Column(modifier = modifier) {
SpacerH(40.dp)
- Text(
- text = stringResourceSafe(R.string.news_related_tokens),
- style = TangemTheme.typography.h3,
- color = TangemTheme.colors.text.primary1,
- )
+ if (LocalRedesignEnabled.current) {
+ Text(
+ modifier = Modifier.padding(horizontal = 8.dp),
+ text = stringResourceSafe(R.string.news_related_tokens),
+ style = TangemTheme.typography2.headingSemibold20,
+ color = TangemTheme.colors2.text.neutral.primary,
+ )
+ } else {
+ Text(
+ text = stringResourceSafe(R.string.news_related_tokens),
+ style = TangemTheme.typography.h3,
+ color = TangemTheme.colors.text.primary1,
+ )
+ }
SpacerH(12.dp)
BlockCard(
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/state/MockArticlesFactory.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/state/MockArticlesFactory.kt
index e0d620f33b..f214105971 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/state/MockArticlesFactory.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/state/MockArticlesFactory.kt
@@ -11,6 +11,7 @@ import kotlinx.collections.immutable.toPersistentList
internal object MockArticlesFactory {
fun createMockArticles(): ImmutableList = listOf(
ArticleUM(
+ isTrending = false,
id = 1,
title = "SEC delays decisions on ETH-staking ETFs and spot XRP/SOL funds",
createdAt = TextReference.Str("20 Jun, 21:45"),
@@ -78,6 +79,7 @@ internal object MockArticlesFactory {
newsUrl = "",
relatedTokens = persistentListOf(),
isLiked = false,
+ isTrending = false,
),
ArticleUM(
id = 3,
@@ -107,6 +109,7 @@ internal object MockArticlesFactory {
newsUrl = "",
relatedTokens = persistentListOf(),
isLiked = false,
+ isTrending = false,
),
ArticleUM(
id = 4,
@@ -136,6 +139,7 @@ internal object MockArticlesFactory {
newsUrl = "",
relatedTokens = persistentListOf(),
isLiked = false,
+ isTrending = false,
),
ArticleUM(
id = 5,
@@ -151,6 +155,7 @@ internal object MockArticlesFactory {
newsUrl = "",
relatedTokens = persistentListOf(),
isLiked = false,
+ isTrending = false,
),
ArticleUM(
id = 6,
@@ -167,6 +172,7 @@ internal object MockArticlesFactory {
newsUrl = "",
relatedTokens = persistentListOf(),
isLiked = false,
+ isTrending = false,
),
ArticleUM(
id = 7,
@@ -182,6 +188,7 @@ internal object MockArticlesFactory {
newsUrl = "",
relatedTokens = persistentListOf(),
isLiked = false,
+ isTrending = false,
),
ArticleUM(
id = 8,
@@ -197,6 +204,7 @@ internal object MockArticlesFactory {
newsUrl = "",
relatedTokens = persistentListOf(),
isLiked = false,
+ isTrending = false,
),
ArticleUM(
id = 9,
@@ -213,6 +221,7 @@ internal object MockArticlesFactory {
newsUrl = "",
relatedTokens = persistentListOf(),
isLiked = false,
+ isTrending = false,
),
ArticleUM(
id = 10,
@@ -229,6 +238,7 @@ internal object MockArticlesFactory {
newsUrl = "",
relatedTokens = persistentListOf(),
isLiked = false,
+ isTrending = false,
),
).toPersistentList()
}
\ No newline at end of file
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/state/NewsDetailsUM.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/state/NewsDetailsUM.kt
index b83bea4e2e..c9d216ab02 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/state/NewsDetailsUM.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/news/details/state/NewsDetailsUM.kt
@@ -25,6 +25,7 @@ internal sealed interface ArticlesStateUM {
data class LoadingError(val onRetryClicked: () -> Unit) : ArticlesStateUM
}
+@Immutable
internal data class ArticleUM(
val id: Int,
val title: String,
@@ -37,6 +38,7 @@ internal data class ArticleUM(
val newsUrl: String,
val relatedTokens: ImmutableList,
val isLiked: Boolean,
+ val isTrending: Boolean,
)
internal data class RelatedArticleUM(
diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/badge/TangemBadgeStory.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/badge/TangemBadgeStory.kt
index f69f3db054..96f315a25c 100644
--- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/badge/TangemBadgeStory.kt
+++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/storybook/page/badge/TangemBadgeStory.kt
@@ -1,4 +1,5 @@
@file:Suppress("MagicNumber", "LongMethod")
+
package com.tangem.feature.tester.presentation.storybook.page.badge
import androidx.compose.foundation.background
@@ -186,7 +187,7 @@ private fun BadgeTypeRow(
) {
TangemBadge(
text = stringReference("New"),
- tangemIconUM = TangemIconUM.Icon(R.drawable.ic_information_24),
+ tangemIconUM = TangemIconUM.Icon(iconRes = R.drawable.ic_information_24),
size = size,
shape = shape,
color = color,