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,