From e018eee87b33dc63160c482bfeb9346abe4dacdf Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 27 May 2026 12:00:15 +0200 Subject: [PATCH] Updated on 2026-08-14 --- .../com/tangem/common/ui/earn/EarnBlock.kt | 70 +++--- .../tangem/core/ui/components/FadeModifier.kt | 33 +++ .../core/ui/components/account/AccountIcon.kt | 28 ++- .../UpdateStakingNotificationTransformer.kt | 19 +- .../tokendetails/ui/TokenDetailsScreen.kt | 110 ++++------ .../tokendetails/ui/TokenDetailsTopBar.kt | 204 ++++++++++++------ .../ui/components/TokenDetailsBalanceBlock.kt | 21 +- .../ui/components/ZeroBalanceActionsBlock.kt | 8 +- 8 files changed, 289 insertions(+), 204 deletions(-) diff --git a/common/ui/src/main/java/com/tangem/common/ui/earn/EarnBlock.kt b/common/ui/src/main/java/com/tangem/common/ui/earn/EarnBlock.kt index be6c477557..da370bebe6 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/earn/EarnBlock.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/earn/EarnBlock.kt @@ -4,13 +4,7 @@ import android.content.res.Configuration import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Text @@ -36,20 +30,12 @@ import com.tangem.common.ui.earn.EarnBlockUM.Type import com.tangem.core.ui.R import com.tangem.core.ui.components.CircleShimmer import com.tangem.core.ui.components.RectangleShimmer -import com.tangem.core.ui.ds.button.TangemButton -import com.tangem.core.ui.ds.button.TangemButtonShape -import com.tangem.core.ui.ds.button.TangemButtonSize -import com.tangem.core.ui.ds.button.TangemButtonType -import com.tangem.core.ui.ds.button.TangemButtonUM +import com.tangem.core.ui.ds.button.* import com.tangem.core.ui.ds.image.TangemIcon import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.ds.row.TangemRowContainer import com.tangem.core.ui.ds.row.TangemRowLayoutId -import com.tangem.core.ui.extensions.resolveAnnotatedReference -import com.tangem.core.ui.extensions.resolveReference -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.stringReference -import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.extensions.* import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.core.res.R as CoreResR @@ -74,7 +60,7 @@ fun EarnBlock(state: EarnBlockUM, modifier: Modifier = Modifier) { @Composable private fun EarnBlockLoading(modifier: Modifier = Modifier) { - val shape = RoundedCornerShape(TangemTheme.dimens2.x4) + val shape = RoundedCornerShape(TangemTheme.dimens2.x5) TangemRowContainer( modifier = modifier .clip(shape) @@ -91,13 +77,13 @@ private fun EarnBlockLoading(modifier: Modifier = Modifier) { RectangleShimmer( modifier = Modifier .layoutId(TangemRowLayoutId.START_TOP) - .size(width = TangemTheme.dimens2.x16, height = TangemTheme.dimens2.x5), + .size(width = ShimmerSubtitleWidth, height = TangemTheme.dimens2.x4), radius = TangemTheme.dimens2.x2, ) RectangleShimmer( modifier = Modifier .layoutId(TangemRowLayoutId.START_BOTTOM) - .size(width = ShimmerSubtitleWidth, height = TangemTheme.dimens2.x4), + .size(width = TangemTheme.dimens2.x16, height = TangemTheme.dimens2.x5), radius = TangemTheme.dimens2.x2, ) }, @@ -106,7 +92,7 @@ private fun EarnBlockLoading(modifier: Modifier = Modifier) { @Composable private fun EarnBlockContent(state: EarnBlockUM.Content, modifier: Modifier = Modifier) { - val shape = RoundedCornerShape(TangemTheme.dimens2.x4) + val shape = RoundedCornerShape(TangemTheme.dimens2.x5) val clickModifier = state.onClick?.let { Modifier.clickable(onClick = it) } ?: Modifier @@ -114,7 +100,7 @@ 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.x3), + contentPadding = PaddingValues(all = TangemTheme.dimens2.x4), content = { EarnBlockIcon( type = state.type, @@ -361,7 +347,7 @@ private val EarnBlockUM.TitleUM.Style.textStyle: TextStyle @Composable @ReadOnlyComposable get() = when (this) { - EarnBlockUM.TitleUM.Style.Large -> TangemTheme.typography2.bodySemibold16 + EarnBlockUM.TitleUM.Style.Large -> TangemTheme.typography2.bodyMedium16 EarnBlockUM.TitleUM.Style.Small -> TangemTheme.typography2.captionMedium12 } @@ -369,7 +355,7 @@ private val EarnBlockUM.SubtitleUM.Style.textStyle: TextStyle @Composable @ReadOnlyComposable get() = when (this) { - EarnBlockUM.SubtitleUM.Style.Large -> TangemTheme.typography2.bodySemibold16 + EarnBlockUM.SubtitleUM.Style.Large -> TangemTheme.typography2.bodyMedium16 EarnBlockUM.SubtitleUM.Style.Small -> TangemTheme.typography2.captionMedium12 } // endregion @@ -410,12 +396,12 @@ private class EarnBlockStakingPreviewProvider : CollectionPreviewParameterProvid iconUM = EarnBlockUM.IconUM.Plain(iconRes = R.drawable.ic_staking_disable_40), titleUM = EarnBlockUM.TitleUM( text = resourceReference(CoreResR.string.common_stake), - style = EarnBlockUM.TitleUM.Style.Large, + style = EarnBlockUM.TitleUM.Style.Small, tone = EarnBlockUM.TitleUM.Tone.Disabled, ), subtitleUM = EarnBlockUM.SubtitleUM.Text( text = resourceReference(CoreResR.string.staking_notification_network_error_text), - style = EarnBlockUM.SubtitleUM.Style.Small, + style = EarnBlockUM.SubtitleUM.Style.Large, tone = EarnBlockUM.SubtitleUM.Tone.Disabled, ), trailingUM = null, @@ -426,12 +412,12 @@ private class EarnBlockStakingPreviewProvider : CollectionPreviewParameterProvid iconUM = EarnBlockUM.IconUM.Glowing(iconRes = R.drawable.ic_staking_40), titleUM = EarnBlockUM.TitleUM( text = resourceReference(CoreResR.string.common_staking), - style = EarnBlockUM.TitleUM.Style.Large, + style = EarnBlockUM.TitleUM.Style.Small, tone = EarnBlockUM.TitleUM.Tone.Primary, ), subtitleUM = EarnBlockUM.SubtitleUM.Text( text = stringReference("Average APR 5.24%"), - style = EarnBlockUM.SubtitleUM.Style.Small, + style = EarnBlockUM.SubtitleUM.Style.Large, tone = EarnBlockUM.SubtitleUM.Tone.Disabled, ), trailingUM = EarnBlockUM.TrailingUM.Button( @@ -445,12 +431,12 @@ private class EarnBlockStakingPreviewProvider : CollectionPreviewParameterProvid iconUM = EarnBlockUM.IconUM.Glowing(iconRes = R.drawable.ic_staking_40), titleUM = EarnBlockUM.TitleUM( text = resourceReference(CoreResR.string.staking_enabled), - style = EarnBlockUM.TitleUM.Style.Large, + style = EarnBlockUM.TitleUM.Style.Small, tone = EarnBlockUM.TitleUM.Tone.Primary, ), subtitleUM = EarnBlockUM.SubtitleUM.Text( text = stringReference("$ 12.34 rewards"), - style = EarnBlockUM.SubtitleUM.Style.Small, + style = EarnBlockUM.SubtitleUM.Style.Large, tone = EarnBlockUM.SubtitleUM.Tone.Accent, ), trailingUM = EarnBlockUM.TrailingUM.Balance( @@ -475,14 +461,14 @@ private class EarnBlockYieldSupplyPreviewProvider : CollectionPreviewParameterPr id = CoreResR.string.yield_module_token_details_earn_notification_subtitle, formatArgs = wrappedList("5.24"), ), - style = EarnBlockUM.TitleUM.Style.Large, + style = EarnBlockUM.TitleUM.Style.Small, tone = EarnBlockUM.TitleUM.Tone.Primary, ), subtitleUM = EarnBlockUM.SubtitleUM.Text( text = resourceReference( CoreResR.string.yield_module_token_details_earn_notification_description, ), - style = EarnBlockUM.SubtitleUM.Style.Small, + style = EarnBlockUM.SubtitleUM.Style.Large, tone = EarnBlockUM.SubtitleUM.Tone.Accent, ), trailingUM = EarnBlockUM.TrailingUM.Button( @@ -497,7 +483,7 @@ private class EarnBlockYieldSupplyPreviewProvider : CollectionPreviewParameterPr iconUM = EarnBlockUM.IconUM.Glowing(iconRes = R.drawable.ic_yield_40), titleUM = EarnBlockUM.TitleUM( text = resourceReference(CoreResR.string.yield_module_transaction_enter), - style = EarnBlockUM.TitleUM.Style.Large, + style = EarnBlockUM.TitleUM.Style.Small, tone = EarnBlockUM.TitleUM.Tone.Primary, ), subtitleUM = EarnBlockUM.SubtitleUM.Text( @@ -505,7 +491,7 @@ private class EarnBlockYieldSupplyPreviewProvider : CollectionPreviewParameterPr id = CoreResR.string.yield_module_average_apy, formatArgs = wrappedList("5.24"), ), - style = EarnBlockUM.SubtitleUM.Style.Small, + style = EarnBlockUM.SubtitleUM.Style.Large, tone = EarnBlockUM.SubtitleUM.Tone.Accent, ), trailingUM = EarnBlockUM.TrailingUM.Button( @@ -521,7 +507,7 @@ private class EarnBlockYieldSupplyPreviewProvider : CollectionPreviewParameterPr iconUM = EarnBlockUM.IconUM.Glowing(iconRes = R.drawable.ic_yield_40), titleUM = EarnBlockUM.TitleUM( text = resourceReference(CoreResR.string.common_yield_mode), - style = EarnBlockUM.TitleUM.Style.Large, + style = EarnBlockUM.TitleUM.Style.Small, tone = EarnBlockUM.TitleUM.Tone.Primary, iconUM = EarnBlockUM.TitleUM.IconUM(tone = EarnBlockUM.TitleUM.IconTone.Warning), ), @@ -530,7 +516,7 @@ private class EarnBlockYieldSupplyPreviewProvider : CollectionPreviewParameterPr id = CoreResR.string.yield_module_average_apy, formatArgs = wrappedList("5.24"), ), - style = EarnBlockUM.SubtitleUM.Style.Small, + style = EarnBlockUM.SubtitleUM.Style.Large, tone = EarnBlockUM.SubtitleUM.Tone.Accent, ), trailingUM = EarnBlockUM.TrailingUM.Button( @@ -546,7 +532,7 @@ private class EarnBlockYieldSupplyPreviewProvider : CollectionPreviewParameterPr iconUM = EarnBlockUM.IconUM.Glowing(iconRes = R.drawable.ic_yield_40), titleUM = EarnBlockUM.TitleUM( text = resourceReference(CoreResR.string.common_yield_mode), - style = EarnBlockUM.TitleUM.Style.Large, + style = EarnBlockUM.TitleUM.Style.Small, tone = EarnBlockUM.TitleUM.Tone.Primary, iconUM = EarnBlockUM.TitleUM.IconUM(tone = EarnBlockUM.TitleUM.IconTone.Info), ), @@ -555,7 +541,7 @@ private class EarnBlockYieldSupplyPreviewProvider : CollectionPreviewParameterPr id = CoreResR.string.yield_module_average_apy, formatArgs = wrappedList("5.24"), ), - style = EarnBlockUM.SubtitleUM.Style.Small, + style = EarnBlockUM.SubtitleUM.Style.Large, tone = EarnBlockUM.SubtitleUM.Tone.Accent, ), trailingUM = EarnBlockUM.TrailingUM.Button( @@ -571,12 +557,12 @@ private class EarnBlockYieldSupplyPreviewProvider : CollectionPreviewParameterPr iconUM = EarnBlockUM.IconUM.Glowing(iconRes = R.drawable.ic_yield_40), titleUM = EarnBlockUM.TitleUM( text = resourceReference(CoreResR.string.common_yield_mode), - style = EarnBlockUM.TitleUM.Style.Large, + style = EarnBlockUM.TitleUM.Style.Small, tone = EarnBlockUM.TitleUM.Tone.Primary, ), subtitleUM = EarnBlockUM.SubtitleUM.Text( text = resourceReference(CoreResR.string.common_enabling), - style = EarnBlockUM.SubtitleUM.Style.Small, + style = EarnBlockUM.SubtitleUM.Style.Large, tone = EarnBlockUM.SubtitleUM.Tone.Accent, loader = EarnBlockUM.SubtitleUM.Loader(tone = EarnBlockUM.SubtitleUM.LoaderTone.Positive), ), @@ -589,12 +575,12 @@ private class EarnBlockYieldSupplyPreviewProvider : CollectionPreviewParameterPr iconUM = EarnBlockUM.IconUM.Plain(iconRes = R.drawable.ic_yield_disabling_40), titleUM = EarnBlockUM.TitleUM( text = resourceReference(CoreResR.string.common_yield_mode), - style = EarnBlockUM.TitleUM.Style.Large, + style = EarnBlockUM.TitleUM.Style.Small, tone = EarnBlockUM.TitleUM.Tone.Primary, ), subtitleUM = EarnBlockUM.SubtitleUM.Text( text = resourceReference(CoreResR.string.common_disabling), - style = EarnBlockUM.SubtitleUM.Style.Small, + style = EarnBlockUM.SubtitleUM.Style.Large, tone = EarnBlockUM.SubtitleUM.Tone.Disabled, loader = EarnBlockUM.SubtitleUM.Loader(tone = EarnBlockUM.SubtitleUM.LoaderTone.Muted), ), diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/FadeModifier.kt b/core/ui/src/main/java/com/tangem/core/ui/components/FadeModifier.kt index 5b934bcc3c..0a8c22119e 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/FadeModifier.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/FadeModifier.kt @@ -93,6 +93,39 @@ fun Modifier.topFade( solidStop = solidStop, ) +/** + * Draws a vertical gradient fade over the top edge with custom [colorStops]. + * + * Stops are defined relative to [height] (0f = top, 1f = bottom of the fade region). + * + * Note: the gradient is drawn over the entire content area, so the last color stop's color + * extends below the fade region down to the bottom. Pass [Color.Transparent] as the last stop + * (or a color matching the underlying content) to avoid covering the area outside the fade. + */ +@Composable +fun Modifier.topFade(height: Dp, vararg colorStops: Pair): Modifier = composed { + drawWithContent { + drawContent() + + if (this.size.height <= 0f) return@drawWithContent + val fraction = (height.toPx() / this.size.height).coerceIn(0f, 1f) + if (fraction <= 0f) return@drawWithContent + + val (start, end) = this.size.getFadeOffsets(FadePosition.TOP) + + drawRect( + brush = Brush.linearGradient( + colorStops = colorStops + .map { (stop, color) -> stop.coerceIn(0f, 1f) * fraction to color } + .toTypedArray(), + start = start, + end = end, + ), + size = this.size, + ) + } +} + enum class FadePosition { TOP, BOTTOM, LEFT, RIGHT } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/account/AccountIcon.kt b/core/ui/src/main/java/com/tangem/core/ui/components/account/AccountIcon.kt index 0d2a37f3fd..35901d7a10 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/account/AccountIcon.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/account/AccountIcon.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -28,9 +29,10 @@ import androidx.compose.ui.unit.sp import com.tangem.core.ui.R import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.res.TangemThemePreviewRedesign enum class AccountIconSize { - Default, Large, Medium, Small, ExtraSmall, RedesignedDefault + Default, Large, Medium, Small, ExtraSmall, RedesignedDefault, RedesignExtraSmall } /** @@ -129,6 +131,7 @@ fun AccountCharIcon(char: Char, color: Color, size: AccountIconSize, modifier: M AccountIconSize.Small -> TangemTheme.typography.subtitle2 AccountIconSize.ExtraSmall -> TangemTheme.typography.caption1 AccountIconSize.RedesignedDefault -> TangemTheme.typography2.headingSemibold28 + AccountIconSize.RedesignExtraSmall -> TangemTheme.typography2.captionMedium11 } val textSize by animateFloatAsState( @@ -148,6 +151,7 @@ fun AccountCharIcon(char: Char, color: Color, size: AccountIconSize, modifier: M text = char.uppercase(), style = textStyle.copy(fontSize = textSize.sp), color = TangemTheme.colors.text.constantWhite, + textAlign = TextAlign.Center, ) } } @@ -161,6 +165,7 @@ private fun AccountIconSize.iconSizeInDp(): Dp = when (this) { AccountIconSize.Small -> 12.dp AccountIconSize.ExtraSmall -> 8.dp AccountIconSize.RedesignedDefault -> 20.dp + AccountIconSize.RedesignExtraSmall -> 8.dp } fun AccountIconSize.toBoxSize(): Dp = when (this) { @@ -170,6 +175,7 @@ fun AccountIconSize.toBoxSize(): Dp = when (this) { AccountIconSize.Small -> 20.dp AccountIconSize.ExtraSmall -> 14.dp AccountIconSize.RedesignedDefault -> 40.dp + AccountIconSize.RedesignExtraSmall -> 16.dp } private fun AccountIconSize.boxShapeSizeInDp(): Dp = when (this) { @@ -179,6 +185,7 @@ private fun AccountIconSize.boxShapeSizeInDp(): Dp = when (this) { AccountIconSize.Small -> 6.dp AccountIconSize.ExtraSmall -> 4.dp AccountIconSize.RedesignedDefault -> 12.dp + AccountIconSize.RedesignExtraSmall -> 6.dp } @Preview(showBackground = true) @@ -194,6 +201,19 @@ private fun Preview() { } } +@Preview(showBackground = true) +@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun PreviewRedesigned() { + TangemThemePreviewRedesign { + Row( + modifier = Modifier.background(TangemTheme.colors.background.primary), + ) { + Sample() + } + } +} + @Composable private fun Sample() { var sizeState by remember { mutableStateOf(AccountIconSize.ExtraSmall) } @@ -206,8 +226,9 @@ private fun Sample() { AccountIconSize.Large -> AccountIconSize.Medium AccountIconSize.Medium -> AccountIconSize.Small AccountIconSize.Small -> AccountIconSize.ExtraSmall - AccountIconSize.ExtraSmall -> AccountIconSize.Default - AccountIconSize.RedesignedDefault -> AccountIconSize.Large + AccountIconSize.ExtraSmall -> AccountIconSize.RedesignedDefault + AccountIconSize.RedesignedDefault -> AccountIconSize.RedesignExtraSmall + AccountIconSize.RedesignExtraSmall -> AccountIconSize.Default } }) { Text("Change") } @@ -225,5 +246,6 @@ private fun Sample() { AccountCharIcon(char = 'M', color = Color.Magenta, size = AccountIconSize.Medium) AccountCharIcon(char = 'S', color = Color.DarkGray, size = AccountIconSize.Small) AccountCharIcon(char = 'E', color = Color.Green, size = AccountIconSize.ExtraSmall) + AccountCharIcon(char = 'D', color = Color.LightGray, size = AccountIconSize.RedesignExtraSmall) } } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateStakingNotificationTransformer.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateStakingNotificationTransformer.kt index 35258d56cd..a23cd14f49 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateStakingNotificationTransformer.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateStakingNotificationTransformer.kt @@ -8,12 +8,7 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.wrappedList -import com.tangem.core.ui.format.bigdecimal.crypto -import com.tangem.core.ui.format.bigdecimal.defaultAmount -import com.tangem.core.ui.format.bigdecimal.fiat -import com.tangem.core.ui.format.bigdecimal.format -import com.tangem.core.ui.format.bigdecimal.formatStyled -import com.tangem.core.ui.format.bigdecimal.percent +import com.tangem.core.ui.format.bigdecimal.* import com.tangem.core.ui.res.TangemTheme import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus @@ -61,12 +56,12 @@ internal class UpdateStakingNotificationTransformer( iconUM = EarnBlockUM.IconUM.Plain(iconRes = CoreUiR.drawable.ic_staking_disable_40), titleUM = EarnBlockUM.TitleUM( text = resourceReference(CoreResR.string.common_staking), - style = EarnBlockUM.TitleUM.Style.Large, + style = EarnBlockUM.TitleUM.Style.Small, tone = EarnBlockUM.TitleUM.Tone.Disabled, ), subtitleUM = EarnBlockUM.SubtitleUM.Text( text = resourceReference(CoreResR.string.staking_notification_network_error_text), - style = EarnBlockUM.SubtitleUM.Style.Small, + style = EarnBlockUM.SubtitleUM.Style.Large, tone = EarnBlockUM.SubtitleUM.Tone.Disabled, ), trailingUM = null, @@ -122,12 +117,12 @@ internal class UpdateStakingNotificationTransformer( iconUM = EarnBlockUM.IconUM.Glowing(iconRes = CoreUiR.drawable.ic_staking_40), titleUM = EarnBlockUM.TitleUM( text = resourceReference(id = CoreResR.string.common_staking), - style = EarnBlockUM.TitleUM.Style.Large, + style = EarnBlockUM.TitleUM.Style.Small, tone = EarnBlockUM.TitleUM.Tone.Primary, ), subtitleUM = EarnBlockUM.SubtitleUM.Text( text = stakeAvailableSubtitle(availability.option.displayRewardInfo), - style = EarnBlockUM.SubtitleUM.Style.Small, + style = EarnBlockUM.SubtitleUM.Style.Large, tone = EarnBlockUM.SubtitleUM.Tone.Disabled, ), trailingUM = EarnBlockUM.TrailingUM.Button( @@ -170,7 +165,7 @@ internal class UpdateStakingNotificationTransformer( iconUM = EarnBlockUM.IconUM.Glowing(iconRes = CoreUiR.drawable.ic_staking_40), titleUM = EarnBlockUM.TitleUM( text = resourceReference(CoreResR.string.staking_enabled), - style = EarnBlockUM.TitleUM.Style.Large, + style = EarnBlockUM.TitleUM.Style.Small, tone = EarnBlockUM.TitleUM.Tone.Primary, ), subtitleUM = getRewardSubtitle(status, rewardFiatAmount), @@ -257,7 +252,7 @@ internal class UpdateStakingNotificationTransformer( return EarnBlockUM.SubtitleUM.Text( text = text, - style = EarnBlockUM.SubtitleUM.Style.Small, + style = EarnBlockUM.SubtitleUM.Style.Large, tone = if (isAccent) EarnBlockUM.SubtitleUM.Tone.Accent else EarnBlockUM.SubtitleUM.Tone.Disabled, ) } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt index 1f5913e64b..ba581e7537 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt @@ -2,27 +2,12 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.ui import android.content.res.Configuration import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxScope -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.navigationBarsPadding -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.rememberLazyListState -import com.tangem.common.ui.earn.EarnBlock -import com.tangem.common.ui.notifications.notifications -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -31,31 +16,30 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.common.ui.account.AccountIconUM +import com.tangem.common.ui.earn.EarnBlock import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM import com.tangem.common.ui.expressStatus.state.ExpressTransactionsBlockState +import com.tangem.common.ui.notifications.notifications import com.tangem.core.ui.components.BottomFade import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig -import com.tangem.core.ui.ds.button.TangemButtonType -import com.tangem.core.ui.ds.button.TangemButtonUM import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshSlidingContainer import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem import com.tangem.core.ui.components.haze.hazeSourceTangem import com.tangem.core.ui.components.marketprice.MarketPriceBlockState +import com.tangem.core.ui.components.topFade +import com.tangem.core.ui.ds.button.TangemButtonType +import com.tangem.core.ui.ds.button.TangemButtonUM import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.themedColor +import com.tangem.core.ui.res.LocalHazeState import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.domain.models.account.CryptoPortfolioIcon -import com.tangem.feature.tokendetails.presentation.tokendetails.state.AddFundsUM -import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenBalanceTypeUM -import com.tangem.feature.tokendetails.presentation.tokendetails.state.TransferUM -import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockUM -import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM +import com.tangem.feature.tokendetails.presentation.tokendetails.state.* import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM.TitleState -import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsUM -import com.tangem.feature.tokendetails.presentation.tokendetails.state.ZeroBalanceActionsUM import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsBalanceBlock import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.ZeroBalanceActionsBlock import com.tangem.features.markets.token.block.TokenMarketBlockComponent @@ -64,6 +48,7 @@ import com.tangem.features.txhistory.component.TxHistoryComponent import com.tangem.features.txhistory.entity.TxHistoryItemsUM import com.tangem.features.txhistory.entity.TxHistoryUM import com.tangem.features.yield.supply.api.YieldSupplyComponent +import dev.chrisbanes.haze.rememberHazeState import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow @@ -72,6 +57,9 @@ import kotlinx.coroutines.flow.StateFlow private val TopBarHeight: Dp = 64.dp private val MarketBlockHorizontalPadding: Dp = 14.dp +private const val TOP_FADE_MID_STOP = 0.8f +private const val TOP_FADE_MID_ALPHA = 0.8f + @Composable internal fun TokenDetailsScreen( tokenDetailsUM: TokenDetailsUM, @@ -84,20 +72,17 @@ internal fun TokenDetailsScreen( val expressState by expressTransactionsComponent.state.collectAsStateWithLifecycle() val statusBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getTop(this).toDp() } val topBarTotalHeight = TopBarHeight + statusBarHeight + val hazeState = rememberHazeState() val rootBackground = TangemTheme.colors2.surface.level2 var marketBlockHeight by remember { mutableStateOf(0.dp) } val effectiveBottomPadding = marketBlockHeight + TangemTheme.dimens2.x4 - Box( - modifier = modifier - .fillMaxSize() - .background(rootBackground), - ) { + CompositionLocalProvider(LocalHazeState provides hazeState) { Box( - modifier = Modifier + modifier = modifier .fillMaxSize() - .hazeSourceTangem(zIndex = -2f), + .background(rootBackground), ) { TangemPullToRefreshSlidingContainer( config = tokenDetailsUM.pullToRefreshConfig, @@ -115,27 +100,17 @@ internal fun TokenDetailsScreen( modifier = Modifier.fillMaxSize(), ) } + + TokenDetailsTopBar(topAppBarUM = tokenDetailsUM.topAppBarUM) + + if (tokenMarketBlockComponent != null) { + TokenDetailsMarketBlockOverlay( + component = tokenMarketBlockComponent, + onHeightChange = { marketBlockHeight = it }, + ) + } + expressState.bottomSheetSlot?.content(null) } - - TokenDetailsTopBarOverlay(topAppBarUM = tokenDetailsUM.topAppBarUM) - - if (tokenMarketBlockComponent != null) { - TokenDetailsMarketBlockOverlay( - component = tokenMarketBlockComponent, - onHeightChange = { marketBlockHeight = it }, - ) - } - - expressState.bottomSheetSlot?.content(null) - } -} - -@Composable -private fun TokenDetailsTopBarOverlay(topAppBarUM: TokenDetailsTopAppBarUM) { - Box( - modifier = Modifier.background(TangemTheme.colors2.surface.level2), - ) { - TokenDetailsTopBar(topAppBarUM = topAppBarUM) } } @@ -189,7 +164,14 @@ private fun TokenDetailsBody( .padding(start = TangemTheme.dimens2.x4, end = TangemTheme.dimens2.x4, top = TangemTheme.dimens2.x4) LazyColumn( - modifier = modifier, + modifier = modifier + .hazeSourceTangem(state = LocalHazeState.current) + .topFade( + height = topContentPadding, + 0f to rootBackground, + TOP_FADE_MID_STOP to rootBackground.copy(alpha = TOP_FADE_MID_ALPHA), + 1f to Color.Transparent, + ), state = listState, contentPadding = PaddingValues(top = topContentPadding, bottom = bottomContentPadding), ) { @@ -201,14 +183,6 @@ private fun TokenDetailsBody( ) } val balance = tokenDetailsUM.balanceBlockUM - if (balance is TokenDetailsBalanceBlockUM.Content && balance.isBalanceZero) { - item(key = "zero_balance_actions") { - ZeroBalanceActionsBlock( - state = tokenDetailsUM.zeroBalanceActionsUM, - modifier = itemModifier, - ) - } - } notifications( notifications = tokenDetailsUM.notifications, contentColor = rootBackground, @@ -218,12 +192,20 @@ private fun TokenDetailsBody( item(key = "staking_block") { EarnBlock( state = earnBlock, - modifier = itemModifier, + modifier = itemModifier.padding(vertical = TangemTheme.dimens2.x3), ) } } item(key = "yield_supply_block") { - yieldSupplyComponent.Content(modifier = itemModifier.padding(vertical = TangemTheme.dimens2.x2)) + yieldSupplyComponent.Content(modifier = itemModifier.padding(vertical = TangemTheme.dimens2.x3)) + } + if (balance is TokenDetailsBalanceBlockUM.Content && balance.isBalanceZero) { + item(key = "zero_balance_actions") { + ZeroBalanceActionsBlock( + state = tokenDetailsUM.zeroBalanceActionsUM, + modifier = itemModifier.padding(vertical = TangemTheme.dimens2.x2), + ) + } } with(expressTransactionsComponent) { expressTransactionsContent( diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsTopBar.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsTopBar.kt index d49d928013..a25b282c16 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsTopBar.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsTopBar.kt @@ -1,8 +1,10 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.ui import android.content.res.Configuration +import androidx.annotation.StringRes import androidx.compose.foundation.background import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.text.InlineTextContent import androidx.compose.foundation.text.TextAutoSize import androidx.compose.foundation.text.appendInlineContent @@ -11,6 +13,7 @@ import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable 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.platform.LocalDensity import androidx.compose.ui.text.* @@ -30,6 +33,8 @@ import com.tangem.core.ui.components.account.AccountIconSize import com.tangem.core.ui.components.dropdownmenu.TangemDropdownItem import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenu import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem +import com.tangem.core.ui.components.haze.ProvideHaze +import com.tangem.core.ui.components.haze.hazeEffectTangem import com.tangem.core.ui.ds.image.DeviceIconUM import com.tangem.core.ui.ds.image.TangemDeviceIcon import com.tangem.core.ui.ds.topbar.TangemTopBar @@ -44,19 +49,22 @@ import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.domain.models.account.CryptoPortfolioIcon import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM.TitleState -import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.* import com.tangem.core.ui.R as CoreUiR @Composable internal fun TokenDetailsTopBar(topAppBarUM: TokenDetailsTopAppBarUM, modifier: Modifier = Modifier) { + val actionModifier = Modifier + .clip(CircleShape) + .hazeEffectTangem { blurRadius = ACTION_BLUR_RADIUS } TangemTopBar( modifier = modifier.statusBarsPadding(), startContent = { TangemTopBarActionContent( + modifier = actionModifier, actionUM = TangemTopBarActionUM( iconRes = R.drawable.ic_arrow_back_28, onClick = topAppBarUM.onBackClick, - ghostModeProgress = 1f, ), ) }, @@ -65,10 +73,10 @@ internal fun TokenDetailsTopBar(topAppBarUM: TokenDetailsTopAppBarUM, modifier: var isDropdownMenuShown by rememberSaveable { mutableStateOf(false) } Box { TangemTopBarActionContent( + modifier = actionModifier, actionUM = TangemTopBarActionUM( iconRes = CoreUiR.drawable.ic_more_default_24, onClick = { isDropdownMenuShown = true }, - ghostModeProgress = 1f, ), ) TangemDropdownMenu( @@ -100,8 +108,8 @@ internal fun TokenDetailsTopBar(topAppBarUM: TokenDetailsTopAppBarUM, modifier: TokenDetailsTitle(titleState = topAppBarUM.titleState) Text( text = topAppBarUM.subtitle.resolveAnnotatedReference(), - color = TangemTheme.colors2.text.neutral.secondary, - style = TangemTheme.typography2.captionMedium12, + color = TangemTheme.colors2.text.neutral.tertiary, + style = TangemTheme.typography2.captionSemibold12, textAlign = TextAlign.Center, maxLines = 1, overflow = TextOverflow.Ellipsis, @@ -138,11 +146,7 @@ private fun TokenDetailsTitle(titleState: TitleState) { AdaptiveTokenWithSecondaryRow( tokenName = titleState.tokenName, secondaryName = titleState.walletName, - template = stringResourceSafe( - id = CoreUiR.string.token_details_toolbar_title_token_in_wallet, - titleState.tokenName, - titleState.walletName, - ), + template = formattedTitleTemplate(CoreUiR.string.token_details_toolbar_title_token_in_wallet), appearance = appearance, icon = { TangemDeviceIcon( @@ -153,21 +157,16 @@ private fun TokenDetailsTitle(titleState: TitleState) { ) } is TitleState.WithAccount -> { - val accountNameStr = titleState.accountName.resolveAnnotatedReference().toString() AdaptiveTokenWithSecondaryRow( tokenName = titleState.tokenName, - secondaryName = accountNameStr, - template = stringResourceSafe( - id = CoreUiR.string.token_details_toolbar_title_token_in_account, - titleState.tokenName, - accountNameStr, - ), + secondaryName = titleState.accountName.resolveAnnotatedReference().toString(), + template = formattedTitleTemplate(CoreUiR.string.token_details_toolbar_title_token_in_account), appearance = appearance, icon = { AccountIcon( name = titleState.accountName, icon = titleState.accountIconUM, - size = AccountIconSize.ExtraSmall, + size = AccountIconSize.RedesignExtraSmall, ) }, ) @@ -178,9 +177,12 @@ private fun TokenDetailsTitle(titleState: TitleState) { /** * Adaptive title for [TitleState.WithAccount] / [TitleState.WithWallet]. * - * Phrase template carries the [IMAGE_PLACEHOLDER] marker — translator decides where - * the icon sits (e.g. "Tether in [⭐] Portfolio" or "Tether in My Wallet [⭐]"). - * RTL is handled by BiDi inside the single [Text]. + * Raw template carries [TOKEN_MARKER] / [SECONDARY_MARKER] for the names and + * [IMAGE_PLACEHOLDER] for the icon — translator decides where each sits + * (e.g. "Tether in [⭐] Portfolio" or "Tether in My Wallet [⭐]"). Anything outside + * the markers (connecting words, punctuation) is painted as tertiary text — no + * locale-specific substring matching required. RTL is handled by BiDi inside the + * single [Text]. * * Width-driven cascade: * 1–2. Full phrase as single [Text] with inline icon; [TextOverflow.Ellipsis] @@ -201,7 +203,7 @@ private fun AdaptiveTokenWithSecondaryRow( appearance: TitleAppearance, icon: @Composable () -> Unit, ) { - val (beforeIcon, afterIcon) = remember(template) { splitTemplate(template) } + val segments = remember(template) { parseTemplate(template) } val inlineContent = rememberIconInlineContent(appearance.iconSize, icon) BoxWithConstraints( @@ -209,16 +211,16 @@ private fun AdaptiveTokenWithSecondaryRow( contentAlignment = Alignment.Center, ) { val isFullTextShown = rememberShouldShowFullText( - beforeIcon = beforeIcon, - afterIcon = afterIcon, - secondaryName = secondaryName, + segments = segments, + tokenName = tokenName, appearance = appearance, maxWidthPx = constraints.maxWidth, ) if (isFullTextShown) { FullPhraseTitle( - beforeIcon = beforeIcon, - afterIcon = afterIcon, + segments = segments, + tokenName = tokenName, + secondaryName = secondaryName, style = appearance.style, inlineContent = inlineContent, ) @@ -232,17 +234,25 @@ private fun AdaptiveTokenWithSecondaryRow( } } -private fun splitTemplate(template: String): Pair { - val parts = template.split(IMAGE_PLACEHOLDER, limit = 2) - return if (parts.size == 2) parts[0] to parts[1] else template to "" -} +/** + * Returns the title template with its `%1$s` / `%2$s` placeholders replaced by [TOKEN_MARKER] / + * [SECONDARY_MARKER] sentinels (and `%%image%%` collapsed to [IMAGE_PLACEHOLDER]). + * + * Feeding the markers in as format args lets [String.format] resolve all escaping for us, so + * [parseTemplate] sees a clean string and the real names stay un-substituted until render time. + */ +@Composable +private fun formattedTitleTemplate(@StringRes id: Int): String = stringResourceSafe(id, TOKEN_MARKER, SECONDARY_MARKER) @Composable -private fun rememberIconInlineContent(iconSize: Dp, icon: @Composable () -> Unit): Map { +private fun rememberIconInlineContent( + iconSize: Dp, + icon: @Composable () -> Unit, +): ImmutableMap { val iconSizeSp = with(LocalDensity.current) { iconSize.toSp() } val currentIcon by rememberUpdatedState(icon) return remember(iconSizeSp) { - mapOf( + persistentMapOf( ICON_INLINE_ID to InlineTextContent( placeholder = Placeholder( width = iconSizeSp, @@ -261,23 +271,29 @@ private fun rememberIconInlineContent(iconSize: Dp, icon: @Composable () -> Unit @Composable private fun rememberShouldShowFullText( - beforeIcon: String, - afterIcon: String, - secondaryName: String, + segments: ImmutableList, + tokenName: String, appearance: TitleAppearance, maxWidthPx: Int, ): Boolean { val measurer = rememberTextMeasurer() val density = LocalDensity.current - return remember(beforeIcon, afterIcon, secondaryName, maxWidthPx, appearance, density) { + return remember(segments, tokenName, maxWidthPx, appearance, density) { if (maxWidthPx <= 0) return@remember true - val fullTextWidthPx = measurer - .measure(text = beforeIcon + afterIcon, style = appearance.style, softWrap = false) - .size.width - val secondaryWidthPx = measurer - .measure(text = secondaryName, style = appearance.style, softWrap = false) - .size.width - val staticWidthPx = (fullTextWidthPx - secondaryWidthPx).coerceAtLeast(0) + val staticText = buildString { + for (segment in segments) { + when (segment) { + TitleSegment.Token -> append(tokenName) + is TitleSegment.Plain -> append(segment.text) + TitleSegment.Secondary, TitleSegment.Image -> Unit + } + } + } + val staticWidthPx = if (staticText.isEmpty()) { + 0 + } else { + measurer.measure(text = staticText, style = appearance.style, softWrap = false).size.width + } val iconReservePx = with(density) { (appearance.iconSize + appearance.spacing * 2).toPx() }.toInt() @@ -288,16 +304,25 @@ private fun rememberShouldShowFullText( @Composable private fun FullPhraseTitle( - beforeIcon: String, - afterIcon: String, + segments: ImmutableList, + tokenName: String, + secondaryName: String, style: TextStyle, - inlineContent: Map, + inlineContent: ImmutableMap, ) { - val fullText = remember(beforeIcon, afterIcon) { + val tertiaryColor = TangemTheme.colors2.text.neutral.tertiary + val fullText = remember(segments, tokenName, secondaryName, tertiaryColor) { buildAnnotatedString { - append(beforeIcon) - appendInlineContent(ICON_INLINE_ID, IMAGE_PLACEHOLDER) - append(afterIcon) + for (segment in segments) { + when (segment) { + TitleSegment.Token -> append(tokenName) + TitleSegment.Secondary -> append(secondaryName) + TitleSegment.Image -> appendInlineContent(ICON_INLINE_ID, IMAGE_PLACEHOLDER) + is TitleSegment.Plain -> withStyle(SpanStyle(color = tertiaryColor)) { + append(segment.text) + } + } + } } } Text( @@ -311,6 +336,47 @@ private fun FullPhraseTitle( ) } +private sealed interface TitleSegment { + data object Token : TitleSegment + data object Secondary : TitleSegment + data object Image : TitleSegment + data class Plain(val text: String) : TitleSegment +} + +/** + * Splits the [String.format]ed template (see [formattedTitleTemplate]) into ordered segments. + * + * Markers appear in their resolved form: [TOKEN_MARKER] / [SECONDARY_MARKER] for the names and + * [IMAGE_PLACEHOLDER] for the icon (escaping was already collapsed by [String.format]). Anything + * else is plain connecting text painted as tertiary. + */ +private fun parseTemplate(template: String): ImmutableList { + val segments = mutableListOf() + val plain = StringBuilder() + + fun flushPlain() { + if (plain.isNotEmpty()) { + segments += TitleSegment.Plain(plain.toString()) + plain.clear() + } + } + + var i = 0 + while (i < template.length) { + val marker = MARKERS.firstOrNull { (text, _) -> template.startsWith(text, i) } + if (marker != null) { + flushPlain() + segments += marker.second + i += marker.first.length + } else { + plain.append(template[i]) + i++ + } + } + flushPlain() + return segments.toImmutableList() +} + @Composable private fun FallbackTokenWithIconTitle(tokenName: String, appearance: TitleAppearance, icon: @Composable () -> Unit) { Row( @@ -342,7 +408,17 @@ private data class TitleAppearance( private const val ICON_INLINE_ID = "account_icon" private const val IMAGE_PLACEHOLDER = "%image%" +private const val TOKEN_MARKER = "%1\$s" +private const val SECONDARY_MARKER = "%2\$s" +// Order matters: longer/overlapping markers must be tried first by [parseTemplate]. +private val MARKERS: List> = listOf( + IMAGE_PLACEHOLDER to TitleSegment.Image, + TOKEN_MARKER to TitleSegment.Token, + SECONDARY_MARKER to TitleSegment.Secondary, +) + +private val ACTION_BLUR_RADIUS = 8.dp private val MIN_TITLE_FONT_SIZE = 12.sp private val MAX_TITLE_FONT_SIZE = 16.sp private val MIN_SECONDARY_NAME_WIDTH = 48.dp @@ -355,20 +431,22 @@ private fun TokenDetailsTopBar_Preview( @PreviewParameter(TokenDetailsTopBarPreviewProvider::class) titleState: TitleState, ) { TangemThemePreviewRedesign { - TokenDetailsTopBar( - topAppBarUM = TokenDetailsTopAppBarUM( - titleState = titleState, - subtitle = stringReference("ERC-20 in Ethereum network"), - onBackClick = {}, - menuItems = persistentListOf( - TangemDropdownMenuItem( - title = stringReference("Hide Token"), - textColor = themedColor { TangemTheme.colors.text.warning }, - onClick = {}, + ProvideHaze { + TokenDetailsTopBar( + topAppBarUM = TokenDetailsTopAppBarUM( + titleState = titleState, + subtitle = stringReference("ERC-20 in Ethereum network"), + onBackClick = {}, + menuItems = persistentListOf( + TangemDropdownMenuItem( + title = stringReference("Hide Token"), + textColor = themedColor { TangemTheme.colors.text.warning }, + onClick = {}, + ), ), ), - ), - ) + ) + } } } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt index 12fb6fcc26..ad37bfa321 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt @@ -4,16 +4,10 @@ import android.content.res.Configuration import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.* import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -102,8 +96,7 @@ private fun ContentBody(state: TokenDetailsBalanceBlockUM.Content, isBalanceHidd targetState = state.tokenBalanceTypeUM.type, label = "Token balance type", ) { currentType -> - val tokenBalanceTypeUM = state.tokenBalanceTypeUM - when (tokenBalanceTypeUM) { + when (val tokenBalanceTypeUM = state.tokenBalanceTypeUM) { is TokenBalanceTypeUM.Multiple -> Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1), @@ -111,20 +104,20 @@ private fun ContentBody(state: TokenDetailsBalanceBlockUM.Content, isBalanceHidd ) { Text( text = currentType.text.resolveReference(), - style = TangemTheme.typography2.calloutSemibold15, - color = TangemTheme.colors2.text.neutral.secondary, + style = TangemTheme.typography2.subheadlineMedium14, + color = TangemTheme.colors2.text.neutral.primary, ) Icon( imageVector = ImageVector.vectorResource(R.drawable.ic_sort_24), contentDescription = null, tint = TangemTheme.colors2.graphic.neutral.secondary, - modifier = Modifier.size(TangemTheme.dimens2.x4), + modifier = Modifier.size(TangemTheme.dimens2.x5), ) } TokenBalanceTypeUM.Single -> Text( text = currentType.text.resolveReference(), - style = TangemTheme.typography2.calloutSemibold15, - color = TangemTheme.colors2.text.neutral.secondary, + style = TangemTheme.typography2.subheadlineMedium14, + color = TangemTheme.colors2.text.neutral.primary, ) } } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/ZeroBalanceActionsBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/ZeroBalanceActionsBlock.kt index 58173be9e0..624dc99bab 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/ZeroBalanceActionsBlock.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/ZeroBalanceActionsBlock.kt @@ -1,11 +1,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components import androidx.annotation.DrawableRes -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.* import androidx.compose.material3.CircularProgressIndicator import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -26,7 +22,7 @@ internal fun ZeroBalanceActionsBlock(state: ZeroBalanceActionsUM, modifier: Modi Column( modifier = modifier .fillMaxWidth() - .padding(bottom = TangemTheme.dimens2.x10), + .padding(bottom = TangemTheme.dimens2.x6), verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x2), ) { ActionRow(