diff --git a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/CryptoCurrencyData.kt b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/CryptoCurrencyData.kt index 34ecb8e2f3..d4133cc3ef 100644 --- a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/CryptoCurrencyData.kt +++ b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/CryptoCurrencyData.kt @@ -1,5 +1,6 @@ package com.tangem.common.ui.markets.action +import com.tangem.domain.models.account.AccountStatus import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.tokens.model.TokenActionsState @@ -8,4 +9,6 @@ data class CryptoCurrencyData( val userWallet: UserWallet, val status: CryptoCurrencyStatus, val actions: List, + val isAccountMode: Boolean, + val account: AccountStatus.CryptoPortfolio, ) \ No newline at end of file diff --git a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActionUM.kt b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActionUM.kt index c2565c295b..88b65e767a 100644 --- a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActionUM.kt +++ b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActionUM.kt @@ -9,43 +9,103 @@ import com.tangem.core.ui.extensions.wrappedList @Immutable sealed class QuickActionUM( - val title: TextReference, - val description: TextReference, - @DrawableRes val icon: Int, - val isLongClickAvailable: Boolean = false, + open val title: TextReference, + open val description: TextReference, + @param:DrawableRes open val icon: Int, + open val isLongClickAvailable: Boolean = false, ) { - data object Buy : QuickActionUM( - title = resourceReference(R.string.common_buy), - description = resourceReference(R.string.buy_token_description), - icon = R.drawable.ic_plus_24, - ) - data class Exchange( - val shouldShowBadge: Boolean, + sealed class V1( + override val title: TextReference, + override val description: TextReference, + @param:DrawableRes override val icon: Int, + override val isLongClickAvailable: Boolean = false, ) : QuickActionUM( - title = resourceReference(R.string.common_exchange), - description = resourceReference(R.string.exсhange_token_description), - icon = R.drawable.ic_exchange_vertical_24, - ) + title = title, + description = description, + icon = icon, + isLongClickAvailable = isLongClickAvailable, + ) { + data object Buy : V1( + title = resourceReference(R.string.common_buy), + description = resourceReference(R.string.buy_token_description), + icon = R.drawable.ic_plus_24, + ) - data object Receive : QuickActionUM( - title = resourceReference(R.string.common_receive), - description = resourceReference(R.string.receive_token_description), - icon = R.drawable.ic_arrow_down_24, - isLongClickAvailable = true, - ) + data class Exchange( + val shouldShowBadge: Boolean, + ) : V1( + title = resourceReference(R.string.common_exchange), + description = resourceReference(R.string.exсhange_token_description), + icon = R.drawable.ic_exchange_vertical_24, + ) - data object Stake : QuickActionUM( - title = resourceReference(R.string.common_stake), - description = resourceReference(R.string.stake_token_description), - icon = R.drawable.ic_staking_24, - ) + data object Receive : V1( + title = resourceReference(R.string.common_receive), + description = resourceReference(R.string.receive_token_description), + icon = R.drawable.ic_arrow_down_24, + isLongClickAvailable = true, + ) - data class YieldMode( - private val apy: String, + data object Stake : V1( + title = resourceReference(R.string.common_stake), + description = resourceReference(R.string.stake_token_description), + icon = R.drawable.ic_staking_24, + ) + + data class YieldMode( + private val apy: String, + ) : V1( + title = resourceReference(R.string.common_yield_mode), + description = resourceReference(R.string.yield_module_main_screen_promo_banner_message, wrappedList(apy)), + icon = R.drawable.ic_analytics_up_mini_24, + ) + } + + sealed class V2( + override val title: TextReference, + override val description: TextReference, + @param:DrawableRes override val icon: Int, + override val isLongClickAvailable: Boolean = false, ) : QuickActionUM( - title = resourceReference(R.string.common_yield_mode), - description = resourceReference(R.string.yield_module_main_screen_promo_banner_message, wrappedList(apy)), - icon = R.drawable.ic_analytics_up_mini_24, - ) + title = title, + description = description, + icon = icon, + isLongClickAvailable = isLongClickAvailable, + ) { + data object Buy : V2( + title = resourceReference(R.string.common_buy), + description = resourceReference(R.string.quick_action_buy_description), + icon = R.drawable.ic_credit_card_20, + ) + + data class Exchange( + val shouldShowBadge: Boolean, + ) : V2( + title = resourceReference(R.string.common_exchange), + description = resourceReference(R.string.quick_action_swap_description), + icon = R.drawable.ic_exchange_mini_24, + ) + + data object Receive : V2( + title = resourceReference(R.string.common_receive), + description = resourceReference(R.string.quick_action_receive_description), + icon = R.drawable.ic_qrcode_new_24, + isLongClickAvailable = true, + ) + + data object Stake : V2( + title = resourceReference(R.string.common_stake), + description = resourceReference(R.string.stake_token_description), + icon = R.drawable.ic_staking_24, + ) + + data class YieldMode( + private val apy: String, + ) : V2( + title = resourceReference(R.string.common_yield_mode), + description = resourceReference(R.string.yield_module_main_screen_promo_banner_message, wrappedList(apy)), + icon = R.drawable.ic_analytics_up_mini_24, + ) + } } \ No newline at end of file diff --git a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActionsConverter.kt b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActionsConverter.kt index f0112cab38..47f53be359 100644 --- a/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActionsConverter.kt +++ b/common/ui-markets/src/main/kotlin/com/tangem/common/ui/markets/action/QuickActionsConverter.kt @@ -2,39 +2,64 @@ package com.tangem.common.ui.markets.action import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.TokenActionsState +import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList object QuickActionsConverter { - fun quickActions(cryptoData: CryptoCurrencyData, tokenActionsHandler: TokenActionsHandler): QuickActions { + fun quickActions( + cryptoData: CryptoCurrencyData, + tokenActionsHandler: TokenActionsHandler, + isRedesignEnabled: Boolean, + ): QuickActions { return QuickActions( - actions = toQuickActions(cryptoData.actions), + actions = toQuickActions(cryptoData.actions, isRedesignEnabled), onQuickActionClick = { quickActionUM -> when (quickActionUM) { - QuickActionUM.Buy -> tokenActionsHandler.handle( + QuickActionUM.V1.Buy -> tokenActionsHandler.handle( action = TokenActionsBSContentUM.Action.Buy, cryptoCurrencyData = cryptoData, ) - is QuickActionUM.Exchange -> tokenActionsHandler.handle( + is QuickActionUM.V1.Exchange -> tokenActionsHandler.handle( action = TokenActionsBSContentUM.Action.Exchange, cryptoCurrencyData = cryptoData, ) - QuickActionUM.Receive -> tokenActionsHandler.handle( + QuickActionUM.V1.Receive -> tokenActionsHandler.handle( action = TokenActionsBSContentUM.Action.Receive, cryptoCurrencyData = cryptoData, ) - QuickActionUM.Stake -> tokenActionsHandler.handle( + QuickActionUM.V1.Stake -> tokenActionsHandler.handle( action = TokenActionsBSContentUM.Action.Stake, cryptoCurrencyData = cryptoData, ) - is QuickActionUM.YieldMode -> tokenActionsHandler.handle( + is QuickActionUM.V1.YieldMode -> tokenActionsHandler.handle( + action = TokenActionsBSContentUM.Action.YieldMode, + cryptoCurrencyData = cryptoData, + ) + QuickActionUM.V2.Buy -> tokenActionsHandler.handle( + action = TokenActionsBSContentUM.Action.Buy, + cryptoCurrencyData = cryptoData, + ) + is QuickActionUM.V2.Exchange -> tokenActionsHandler.handle( + action = TokenActionsBSContentUM.Action.Exchange, + cryptoCurrencyData = cryptoData, + ) + QuickActionUM.V2.Receive -> tokenActionsHandler.handle( + action = TokenActionsBSContentUM.Action.Receive, + cryptoCurrencyData = cryptoData, + ) + QuickActionUM.V2.Stake -> tokenActionsHandler.handle( + action = TokenActionsBSContentUM.Action.Stake, + cryptoCurrencyData = cryptoData, + ) + is QuickActionUM.V2.YieldMode -> tokenActionsHandler.handle( action = TokenActionsBSContentUM.Action.YieldMode, cryptoCurrencyData = cryptoData, ) } }, onQuickActionLongClick = { actionUM -> - if (actionUM == QuickActionUM.Receive) { + if (actionUM == QuickActionUM.V1.Receive || actionUM == QuickActionUM.V2.Receive) { tokenActionsHandler.handle( action = TokenActionsBSContentUM.Action.CopyAddress, cryptoCurrencyData = cryptoData, @@ -44,18 +69,44 @@ object QuickActionsConverter { ) } - fun toQuickActions(actions: List) = buildList { - actions.forEach { action -> - if (action.unavailabilityReason == ScenarioUnavailabilityReason.None) { - when (action) { - is TokenActionsState.ActionState.Buy -> QuickActionUM.Buy - is TokenActionsState.ActionState.Swap -> QuickActionUM.Exchange(action.shouldShowBadge) - is TokenActionsState.ActionState.Receive -> QuickActionUM.Receive - is TokenActionsState.ActionState.Stake -> QuickActionUM.Stake - is TokenActionsState.ActionState.YieldMode -> QuickActionUM.YieldMode(action.apy) - else -> null - }?.let(::add) - } + fun toQuickActions(actions: List, isRedesignEnabled: Boolean) = + if (isRedesignEnabled) { + redesignedQuickActions(actions) + } else { + legacyQuickActions(actions) } - }.toImmutableList() + + private fun redesignedQuickActions(actions: List): ImmutableList { + return buildList { + actions.forEach { action -> + if (action.unavailabilityReason == ScenarioUnavailabilityReason.None) { + when (action) { + is TokenActionsState.ActionState.Buy -> QuickActionUM.V2.Buy + is TokenActionsState.ActionState.Swap -> QuickActionUM.V2.Exchange(action.shouldShowBadge) + is TokenActionsState.ActionState.Receive -> QuickActionUM.V2.Receive + is TokenActionsState.ActionState.Stake -> QuickActionUM.V2.Stake + is TokenActionsState.ActionState.YieldMode -> QuickActionUM.V2.YieldMode(action.apy) + else -> null + }?.let(::add) + } + } + }.toImmutableList() + } + + private fun legacyQuickActions(actions: List): ImmutableList { + return buildList { + actions.forEach { action -> + if (action.unavailabilityReason == ScenarioUnavailabilityReason.None) { + when (action) { + is TokenActionsState.ActionState.Buy -> QuickActionUM.V1.Buy + is TokenActionsState.ActionState.Swap -> QuickActionUM.V1.Exchange(action.shouldShowBadge) + is TokenActionsState.ActionState.Receive -> QuickActionUM.V1.Receive + is TokenActionsState.ActionState.Stake -> QuickActionUM.V1.Stake + is TokenActionsState.ActionState.YieldMode -> QuickActionUM.V1.YieldMode(action.apy) + else -> null + }?.let(::add) + } + } + }.toImmutableList() + } } \ No newline at end of file diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/PortfolioSelectRow.kt b/common/ui/src/main/java/com/tangem/common/ui/account/PortfolioSelectRow.kt index f45c250868..e74695b572 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/account/PortfolioSelectRow.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/account/PortfolioSelectRow.kt @@ -3,16 +3,15 @@ package com.tangem.common.ui.account import android.content.res.Configuration import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.RowScope -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.CompositionLocalProvider import androidx.compose.runtime.Immutable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.layoutId import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -22,12 +21,16 @@ import androidx.compose.ui.unit.dp import com.tangem.common.ui.R import com.tangem.core.ui.components.SpacerW12 import com.tangem.core.ui.components.account.AccountIconSize +import com.tangem.core.ui.ds.row.TangemRowContainer +import com.tangem.core.ui.ds.row.TangemRowLayoutId import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.LocalRedesignEnabled import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.domain.models.account.AccountName @Composable @@ -80,6 +83,66 @@ fun PortfolioSelectRow( } } +@Composable +fun PortfolioSelectRowV2( + state: PortfolioSelectUM, + modifier: Modifier = Modifier, + leftContent: @Composable RowScope.() -> Unit = {}, +) { + val leftText = if (state.isAccountMode) R.string.account_details_title else R.string.wc_common_wallet + TangemRowContainer( + modifier.clickable(enabled = state.isMultiChoice, onClick = state.onClick), + ) { + if (state.icon != null) { + Box( + modifier = Modifier.layoutId(TangemRowLayoutId.HEAD), + contentAlignment = Alignment.Center, + ) { + AccountIcon( + modifier = Modifier.padding(end = TangemTheme.dimens2.x3), + name = state.name, + icon = state.icon, + size = AccountIconSize.RedesignedDefault, + ) + } + } + + Row( + modifier = Modifier.layoutId(TangemRowLayoutId.START_TOP), + verticalAlignment = Alignment.Bottom, + ) { + leftContent() + Text( + modifier = Modifier.weight(1f), + text = stringResourceSafe(leftText), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = TangemTheme.typography2.captionMedium12, + color = TangemTheme.colors2.text.neutral.secondary, + ) + } + + Text( + modifier = Modifier.layoutId(TangemRowLayoutId.START_BOTTOM), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + text = state.name.resolveReference(), + style = TangemTheme.typography2.bodyMedium16, + color = TangemTheme.colors2.text.neutral.primary, + ) + if (state.isMultiChoice) { + Icon( + modifier = Modifier + .layoutId(TangemRowLayoutId.TAIL) + .size(TangemTheme.dimens2.x5), + painter = painterResource(id = R.drawable.ic_select_choice_20), + contentDescription = null, + tint = TangemTheme.colors2.graphic.neutral.secondary, + ) + } + } +} + @Immutable data class PortfolioSelectUM( val icon: AccountIconUM?, @@ -101,6 +164,20 @@ private fun PortfolioSelectRowPreview(@PreviewParameter(PreviewProvider::class) } } +@Preview(widthDp = 360, showBackground = true) +@Preview(widthDp = 360, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun PortfolioSelectRowPreviewV2(@PreviewParameter(PreviewProvider::class) state: PortfolioSelectUM) { + TangemThemePreviewRedesign { + CompositionLocalProvider(LocalRedesignEnabled provides true) { + PortfolioSelectRowV2( + state = state, + modifier = Modifier.background(TangemTheme.colors2.surface.level3), + ) + } + } +} + private class PreviewProvider : PreviewParameterProvider { override val values: Sequence diff --git a/common/ui/src/main/java/com/tangem/common/ui/addtoken/AddTokenContent.kt b/common/ui/src/main/java/com/tangem/common/ui/addtoken/AddTokenContent.kt index 5e0f916630..3793e29d50 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/addtoken/AddTokenContent.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/addtoken/AddTokenContent.kt @@ -154,7 +154,7 @@ private fun Preview(@PreviewParameter(PreviewProvider::class) state: AddTokenUM) } } -private class PreviewProvider : PreviewParameterProvider { +internal class PreviewProvider : PreviewParameterProvider { private val tokenState get() = TokenItemState.Content( id = UUID.randomUUID().toString(), diff --git a/common/ui/src/main/java/com/tangem/common/ui/addtoken/AddTokenContentV2.kt b/common/ui/src/main/java/com/tangem/common/ui/addtoken/AddTokenContentV2.kt new file mode 100644 index 0000000000..701d39d6c8 --- /dev/null +++ b/common/ui/src/main/java/com/tangem/common/ui/addtoken/AddTokenContentV2.kt @@ -0,0 +1,213 @@ +package com.tangem.common.ui.addtoken + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import com.tangem.common.ui.account.PortfolioSelectRowV2 +import com.tangem.core.ui.R +import com.tangem.core.ui.components.RectangleShimmer +import com.tangem.core.ui.components.SpacerH +import com.tangem.core.ui.components.currency.icon.CurrencyIcon +import com.tangem.core.ui.components.currency.icon.CurrencyIconState +import com.tangem.core.ui.components.token.state.TokenItemState +import com.tangem.core.ui.ds.button.PrimaryTangemButton +import com.tangem.core.ui.ds.button.TangemButtonIconPosition +import com.tangem.core.ui.ds.button.TangemButtonShape +import com.tangem.core.ui.ds.button.TangemButtonSize +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.resolveReference +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.LocalRedesignEnabled +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreviewRedesign + +@Composable +fun AddTokenContentV2(state: AddTokenUM, modifier: Modifier = Modifier) { + Column( + modifier = modifier.fillMaxWidth(), + ) { + TokenHeader( + cryptoCurrencyIconState = state.tokenToAdd.iconState, + tokenName = state.tokenToAdd.titleState, + ) + Column { + if (state.portfolio.isMultiChoice) { + PortfolioSelectRowV2( + modifier = Modifier.background( + color = TangemTheme.colors2.surface.level3, + shape = RoundedCornerShape(TangemTheme.dimens2.x5), + ), + state = state.portfolio, + ) + + SpacerH(TangemTheme.dimens2.x2) + } + + NetworkRow(state.network) + } + + SpacerH(TangemTheme.dimens2.x4) + + AddButton( + modifier = Modifier.fillMaxWidth(), + state = state.button, + ) + } +} + +@Composable +private fun TokenHeader( + cryptoCurrencyIconState: CurrencyIconState, + tokenName: TokenItemState.TitleState, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxWidth() + .padding(vertical = TangemTheme.dimens2.x8), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + CurrencyIcon( + state = cryptoCurrencyIconState, + iconSize = 70.dp, + shouldDisplayNetwork = false, + ) + + SpacerH(TangemTheme.dimens2.x4) + + when (tokenName) { + is TokenItemState.TitleState.Content -> { + Text( + text = tokenName.text.resolveReference(), + style = TangemTheme.typography2.headingSemibold28, + color = TangemTheme.colors2.text.neutral.primary, + ) + } + TokenItemState.TitleState.Loading -> { + RectangleShimmer( + modifier = Modifier.size(width = TangemTheme.dimens2.x17, height = TangemTheme.dimens2.x9), + radius = TangemTheme.dimens2.x25, + ) + } + TokenItemState.TitleState.Locked -> { + Box( + modifier = Modifier.background( + color = TangemTheme.colors2.surface.level4, + shape = RoundedCornerShape(TangemTheme.dimens2.x25), + ), + ) + } + } + } +} + +@Composable +private fun NetworkRow(state: AddTokenUM.Network, modifier: Modifier = Modifier) { + TangemRowContainer( + modifier + .clickable(enabled = state.editable, onClick = state.onClick) + .background( + color = TangemTheme.colors2.surface.level3, + shape = RoundedCornerShape(TangemTheme.dimens2.x5), + ), + ) { + Icon( + modifier = Modifier + .layoutId(TangemRowLayoutId.HEAD) + .size(TangemTheme.dimens2.x10) + .padding(end = TangemTheme.dimens2.x3), + tint = Color.Unspecified, + imageVector = ImageVector.vectorResource(id = state.icon), + contentDescription = null, + ) + Text( + modifier = Modifier.layoutId(TangemRowLayoutId.START_TOP), + text = stringResourceSafe(R.string.wc_common_network), + style = TangemTheme.typography2.captionMedium12, + color = TangemTheme.colors2.text.neutral.secondary, + ) + Text( + modifier = Modifier.layoutId(TangemRowLayoutId.START_BOTTOM), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + text = state.name.resolveReference(), + style = TangemTheme.typography2.bodyMedium16, + color = TangemTheme.colors2.text.neutral.primary, + ) + if (state.editable) { + Icon( + modifier = Modifier + .layoutId(TangemRowLayoutId.TAIL) + .size(TangemTheme.dimens2.x5), + painter = painterResource(id = com.tangem.common.ui.R.drawable.ic_select_choice_20), + contentDescription = null, + tint = TangemTheme.colors2.graphic.neutral.secondary, + ) + } + } +} + +@Composable +private fun AddButton(state: AddTokenUM.Button, modifier: Modifier = Modifier) { + val isIconExists = state.isEnabled && state.isTangemIconVisible + PrimaryTangemButton( + modifier = modifier, + text = state.text, + onClick = state.onConfirmClick, + tangemIconUM = if (isIconExists) { + TangemIconUM.Icon( + iconRes = R.drawable.ic_tangem_24, + tintReference = { + if (state.isEnabled) { + TangemTheme.colors2.graphic.neutral.primaryInverted + } else { + TangemTheme.colors2.graphic.neutral.quaternary + } + }, + ) + } else { + null + }, + iconPosition = TangemButtonIconPosition.Start, + isEnabled = state.isEnabled, + size = TangemButtonSize.X12, + shape = TangemButtonShape.Rounded, + ) +} + +@Preview(widthDp = 360, showBackground = true) +@Preview(widthDp = 360, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun Preview(@PreviewParameter(PreviewProvider::class) state: AddTokenUM) { + TangemThemePreviewRedesign { + CompositionLocalProvider(LocalRedesignEnabled provides true) { + Box( + modifier = Modifier + .fillMaxWidth() + .background(TangemTheme.colors2.surface.level2) + .padding(horizontal = 16.dp), + ) { + AddTokenContentV2(state = state) + } + } + } +} \ No newline at end of file diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index b3f833f9fd..7c4a7e1cd7 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -1177,6 +1177,9 @@ No supported tokens found This QR code contains parameters that are not recognized: %s. Some payment details may be lost if you continue. Unknown Parameters + Credit card or bank account + Share your address or QR-code + Between your portfolios No memo required %1$s (%2$s) on %3$s network %1$s on %2$s network 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 939eac7407..38674b5788 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 @@ -30,7 +30,7 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview enum class AccountIconSize { - Default, Large, Medium, Small, ExtraSmall + Default, Large, Medium, Small, ExtraSmall, RedesignedDefault } /** @@ -128,6 +128,7 @@ fun AccountCharIcon(char: Char, color: Color, size: AccountIconSize, modifier: M AccountIconSize.Medium -> TangemTheme.typography.subtitle1 AccountIconSize.Small -> TangemTheme.typography.subtitle2 AccountIconSize.ExtraSmall -> TangemTheme.typography.caption1 + AccountIconSize.RedesignedDefault -> TangemTheme.typography2.headingSemibold28 } val textSize by animateFloatAsState( @@ -159,6 +160,7 @@ private fun AccountIconSize.iconSizeInDp(): Dp = when (this) { AccountIconSize.Medium -> 16.dp AccountIconSize.Small -> 12.dp AccountIconSize.ExtraSmall -> 8.dp + AccountIconSize.RedesignedDefault -> 20.dp } private fun AccountIconSize.boxSizeInDp(): Dp = when (this) { @@ -167,6 +169,7 @@ private fun AccountIconSize.boxSizeInDp(): Dp = when (this) { AccountIconSize.Medium -> 28.dp AccountIconSize.Small -> 20.dp AccountIconSize.ExtraSmall -> 14.dp + AccountIconSize.RedesignedDefault -> 40.dp } private fun AccountIconSize.boxShapeSizeInDp(): Dp = when (this) { @@ -175,6 +178,7 @@ private fun AccountIconSize.boxShapeSizeInDp(): Dp = when (this) { AccountIconSize.Medium -> 8.dp AccountIconSize.Small -> 6.dp AccountIconSize.ExtraSmall -> 4.dp + AccountIconSize.RedesignedDefault -> 12.dp } @Preview(showBackground = true) @@ -203,6 +207,7 @@ private fun Sample() { AccountIconSize.Medium -> AccountIconSize.Small AccountIconSize.Small -> AccountIconSize.ExtraSmall AccountIconSize.ExtraSmall -> AccountIconSize.Default + AccountIconSize.RedesignedDefault -> AccountIconSize.Large } }) { Text("Change") } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/token/internal/TokenFiatAmount.kt b/core/ui/src/main/java/com/tangem/core/ui/components/token/internal/TokenFiatAmount.kt index d821328360..3bc40e3242 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/token/internal/TokenFiatAmount.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/token/internal/TokenFiatAmount.kt @@ -1,11 +1,7 @@ package com.tangem.core.ui.components.token.internal import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -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 @@ -13,11 +9,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.composed import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.semantics.testTag -import com.tangem.core.ui.test.TokenElementsTestTags import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.testTag +import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastForEach @@ -25,7 +21,10 @@ import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.components.icons.IconTint import com.tangem.core.ui.components.text.applyBladeBrush import com.tangem.core.ui.extensions.orMaskWithStars +import com.tangem.core.ui.extensions.resolveAnnotatedReference import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.test.TokenElementsTestTags +import kotlinx.collections.immutable.ImmutableList import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState as TokenFiatAmountState @Composable @@ -60,6 +59,11 @@ internal fun TokenFiatAmount(state: TokenFiatAmountState?, isBalanceHidden: Bool // Empty box for proper measurements Box(modifier) } + is TokenFiatAmountState.AnnotatedContent -> FiatAmountAnnotatedText( + text = state.text.orMaskWithStars(isBalanceHidden).resolveAnnotatedReference(), + modifier = modifier, + isFlickering = state.isFlickering, + ) null -> Unit } } @@ -77,7 +81,7 @@ private fun IconAmount(state: TokenFiatAmountState.Icon, modifier: Modifier = Mo @Composable private fun ContentFiatAmount( text: String, - icons: List, + icons: ImmutableList, isAmountFlickering: Boolean, modifier: Modifier = Modifier, ) { @@ -135,6 +139,25 @@ private fun FiatAmountText( ) } +@Composable +private fun FiatAmountAnnotatedText( + text: AnnotatedString, + modifier: Modifier = Modifier, + isAvailable: Boolean = true, + isFlickering: Boolean = false, +) { + Text( + modifier = modifier, + text = text, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = TangemTheme.typography.body2.applyBladeBrush( + isEnabled = isFlickering, + textColor = if (isAvailable) TangemTheme.colors.text.primary1 else TangemTheme.colors.text.tertiary, + ), + ) +} + private fun Modifier.placeholderSize(): Modifier = composed { return@composed this .padding(vertical = 4.dp) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/token/state/TokenItemState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/token/state/TokenItemState.kt index beec4ce551..bb3d8c4b4f 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/token/state/TokenItemState.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/token/state/TokenItemState.kt @@ -230,6 +230,11 @@ sealed class TokenItemState { val tint: IconTint = IconTint.Inactive, ) : FiatAmountState() + data class AnnotatedContent( + val text: TextReference, + val isFlickering: Boolean = false, + ) : FiatAmountState() + data object Loading : FiatAmountState() data object Locked : FiatAmountState() diff --git a/core/ui/src/main/res/drawable/ic_credit_card_20.xml b/core/ui/src/main/res/drawable/ic_credit_card_20.xml new file mode 100644 index 0000000000..800492e8b1 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_credit_card_20.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_select_choice_20.xml b/core/ui/src/main/res/drawable/ic_select_choice_20.xml new file mode 100644 index 0000000000..514de3e3a4 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_select_choice_20.xml @@ -0,0 +1,9 @@ + + + diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/AddTokenComponent.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/AddTokenComponent.kt index a893a9c1c4..59f8f27bd1 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/AddTokenComponent.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/AddTokenComponent.kt @@ -4,15 +4,17 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.common.ui.addtoken.AddTokenContent +import com.tangem.common.ui.addtoken.AddTokenContentV2 import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.core.ui.res.LocalRedesignEnabled import com.tangem.domain.models.currency.CryptoCurrencyStatus -import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddTokenModel import com.tangem.features.commonfeatures.api.addtoportfolio.SelectedNetwork import com.tangem.features.commonfeatures.api.addtoportfolio.SelectedPortfolio import com.tangem.features.commonfeatures.impl.addtoportfolio.analytics.PortfolioAnalyticsEvent +import com.tangem.features.commonfeatures.impl.addtoportfolio.model.AddTokenModel import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -29,10 +31,17 @@ internal class AddTokenComponent @AssistedInject constructor( override fun Content(modifier: Modifier) { val state = model.uiState.collectAsStateWithLifecycle() val um = state.value ?: return - AddTokenContent( - modifier = modifier, - state = um, - ) + if (LocalRedesignEnabled.current) { + AddTokenContentV2( + modifier = modifier, + state = um, + ) + } else { + AddTokenContent( + modifier = modifier, + state = um, + ) + } } data class Params( diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/TokenActionsComponent.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/TokenActionsComponent.kt index c5e6430356..c4bce69e42 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/TokenActionsComponent.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/TokenActionsComponent.kt @@ -15,10 +15,12 @@ import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.core.ui.res.LocalRedesignEnabled import com.tangem.domain.models.TokenReceiveConfig import com.tangem.features.commonfeatures.impl.addtoportfolio.analytics.PortfolioAnalyticsEvent import com.tangem.features.commonfeatures.impl.addtoportfolio.model.TokenActionsModel import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.TokenActionsContent +import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.TokenActionsContentV2 import com.tangem.features.tokenreceive.TokenReceiveComponent import dagger.assisted.Assisted import dagger.assisted.AssistedFactory @@ -44,10 +46,17 @@ internal class TokenActionsComponent @AssistedInject constructor( val state = model.uiState.collectAsStateWithLifecycle() val bottomSheet by bottomSheetSlot.subscribeAsState() val tokenActionsUM = state.value ?: return - TokenActionsContent( - modifier = modifier, - state = tokenActionsUM, - ) + if (LocalRedesignEnabled.current) { + TokenActionsContentV2( + modifier = modifier, + state = tokenActionsUM, + ) + } else { + TokenActionsContent( + modifier = modifier, + state = tokenActionsUM, + ) + } bottomSheet.child?.instance?.BottomSheet() } diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/AddToPortfolioModel.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/AddToPortfolioModel.kt index 3749d47c9d..7754a0d1ea 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/AddToPortfolioModel.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/AddToPortfolioModel.kt @@ -51,9 +51,9 @@ private const val TOKEN_ACTIONS_DELAY = 500L @Suppress("LongParameterList", "LargeClass") internal class AddToPortfolioModel @Inject constructor( paramsContainer: ParamsContainer, - designFeatureToggles: DesignFeatureToggles, override val dispatchers: CoroutineDispatcherProvider, val portfolioSelectorController: PortfolioSelectorController, + private val designFeatureToggles: DesignFeatureToggles, private val callbackDelegate: AddToPortfolioCallbackDelegate, private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCaseV2, private val getTokenMarketCryptoCurrency: GetTokenMarketCryptoCurrency, @@ -441,7 +441,7 @@ internal class AddToPortfolioModel @Inject constructor( currency = addedToken.currency, accountId = selectedPortfolio.account.account.account.accountId, ).onEach { state -> - val requestedQuickActions = toQuickActions(state.states) + val requestedQuickActions = toQuickActions(state.states, designFeatureToggles.isRedesignEnabled) when { requestedQuickActions.isNotEmpty() -> { timerJob.cancel() @@ -458,6 +458,8 @@ internal class AddToPortfolioModel @Inject constructor( userWallet = selectedPortfolio.userWallet, status = actionsState.cryptoCurrencyStatus, actions = actionsState.states, + isAccountMode = selectedPortfolio.isAccountMode, + account = selectedPortfolio.account.account, ) } } diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/TokenActionsModel.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/TokenActionsModel.kt index 575c386ebd..7d2aaadc8c 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/TokenActionsModel.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/TokenActionsModel.kt @@ -10,17 +10,15 @@ import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.models.TokenReceiveConfig import com.tangem.domain.transaction.usecase.ReceiveAddressesFactory import com.tangem.features.commonfeatures.impl.addtoportfolio.TokenActionsComponent import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.state.TokenActionsUM import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import javax.inject.Inject @@ -29,6 +27,7 @@ import javax.inject.Inject internal class TokenActionsModel @Inject constructor( paramsContainer: ParamsContainer, getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, + getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, tokenActionsIntentsFactory: TokenActionsHandler.Factory, override val dispatchers: CoroutineDispatcherProvider, private val uiBuilder: TokenActionsUiBuilder, @@ -52,13 +51,29 @@ internal class TokenActionsModel @Inject constructor( ) val bottomSheetNavigation: SlotNavigation = SlotNavigation() - val uiState: StateFlow = params.data - .mapLatest { uiBuilder.build(it, tokenActionsHandler, analyticsEventBuilder.first()) } - .stateIn( - scope = modelScope, - started = SharingStarted.Eagerly, - initialValue = null, - ) + + @OptIn(ExperimentalCoroutinesApi::class) + val uiState: StateFlow = + combine( + params.data, + getBalanceHidingSettingsUseCase.isBalanceHidden(), + ) { cryptoCurrencyData, isBalanceHidden -> + cryptoCurrencyData to isBalanceHidden + } + .mapLatest { (cryptoCurrencyData, isBalanceHidden) -> + uiBuilder.build( + cryptoCurrencyData = cryptoCurrencyData, + tokenActionsHandler = tokenActionsHandler, + eventBuilder = analyticsEventBuilder.first(), + appCurrency = currentAppCurrency.value, + isBalanceHidden = isBalanceHidden, + ) + } + .stateIn( + scope = modelScope, + started = SharingStarted.Eagerly, + initialValue = null, + ) private fun handledQuickAction(handledAction: TokenActionsHandler.HandledQuickAction) = modelScope.launch { val event = analyticsEventBuilder.first().getTokenActionClick(actionUM = handledAction.action) diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/TokenActionsUiBuilder.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/TokenActionsUiBuilder.kt index 4b96d1c78a..f461212f82 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/TokenActionsUiBuilder.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/model/TokenActionsUiBuilder.kt @@ -1,32 +1,73 @@ package com.tangem.features.commonfeatures.impl.addtoportfolio.model +import androidx.compose.ui.text.SpanStyle +import com.tangem.common.getTotalCryptoAmount +import com.tangem.common.getTotalFiatAmount +import com.tangem.common.ui.account.* +import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.common.ui.markets.action.CryptoCurrencyData import com.tangem.common.ui.markets.action.QuickActionsConverter.quickActions import com.tangem.common.ui.markets.action.TokenActionsHandler import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.ParamsContainer -import com.tangem.common.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter +import com.tangem.core.ui.DesignFeatureToggles +import com.tangem.core.ui.R import com.tangem.core.ui.components.token.state.TokenItemState +import com.tangem.core.ui.ds.badge.TangemBadgeIconPosition +import com.tangem.core.ui.ds.badge.TangemBadgeShape +import com.tangem.core.ui.ds.badge.TangemBadgeSize +import com.tangem.core.ui.ds.badge.TangemBadgeUM +import com.tangem.core.ui.ds.image.TangemIconUM import com.tangem.core.ui.extensions.stringReference +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 import com.tangem.features.commonfeatures.impl.addtoportfolio.TokenActionsComponent import com.tangem.features.commonfeatures.impl.addtoportfolio.analytics.PortfolioAnalyticsEvent import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.state.TokenActionsUM +import java.math.BigDecimal import javax.inject.Inject @ModelScoped internal class TokenActionsUiBuilder @Inject constructor( paramsContainer: ParamsContainer, private val analyticsEventHandler: AnalyticsEventHandler, + private val designFeatureToggles: DesignFeatureToggles, ) { private val params = paramsContainer.require() fun build( - data: CryptoCurrencyData, + cryptoCurrencyData: CryptoCurrencyData, + tokenActionsHandler: TokenActionsHandler, + eventBuilder: PortfolioAnalyticsEvent.EventBuilder, + appCurrency: AppCurrency, + isBalanceHidden: Boolean, + ): TokenActionsUM { + return if (designFeatureToggles.isRedesignEnabled) { + buildV2( + cryptoCurrencyData = cryptoCurrencyData, + tokenActionsHandler = tokenActionsHandler, + eventBuilder = eventBuilder, + appCurrency = appCurrency, + isBalanceHidden = isBalanceHidden, + ) + } else { + buildV1( + cryptoCurrencyData = cryptoCurrencyData, + tokenActionsHandler = tokenActionsHandler, + eventBuilder = eventBuilder, + ) + } + } + + private fun buildV1( + cryptoCurrencyData: CryptoCurrencyData, tokenActionsHandler: TokenActionsHandler, eventBuilder: PortfolioAnalyticsEvent.EventBuilder, ): TokenActionsUM { - val status = data.status + val status = cryptoCurrencyData.status val tokenUM = TokenItemState.Content( id = status.currency.id.value, iconState = CryptoCurrencyToIconStateConverter().convert(status.currency), @@ -39,11 +80,147 @@ internal class TokenActionsUiBuilder @Inject constructor( ) return TokenActionsUM( token = tokenUM, + quickActions = quickActions( + cryptoData = cryptoCurrencyData, + tokenActionsHandler = tokenActionsHandler, + isRedesignEnabled = false, + ), onLaterClick = { analyticsEventHandler.send(eventBuilder.getTokenLater()) params.callbacks.onLaterClick() }, - quickActions = quickActions(data, tokenActionsHandler), ) } + + private fun buildV2( + cryptoCurrencyData: CryptoCurrencyData, + tokenActionsHandler: TokenActionsHandler, + eventBuilder: PortfolioAnalyticsEvent.EventBuilder, + appCurrency: AppCurrency, + isBalanceHidden: Boolean, + ): TokenActionsUM { + val status = cryptoCurrencyData.status + val tokenUM = TokenItemState.Content( + id = status.currency.id.value, + iconState = CryptoCurrencyToIconStateConverter().convert(status.currency), + titleState = TokenItemState.TitleState.Content(stringReference(status.currency.name)), + fiatAmountState = createFiatAmountState(status, appCurrency), + subtitle2State = createSubtitle2State(status), + subtitleState = TokenItemState.SubtitleState.TextContent(stringReference(status.currency.symbol)), + onItemClick = null, + onItemLongClick = null, + ) + return TokenActionsUM( + token = tokenUM, + quickActions = quickActions( + cryptoData = cryptoCurrencyData, + tokenActionsHandler = tokenActionsHandler, + isRedesignEnabled = true, + ), + onLaterClick = { + analyticsEventHandler.send(eventBuilder.getTokenLater()) + params.callbacks.onLaterClick() + }, + isBalancesHidden = isBalanceHidden, + portfolioBadge = createPortfolioBadge(cryptoCurrencyData), + ) + } + + private fun createPortfolioBadge(cryptoCurrencyData: CryptoCurrencyData): TangemBadgeUM { + val icon: AccountIconUM? + val name = if (cryptoCurrencyData.isAccountMode) { + icon = CryptoPortfolioIconConverter.convert(cryptoCurrencyData.account.account.icon) + cryptoCurrencyData + .account + .account + .accountName + .toUM() + .value + } else { + icon = null + stringReference(cryptoCurrencyData.userWallet.name) + } + return TangemBadgeUM( + text = name, + tangemIconUM = if (icon == null) { + TangemIconUM.Icon( + iconRes = R.drawable.ic_key_card_20, + tintReference = { TangemTheme.colors2.graphic.neutral.tertiary }, + ) + } else { + TangemIconUM.Icon( + iconRes = icon.value.getResId(), + tintReference = { icon.color.getUiColor() }, + ) + }, + size = TangemBadgeSize.X6, + shape = TangemBadgeShape.Rounded, + iconPosition = if (cryptoCurrencyData.isAccountMode) { + TangemBadgeIconPosition.Start + } else { + TangemBadgeIconPosition.End + }, + ) + } + + private fun createSubtitle2State(status: CryptoCurrencyStatus): TokenItemState.Subtitle2State? { + return when (status.value) { + is CryptoCurrencyStatus.Loaded, + is CryptoCurrencyStatus.Custom, + is CryptoCurrencyStatus.NoQuote, + is CryptoCurrencyStatus.NoAccount, + -> { + TokenItemState.Subtitle2State.TextContent( + text = status.getTotalCryptoAmount().format { + crypto(status.currency) + }, + ) + } + is CryptoCurrencyStatus.Loading, + is CryptoCurrencyStatus.MissedDerivation, + is CryptoCurrencyStatus.Unreachable, + is CryptoCurrencyStatus.NoAmount, + -> TokenItemState.Subtitle2State.TextContent( + text = BigDecimal.ZERO.format { + crypto(status.currency) + }, + ) + } + } + + private fun createFiatAmountState( + status: CryptoCurrencyStatus, + appCurrency: AppCurrency, + ): TokenItemState.FiatAmountState? { + return when (status.value) { + is CryptoCurrencyStatus.Loaded, + is CryptoCurrencyStatus.Custom, + is CryptoCurrencyStatus.NoQuote, + is CryptoCurrencyStatus.NoAccount, + -> { + TokenItemState.FiatAmountState.AnnotatedContent( + text = status.getTotalFiatAmount().formatStyled { + fiat( + fiatCurrencyCode = appCurrency.code, + fiatCurrencySymbol = appCurrency.symbol, + spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.neutral.secondary) }, + ).price() + }, + ) + } + is CryptoCurrencyStatus.Unreachable, + is CryptoCurrencyStatus.NoAmount, + is CryptoCurrencyStatus.MissedDerivation, + is CryptoCurrencyStatus.Loading, + -> TokenItemState.FiatAmountState.AnnotatedContent( + text = BigDecimal.ZERO.formatStyled { + fiat( + fiatCurrencyCode = appCurrency.code, + fiatCurrencySymbol = appCurrency.symbol, + spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.neutral.secondary) }, + ).price() + }, + ) + } + } } \ No newline at end of file diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/ui/ChooseNetworkContent.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/ui/ChooseNetworkContent.kt index a682ee6396..a55e97e605 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/ui/ChooseNetworkContent.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/ui/ChooseNetworkContent.kt @@ -1,31 +1,47 @@ package com.tangem.features.commonfeatures.impl.addtoportfolio.ui import android.content.res.Configuration +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.key +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider -import androidx.compose.ui.util.fastForEachIndexed +import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.fastForEach import com.tangem.core.ui.components.label.Label import com.tangem.core.ui.components.label.entity.LabelStyle import com.tangem.core.ui.components.label.entity.LabelUM import com.tangem.core.ui.components.rows.BlockchainRow import com.tangem.core.ui.components.rows.model.BlockchainRowUM +import com.tangem.core.ui.ds.badge.TangemBadge +import com.tangem.core.ui.ds.badge.TangemBadgeShape +import com.tangem.core.ui.ds.badge.TangemBadgeSize +import com.tangem.core.ui.ds.row.TangemRowContainer +import com.tangem.core.ui.ds.row.TangemRowLayoutId import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.LocalRedesignEnabled import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview -import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.state.ChooseNetworkUM +import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.features.commonfeatures.impl.R +import com.tangem.features.commonfeatures.impl.addtoportfolio.ui.state.ChooseNetworkUM import kotlinx.collections.immutable.persistentListOf import java.util.UUID @@ -33,13 +49,28 @@ private const val DISABLED_ALPHA = 0.4f @Composable internal fun ChooseNetworkContent(state: ChooseNetworkUM, modifier: Modifier = Modifier) { + if (LocalRedesignEnabled.current) { + ChooseNetworkContentV2( + state = state, + modifier = modifier, + ) + } else { + ChooseNetworkContentV1( + state = state, + modifier = modifier, + ) + } +} + +@Composable +internal fun ChooseNetworkContentV1(state: ChooseNetworkUM, modifier: Modifier = Modifier) { Column( modifier = modifier .fillMaxWidth() .clip(RoundedCornerShape(TangemTheme.dimens.radius14)) .background(TangemTheme.colors.background.action), ) { - state.networks.fastForEachIndexed { index, model -> + state.networks.fastForEach { model -> key(model.id) { BlockchainRow( model = model, @@ -66,6 +97,120 @@ internal fun ChooseNetworkContent(state: ChooseNetworkUM, modifier: Modifier = M } } +@Composable +internal fun ChooseNetworkContentV2(state: ChooseNetworkUM, modifier: Modifier = Modifier) { + Column( + modifier = modifier + .fillMaxWidth() + .background( + color = TangemTheme.colors2.surface.level3, + shape = RoundedCornerShape(34.dp), + ) + .padding(horizontal = TangemTheme.dimens2.x4, vertical = TangemTheme.dimens2.x2), + ) { + Text( + modifier = Modifier.padding(top = TangemTheme.dimens2.x4, bottom = TangemTheme.dimens2.x2), + text = stringResourceSafe(R.string.common_choose_network), + style = TangemTheme.typography2.subheadlineMedium14, + color = TangemTheme.colors2.text.neutral.tertiary, + ) + + state.networks.fastForEach { model -> + key(model.id) { + TangemRowContainer( + modifier = Modifier.clickable( + enabled = model.isEnabled, + onClick = { state.onNetworkClick(model) }, + ), + contentPadding = PaddingValues(vertical = TangemTheme.dimens2.x3), + content = { + NetworkIcon( + modifier = Modifier + .layoutId(layoutId = TangemRowLayoutId.HEAD) + .padding(end = TangemTheme.dimens2.x3), + model = model, + ) + + NetworkText( + modifier = Modifier.layoutId(layoutId = TangemRowLayoutId.START_TOP), + model = model, + ) + if (!model.isEnabled) { + TangemBadge( + modifier = Modifier + .layoutId(layoutId = TangemRowLayoutId.TAIL) + .padding(start = TangemTheme.dimens2.x2), + text = resourceReference(R.string.common_added), + size = TangemBadgeSize.X6, + shape = TangemBadgeShape.Rounded, + ) + } + }, + ) + } + } + } +} + +@Composable +private fun NetworkIcon(model: BlockchainRowUM, modifier: Modifier = Modifier) { + if (model.isSelected && model.isEnabled) { + Image( + modifier = modifier + .size(TangemTheme.dimens2.x10), + painter = painterResource(id = model.iconResId), + contentDescription = null, + ) + } else { + Icon( + modifier = modifier + .background( + color = TangemTheme.colors2.button.backgroundSecondary, + shape = CircleShape, + ) + .size(TangemTheme.dimens2.x10), + painter = painterResource(id = model.iconResId), + tint = TangemTheme.colors2.graphic.neutral.tertiary, + contentDescription = null, + ) + } +} + +@Composable +private fun NetworkText(model: BlockchainRowUM, modifier: Modifier = Modifier) { + Row( + modifier = modifier, + verticalAlignment = Alignment.Bottom, + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1), + ) { + Text( + modifier = Modifier.weight(weight = 10f, fill = false), + text = model.name, + style = TangemTheme.typography2.bodyMedium16, + color = when { + model.isEnabled && model.isMainNetwork -> TangemTheme.colors2.text.neutral.primary + model.isEnabled -> TangemTheme.colors2.text.neutral.secondary + else -> TangemTheme.colors2.text.status.disabled + }, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + + Text( + modifier = Modifier.weight(weight = 5f, fill = false), + text = model.type, + style = TangemTheme.typography2.captionMedium12, + color = if (model.isEnabled) { + TangemTheme.colors2.text.neutral.tertiary + } else { + TangemTheme.colors2.text.status.disabled + }, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + } +} + @Composable @Preview(showBackground = true, widthDp = 360) @Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) @@ -77,6 +222,19 @@ private fun Preview(@PreviewParameter(ChooseNetworkContentProvider::class) conte } } +@Composable +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun PreviewV2(@PreviewParameter(ChooseNetworkContentProvider::class) content: ChooseNetworkUM) { + TangemThemePreviewRedesign { + CompositionLocalProvider(LocalRedesignEnabled provides true) { + ChooseNetworkContent( + state = content, + ) + } + } +} + internal class ChooseNetworkContentProvider : PreviewParameterProvider { private val blockchainRow = BlockchainRowUM( diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/ui/TokenActionsContent.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/ui/TokenActionsContent.kt index 23d8df105c..1b7f686fb7 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/ui/TokenActionsContent.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/ui/TokenActionsContent.kt @@ -123,7 +123,7 @@ private fun ActionRow( .size(36.dp) .drawWithContent { drawContent() - if (state is QuickActionUM.Exchange && state.shouldShowBadge) { + if (state is QuickActionUM.V1.Exchange && state.shouldShowBadge) { drawBadge(containerColor = containerColor, offset = 4.dp) } }, @@ -190,9 +190,9 @@ private class TokenActionsContentPreviewProvider : PreviewParameterProvider + key(actionUM.title) { + ActionRow( + state = actionUM, + onClick = { state.quickActions.onQuickActionClick(actionUM) }, + onLongClick = { state.quickActions.onQuickActionLongClick(actionUM) }, + ) + } + } + } + + SpacerH(TangemTheme.dimens2.x2) + + SecondaryTangemButton( + modifier = Modifier.fillMaxWidth(), + onClick = state.onLaterClick, + text = resourceReference(R.string.common_later), + size = TangemButtonSize.X12, + shape = TangemButtonShape.Rounded, + ) + } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +private fun ActionRow( + state: QuickActionUM, + onClick: () -> Unit, + onLongClick: (() -> Unit), + modifier: Modifier = Modifier, +) { + val hapticManager = LocalHapticManager.current + val onLongClickInternal = { + hapticManager.perform(TangemHapticEffect.View.LongPress) + onLongClick() + } + + TangemRowContainer( + modifier = modifier + .combinedClickable( + onLongClick = onLongClickInternal.takeIf { state.isLongClickAvailable }, + onClick = { + hapticManager.perform(TangemHapticEffect.View.SegmentTick) + onClick() + }, + ) + .background( + color = TangemTheme.colors2.surface.level3, + shape = RoundedCornerShape(TangemTheme.dimens2.x5), + ), + ) { + Box( + modifier = Modifier + .layoutId(TangemRowLayoutId.HEAD) + .padding(end = TangemTheme.dimens2.x3) + .size(40.dp) + .background( + color = TangemTheme.colors2.graphic.status.accent.copy(alpha = ACTION_BACKGROUND_ALPHA), + shape = CircleShape, + ), + contentAlignment = Alignment.Center, + ) { + Icon( + modifier = Modifier.size(20.dp), + imageVector = ImageVector.vectorResource(id = state.icon), + contentDescription = null, + tint = TangemTheme.colors2.graphic.status.accent, + ) + } + Text( + modifier = Modifier.layoutId(TangemRowLayoutId.START_TOP), + text = state.title.resolveReference(), + style = TangemTheme.typography2.bodyMedium16, + color = TangemTheme.colors2.text.neutral.primary, + ) + Text( + modifier = Modifier.layoutId(TangemRowLayoutId.START_BOTTOM), + text = state.description.resolveReference(), + style = TangemTheme.typography2.captionMedium12, + color = TangemTheme.colors2.text.neutral.secondary, + ) + Icon( + modifier = Modifier + .layoutId(TangemRowLayoutId.TAIL) + .padding(start = TangemTheme.dimens2.x2) + .size(24.dp), + imageVector = ImageVector.vectorResource(R.drawable.ic_chevron_small_right_24), + tint = TangemTheme.colors2.graphic.neutral.tertiary, + contentDescription = null, + ) + } +} + +@Composable +private fun TokenHeader( + addedToken: TokenItemState, + isBalanceHidden: Boolean, + portfolioBadge: TangemBadgeUM?, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxWidth() + .padding(vertical = TangemTheme.dimens2.x8), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + CurrencyIcon( + state = addedToken.iconState, + iconSize = 70.dp, + networkBadgeSize = 24.dp, + ) + + SpacerH(TangemTheme.dimens2.x5) + + when (val fiat = addedToken.fiatAmountState) { + is TokenItemState.FiatAmountState.AnnotatedContent -> { + Text( + text = fiat.text.orMaskWithStars(isBalanceHidden).resolveAnnotatedReference(), + style = TangemTheme.typography2.titleRegular44, + color = TangemTheme.colors2.text.neutral.primary, + ) + SpacerH(TangemTheme.dimens2.x2) + } + else -> Unit + } + + when (val cryptoAmount = addedToken.subtitle2State) { + is TokenItemState.Subtitle2State.TextContent -> { + Text( + text = cryptoAmount.text.orMaskWithStars(isBalanceHidden), + style = TangemTheme.typography2.bodyMedium16, + color = TangemTheme.colors2.text.neutral.secondary, + ) + SpacerH(TangemTheme.dimens2.x2) + } + else -> Unit + } + + SpacerH(TangemTheme.dimens2.x7) + + if (portfolioBadge == null) return + TangemBadge(portfolioBadge) + } +} + +@Preview(widthDp = 360, showBackground = true) +@Preview(widthDp = 360, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun Preview(@PreviewParameter(TokenActionsContentPreviewProviderV2::class) state: TokenActionsUM) { + TangemThemePreviewRedesign { + CompositionLocalProvider(LocalRedesignEnabled provides true) { + Box( + modifier = Modifier + .fillMaxWidth() + .background(TangemTheme.colors2.surface.level2) + .padding(horizontal = 16.dp), + ) { + TokenActionsContentV2( + state = state, + ) + } + } + } +} + +private class TokenActionsContentPreviewProviderV2 : PreviewParameterProvider { + private val tokenState + get() = TokenItemState.Content( + id = UUID.randomUUID().toString(), + iconState = CurrencyIconState.TokenIcon( + url = null, + topBadgeIconResId = R.drawable.img_eth_22, + fallbackTint = TangemColorPalette.Black, + fallbackBackground = TangemColorPalette.Meadow, + isGrayscale = false, + shouldShowCustomBadge = false, + ), + titleState = TokenItemState.TitleState.Content( + text = stringReference(value = "Tether"), + ), + fiatAmountState = TokenItemState.FiatAmountState.AnnotatedContent( + text = BigDecimal.ONE.formatStyled { + fiat( + fiatCurrencyCode = "USD", + fiatCurrencySymbol = "$", + spanStyleReference = { SpanStyle(color = TangemTheme.colors2.text.neutral.secondary) }, + ).price() + }, + ), + subtitle2State = TokenItemState.Subtitle2State.TextContent( + "1.01 USDT", + ), + subtitleState = TokenItemState.SubtitleState.TextContent(value = stringReference("USDT")), + onItemClick = {}, + onItemLongClick = {}, + ) + + override val values: Sequence + get() = sequenceOf( + TokenActionsUM( + quickActions = QuickActions( + actions = persistentListOf( + QuickActionUM.V2.Buy, + QuickActionUM.V2.Exchange(shouldShowBadge = true), + QuickActionUM.V2.Receive, + ), + onQuickActionClick = {}, + onQuickActionLongClick = {}, + ), + token = tokenState, + onLaterClick = {}, + portfolioBadge = TangemBadgeUM( + text = stringReference("Wallet 2"), + tangemIconUM = TangemIconUM.Icon( + iconRes = R.drawable.ic_key_card_20, + tintReference = { TangemTheme.colors2.graphic.neutral.tertiary }, + ), + size = TangemBadgeSize.X6, + shape = TangemBadgeShape.Rounded, + iconPosition = TangemBadgeIconPosition.End, + ), + ), + ) +} \ No newline at end of file diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/ui/state/TokenActionsUM.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/ui/state/TokenActionsUM.kt index b0b0b9b56e..8af3513085 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/ui/state/TokenActionsUM.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/addtoportfolio/ui/state/TokenActionsUM.kt @@ -2,9 +2,12 @@ package com.tangem.features.commonfeatures.impl.addtoportfolio.ui.state import com.tangem.common.ui.markets.action.QuickActions import com.tangem.core.ui.components.token.state.TokenItemState +import com.tangem.core.ui.ds.badge.TangemBadgeUM internal data class TokenActionsUM( val token: TokenItemState, val quickActions: QuickActions, val onLaterClick: () -> Unit, + val isBalancesHidden: Boolean = false, + val portfolioBadge: TangemBadgeUM? = null, ) \ No newline at end of file diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/portfolioselector/DefaultPortfolioSelectorComponent.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/portfolioselector/DefaultPortfolioSelectorComponent.kt index b76d6089cc..1d3df6157b 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/portfolioselector/DefaultPortfolioSelectorComponent.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/portfolioselector/DefaultPortfolioSelectorComponent.kt @@ -7,9 +7,11 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.res.LocalRedesignEnabled import com.tangem.features.commonfeatures.api.portfolioselector.PortfolioSelectorComponent import com.tangem.features.commonfeatures.impl.portfolioselector.ui.PortfolioSelectorBS import com.tangem.features.commonfeatures.impl.portfolioselector.ui.PortfolioSelectorContent +import com.tangem.features.commonfeatures.impl.portfolioselector.ui.PortfolioSelectorContentV2 import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -43,10 +45,17 @@ internal class DefaultPortfolioSelectorComponent @AssistedInject constructor( @Composable override fun Content(modifier: Modifier) { val state by model.state.collectAsStateWithLifecycle() - PortfolioSelectorContent( - state = state, - modifier = modifier, - ) + if (LocalRedesignEnabled.current) { + PortfolioSelectorContentV2( + state = state, + modifier = modifier, + ) + } else { + PortfolioSelectorContent( + state = state, + modifier = modifier, + ) + } } @AssistedFactory diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/portfolioselector/ui/PortfolioSelectorBS.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/portfolioselector/ui/PortfolioSelectorBS.kt index cd69452b68..14db910070 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/portfolioselector/ui/PortfolioSelectorBS.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/portfolioselector/ui/PortfolioSelectorBS.kt @@ -4,6 +4,7 @@ import android.content.res.Configuration import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter @@ -12,8 +13,10 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle +import com.tangem.core.ui.res.LocalRedesignEnabled import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.res.TangemThemePreviewRedesign import com.tangem.features.commonfeatures.impl.R import com.tangem.features.commonfeatures.impl.portfolioselector.entity.PortfolioSelectorUM @@ -41,11 +44,19 @@ internal fun PortfolioSelectorBS( ) }, content = { - PortfolioSelectorContent( - state = state, - contentPadding = PaddingValues(bottom = 16.dp), - modifier = modifier.padding(horizontal = 16.dp), - ) + if (LocalRedesignEnabled.current) { + PortfolioSelectorContentV2( + state = state, + contentPadding = PaddingValues(bottom = 16.dp), + modifier = modifier.padding(horizontal = 16.dp), + ) + } else { + PortfolioSelectorContent( + state = state, + contentPadding = PaddingValues(bottom = 16.dp), + modifier = modifier.padding(horizontal = 16.dp), + ) + } }, ) } @@ -62,4 +73,20 @@ private fun Preview(@PreviewParameter(PortfolioSelectorPreviewStateProvider::cla onBack = {}, ) } +} + +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun PreviewV2(@PreviewParameter(PortfolioSelectorPreviewStateProvider::class) params: PortfolioSelectorUM) { + TangemThemePreviewRedesign { + CompositionLocalProvider(LocalRedesignEnabled provides true) { + PortfolioSelectorBS( + state = params, + onDismiss = {}, + modifier = Modifier, + onBack = {}, + ) + } + } } \ No newline at end of file diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/portfolioselector/ui/PortfolioSelectorContentV2.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/portfolioselector/ui/PortfolioSelectorContentV2.kt new file mode 100644 index 0000000000..1d7b42931f --- /dev/null +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/portfolioselector/ui/PortfolioSelectorContentV2.kt @@ -0,0 +1,228 @@ +package com.tangem.features.commonfeatures.impl.portfolioselector.ui + +import android.content.res.Configuration +import androidx.compose.animation.AnimatedContent +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.res.vectorResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import com.tangem.common.ui.userwallet.CardImage +import com.tangem.common.ui.userwallet.getBalanceValueAndFlickerState +import com.tangem.common.ui.userwallet.getInformationValue +import com.tangem.common.ui.userwallet.state.UserWalletItemUM +import com.tangem.core.ui.R +import com.tangem.core.ui.components.TextShimmer +import com.tangem.core.ui.components.text.applyBladeBrush +import com.tangem.core.ui.decorations.roundedShapeItemDecoration +import com.tangem.core.ui.ds.row.TangemRowContainer +import com.tangem.core.ui.ds.row.TangemRowLayoutId +import com.tangem.core.ui.extensions.conditional +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.LocalRedesignEnabled +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreviewRedesign +import com.tangem.features.commonfeatures.impl.portfolioselector.entity.PortfolioSelectorItemUM +import com.tangem.features.commonfeatures.impl.portfolioselector.entity.PortfolioSelectorUM +import com.tangem.utils.StringsSigns.DOT + +private const val DISABLED_WALLET_ALPHA = 0.5f + +@Composable +internal fun PortfolioSelectorContentV2( + state: PortfolioSelectorUM, + modifier: Modifier = Modifier, + contentPadding: PaddingValues = PaddingValues(), +) { + LazyColumn( + modifier = modifier, + contentPadding = contentPadding, + ) { + val items = state.items + itemsIndexed( + items = items, + key = { _, item -> item.id }, + ) { index, item -> + when (item) { + is PortfolioSelectorItemUM.Portfolio -> + PortfolioSelectorItem( + state = item.item, + modifier = Modifier + .roundedShapeItemDecoration( + currentIndex = index, + lastIndex = state.items.lastIndex, + backgroundColor = TangemTheme.colors2.surface.level3, + radius = TangemTheme.dimens2.x5, + addDefaultPadding = false, + ) + .clickable(enabled = item.item.isEnabled, onClick = item.item.onClick) + .conditional(!item.item.isEnabled) { alpha(DISABLED_WALLET_ALPHA) }, + ) + is PortfolioSelectorItemUM.GroupTitle -> WalletNameRow( + model = item, + modifier = Modifier + .fillMaxWidth() + .roundedShapeItemDecoration( + currentIndex = index, + lastIndex = state.items.lastIndex, + backgroundColor = TangemTheme.colors2.surface.level3, + radius = TangemTheme.dimens2.x5, + addDefaultPadding = false, + ) + .padding(horizontal = TangemTheme.dimens.spacing16), + ) + } + } + } +} + +@Composable +private fun PortfolioSelectorItem(state: UserWalletItemUM, modifier: Modifier = Modifier) { + TangemRowContainer(modifier = modifier) { + Box( + modifier = Modifier + .layoutId(TangemRowLayoutId.HEAD) + .padding(end = TangemTheme.dimens2.x3), + contentAlignment = Alignment.Center, + ) { + CardImage( + modifier = Modifier.size(40.dp), + imageState = state.imageState, + ) + } + + Text( + modifier = Modifier.layoutId(layoutId = TangemRowLayoutId.START_TOP), + text = state.name.resolveReference(), + style = TangemTheme.typography2.bodyMedium16, + color = TangemTheme.colors2.text.neutral.primary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + + InfoRow( + modifier = Modifier.layoutId(layoutId = TangemRowLayoutId.START_BOTTOM), + information = state.information, + balance = state.balance, + ) + } +} + +@Composable +private fun InfoRow( + information: UserWalletItemUM.Information, + balance: UserWalletItemUM.Balance, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically, + ) { + AnimatedContent( + targetState = information, + label = "Information content", + ) { information -> + val informationValue = getInformationValue(information) + + if (informationValue == null) { + TextShimmer( + style = TangemTheme.typography2.captionMedium12, + text = "aaaaa", + ) + } else { + Text( + text = informationValue, + style = TangemTheme.typography2.captionMedium12, + color = TangemTheme.colors2.text.neutral.secondary, + maxLines = 1, + ) + } + } + + Row { + Text( + text = " $DOT ", + style = TangemTheme.typography2.captionMedium12, + color = TangemTheme.colors2.text.neutral.secondary, + maxLines = 1, + ) + + val (balanceValue, isFlickering) = getBalanceValueAndFlickerState(balance) + + if (balanceValue == null) { + TextShimmer( + style = TangemTheme.typography2.captionMedium12, + text = "aaaaa", + ) + } else { + Text( + text = balanceValue, + style = TangemTheme.typography2.captionMedium12.applyBladeBrush( + isEnabled = isFlickering, + textColor = TangemTheme.colors2.text.neutral.secondary, + ), + maxLines = 1, + ) + } + } + } +} + +@Composable +private fun WalletNameRow(model: PortfolioSelectorItemUM.GroupTitle, modifier: Modifier = Modifier) { + Row( + modifier = modifier.padding(top = TangemTheme.dimens2.x4, bottom = TangemTheme.dimens2.x2), + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens2.x1), + ) { + Text( + modifier = Modifier.align(Alignment.CenterVertically), + text = model.name.resolveReference(), + style = TangemTheme.typography2.subheadlineMedium14, + color = TangemTheme.colors2.text.neutral.secondary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + + Icon( + imageVector = ImageVector.vectorResource(R.drawable.ic_key_card_20), + modifier = Modifier + .align(Alignment.Bottom) + .size(TangemTheme.dimens2.x5), + tint = TangemTheme.colors2.graphic.neutral.tertiary, + contentDescription = null, + ) + } +} + +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun Preview(@PreviewParameter(PortfolioSelectorPreviewStateProvider::class) params: PortfolioSelectorUM) { + TangemThemePreviewRedesign { + CompositionLocalProvider(LocalRedesignEnabled provides true) { + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center, + ) { + PortfolioSelectorContentV2( + state = params, + modifier = Modifier.background(color = TangemTheme.colors.background.tertiary), + ) + } + } + } +} \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/MarketsPortfolioDelegate.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/MarketsPortfolioDelegate.kt index 6c8cb07a7c..f4ba22eab4 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/MarketsPortfolioDelegate.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/MarketsPortfolioDelegate.kt @@ -7,6 +7,7 @@ import com.tangem.common.ui.account.CryptoPortfolioIconConverter import com.tangem.common.ui.account.toUM import com.tangem.common.ui.markets.action.CryptoCurrencyData import com.tangem.common.ui.markets.action.TokenActionsHandler +import com.tangem.core.ui.DesignFeatureToggles import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.stringReference import com.tangem.domain.account.models.AccountStatusList @@ -53,6 +54,7 @@ internal class MarketsPortfolioDelegate @AssistedInject constructor( private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCaseV2, private val getUserWalletUseCase: GetUserWalletUseCase, private val yieldSupplyGetAvailabilityUseCase: YieldSupplyGetAvailabilityUseCase, + private val designFeatureToggles: DesignFeatureToggles, isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase, @Assisted private val scope: CoroutineScope, @Assisted private val token: TokenMarketParams, @@ -229,6 +231,7 @@ internal class MarketsPortfolioDelegate @AssistedInject constructor( isBalanceHidden = isBalanceHidden, onTokenItemClick = { }, tokenActionsHandler = tokenActionsHandler, + isRedesignEnabled = designFeatureToggles.isRedesignEnabled, ) portfolio.portfolios.forEach { portfolioItem -> @@ -253,6 +256,8 @@ internal class MarketsPortfolioDelegate @AssistedInject constructor( userWallet = userWallet, status = currencyStatus, actions = actions, + isAccountMode = isAccountMode, + account = accountWithAdded.accountStatus, ) val expandedKey = portfolioItem.userWallet.walletId to currencyStatus.currency.id val isExpand = expanded.contains(expandedKey) diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/PortfolioTokenUMConverter.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/PortfolioTokenUMConverter.kt index 5474899d5c..30ca070cee 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/PortfolioTokenUMConverter.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/PortfolioTokenUMConverter.kt @@ -22,6 +22,7 @@ internal class PortfolioTokenUMConverter( private val isBalanceHidden: Boolean, private val onTokenItemClick: (CryptoCurrencyStatus) -> Unit, private val tokenActionsHandler: TokenActionsHandler, + private val isRedesignEnabled: Boolean, ) : Converter { fun convertV2( @@ -38,7 +39,11 @@ internal class PortfolioTokenUMConverter( walletId = value.userWallet.walletId, isBalanceHidden = isBalanceHidden, isQuickActionsShown = isQuickActionsShown, - quickActions = quickActions(cryptoData = value, tokenActionsHandler = tokenActionsHandler), + quickActions = quickActions( + cryptoData = value, + tokenActionsHandler = tokenActionsHandler, + isRedesignEnabled = isRedesignEnabled, + ), ) } @@ -57,7 +62,11 @@ internal class PortfolioTokenUMConverter( walletId = value.userWallet.walletId, isBalanceHidden = isBalanceHidden, isQuickActionsShown = false, - quickActions = quickActions(cryptoData = value, tokenActionsHandler = tokenActionsHandler), + quickActions = quickActions( + cryptoData = value, + tokenActionsHandler = tokenActionsHandler, + isRedesignEnabled = isRedesignEnabled, + ), ) } } \ No newline at end of file diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/ui/PortfolioQuickActions.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/ui/PortfolioQuickActions.kt index b1ad93e9b2..8c447cad20 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/ui/PortfolioQuickActions.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/ui/PortfolioQuickActions.kt @@ -180,7 +180,7 @@ private fun AnimatedVisibilityScope.QuickActionIcon(state: QuickActionUM) { ) .size(TangemTheme.dimens.size32) .semantics { - contentDescription = if (state is QuickActionUM.Exchange && state.shouldShowBadge) { + contentDescription = if (state is QuickActionUM.V1.Exchange && state.shouldShowBadge) { "Badge shown" } else { "Badge hidden" @@ -188,7 +188,7 @@ private fun AnimatedVisibilityScope.QuickActionIcon(state: QuickActionUM) { } .drawWithContent { drawContent() - if (state is QuickActionUM.Exchange && state.shouldShowBadge) { + if (state is QuickActionUM.V1.Exchange && state.shouldShowBadge) { drawBadge(containerColor = containerColor, offset = 4.dp) } }, @@ -229,9 +229,9 @@ private fun Preview() { ) { PortfolioQuickActions( actions = persistentListOf( - QuickActionUM.Buy, - QuickActionUM.Exchange(shouldShowBadge = true), - QuickActionUM.Receive, + QuickActionUM.V1.Buy, + QuickActionUM.V1.Exchange(shouldShowBadge = true), + QuickActionUM.V1.Receive, ), isVisible = isVisible, onActionClick = {}, @@ -250,9 +250,9 @@ private fun PreviewRtl() { Box(modifier = Modifier.background(color = TangemTheme.colors.background.action)) { PortfolioQuickActions( actions = persistentListOf( - QuickActionUM.Buy, - QuickActionUM.Exchange(shouldShowBadge = true), - QuickActionUM.Receive, + QuickActionUM.V1.Buy, + QuickActionUM.V1.Exchange(shouldShowBadge = true), + QuickActionUM.V1.Receive, ), isVisible = true, onActionClick = {}, diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt index ebc36fbbd5..38675976ef 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt @@ -129,9 +129,9 @@ internal class PreviewMyPortfolioUMProvider : PreviewParameterProvider