Updated on 2026-08-14

This commit is contained in:
Tangem 2026-07-24 16:07:06 +02:00
parent 698f1e6047
commit d84f383cf8
29 changed files with 567 additions and 547 deletions

View file

@ -15,11 +15,11 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.BlurredEdgeTreatment import androidx.compose.ui.draw.BlurredEdgeTreatment
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.innerShadow import androidx.compose.ui.draw.innerShadow
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.shadow.Shadow import androidx.compose.ui.graphics.shadow.Shadow
import androidx.compose.ui.layout.layoutId import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.buildAnnotatedString
@ -43,6 +43,8 @@ import com.tangem.core.ui.ds.row.TangemRowLayoutId
import com.tangem.core.ui.extensions.* import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.core.ui.res.generated.icons.Icons
import com.tangem.core.ui.res.generated.icons.ic_chevron_right_24
import com.tangem.core.ui.test.TokenDetailsScreenTestTags import com.tangem.core.ui.test.TokenDetailsScreenTestTags
import dev.chrisbanes.haze.HazeStyle import dev.chrisbanes.haze.HazeStyle
import dev.chrisbanes.haze.HazeTint import dev.chrisbanes.haze.HazeTint
@ -55,8 +57,8 @@ private const val GLOW_ALPHA = 0.7f
private val BorderWidth = 1.dp private val BorderWidth = 1.dp
private val InnerShadowBlur = 20.dp private val InnerShadowBlur = 20.dp
private val ShimmerSubtitleWidth = 78.dp private val ShimmerSubtitleWidth = 78.dp
private val LoaderSize = 12.dp private val LoaderSize = 20.dp
private val LoaderStrokeWidth = 1.5.dp private val LoaderStrokeWidth = 2.dp
@Composable @Composable
fun EarnBlock(state: EarnBlockUM, modifier: Modifier = Modifier) { fun EarnBlock(state: EarnBlockUM, modifier: Modifier = Modifier) {
@ -69,31 +71,31 @@ fun EarnBlock(state: EarnBlockUM, modifier: Modifier = Modifier) {
@Composable @Composable
private fun EarnBlockLoading(modifier: Modifier = Modifier) { private fun EarnBlockLoading(modifier: Modifier = Modifier) {
val shape = RoundedCornerShape(TangemTheme.dimens2.x5) val shape = RoundedCornerShape(24.dp)
TangemRowContainer( TangemRowContainer(
modifier = modifier modifier = modifier
.clip(shape) .clip(shape)
.background(TangemTheme.colors2.surface.level3) .background(TangemTheme.colors3.bg.secondary)
.border(width = BorderWidth, color = TangemTheme.colors2.border.neutral.primary, shape = shape), .border(width = BorderWidth, color = TangemTheme.colors3.border.primary, shape = shape),
contentPadding = PaddingValues(all = TangemTheme.dimens2.x3), contentPadding = PaddingValues(all = 16.dp),
content = { content = {
CircleShimmer( CircleShimmer(
modifier = Modifier modifier = Modifier
.layoutId(TangemRowLayoutId.HEAD) .layoutId(TangemRowLayoutId.HEAD)
.padding(end = TangemTheme.dimens2.x3) .padding(end = 12.dp)
.size(TangemTheme.dimens2.x10), .size(40.dp),
) )
RectangleShimmer( RectangleShimmer(
modifier = Modifier modifier = Modifier
.layoutId(TangemRowLayoutId.START_TOP) .layoutId(TangemRowLayoutId.START_TOP)
.size(width = ShimmerSubtitleWidth, height = TangemTheme.dimens2.x4), .size(width = ShimmerSubtitleWidth, height = 16.dp),
radius = TangemTheme.dimens2.x2, radius = 8.dp,
) )
RectangleShimmer( RectangleShimmer(
modifier = Modifier modifier = Modifier
.layoutId(TangemRowLayoutId.START_BOTTOM) .layoutId(TangemRowLayoutId.START_BOTTOM)
.size(width = TangemTheme.dimens2.x16, height = TangemTheme.dimens2.x5), .size(width = 64.dp, height = 20.dp),
radius = TangemTheme.dimens2.x2, radius = 8.dp,
) )
}, },
) )
@ -101,7 +103,7 @@ private fun EarnBlockLoading(modifier: Modifier = Modifier) {
@Composable @Composable
private fun EarnBlockContent(state: EarnBlockUM.Content, modifier: Modifier = Modifier) { private fun EarnBlockContent(state: EarnBlockUM.Content, modifier: Modifier = Modifier) {
val shape = RoundedCornerShape(TangemTheme.dimens2.x5) val shape = RoundedCornerShape(24.dp)
val clickModifier = state.onClick?.let { Modifier.clickable(onClick = it) } ?: Modifier val clickModifier = state.onClick?.let { Modifier.clickable(onClick = it) } ?: Modifier
@ -109,14 +111,14 @@ private fun EarnBlockContent(state: EarnBlockUM.Content, modifier: Modifier = Mo
modifier = modifier modifier = modifier
.clip(shape) .clip(shape)
.then(clickModifier.backgroundModifier(state.type, state.backgroundUM, shape)), .then(clickModifier.backgroundModifier(state.type, state.backgroundUM, shape)),
contentPadding = PaddingValues(all = TangemTheme.dimens2.x4), contentPadding = PaddingValues(all = 16.dp),
content = { content = {
EarnBlockIcon( EarnBlockIcon(
type = state.type, type = state.type,
iconUM = state.iconUM, iconUM = state.iconUM,
modifier = Modifier modifier = Modifier
.layoutId(TangemRowLayoutId.HEAD) .layoutId(TangemRowLayoutId.HEAD)
.padding(end = TangemTheme.dimens2.x3), .padding(end = 12.dp),
) )
EarnBlockTitle( EarnBlockTitle(
@ -124,12 +126,12 @@ private fun EarnBlockContent(state: EarnBlockUM.Content, modifier: Modifier = Mo
type = state.type, type = state.type,
modifier = Modifier modifier = Modifier
.layoutId(TangemRowLayoutId.START_TOP) .layoutId(TangemRowLayoutId.START_TOP)
.padding(end = TangemTheme.dimens2.x2), .padding(end = 8.dp),
) )
val subtitleModifier = Modifier val subtitleModifier = Modifier
.layoutId(TangemRowLayoutId.START_BOTTOM) .layoutId(TangemRowLayoutId.START_BOTTOM)
.padding(end = TangemTheme.dimens2.x2) .padding(end = 8.dp)
when (val subtitle = state.subtitleUM) { when (val subtitle = state.subtitleUM) {
is EarnBlockUM.SubtitleUM.Text -> EarnBlockSubtitle( is EarnBlockUM.SubtitleUM.Text -> EarnBlockSubtitle(
subtitle = subtitle, subtitle = subtitle,
@ -151,37 +153,37 @@ private fun EarnBlockContent(state: EarnBlockUM.Content, modifier: Modifier = Mo
@Composable @Composable
private fun EarnBlockPromo(state: EarnBlockUM.Promo, modifier: Modifier = Modifier) { private fun EarnBlockPromo(state: EarnBlockUM.Promo, modifier: Modifier = Modifier) {
val shape = RoundedCornerShape(TangemTheme.dimens2.x5) val shape = RoundedCornerShape(24.dp)
Column( Column(
modifier = modifier modifier = modifier
.clip(shape) .clip(shape)
.backgroundModifier(state.type, state.backgroundUM, shape) .backgroundModifier(state.type, state.backgroundUM, shape)
.padding(all = TangemTheme.dimens2.x4), .padding(all = 16.dp),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x3), verticalArrangement = Arrangement.spacedBy(12.dp),
) { ) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
EarnBlockIcon( EarnBlockIcon(
type = state.type, type = state.type,
iconUM = state.iconUM, iconUM = state.iconUM,
modifier = Modifier.padding(end = TangemTheme.dimens2.x3), modifier = Modifier.padding(end = 12.dp),
) )
Column( Column(
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1), verticalArrangement = Arrangement.spacedBy(4.dp),
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
) { ) {
Text( Text(
text = state.title.resolveAnnotatedReference(), text = state.title.resolveAnnotatedReference(),
style = TangemTheme.typography2.bodyMedium16, style = TangemTheme.typography3.body.medium,
color = TangemTheme.colors2.text.neutral.primary, color = TangemTheme.colors3.text.primary,
) )
Text( Text(
text = state.subtitle.resolveReference(), text = state.subtitle.resolveReference(),
style = TangemTheme.typography2.captionMedium12, style = TangemTheme.typography3.caption.medium,
color = state.type.accentText(), color = state.type.accentText(),
) )
} }
} }
Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2)) { Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
EarnBlockPromoButton( EarnBlockPromoButton(
text = resourceReference(CoreResR.string.common_learn_more), text = resourceReference(CoreResR.string.common_learn_more),
type = TangemButtonType.Secondary, type = TangemButtonType.Secondary,
@ -225,8 +227,8 @@ private fun Modifier.backgroundModifier(
): Modifier { ): Modifier {
return when (backgroundUM) { return when (backgroundUM) {
is EarnBlockUM.BackgroundUM.Surface -> this is EarnBlockUM.BackgroundUM.Surface -> this
.background(TangemTheme.colors2.surface.level3) .background(TangemTheme.colors3.bg.secondary)
.border(width = BorderWidth, color = TangemTheme.colors2.border.neutral.primary, shape = shape) .border(width = BorderWidth, color = TangemTheme.colors3.border.primary, shape = shape)
is EarnBlockUM.BackgroundUM.AccentSoft -> tintedBackground(type.accentSoftTint(), shape) is EarnBlockUM.BackgroundUM.AccentSoft -> tintedBackground(type.accentSoftTint(), shape)
is EarnBlockUM.BackgroundUM.AccentStrong -> tintedBackground(type.accentStrongTint(), shape) is EarnBlockUM.BackgroundUM.AccentStrong -> tintedBackground(type.accentStrongTint(), shape)
} }
@ -260,77 +262,84 @@ private fun EarnBlockTrailing(type: Type, trailingUM: EarnBlockUM.TrailingUM?, o
modifier = Modifier.layoutId(TangemRowLayoutId.TAIL), modifier = Modifier.layoutId(TangemRowLayoutId.TAIL),
) )
} }
is EarnBlockUM.TrailingUM.Balance -> { is EarnBlockUM.TrailingUM.Balance -> EarnBlockBalance(type = type, trailingUM = trailingUM)
val fiatModifier = Modifier.layoutId(TangemRowLayoutId.END_TOP).let { is EarnBlockUM.TrailingUM.Chevron -> {
if (type == Type.Staking) it.testTag(TokenDetailsScreenTestTags.STAKING_FIAT_AMOUNT) else it TangemIcon(
} tangemIconUM = TangemIconUM.Icon(
val cryptoModifier = Modifier.layoutId(TangemRowLayoutId.END_BOTTOM).let { imageVector = Icons.ic_chevron_right_24,
if (type == Type.Staking) it.testTag(TokenDetailsScreenTestTags.STAKING_TOKEN_AMOUNT) else it tintReference = { TangemTheme.colors3.icon.secondary },
} ),
Text( modifier = Modifier
text = trailingUM.fiatValue.orMaskWithStars(trailingUM.isBalanceHidden).resolveAnnotatedReference(), .layoutId(TangemRowLayoutId.TAIL)
style = TangemTheme.typography2.bodySemibold16, .size(24.dp),
color = TangemTheme.colors2.text.neutral.primary,
modifier = fiatModifier,
) )
Text( }
text = trailingUM.cryptoValue.orMaskWithStars(trailingUM.isBalanceHidden).resolveReference(), is EarnBlockUM.TrailingUM.StatusIcon -> {
style = TangemTheme.typography2.captionMedium12, TangemIcon(
color = TangemTheme.colors2.text.neutral.secondary, tangemIconUM = TangemIconUM.Icon(
modifier = cryptoModifier, iconRes = trailingUM.tone.iconRes(),
tintReference = { trailingUM.tone.tint() },
),
modifier = Modifier
.layoutId(TangemRowLayoutId.TAIL)
.size(24.dp)
.testTag(TokenDetailsScreenTestTags.EARN_BLOCK_TITLE_ICON),
)
}
is EarnBlockUM.TrailingUM.Loader -> {
CircularProgressIndicator(
color = trailingUM.tone.color(),
strokeWidth = LoaderStrokeWidth,
strokeCap = StrokeCap.Round,
modifier = Modifier
.layoutId(TangemRowLayoutId.TAIL)
.size(LoaderSize),
) )
} }
null -> Unit null -> Unit
} }
} }
@Composable
private fun EarnBlockBalance(type: Type, trailingUM: EarnBlockUM.TrailingUM.Balance) {
val fiatModifier = Modifier.layoutId(TangemRowLayoutId.END_TOP).let {
if (type == Type.Staking) it.testTag(TokenDetailsScreenTestTags.STAKING_FIAT_AMOUNT) else it
}
val cryptoModifier = Modifier.layoutId(TangemRowLayoutId.END_BOTTOM).let {
if (type == Type.Staking) it.testTag(TokenDetailsScreenTestTags.STAKING_TOKEN_AMOUNT) else it
}
Text(
text = trailingUM.fiatValue.orMaskWithStars(trailingUM.isBalanceHidden).resolveAnnotatedReference(),
style = TangemTheme.typography3.body.medium,
color = TangemTheme.colors3.text.primary,
modifier = fiatModifier,
)
Text(
text = trailingUM.cryptoValue.orMaskWithStars(trailingUM.isBalanceHidden).resolveReference(),
style = TangemTheme.typography3.caption.medium,
color = TangemTheme.colors3.text.secondary,
modifier = cryptoModifier,
)
}
@Composable @Composable
private fun EarnBlockTitle(titleUM: EarnBlockUM.TitleUM, type: Type, modifier: Modifier = Modifier) { private fun EarnBlockTitle(titleUM: EarnBlockUM.TitleUM, type: Type, modifier: Modifier = Modifier) {
val titleText: @Composable () -> Unit = { Text(
Text( text = titleUM.text.resolveReference(),
text = titleUM.text.resolveReference(), style = titleUM.style.textStyle,
style = titleUM.style.textStyle, color = titleUM.tone.color(type),
color = titleUM.tone.color(type), modifier = modifier,
) )
}
val icon = titleUM.iconUM
if (icon == null) {
Box(modifier = modifier) { titleText() }
return
}
Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) {
titleText()
Spacer(modifier = Modifier.width(TangemTheme.dimens2.x1))
TangemIcon(
tangemIconUM = TangemIconUM.Icon(
iconRes = icon.tone.iconRes(),
tintReference = { icon.tone.tint() },
),
modifier = Modifier
.size(TangemTheme.dimens2.x4)
.testTag(TokenDetailsScreenTestTags.EARN_BLOCK_TITLE_ICON),
)
}
} }
@Composable @Composable
private fun EarnBlockSubtitle(subtitle: EarnBlockUM.SubtitleUM.Text, type: Type, modifier: Modifier = Modifier) { private fun EarnBlockSubtitle(subtitle: EarnBlockUM.SubtitleUM.Text, type: Type, modifier: Modifier = Modifier) {
val textStyle = subtitle.style.textStyle Text(
val textColor = subtitle.tone.color(type) text = subtitle.text.resolveReference(),
if (subtitle.loader == null) { style = subtitle.style.textStyle,
Text(text = subtitle.text.resolveReference(), style = textStyle, color = textColor, modifier = modifier) color = subtitle.tone.color(type),
return modifier = modifier,
} )
Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) {
Text(text = subtitle.text.resolveReference(), style = textStyle, color = textColor)
Spacer(modifier = Modifier.width(3.dp))
CircularProgressIndicator(
color = subtitle.loader.tone.color(),
strokeWidth = LoaderStrokeWidth,
strokeCap = StrokeCap.Round,
modifier = Modifier.size(LoaderSize),
)
}
} }
@Composable @Composable
@ -351,7 +360,7 @@ private fun EarnBlockAccentedSubtitle(
} }
}, },
style = subtitle.style.textStyle, style = subtitle.style.textStyle,
color = TangemTheme.colors2.text.neutral.tertiary, color = TangemTheme.colors3.text.secondary,
modifier = modifier, modifier = modifier,
) )
} }
@ -360,32 +369,40 @@ private fun EarnBlockAccentedSubtitle(
private fun EarnBlockIcon(type: Type, iconUM: EarnBlockUM.IconUM, modifier: Modifier = Modifier) { private fun EarnBlockIcon(type: Type, iconUM: EarnBlockUM.IconUM, modifier: Modifier = Modifier) {
Box( Box(
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
modifier = modifier.size(TangemTheme.dimens2.x10), modifier = modifier.size(40.dp),
) { ) {
if (iconUM is EarnBlockUM.IconUM.Glowing) { val glowColor = when (iconUM) {
val glowShape = RoundedCornerShape(percent = 50) is EarnBlockUM.IconUM.Glowing -> iconUM.tone.glowColor(type)
is EarnBlockUM.IconUM.Plain -> null
}
if (glowColor != null) {
Box( Box(
modifier = Modifier modifier = Modifier
.size(TangemTheme.dimens2.x6) .size(24.dp)
.hazeForegroundEffectTangem( .hazeForegroundEffectTangem(
style = HazeStyle( style = HazeStyle(
backgroundColor = Color.Transparent, backgroundColor = Color.Transparent,
tint = HazeTint(Color.Transparent), tint = HazeTint(Color.Transparent),
blurRadius = TangemTheme.dimens2.x4, blurRadius = 16.dp,
), ),
) { ) {
blurredEdgeTreatment = BlurredEdgeTreatment.Unbounded blurredEdgeTreatment = BlurredEdgeTreatment.Unbounded
} }
.background(color = type.accentGlow().copy(alpha = GLOW_ALPHA), shape = glowShape), .background(color = glowColor.copy(alpha = GLOW_ALPHA), shape = RoundedCornerShape(percent = 50)),
) )
} }
val isWarningTone = (iconUM as? EarnBlockUM.IconUM.Glowing)?.tone == EarnBlockUM.IconUM.Tone.Warning
val iconRes = when (iconUM) { val iconRes = when (iconUM) {
is EarnBlockUM.IconUM.Glowing -> iconUM.iconRes is EarnBlockUM.IconUM.Glowing -> iconUM.iconRes
is EarnBlockUM.IconUM.Plain -> iconUM.iconRes is EarnBlockUM.IconUM.Plain -> iconUM.iconRes
} }
TangemIcon( TangemIcon(
tangemIconUM = TangemIconUM.Image(imageRes = iconRes), tangemIconUM = if (isWarningTone) {
modifier = Modifier.size(TangemTheme.dimens2.x10), TangemIconUM.Icon(iconRes = iconRes, tintReference = { TangemTheme.colors3.icon.status.warning })
} else {
TangemIconUM.Image(imageRes = iconRes)
},
modifier = Modifier.size(40.dp),
) )
} }
} }
@ -394,29 +411,36 @@ private fun EarnBlockIcon(type: Type, iconUM: EarnBlockUM.IconUM, modifier: Modi
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun Type.accentText(): Color = when (this) { private fun Type.accentText(): Color = when (this) {
Type.Staking -> TangemTheme.colors2.text.status.accent Type.Staking -> TangemTheme.colors3.text.brand
Type.YieldSupply -> TangemTheme.colors2.text.status.positive Type.YieldSupply -> TangemTheme.colors3.text.accent.green
}
@Composable
@ReadOnlyComposable
private fun EarnBlockUM.IconUM.Tone.glowColor(type: Type): Color = when (this) {
EarnBlockUM.IconUM.Tone.Accent -> type.accentGlow()
EarnBlockUM.IconUM.Tone.Warning -> TangemTheme.colors3.icon.status.warning
} }
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun Type.accentGlow(): Color = when (this) { private fun Type.accentGlow(): Color = when (this) {
Type.Staking -> TangemTheme.colors2.border.status.accent Type.Staking -> TangemTheme.colors3.text.brand
Type.YieldSupply -> TangemTheme.colors2.text.status.positive Type.YieldSupply -> TangemTheme.colors3.text.accent.green
} }
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun Type.accentSoftTint(): Color = when (this) { private fun Type.accentSoftTint(): Color = when (this) {
Type.Staking -> TangemTheme.colors2.markers.backgroundTintedBlue Type.Staking -> TangemTheme.colors3.text.brand
Type.YieldSupply -> TangemTheme.colors2.markers.backgroundTintedGreen Type.YieldSupply -> TangemTheme.colors3.text.accent.green
} }
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun Type.accentStrongTint(): Color = when (this) { private fun Type.accentStrongTint(): Color = when (this) {
Type.Staking -> TangemTheme.colors2.text.status.accent Type.Staking -> TangemTheme.colors3.text.brand
Type.YieldSupply -> TangemTheme.colors2.text.status.positive Type.YieldSupply -> TangemTheme.colors3.text.accent.green
} }
private fun EarnBlockUM.TrailingUM.Button.Style.buttonType(type: Type): TangemButtonType = when (this) { private fun EarnBlockUM.TrailingUM.Button.Style.buttonType(type: Type): TangemButtonType = when (this) {
@ -430,53 +454,53 @@ private fun EarnBlockUM.TrailingUM.Button.Style.buttonType(type: Type): TangemBu
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun EarnBlockUM.TitleUM.Tone.color(type: Type): Color = when (this) { private fun EarnBlockUM.TitleUM.Tone.color(type: Type): Color = when (this) {
EarnBlockUM.TitleUM.Tone.Primary -> TangemTheme.colors2.text.neutral.primary EarnBlockUM.TitleUM.Tone.Primary -> TangemTheme.colors3.text.primary
EarnBlockUM.TitleUM.Tone.Secondary -> TangemTheme.colors2.text.neutral.secondary EarnBlockUM.TitleUM.Tone.Secondary -> TangemTheme.colors3.text.secondary
EarnBlockUM.TitleUM.Tone.Disabled -> TangemTheme.colors2.text.neutral.tertiary EarnBlockUM.TitleUM.Tone.Disabled -> TangemTheme.colors3.text.tertiary
EarnBlockUM.TitleUM.Tone.Accent -> type.accentText() EarnBlockUM.TitleUM.Tone.Accent -> type.accentText()
} }
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun EarnBlockUM.SubtitleUM.Tone.color(type: Type): Color = when (this) { private fun EarnBlockUM.SubtitleUM.Tone.color(type: Type): Color = when (this) {
EarnBlockUM.SubtitleUM.Tone.Primary -> TangemTheme.colors2.text.neutral.primary EarnBlockUM.SubtitleUM.Tone.Primary -> TangemTheme.colors3.text.primary
EarnBlockUM.SubtitleUM.Tone.Disabled -> TangemTheme.colors2.text.neutral.tertiary EarnBlockUM.SubtitleUM.Tone.Disabled -> TangemTheme.colors3.text.tertiary
EarnBlockUM.SubtitleUM.Tone.Accent -> type.accentText() EarnBlockUM.SubtitleUM.Tone.Accent -> type.accentText()
} }
private fun EarnBlockUM.TitleUM.IconTone.iconRes(): Int = when (this) { private fun EarnBlockUM.TrailingUM.StatusIcon.Tone.iconRes(): Int = when (this) {
EarnBlockUM.TitleUM.IconTone.Warning -> R.drawable.ic_attention_default_24 EarnBlockUM.TrailingUM.StatusIcon.Tone.Warning -> R.drawable.ic_attention_default_24
EarnBlockUM.TitleUM.IconTone.Info -> R.drawable.ic_alert_circle_24 EarnBlockUM.TrailingUM.StatusIcon.Tone.Info -> R.drawable.ic_alert_circle_24
} }
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun EarnBlockUM.TitleUM.IconTone.tint(): Color = when (this) { private fun EarnBlockUM.TrailingUM.StatusIcon.Tone.tint(): Color = when (this) {
EarnBlockUM.TitleUM.IconTone.Warning -> TangemTheme.colors2.graphic.status.attention EarnBlockUM.TrailingUM.StatusIcon.Tone.Warning -> TangemTheme.colors3.icon.status.warning
EarnBlockUM.TitleUM.IconTone.Info -> TangemTheme.colors2.graphic.neutral.secondary EarnBlockUM.TrailingUM.StatusIcon.Tone.Info -> TangemTheme.colors3.icon.secondary
} }
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
private fun EarnBlockUM.SubtitleUM.LoaderTone.color(): Color = when (this) { private fun EarnBlockUM.TrailingUM.Loader.LoaderTone.color(): Color = when (this) {
EarnBlockUM.SubtitleUM.LoaderTone.Positive -> TangemTheme.colors2.text.status.positive EarnBlockUM.TrailingUM.Loader.LoaderTone.Positive -> TangemTheme.colors3.icon.accent.green
EarnBlockUM.SubtitleUM.LoaderTone.Muted -> TangemTheme.colors2.graphic.neutral.tertiaryConstant EarnBlockUM.TrailingUM.Loader.LoaderTone.Muted -> TangemTheme.colors3.icon.tertiary
} }
private val EarnBlockUM.TitleUM.Style.textStyle: TextStyle private val EarnBlockUM.TitleUM.Style.textStyle: TextStyle
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
get() = when (this) { get() = when (this) {
EarnBlockUM.TitleUM.Style.Large -> TangemTheme.typography2.bodyMedium16 EarnBlockUM.TitleUM.Style.Large -> TangemTheme.typography3.body.medium
EarnBlockUM.TitleUM.Style.Small -> TangemTheme.typography2.captionMedium12 EarnBlockUM.TitleUM.Style.Small -> TangemTheme.typography3.caption.medium
} }
private val EarnBlockUM.SubtitleUM.Style.textStyle: TextStyle private val EarnBlockUM.SubtitleUM.Style.textStyle: TextStyle
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
get() = when (this) { get() = when (this) {
EarnBlockUM.SubtitleUM.Style.Large -> TangemTheme.typography2.bodyMedium16 EarnBlockUM.SubtitleUM.Style.Large -> TangemTheme.typography3.body.medium
EarnBlockUM.SubtitleUM.Style.Small -> TangemTheme.typography2.captionMedium12 EarnBlockUM.SubtitleUM.Style.Small -> TangemTheme.typography3.caption.medium
} }
// endregion // endregion
@ -488,7 +512,7 @@ private fun EarnBlock_Staking_Preview(@PreviewParameter(EarnBlockStakingPreviewP
TangemThemePreviewRedesign { TangemThemePreviewRedesign {
EarnBlock( EarnBlock(
state = state, state = state,
modifier = Modifier.padding(TangemTheme.dimens2.x4), modifier = Modifier.padding(16.dp),
) )
} }
} }
@ -502,7 +526,7 @@ private fun EarnBlock_YieldSupply_Preview(
TangemThemePreviewRedesign { TangemThemePreviewRedesign {
EarnBlock( EarnBlock(
state = state, state = state,
modifier = Modifier.padding(TangemTheme.dimens2.x4), modifier = Modifier.padding(16.dp),
) )
} }
} }
@ -612,7 +636,7 @@ private class EarnBlockYieldSupplyPreviewProvider : CollectionPreviewParameterPr
), ),
onClick = {}, onClick = {},
), ),
// Content — yield enabled, "Details" button // Content — yield enabled, chevron
EarnBlockUM.Content( EarnBlockUM.Content(
type = Type.YieldSupply, type = Type.YieldSupply,
backgroundUM = EarnBlockUM.BackgroundUM.Surface, backgroundUM = EarnBlockUM.BackgroundUM.Surface,
@ -630,13 +654,10 @@ private class EarnBlockYieldSupplyPreviewProvider : CollectionPreviewParameterPr
style = EarnBlockUM.SubtitleUM.Style.Large, style = EarnBlockUM.SubtitleUM.Style.Large,
tone = EarnBlockUM.SubtitleUM.Tone.Accent, tone = EarnBlockUM.SubtitleUM.Tone.Accent,
), ),
trailingUM = EarnBlockUM.TrailingUM.Button( trailingUM = EarnBlockUM.TrailingUM.Chevron,
text = resourceReference(CoreResR.string.details_title),
style = EarnBlockUM.TrailingUM.Button.Style.Secondary,
),
onClick = {}, onClick = {},
), ),
// Content with Warning title icon // Content with Warning status icon in trailing
EarnBlockUM.Content( EarnBlockUM.Content(
type = Type.YieldSupply, type = Type.YieldSupply,
backgroundUM = EarnBlockUM.BackgroundUM.Surface, backgroundUM = EarnBlockUM.BackgroundUM.Surface,
@ -645,7 +666,6 @@ private class EarnBlockYieldSupplyPreviewProvider : CollectionPreviewParameterPr
text = resourceReference(CoreResR.string.common_yield_mode), text = resourceReference(CoreResR.string.common_yield_mode),
style = EarnBlockUM.TitleUM.Style.Small, style = EarnBlockUM.TitleUM.Style.Small,
tone = EarnBlockUM.TitleUM.Tone.Primary, tone = EarnBlockUM.TitleUM.Tone.Primary,
iconUM = EarnBlockUM.TitleUM.IconUM(tone = EarnBlockUM.TitleUM.IconTone.Warning),
), ),
subtitleUM = EarnBlockUM.SubtitleUM.Text( subtitleUM = EarnBlockUM.SubtitleUM.Text(
text = resourceReference( text = resourceReference(
@ -655,13 +675,12 @@ private class EarnBlockYieldSupplyPreviewProvider : CollectionPreviewParameterPr
style = EarnBlockUM.SubtitleUM.Style.Large, style = EarnBlockUM.SubtitleUM.Style.Large,
tone = EarnBlockUM.SubtitleUM.Tone.Accent, tone = EarnBlockUM.SubtitleUM.Tone.Accent,
), ),
trailingUM = EarnBlockUM.TrailingUM.Button( trailingUM = EarnBlockUM.TrailingUM.StatusIcon(
text = resourceReference(CoreResR.string.details_title), tone = EarnBlockUM.TrailingUM.StatusIcon.Tone.Warning,
style = EarnBlockUM.TrailingUM.Button.Style.Secondary,
), ),
onClick = {}, onClick = {},
), ),
// Content with Info title icon // Content with Info status icon in trailing
EarnBlockUM.Content( EarnBlockUM.Content(
type = Type.YieldSupply, type = Type.YieldSupply,
backgroundUM = EarnBlockUM.BackgroundUM.Surface, backgroundUM = EarnBlockUM.BackgroundUM.Surface,
@ -670,7 +689,6 @@ private class EarnBlockYieldSupplyPreviewProvider : CollectionPreviewParameterPr
text = resourceReference(CoreResR.string.common_yield_mode), text = resourceReference(CoreResR.string.common_yield_mode),
style = EarnBlockUM.TitleUM.Style.Small, style = EarnBlockUM.TitleUM.Style.Small,
tone = EarnBlockUM.TitleUM.Tone.Primary, tone = EarnBlockUM.TitleUM.Tone.Primary,
iconUM = EarnBlockUM.TitleUM.IconUM(tone = EarnBlockUM.TitleUM.IconTone.Info),
), ),
subtitleUM = EarnBlockUM.SubtitleUM.Text( subtitleUM = EarnBlockUM.SubtitleUM.Text(
text = resourceReference( text = resourceReference(
@ -680,47 +698,51 @@ private class EarnBlockYieldSupplyPreviewProvider : CollectionPreviewParameterPr
style = EarnBlockUM.SubtitleUM.Style.Large, style = EarnBlockUM.SubtitleUM.Style.Large,
tone = EarnBlockUM.SubtitleUM.Tone.Accent, tone = EarnBlockUM.SubtitleUM.Tone.Accent,
), ),
trailingUM = EarnBlockUM.TrailingUM.Button( trailingUM = EarnBlockUM.TrailingUM.StatusIcon(
text = resourceReference(CoreResR.string.details_title), tone = EarnBlockUM.TrailingUM.StatusIcon.Tone.Info,
style = EarnBlockUM.TrailingUM.Button.Style.Secondary,
), ),
onClick = {}, onClick = {},
), ),
// Processing.Enter — enabling, no trailing // Processing.Enter — enabling, single-line title, loader in trailing
EarnBlockUM.Content( EarnBlockUM.Content(
type = Type.YieldSupply, type = Type.YieldSupply,
backgroundUM = EarnBlockUM.BackgroundUM.Surface, backgroundUM = EarnBlockUM.BackgroundUM.Surface,
iconUM = EarnBlockUM.IconUM.Glowing(iconRes = R.drawable.ic_yield_40), iconUM = EarnBlockUM.IconUM.Glowing(iconRes = R.drawable.ic_yield_40),
titleUM = EarnBlockUM.TitleUM( titleUM = EarnBlockUM.TitleUM(
text = resourceReference(CoreResR.string.common_yield_mode), text = combinedReference(
resourceReference(CoreResR.string.common_yield_mode),
stringReference(" "),
resourceReference(CoreResR.string.common_enabling),
),
style = EarnBlockUM.TitleUM.Style.Small, style = EarnBlockUM.TitleUM.Style.Small,
tone = EarnBlockUM.TitleUM.Tone.Primary, tone = EarnBlockUM.TitleUM.Tone.Primary,
), ),
subtitleUM = EarnBlockUM.SubtitleUM.Text( subtitleUM = null,
text = resourceReference(CoreResR.string.common_enabling), trailingUM = EarnBlockUM.TrailingUM.Loader(
style = EarnBlockUM.SubtitleUM.Style.Large, tone = EarnBlockUM.TrailingUM.Loader.LoaderTone.Positive,
tone = EarnBlockUM.SubtitleUM.Tone.Accent,
loader = EarnBlockUM.SubtitleUM.Loader(tone = EarnBlockUM.SubtitleUM.LoaderTone.Positive),
), ),
trailingUM = null,
), ),
// Processing.Exit — disabling, no trailing, plain icon // Processing.Exit — disabling, single-line title, warning icon, loader in trailing
EarnBlockUM.Content( EarnBlockUM.Content(
type = Type.YieldSupply, type = Type.YieldSupply,
backgroundUM = EarnBlockUM.BackgroundUM.Surface, backgroundUM = EarnBlockUM.BackgroundUM.Surface,
iconUM = EarnBlockUM.IconUM.Plain(iconRes = R.drawable.ic_yield_disabling_40), iconUM = EarnBlockUM.IconUM.Glowing(
iconRes = R.drawable.ic_yield_40,
tone = EarnBlockUM.IconUM.Tone.Warning,
),
titleUM = EarnBlockUM.TitleUM( titleUM = EarnBlockUM.TitleUM(
text = resourceReference(CoreResR.string.common_yield_mode), text = combinedReference(
resourceReference(CoreResR.string.common_yield_mode),
stringReference(" "),
resourceReference(CoreResR.string.common_disabling),
),
style = EarnBlockUM.TitleUM.Style.Small, style = EarnBlockUM.TitleUM.Style.Small,
tone = EarnBlockUM.TitleUM.Tone.Primary, tone = EarnBlockUM.TitleUM.Tone.Primary,
), ),
subtitleUM = EarnBlockUM.SubtitleUM.Text( subtitleUM = null,
text = resourceReference(CoreResR.string.common_disabling), trailingUM = EarnBlockUM.TrailingUM.Loader(
style = EarnBlockUM.SubtitleUM.Style.Large, tone = EarnBlockUM.TrailingUM.Loader.LoaderTone.Muted,
tone = EarnBlockUM.SubtitleUM.Tone.Disabled,
loader = EarnBlockUM.SubtitleUM.Loader(tone = EarnBlockUM.SubtitleUM.LoaderTone.Muted),
), ),
trailingUM = null,
), ),
), ),
) )

View file

@ -45,8 +45,11 @@ sealed interface EarnBlockUM {
@Immutable @Immutable
sealed interface IconUM { sealed interface IconUM {
data class Glowing(@DrawableRes val iconRes: Int) : IconUM data class Glowing(@DrawableRes val iconRes: Int, val tone: Tone = Tone.Accent) : IconUM
data class Plain(@DrawableRes val iconRes: Int) : IconUM data class Plain(@DrawableRes val iconRes: Int) : IconUM
/** Accent — glow/tint follow the block [Type]; Warning — yellow icon tint and glow. */
enum class Tone { Accent, Warning }
} }
@Immutable @Immutable
@ -54,13 +57,9 @@ sealed interface EarnBlockUM {
val text: TextReference, val text: TextReference,
val style: Style, val style: Style,
val tone: Tone, val tone: Tone,
val iconUM: IconUM? = null,
) { ) {
enum class Style { Large, Small } enum class Style { Large, Small }
enum class Tone { Primary, Secondary, Disabled, Accent } enum class Tone { Primary, Secondary, Disabled, Accent }
data class IconUM(val tone: IconTone)
enum class IconTone { Warning, Info }
} }
@Immutable @Immutable
@ -69,7 +68,6 @@ sealed interface EarnBlockUM {
val text: TextReference, val text: TextReference,
val style: Style, val style: Style,
val tone: Tone, val tone: Tone,
val loader: Loader? = null,
) : SubtitleUM ) : SubtitleUM
data class AccentedText( data class AccentedText(
@ -78,11 +76,8 @@ sealed interface EarnBlockUM {
val style: Style, val style: Style,
) : SubtitleUM ) : SubtitleUM
data class Loader(val tone: LoaderTone)
enum class Style { Large, Small } enum class Style { Large, Small }
enum class Tone { Primary, Disabled, Accent } enum class Tone { Primary, Disabled, Accent }
enum class LoaderTone { Positive, Muted }
} }
@Immutable @Immutable
@ -100,5 +95,18 @@ sealed interface EarnBlockUM {
val cryptoValue: TextReference, val cryptoValue: TextReference,
val isBalanceHidden: Boolean, val isBalanceHidden: Boolean,
) : TrailingUM ) : TrailingUM
/** Chevron indicating the whole row is clickable. */
data object Chevron : TrailingUM
/** Status icon (warning / info) placed in the trailing slot. */
data class StatusIcon(val tone: Tone) : TrailingUM {
enum class Tone { Warning, Info }
}
/** Circular progress indicator placed in the trailing slot (enabling / disabling states). */
data class Loader(val tone: LoaderTone) : TrailingUM {
enum class LoaderTone { Positive, Muted }
}
} }
} }

View file

@ -97,9 +97,11 @@ fun TangemPagerIndicator(
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
) { ) {
Row( Row(
modifier = Modifier.offset { modifier = Modifier
IntOffset(animState.slideOffset.value.roundToInt(), 0) .wrapContentWidth(unbounded = true)
}, .offset {
IntOffset(animState.slideOffset.value.roundToInt(), 0)
},
horizontalArrangement = Arrangement.spacedBy(SPACING), horizontalArrangement = Arrangement.spacedBy(SPACING),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
@ -367,8 +369,8 @@ val TangemPagerIndicatorColors: PagerIndicatorColors
@Composable @Composable
@ReadOnlyComposable @ReadOnlyComposable
get() = PagerIndicatorColors( get() = PagerIndicatorColors(
active = TangemTheme.colors2.graphic.neutral.primary, active = TangemTheme.colors3.bg.inverse,
inactive = TangemTheme.colors2.graphic.neutral.tertiary, inactive = TangemTheme.colors3.bg.opaque.secondary,
overlay = null, overlay = null,
) )
@ -413,6 +415,7 @@ private class TangemPagerIndicatorPreviewProvider : PreviewParameterProvider<Int
1, 1,
2, 2,
3, 3,
4,
5, 5,
6, 6,
7, 7,

View file

@ -23,11 +23,10 @@ import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.constrainHeight import androidx.compose.ui.unit.constrainHeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.tangem.core.ui.R import com.tangem.core.ui.R
import com.tangem.core.ui.ds.button.SecondaryTangemButton
import com.tangem.core.ui.ds.button.TangemButtonShape
import com.tangem.core.ui.ds.button.TangemButtonType import com.tangem.core.ui.ds.button.TangemButtonType
import com.tangem.core.ui.ds.button.TangemButtonUM import com.tangem.core.ui.ds.button.TangemButtonUM
import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds2.button.TangemButton
import com.tangem.core.ui.extensions.orEmpty import com.tangem.core.ui.extensions.orEmpty
import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringReference
@ -109,12 +108,12 @@ private fun ActionButton(button: TangemButtonUM, modifier: Modifier = Modifier)
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2), verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
SecondaryTangemButton( TangemButton(
tangemIconUM = button.tangemIconUM,
onClick = button.onClick, onClick = button.onClick,
variant = TangemButton.Variant.Material,
isEnabled = button.isEnabled, isEnabled = button.isEnabled,
shape = TangemButtonShape.Rounded, iconStart = button.tangemIconUM,
onLongClick = button.onLongClick, size = TangemButton.Size.X14,
) )
Text( Text(
text = button.text.orEmpty().resolveReference(), text = button.text.orEmpty().resolveReference(),

View file

@ -3,11 +3,7 @@ package com.tangem.features.markets.token.block.impl.ui
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -38,12 +34,12 @@ import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.core.ui.test.TokenMarketBlockTestTags import com.tangem.core.ui.test.TokenMarketBlockTestTags
import com.tangem.core.ui.R as CoreR
import com.tangem.features.markets.impl.R import com.tangem.features.markets.impl.R
import com.tangem.features.markets.token.block.impl.model.formatter.toChartType import com.tangem.features.markets.token.block.impl.model.formatter.toChartType
import com.tangem.features.markets.token.block.impl.ui.state.TokenMarketBlockUM import com.tangem.features.markets.token.block.impl.ui.state.TokenMarketBlockUM
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlin.random.Random import kotlin.random.Random
import com.tangem.core.ui.R as CoreR
private val ChartWidth: Dp = 52.dp private val ChartWidth: Dp = 52.dp
private val ChartHeight: Dp = 32.dp private val ChartHeight: Dp = 32.dp
@ -115,7 +111,7 @@ internal fun TokenMarketBlock(tokenMarketBlockUM: TokenMarketBlockUM, modifier:
withHazeEffect = false, withHazeEffect = false,
modifier = Modifier modifier = Modifier
.layoutId(TangemRowLayoutId.TAIL) .layoutId(TangemRowLayoutId.TAIL)
.padding(start = TangemTheme.dimens2.x10), .padding(start = 12.dp),
) )
} }
} }

View file

@ -21,7 +21,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp import androidx.compose.ui.util.lerp
import com.tangem.core.ui.components.SpacerH8 import com.tangem.core.ui.components.SpacerH8
import com.tangem.core.ui.components.notifications.NotificationConfig import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.components.pager.PagerIndicator import com.tangem.core.ui.ds.TangemPagerIndicator
import com.tangem.core.ui.ds.message.TangemMessage import com.tangem.core.ui.ds.message.TangemMessage
import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
@ -134,10 +134,7 @@ private fun BannersCarousel(
SpacerH8() SpacerH8()
PagerIndicator( TangemPagerIndicator(pagerState = pagerState)
pagerState = pagerState,
hasBackground = false,
)
} }
} }

View file

@ -9,7 +9,6 @@ import com.tangem.core.ui.ds.button.TangemButtonUM
import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.tokendetails.impl.R import com.tangem.features.tokendetails.impl.R
import com.tangem.utils.transformer.Transformer import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
@ -54,10 +53,7 @@ internal class TokenDetailsStateController @Inject constructor() {
), ),
swapButton = TangemButtonUM( swapButton = TangemButtonUM(
text = resourceReference(R.string.actionbutton_swap_title), text = resourceReference(R.string.actionbutton_swap_title),
tangemIconUM = TangemIconUM.Icon( tangemIconUM = TangemIconUM.Icon(iconRes = R.drawable.ic_exchange_default_24),
iconRes = R.drawable.ic_exchange_default_24,
tintReference = { TangemTheme.colors2.graphic.neutral.quaternary },
),
onClick = { }, onClick = { },
isEnabled = false, isEnabled = false,
type = TangemButtonType.Secondary, type = TangemButtonType.Secondary,

View file

@ -144,13 +144,13 @@ private fun AddAndManageRow(
) { ) {
Text( Text(
text = stringResourceSafe(id = title), text = stringResourceSafe(id = title),
style = TangemTheme.typography2.bodyMedium16, style = TangemTheme.typography3.body.medium,
color = TangemTheme.colors2.text.neutral.primary, color = TangemTheme.colors3.text.primary,
) )
Text( Text(
text = stringResourceSafe(id = subtitle), text = stringResourceSafe(id = subtitle),
style = TangemTheme.typography2.captionMedium12, style = TangemTheme.typography3.caption.medium,
color = TangemTheme.colors2.text.neutral.secondary, color = TangemTheme.colors3.text.secondary,
) )
} }
SpacerW(8.dp) SpacerW(8.dp)

View file

@ -1,13 +1,17 @@
package com.tangem.feature.wallet.child.organizetokens.model.converter.items package com.tangem.feature.wallet.child.organizetokens.model.converter.items
import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import com.tangem.common.getTotalCryptoAmount import com.tangem.common.getTotalCryptoAmount
import com.tangem.common.getTotalFiatAmount import com.tangem.common.getTotalFiatAmount
import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.row.internal.TangemRowTailUM import com.tangem.core.ui.ds.row.internal.TangemRowTailUM
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.core.ui.extensions.combinedReference
import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.styledStringReference
import com.tangem.core.ui.format.bigdecimal.cryptoStyled import com.tangem.core.ui.format.bigdecimal.cryptoStyled
import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.formatStyled import com.tangem.core.ui.format.bigdecimal.formatStyled
@ -36,7 +40,21 @@ internal class OrganizeTokenItemConverter(
id = getTokenItemId(currency.id), id = getTokenItemId(currency.id),
headIconUM = TangemIconUM.Currency(iconStateConverter.convert(currencyStatus)), headIconUM = TangemIconUM.Currency(iconStateConverter.convert(currencyStatus)),
titleUM = TangemTokenRowUM.TitleUM.Content( titleUM = TangemTokenRowUM.TitleUM.Content(
text = stringReference(currency.name), text = combinedReference(
stringReference(currency.name),
stringReference(" "),
styledStringReference(
value = currency.symbol,
spanStyleReference = {
SpanStyle(
color = TangemTheme.colors3.text.secondary,
fontWeight = FontWeight.Medium,
fontSize = 12.sp,
letterSpacing = 0.18.sp,
)
},
),
),
), ),
subtitleUM = TangemTokenRowUM.SubtitleUM.Empty, subtitleUM = TangemTokenRowUM.SubtitleUM.Empty,
topEndContentUM = TangemTokenRowUM.EndContentUM.Content( topEndContentUM = TangemTokenRowUM.EndContentUM.Content(

View file

@ -62,11 +62,11 @@ private fun Menu(
private fun SortByBalanceMenuSection(organizeMenuUM: OrganizeTokensUM.OrganizeMenuUM, onDropdownDismiss: () -> Unit) { private fun SortByBalanceMenuSection(organizeMenuUM: OrganizeTokensUM.OrganizeMenuUM, onDropdownDismiss: () -> Unit) {
Text( Text(
text = stringResourceSafe(R.string.organize_tokens_sort_by_balance), text = stringResourceSafe(R.string.organize_tokens_sort_by_balance),
style = TangemTheme.typography2.headingSemibold17, style = TangemTheme.typography3.body.medium,
color = if (organizeMenuUM.isSortedByBalance) { color = if (organizeMenuUM.isSortedByBalance) {
TangemTheme.colors2.text.status.disabled TangemTheme.colors3.text.tertiary
} else { } else {
TangemTheme.colors2.text.neutral.primary TangemTheme.colors3.text.primary
}, },
maxLines = 1, maxLines = 1,
modifier = Modifier modifier = Modifier
@ -79,12 +79,12 @@ private fun SortByBalanceMenuSection(organizeMenuUM: OrganizeTokensUM.OrganizeMe
}, },
enabled = !organizeMenuUM.isSortedByBalance, enabled = !organizeMenuUM.isSortedByBalance,
) )
.padding(vertical = TangemTheme.dimens2.x2_5, horizontal = TangemTheme.dimens2.x4), .padding(vertical = 10.dp, horizontal = 16.dp),
) )
HorizontalDivider( HorizontalDivider(
thickness = 0.5.dp, thickness = 0.5.dp,
color = TangemTheme.colors2.border.neutral.quaternary, color = TangemTheme.colors3.border.tertiary,
) )
} }
@ -102,36 +102,36 @@ private fun GroupTokensMenuSection(organizeMenuUM: OrganizeTokensUM.OrganizeMenu
onDropdownDismiss() onDropdownDismiss()
}, },
) )
.padding(vertical = TangemTheme.dimens2.x2_5, horizontal = TangemTheme.dimens2.x4), .padding(vertical = 10.dp, horizontal = 16.dp),
) { ) {
Text( Text(
text = stringResourceSafe(R.string.organize_tokens_group), text = stringResourceSafe(R.string.organize_tokens_group),
style = TangemTheme.typography2.headingSemibold17, style = TangemTheme.typography3.body.medium,
color = TangemTheme.colors2.text.neutral.primary, color = TangemTheme.colors3.text.primary,
maxLines = 1, maxLines = 1,
) )
Box( Box(
modifier = Modifier.size(TangemTheme.dimens2.x6), modifier = Modifier.size(24.dp),
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
) { ) {
if (organizeMenuUM.isGrouped) { if (organizeMenuUM.isGrouped) {
Box( Box(
modifier = Modifier modifier = Modifier
.padding(TangemTheme.dimens2.x0_5) .padding(2.dp)
.size(TangemTheme.dimens2.x5) .size(20.dp)
.background( .background(
color = TangemTheme.colors2.graphic.neutral.primary, color = TangemTheme.colors3.icon.primary,
shape = CircleShape, shape = CircleShape,
), ),
) { ) {
Icon( Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_check_default_24), imageVector = ImageVector.vectorResource(R.drawable.ic_check_default_24),
contentDescription = null, contentDescription = null,
tint = TangemTheme.colors2.graphic.neutral.primaryInverted, tint = TangemTheme.colors3.icon.inverse,
modifier = Modifier modifier = Modifier
.align(Alignment.Center) .align(Alignment.Center)
.padding(TangemTheme.dimens2.x0_5) .padding(2.dp)
.size(TangemTheme.dimens2.x4), .size(16.dp),
) )
} }
} }

View file

@ -34,8 +34,8 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.haze.hazeEffectTangem import com.tangem.core.ui.components.haze.hazeEffectTangem
import com.tangem.core.ui.components.haze.hazeSourceTangem import com.tangem.core.ui.components.haze.hazeSourceTangem
import com.tangem.core.ui.ds.button.TangemButton
import com.tangem.core.ui.ds.image.TangemIcon 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.TangemRowContainer
import com.tangem.core.ui.ds.row.TangemRowLayoutId import com.tangem.core.ui.ds.row.TangemRowLayoutId
import com.tangem.core.ui.ds.row.header.TangemHeaderRow import com.tangem.core.ui.ds.row.header.TangemHeaderRow
@ -43,10 +43,9 @@ import com.tangem.core.ui.ds.row.internal.TangemRowTail
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.core.ui.ds.row.token.internal.TokenRowEndContent import com.tangem.core.ui.ds.row.token.internal.TokenRowEndContent
import com.tangem.core.ui.ds.row.token.internal.TokenRowTitle import com.tangem.core.ui.ds.row.token.internal.TokenRowTitle
import com.tangem.core.ui.ds.topbar.TangemTopBar import com.tangem.core.ui.ds2.button.TangemButton
import com.tangem.core.ui.ds.topbar.TangemTopBarActionContent import com.tangem.core.ui.ds2.topnavigation.TangemTopNavigation
import com.tangem.core.ui.ds.topbar.TangemTopBarActionUM import com.tangem.core.ui.extensions.copy
import com.tangem.core.ui.ds.topbar.TangemTopBarType
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.core.ui.res.TangemThemePreviewRedesign
@ -82,21 +81,21 @@ internal fun OrganizeTokensContent(
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
content = TangemBottomSheetConfigContent.Empty, content = TangemBottomSheetConfigContent.Empty,
), ),
containerColor = TangemTheme.colors2.surface.level2, containerColor = TangemTheme.colors3.bg.primary,
title = { title = {
TangemTopBar( TangemTopNavigation(
contentPadding = TangemTopNavigation.DefaultContentPadding.copy(top = 16.dp),
windowInsets = WindowInsets(0.dp),
blurBackground = false,
contentAlign = TangemTopNavigation.ContentAlign.Center,
title = resourceReference(R.string.organize_tokens_title), title = resourceReference(R.string.organize_tokens_title),
type = TangemTopBarType.BottomSheet, endButton = {
endContent = { TangemButton(
TangemTopBarActionContent(
actionUM = TangemTopBarActionUM(
iconRes = R.drawable.ic_exchange_mini_24,
isActionable = true,
onClick = { isShowDropdownMenu = true },
ghostModeProgress = 0f,
),
modifier = Modifier.testTag(OrganizeTokensScreenTestTags.MENU_BUTTON), modifier = Modifier.testTag(OrganizeTokensScreenTestTags.MENU_BUTTON),
type = TangemTopBarType.BottomSheet, variant = TangemButton.Variant.Material,
size = TangemButton.Size.X11,
iconStart = TangemIconUM.Icon(iconRes = R.drawable.ic_exchange_mini_24),
onClick = { isShowDropdownMenu = true },
) )
OrganizeDropDownMenu( OrganizeDropDownMenu(
organizeMenuUM = organizeTokensUM.organizeMenuUM, organizeMenuUM = organizeTokensUM.organizeMenuUM,
@ -137,7 +136,7 @@ private fun TokenList(
var draggingItem by remember { mutableStateOf<OrganizeRowItemUM?>(null) } var draggingItem by remember { mutableStateOf<OrganizeRowItemUM?>(null) }
Box( Box(
modifier = modifier.background(TangemTheme.colors2.surface.level2), modifier = modifier.background(TangemTheme.colors3.bg.primary),
) { ) {
val footerInset = LocalTangemBottomSheetContentBottomInset.current val footerInset = LocalTangemBottomSheetContentBottomInset.current
@ -238,7 +237,7 @@ private fun LazyItemScope.DraggableItem(
enabled = isValidDropTarget, enabled = isValidDropTarget,
) { _ -> ) { _ ->
val modifierWithBackground = itemModifier val modifierWithBackground = itemModifier
.background(color = TangemTheme.colors.background.primary) .background(color = TangemTheme.colors3.bg.secondary)
.semantics { lazyListItemPosition = index } .semantics { lazyListItemPosition = index }
when (item) { when (item) {
@ -281,16 +280,23 @@ private fun BoxScope.BottomButtons(organizeTokensUM: OrganizeTokensUM) {
.padding(bottom = bottomBarHeight + TangemTheme.dimens2.x4), .padding(bottom = bottomBarHeight + TangemTheme.dimens2.x4),
) { ) {
TangemButton( TangemButton(
buttonUM = organizeTokensUM.cancelButton,
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.testTag(OrganizeTokensScreenTestTags.CANCEL_BUTTON), .testTag(OrganizeTokensScreenTestTags.CANCEL_BUTTON),
onClick = organizeTokensUM.cancelButton.onClick,
text = organizeTokensUM.cancelButton.text,
variant = TangemButton.Variant.Secondary,
size = TangemButton.Size.X12,
isEnabled = organizeTokensUM.cancelButton.isEnabled,
) )
TangemButton( TangemButton(
buttonUM = organizeTokensUM.applyButton,
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.testTag(OrganizeTokensScreenTestTags.APPLY_BUTTON), .testTag(OrganizeTokensScreenTestTags.APPLY_BUTTON),
onClick = organizeTokensUM.applyButton.onClick,
text = organizeTokensUM.applyButton.text,
size = TangemButton.Size.X12,
isEnabled = organizeTokensUM.applyButton.isEnabled,
) )
} }
} }
@ -355,8 +361,8 @@ private fun OrganizeTokenRow(
TokenRowEndContent( TokenRowEndContent(
endContentUM = tokenRowUM.topEndContentUM, endContentUM = tokenRowUM.topEndContentUM,
isBalanceHidden = isBalanceHidden, isBalanceHidden = isBalanceHidden,
textStyle = TangemTheme.typography2.captionSemibold12, textStyle = TangemTheme.typography3.caption.medium,
textColor = TangemTheme.colors2.text.neutral.secondary, textColor = TangemTheme.colors3.text.secondary,
placeholderWidth = TangemTheme.dimens2.x11, placeholderWidth = TangemTheme.dimens2.x11,
modifier = Modifier modifier = Modifier
.layoutId(layoutId = TangemRowLayoutId.START_BOTTOM) .layoutId(layoutId = TangemRowLayoutId.START_BOTTOM)

View file

@ -1,5 +1,6 @@
package com.tangem.feature.wallet.child.organizetokens.ui.preview package com.tangem.feature.wallet.child.organizetokens.ui.preview
import androidx.compose.ui.text.SpanStyle
import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.ds.button.TangemButtonType import com.tangem.core.ui.ds.button.TangemButtonType
import com.tangem.core.ui.ds.button.TangemButtonUM import com.tangem.core.ui.ds.button.TangemButtonUM
@ -8,9 +9,12 @@ import com.tangem.core.ui.ds.row.header.TangemHeaderRowUM
import com.tangem.core.ui.ds.row.internal.TangemRowTailUM import com.tangem.core.ui.ds.row.internal.TangemRowTailUM
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.extensions.combinedReference
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.styledStringReference
import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.child.organizetokens.entity.OrganizeRowItemUM import com.tangem.feature.wallet.child.organizetokens.entity.OrganizeRowItemUM
import com.tangem.feature.wallet.child.organizetokens.entity.OrganizeTokensUM import com.tangem.feature.wallet.child.organizetokens.entity.OrganizeTokensUM
import com.tangem.feature.wallet.child.organizetokens.entity.RoundingModeUM import com.tangem.feature.wallet.child.organizetokens.entity.RoundingModeUM
@ -75,7 +79,16 @@ internal object OrganizeTokensPreview {
tokenRowUM = draggableToken.copy( tokenRowUM = draggableToken.copy(
id = "${group.id}_token_$tokenNumber", id = "${group.id}_token_$tokenNumber",
titleUM = TangemTokenRowUM.TitleUM.Content( titleUM = TangemTokenRowUM.TitleUM.Content(
text = stringReference(value = "Token $tokenNumber from $networkNumber network"), text = combinedReference(
stringReference(value = "Token $tokenNumber"),
stringReference(value = " "),
styledStringReference(
value = "TKN$tokenNumber",
spanStyleReference = {
SpanStyle(color = TangemTheme.colors2.text.neutral.secondary)
},
),
),
), ),
), ),
groupId = group.id, groupId = group.id,

View file

@ -16,7 +16,6 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
import com.tangem.core.ui.test.BaseBottomSheetTestTags
import androidx.compose.ui.res.vectorResource import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.DpOffset
@ -31,6 +30,7 @@ import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.core.ui.test.BaseBottomSheetTestTags
import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonUM import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonUM
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@ -60,9 +60,9 @@ internal fun TokenActionContent(
tokenRowUM = tokenRowUM, tokenRowUM = tokenRowUM,
isBalanceHidden = isBalanceHidden, isBalanceHidden = isBalanceHidden,
modifier = Modifier modifier = Modifier
.padding(horizontal = TangemTheme.dimens2.x3) .padding(horizontal = 12.dp)
.clip(RoundedCornerShape(18.dp)) .clip(RoundedCornerShape(18.dp))
.background(TangemTheme.colors2.surface.level3), .background(TangemTheme.colors3.bg.secondary),
) )
TangemContextMenu( TangemContextMenu(
expanded = true, expanded = true,
@ -93,17 +93,17 @@ private fun TokenActionContextMenuContent(actions: ImmutableList<TokenActionButt
Column( Column(
modifier = Modifier modifier = Modifier
.widthIn(min = 206.dp) .widthIn(min = 206.dp)
.padding(TangemTheme.dimens2.x1_5), .padding(6.dp),
) { ) {
actions.fastForEach { item -> actions.fastForEach { item ->
Column { Column {
Row( Row(
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2_5), horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier modifier = Modifier
.testTag(BaseBottomSheetTestTags.ACTION_BUTTON) .testTag(BaseBottomSheetTestTags.ACTION_BUTTON)
.fillMaxWidth() .fillMaxWidth()
.clip(RoundedCornerShape(TangemTheme.dimens2.x2_5)) .clip(RoundedCornerShape(10.dp))
.clickable( .clickable(
enabled = item.isEnabled, enabled = item.isEnabled,
onClick = { onClick = {
@ -112,39 +112,39 @@ private fun TokenActionContextMenuContent(actions: ImmutableList<TokenActionButt
}, },
) )
.padding( .padding(
horizontal = TangemTheme.dimens2.x2_5, horizontal = 10.dp,
vertical = TangemTheme.dimens2.x2_5, vertical = 10.dp,
), ),
) { ) {
Icon( Icon(
imageVector = ImageVector.vectorResource(item.iconResId), imageVector = ImageVector.vectorResource(item.iconResId),
contentDescription = null, contentDescription = null,
tint = if (item.isWarning) { tint = if (item.isWarning) {
TangemTheme.colors2.graphic.status.warning TangemTheme.colors3.icon.accent.red
} else { } else {
TangemTheme.colors2.graphic.neutral.primary TangemTheme.colors3.icon.primary
}, },
modifier = Modifier modifier = Modifier
.size(TangemTheme.dimens2.x5) .size(20.dp)
.testTag(BaseBottomSheetTestTags.ACTION_ICON), .testTag(BaseBottomSheetTestTags.ACTION_ICON),
) )
Text( Text(
text = item.text.resolveReference(), text = item.text.resolveReference(),
style = TangemTheme.typography2.headingRegular17, style = TangemTheme.typography3.body.medium,
color = if (item.isWarning) { color = if (item.isWarning) {
TangemTheme.colors2.text.status.warning TangemTheme.colors3.text.accent.red
} else { } else {
TangemTheme.colors2.text.neutral.primary TangemTheme.colors3.text.primary
}, },
) )
} }
if (item.hasDivider) { if (item.hasDivider) {
Spacer( Spacer(
modifier = Modifier modifier = Modifier
.padding(vertical = TangemTheme.dimens2.x1) .padding(vertical = 4.dp)
.fillMaxWidth() .fillMaxWidth()
.height(1.dp) .height(1.dp)
.background(TangemTheme.colors2.border.neutral.primary), .background(TangemTheme.colors3.border.primary),
) )
} }
} }

View file

@ -29,7 +29,7 @@ internal object WalletBalancePreview {
styledStringReference( styledStringReference(
".56", ".56",
{ {
TangemTheme.typography2.headingRegular28.toSpanStyle() TangemTheme.typography3.heading.medium.toSpanStyle()
}, },
), ),
stringReference(" $"), stringReference(" $"),
@ -55,7 +55,7 @@ internal object WalletBalancePreview {
styledStringReference( styledStringReference(
".56", ".56",
{ {
TangemTheme.typography2.headingRegular28.toSpanStyle() TangemTheme.typography3.heading.medium.toSpanStyle()
}, },
), ),
stringReference(" $"), stringReference(" $"),

View file

@ -8,7 +8,6 @@ import com.tangem.core.ui.ds.button.TangemButtonUM
import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.impl.R
/** /**
@ -27,16 +26,7 @@ internal sealed class WalletActionButtons(
val buttonUM: TangemButtonUM val buttonUM: TangemButtonUM
get() = TangemButtonUM( get() = TangemButtonUM(
text = text, text = text,
tangemIconUM = TangemIconUM.Icon( tangemIconUM = TangemIconUM.Icon(iconRes = iconRes),
iconRes = iconRes,
tintReference = {
if (isEnabled) {
TangemTheme.colors2.graphic.neutral.primary
} else {
TangemTheme.colors2.graphic.neutral.quaternary
}
},
),
type = TangemButtonType.Secondary, type = TangemButtonType.Secondary,
shape = TangemButtonShape.Rounded, shape = TangemButtonShape.Rounded,
onClick = onClick, onClick = onClick,

View file

@ -103,14 +103,14 @@ internal class SetTokenListErrorTransformer(
fiat( fiat(
fiatCurrencyCode = appCurrency.code, fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol, fiatCurrencySymbol = appCurrency.symbol,
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.neutral.secondary) }, spanStyleReference = { SpanStyle(color = TangemTheme.colors3.text.secondary) },
) )
}, },
balance = BigDecimal.ZERO.formatStyled { balance = BigDecimal.ZERO.formatStyled {
fiat( fiat(
fiatCurrencyCode = appCurrency.code, fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol, fiatCurrencySymbol = appCurrency.symbol,
spanStyleReference = { TangemTheme.typography2.headingRegular28.toSpanStyle() }, spanStyleReference = { TangemTheme.typography3.heading.medium.toSpanStyle() },
) )
}, },
isZeroBalance = true, isZeroBalance = true,

View file

@ -52,7 +52,7 @@ internal class MultiWalletBalanceUMTransformer(
fiat( fiat(
fiatCurrencyCode = appCurrency.code, fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol, fiatCurrencySymbol = appCurrency.symbol,
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.neutral.secondary) }, spanStyleReference = { SpanStyle(color = TangemTheme.colors3.text.secondary) },
) )
}, },
balance = fiatBalance.amount.formatStyled { balance = fiatBalance.amount.formatStyled {
@ -60,7 +60,7 @@ internal class MultiWalletBalanceUMTransformer(
fiatCurrencyCode = appCurrency.code, fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol, fiatCurrencySymbol = appCurrency.symbol,
spanStyleReference = { spanStyleReference = {
TangemTheme.typography2.headingRegular28.toSpanStyle() TangemTheme.typography3.heading.medium.toSpanStyle()
}, },
) )
}, },

View file

@ -36,6 +36,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.clearAndSetSemantics
@ -46,7 +47,6 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.decompose.ExperimentalDecomposeApi
import com.tangem.core.ui.components.atoms.handComposableComponentHeight
import com.tangem.core.ui.components.background.northernlights.NorthernLightsBackground import com.tangem.core.ui.components.background.northernlights.NorthernLightsBackground
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheetDraggableHeader import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheetDraggableHeader
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
@ -181,9 +181,6 @@ private fun WalletContent2(
onBottomSheetStateChange: (BottomSheetState) -> Unit, onBottomSheetStateChange: (BottomSheetState) -> Unit,
bottomSheetContent: @Composable (onExpandSheet: () -> Unit) -> Unit, bottomSheetContent: @Composable (onExpandSheet: () -> Unit) -> Unit,
) { ) {
val density = LocalDensity.current
val bottomBarHeight = with(density) { WindowInsets.systemBars.getBottom(this).toDp() }
var walletBalance by remember { mutableStateOf<TextReference?>(TextReference.EMPTY) } var walletBalance by remember { mutableStateOf<TextReference?>(TextReference.EMPTY) }
var pullToRefreshConfig by remember { var pullToRefreshConfig by remember {
mutableStateOf( mutableStateOf(
@ -207,10 +204,15 @@ private fun WalletContent2(
) )
}, },
) { paddingValues, bottomSheetState -> ) { paddingValues, bottomSheetState ->
val marketHintApproxHeight = 140.dp val density = LocalDensity.current
var marketsHintHeight by remember { mutableStateOf(0.dp) }
val collapsedBodyOverhang = with(density) { behavior.state.partialHeightLimit.toDp() }
val contentPadding = PaddingValues( val contentPadding = PaddingValues(
bottom = paddingValues.calculateBottomPadding() + marketHintApproxHeight, bottom = paddingValues.calculateBottomPadding() +
marketsHintHeight +
collapsedBodyOverhang +
TangemTheme.dimens2.x2,
) )
val selectedWalletIndex by rememberUpdatedState(state.selectedWalletIndex) val selectedWalletIndex by rememberUpdatedState(state.selectedWalletIndex)
@ -235,22 +237,17 @@ private fun WalletContent2(
.fillMaxSize() .fillMaxSize()
.hazeSourceTangem(zIndex = -2f), .hazeSourceTangem(zIndex = -2f),
) { ) {
val backgroundColor = if (LocalIsInDarkTheme.current) {
TangemTheme.colors2.surface.level1
} else {
TangemTheme.colors2.surface.level2
}
Box( Box(
modifier = Modifier modifier = Modifier
.matchParentSize() .matchParentSize()
.background(backgroundColor), .background(TangemTheme.colors3.bg.primary),
) )
val isSheetExpanded by remember { val isSheetExpanded by remember {
derivedStateOf { bottomSheetState.targetValue == TangemSheetValue.Expanded } derivedStateOf { bottomSheetState.targetValue == TangemSheetValue.Expanded }
} }
if (!isSheetExpanded) { if (!isSheetExpanded) {
NorthernLightsBackground( NorthernLightsBackground(
containerColor = backgroundColor, containerColor = TangemTheme.colors3.bg.primary,
modifier = Modifier modifier = Modifier
.graphicsLayer { alpha = 1 - behavior.state.collapsedFraction * 2 } .graphicsLayer { alpha = 1 - behavior.state.collapsedFraction * 2 }
.matchParentSize(), .matchParentSize(),
@ -262,10 +259,10 @@ private fun WalletContent2(
pullToRefreshState = pullToRefreshState, pullToRefreshState = pullToRefreshState,
pullToRefreshConfig = pullToRefreshConfig, pullToRefreshConfig = pullToRefreshConfig,
behavior = behavior, behavior = behavior,
topOffset = subtitleBottom + TangemTheme.dimens2.x2, topOffset = subtitleBottom + 8.dp,
) )
val overlay = TangemTheme.colors2.overlay.overlayPrimary val overlay = TangemTheme.colors3.material.tint.solid
// Root-coordinates bounds of the "Add & Manage" button per pager page, reported by the // Root-coordinates bounds of the "Add & Manage" button per pager page, reported by the
// button itself, so the markets hint and tooltip can avoid covering it // button itself, so the markets hint and tooltip can avoid covering it
@ -376,14 +373,14 @@ private fun WalletContent2(
}, },
) )
} }
val peekHeight =
bottomSheetHeaderHeightProvider() + handComposableComponentHeight + bottomBarHeight
MarketsHint( MarketsHint(
modifier = Modifier modifier = Modifier
.align(Alignment.BottomCenter) .align(Alignment.BottomCenter)
.fillMaxWidth(fraction = .6f) .fillMaxWidth(fraction = .6f)
.padding(bottom = peekHeight), .padding(bottom = paddingValues.calculateBottomPadding())
.onSizeChanged { size ->
marketsHintHeight = with(density) { size.height.toDp() }
},
isVisible = isShowMarketsHint, isVisible = isShowMarketsHint,
obstacleBounds = remember(currentWalletIndex) { obstacleBounds = remember(currentWalletIndex) {
{ organizeButtonBounds[currentWalletIndex] } { organizeButtonBounds[currentWalletIndex] }
@ -400,7 +397,7 @@ private fun WalletContent2(
.fillMaxWidth(), .fillMaxWidth(),
isVisible = state.showMarketsOnboarding, isVisible = state.showMarketsOnboarding,
availableHeight = maxHeight, availableHeight = maxHeight,
sheetTopInset = TangemTheme.dimens2.x3, sheetTopInset = 12.dp,
bottomSheetState = bottomSheetState, bottomSheetState = bottomSheetState,
onCloseClick = state.onDismissMarketsTooltip, onCloseClick = state.onDismissMarketsTooltip,
obstacleBounds = remember(walletsPagerState, organizeButtonBounds) { obstacleBounds = remember(walletsPagerState, organizeButtonBounds) {
@ -425,15 +422,15 @@ private inline fun BaseScaffoldWithMarkets(
) { ) {
val density = LocalDensity.current val density = LocalDensity.current
val bottomBarHeight = with(density) { WindowInsets.systemBars.getBottom(density = this).toDp() } val bottomBarHeight = with(density) { WindowInsets.systemBars.getBottom(density = this).toDp() }
val peekHeight = bottomSheetHeaderHeightProvider() + TangemTheme.dimens2.x3 + bottomBarHeight val peekHeight = bottomSheetHeaderHeightProvider() + 12.dp + bottomBarHeight
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val bottomSheetState = rememberTangemStandardBottomSheetState() val bottomSheetState = rememberTangemStandardBottomSheetState()
val scaffoldState = rememberTangemBottomSheetScaffoldState(bottomSheetState = bottomSheetState) val scaffoldState = rememberTangemBottomSheetScaffoldState(bottomSheetState = bottomSheetState)
val expandedBackground = TangemTheme.colors2.surface.level2 val expandedBackground = TangemTheme.colors3.bg.primary
val collapsedBackground = TangemTheme.colors2.surface.level3 val collapsedBackground = TangemTheme.colors3.bg.secondary
val background by animateColorAsState( val background by animateColorAsState(
targetValue = if (bottomSheetState.targetValue == TangemSheetValue.Expanded) { targetValue = if (bottomSheetState.targetValue == TangemSheetValue.Expanded) {
expandedBackground expandedBackground
@ -527,8 +524,8 @@ private fun BottomSheet(
val maxHeight = LocalWindowSize.current.height val maxHeight = LocalWindowSize.current.height
val shape = RoundedCornerShape( val shape = RoundedCornerShape(
topStart = TangemTheme.dimens2.x8, topStart = 32.dp,
topEnd = TangemTheme.dimens2.x8, topEnd = 32.dp,
) )
CustomBottomSheet( CustomBottomSheet(
state = bottomSheetState, state = bottomSheetState,

View file

@ -61,14 +61,14 @@ internal fun MarketsHint(isVisible: Boolean, modifier: Modifier = Modifier, obst
) { ) {
Text( Text(
text = stringResourceSafe(R.string.markets_hint), text = stringResourceSafe(R.string.markets_hint),
style = TangemTheme.typography2.bodyRegular15, style = TangemTheme.typography3.subheading.medium,
color = TangemTheme.colors2.text.neutral.tertiary, color = TangemTheme.colors3.text.secondary,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
) )
Icon( Icon(
modifier = Modifier.size(size = 24.dp), modifier = Modifier.size(size = 24.dp),
painter = painterResource(id = R.drawable.ic_chevron_24), painter = painterResource(id = R.drawable.ic_chevron_24),
tint = TangemTheme.colors2.graphic.neutral.tertiary, tint = TangemTheme.colors3.icon.secondary,
contentDescription = null, contentDescription = null,
) )
} }

View file

@ -32,9 +32,13 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.* import androidx.compose.ui.unit.*
import com.tangem.core.ui.components.sheetscaffold.TangemSheetState import com.tangem.core.ui.components.sheetscaffold.TangemSheetState
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds2.button.TangemButton
import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.core.ui.res.generated.icons.Icons
import com.tangem.core.ui.res.generated.icons.ic_cross_20
import com.tangem.core.ui.test.MarketTooltipTestTags import com.tangem.core.ui.test.MarketTooltipTestTags
import com.tangem.core.ui.utils.toPx import com.tangem.core.ui.utils.toPx
import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.impl.R
@ -118,14 +122,14 @@ internal fun MarketsTooltip(
@Composable @Composable
internal fun MarketsTooltipContent(onCloseClick: () -> Unit, modifier: Modifier = Modifier) { internal fun MarketsTooltipContent(onCloseClick: () -> Unit, modifier: Modifier = Modifier) {
val backgroundColor = TangemTheme.colors.background.action val backgroundColor = TangemTheme.colors3.bg.tertiary
val tipDpSize = DpSize(width = 20.dp, height = 8.dp) val tipDpSize = DpSize(width = 20.dp, height = 8.dp)
val tooltipShape = remember(tipDpSize) { TooltipShape(cornerRadius = 16.dp, tipSize = tipDpSize) } val tooltipShape = remember(tipDpSize) { TooltipShape(cornerRadius = 16.dp, tipSize = tipDpSize) }
Row( Row(
modifier = modifier modifier = modifier
.shadow( .shadow(
elevation = TangemTheme.dimens.elevation12, elevation = 12.dp,
shape = tooltipShape, shape = tooltipShape,
clip = false, clip = false,
ambientColor = Color.Black.copy(alpha = 0.7f), ambientColor = Color.Black.copy(alpha = 0.7f),
@ -149,27 +153,21 @@ internal fun MarketsTooltipContent(onCloseClick: () -> Unit, modifier: Modifier
) { ) {
Text( Text(
text = stringResourceSafe(id = R.string.markets_tooltip_v2_title), text = stringResourceSafe(id = R.string.markets_tooltip_v2_title),
style = TangemTheme.typography.subtitle2, style = TangemTheme.typography3.subheading.medium,
color = TangemTheme.colors.text.primary1, color = TangemTheme.colors3.text.primary,
) )
Text( Text(
text = stringResourceSafe(id = R.string.markets_tooltip_message), text = stringResourceSafe(id = R.string.markets_tooltip_message),
style = TangemTheme.typography.caption2, style = TangemTheme.typography3.caption.medium,
color = TangemTheme.colors.text.secondary, color = TangemTheme.colors3.text.secondary,
) )
} }
Icon( TangemButton(
modifier = Modifier variant = TangemButton.Variant.Ghost,
.size(size = 16.dp) size = TangemButton.Size.X7,
.clickable( iconStart = TangemIconUM.Icon(imageVector = Icons.ic_cross_20),
interactionSource = null, onClick = onCloseClick,
indication = null, modifier = Modifier.testTag(MarketTooltipTestTags.CLOSE_BUTTON),
onClick = onCloseClick,
)
.testTag(MarketTooltipTestTags.CLOSE_BUTTON),
painter = painterResource(id = R.drawable.ic_close_24),
tint = TangemTheme.colors.icon.informative,
contentDescription = null,
) )
} }
} }

View file

@ -9,7 +9,7 @@ import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.layout.boundsInRoot import androidx.compose.ui.layout.boundsInRoot
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
import com.tangem.core.ui.ds.button.TangemButton import com.tangem.core.ui.ds2.button.TangemButton
import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.haptic.TangemHapticEffect import com.tangem.core.ui.haptic.TangemHapticEffect
import com.tangem.core.ui.res.LocalHapticManager import com.tangem.core.ui.res.LocalHapticManager
@ -26,7 +26,7 @@ internal const val ORGANIZE_TOKENS_BUTTON_ITEM_KEY = "OrganizeTokensButton"
internal fun LazyListScope.nftCollections2(state: WalletUM, itemModifier: Modifier) { internal fun LazyListScope.nftCollections2(state: WalletUM, itemModifier: Modifier) {
(state as? WalletUM.Content)?.let { content -> (state as? WalletUM.Content)?.let { content ->
item(key = "NFTCollections", contentType = "NFTCollections") { item(key = "NFTCollections", contentType = "NFTCollections") {
WalletNFTItem( WalletNFTItem2(
modifier = itemModifier, modifier = itemModifier,
state = content.nftState, state = content.nftState,
) )
@ -62,15 +62,18 @@ internal fun LazyListScope.organizeTokens2(
} }
TangemButton( TangemButton(
buttonUM = organizeButton.copy(
onClick = {
hapticManager.perform(TangemHapticEffect.View.ContextClick)
organizeButton.onClick()
},
),
modifier = itemModifier modifier = itemModifier
.onGloballyPositioned { currentOnButtonBoundsChange(it.boundsInRoot()) } .onGloballyPositioned { currentOnButtonBoundsChange(it.boundsInRoot()) }
.testTag(testTag), .testTag(testTag),
size = TangemButton.Size.X9,
onClick = {
hapticManager.perform(TangemHapticEffect.View.ContextClick)
organizeButton.onClick()
},
variant = TangemButton.Variant.Secondary,
iconStart = organizeButton.tangemIconUM,
text = organizeButton.text,
isEnabled = organizeButton.isEnabled,
) )
} }
} }

View file

@ -41,7 +41,7 @@ import kotlinx.collections.immutable.persistentListOf
internal fun WalletNFTItem2(state: WalletNFTItemUM, modifier: Modifier = Modifier) { internal fun WalletNFTItem2(state: WalletNFTItemUM, modifier: Modifier = Modifier) {
val nftModifier = modifier val nftModifier = modifier
.clip(RoundedCornerShape(18.dp)) .clip(RoundedCornerShape(18.dp))
.background(TangemTheme.colors2.surface.level3) .background(TangemTheme.colors3.bg.secondary)
when (state) { when (state) {
is WalletNFTItemUM.Hidden -> Unit is WalletNFTItemUM.Hidden -> Unit
is WalletNFTItemUM.Empty -> WalletNFTItemEmpty( is WalletNFTItemUM.Empty -> WalletNFTItemEmpty(
@ -70,13 +70,13 @@ private fun WalletNFTItemEmpty(onClick: () -> Unit, modifier: Modifier = Modifie
painter = painterResource(R.drawable.img_nft_empty_collection), painter = painterResource(R.drawable.img_nft_empty_collection),
contentDescription = null, contentDescription = null,
modifier = Modifier modifier = Modifier
.size(TangemTheme.dimens2.x10) .size(40.dp)
.layoutId(TangemRowLayoutId.HEAD), .layoutId(TangemRowLayoutId.HEAD),
) )
Text( Text(
text = stringResourceSafe(R.string.nft_wallet_title), text = stringResourceSafe(R.string.nft_wallet_title),
style = TangemTheme.typography2.bodySemibold16, style = TangemTheme.typography3.body.medium,
color = TangemTheme.colors2.text.neutral.primary, color = TangemTheme.colors3.text.primary,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier modifier = Modifier
@ -85,8 +85,8 @@ private fun WalletNFTItemEmpty(onClick: () -> Unit, modifier: Modifier = Modifie
) )
Text( Text(
text = stringResourceSafe(R.string.nft_wallet_receive_nft), text = stringResourceSafe(R.string.nft_wallet_receive_nft),
style = TangemTheme.typography2.captionSemibold12, style = TangemTheme.typography3.caption.medium,
color = TangemTheme.colors2.text.neutral.secondary, color = TangemTheme.colors3.text.secondary,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier modifier = Modifier
@ -96,7 +96,7 @@ private fun WalletNFTItemEmpty(onClick: () -> Unit, modifier: Modifier = Modifie
Icon( Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_chevron_small_right_24), imageVector = ImageVector.vectorResource(R.drawable.ic_chevron_small_right_24),
contentDescription = null, contentDescription = null,
tint = TangemTheme.colors2.graphic.neutral.tertiaryConstant, tint = TangemTheme.colors3.icon.secondary,
modifier = Modifier.layoutId(TangemRowLayoutId.TAIL), modifier = Modifier.layoutId(TangemRowLayoutId.TAIL),
) )
} }
@ -116,9 +116,9 @@ private fun WalletNFTItemContent(state: WalletNFTItemUM.Content, onClick: () ->
} }
Text( Text(
text = stringResourceSafe(R.string.nft_wallet_title), text = stringResourceSafe(R.string.nft_wallet_title),
style = TangemTheme.typography2.bodySemibold16.applyBladeBrush( style = TangemTheme.typography3.body.medium.applyBladeBrush(
isEnabled = state.isFlickering, isEnabled = state.isFlickering,
textColor = TangemTheme.colors2.text.neutral.primary, textColor = TangemTheme.colors3.text.primary,
), ),
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
@ -132,9 +132,9 @@ private fun WalletNFTItemContent(state: WalletNFTItemUM.Content, onClick: () ->
state.allAssetsCount, state.allAssetsCount,
state.collectionsCount, state.collectionsCount,
), ),
style = TangemTheme.typography2.captionSemibold12.applyBladeBrush( style = TangemTheme.typography3.caption.medium.applyBladeBrush(
isEnabled = state.isFlickering, isEnabled = state.isFlickering,
textColor = TangemTheme.colors2.text.neutral.secondary, textColor = TangemTheme.colors3.text.secondary,
), ),
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
@ -145,7 +145,7 @@ private fun WalletNFTItemContent(state: WalletNFTItemUM.Content, onClick: () ->
Icon( Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_chevron_small_right_24), imageVector = ImageVector.vectorResource(R.drawable.ic_chevron_small_right_24),
contentDescription = null, contentDescription = null,
tint = TangemTheme.colors2.graphic.neutral.tertiaryConstant, tint = TangemTheme.colors3.icon.secondary,
modifier = Modifier.layoutId(TangemRowLayoutId.TAIL), modifier = Modifier.layoutId(TangemRowLayoutId.TAIL),
) )
} }
@ -173,8 +173,8 @@ private fun WalletNFTItemFailed(modifier: Modifier = Modifier) {
} }
Text( Text(
text = stringResourceSafe(R.string.nft_wallet_title), text = stringResourceSafe(R.string.nft_wallet_title),
style = TangemTheme.typography2.bodySemibold16, style = TangemTheme.typography3.body.medium,
color = TangemTheme.colors2.text.neutral.primary, color = TangemTheme.colors3.text.primary,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier modifier = Modifier
@ -183,8 +183,8 @@ private fun WalletNFTItemFailed(modifier: Modifier = Modifier) {
) )
Text( Text(
text = stringResourceSafe(R.string.nft_wallet_unable_to_load), text = stringResourceSafe(R.string.nft_wallet_unable_to_load),
style = TangemTheme.typography2.captionSemibold12, style = TangemTheme.typography3.caption.medium,
color = TangemTheme.colors2.text.neutral.secondary, color = TangemTheme.colors3.text.secondary,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier modifier = Modifier
@ -207,7 +207,7 @@ private fun WalletNFTItemLoading(modifier: Modifier = Modifier) {
.layoutId(TangemRowLayoutId.HEAD), .layoutId(TangemRowLayoutId.HEAD),
) )
TextShimmer( TextShimmer(
style = TangemTheme.typography2.bodySemibold16, style = TangemTheme.typography3.body.medium,
radius = TangemTheme.dimens2.x25, radius = TangemTheme.dimens2.x25,
modifier = Modifier modifier = Modifier
@ -216,7 +216,7 @@ private fun WalletNFTItemLoading(modifier: Modifier = Modifier) {
.width(TangemTheme.dimens.size110), .width(TangemTheme.dimens.size110),
) )
TextShimmer( TextShimmer(
style = TangemTheme.typography2.captionSemibold12, style = TangemTheme.typography3.caption.medium,
radius = TangemTheme.dimens2.x25, radius = TangemTheme.dimens2.x25,
modifier = Modifier modifier = Modifier
.layoutId(TangemRowLayoutId.START_BOTTOM) .layoutId(TangemRowLayoutId.START_BOTTOM)
@ -251,7 +251,7 @@ private fun BoxScope.CollectionsPreviews(previews: ImmutableList<CollectionPrevi
}, },
error = { error = {
Box( Box(
modifier = previewModifier.background(TangemTheme.colors2.surface.level2), modifier = previewModifier.background(TangemTheme.colors3.bg.primary),
) )
}, },
contentDescription = null, contentDescription = null,
@ -259,10 +259,9 @@ private fun BoxScope.CollectionsPreviews(previews: ImmutableList<CollectionPrevi
} }
is CollectionPreview.More -> { is CollectionPreview.More -> {
Icon( Icon(
modifier = previewModifier modifier = previewModifier.background(TangemTheme.colors3.bg.primary),
.background(TangemTheme.colors2.surface.level2),
imageVector = ImageVector.vectorResource(R.drawable.ic_nft_preview_more_16), imageVector = ImageVector.vectorResource(R.drawable.ic_nft_preview_more_16),
tint = TangemTheme.colors2.text.neutral.secondary, tint = TangemTheme.colors3.text.secondary,
contentDescription = null, contentDescription = null,
) )
} }
@ -289,7 +288,7 @@ private fun BoxScope.previews2Modifiers(): List<Modifier> = listOf(
.zIndex(1f) .zIndex(1f)
.padding(TangemTheme.dimens2.x0_5) .padding(TangemTheme.dimens2.x0_5)
.clip(RoundedCornerShape(topStart = 10.dp)) .clip(RoundedCornerShape(topStart = 10.dp))
.background(TangemTheme.colors2.surface.level3) .background(TangemTheme.colors3.bg.secondary)
.padding(start = TangemTheme.dimens2.x0_5, top = TangemTheme.dimens2.x0_5) .padding(start = TangemTheme.dimens2.x0_5, top = TangemTheme.dimens2.x0_5)
.size(TangemTheme.dimens2.x6) .size(TangemTheme.dimens2.x6)
.clip(RoundedCornerShape(TangemTheme.dimens2.x2)) .clip(RoundedCornerShape(TangemTheme.dimens2.x2))
@ -307,7 +306,7 @@ private fun BoxScope.previews3Modifiers(): List<Modifier> = listOf(
.zIndex(1f) .zIndex(1f)
.padding(top = 10.dp, end = 1.dp) .padding(top = 10.dp, end = 1.dp)
.clip(RoundedCornerShape(8.dp)) .clip(RoundedCornerShape(8.dp))
.background(TangemTheme.colors2.surface.level3) .background(TangemTheme.colors3.bg.secondary)
.padding(TangemTheme.dimens2.x0_5) .padding(TangemTheme.dimens2.x0_5)
.size(18.dp) .size(18.dp)
.clip(RoundedCornerShape(6.dp)) .clip(RoundedCornerShape(6.dp))
@ -347,7 +346,7 @@ private fun Preview_WalletNFTItem(@PreviewParameter(WalletNFTItemProvider2::clas
WalletNFTItem2( WalletNFTItem2(
state = state, state = state,
modifier = Modifier modifier = Modifier
.background(TangemTheme.colors2.surface.level1), .background(TangemTheme.colors3.bg.primary),
) )
} }
} }

View file

@ -102,7 +102,7 @@ internal fun WalletBalance(
walletBalanceUM = walletBalanceUM, walletBalanceUM = walletBalanceUM,
isBalanceHidden = isBalanceHidden, isBalanceHidden = isBalanceHidden,
) )
SpacerH(TangemTheme.dimens2.x3) SpacerH(12.dp)
Box( Box(
modifier = Modifier.onGloballyPositioned { coordinates -> modifier = Modifier.onGloballyPositioned { coordinates ->
val rawBottomPx = coordinates.boundsInRoot().bottom val rawBottomPx = coordinates.boundsInRoot().bottom
@ -113,9 +113,9 @@ internal fun WalletBalance(
SubtitleRow(walletBalanceUM = walletBalanceUM) SubtitleRow(walletBalanceUM = walletBalanceUM)
} }
} }
SpacerH(TangemTheme.dimens2.x2) SpacerH(8.dp)
ActionButtons(buttons = hapticButtons, modifier = Modifier.fillMaxWidth()) ActionButtons(buttons = hapticButtons, modifier = Modifier.fillMaxWidth())
SpacerH(TangemTheme.dimens2.x6) SpacerH(24.dp)
} }
} }
@ -140,20 +140,20 @@ private fun SubtitleRow(walletBalanceUM: WalletBalanceUM, modifier: Modifier = M
is WalletAdditionalInfo.Content.SyncProgress -> { is WalletAdditionalInfo.Content.SyncProgress -> {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1_5), horizontalArrangement = Arrangement.spacedBy(6.dp),
) { ) {
Text( Text(
text = resourceReference( text = resourceReference(
id = R.string.initial_wallet_sync_restore_progress, id = R.string.initial_wallet_sync_restore_progress,
formatArgs = wrappedList(content.progressPercent), formatArgs = wrappedList(content.progressPercent),
).resolveReference(), ).resolveReference(),
style = TangemTheme.typography2.bodyRegular14, style = TangemTheme.typography3.subheading.medium,
color = TangemTheme.colors2.text.neutral.tertiary, color = TangemTheme.colors3.text.secondary,
modifier = Modifier.testTag(MainScreenTestTags.SYNC_PROGRESS_TEXT), modifier = Modifier.testTag(MainScreenTestTags.SYNC_PROGRESS_TEXT),
) )
CircularProgressIndicator( CircularProgressIndicator(
modifier = Modifier.size(19.dp), modifier = Modifier.size(19.dp),
color = TangemTheme.colors2.graphic.neutral.primary, color = TangemTheme.colors3.icon.secondary,
strokeWidth = 2.dp, strokeWidth = 2.dp,
) )
} }
@ -161,12 +161,12 @@ private fun SubtitleRow(walletBalanceUM: WalletBalanceUM, modifier: Modifier = M
else -> { else -> {
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1), horizontalArrangement = Arrangement.spacedBy(4.dp),
) { ) {
Text( Text(
text = walletBalanceUM.name, text = walletBalanceUM.name,
style = TangemTheme.typography2.bodyRegular14, style = TangemTheme.typography3.subheading.medium,
color = TangemTheme.colors2.text.neutral.tertiary, color = TangemTheme.colors3.text.secondary,
modifier = Modifier.testTag(MainScreenTestTags.CARD_TITLE), modifier = Modifier.testTag(MainScreenTestTags.CARD_TITLE),
) )
TangemDeviceIcon( TangemDeviceIcon(
@ -194,36 +194,36 @@ private fun Balance(walletBalanceUM: WalletBalanceUM, isBalanceHidden: Boolean,
when (balanceUM) { when (balanceUM) {
is WalletBalanceUM.Content -> Text( is WalletBalanceUM.Content -> Text(
text = balanceUM.balance.orMaskWithStars(isBalanceHidden).resolveAnnotatedReference(), text = balanceUM.balance.orMaskWithStars(isBalanceHidden).resolveAnnotatedReference(),
style = TangemTheme.typography2.titleRegular44.applyBladeBrush( style = TangemTheme.typography3.display.medium.applyBladeBrush(
isEnabled = balanceUM.isBalanceFlickering, isEnabled = balanceUM.isBalanceFlickering,
textColor = TangemTheme.colors2.text.neutral.primary, textColor = TangemTheme.colors3.text.primary,
), ),
color = TangemTheme.colors2.text.neutral.primary, color = TangemTheme.colors3.text.primary,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
autoSize = TextAutoSize.StepBased( autoSize = TextAutoSize.StepBased(
minFontSize = TangemTheme.typography2.bodySemibold15.fontSize, minFontSize = TangemTheme.typography3.body.medium.fontSize,
maxFontSize = TangemTheme.typography2.titleRegular44.fontSize, maxFontSize = TangemTheme.typography3.display.medium.fontSize,
), ),
) )
is WalletBalanceUM.Error -> Text( is WalletBalanceUM.Error -> Text(
text = StringsSigns.DASH_SIGN, text = StringsSigns.DASH_SIGN,
style = TangemTheme.typography2.titleRegular44, style = TangemTheme.typography3.display.medium,
color = TangemTheme.colors2.text.neutral.primary, color = TangemTheme.colors3.text.primary,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
autoSize = TextAutoSize.StepBased( autoSize = TextAutoSize.StepBased(
minFontSize = TangemTheme.typography2.bodySemibold15.fontSize, minFontSize = TangemTheme.typography3.body.medium.fontSize,
maxFontSize = TangemTheme.typography2.titleRegular44.fontSize, maxFontSize = TangemTheme.typography3.display.medium.fontSize,
), ),
) )
is WalletBalanceUM.Loading -> TextShimmer( is WalletBalanceUM.Loading -> TextShimmer(
text = "123456", text = "123456",
style = TangemTheme.typography2.titleRegular44, style = TangemTheme.typography3.display.medium,
radius = TangemTheme.dimens2.x25, radius = 100.dp,
) )
is WalletBalanceUM.Empty -> TextPlaceholder( is WalletBalanceUM.Empty -> TextPlaceholder(
textStyle = TangemTheme.typography2.titleRegular44, textStyle = TangemTheme.typography3.display.medium,
width = 200.dp, width = 200.dp,
) )
} }
@ -244,7 +244,7 @@ private fun WalletBalance_Preview(
behavior = rememberTangemExitUntilCollapsedScrollBehavior(), behavior = rememberTangemExitUntilCollapsedScrollBehavior(),
buttons = params.actionsList, buttons = params.actionsList,
isBalanceHidden = false, isBalanceHidden = false,
modifier = Modifier.background(TangemTheme.colors2.surface.level1), modifier = Modifier.background(TangemTheme.colors3.bg.secondary),
) )
} }
} }

View file

@ -46,10 +46,10 @@ internal fun WalletListContent(
promoBannersBlockComponent: PromoBannersBlockComponent? = null, promoBannersBlockComponent: PromoBannersBlockComponent? = null,
walletId: String? = null, walletId: String? = null,
) { ) {
val containerColor = TangemTheme.colors2.surface.level1 val containerColor = TangemTheme.colors3.bg.primary
val movableItemModifier = Modifier.padding(horizontal = TangemTheme.dimens2.x3) val movableItemModifier = Modifier.padding(horizontal = 12.dp)
val itemModifier = movableItemModifier.padding(top = TangemTheme.dimens2.x3) val itemModifier = movableItemModifier.padding(top = 12.dp)
LazyColumn( LazyColumn(
modifier = modifier.testTag(MainScreenTestTags.SCREEN_CONTAINER), modifier = modifier.testTag(MainScreenTestTags.SCREEN_CONTAINER),
@ -73,7 +73,7 @@ internal fun WalletListContent(
item(key = "PromoBannersBlock") { item(key = "PromoBannersBlock") {
component.ContentWithPadding( component.ContentWithPadding(
horizontalItemPadding = 12.dp, horizontalItemPadding = 12.dp,
modifier = Modifier.padding(top = TangemTheme.dimens2.x3), modifier = Modifier.padding(top = 12.dp),
walletId = walletId, walletId = walletId,
) )
} }

View file

@ -1,41 +1,41 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.common package com.tangem.feature.wallet.presentation.wallet.ui.components.common
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.material3.Icon
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.lerp
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.haze.hazeEffectTangem import androidx.compose.ui.unit.dp
import com.tangem.core.ui.ds.topbar.TangemTopBar import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.topbar.TangemTopBarActionContent
import com.tangem.core.ui.ds.topbar.TangemTopBarActionUM import com.tangem.core.ui.ds.topbar.TangemTopBarActionUM
import com.tangem.core.ui.ds.topbar.collapsing.TangemCollapsingAppBarBehavior import com.tangem.core.ui.ds.topbar.collapsing.TangemCollapsingAppBarBehavior
import com.tangem.core.ui.ds.topbar.collapsing.rememberTangemExitUntilCollapsedScrollBehavior import com.tangem.core.ui.ds.topbar.collapsing.rememberTangemExitUntilCollapsedScrollBehavior
import com.tangem.core.ui.ds2.button.TangemButton
import com.tangem.core.ui.ds2.topnavigation.TangemNavigationText
import com.tangem.core.ui.ds2.topnavigation.TangemTopNavigation
import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.orMaskWithStars import com.tangem.core.ui.extensions.orMaskWithStars
import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.haptic.TangemHapticEffect import com.tangem.core.ui.haptic.TangemHapticEffect
import com.tangem.core.ui.res.* import com.tangem.core.ui.res.LocalHapticManager
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
import com.tangem.core.ui.test.MainScreenTestTags import com.tangem.core.ui.test.MainScreenTestTags
import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTopBarConfig import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTopBarConfig
import dev.chrisbanes.haze.HazeProgressive
import dev.chrisbanes.haze.HazeTint
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
private const val VISIBILITY_THRESHOLD = 0.5f private const val VISIBILITY_THRESHOLD = 0.5f
private val BRAND_ICON_SLOT_SIZE = 44.dp
/** /**
* Wallet screen collapsing top bar * Wallet screen collapsing top bar
@ -53,76 +53,54 @@ internal fun WalletTopBar(
behavior: TangemCollapsingAppBarBehavior, behavior: TangemCollapsingAppBarBehavior,
) { ) {
val hapticManager = LocalHapticManager.current val hapticManager = LocalHapticManager.current
Surface( val isWrappedBalanceShown by remember {
color = Color.Unspecified, derivedStateOf { behavior.state.collapsedFraction > VISIBILITY_THRESHOLD }
contentColor = Color.Unspecified, }
modifier = Modifier.hazeEffectTangemTopBar(behavior),
) {
val isWrappedBalanceShown by remember {
derivedStateOf { behavior.state.collapsedFraction > VISIBILITY_THRESHOLD }
}
val wrappedBalance = remember(walletBalance, isWrappedBalanceShown, isBalanceHidden) { val wrappedBalance = remember(walletBalance, isWrappedBalanceShown, isBalanceHidden) {
walletBalance?.orMaskWithStars(isBalanceHidden).takeIf { isWrappedBalanceShown } walletBalance?.orMaskWithStars(isBalanceHidden).takeIf { isWrappedBalanceShown }
} }
TangemTopBar( TangemTopNavigation(
title = wrappedBalance, modifier = Modifier.testTag(MainScreenTestTags.TOP_BAR),
startContent = { contentAlign = TangemTopNavigation.ContentAlign.Center,
TangemTopBarActionContent( startButton = {
TangemTopBarActionUM( // Non-clickable brand mark, sized to align with the trailing action buttons.
iconRes = R.drawable.ic_tangem_24, Box(modifier = Modifier.size(BRAND_ICON_SLOT_SIZE), contentAlignment = Alignment.Center) {
isActionable = false, Icon(
), painter = painterResource(id = R.drawable.ic_tangem_24),
contentDescription = null,
tint = TangemTheme.colors3.icon.primary,
) )
}, }
endContent = { },
Row( endButtonsGroup = if (topBarConfig.endActions.isNotEmpty()) {
modifier = Modifier {
.clip(CircleShape) topBarConfig.endActions.forEach { action ->
.background( TangemButton(
lerp( variant = TangemButton.Variant.Ghost,
start = Color.Transparent, size = TangemButton.Size.X11,
stop = TangemTheme.colors2.button.backgroundSecondary, iconStart = TangemIconUM.Icon(iconRes = action.iconRes),
fraction = behavior.state.collapsedFraction, isEnabled = action.isActionable && action.onClick != null,
), onClick = {
), hapticManager.perform(TangemHapticEffect.View.ContextClick)
) { action.onClick?.invoke()
topBarConfig.endActions.forEach { action -> },
TangemTopBarActionContent( modifier = Modifier.testTag(MainScreenTestTags.MORE_BUTTON),
action.copy( )
onClick = action.onClick?.let { onClick ->
{
hapticManager.perform(TangemHapticEffect.View.ContextClick)
onClick()
}
},
),
modifier = Modifier.testTag(MainScreenTestTags.MORE_BUTTON),
)
}
} }
}, }
modifier = Modifier } else {
.statusBarsPadding() null
.testTag(MainScreenTestTags.TOP_BAR), },
) contentColumn = {
} AnimatedVisibility(visible = wrappedBalance != null) {
} wrappedBalance?.let { balance ->
TangemNavigationText(text = balance, role = TangemNavigationText.Role.Title)
@Composable }
private fun Modifier.hazeEffectTangemTopBar(behavior: TangemCollapsingAppBarBehavior): Modifier { }
val rootBackground by LocalRootBackgroundColor.current },
val intensity by animateFloatAsState(targetValue = behavior.state.collapsedFraction * 2f) )
return hazeEffectTangem {
fallbackTint = HazeTint(rootBackground.copy(alpha = intensity / 2))
progressive = HazeProgressive.verticalGradient(
startIntensity = intensity,
endIntensity = 0f,
preferPerformance = true,
)
}
} }
// region Preview // region Preview

View file

@ -40,9 +40,6 @@ import com.tangem.core.ui.components.tokenlist.NON_CONTENT_TOKENS_LIST_KEY
import com.tangem.core.ui.components.tokenlist.TokenListItem import com.tangem.core.ui.components.tokenlist.TokenListItem
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
import com.tangem.core.ui.decorations.roundedShapeItemDecoration import com.tangem.core.ui.decorations.roundedShapeItemDecoration
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.ds.image.TangemIcon import com.tangem.core.ui.ds.image.TangemIcon
import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.row.header.TangemHeaderRow import com.tangem.core.ui.ds.row.header.TangemHeaderRow
@ -51,6 +48,7 @@ import com.tangem.core.ui.ds.row.internal.TangemRowTailUM
import com.tangem.core.ui.ds.row.token.TangemTokenRow import com.tangem.core.ui.ds.row.token.TangemTokenRow
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.core.ui.ds.row.token.internal.TokenRowTitle import com.tangem.core.ui.ds.row.token.internal.TokenRowTitle
import com.tangem.core.ui.ds2.button.TangemButton
import com.tangem.core.ui.extensions.* import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.test.MainScreenTestTags import com.tangem.core.ui.test.MainScreenTestTags
@ -159,13 +157,13 @@ private fun LazyListScope.tokenItem(
val itemModifier = modifier val itemModifier = modifier
.testTag(MainScreenTestTags.TOKEN_LIST_ITEM) .testTag(MainScreenTestTags.TOKEN_LIST_ITEM)
.semantics { lazyListItemPosition = index } .semantics { lazyListItemPosition = index }
.padding(top = if (index == 0) TangemTheme.dimens2.x3 else 0.dp) .padding(top = if (index == 0) 12.dp else 0.dp)
.roundedShapeItemDecoration( .roundedShapeItemDecoration(
radius = 18.dp, radius = 18.dp,
currentIndex = index, currentIndex = index,
addDefaultPadding = false, addDefaultPadding = false,
lastIndex = lastIndex, lastIndex = lastIndex,
backgroundColor = TangemTheme.colors2.surface.level3, backgroundColor = TangemTheme.colors3.bg.secondary,
) )
var position by remember { mutableStateOf(Offset.Zero) } var position by remember { mutableStateOf(Offset.Zero) }
@ -261,11 +259,11 @@ private fun LazyListScope.portfolioItem(
modifier = modifier modifier = modifier
.animateItem(fadeInSpec = null, placementSpec = null, fadeOutSpec = null) .animateItem(fadeInSpec = null, placementSpec = null, fadeOutSpec = null)
.roundedShapeItemDecoration( .roundedShapeItemDecoration(
radius = if (listItem.isExpanded) TangemTheme.dimens2.x6 else TangemTheme.dimens2.x5, radius = if (listItem.isExpanded) 24.dp else 20.dp,
currentIndex = tokenIndex + 1, currentIndex = tokenIndex + 1,
addDefaultPadding = false, addDefaultPadding = false,
lastIndex = lastIndex, lastIndex = lastIndex,
backgroundColor = TangemTheme.colors2.surface.level3, backgroundColor = TangemTheme.colors3.bg.secondary,
), ),
visible = listItem.isExpanded, visible = listItem.isExpanded,
) { ) {
@ -327,15 +325,15 @@ private fun LazyListScope.accountItem(
) )
val portfolioModifier = modifier val portfolioModifier = modifier
.padding(top = if (index != 0) TangemTheme.dimens2.x2 else TangemTheme.dimens2.x3) .padding(top = if (index != 0) 8.dp else 12.dp)
.testTag(MainScreenTestTags.ACCOUNT_LIST_ITEM) .testTag(MainScreenTestTags.ACCOUNT_LIST_ITEM)
.semantics { lazyListItemPosition = index } .semantics { lazyListItemPosition = index }
.roundedShapeItemDecoration( .roundedShapeItemDecoration(
currentIndex = 0, currentIndex = 0,
radius = if (listItem.isExpanded) TangemTheme.dimens2.x6 else TangemTheme.dimens2.x5, radius = if (listItem.isExpanded) 24.dp else 20.dp,
addDefaultPadding = false, addDefaultPadding = false,
lastIndex = effectiveLastIndex, lastIndex = effectiveLastIndex,
backgroundColor = TangemTheme.colors2.surface.level3, backgroundColor = TangemTheme.colors3.bg.secondary,
) )
if (listItem.isCollapsable) { if (listItem.isCollapsable) {
PortfolioRowItem( PortfolioRowItem(
@ -422,7 +420,7 @@ internal fun PortfolioRowItem(
} }
val iconBoxSize = when (headIcon) { val iconBoxSize = when (headIcon) {
is TangemIconUM.Empty -> TangemTheme.dimens2.x9 is TangemIconUM.Empty -> 36.dp
else -> size.toBoxSize() else -> size.toBoxSize()
} }
TangemIcon( TangemIcon(
@ -444,8 +442,8 @@ internal fun PortfolioRowItem(
animationSpec = tween(durationMillis = 350), animationSpec = tween(durationMillis = 350),
) )
val startStyle = TangemTheme.typography2.captionSemibold12 val startStyle = TangemTheme.typography3.caption.medium
val stopStyle = TangemTheme.typography2.bodySemibold16 val stopStyle = TangemTheme.typography3.body.medium
val textStyle by remember(animationFraction.value) { val textStyle by remember(animationFraction.value) {
derivedStateOf { lerp(startStyle, stopStyle, animationFraction.value) } derivedStateOf { lerp(startStyle, stopStyle, animationFraction.value) }
@ -509,7 +507,7 @@ private fun LazyListScope.nonContentItem(modifier: Modifier = Modifier) {
NonContentItemContent( NonContentItemContent(
modifier = modifier modifier = modifier
.animateItem(fadeInSpec = null, fadeOutSpec = null) .animateItem(fadeInSpec = null, fadeOutSpec = null)
.padding(top = TangemTheme.dimens.spacing96), .padding(top = 96.dp),
) )
} }
} }
@ -521,7 +519,7 @@ private fun LazyListScope.nonContentItem2(onEmptyClick: () -> Unit, modifier: Mo
) { ) {
NonContentItemContentV2( NonContentItemContentV2(
onClick = onEmptyClick, onClick = onEmptyClick,
textColor = TangemTheme.colors2.text.neutral.tertiary, textColor = TangemTheme.colors3.text.secondary,
modifier = modifier modifier = modifier
.animateItem(fadeInSpec = null, fadeOutSpec = null) .animateItem(fadeInSpec = null, fadeOutSpec = null)
.padding(top = 46.dp), .padding(top = 46.dp),
@ -540,17 +538,17 @@ private fun LazyListScope.nonContentAccountItem(listItem: TokensListItemUM2.Port
modifier = modifier modifier = modifier
.animateItem(fadeInSpec = null, placementSpec = null, fadeOutSpec = null) .animateItem(fadeInSpec = null, placementSpec = null, fadeOutSpec = null)
.roundedShapeItemDecoration( .roundedShapeItemDecoration(
radius = if (listItem.isExpanded) TangemTheme.dimens2.x6 else TangemTheme.dimens2.x5, radius = if (listItem.isExpanded) 24.dp else 20.dp,
addDefaultPadding = false, addDefaultPadding = false,
currentIndex = 1, currentIndex = 1,
lastIndex = 1, lastIndex = 1,
backgroundColor = TangemTheme.colors2.surface.level3, backgroundColor = TangemTheme.colors3.bg.secondary,
), ),
visible = listItem.isExpanded, visible = listItem.isExpanded,
) { ) {
NonContentItemContentV2( NonContentItemContentV2(
modifier = Modifier.padding(vertical = 36.dp), modifier = Modifier.padding(vertical = 36.dp),
textColor = TangemTheme.colors2.text.neutral.secondary, textColor = TangemTheme.colors3.text.secondary,
onClick = listItem.onEmptyClick, onClick = listItem.onEmptyClick,
) )
} }
@ -566,23 +564,23 @@ internal fun NonContentItemContentV2(textColor: Color, modifier: Modifier = Modi
Icon( Icon(
painter = painterResource(id = R.drawable.ic_empty_64), painter = painterResource(id = R.drawable.ic_empty_64),
contentDescription = null, contentDescription = null,
modifier = Modifier.size(size = TangemTheme.dimens2.x16), modifier = Modifier.size(64.dp),
tint = TangemTheme.colors2.graphic.neutral.quaternary, tint = TangemTheme.colors3.icon.tertiary,
) )
SpacerH(TangemTheme.dimens2.x5) SpacerH(20.dp)
Text( Text(
text = stringResourceSafe(id = R.string.main_empty_tokens_list_message), text = stringResourceSafe(id = R.string.main_empty_tokens_list_message),
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing48), modifier = Modifier.padding(horizontal = 48.dp),
color = textColor, color = textColor,
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
style = TangemTheme.typography2.bodyRegular14, style = TangemTheme.typography3.subheading.medium,
) )
SpacerH(TangemTheme.dimens2.x4) SpacerH(16.dp)
SecondaryTangemButton( TangemButton(
text = resourceReference(id = R.string.common_add_tokens), text = resourceReference(id = R.string.common_add_tokens),
variant = TangemButton.Variant.Secondary,
size = TangemButton.Size.X9,
onClick = onClick, onClick = onClick,
size = TangemButtonSize.X8,
shape = TangemButtonShape.Rounded,
modifier = Modifier.testTag(MainScreenTestTags.EMPTY_TOKENS_ADD_BUTTON), modifier = Modifier.testTag(MainScreenTestTags.EMPTY_TOKENS_ADD_BUTTON),
) )
} }

View file

@ -78,7 +78,6 @@ internal class YieldSupplyToEarnBlockConverter : Converter<YieldSupplyUM, EarnBl
text = resourceReference(CoreResR.string.yield_module_transaction_enter), text = resourceReference(CoreResR.string.yield_module_transaction_enter),
style = EarnBlockUM.TitleUM.Style.Large, style = EarnBlockUM.TitleUM.Style.Large,
tone = EarnBlockUM.TitleUM.Tone.Primary, tone = EarnBlockUM.TitleUM.Tone.Primary,
iconUM = buildTitleIcon(value),
), ),
subtitleUM = EarnBlockUM.SubtitleUM.Text( subtitleUM = EarnBlockUM.SubtitleUM.Text(
text = combinedReference( text = combinedReference(
@ -89,18 +88,19 @@ internal class YieldSupplyToEarnBlockConverter : Converter<YieldSupplyUM, EarnBl
style = EarnBlockUM.SubtitleUM.Style.Small, style = EarnBlockUM.SubtitleUM.Style.Small,
tone = EarnBlockUM.SubtitleUM.Tone.Accent, tone = EarnBlockUM.SubtitleUM.Tone.Accent,
), ),
trailingUM = EarnBlockUM.TrailingUM.Button( trailingUM = buildTrailing(value),
text = resourceReference(CoreResR.string.details_title),
style = EarnBlockUM.TrailingUM.Button.Style.Secondary,
),
onClick = value.onClick, onClick = value.onClick,
) )
} }
private fun buildTitleIcon(value: YieldSupplyUM.Content): EarnBlockUM.TitleUM.IconUM? = when { private fun buildTrailing(value: YieldSupplyUM.Content): EarnBlockUM.TrailingUM = when {
value.shouldShowWarningIcon -> EarnBlockUM.TitleUM.IconUM(tone = EarnBlockUM.TitleUM.IconTone.Warning) value.shouldShowWarningIcon -> EarnBlockUM.TrailingUM.StatusIcon(
value.shouldShowInfoIcon -> EarnBlockUM.TitleUM.IconUM(tone = EarnBlockUM.TitleUM.IconTone.Info) tone = EarnBlockUM.TrailingUM.StatusIcon.Tone.Warning,
else -> null )
value.shouldShowInfoIcon -> EarnBlockUM.TrailingUM.StatusIcon(
tone = EarnBlockUM.TrailingUM.StatusIcon.Tone.Info,
)
else -> EarnBlockUM.TrailingUM.Chevron
} }
private fun buildProcessingEnter(): EarnBlockUM.Content { private fun buildProcessingEnter(): EarnBlockUM.Content {
@ -109,17 +109,18 @@ internal class YieldSupplyToEarnBlockConverter : Converter<YieldSupplyUM, EarnBl
backgroundUM = EarnBlockUM.BackgroundUM.Surface, backgroundUM = EarnBlockUM.BackgroundUM.Surface,
iconUM = EarnBlockUM.IconUM.Glowing(iconRes = CoreUiR.drawable.ic_yield_40), iconUM = EarnBlockUM.IconUM.Glowing(iconRes = CoreUiR.drawable.ic_yield_40),
titleUM = EarnBlockUM.TitleUM( titleUM = EarnBlockUM.TitleUM(
text = resourceReference(CoreResR.string.common_yield_mode), text = combinedReference(
resourceReference(CoreResR.string.common_yield_mode),
stringReference(StringsSigns.WHITE_SPACE),
resourceReference(CoreResR.string.common_enabling),
),
style = EarnBlockUM.TitleUM.Style.Large, style = EarnBlockUM.TitleUM.Style.Large,
tone = EarnBlockUM.TitleUM.Tone.Primary, tone = EarnBlockUM.TitleUM.Tone.Primary,
), ),
subtitleUM = EarnBlockUM.SubtitleUM.Text( subtitleUM = null,
text = resourceReference(CoreResR.string.common_enabling), trailingUM = EarnBlockUM.TrailingUM.Loader(
style = EarnBlockUM.SubtitleUM.Style.Small, tone = EarnBlockUM.TrailingUM.Loader.LoaderTone.Positive,
tone = EarnBlockUM.SubtitleUM.Tone.Accent,
loader = EarnBlockUM.SubtitleUM.Loader(tone = EarnBlockUM.SubtitleUM.LoaderTone.Positive),
), ),
trailingUM = null,
) )
} }
@ -127,19 +128,23 @@ internal class YieldSupplyToEarnBlockConverter : Converter<YieldSupplyUM, EarnBl
return EarnBlockUM.Content( return EarnBlockUM.Content(
type = EarnBlockUM.Type.YieldSupply, type = EarnBlockUM.Type.YieldSupply,
backgroundUM = EarnBlockUM.BackgroundUM.Surface, backgroundUM = EarnBlockUM.BackgroundUM.Surface,
iconUM = EarnBlockUM.IconUM.Plain(iconRes = CoreUiR.drawable.ic_yield_disabling_40), iconUM = EarnBlockUM.IconUM.Glowing(
iconRes = CoreUiR.drawable.ic_yield_40,
tone = EarnBlockUM.IconUM.Tone.Warning,
),
titleUM = EarnBlockUM.TitleUM( titleUM = EarnBlockUM.TitleUM(
text = resourceReference(CoreResR.string.common_yield_mode), text = combinedReference(
resourceReference(CoreResR.string.common_yield_mode),
stringReference(StringsSigns.WHITE_SPACE),
resourceReference(CoreResR.string.common_disabling),
),
style = EarnBlockUM.TitleUM.Style.Large, style = EarnBlockUM.TitleUM.Style.Large,
tone = EarnBlockUM.TitleUM.Tone.Primary, tone = EarnBlockUM.TitleUM.Tone.Primary,
), ),
subtitleUM = EarnBlockUM.SubtitleUM.Text( subtitleUM = null,
text = resourceReference(CoreResR.string.common_disabling), trailingUM = EarnBlockUM.TrailingUM.Loader(
style = EarnBlockUM.SubtitleUM.Style.Small, tone = EarnBlockUM.TrailingUM.Loader.LoaderTone.Muted,
tone = EarnBlockUM.SubtitleUM.Tone.Disabled,
loader = EarnBlockUM.SubtitleUM.Loader(tone = EarnBlockUM.SubtitleUM.LoaderTone.Muted),
), ),
trailingUM = null,
) )
} }
} }

View file

@ -1,10 +1,6 @@
package com.tangem.features.yield.supply.impl.main.model package com.tangem.features.yield.supply.impl.main.model
import arrow.core.Option import arrow.core.*
import arrow.core.left
import arrow.core.none
import arrow.core.right
import arrow.core.some
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter import com.tangem.common.routing.AppRouter
@ -34,27 +30,13 @@ import com.tangem.domain.yield.supply.models.YieldMarketToken
import com.tangem.domain.yield.supply.models.YieldSupplyPendingStatus import com.tangem.domain.yield.supply.models.YieldSupplyPendingStatus
import com.tangem.domain.yield.supply.promo.usecase.GetBoostedApyUseCase import com.tangem.domain.yield.supply.promo.usecase.GetBoostedApyUseCase
import com.tangem.domain.yield.supply.promo.usecase.IsYieldBoostPromoEnabledForTokenUseCase import com.tangem.domain.yield.supply.promo.usecase.IsYieldBoostPromoEnabledForTokenUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyActivateUseCase import com.tangem.domain.yield.supply.usecase.*
import com.tangem.domain.yield.supply.usecase.YieldSupplyDeactivateUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyEnterStatusFlowUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyEnterStatusUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetDustMinAmountUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyGetTokenStatusUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyIsAvailableUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyMinAmountUseCase
import com.tangem.features.yield.supply.api.YieldSupplyComponent import com.tangem.features.yield.supply.api.YieldSupplyComponent
import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics
import com.tangem.features.yield.supply.impl.YieldBoostStoryPreloader import com.tangem.features.yield.supply.impl.YieldBoostStoryPreloader
import com.tangem.features.yield.supply.impl.main.entity.YieldSupplyUM import com.tangem.features.yield.supply.impl.main.entity.YieldSupplyUM
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.coEvery import io.mockk.*
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.slot
import io.mockk.unmockkObject
import io.mockk.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
@ -258,7 +240,9 @@ internal class YieldSupplyModelTest {
assertThat(approveEvent.blockchain).isEqualTo("Ethereum") assertThat(approveEvent.blockchain).isEqualTo("Ethereum")
val block = model.uiState.value as EarnBlockUM.Content val block = model.uiState.value as EarnBlockUM.Content
assertThat(block.titleUM.iconUM?.tone).isEqualTo(EarnBlockUM.TitleUM.IconTone.Warning) assertThat(block.trailingUM).isEqualTo(
EarnBlockUM.TrailingUM.StatusIcon(tone = EarnBlockUM.TrailingUM.StatusIcon.Tone.Warning),
)
} }
@Test @Test
@ -290,7 +274,9 @@ internal class YieldSupplyModelTest {
assertThat(legacy.shouldShowInfoIcon).isTrue() assertThat(legacy.shouldShowInfoIcon).isTrue()
assertThat(legacy.shouldShowWarningIcon).isFalse() assertThat(legacy.shouldShowWarningIcon).isFalse()
val block = model.uiState.value as EarnBlockUM.Content val block = model.uiState.value as EarnBlockUM.Content
assertThat(block.titleUM.iconUM?.tone).isEqualTo(EarnBlockUM.TitleUM.IconTone.Info) assertThat(block.trailingUM).isEqualTo(
EarnBlockUM.TrailingUM.StatusIcon(tone = EarnBlockUM.TrailingUM.StatusIcon.Tone.Info),
)
} }
@Test @Test

View file

@ -54,9 +54,7 @@ internal class YieldSupplyToEarnBlockConverterTest {
assertThat(earnBlock.titleUM.tone).isEqualTo(EarnBlockUM.TitleUM.Tone.Primary) assertThat(earnBlock.titleUM.tone).isEqualTo(EarnBlockUM.TitleUM.Tone.Primary)
assertThat((earnBlock.subtitleUM as EarnBlockUM.SubtitleUM.Text).tone) assertThat((earnBlock.subtitleUM as EarnBlockUM.SubtitleUM.Text).tone)
.isEqualTo(EarnBlockUM.SubtitleUM.Tone.Accent) .isEqualTo(EarnBlockUM.SubtitleUM.Tone.Accent)
assertThat(earnBlock.trailingUM).isInstanceOf(EarnBlockUM.TrailingUM.Button::class.java) assertThat(earnBlock.trailingUM).isEqualTo(EarnBlockUM.TrailingUM.Chevron)
val button = earnBlock.trailingUM as EarnBlockUM.TrailingUM.Button
assertThat(button.isEnabled).isTrue()
assertThat(earnBlock.onClick).isNotNull() assertThat(earnBlock.onClick).isNotNull()
earnBlock.onClick?.invoke() earnBlock.onClick?.invoke()
assertThat(clicked).isTrue() assertThat(clicked).isTrue()
@ -71,7 +69,9 @@ internal class YieldSupplyToEarnBlockConverterTest {
assertThat(earnBlock.type).isEqualTo(EarnBlockUM.Type.YieldSupply) assertThat(earnBlock.type).isEqualTo(EarnBlockUM.Type.YieldSupply)
assertThat(earnBlock.backgroundUM).isEqualTo(EarnBlockUM.BackgroundUM.Surface) assertThat(earnBlock.backgroundUM).isEqualTo(EarnBlockUM.BackgroundUM.Surface)
assertThat(earnBlock.iconUM).isInstanceOf(EarnBlockUM.IconUM.Glowing::class.java) assertThat(earnBlock.iconUM).isInstanceOf(EarnBlockUM.IconUM.Glowing::class.java)
assertThat(earnBlock.trailingUM).isNull() assertThat(earnBlock.trailingUM).isEqualTo(
EarnBlockUM.TrailingUM.Loader(tone = EarnBlockUM.TrailingUM.Loader.LoaderTone.Positive),
)
} }
@Test @Test
@ -82,12 +82,19 @@ internal class YieldSupplyToEarnBlockConverterTest {
val earnBlock = result as EarnBlockUM.Content val earnBlock = result as EarnBlockUM.Content
assertThat(earnBlock.type).isEqualTo(EarnBlockUM.Type.YieldSupply) assertThat(earnBlock.type).isEqualTo(EarnBlockUM.Type.YieldSupply)
assertThat(earnBlock.backgroundUM).isEqualTo(EarnBlockUM.BackgroundUM.Surface) assertThat(earnBlock.backgroundUM).isEqualTo(EarnBlockUM.BackgroundUM.Surface)
assertThat(earnBlock.iconUM).isInstanceOf(EarnBlockUM.IconUM.Plain::class.java) assertThat(earnBlock.iconUM).isEqualTo(
assertThat(earnBlock.trailingUM).isNull() EarnBlockUM.IconUM.Glowing(
iconRes = com.tangem.core.ui.R.drawable.ic_yield_40,
tone = EarnBlockUM.IconUM.Tone.Warning,
),
)
assertThat(earnBlock.trailingUM).isEqualTo(
EarnBlockUM.TrailingUM.Loader(tone = EarnBlockUM.TrailingUM.Loader.LoaderTone.Muted),
)
} }
@Test @Test
fun `GIVEN Content with showWarningIcon WHEN convert THEN title Warning Icon`() { fun `GIVEN Content with showWarningIcon WHEN convert THEN trailing Warning StatusIcon`() {
val content = YieldSupplyUM.Content( val content = YieldSupplyUM.Content(
apy = "5.1", apy = "5.1",
title = stringReference("Yield Mode"), title = stringReference("Yield Mode"),
@ -102,13 +109,13 @@ internal class YieldSupplyToEarnBlockConverterTest {
assertThat(result).isInstanceOf(EarnBlockUM.Content::class.java) assertThat(result).isInstanceOf(EarnBlockUM.Content::class.java)
val earnBlock = result as EarnBlockUM.Content val earnBlock = result as EarnBlockUM.Content
assertThat(earnBlock.titleUM.iconUM).isNotNull() assertThat(earnBlock.trailingUM).isEqualTo(
assertThat(earnBlock.titleUM.iconUM?.tone).isEqualTo(EarnBlockUM.TitleUM.IconTone.Warning) EarnBlockUM.TrailingUM.StatusIcon(tone = EarnBlockUM.TrailingUM.StatusIcon.Tone.Warning),
assertThat(earnBlock.trailingUM).isInstanceOf(EarnBlockUM.TrailingUM.Button::class.java) )
} }
@Test @Test
fun `GIVEN Content with showInfoIcon WHEN convert THEN title Info Icon`() { fun `GIVEN Content with showInfoIcon WHEN convert THEN trailing Info StatusIcon`() {
val content = YieldSupplyUM.Content( val content = YieldSupplyUM.Content(
apy = "5.1", apy = "5.1",
title = stringReference("Yield Mode"), title = stringReference("Yield Mode"),
@ -123,9 +130,9 @@ internal class YieldSupplyToEarnBlockConverterTest {
assertThat(result).isInstanceOf(EarnBlockUM.Content::class.java) assertThat(result).isInstanceOf(EarnBlockUM.Content::class.java)
val earnBlock = result as EarnBlockUM.Content val earnBlock = result as EarnBlockUM.Content
assertThat(earnBlock.titleUM.iconUM).isNotNull() assertThat(earnBlock.trailingUM).isEqualTo(
assertThat(earnBlock.titleUM.iconUM?.tone).isEqualTo(EarnBlockUM.TitleUM.IconTone.Info) EarnBlockUM.TrailingUM.StatusIcon(tone = EarnBlockUM.TrailingUM.StatusIcon.Tone.Info),
assertThat(earnBlock.trailingUM).isInstanceOf(EarnBlockUM.TrailingUM.Button::class.java) )
} }
@Test @Test
@ -143,8 +150,9 @@ internal class YieldSupplyToEarnBlockConverterTest {
val result = converter.convert(content) val result = converter.convert(content)
val earnBlock = result as EarnBlockUM.Content val earnBlock = result as EarnBlockUM.Content
assertThat(earnBlock.titleUM.iconUM).isNotNull() assertThat(earnBlock.trailingUM).isEqualTo(
assertThat(earnBlock.titleUM.iconUM?.tone).isEqualTo(EarnBlockUM.TitleUM.IconTone.Warning) EarnBlockUM.TrailingUM.StatusIcon(tone = EarnBlockUM.TrailingUM.StatusIcon.Tone.Warning),
)
} }
@Test @Test