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

View file

@ -45,8 +45,11 @@ sealed interface EarnBlockUM {
@Immutable
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
/** Accent — glow/tint follow the block [Type]; Warning — yellow icon tint and glow. */
enum class Tone { Accent, Warning }
}
@Immutable
@ -54,13 +57,9 @@ sealed interface EarnBlockUM {
val text: TextReference,
val style: Style,
val tone: Tone,
val iconUM: IconUM? = null,
) {
enum class Style { Large, Small }
enum class Tone { Primary, Secondary, Disabled, Accent }
data class IconUM(val tone: IconTone)
enum class IconTone { Warning, Info }
}
@Immutable
@ -69,7 +68,6 @@ sealed interface EarnBlockUM {
val text: TextReference,
val style: Style,
val tone: Tone,
val loader: Loader? = null,
) : SubtitleUM
data class AccentedText(
@ -78,11 +76,8 @@ sealed interface EarnBlockUM {
val style: Style,
) : SubtitleUM
data class Loader(val tone: LoaderTone)
enum class Style { Large, Small }
enum class Tone { Primary, Disabled, Accent }
enum class LoaderTone { Positive, Muted }
}
@Immutable
@ -100,5 +95,18 @@ sealed interface EarnBlockUM {
val cryptoValue: TextReference,
val isBalanceHidden: Boolean,
) : 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,
) {
Row(
modifier = Modifier.offset {
IntOffset(animState.slideOffset.value.roundToInt(), 0)
},
modifier = Modifier
.wrapContentWidth(unbounded = true)
.offset {
IntOffset(animState.slideOffset.value.roundToInt(), 0)
},
horizontalArrangement = Arrangement.spacedBy(SPACING),
verticalAlignment = Alignment.CenterVertically,
) {
@ -367,8 +369,8 @@ val TangemPagerIndicatorColors: PagerIndicatorColors
@Composable
@ReadOnlyComposable
get() = PagerIndicatorColors(
active = TangemTheme.colors2.graphic.neutral.primary,
inactive = TangemTheme.colors2.graphic.neutral.tertiary,
active = TangemTheme.colors3.bg.inverse,
inactive = TangemTheme.colors3.bg.opaque.secondary,
overlay = null,
)
@ -413,6 +415,7 @@ private class TangemPagerIndicatorPreviewProvider : PreviewParameterProvider<Int
1,
2,
3,
4,
5,
6,
7,

View file

@ -23,11 +23,10 @@ import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.constrainHeight
import androidx.compose.ui.unit.dp
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.TangemButtonUM
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.resolveReference
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),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SecondaryTangemButton(
tangemIconUM = button.tangemIconUM,
TangemButton(
onClick = button.onClick,
variant = TangemButton.Variant.Material,
isEnabled = button.isEnabled,
shape = TangemButtonShape.Rounded,
onLongClick = button.onLongClick,
iconStart = button.tangemIconUM,
size = TangemButton.Size.X14,
)
Text(
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 androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
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.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
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.TangemThemePreviewRedesign
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.token.block.impl.model.formatter.toChartType
import com.tangem.features.markets.token.block.impl.ui.state.TokenMarketBlockUM
import kotlinx.collections.immutable.toImmutableList
import kotlin.random.Random
import com.tangem.core.ui.R as CoreR
private val ChartWidth: Dp = 52.dp
private val ChartHeight: Dp = 32.dp
@ -115,7 +111,7 @@ internal fun TokenMarketBlock(tokenMarketBlockUM: TokenMarketBlockUM, modifier:
withHazeEffect = false,
modifier = Modifier
.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 com.tangem.core.ui.components.SpacerH8
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.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
@ -134,10 +134,7 @@ private fun BannersCarousel(
SpacerH8()
PagerIndicator(
pagerState = pagerState,
hasBackground = false,
)
TangemPagerIndicator(pagerState = pagerState)
}
}

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

View file

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

View file

@ -1,13 +1,17 @@
package com.tangem.feature.wallet.child.organizetokens.model.converter.items
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.getTotalFiatAmount
import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.ds.image.TangemIconUM
import com.tangem.core.ui.ds.row.internal.TangemRowTailUM
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.styledStringReference
import com.tangem.core.ui.format.bigdecimal.cryptoStyled
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.formatStyled
@ -36,7 +40,21 @@ internal class OrganizeTokenItemConverter(
id = getTokenItemId(currency.id),
headIconUM = TangemIconUM.Currency(iconStateConverter.convert(currencyStatus)),
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,
topEndContentUM = TangemTokenRowUM.EndContentUM.Content(

View file

@ -62,11 +62,11 @@ private fun Menu(
private fun SortByBalanceMenuSection(organizeMenuUM: OrganizeTokensUM.OrganizeMenuUM, onDropdownDismiss: () -> Unit) {
Text(
text = stringResourceSafe(R.string.organize_tokens_sort_by_balance),
style = TangemTheme.typography2.headingSemibold17,
style = TangemTheme.typography3.body.medium,
color = if (organizeMenuUM.isSortedByBalance) {
TangemTheme.colors2.text.status.disabled
TangemTheme.colors3.text.tertiary
} else {
TangemTheme.colors2.text.neutral.primary
TangemTheme.colors3.text.primary
},
maxLines = 1,
modifier = Modifier
@ -79,12 +79,12 @@ private fun SortByBalanceMenuSection(organizeMenuUM: OrganizeTokensUM.OrganizeMe
},
enabled = !organizeMenuUM.isSortedByBalance,
)
.padding(vertical = TangemTheme.dimens2.x2_5, horizontal = TangemTheme.dimens2.x4),
.padding(vertical = 10.dp, horizontal = 16.dp),
)
HorizontalDivider(
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()
},
)
.padding(vertical = TangemTheme.dimens2.x2_5, horizontal = TangemTheme.dimens2.x4),
.padding(vertical = 10.dp, horizontal = 16.dp),
) {
Text(
text = stringResourceSafe(R.string.organize_tokens_group),
style = TangemTheme.typography2.headingSemibold17,
color = TangemTheme.colors2.text.neutral.primary,
style = TangemTheme.typography3.body.medium,
color = TangemTheme.colors3.text.primary,
maxLines = 1,
)
Box(
modifier = Modifier.size(TangemTheme.dimens2.x6),
modifier = Modifier.size(24.dp),
contentAlignment = Alignment.Center,
) {
if (organizeMenuUM.isGrouped) {
Box(
modifier = Modifier
.padding(TangemTheme.dimens2.x0_5)
.size(TangemTheme.dimens2.x5)
.padding(2.dp)
.size(20.dp)
.background(
color = TangemTheme.colors2.graphic.neutral.primary,
color = TangemTheme.colors3.icon.primary,
shape = CircleShape,
),
) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_check_default_24),
contentDescription = null,
tint = TangemTheme.colors2.graphic.neutral.primaryInverted,
tint = TangemTheme.colors3.icon.inverse,
modifier = Modifier
.align(Alignment.Center)
.padding(TangemTheme.dimens2.x0_5)
.size(TangemTheme.dimens2.x4),
.padding(2.dp)
.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.haze.hazeEffectTangem
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.TangemIconUM
import com.tangem.core.ui.ds.row.TangemRowContainer
import com.tangem.core.ui.ds.row.TangemRowLayoutId
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.internal.TokenRowEndContent
import com.tangem.core.ui.ds.row.token.internal.TokenRowTitle
import com.tangem.core.ui.ds.topbar.TangemTopBar
import com.tangem.core.ui.ds.topbar.TangemTopBarActionContent
import com.tangem.core.ui.ds.topbar.TangemTopBarActionUM
import com.tangem.core.ui.ds.topbar.TangemTopBarType
import com.tangem.core.ui.ds2.button.TangemButton
import com.tangem.core.ui.ds2.topnavigation.TangemTopNavigation
import com.tangem.core.ui.extensions.copy
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
@ -82,21 +81,21 @@ internal fun OrganizeTokensContent(
onDismissRequest = onDismiss,
content = TangemBottomSheetConfigContent.Empty,
),
containerColor = TangemTheme.colors2.surface.level2,
containerColor = TangemTheme.colors3.bg.primary,
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),
type = TangemTopBarType.BottomSheet,
endContent = {
TangemTopBarActionContent(
actionUM = TangemTopBarActionUM(
iconRes = R.drawable.ic_exchange_mini_24,
isActionable = true,
onClick = { isShowDropdownMenu = true },
ghostModeProgress = 0f,
),
endButton = {
TangemButton(
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(
organizeMenuUM = organizeTokensUM.organizeMenuUM,
@ -137,7 +136,7 @@ private fun TokenList(
var draggingItem by remember { mutableStateOf<OrganizeRowItemUM?>(null) }
Box(
modifier = modifier.background(TangemTheme.colors2.surface.level2),
modifier = modifier.background(TangemTheme.colors3.bg.primary),
) {
val footerInset = LocalTangemBottomSheetContentBottomInset.current
@ -238,7 +237,7 @@ private fun LazyItemScope.DraggableItem(
enabled = isValidDropTarget,
) { _ ->
val modifierWithBackground = itemModifier
.background(color = TangemTheme.colors.background.primary)
.background(color = TangemTheme.colors3.bg.secondary)
.semantics { lazyListItemPosition = index }
when (item) {
@ -281,16 +280,23 @@ private fun BoxScope.BottomButtons(organizeTokensUM: OrganizeTokensUM) {
.padding(bottom = bottomBarHeight + TangemTheme.dimens2.x4),
) {
TangemButton(
buttonUM = organizeTokensUM.cancelButton,
modifier = Modifier
.weight(1f)
.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(
buttonUM = organizeTokensUM.applyButton,
modifier = Modifier
.weight(1f)
.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(
endContentUM = tokenRowUM.topEndContentUM,
isBalanceHidden = isBalanceHidden,
textStyle = TangemTheme.typography2.captionSemibold12,
textColor = TangemTheme.colors2.text.neutral.secondary,
textStyle = TangemTheme.typography3.caption.medium,
textColor = TangemTheme.colors3.text.secondary,
placeholderWidth = TangemTheme.dimens2.x11,
modifier = Modifier
.layoutId(layoutId = TangemRowLayoutId.START_BOTTOM)

View file

@ -1,5 +1,6 @@
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.ds.button.TangemButtonType
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.token.TangemTokenRowUM
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.stringReference
import com.tangem.core.ui.extensions.styledStringReference
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.OrganizeTokensUM
import com.tangem.feature.wallet.child.organizetokens.entity.RoundingModeUM
@ -75,7 +79,16 @@ internal object OrganizeTokensPreview {
tokenRowUM = draggableToken.copy(
id = "${group.id}_token_$tokenNumber",
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,

View file

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

View file

@ -29,7 +29,7 @@ internal object WalletBalancePreview {
styledStringReference(
".56",
{
TangemTheme.typography2.headingRegular28.toSpanStyle()
TangemTheme.typography3.heading.medium.toSpanStyle()
},
),
stringReference(" $"),
@ -55,7 +55,7 @@ internal object WalletBalancePreview {
styledStringReference(
".56",
{
TangemTheme.typography2.headingRegular28.toSpanStyle()
TangemTheme.typography3.heading.medium.toSpanStyle()
},
),
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.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.wallet.impl.R
/**
@ -27,16 +26,7 @@ internal sealed class WalletActionButtons(
val buttonUM: TangemButtonUM
get() = TangemButtonUM(
text = text,
tangemIconUM = TangemIconUM.Icon(
iconRes = iconRes,
tintReference = {
if (isEnabled) {
TangemTheme.colors2.graphic.neutral.primary
} else {
TangemTheme.colors2.graphic.neutral.quaternary
}
},
),
tangemIconUM = TangemIconUM.Icon(iconRes = iconRes),
type = TangemButtonType.Secondary,
shape = TangemButtonShape.Rounded,
onClick = onClick,

View file

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

View file

@ -52,7 +52,7 @@ internal class MultiWalletBalanceUMTransformer(
fiat(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.neutral.secondary) },
spanStyleReference = { SpanStyle(color = TangemTheme.colors3.text.secondary) },
)
},
balance = fiatBalance.amount.formatStyled {
@ -60,7 +60,7 @@ internal class MultiWalletBalanceUMTransformer(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
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.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
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.dp
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.bottomsheets.sheet.TangemBottomSheetDraggableHeader
import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
@ -181,9 +181,6 @@ private fun WalletContent2(
onBottomSheetStateChange: (BottomSheetState) -> 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 pullToRefreshConfig by remember {
mutableStateOf(
@ -207,10 +204,15 @@ private fun WalletContent2(
)
},
) { 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(
bottom = paddingValues.calculateBottomPadding() + marketHintApproxHeight,
bottom = paddingValues.calculateBottomPadding() +
marketsHintHeight +
collapsedBodyOverhang +
TangemTheme.dimens2.x2,
)
val selectedWalletIndex by rememberUpdatedState(state.selectedWalletIndex)
@ -235,22 +237,17 @@ private fun WalletContent2(
.fillMaxSize()
.hazeSourceTangem(zIndex = -2f),
) {
val backgroundColor = if (LocalIsInDarkTheme.current) {
TangemTheme.colors2.surface.level1
} else {
TangemTheme.colors2.surface.level2
}
Box(
modifier = Modifier
.matchParentSize()
.background(backgroundColor),
.background(TangemTheme.colors3.bg.primary),
)
val isSheetExpanded by remember {
derivedStateOf { bottomSheetState.targetValue == TangemSheetValue.Expanded }
}
if (!isSheetExpanded) {
NorthernLightsBackground(
containerColor = backgroundColor,
containerColor = TangemTheme.colors3.bg.primary,
modifier = Modifier
.graphicsLayer { alpha = 1 - behavior.state.collapsedFraction * 2 }
.matchParentSize(),
@ -262,10 +259,10 @@ private fun WalletContent2(
pullToRefreshState = pullToRefreshState,
pullToRefreshConfig = pullToRefreshConfig,
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
// 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(
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth(fraction = .6f)
.padding(bottom = peekHeight),
.padding(bottom = paddingValues.calculateBottomPadding())
.onSizeChanged { size ->
marketsHintHeight = with(density) { size.height.toDp() }
},
isVisible = isShowMarketsHint,
obstacleBounds = remember(currentWalletIndex) {
{ organizeButtonBounds[currentWalletIndex] }
@ -400,7 +397,7 @@ private fun WalletContent2(
.fillMaxWidth(),
isVisible = state.showMarketsOnboarding,
availableHeight = maxHeight,
sheetTopInset = TangemTheme.dimens2.x3,
sheetTopInset = 12.dp,
bottomSheetState = bottomSheetState,
onCloseClick = state.onDismissMarketsTooltip,
obstacleBounds = remember(walletsPagerState, organizeButtonBounds) {
@ -425,15 +422,15 @@ private inline fun BaseScaffoldWithMarkets(
) {
val density = LocalDensity.current
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 bottomSheetState = rememberTangemStandardBottomSheetState()
val scaffoldState = rememberTangemBottomSheetScaffoldState(bottomSheetState = bottomSheetState)
val expandedBackground = TangemTheme.colors2.surface.level2
val collapsedBackground = TangemTheme.colors2.surface.level3
val expandedBackground = TangemTheme.colors3.bg.primary
val collapsedBackground = TangemTheme.colors3.bg.secondary
val background by animateColorAsState(
targetValue = if (bottomSheetState.targetValue == TangemSheetValue.Expanded) {
expandedBackground
@ -527,8 +524,8 @@ private fun BottomSheet(
val maxHeight = LocalWindowSize.current.height
val shape = RoundedCornerShape(
topStart = TangemTheme.dimens2.x8,
topEnd = TangemTheme.dimens2.x8,
topStart = 32.dp,
topEnd = 32.dp,
)
CustomBottomSheet(
state = bottomSheetState,

View file

@ -61,14 +61,14 @@ internal fun MarketsHint(isVisible: Boolean, modifier: Modifier = Modifier, obst
) {
Text(
text = stringResourceSafe(R.string.markets_hint),
style = TangemTheme.typography2.bodyRegular15,
color = TangemTheme.colors2.text.neutral.tertiary,
style = TangemTheme.typography3.subheading.medium,
color = TangemTheme.colors3.text.secondary,
textAlign = TextAlign.Center,
)
Icon(
modifier = Modifier.size(size = 24.dp),
painter = painterResource(id = R.drawable.ic_chevron_24),
tint = TangemTheme.colors2.graphic.neutral.tertiary,
tint = TangemTheme.colors3.icon.secondary,
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.unit.*
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.res.TangemTheme
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.utils.toPx
import com.tangem.feature.wallet.impl.R
@ -118,14 +122,14 @@ internal fun MarketsTooltip(
@Composable
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 tooltipShape = remember(tipDpSize) { TooltipShape(cornerRadius = 16.dp, tipSize = tipDpSize) }
Row(
modifier = modifier
.shadow(
elevation = TangemTheme.dimens.elevation12,
elevation = 12.dp,
shape = tooltipShape,
clip = false,
ambientColor = Color.Black.copy(alpha = 0.7f),
@ -149,27 +153,21 @@ internal fun MarketsTooltipContent(onCloseClick: () -> Unit, modifier: Modifier
) {
Text(
text = stringResourceSafe(id = R.string.markets_tooltip_v2_title),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.primary1,
style = TangemTheme.typography3.subheading.medium,
color = TangemTheme.colors3.text.primary,
)
Text(
text = stringResourceSafe(id = R.string.markets_tooltip_message),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.secondary,
style = TangemTheme.typography3.caption.medium,
color = TangemTheme.colors3.text.secondary,
)
}
Icon(
modifier = Modifier
.size(size = 16.dp)
.clickable(
interactionSource = null,
indication = null,
onClick = onCloseClick,
)
.testTag(MarketTooltipTestTags.CLOSE_BUTTON),
painter = painterResource(id = R.drawable.ic_close_24),
tint = TangemTheme.colors.icon.informative,
contentDescription = null,
TangemButton(
variant = TangemButton.Variant.Ghost,
size = TangemButton.Size.X7,
iconStart = TangemIconUM.Icon(imageVector = Icons.ic_cross_20),
onClick = onCloseClick,
modifier = Modifier.testTag(MarketTooltipTestTags.CLOSE_BUTTON),
)
}
}

View file

@ -9,7 +9,7 @@ import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.layout.boundsInRoot
import androidx.compose.ui.layout.onGloballyPositioned
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.haptic.TangemHapticEffect
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) {
(state as? WalletUM.Content)?.let { content ->
item(key = "NFTCollections", contentType = "NFTCollections") {
WalletNFTItem(
WalletNFTItem2(
modifier = itemModifier,
state = content.nftState,
)
@ -62,15 +62,18 @@ internal fun LazyListScope.organizeTokens2(
}
TangemButton(
buttonUM = organizeButton.copy(
onClick = {
hapticManager.perform(TangemHapticEffect.View.ContextClick)
organizeButton.onClick()
},
),
modifier = itemModifier
.onGloballyPositioned { currentOnButtonBoundsChange(it.boundsInRoot()) }
.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) {
val nftModifier = modifier
.clip(RoundedCornerShape(18.dp))
.background(TangemTheme.colors2.surface.level3)
.background(TangemTheme.colors3.bg.secondary)
when (state) {
is WalletNFTItemUM.Hidden -> Unit
is WalletNFTItemUM.Empty -> WalletNFTItemEmpty(
@ -70,13 +70,13 @@ private fun WalletNFTItemEmpty(onClick: () -> Unit, modifier: Modifier = Modifie
painter = painterResource(R.drawable.img_nft_empty_collection),
contentDescription = null,
modifier = Modifier
.size(TangemTheme.dimens2.x10)
.size(40.dp)
.layoutId(TangemRowLayoutId.HEAD),
)
Text(
text = stringResourceSafe(R.string.nft_wallet_title),
style = TangemTheme.typography2.bodySemibold16,
color = TangemTheme.colors2.text.neutral.primary,
style = TangemTheme.typography3.body.medium,
color = TangemTheme.colors3.text.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier
@ -85,8 +85,8 @@ private fun WalletNFTItemEmpty(onClick: () -> Unit, modifier: Modifier = Modifie
)
Text(
text = stringResourceSafe(R.string.nft_wallet_receive_nft),
style = TangemTheme.typography2.captionSemibold12,
color = TangemTheme.colors2.text.neutral.secondary,
style = TangemTheme.typography3.caption.medium,
color = TangemTheme.colors3.text.secondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier
@ -96,7 +96,7 @@ private fun WalletNFTItemEmpty(onClick: () -> Unit, modifier: Modifier = Modifie
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_chevron_small_right_24),
contentDescription = null,
tint = TangemTheme.colors2.graphic.neutral.tertiaryConstant,
tint = TangemTheme.colors3.icon.secondary,
modifier = Modifier.layoutId(TangemRowLayoutId.TAIL),
)
}
@ -116,9 +116,9 @@ private fun WalletNFTItemContent(state: WalletNFTItemUM.Content, onClick: () ->
}
Text(
text = stringResourceSafe(R.string.nft_wallet_title),
style = TangemTheme.typography2.bodySemibold16.applyBladeBrush(
style = TangemTheme.typography3.body.medium.applyBladeBrush(
isEnabled = state.isFlickering,
textColor = TangemTheme.colors2.text.neutral.primary,
textColor = TangemTheme.colors3.text.primary,
),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
@ -132,9 +132,9 @@ private fun WalletNFTItemContent(state: WalletNFTItemUM.Content, onClick: () ->
state.allAssetsCount,
state.collectionsCount,
),
style = TangemTheme.typography2.captionSemibold12.applyBladeBrush(
style = TangemTheme.typography3.caption.medium.applyBladeBrush(
isEnabled = state.isFlickering,
textColor = TangemTheme.colors2.text.neutral.secondary,
textColor = TangemTheme.colors3.text.secondary,
),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
@ -145,7 +145,7 @@ private fun WalletNFTItemContent(state: WalletNFTItemUM.Content, onClick: () ->
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_chevron_small_right_24),
contentDescription = null,
tint = TangemTheme.colors2.graphic.neutral.tertiaryConstant,
tint = TangemTheme.colors3.icon.secondary,
modifier = Modifier.layoutId(TangemRowLayoutId.TAIL),
)
}
@ -173,8 +173,8 @@ private fun WalletNFTItemFailed(modifier: Modifier = Modifier) {
}
Text(
text = stringResourceSafe(R.string.nft_wallet_title),
style = TangemTheme.typography2.bodySemibold16,
color = TangemTheme.colors2.text.neutral.primary,
style = TangemTheme.typography3.body.medium,
color = TangemTheme.colors3.text.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier
@ -183,8 +183,8 @@ private fun WalletNFTItemFailed(modifier: Modifier = Modifier) {
)
Text(
text = stringResourceSafe(R.string.nft_wallet_unable_to_load),
style = TangemTheme.typography2.captionSemibold12,
color = TangemTheme.colors2.text.neutral.secondary,
style = TangemTheme.typography3.caption.medium,
color = TangemTheme.colors3.text.secondary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier
@ -207,7 +207,7 @@ private fun WalletNFTItemLoading(modifier: Modifier = Modifier) {
.layoutId(TangemRowLayoutId.HEAD),
)
TextShimmer(
style = TangemTheme.typography2.bodySemibold16,
style = TangemTheme.typography3.body.medium,
radius = TangemTheme.dimens2.x25,
modifier = Modifier
@ -216,7 +216,7 @@ private fun WalletNFTItemLoading(modifier: Modifier = Modifier) {
.width(TangemTheme.dimens.size110),
)
TextShimmer(
style = TangemTheme.typography2.captionSemibold12,
style = TangemTheme.typography3.caption.medium,
radius = TangemTheme.dimens2.x25,
modifier = Modifier
.layoutId(TangemRowLayoutId.START_BOTTOM)
@ -251,7 +251,7 @@ private fun BoxScope.CollectionsPreviews(previews: ImmutableList<CollectionPrevi
},
error = {
Box(
modifier = previewModifier.background(TangemTheme.colors2.surface.level2),
modifier = previewModifier.background(TangemTheme.colors3.bg.primary),
)
},
contentDescription = null,
@ -259,10 +259,9 @@ private fun BoxScope.CollectionsPreviews(previews: ImmutableList<CollectionPrevi
}
is CollectionPreview.More -> {
Icon(
modifier = previewModifier
.background(TangemTheme.colors2.surface.level2),
modifier = previewModifier.background(TangemTheme.colors3.bg.primary),
imageVector = ImageVector.vectorResource(R.drawable.ic_nft_preview_more_16),
tint = TangemTheme.colors2.text.neutral.secondary,
tint = TangemTheme.colors3.text.secondary,
contentDescription = null,
)
}
@ -289,7 +288,7 @@ private fun BoxScope.previews2Modifiers(): List<Modifier> = listOf(
.zIndex(1f)
.padding(TangemTheme.dimens2.x0_5)
.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)
.size(TangemTheme.dimens2.x6)
.clip(RoundedCornerShape(TangemTheme.dimens2.x2))
@ -307,7 +306,7 @@ private fun BoxScope.previews3Modifiers(): List<Modifier> = listOf(
.zIndex(1f)
.padding(top = 10.dp, end = 1.dp)
.clip(RoundedCornerShape(8.dp))
.background(TangemTheme.colors2.surface.level3)
.background(TangemTheme.colors3.bg.secondary)
.padding(TangemTheme.dimens2.x0_5)
.size(18.dp)
.clip(RoundedCornerShape(6.dp))
@ -347,7 +346,7 @@ private fun Preview_WalletNFTItem(@PreviewParameter(WalletNFTItemProvider2::clas
WalletNFTItem2(
state = state,
modifier = Modifier
.background(TangemTheme.colors2.surface.level1),
.background(TangemTheme.colors3.bg.primary),
)
}
}

View file

@ -102,7 +102,7 @@ internal fun WalletBalance(
walletBalanceUM = walletBalanceUM,
isBalanceHidden = isBalanceHidden,
)
SpacerH(TangemTheme.dimens2.x3)
SpacerH(12.dp)
Box(
modifier = Modifier.onGloballyPositioned { coordinates ->
val rawBottomPx = coordinates.boundsInRoot().bottom
@ -113,9 +113,9 @@ internal fun WalletBalance(
SubtitleRow(walletBalanceUM = walletBalanceUM)
}
}
SpacerH(TangemTheme.dimens2.x2)
SpacerH(8.dp)
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 -> {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1_5),
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
Text(
text = resourceReference(
id = R.string.initial_wallet_sync_restore_progress,
formatArgs = wrappedList(content.progressPercent),
).resolveReference(),
style = TangemTheme.typography2.bodyRegular14,
color = TangemTheme.colors2.text.neutral.tertiary,
style = TangemTheme.typography3.subheading.medium,
color = TangemTheme.colors3.text.secondary,
modifier = Modifier.testTag(MainScreenTestTags.SYNC_PROGRESS_TEXT),
)
CircularProgressIndicator(
modifier = Modifier.size(19.dp),
color = TangemTheme.colors2.graphic.neutral.primary,
color = TangemTheme.colors3.icon.secondary,
strokeWidth = 2.dp,
)
}
@ -161,12 +161,12 @@ private fun SubtitleRow(walletBalanceUM: WalletBalanceUM, modifier: Modifier = M
else -> {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1),
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
text = walletBalanceUM.name,
style = TangemTheme.typography2.bodyRegular14,
color = TangemTheme.colors2.text.neutral.tertiary,
style = TangemTheme.typography3.subheading.medium,
color = TangemTheme.colors3.text.secondary,
modifier = Modifier.testTag(MainScreenTestTags.CARD_TITLE),
)
TangemDeviceIcon(
@ -194,36 +194,36 @@ private fun Balance(walletBalanceUM: WalletBalanceUM, isBalanceHidden: Boolean,
when (balanceUM) {
is WalletBalanceUM.Content -> Text(
text = balanceUM.balance.orMaskWithStars(isBalanceHidden).resolveAnnotatedReference(),
style = TangemTheme.typography2.titleRegular44.applyBladeBrush(
style = TangemTheme.typography3.display.medium.applyBladeBrush(
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,
overflow = TextOverflow.Ellipsis,
autoSize = TextAutoSize.StepBased(
minFontSize = TangemTheme.typography2.bodySemibold15.fontSize,
maxFontSize = TangemTheme.typography2.titleRegular44.fontSize,
minFontSize = TangemTheme.typography3.body.medium.fontSize,
maxFontSize = TangemTheme.typography3.display.medium.fontSize,
),
)
is WalletBalanceUM.Error -> Text(
text = StringsSigns.DASH_SIGN,
style = TangemTheme.typography2.titleRegular44,
color = TangemTheme.colors2.text.neutral.primary,
style = TangemTheme.typography3.display.medium,
color = TangemTheme.colors3.text.primary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
autoSize = TextAutoSize.StepBased(
minFontSize = TangemTheme.typography2.bodySemibold15.fontSize,
maxFontSize = TangemTheme.typography2.titleRegular44.fontSize,
minFontSize = TangemTheme.typography3.body.medium.fontSize,
maxFontSize = TangemTheme.typography3.display.medium.fontSize,
),
)
is WalletBalanceUM.Loading -> TextShimmer(
text = "123456",
style = TangemTheme.typography2.titleRegular44,
radius = TangemTheme.dimens2.x25,
style = TangemTheme.typography3.display.medium,
radius = 100.dp,
)
is WalletBalanceUM.Empty -> TextPlaceholder(
textStyle = TangemTheme.typography2.titleRegular44,
textStyle = TangemTheme.typography3.display.medium,
width = 200.dp,
)
}
@ -244,7 +244,7 @@ private fun WalletBalance_Preview(
behavior = rememberTangemExitUntilCollapsedScrollBehavior(),
buttons = params.actionsList,
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,
walletId: String? = null,
) {
val containerColor = TangemTheme.colors2.surface.level1
val containerColor = TangemTheme.colors3.bg.primary
val movableItemModifier = Modifier.padding(horizontal = TangemTheme.dimens2.x3)
val itemModifier = movableItemModifier.padding(top = TangemTheme.dimens2.x3)
val movableItemModifier = Modifier.padding(horizontal = 12.dp)
val itemModifier = movableItemModifier.padding(top = 12.dp)
LazyColumn(
modifier = modifier.testTag(MainScreenTestTags.SCREEN_CONTAINER),
@ -73,7 +73,7 @@ internal fun WalletListContent(
item(key = "PromoBannersBlock") {
component.ContentWithPadding(
horizontalItemPadding = 12.dp,
modifier = Modifier.padding(top = TangemTheme.dimens2.x3),
modifier = Modifier.padding(top = 12.dp),
walletId = walletId,
)
}

View file

@ -1,41 +1,41 @@
package com.tangem.feature.wallet.presentation.wallet.ui.components.common
import android.content.res.Configuration
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.*
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.lerp
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.haze.hazeEffectTangem
import com.tangem.core.ui.ds.topbar.TangemTopBar
import com.tangem.core.ui.ds.topbar.TangemTopBarActionContent
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.ds.image.TangemIconUM
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.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.orMaskWithStars
import com.tangem.core.ui.extensions.stringReference
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.feature.wallet.impl.R
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
private const val VISIBILITY_THRESHOLD = 0.5f
private val BRAND_ICON_SLOT_SIZE = 44.dp
/**
* Wallet screen collapsing top bar
@ -53,76 +53,54 @@ internal fun WalletTopBar(
behavior: TangemCollapsingAppBarBehavior,
) {
val hapticManager = LocalHapticManager.current
Surface(
color = Color.Unspecified,
contentColor = Color.Unspecified,
modifier = Modifier.hazeEffectTangemTopBar(behavior),
) {
val isWrappedBalanceShown by remember {
derivedStateOf { behavior.state.collapsedFraction > VISIBILITY_THRESHOLD }
}
val isWrappedBalanceShown by remember {
derivedStateOf { behavior.state.collapsedFraction > VISIBILITY_THRESHOLD }
}
val wrappedBalance = remember(walletBalance, isWrappedBalanceShown, isBalanceHidden) {
walletBalance?.orMaskWithStars(isBalanceHidden).takeIf { isWrappedBalanceShown }
}
val wrappedBalance = remember(walletBalance, isWrappedBalanceShown, isBalanceHidden) {
walletBalance?.orMaskWithStars(isBalanceHidden).takeIf { isWrappedBalanceShown }
}
TangemTopBar(
title = wrappedBalance,
startContent = {
TangemTopBarActionContent(
TangemTopBarActionUM(
iconRes = R.drawable.ic_tangem_24,
isActionable = false,
),
TangemTopNavigation(
modifier = Modifier.testTag(MainScreenTestTags.TOP_BAR),
contentAlign = TangemTopNavigation.ContentAlign.Center,
startButton = {
// Non-clickable brand mark, sized to align with the trailing action buttons.
Box(modifier = Modifier.size(BRAND_ICON_SLOT_SIZE), contentAlignment = Alignment.Center) {
Icon(
painter = painterResource(id = R.drawable.ic_tangem_24),
contentDescription = null,
tint = TangemTheme.colors3.icon.primary,
)
},
endContent = {
Row(
modifier = Modifier
.clip(CircleShape)
.background(
lerp(
start = Color.Transparent,
stop = TangemTheme.colors2.button.backgroundSecondary,
fraction = behavior.state.collapsedFraction,
),
),
) {
topBarConfig.endActions.forEach { action ->
TangemTopBarActionContent(
action.copy(
onClick = action.onClick?.let { onClick ->
{
hapticManager.perform(TangemHapticEffect.View.ContextClick)
onClick()
}
},
),
modifier = Modifier.testTag(MainScreenTestTags.MORE_BUTTON),
)
}
}
},
endButtonsGroup = if (topBarConfig.endActions.isNotEmpty()) {
{
topBarConfig.endActions.forEach { action ->
TangemButton(
variant = TangemButton.Variant.Ghost,
size = TangemButton.Size.X11,
iconStart = TangemIconUM.Icon(iconRes = action.iconRes),
isEnabled = action.isActionable && action.onClick != null,
onClick = {
hapticManager.perform(TangemHapticEffect.View.ContextClick)
action.onClick?.invoke()
},
modifier = Modifier.testTag(MainScreenTestTags.MORE_BUTTON),
)
}
},
modifier = Modifier
.statusBarsPadding()
.testTag(MainScreenTestTags.TOP_BAR),
)
}
}
@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,
)
}
}
} else {
null
},
contentColumn = {
AnimatedVisibility(visible = wrappedBalance != null) {
wrappedBalance?.let { balance ->
TangemNavigationText(text = balance, role = TangemNavigationText.Role.Title)
}
}
},
)
}
// 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.state.TokensListItemUM
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.TangemIconUM
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.TangemTokenRowUM
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.res.TangemTheme
import com.tangem.core.ui.test.MainScreenTestTags
@ -159,13 +157,13 @@ private fun LazyListScope.tokenItem(
val itemModifier = modifier
.testTag(MainScreenTestTags.TOKEN_LIST_ITEM)
.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(
radius = 18.dp,
currentIndex = index,
addDefaultPadding = false,
lastIndex = lastIndex,
backgroundColor = TangemTheme.colors2.surface.level3,
backgroundColor = TangemTheme.colors3.bg.secondary,
)
var position by remember { mutableStateOf(Offset.Zero) }
@ -261,11 +259,11 @@ private fun LazyListScope.portfolioItem(
modifier = modifier
.animateItem(fadeInSpec = null, placementSpec = null, fadeOutSpec = null)
.roundedShapeItemDecoration(
radius = if (listItem.isExpanded) TangemTheme.dimens2.x6 else TangemTheme.dimens2.x5,
radius = if (listItem.isExpanded) 24.dp else 20.dp,
currentIndex = tokenIndex + 1,
addDefaultPadding = false,
lastIndex = lastIndex,
backgroundColor = TangemTheme.colors2.surface.level3,
backgroundColor = TangemTheme.colors3.bg.secondary,
),
visible = listItem.isExpanded,
) {
@ -327,15 +325,15 @@ private fun LazyListScope.accountItem(
)
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)
.semantics { lazyListItemPosition = index }
.roundedShapeItemDecoration(
currentIndex = 0,
radius = if (listItem.isExpanded) TangemTheme.dimens2.x6 else TangemTheme.dimens2.x5,
radius = if (listItem.isExpanded) 24.dp else 20.dp,
addDefaultPadding = false,
lastIndex = effectiveLastIndex,
backgroundColor = TangemTheme.colors2.surface.level3,
backgroundColor = TangemTheme.colors3.bg.secondary,
)
if (listItem.isCollapsable) {
PortfolioRowItem(
@ -422,7 +420,7 @@ internal fun PortfolioRowItem(
}
val iconBoxSize = when (headIcon) {
is TangemIconUM.Empty -> TangemTheme.dimens2.x9
is TangemIconUM.Empty -> 36.dp
else -> size.toBoxSize()
}
TangemIcon(
@ -444,8 +442,8 @@ internal fun PortfolioRowItem(
animationSpec = tween(durationMillis = 350),
)
val startStyle = TangemTheme.typography2.captionSemibold12
val stopStyle = TangemTheme.typography2.bodySemibold16
val startStyle = TangemTheme.typography3.caption.medium
val stopStyle = TangemTheme.typography3.body.medium
val textStyle by remember(animationFraction.value) {
derivedStateOf { lerp(startStyle, stopStyle, animationFraction.value) }
@ -509,7 +507,7 @@ private fun LazyListScope.nonContentItem(modifier: Modifier = Modifier) {
NonContentItemContent(
modifier = modifier
.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(
onClick = onEmptyClick,
textColor = TangemTheme.colors2.text.neutral.tertiary,
textColor = TangemTheme.colors3.text.secondary,
modifier = modifier
.animateItem(fadeInSpec = null, fadeOutSpec = null)
.padding(top = 46.dp),
@ -540,17 +538,17 @@ private fun LazyListScope.nonContentAccountItem(listItem: TokensListItemUM2.Port
modifier = modifier
.animateItem(fadeInSpec = null, placementSpec = null, fadeOutSpec = null)
.roundedShapeItemDecoration(
radius = if (listItem.isExpanded) TangemTheme.dimens2.x6 else TangemTheme.dimens2.x5,
radius = if (listItem.isExpanded) 24.dp else 20.dp,
addDefaultPadding = false,
currentIndex = 1,
lastIndex = 1,
backgroundColor = TangemTheme.colors2.surface.level3,
backgroundColor = TangemTheme.colors3.bg.secondary,
),
visible = listItem.isExpanded,
) {
NonContentItemContentV2(
modifier = Modifier.padding(vertical = 36.dp),
textColor = TangemTheme.colors2.text.neutral.secondary,
textColor = TangemTheme.colors3.text.secondary,
onClick = listItem.onEmptyClick,
)
}
@ -566,23 +564,23 @@ internal fun NonContentItemContentV2(textColor: Color, modifier: Modifier = Modi
Icon(
painter = painterResource(id = R.drawable.ic_empty_64),
contentDescription = null,
modifier = Modifier.size(size = TangemTheme.dimens2.x16),
tint = TangemTheme.colors2.graphic.neutral.quaternary,
modifier = Modifier.size(64.dp),
tint = TangemTheme.colors3.icon.tertiary,
)
SpacerH(TangemTheme.dimens2.x5)
SpacerH(20.dp)
Text(
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,
textAlign = TextAlign.Center,
style = TangemTheme.typography2.bodyRegular14,
style = TangemTheme.typography3.subheading.medium,
)
SpacerH(TangemTheme.dimens2.x4)
SecondaryTangemButton(
SpacerH(16.dp)
TangemButton(
text = resourceReference(id = R.string.common_add_tokens),
variant = TangemButton.Variant.Secondary,
size = TangemButton.Size.X9,
onClick = onClick,
size = TangemButtonSize.X8,
shape = TangemButtonShape.Rounded,
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),
style = EarnBlockUM.TitleUM.Style.Large,
tone = EarnBlockUM.TitleUM.Tone.Primary,
iconUM = buildTitleIcon(value),
),
subtitleUM = EarnBlockUM.SubtitleUM.Text(
text = combinedReference(
@ -89,18 +88,19 @@ internal class YieldSupplyToEarnBlockConverter : Converter<YieldSupplyUM, EarnBl
style = EarnBlockUM.SubtitleUM.Style.Small,
tone = EarnBlockUM.SubtitleUM.Tone.Accent,
),
trailingUM = EarnBlockUM.TrailingUM.Button(
text = resourceReference(CoreResR.string.details_title),
style = EarnBlockUM.TrailingUM.Button.Style.Secondary,
),
trailingUM = buildTrailing(value),
onClick = value.onClick,
)
}
private fun buildTitleIcon(value: YieldSupplyUM.Content): EarnBlockUM.TitleUM.IconUM? = when {
value.shouldShowWarningIcon -> EarnBlockUM.TitleUM.IconUM(tone = EarnBlockUM.TitleUM.IconTone.Warning)
value.shouldShowInfoIcon -> EarnBlockUM.TitleUM.IconUM(tone = EarnBlockUM.TitleUM.IconTone.Info)
else -> null
private fun buildTrailing(value: YieldSupplyUM.Content): EarnBlockUM.TrailingUM = when {
value.shouldShowWarningIcon -> EarnBlockUM.TrailingUM.StatusIcon(
tone = EarnBlockUM.TrailingUM.StatusIcon.Tone.Warning,
)
value.shouldShowInfoIcon -> EarnBlockUM.TrailingUM.StatusIcon(
tone = EarnBlockUM.TrailingUM.StatusIcon.Tone.Info,
)
else -> EarnBlockUM.TrailingUM.Chevron
}
private fun buildProcessingEnter(): EarnBlockUM.Content {
@ -109,17 +109,18 @@ internal class YieldSupplyToEarnBlockConverter : Converter<YieldSupplyUM, EarnBl
backgroundUM = EarnBlockUM.BackgroundUM.Surface,
iconUM = EarnBlockUM.IconUM.Glowing(iconRes = CoreUiR.drawable.ic_yield_40),
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,
tone = EarnBlockUM.TitleUM.Tone.Primary,
),
subtitleUM = EarnBlockUM.SubtitleUM.Text(
text = resourceReference(CoreResR.string.common_enabling),
style = EarnBlockUM.SubtitleUM.Style.Small,
tone = EarnBlockUM.SubtitleUM.Tone.Accent,
loader = EarnBlockUM.SubtitleUM.Loader(tone = EarnBlockUM.SubtitleUM.LoaderTone.Positive),
subtitleUM = null,
trailingUM = EarnBlockUM.TrailingUM.Loader(
tone = EarnBlockUM.TrailingUM.Loader.LoaderTone.Positive,
),
trailingUM = null,
)
}
@ -127,19 +128,23 @@ internal class YieldSupplyToEarnBlockConverter : Converter<YieldSupplyUM, EarnBl
return EarnBlockUM.Content(
type = EarnBlockUM.Type.YieldSupply,
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(
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,
tone = EarnBlockUM.TitleUM.Tone.Primary,
),
subtitleUM = EarnBlockUM.SubtitleUM.Text(
text = resourceReference(CoreResR.string.common_disabling),
style = EarnBlockUM.SubtitleUM.Style.Small,
tone = EarnBlockUM.SubtitleUM.Tone.Disabled,
loader = EarnBlockUM.SubtitleUM.Loader(tone = EarnBlockUM.SubtitleUM.LoaderTone.Muted),
subtitleUM = null,
trailingUM = EarnBlockUM.TrailingUM.Loader(
tone = EarnBlockUM.TrailingUM.Loader.LoaderTone.Muted,
),
trailingUM = null,
)
}
}

View file

@ -1,10 +1,6 @@
package com.tangem.features.yield.supply.impl.main.model
import arrow.core.Option
import arrow.core.left
import arrow.core.none
import arrow.core.right
import arrow.core.some
import arrow.core.*
import com.google.common.truth.Truth.assertThat
import com.tangem.common.routing.AppRoute
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.promo.usecase.GetBoostedApyUseCase
import com.tangem.domain.yield.supply.promo.usecase.IsYieldBoostPromoEnabledForTokenUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplyActivateUseCase
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.domain.yield.supply.usecase.*
import com.tangem.features.yield.supply.api.YieldSupplyComponent
import com.tangem.features.yield.supply.api.analytics.YieldSupplyAnalytics
import com.tangem.features.yield.supply.impl.YieldBoostStoryPreloader
import com.tangem.features.yield.supply.impl.main.entity.YieldSupplyUM
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.coEvery
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 io.mockk.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
@ -258,7 +240,9 @@ internal class YieldSupplyModelTest {
assertThat(approveEvent.blockchain).isEqualTo("Ethereum")
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
@ -290,7 +274,9 @@ internal class YieldSupplyModelTest {
assertThat(legacy.shouldShowInfoIcon).isTrue()
assertThat(legacy.shouldShowWarningIcon).isFalse()
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

View file

@ -54,9 +54,7 @@ internal class YieldSupplyToEarnBlockConverterTest {
assertThat(earnBlock.titleUM.tone).isEqualTo(EarnBlockUM.TitleUM.Tone.Primary)
assertThat((earnBlock.subtitleUM as EarnBlockUM.SubtitleUM.Text).tone)
.isEqualTo(EarnBlockUM.SubtitleUM.Tone.Accent)
assertThat(earnBlock.trailingUM).isInstanceOf(EarnBlockUM.TrailingUM.Button::class.java)
val button = earnBlock.trailingUM as EarnBlockUM.TrailingUM.Button
assertThat(button.isEnabled).isTrue()
assertThat(earnBlock.trailingUM).isEqualTo(EarnBlockUM.TrailingUM.Chevron)
assertThat(earnBlock.onClick).isNotNull()
earnBlock.onClick?.invoke()
assertThat(clicked).isTrue()
@ -71,7 +69,9 @@ internal class YieldSupplyToEarnBlockConverterTest {
assertThat(earnBlock.type).isEqualTo(EarnBlockUM.Type.YieldSupply)
assertThat(earnBlock.backgroundUM).isEqualTo(EarnBlockUM.BackgroundUM.Surface)
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
@ -82,12 +82,19 @@ internal class YieldSupplyToEarnBlockConverterTest {
val earnBlock = result as EarnBlockUM.Content
assertThat(earnBlock.type).isEqualTo(EarnBlockUM.Type.YieldSupply)
assertThat(earnBlock.backgroundUM).isEqualTo(EarnBlockUM.BackgroundUM.Surface)
assertThat(earnBlock.iconUM).isInstanceOf(EarnBlockUM.IconUM.Plain::class.java)
assertThat(earnBlock.trailingUM).isNull()
assertThat(earnBlock.iconUM).isEqualTo(
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
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(
apy = "5.1",
title = stringReference("Yield Mode"),
@ -102,13 +109,13 @@ internal class YieldSupplyToEarnBlockConverterTest {
assertThat(result).isInstanceOf(EarnBlockUM.Content::class.java)
val earnBlock = result as EarnBlockUM.Content
assertThat(earnBlock.titleUM.iconUM).isNotNull()
assertThat(earnBlock.titleUM.iconUM?.tone).isEqualTo(EarnBlockUM.TitleUM.IconTone.Warning)
assertThat(earnBlock.trailingUM).isInstanceOf(EarnBlockUM.TrailingUM.Button::class.java)
assertThat(earnBlock.trailingUM).isEqualTo(
EarnBlockUM.TrailingUM.StatusIcon(tone = EarnBlockUM.TrailingUM.StatusIcon.Tone.Warning),
)
}
@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(
apy = "5.1",
title = stringReference("Yield Mode"),
@ -123,9 +130,9 @@ internal class YieldSupplyToEarnBlockConverterTest {
assertThat(result).isInstanceOf(EarnBlockUM.Content::class.java)
val earnBlock = result as EarnBlockUM.Content
assertThat(earnBlock.titleUM.iconUM).isNotNull()
assertThat(earnBlock.titleUM.iconUM?.tone).isEqualTo(EarnBlockUM.TitleUM.IconTone.Info)
assertThat(earnBlock.trailingUM).isInstanceOf(EarnBlockUM.TrailingUM.Button::class.java)
assertThat(earnBlock.trailingUM).isEqualTo(
EarnBlockUM.TrailingUM.StatusIcon(tone = EarnBlockUM.TrailingUM.StatusIcon.Tone.Info),
)
}
@Test
@ -143,8 +150,9 @@ internal class YieldSupplyToEarnBlockConverterTest {
val result = converter.convert(content)
val earnBlock = result as EarnBlockUM.Content
assertThat(earnBlock.titleUM.iconUM).isNotNull()
assertThat(earnBlock.titleUM.iconUM?.tone).isEqualTo(EarnBlockUM.TitleUM.IconTone.Warning)
assertThat(earnBlock.trailingUM).isEqualTo(
EarnBlockUM.TrailingUM.StatusIcon(tone = EarnBlockUM.TrailingUM.StatusIcon.Tone.Warning),
)
}
@Test