Updated on 2026-08-14
This commit is contained in:
parent
09f411a701
commit
16fd81429c
25 changed files with 1445 additions and 320 deletions
|
|
@ -3,7 +3,6 @@ package com.tangem.common.ui.markets
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -34,10 +33,12 @@ import com.tangem.core.ui.components.RectangleShimmer
|
|||
import com.tangem.core.ui.components.SpacerW4
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeInPercent
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.ds.badge.*
|
||||
import com.tangem.core.ui.ds.image.TangemIcon
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.ds.row.TangemRowContainer
|
||||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.res.LocalIsInDarkTheme
|
||||
import com.tangem.core.ui.res.LocalWindowSize
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -95,7 +96,7 @@ fun MarketListItemContentV2(model: MarketsListItemUM, modifier: Modifier = Modif
|
|||
.padding(end = TangemTheme.dimens2.x2, start = TangemTheme.dimens2.x3),
|
||||
ratingPosition = model.ratingPosition,
|
||||
marketCap = model.marketCap,
|
||||
// stakingRate = model.stakingRate, TODO in [REDACTED_TASK_KEY]
|
||||
stakingRate = model.stakingRate,
|
||||
)
|
||||
|
||||
PriceChangeInPercent(
|
||||
|
|
@ -146,7 +147,7 @@ private fun TokenTitle(name: String, currencySymbol: String, modifier: Modifier
|
|||
private fun TokenSubtitle(
|
||||
ratingPosition: String?,
|
||||
marketCap: String?,
|
||||
// stakingRate: TextReference?, TODO in [REDACTED_TASK_KEY]
|
||||
stakingRate: TextReference?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val ratingColor = mapRatingToColor(ratingPosition)
|
||||
|
|
@ -154,7 +155,7 @@ private fun TokenSubtitle(
|
|||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1),
|
||||
) {
|
||||
TokenRatingPlace(
|
||||
ratingPosition = ratingPosition,
|
||||
|
|
@ -167,6 +168,9 @@ private fun TokenSubtitle(
|
|||
text = marketCap,
|
||||
)
|
||||
}
|
||||
if (stakingRate != null) {
|
||||
StakingRate(stakingRate = stakingRate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -214,6 +218,18 @@ private fun RowScope.TokenMarketCapText(text: String, ratingColor: Color, modifi
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RowScope.StakingRate(stakingRate: TextReference, modifier: Modifier = Modifier) {
|
||||
TangemBadge(
|
||||
modifier = modifier.alignByBaseline(),
|
||||
text = stakingRate,
|
||||
shape = TangemBadgeShape.Rounded,
|
||||
size = TangemBadgeSize.X4,
|
||||
type = TangemBadgeType.Tinted,
|
||||
color = TangemBadgeColor.Gray,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Chart(chartType: MarketChartLook.Type, chartRawData: MarketChartRawData?, modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
|
|
|
|||
|
|
@ -4,23 +4,29 @@ import android.content.res.Configuration
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tangem.core.ui.components.CircleShimmer
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.SpacerW12
|
||||
import com.tangem.core.ui.res.LocalWindowSize
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.res.*
|
||||
import com.tangem.core.ui.windowsize.WindowSizeType
|
||||
|
||||
@Composable
|
||||
fun MarketsListItemPlaceholder() {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
MarketsListItemPlaceholderV2()
|
||||
} else {
|
||||
MarketsListItemPlaceholderV1()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
fun MarketsListItemPlaceholder() {
|
||||
private fun MarketsListItemPlaceholderV1() {
|
||||
val density = LocalDensity.current
|
||||
val windowSize = LocalWindowSize.current
|
||||
val sp12 = with(density) { 12.sp.toDp() }
|
||||
|
|
@ -84,11 +90,71 @@ fun MarketsListItemPlaceholder() {
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun MarketsListItemPlaceholderV2() {
|
||||
val windowSize = LocalWindowSize.current
|
||||
Row(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = 12.dp,
|
||||
vertical = 14.dp,
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
CircleShimmer(modifier = Modifier.size(40.dp))
|
||||
|
||||
SpacerW(4.dp)
|
||||
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(96.dp)
|
||||
.height(20.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(46.dp)
|
||||
.height(16.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
}
|
||||
|
||||
if (windowSize.widthAtLeast(WindowSizeType.Small)) {
|
||||
SpacerW(10.dp)
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
horizontalAlignment = Alignment.End,
|
||||
) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(56.dp)
|
||||
.height(20.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(46.dp)
|
||||
.height(16.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360, name = "normal")
|
||||
@Preview(showBackground = true, widthDp = 360, name = "normal night", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(showBackground = true, widthDp = 320, name = "small width")
|
||||
@Composable
|
||||
private fun Preview() {
|
||||
private fun PreviewV1() {
|
||||
TangemThemePreview {
|
||||
Column(Modifier.background(TangemTheme.colors.background.tertiary)) {
|
||||
repeat(20) {
|
||||
|
|
@ -96,4 +162,20 @@ private fun Preview() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360, name = "normal")
|
||||
@Preview(showBackground = true, widthDp = 360, name = "normal night", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Preview(showBackground = true, widthDp = 320, name = "small width")
|
||||
@Composable
|
||||
private fun PreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
CompositionLocalProvider(LocalRedesignEnabled provides true) {
|
||||
Column(Modifier.background(TangemTheme.colors2.surface.level3)) {
|
||||
repeat(20) {
|
||||
MarketsListItemPlaceholder()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,12 +21,8 @@ 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 com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import dev.chrisbanes.haze.HazeStyle
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
|
@ -44,15 +40,6 @@ private val SMALL_DOT_SIZE = DpSize(4.dp, 4.dp)
|
|||
|
||||
@Composable
|
||||
fun PagerIndicator(pagerState: PagerState, modifier: Modifier = Modifier) {
|
||||
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,
|
||||
|
|
@ -65,21 +52,6 @@ private fun PagerIndicatorV1(pagerState: PagerState, modifier: Modifier = Modifi
|
|||
)
|
||||
}
|
||||
|
||||
@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
|
||||
|
|
@ -291,7 +263,7 @@ private class DotSizeParams private constructor(
|
|||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun PagerIndicatorPreviewV1() {
|
||||
private fun PagerIndicatorPreviewV() {
|
||||
TangemThemePreview {
|
||||
Column(
|
||||
Modifier
|
||||
|
|
@ -306,23 +278,6 @@ private fun PagerIndicatorPreviewV1() {
|
|||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun PagerIndicatorPreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
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 PagerIndicator6ItemsPreview() {
|
||||
|
|
|
|||
|
|
@ -54,18 +54,14 @@ private val SMALL_DOT_SIZE = DpSize(4.dp, 4.dp)
|
|||
*
|
||||
* @param pagerState state of the pager to observe
|
||||
* @param modifier modifier for styling
|
||||
* @param hasBackground whether to show a background behind the indicators
|
||||
* @param activeIndicatorColor color for the active page indicator
|
||||
* @param inactiveIndicatorColor color for the inactive page indicators
|
||||
* @param colors colors of indicators(active/inactive) and overlay
|
||||
*/
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Composable
|
||||
fun TangemPagerIndicator(
|
||||
pagerState: PagerState,
|
||||
modifier: Modifier = Modifier,
|
||||
hasBackground: Boolean = false,
|
||||
activeIndicatorColor: Color = TangemTheme.colors2.graphic.neutral.primary,
|
||||
inactiveIndicatorColor: Color = TangemTheme.colors2.graphic.neutral.tertiary,
|
||||
colors: PagerIndicatorColors = TangemPagerIndicatorColors,
|
||||
) {
|
||||
val totalPages = pagerState.pageCount
|
||||
val currentIndex = pagerState.currentPage
|
||||
|
|
@ -131,11 +127,13 @@ fun TangemPagerIndicator(
|
|||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.conditionalCompose(hasBackground) {
|
||||
background(
|
||||
color = TangemTheme.colors2.tabs.backgroundSecondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.conditionalCompose(colors.overlay != null) {
|
||||
colors.overlay?.let { overlay ->
|
||||
background(
|
||||
color = overlay,
|
||||
shape = CircleShape,
|
||||
)
|
||||
} ?: this
|
||||
}
|
||||
.padding(TangemTheme.dimens2.x3),
|
||||
contentAlignment = Alignment.Center,
|
||||
|
|
@ -162,8 +160,8 @@ fun TangemPagerIndicator(
|
|||
index = index,
|
||||
currentIndex = currentIndex,
|
||||
totalPages = totalPages,
|
||||
activeColor = activeIndicatorColor,
|
||||
inactiveColor = inactiveIndicatorColor,
|
||||
activeColor = colors.active,
|
||||
inactiveColor = colors.inactive,
|
||||
modifier = Modifier.graphicsLayer { alpha = dotAlpha },
|
||||
)
|
||||
}
|
||||
|
|
@ -297,6 +295,22 @@ private fun Dot(
|
|||
}
|
||||
}
|
||||
|
||||
val TangemPagerIndicatorColors: PagerIndicatorColors
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = PagerIndicatorColors(
|
||||
active = TangemTheme.colors2.graphic.neutral.primary,
|
||||
inactive = TangemTheme.colors2.graphic.neutral.tertiary,
|
||||
overlay = null,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
data class PagerIndicatorColors(
|
||||
val active: Color,
|
||||
val inactive: Color,
|
||||
val overlay: Color?,
|
||||
)
|
||||
|
||||
// region Preview
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
|
|
@ -312,7 +326,13 @@ private fun TangemPagerIndicator_Preview(@PreviewParameter(TangemPagerIndicatorP
|
|||
repeat(params) { index ->
|
||||
TangemPagerIndicator(
|
||||
pagerState = rememberPagerState(index) { params },
|
||||
hasBackground = index % 2 == 0,
|
||||
colors = TangemPagerIndicatorColors.copy(
|
||||
overlay = if (index % 2 == 0) {
|
||||
TangemTheme.colors2.tabs.backgroundSecondary
|
||||
} else {
|
||||
null
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ private fun TangemButtonIcon(
|
|||
visible = isVisible,
|
||||
modifier = Modifier.size(size = size.toContentSize()),
|
||||
) {
|
||||
val wrappedIconRes = remember(this) { requireNotNull(iconRes) }
|
||||
val wrappedIconRes = remember(iconRes) { requireNotNull(iconRes) }
|
||||
Icon(
|
||||
painter = painterResource(id = wrappedIconRes),
|
||||
contentDescription = null,
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ fun TangemTab(
|
|||
shape = shape,
|
||||
)
|
||||
.clip(shape = shape)
|
||||
.padding(all = TangemTheme.dimens2.x3)
|
||||
.toggleable(
|
||||
value = isChecked,
|
||||
onValueChange = onCheckedChange,
|
||||
|
|
@ -67,7 +66,8 @@ fun TangemTab(
|
|||
role = Role.Checkbox,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = ripple(),
|
||||
),
|
||||
)
|
||||
.padding(all = TangemTheme.dimens2.x3),
|
||||
) {
|
||||
Text(
|
||||
text = text.resolveReference(),
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
package com.tangem.features.feed.components.news.details
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
|
|
@ -12,7 +19,13 @@ import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
|||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
||||
import com.tangem.core.ui.decompose.ComposableModularBottomSheetContentComponent
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.LocalMainBottomSheetColor
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.TokenMarketParams
|
||||
import com.tangem.domain.news.model.NewsListConfig
|
||||
|
|
@ -32,21 +45,65 @@ internal class DefaultNewsDetailsComponent(
|
|||
override fun Title(bottomSheetState: State<BottomSheetState>) {
|
||||
val background = LocalMainBottomSheetColor.current.value
|
||||
val state by newsDetailsModel.state.collectAsStateWithLifecycle()
|
||||
TangemTopAppBar(
|
||||
containerColor = background,
|
||||
title = null,
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
onClicked = state.onBackClick,
|
||||
isEnabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
),
|
||||
endButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_share_24,
|
||||
onClicked = state.onShareClick,
|
||||
isEnabled = bottomSheetState.value == BottomSheetState.EXPANDED &&
|
||||
state.articlesStateUM is ArticlesStateUM.Content,
|
||||
),
|
||||
)
|
||||
if (LocalRedesignEnabled.current) {
|
||||
TangemTopBar(
|
||||
title = resourceReference(R.string.common_news),
|
||||
type = TangemTopBarType.BottomSheet,
|
||||
startContent = {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_back_28),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.primary,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x11)
|
||||
.background(
|
||||
color = TangemTheme.colors2.button.backgroundSecondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.clickableSingle(
|
||||
onClick = state.onBackClick,
|
||||
enabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
)
|
||||
.padding(TangemTheme.dimens2.x2),
|
||||
)
|
||||
},
|
||||
endContent = {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_share_new_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.primary,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x11)
|
||||
.background(
|
||||
color = TangemTheme.colors2.button.backgroundSecondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.clickableSingle(
|
||||
onClick = state.onShareClick,
|
||||
enabled = bottomSheetState.value == BottomSheetState.EXPANDED &&
|
||||
state.articlesStateUM is ArticlesStateUM.Content,
|
||||
)
|
||||
.padding(TangemTheme.dimens2.x2_5),
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
TangemTopAppBar(
|
||||
containerColor = background,
|
||||
title = null,
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
onClicked = state.onBackClick,
|
||||
isEnabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
),
|
||||
endButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_share_24,
|
||||
onClicked = state.onShareClick,
|
||||
isEnabled = bottomSheetState.value == BottomSheetState.EXPANDED &&
|
||||
state.articlesStateUM is ArticlesStateUM.Content,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
package com.tangem.features.feed.components.news.list
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
|
|
@ -12,8 +19,14 @@ import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
|||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
|
||||
import com.tangem.core.ui.decompose.ComposableModularBottomSheetContentComponent
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
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.domain.news.model.NewsListConfig
|
||||
import com.tangem.features.feed.model.news.list.NewsListModel
|
||||
import com.tangem.features.feed.ui.news.list.NewsListContent
|
||||
|
|
@ -30,15 +43,40 @@ internal class DefaultNewsListComponent(
|
|||
override fun Title(bottomSheetState: State<BottomSheetState>) {
|
||||
val background = LocalMainBottomSheetColor.current.value
|
||||
val state by newsListModel.state.collectAsStateWithLifecycle()
|
||||
TangemTopAppBar(
|
||||
containerColor = background,
|
||||
title = stringResourceSafe(R.string.common_news),
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
onClicked = state.onBackClick,
|
||||
isEnabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
),
|
||||
)
|
||||
if (LocalRedesignEnabled.current) {
|
||||
TangemTopBar(
|
||||
title = resourceReference(R.string.common_news),
|
||||
type = TangemTopBarType.BottomSheet,
|
||||
startContent = {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_back_28),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.primary,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x11)
|
||||
.background(
|
||||
color = TangemTheme.colors2.button.backgroundSecondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.clickableSingle(
|
||||
onClick = state.onBackClick,
|
||||
enabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
)
|
||||
.padding(TangemTheme.dimens2.x2),
|
||||
)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
TangemTopAppBar(
|
||||
containerColor = background,
|
||||
title = stringResourceSafe(R.string.common_news),
|
||||
startButton = TopAppBarButtonUM.Icon(
|
||||
iconRes = R.drawable.ic_back_24,
|
||||
onClicked = state.onBackClick,
|
||||
isEnabled = bottomSheetState.value == BottomSheetState.EXPANDED,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ import com.tangem.features.feed.model.converter.ShortArticleToArticleConfigUMCon
|
|||
import com.tangem.features.feed.model.market.details.analytics.MarketDetailsAnalyticsEvent
|
||||
import com.tangem.features.feed.model.market.details.converter.DescriptionConverter
|
||||
import com.tangem.features.feed.model.market.details.converter.ExchangeItemStateConverter
|
||||
import com.tangem.features.feed.model.market.details.converter.ExchangeItemStateConverterV2
|
||||
import com.tangem.features.feed.model.market.details.converter.TokenMarketInfoConverter
|
||||
import com.tangem.features.feed.model.market.details.formatter.*
|
||||
import com.tangem.features.feed.model.market.details.state.QuotesStateUpdater
|
||||
|
|
@ -78,7 +79,7 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
getUserCountryUseCase: GetUserCountryUseCase,
|
||||
paramsContainer: ParamsContainer,
|
||||
designFeatureToggles: DesignFeatureToggles,
|
||||
private val designFeatureToggles: DesignFeatureToggles,
|
||||
private val getTokenPriceChartUseCase: GetTokenPriceChartUseCase,
|
||||
private val getTokenMarketInfoUseCase: GetTokenMarketInfoUseCase,
|
||||
private val getTokenFullQuotesUseCase: GetTokenFullQuotesUseCase,
|
||||
|
|
@ -704,9 +705,15 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
ExchangesBottomSheetContent.Error(onRetryClick = { onListedOnClick(exchangesCount) })
|
||||
},
|
||||
ifRight = { list ->
|
||||
ExchangesBottomSheetContent.Content(
|
||||
exchangeItems = ExchangeItemStateConverter.convertList(list).toImmutableList(),
|
||||
)
|
||||
if (designFeatureToggles.isRedesignEnabled) {
|
||||
ExchangesBottomSheetContent.ContentV2(
|
||||
exchangeItemsV2 = ExchangeItemStateConverterV2.convertList(list).toImmutableList(),
|
||||
)
|
||||
} else {
|
||||
ExchangesBottomSheetContent.ContentV1(
|
||||
exchangeItems = ExchangeItemStateConverter.convertList(list).toImmutableList(),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
package com.tangem.features.feed.model.market.details.converter
|
||||
|
||||
import com.tangem.core.ui.components.audits.AuditLabelUM
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.format.bigdecimal.compact
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.markets.TokenMarketExchange
|
||||
import com.tangem.domain.markets.TokenMarketExchange.TrustScore
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.ui.market.detailed.state.ExchangeItemUM
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal object ExchangeItemStateConverterV2 : Converter<TokenMarketExchange, ExchangeItemUM> {
|
||||
|
||||
override fun convert(value: TokenMarketExchange): ExchangeItemUM.Content {
|
||||
return ExchangeItemUM.Content(
|
||||
id = value.id,
|
||||
title = stringReference(value.name),
|
||||
subTitle = stringReference(value = if (value.isCentralized) "CEX" else "DEX"),
|
||||
icon = TangemIconUM.Currency(
|
||||
CurrencyIconState.CoinIcon(
|
||||
url = value.imageUrl,
|
||||
fallbackResId = R.drawable.ic_alert_24,
|
||||
isGrayscale = false,
|
||||
shouldShowCustomBadge = false,
|
||||
),
|
||||
),
|
||||
volumeInUsd = stringReference(
|
||||
value.volumeInUsd.format {
|
||||
fiat(
|
||||
fiatCurrencyCode = "USD",
|
||||
fiatCurrencySymbol = "$",
|
||||
).compact()
|
||||
},
|
||||
),
|
||||
auditLabel = value.trustScore.toAuditLabelUM(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun TrustScore.toAuditLabelUM(): AuditLabelUM {
|
||||
return when (this) {
|
||||
TrustScore.Risky -> AuditLabelUM(
|
||||
text = resourceReference(id = R.string.markets_token_details_exchange_trust_score_risky),
|
||||
type = AuditLabelUM.Type.Prohibition,
|
||||
)
|
||||
TrustScore.Caution -> AuditLabelUM(
|
||||
text = resourceReference(id = R.string.markets_token_details_exchange_trust_score_caution),
|
||||
type = AuditLabelUM.Type.Warning,
|
||||
)
|
||||
TrustScore.Trusted -> AuditLabelUM(
|
||||
text = resourceReference(id = R.string.markets_token_details_exchange_trust_score_trusted),
|
||||
type = AuditLabelUM.Type.Permit,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
|
|
@ -14,21 +13,19 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.onFirstVisible
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
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.*
|
||||
import com.tangem.core.ui.components.buttons.SecondarySmallButton
|
||||
import com.tangem.core.ui.components.buttons.SmallButtonConfig
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.list.InfiniteListHandler
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.extensions.conditional
|
||||
import com.tangem.core.ui.extensions.conditionalCompose
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.*
|
||||
import com.tangem.features.feed.ui.earn.components.*
|
||||
import com.tangem.features.feed.ui.earn.state.*
|
||||
|
|
@ -348,66 +345,6 @@ private fun FilterButtonsShimmer(modifier: Modifier = Modifier) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BestOpportunitiesEmpty(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
)
|
||||
.padding(vertical = 32.dp, horizontal = 12.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
painter = painterResource(R.drawable.ic_empty_64),
|
||||
contentDescription = null,
|
||||
tint = Color.Unspecified,
|
||||
)
|
||||
SpacerH(24.dp)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 32.dp),
|
||||
text = stringResourceSafe(R.string.earn_empty),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BestOpportunitiesEmptyFiltered(onClearFilterClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
)
|
||||
.padding(vertical = 32.dp, horizontal = 12.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.earn_no_results),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
SpacerH(12.dp)
|
||||
SecondarySmallButton(
|
||||
config = SmallButtonConfig(
|
||||
text = resourceReference(R.string.earn_clear_filter),
|
||||
onClick = onClearFilterClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SectionHeader(title: String, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
package com.tangem.features.feed.ui.earn.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
internal fun BestOpportunitiesEmpty(modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
BestOpportunitiesEmptyV2(modifier)
|
||||
} else {
|
||||
BestOpportunitiesEmptyV1(modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BestOpportunitiesEmptyV1(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
)
|
||||
.padding(vertical = 32.dp, horizontal = 12.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
painter = painterResource(R.drawable.ic_empty_64),
|
||||
contentDescription = null,
|
||||
tint = Color.Unspecified,
|
||||
)
|
||||
SpacerH(TangemTheme.dimens2.x6)
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens2.x8),
|
||||
text = stringResourceSafe(R.string.earn_empty),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BestOpportunitiesEmptyV2(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens2.x4)
|
||||
.background(
|
||||
color = TangemTheme.colors2.surface.level3,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||
)
|
||||
.padding(vertical = TangemTheme.dimens2.x8, horizontal = TangemTheme.dimens2.x3),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
painter = painterResource(R.drawable.ic_empty_64),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.quaternary,
|
||||
)
|
||||
SpacerH(TangemTheme.dimens2.x5)
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x8),
|
||||
text = stringResourceSafe(R.string.earn_empty),
|
||||
style = TangemTheme.typography2.bodyRegular14,
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
package com.tangem.features.feed.ui.earn.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.buttons.SecondarySmallButton
|
||||
import com.tangem.core.ui.components.buttons.SmallButtonConfig
|
||||
import com.tangem.core.ui.ds.button.*
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
internal fun BestOpportunitiesEmptyFiltered(onClearFilterClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
BestOpportunitiesEmptyFilteredV2(onClearFilterClick, modifier)
|
||||
} else {
|
||||
BestOpportunitiesEmptyFilteredV1(onClearFilterClick, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BestOpportunitiesEmptyFilteredV2(onClearFilterClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens2.x4)
|
||||
.background(
|
||||
color = TangemTheme.colors2.surface.level3,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||
)
|
||||
.padding(vertical = TangemTheme.dimens2.x8, horizontal = TangemTheme.dimens2.x3),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.earn_no_results),
|
||||
style = TangemTheme.typography2.bodyRegular14,
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
)
|
||||
SpacerH(TangemTheme.dimens2.x2)
|
||||
TangemButton(
|
||||
buttonUM = TangemButtonUM(
|
||||
text = resourceReference(R.string.earn_clear_filter),
|
||||
onClick = onClearFilterClick,
|
||||
type = TangemButtonType.Secondary,
|
||||
size = TangemButtonSize.X8,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BestOpportunitiesEmptyFilteredV1(onClearFilterClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.background(
|
||||
color = TangemTheme.colors.background.action,
|
||||
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||
)
|
||||
.padding(vertical = 32.dp, horizontal = 12.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.earn_no_results),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
SpacerH(12.dp)
|
||||
SecondarySmallButton(
|
||||
config = SmallButtonConfig(
|
||||
text = resourceReference(R.string.earn_clear_filter),
|
||||
onClick = onClearFilterClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +1,54 @@
|
|||
package com.tangem.features.feed.ui.earn.components
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||
import com.tangem.core.ui.ds.button.SecondaryTangemButton
|
||||
import com.tangem.core.ui.ds.button.TangemButtonShape
|
||||
import com.tangem.core.ui.ds.button.TangemButtonSize
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetType
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.feed.impl.R
|
||||
|
||||
@Composable
|
||||
internal inline fun <reified T : TangemBottomSheetConfigContent> EarnFilterBottomSheet(
|
||||
config: TangemBottomSheetConfig,
|
||||
crossinline content: @Composable ColumnScope.(T) -> Unit,
|
||||
crossinline content: @Composable (T) -> Unit,
|
||||
) {
|
||||
TangemBottomSheet<T>(
|
||||
config = config,
|
||||
type = TangemBottomSheetType.Modal,
|
||||
containerColor = TangemTheme.colors2.surface.level3,
|
||||
title = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens.spacing24)
|
||||
.padding(bottom = TangemTheme.dimens.spacing12),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.earn_filter_by),
|
||||
style = TangemTheme.typography2.headingSemibold17,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
textAlign = TextAlign.Center,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
SecondaryTangemButton(
|
||||
modifier = Modifier.align(Alignment.CenterEnd),
|
||||
onClick = config.onDismissRequest,
|
||||
iconRes = R.drawable.ic_close_24,
|
||||
size = TangemButtonSize.X9,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
)
|
||||
}
|
||||
TangemTopBar(
|
||||
title = resourceReference(R.string.earn_filter_by),
|
||||
type = TangemTopBarType.BottomSheet,
|
||||
endContent = {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_close_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.primary,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x11)
|
||||
.background(
|
||||
color = TangemTheme.colors2.button.backgroundSecondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.clickableSingle(onClick = config.onDismissRequest)
|
||||
.padding(TangemTheme.dimens2.x2_5),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
content = content,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -119,9 +119,10 @@ private fun ContentV2(content: EarnFilterByNetworkBottomSheetContentUM) {
|
|||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(horizontal = TangemTheme.dimens2.x4)
|
||||
.padding(bottom = TangemTheme.dimens2.x4)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x3),
|
||||
) {
|
||||
NetworksTypesBlock(
|
||||
allMyNetworks = allMyNetworks,
|
||||
|
|
|
|||
|
|
@ -93,7 +93,9 @@ private fun ContentV1(content: EarnFilterByTypeBottomSheetContentUM) {
|
|||
@Composable
|
||||
private fun ContentV2(content: EarnFilterByTypeBottomSheetContentUM) {
|
||||
CardFilterBlock(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
modifier = Modifier
|
||||
.padding(horizontal = TangemTheme.dimens2.x4)
|
||||
.padding(bottom = TangemTheme.dimens2.x4),
|
||||
) {
|
||||
EarnFilterTypeUM.entries.forEachIndexed { index, type ->
|
||||
TangemRowContainer(
|
||||
|
|
|
|||
|
|
@ -9,14 +9,15 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.markets.MarketsListItemPlaceholder
|
||||
import com.tangem.features.feed.ui.feed.components.articles.DefaultLoadingArticle
|
||||
import com.tangem.features.feed.ui.feed.components.articles.TrendingLoadingArticle
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
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.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.feed.ui.feed.components.articles.DefaultLoadingArticle
|
||||
import com.tangem.features.feed.ui.feed.components.articles.TrendingLoadingArticle
|
||||
|
||||
@Composable
|
||||
internal fun FeedListLoading(modifier: Modifier = Modifier) {
|
||||
|
|
@ -35,70 +36,142 @@ internal fun FeedListLoading(modifier: Modifier = Modifier) {
|
|||
|
||||
@Composable
|
||||
internal fun MarketLoadingBlock() {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.size(width = 104.dp, height = 18.dp),
|
||||
)
|
||||
SpacerH(15.dp)
|
||||
ChartsLoading(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(35.dp)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun MarketPulseLoadingBlock() {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.size(width = 104.dp, height = 18.dp),
|
||||
)
|
||||
SpacerH(15.dp)
|
||||
LazyRow(
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
state = rememberLazyListState(),
|
||||
) {
|
||||
items(DEFAULT_CHART_SIZE_IN_MARKET) {
|
||||
RectangleShimmer(modifier = Modifier.size(width = 124.dp, height = 36.dp))
|
||||
}
|
||||
}
|
||||
SpacerH(16.dp)
|
||||
ChartsLoading(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(32.dp)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun NewsLoadingBlock() {
|
||||
Column {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, top = 16.dp)
|
||||
.size(width = 132.dp, height = 24.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
SpacerH(20.dp)
|
||||
ChartsLoading(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(40.dp)
|
||||
} else {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.size(width = 104.dp, height = 18.dp),
|
||||
)
|
||||
SpacerH(15.dp)
|
||||
TrendingLoadingArticle(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(12.dp)
|
||||
LazyRow(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
state = rememberLazyListState(),
|
||||
) {
|
||||
items(DEFAULT_CHART_SIZE_IN_MARKET) {
|
||||
DefaultLoadingArticle()
|
||||
}
|
||||
}
|
||||
ChartsLoading(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(35.dp)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun MarketPulseLoadingBlock() {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, top = 16.dp)
|
||||
.size(width = 132.dp, height = 24.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
SpacerH(15.dp)
|
||||
LazyRow(
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
state = rememberLazyListState(),
|
||||
) {
|
||||
items(DEFAULT_CHART_SIZE_IN_MARKET) {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier.size(width = 112.dp, height = 36.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
}
|
||||
}
|
||||
SpacerH(16.dp)
|
||||
ChartsLoading(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(32.dp)
|
||||
} else {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.size(width = 104.dp, height = 18.dp),
|
||||
)
|
||||
SpacerH(15.dp)
|
||||
LazyRow(
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
state = rememberLazyListState(),
|
||||
) {
|
||||
items(DEFAULT_CHART_SIZE_IN_MARKET) {
|
||||
RectangleShimmer(modifier = Modifier.size(width = 124.dp, height = 36.dp))
|
||||
}
|
||||
}
|
||||
SpacerH(16.dp)
|
||||
ChartsLoading(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(32.dp)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun NewsLoadingBlock() {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
Column {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, top = 16.dp)
|
||||
.size(width = 132.dp, height = 24.dp),
|
||||
radius = TangemTheme.dimens2.x25,
|
||||
)
|
||||
SpacerH(20.dp)
|
||||
TrendingLoadingArticle(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(12.dp)
|
||||
LazyRow(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
state = rememberLazyListState(),
|
||||
) {
|
||||
items(DEFAULT_CHART_SIZE_IN_MARKET) {
|
||||
DefaultLoadingArticle()
|
||||
}
|
||||
}
|
||||
SpacerH(40.dp)
|
||||
}
|
||||
} else {
|
||||
Column {
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.size(width = 104.dp, height = 18.dp),
|
||||
)
|
||||
SpacerH(15.dp)
|
||||
TrendingLoadingArticle(modifier = Modifier.padding(horizontal = 16.dp))
|
||||
SpacerH(12.dp)
|
||||
LazyRow(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
state = rememberLazyListState(),
|
||||
) {
|
||||
items(DEFAULT_CHART_SIZE_IN_MARKET) {
|
||||
DefaultLoadingArticle()
|
||||
}
|
||||
}
|
||||
SpacerH(35.dp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ChartsLoading(modifier: Modifier = Modifier) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
BlockCard(
|
||||
modifier = modifier,
|
||||
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
|
||||
colors = TangemBlockCardColors.copy(
|
||||
containerColor = if (isRedesignEnabled) {
|
||||
TangemTheme.colors2.surface.level3
|
||||
} else {
|
||||
TangemTheme.colors.background.action
|
||||
},
|
||||
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -25,7 +25,9 @@ import com.tangem.core.ui.components.SpacerH
|
|||
import com.tangem.core.ui.components.UnableToLoadData
|
||||
import com.tangem.core.ui.components.block.BlockCard
|
||||
import com.tangem.core.ui.components.block.TangemBlockCardColors
|
||||
import com.tangem.core.ui.ds.tabs.TangemTab
|
||||
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.market.list.state.SortByTypeUM
|
||||
import com.tangem.features.feed.ui.feed.state.FeedListCallbacks
|
||||
|
|
@ -34,6 +36,7 @@ import com.tangem.features.feed.ui.feed.state.MarketChartUM
|
|||
|
||||
@Composable
|
||||
internal fun MarketBlock(marketChart: MarketChartUM?, feedListCallbacks: FeedListCallbacks) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
AnimatedContent(
|
||||
targetState = marketChart,
|
||||
label = "MarketBlockChartAnimation",
|
||||
|
|
@ -49,8 +52,16 @@ internal fun MarketBlock(marketChart: MarketChartUM?, feedListCallbacks: FeedLis
|
|||
title = {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.markets_common_title),
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = if (isRedesignEnabled) {
|
||||
TangemTheme.typography2.headingSemibold20
|
||||
} else {
|
||||
TangemTheme.typography.h3
|
||||
},
|
||||
color = if (isRedesignEnabled) {
|
||||
TangemTheme.colors2.text.neutral.primary
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
},
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
|
|
@ -76,8 +87,10 @@ internal fun MarketBlock(marketChart: MarketChartUM?, feedListCallbacks: FeedLis
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
internal fun MarketPulseBlock(marketChartConfig: MarketChartConfig, feedListCallbacks: FeedListCallbacks) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
val onSeeAllClick by rememberUpdatedState {
|
||||
feedListCallbacks.onMarketOpenClick(marketChartConfig.currentSortByType)
|
||||
}
|
||||
|
|
@ -86,8 +99,16 @@ internal fun MarketPulseBlock(marketChartConfig: MarketChartConfig, feedListCall
|
|||
title = {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.markets_pulse_common_title),
|
||||
style = TangemTheme.typography.h3,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = if (isRedesignEnabled) {
|
||||
TangemTheme.typography2.headingSemibold20
|
||||
} else {
|
||||
TangemTheme.typography.h3
|
||||
},
|
||||
color = if (isRedesignEnabled) {
|
||||
TangemTheme.colors2.text.neutral.primary
|
||||
} else {
|
||||
TangemTheme.colors.text.primary1
|
||||
},
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
|
|
@ -98,9 +119,13 @@ internal fun MarketPulseBlock(marketChartConfig: MarketChartConfig, feedListCall
|
|||
)
|
||||
|
||||
LazyRow(
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
modifier = Modifier.padding(vertical = if (isRedesignEnabled) 12.dp else 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
contentPadding = if (isRedesignEnabled) {
|
||||
PaddingValues(horizontal = 16.dp, vertical = 6.dp)
|
||||
} else {
|
||||
PaddingValues(16.dp)
|
||||
},
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
state = rememberLazyListState(),
|
||||
) {
|
||||
|
|
@ -108,16 +133,22 @@ internal fun MarketPulseBlock(marketChartConfig: MarketChartConfig, feedListCall
|
|||
items = marketChartConfig.getFilterPreset(),
|
||||
key = SortByTypeUM::name,
|
||||
) { sortByTypeUM ->
|
||||
ChartsFilterChip(
|
||||
sortByTypeUM = sortByTypeUM,
|
||||
isSelected = sortByTypeUM == marketChartConfig.currentSortByType,
|
||||
onClick = { feedListCallbacks.onSortTypeClick(sortByTypeUM) },
|
||||
)
|
||||
if (isRedesignEnabled) {
|
||||
TangemTab(
|
||||
text = sortByTypeUM.text,
|
||||
isChecked = sortByTypeUM == marketChartConfig.currentSortByType,
|
||||
onCheckedChange = { feedListCallbacks.onSortTypeClick(sortByTypeUM) },
|
||||
)
|
||||
} else {
|
||||
ChartsFilterChip(
|
||||
sortByTypeUM = sortByTypeUM,
|
||||
isSelected = sortByTypeUM == marketChartConfig.currentSortByType,
|
||||
onClick = { feedListCallbacks.onSortTypeClick(sortByTypeUM) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpacerH(12.dp)
|
||||
|
||||
AnimatedContent(
|
||||
targetState = marketChartConfig.currentSortByType,
|
||||
label = "MarketPulseChartAnimation",
|
||||
|
|
@ -141,9 +172,16 @@ private fun Charts(
|
|||
onItemClick: (MarketsListItemUM) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
BlockCard(
|
||||
modifier = modifier,
|
||||
colors = TangemBlockCardColors.copy(containerColor = TangemTheme.colors.background.action),
|
||||
colors = TangemBlockCardColors.copy(
|
||||
containerColor = if (isRedesignEnabled) {
|
||||
TangemTheme.colors2.surface.level3
|
||||
} else {
|
||||
TangemTheme.colors.background.action
|
||||
},
|
||||
),
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
when (marketChart) {
|
||||
|
|
|
|||
|
|
@ -2,28 +2,31 @@ package com.tangem.features.feed.ui.market.detailed.components
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
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.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.CircleShimmer
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.SpacerH12
|
||||
import com.tangem.core.ui.components.appbar.TangemTopAppBar
|
||||
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
|
||||
|
|
@ -35,16 +38,22 @@ import com.tangem.core.ui.components.buttons.SmallButtonConfig
|
|||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.token.TokenItem
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.ds.button.*
|
||||
import com.tangem.core.ui.ds.image.TangemIcon
|
||||
import com.tangem.core.ui.ds.row.TangemRowContainer
|
||||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.ui.market.detailed.state.ExchangeItemUM
|
||||
import com.tangem.features.feed.ui.market.detailed.state.ExchangesBottomSheetContent
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet as TangemBottomSheetV2
|
||||
|
||||
/**
|
||||
* Exchanges bottom sheet
|
||||
|
|
@ -55,6 +64,15 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
*/
|
||||
@Composable
|
||||
internal fun ExchangesBottomSheet(config: TangemBottomSheetConfig) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
ExchangesBottomSheetV2(config)
|
||||
} else {
|
||||
ExchangesBottomSheetV1(config)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExchangesBottomSheetV1(config: TangemBottomSheetConfig) {
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
TangemBottomSheet<ExchangesBottomSheetContent>(
|
||||
|
|
@ -98,6 +116,77 @@ internal fun ExchangesBottomSheet(config: TangemBottomSheetConfig) {
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExchangesBottomSheetV2(config: TangemBottomSheetConfig) {
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
TangemBottomSheetV2<ExchangesBottomSheetContent>(
|
||||
config = config,
|
||||
title = { content ->
|
||||
TangemTopBar(
|
||||
title = resourceReference(content.titleResId),
|
||||
type = TangemTopBarType.BottomSheet,
|
||||
startContent = {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_back_28),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.primary,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x11)
|
||||
.background(
|
||||
color = TangemTheme.colors2.button.backgroundSecondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.clickableSingle(onClick = config.onDismissRequest)
|
||||
.padding(TangemTheme.dimens2.x2),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
content = { content ->
|
||||
Box {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(bottom = bottomBarHeight),
|
||||
) {
|
||||
item(key = "subtitle") {
|
||||
Subtitle(
|
||||
subtitleRes = content.subtitleResId,
|
||||
volumeReference = content.volumeReference,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens2.x7),
|
||||
)
|
||||
}
|
||||
|
||||
if (content.exchangeItemsV2.isNotEmpty()) {
|
||||
val lastIndex = content.exchangeItemsV2.lastIndex
|
||||
itemsIndexed(
|
||||
items = content.exchangeItemsV2,
|
||||
key = { _, item -> item.id },
|
||||
) { index, item ->
|
||||
ExchangeItemRow(
|
||||
exchangeItemUM = item,
|
||||
modifier = Modifier
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = index,
|
||||
lastIndex = lastIndex,
|
||||
backgroundColor = TangemTheme.colors2.surface.level3,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (content is ExchangesBottomSheetContent.Error) {
|
||||
Error(
|
||||
content = content,
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Title(@StringRes textResId: Int, onBackClick: () -> Unit) {
|
||||
TangemTopAppBar(
|
||||
|
|
@ -120,17 +209,36 @@ private fun Subtitle(@StringRes subtitleRes: Int, volumeReference: TextReference
|
|||
|
||||
@Composable
|
||||
private fun SubtitleText(textReference: TextReference) {
|
||||
Text(
|
||||
text = textReference.resolveReference(),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
if (LocalRedesignEnabled.current) {
|
||||
Text(
|
||||
text = textReference.resolveReference(),
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = textReference.resolveReference(),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Error(content: ExchangesBottomSheetContent.Error, modifier: Modifier = Modifier) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
ErrorV2(content, modifier)
|
||||
} else {
|
||||
ErrorV1(content, modifier)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ErrorV1(content: ExchangesBottomSheetContent.Error, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
|
|
@ -154,6 +262,155 @@ private fun Error(content: ExchangesBottomSheetContent.Error, modifier: Modifier
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ErrorV2(content: ExchangesBottomSheetContent.Error, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(id = content.message),
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
style = TangemTheme.typography2.bodyRegular14,
|
||||
)
|
||||
|
||||
SpacerH(8.dp)
|
||||
|
||||
TangemButton(
|
||||
buttonUM = TangemButtonUM(
|
||||
text = resourceReference(com.tangem.core.ui.R.string.try_to_load_data_again_button_title),
|
||||
onClick = content.onRetryClick,
|
||||
type = TangemButtonType.Secondary,
|
||||
size = TangemButtonSize.X8,
|
||||
shape = TangemButtonShape.Rounded,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExchangeItemRow(exchangeItemUM: ExchangeItemUM, modifier: Modifier = Modifier) {
|
||||
when (exchangeItemUM) {
|
||||
is ExchangeItemUM.Content -> {
|
||||
ExchangeItemRowContent(exchangeItemUM, modifier)
|
||||
}
|
||||
is ExchangeItemUM.Loading -> {
|
||||
ExchangeItemRowPlaceholder(modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExchangeItemRowContent(exchangeItemUM: ExchangeItemUM.Content, modifier: Modifier = Modifier) {
|
||||
TangemRowContainer(
|
||||
modifier = modifier,
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 14.dp),
|
||||
) {
|
||||
TangemIcon(
|
||||
tangemIconUM = exchangeItemUM.icon,
|
||||
modifier = Modifier
|
||||
.layoutId(layoutId = TangemRowLayoutId.HEAD)
|
||||
.size(TangemTheme.dimens2.x10),
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x2)
|
||||
.layoutId(TangemRowLayoutId.START_TOP),
|
||||
text = exchangeItemUM.title.resolveReference(),
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x2)
|
||||
.layoutId(TangemRowLayoutId.START_BOTTOM),
|
||||
text = exchangeItemUM.subTitle.resolveReference(),
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x2)
|
||||
.layoutId(TangemRowLayoutId.END_TOP),
|
||||
text = exchangeItemUM.volumeInUsd.resolveReference(),
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = getColorByTrustValue(exchangeItemUM.auditLabel.type).copy(alpha = .1f),
|
||||
shape = CircleShape,
|
||||
)
|
||||
.padding(vertical = 2.dp, horizontal = 6.dp)
|
||||
.layoutId(TangemRowLayoutId.END_BOTTOM),
|
||||
text = exchangeItemUM.auditLabel.text.resolveReference(),
|
||||
style = TangemTheme.typography2.captionSemibold11,
|
||||
color = getColorByTrustValue(exchangeItemUM.auditLabel.type),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ExchangeItemRowPlaceholder(modifier: Modifier = Modifier) {
|
||||
TangemRowContainer(
|
||||
modifier = modifier,
|
||||
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 14.dp),
|
||||
) {
|
||||
CircleShimmer(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.layoutId(TangemRowLayoutId.HEAD),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(108.dp)
|
||||
.height(20.dp)
|
||||
.padding(start = 4.dp)
|
||||
.layoutId(TangemRowLayoutId.START_TOP),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(52.dp)
|
||||
.height(16.dp)
|
||||
.padding(start = 4.dp)
|
||||
.layoutId(TangemRowLayoutId.START_BOTTOM),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(106.dp)
|
||||
.height(20.dp)
|
||||
.padding(start = 4.dp)
|
||||
.layoutId(TangemRowLayoutId.END_TOP),
|
||||
)
|
||||
RectangleShimmer(
|
||||
modifier = Modifier
|
||||
.width(52.dp)
|
||||
.height(16.dp)
|
||||
.padding(start = 4.dp)
|
||||
.layoutId(TangemRowLayoutId.END_BOTTOM),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun getColorByTrustValue(type: AuditLabelUM.Type): Color {
|
||||
return when (type) {
|
||||
AuditLabelUM.Type.Prohibition -> TangemTheme.colors2.fill.status.attention
|
||||
AuditLabelUM.Type.Warning -> TangemTheme.colors2.fill.status.warning
|
||||
AuditLabelUM.Type.Permit,
|
||||
AuditLabelUM.Type.Info,
|
||||
AuditLabelUM.Type.General,
|
||||
-> TangemTheme.colors2.fill.status.accent
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Preview(name = "Dark Theme", uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
|
|
@ -175,7 +432,7 @@ private class ExchangesBottomSheetContentProvider : CollectionPreviewParameterPr
|
|||
listOf(
|
||||
ExchangesBottomSheetContent.Loading(exchangesCount = 13),
|
||||
ExchangesBottomSheetContent.Error(onRetryClick = {}),
|
||||
ExchangesBottomSheetContent.Content(
|
||||
ExchangesBottomSheetContent.ContentV1(
|
||||
exchangeItems = List(size = 13) { index ->
|
||||
TokenItemState.Content(
|
||||
id = index.toString(),
|
||||
|
|
|
|||
|
|
@ -1,30 +1,52 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.components
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import com.tangem.core.ui.components.SpacerH
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetType
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheetTitle
|
||||
import com.tangem.core.ui.components.notifications.Notification
|
||||
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||
import com.tangem.core.ui.ds.row.TangemRowContainer
|
||||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
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.impl.R
|
||||
import com.tangem.features.feed.ui.market.detailed.state.InfoBottomSheetContent
|
||||
import dev.jeziellago.compose.markdowntext.MarkdownText
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet as TangemBottomSheetV2
|
||||
|
||||
@Composable
|
||||
internal fun InfoBottomSheet(config: TangemBottomSheetConfig) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
InfoBottomSheetV2(config)
|
||||
} else {
|
||||
InfoBottomSheetV1(config)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoBottomSheetV1(config: TangemBottomSheetConfig) {
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
TangemBottomSheet<InfoBottomSheetContent>(
|
||||
|
|
@ -62,6 +84,89 @@ internal fun InfoBottomSheet(config: TangemBottomSheetConfig) {
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun InfoBottomSheetV2(config: TangemBottomSheetConfig) {
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
TangemBottomSheetV2<InfoBottomSheetContent>(
|
||||
config = config,
|
||||
type = TangemBottomSheetType.Modal,
|
||||
containerColor = TangemTheme.colors2.surface.level3,
|
||||
title = { content ->
|
||||
TangemTopBar(
|
||||
title = content.title,
|
||||
type = TangemTopBarType.BottomSheet,
|
||||
endContent = {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_close_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.primary,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x11)
|
||||
.background(
|
||||
color = TangemTheme.colors2.button.backgroundSecondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.clickableSingle(onClick = config.onDismissRequest)
|
||||
.padding(TangemTheme.dimens2.x2_5),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
content = { content ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = TangemTheme.dimens2.x4),
|
||||
) {
|
||||
MarkdownText(
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens2.x3),
|
||||
markdown = content.body.resolveReference(),
|
||||
disableLinkMovementMethod = true,
|
||||
linkifyMask = 0,
|
||||
syntaxHighlightColor = TangemTheme.colors2.text.neutral.secondary,
|
||||
style = TangemTheme.typography2.bodyRegular16.copy(
|
||||
TangemTheme.colors2.text.neutral.secondary,
|
||||
),
|
||||
)
|
||||
|
||||
if (content.generatedAINotificationUM != null) {
|
||||
TangemRowContainer(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors2.surface.level4,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||
)
|
||||
.clickableSingle(onClick = content.generatedAINotificationUM.onClick),
|
||||
contentPadding = PaddingValues(TangemTheme.dimens2.x3),
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x10)
|
||||
.padding(TangemTheme.dimens2.x1)
|
||||
.layoutId(TangemRowLayoutId.HEAD),
|
||||
tint = TangemTheme.colors2.markers.iconBlue,
|
||||
contentDescription = null,
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_magic_28),
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x1)
|
||||
.layoutId(TangemRowLayoutId.START_TOP),
|
||||
text = stringResourceSafe(R.string.information_generated_with_ai),
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
}
|
||||
}
|
||||
SpacerH(bottomBarHeight)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AdditionalInfoNotification(onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Notification(
|
||||
|
|
|
|||
|
|
@ -1,42 +1,71 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.BlendMode
|
||||
import androidx.compose.ui.graphics.CompositingStrategy
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetType
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheetTitle
|
||||
import com.tangem.core.ui.components.inputrow.inner.DividerContainer
|
||||
import com.tangem.core.ui.decorations.roundedShapeItemDecoration
|
||||
import com.tangem.core.ui.ds.row.TangemRowContainer
|
||||
import com.tangem.core.ui.ds.row.TangemRowLayoutId
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||
import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
||||
import com.tangem.core.ui.extensions.clickableSingle
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.LocalRedesignEnabled
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||
import com.tangem.features.feed.impl.R
|
||||
import com.tangem.features.feed.ui.market.detailed.preview.SecurityScorePreviewData
|
||||
import com.tangem.features.feed.ui.market.detailed.state.SecurityScoreBottomSheetContent
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet as TangemBottomSheetV2
|
||||
|
||||
@Composable
|
||||
internal fun SecurityScoreBottomSheet(config: TangemBottomSheetConfig) {
|
||||
if (LocalRedesignEnabled.current) {
|
||||
SecurityScoreBottomSheetV2(config)
|
||||
} else {
|
||||
SecurityScoreBottomSheetV1(config)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SecurityScoreBottomSheetV1(config: TangemBottomSheetConfig) {
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
TangemBottomSheet<SecurityScoreBottomSheetContent>(
|
||||
|
|
@ -82,6 +111,62 @@ internal fun SecurityScoreBottomSheet(config: TangemBottomSheetConfig) {
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SecurityScoreBottomSheetV2(config: TangemBottomSheetConfig) {
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
TangemBottomSheetV2<SecurityScoreBottomSheetContent>(
|
||||
config = config,
|
||||
type = TangemBottomSheetType.Modal,
|
||||
title = { content ->
|
||||
TangemTopBar(
|
||||
title = content.title,
|
||||
type = TangemTopBarType.BottomSheet,
|
||||
endContent = {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_close_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.graphic.neutral.primary,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x11)
|
||||
.background(
|
||||
color = TangemTheme.colors2.button.backgroundSecondary,
|
||||
shape = CircleShape,
|
||||
)
|
||||
.clickableSingle(onClick = config.onDismissRequest)
|
||||
.padding(TangemTheme.dimens2.x2_5),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
content = { content ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(horizontal = TangemTheme.dimens2.x4),
|
||||
) {
|
||||
Text(
|
||||
text = content.description.resolveReference(),
|
||||
style = TangemTheme.typography2.bodyRegular15.copy(
|
||||
color = TangemTheme.colors2.text.neutral.tertiary,
|
||||
),
|
||||
)
|
||||
SpacerH12()
|
||||
content.providers.fastForEach { provider ->
|
||||
SecurityScoreProviderRowV2(
|
||||
providerUM = provider,
|
||||
onLinkClick = { content.onProviderLinkClick(provider) },
|
||||
)
|
||||
SpacerH(TangemTheme.dimens2.x2)
|
||||
}
|
||||
|
||||
SpacerH16()
|
||||
SpacerH(bottomBarHeight)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SecurityScoreProviderRow(
|
||||
providerUM: SecurityScoreBottomSheetContent.SecurityScoreProviderUM,
|
||||
|
|
@ -145,17 +230,91 @@ private fun SecurityScoreProviderRow(
|
|||
horizontalSpacing = TangemTheme.dimens.spacing3,
|
||||
)
|
||||
|
||||
UrlBlock(providerUM)
|
||||
UrlBlockV1(providerUM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UrlBlock(providerUM: SecurityScoreBottomSheetContent.SecurityScoreProviderUM) {
|
||||
private fun SecurityScoreProviderRowV2(
|
||||
providerUM: SecurityScoreBottomSheetContent.SecurityScoreProviderUM,
|
||||
onLinkClick: () -> Unit,
|
||||
) {
|
||||
TangemRowContainer(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = TangemTheme.colors2.surface.level4,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||
)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = TangemTheme.colors2.border.neutral.primary,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens2.x5),
|
||||
),
|
||||
) {
|
||||
SubcomposeAsyncImage(
|
||||
modifier = Modifier
|
||||
.size(size = TangemTheme.dimens.size40)
|
||||
.clip(TangemTheme.shapes.roundedCorners8)
|
||||
.layoutId(TangemRowLayoutId.HEAD),
|
||||
model = ImageRequest.Builder(context = LocalContext.current)
|
||||
.data(providerUM.iconUrl)
|
||||
.crossfade(enable = true)
|
||||
.allowHardware(false)
|
||||
.build(),
|
||||
loading = { RectangleShimmer(radius = TangemTheme.dimens2.x3) },
|
||||
error = { RectangleShimmer(radius = TangemTheme.dimens2.x3) },
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x2)
|
||||
.layoutId(TangemRowLayoutId.START_TOP),
|
||||
text = providerUM.name,
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
|
||||
providerUM.lastAuditDate?.let { auditDate ->
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x2)
|
||||
.layoutId(TangemRowLayoutId.START_BOTTOM),
|
||||
text = auditDate,
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
)
|
||||
}
|
||||
|
||||
StarBlock(
|
||||
score = providerUM.score,
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x2)
|
||||
.clickableSingle(onClick = onLinkClick)
|
||||
.layoutId(TangemRowLayoutId.END_TOP),
|
||||
)
|
||||
|
||||
UrlBlockV2(
|
||||
modifier = Modifier
|
||||
.padding(start = TangemTheme.dimens2.x2)
|
||||
.clickableSingle(onClick = onLinkClick)
|
||||
.layoutId(TangemRowLayoutId.END_BOTTOM),
|
||||
providerUM = providerUM,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UrlBlockV1(
|
||||
providerUM: SecurityScoreBottomSheetContent.SecurityScoreProviderUM,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val urlData = providerUM.urlData
|
||||
val rootHost = urlData?.rootHost
|
||||
if (urlData != null && rootHost != null) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4),
|
||||
) {
|
||||
Text(
|
||||
|
|
@ -174,10 +333,75 @@ private fun UrlBlock(providerUM: SecurityScoreBottomSheetContent.SecurityScorePr
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UrlBlockV2(
|
||||
providerUM: SecurityScoreBottomSheetContent.SecurityScoreProviderUM,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val urlData = providerUM.urlData
|
||||
val rootHost = urlData?.rootHost
|
||||
if (urlData != null && rootHost != null) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x0_5),
|
||||
) {
|
||||
Text(
|
||||
text = urlData.rootHost,
|
||||
style = TangemTheme.typography2.captionSemibold12,
|
||||
color = TangemTheme.colors2.text.neutral.secondary,
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens2.x4),
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.ic_arrow_top_right_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.markers.iconGray,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
fun StarBlock(score: Float, modifier: Modifier = Modifier) {
|
||||
val grayColor = TangemTheme.colors2.markers.iconGray
|
||||
val percentage = score / 5
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = score.toString(),
|
||||
style = TangemTheme.typography2.bodySemibold16,
|
||||
color = TangemTheme.colors2.text.neutral.primary,
|
||||
)
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.requiredSize(TangemTheme.dimens2.x5)
|
||||
.graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)
|
||||
.drawWithCache {
|
||||
onDrawWithContent {
|
||||
drawContent()
|
||||
drawRect(
|
||||
color = grayColor,
|
||||
topLeft = Offset(x = size.width * percentage, y = 0f),
|
||||
size = Size(size.width * (1 - percentage), size.height),
|
||||
blendMode = BlendMode.SrcIn,
|
||||
)
|
||||
}
|
||||
},
|
||||
imageVector = ImageVector.vectorResource(R.drawable.ic_star_filled_20),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors2.markers.iconBlue,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun SecurityScoreBottomSheetPreview() {
|
||||
private fun SecurityScoreBottomSheetPreviewV1() {
|
||||
TangemThemePreview {
|
||||
SecurityScoreBottomSheet(
|
||||
config = TangemBottomSheetConfig(
|
||||
|
|
@ -187,4 +411,21 @@ private fun SecurityScoreBottomSheetPreview() {
|
|||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun SecurityScoreBottomSheetPreviewV2() {
|
||||
TangemThemePreviewRedesign {
|
||||
CompositionLocalProvider(LocalRedesignEnabled provides true) {
|
||||
SecurityScoreBottomSheet(
|
||||
config = TangemBottomSheetConfig(
|
||||
isShown = true,
|
||||
onDismissRequest = {},
|
||||
content = SecurityScorePreviewData.bottomSheetContent,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.audits.AuditLabelUM
|
||||
import com.tangem.core.ui.ds.image.TangemIconUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
@Immutable
|
||||
internal sealed interface ExchangeItemUM {
|
||||
|
||||
val id: String
|
||||
|
||||
data class Content(
|
||||
override val id: String,
|
||||
val title: TextReference,
|
||||
val subTitle: TextReference,
|
||||
val icon: TangemIconUM,
|
||||
val volumeInUsd: TextReference,
|
||||
val auditLabel: AuditLabelUM,
|
||||
) : ExchangeItemUM
|
||||
|
||||
data class Loading(override val id: String) : ExchangeItemUM
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.features.feed.ui.market.detailed.state
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
|
@ -18,6 +19,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Immutable
|
||||
internal sealed interface ExchangesBottomSheetContent : TangemBottomSheetConfigContent {
|
||||
|
||||
/** Title of bottom sheet. Like, app bar. */
|
||||
|
|
@ -39,6 +41,8 @@ internal sealed interface ExchangesBottomSheetContent : TangemBottomSheetConfigC
|
|||
/** Exchange items */
|
||||
val exchangeItems: ImmutableList<TokenItemState>
|
||||
|
||||
val exchangeItemsV2: ImmutableList<ExchangeItemUM>
|
||||
|
||||
/**
|
||||
* Loading state
|
||||
*
|
||||
|
|
@ -49,6 +53,10 @@ internal sealed interface ExchangesBottomSheetContent : TangemBottomSheetConfigC
|
|||
override val exchangeItems: ImmutableList<TokenItemState>
|
||||
get() = List(size = exchangesCount) { index -> TokenItemState.Loading(id = "loading#$index") }
|
||||
.toImmutableList()
|
||||
|
||||
override val exchangeItemsV2: ImmutableList<ExchangeItemUM>
|
||||
get() = List(size = exchangesCount) { index -> ExchangeItemUM.Loading(id = "loading#$index") }
|
||||
.toImmutableList()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -56,8 +64,14 @@ internal sealed interface ExchangesBottomSheetContent : TangemBottomSheetConfigC
|
|||
*
|
||||
* @property exchangeItems exchanges
|
||||
*/
|
||||
data class Content(
|
||||
data class ContentV1(
|
||||
override val exchangeItems: ImmutableList<TokenItemState>,
|
||||
override val exchangeItemsV2: ImmutableList<ExchangeItemUM> = persistentListOf(),
|
||||
) : ExchangesBottomSheetContent
|
||||
|
||||
data class ContentV2(
|
||||
override val exchangeItemsV2: ImmutableList<ExchangeItemUM>,
|
||||
override val exchangeItems: ImmutableList<TokenItemState> = persistentListOf(),
|
||||
) : ExchangesBottomSheetContent
|
||||
|
||||
/** Error state */
|
||||
|
|
@ -65,6 +79,7 @@ internal sealed interface ExchangesBottomSheetContent : TangemBottomSheetConfigC
|
|||
val onRetryClick: () -> Unit,
|
||||
) : ExchangesBottomSheetContent {
|
||||
override val exchangeItems: ImmutableList<TokenItemState> = persistentListOf()
|
||||
override val exchangeItemsV2: ImmutableList<ExchangeItemUM> = persistentListOf()
|
||||
|
||||
@StringRes
|
||||
val message: Int = R.string.markets_loading_error_title
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import androidx.compose.ui.unit.dp
|
|||
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.ds.TangemPagerIndicator
|
||||
import com.tangem.core.ui.ds.TangemPagerIndicatorColors
|
||||
import com.tangem.core.ui.extensions.conditionalCompose
|
||||
import com.tangem.core.ui.res.*
|
||||
import com.tangem.features.feed.ui.news.details.components.ArticleDetail
|
||||
|
|
@ -105,12 +107,24 @@ private fun Content(state: NewsDetailsUM, background: Color) {
|
|||
)
|
||||
}
|
||||
if (state.articles.size > 1) {
|
||||
PagerIndicator(
|
||||
pagerState = pagerState,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.windowInsetsPadding(WindowInsets.navigationBars),
|
||||
)
|
||||
if (isRedesignEnabled) {
|
||||
TangemPagerIndicator(
|
||||
pagerState = pagerState,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.windowInsetsPadding(WindowInsets.navigationBars),
|
||||
colors = TangemPagerIndicatorColors.copy(
|
||||
overlay = TangemTheme.colors2.tabs.backgroundSecondary.copy(alpha = .1f),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
PagerIndicator(
|
||||
pagerState = pagerState,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.windowInsetsPadding(WindowInsets.navigationBars),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ internal fun RelatedTokensBlock(
|
|||
onItemClick: ((MarketsListItemUM) -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val isRedesignEnabled = LocalRedesignEnabled.current
|
||||
val isVisible = remember(relatedTokensUM) {
|
||||
when (relatedTokensUM) {
|
||||
is RelatedTokensUM.Content -> relatedTokensUM.items.isNotEmpty()
|
||||
|
|
@ -40,7 +41,7 @@ internal fun RelatedTokensBlock(
|
|||
|
||||
Column(modifier = modifier) {
|
||||
SpacerH(40.dp)
|
||||
if (LocalRedesignEnabled.current) {
|
||||
if (isRedesignEnabled) {
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = 8.dp),
|
||||
text = stringResourceSafe(R.string.news_related_tokens),
|
||||
|
|
@ -58,7 +59,11 @@ internal fun RelatedTokensBlock(
|
|||
|
||||
BlockCard(
|
||||
colors = TangemBlockCardColors.copy(
|
||||
containerColor = TangemTheme.colors.background.action,
|
||||
containerColor = if (isRedesignEnabled) {
|
||||
TangemTheme.colors2.surface.level3
|
||||
} else {
|
||||
TangemTheme.colors.background.action
|
||||
},
|
||||
),
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue