From f3ee1dd9a965f2ba1af5b5147986957a0ab00b26 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 30 Oct 2024 18:24:10 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../components/currency/icon/ContentIcon.kt | 1 + .../components/currency/icon/CurrencyIcon.kt | 23 ++++ .../currency/icon/CurrencyIconState.kt | 10 ++ .../ui}/src/main/res/drawable/ic_empty_64.xml | 0 .../onramp/buy/DefaultBuyCryptoComponent.kt | 6 +- .../DefaultOnrampOperationComponent.kt | 2 +- .../selecttoken/OnrampOperationComponent.kt | 7 +- .../onramp/sell/DefaultSellCryptoComponent.kt | 6 +- .../onramp/swap/entity/ExchangeCardUM.kt | 63 +++++++++ .../features/onramp/swap/ui/ExchangeCard.kt | 124 ++++++++++++++++++ 10 files changed, 225 insertions(+), 17 deletions(-) rename {features/wallet/impl => core/ui}/src/main/res/drawable/ic_empty_64.xml (100%) create mode 100644 features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/ExchangeCardUM.kt create mode 100644 features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/ui/ExchangeCard.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/ContentIcon.kt b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/ContentIcon.kt index 089d050c7d..8d1787952d 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/ContentIcon.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/ContentIcon.kt @@ -53,6 +53,7 @@ internal fun ContentIcon( ) CurrencyIconState.Loading, CurrencyIconState.Locked, + is CurrencyIconState.Empty, -> Unit } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIcon.kt b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIcon.kt index d06e59fc30..1b92960c51 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIcon.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIcon.kt @@ -1,15 +1,19 @@ package com.tangem.core.ui.components.currency.icon +import androidx.annotation.DrawableRes import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp import com.tangem.core.ui.components.CircleShimmer import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.utils.getGreyScaleColorFilter @@ -33,6 +37,7 @@ fun CurrencyIcon(state: CurrencyIconState, modifier: Modifier = Modifier, should when (state) { is CurrencyIconState.Loading -> LoadingIcon(modifier = iconModifier) is CurrencyIconState.Locked -> LockedIcon(modifier = iconModifier) + is CurrencyIconState.Empty -> EmptyIcon(resId = state.resId, modifier = iconModifier) is CurrencyIconState.CoinIcon, is CurrencyIconState.CustomTokenIcon, is CurrencyIconState.TokenIcon, @@ -66,6 +71,24 @@ private fun LockedIcon(modifier: Modifier = Modifier) { } } +@Composable +private fun EmptyIcon(@DrawableRes resId: Int, modifier: Modifier = Modifier) { + Box( + modifier = modifier.background( + color = TangemTheme.colors.button.secondary, + shape = CircleShape, + ), + contentAlignment = Alignment.Center, + ) { + Icon( + painter = painterResource(resId), + contentDescription = null, + modifier = Modifier.size(size = 24.dp), + tint = TangemTheme.colors.icon.informative, + ) + } +} + @Composable private fun BoxScope.ContentIconContainer( icon: CurrencyIconState, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIconState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIconState.kt index f19975ab9e..371ad59f61 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIconState.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/currency/icon/CurrencyIconState.kt @@ -3,6 +3,7 @@ package com.tangem.core.ui.components.currency.icon import androidx.annotation.DrawableRes import androidx.compose.runtime.Immutable import androidx.compose.ui.graphics.Color +import com.tangem.core.ui.R /** * Represents the various states an icon can be in. @@ -82,6 +83,14 @@ sealed class CurrencyIconState { override val topBadgeIconResId: Int? = null } + data class Empty( + @DrawableRes val resId: Int = R.drawable.ic_empty_64, + ) : CurrencyIconState() { + override val isGrayscale: Boolean = true + override val showCustomBadge: Boolean = false + override val topBadgeIconResId: Int? = null + } + fun copySealed( isGrayscale: Boolean = this.isGrayscale, showCustomBadge: Boolean = this.showCustomBadge, @@ -103,6 +112,7 @@ sealed class CurrencyIconState { ) is Loading, is Locked, + is Empty, -> this } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/res/drawable/ic_empty_64.xml b/core/ui/src/main/res/drawable/ic_empty_64.xml similarity index 100% rename from features/wallet/impl/src/main/res/drawable/ic_empty_64.xml rename to core/ui/src/main/res/drawable/ic_empty_64.xml diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/buy/DefaultBuyCryptoComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/buy/DefaultBuyCryptoComponent.kt index f8b1be4d51..8e36bf09b0 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/buy/DefaultBuyCryptoComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/buy/DefaultBuyCryptoComponent.kt @@ -20,11 +20,7 @@ internal class DefaultBuyCryptoComponent @AssistedInject constructor( private val selectTokenComponent: OnrampOperationComponent = onrampOperationComponentFactory.create( context = appComponentContext, - params = OnrampOperationComponent.Params( - operation = OnrampOperation.BUY, - hasSearchBar = true, - userWalletId = params.userWalletId, - ), + params = OnrampOperationComponent.Params(operation = OnrampOperation.BUY, userWalletId = params.userWalletId), ) @Composable diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/DefaultOnrampOperationComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/DefaultOnrampOperationComponent.kt index 429df40cb5..454ba6bea6 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/DefaultOnrampOperationComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/DefaultOnrampOperationComponent.kt @@ -33,7 +33,7 @@ internal class DefaultOnrampOperationComponent @AssistedInject constructor( context = child(key = "token_list"), params = OnrampTokenListComponent.Params( filterOperation = params.operation, - hasSearchBar = params.hasSearchBar, + hasSearchBar = true, userWalletId = params.userWalletId, onTokenClick = ::onTokenClick, ), diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/OnrampOperationComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/OnrampOperationComponent.kt index 3f6b4fc489..c35a452d58 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/OnrampOperationComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/OnrampOperationComponent.kt @@ -18,12 +18,7 @@ internal interface OnrampOperationComponent : ComposableContentComponent { * Params * * @property operation operation - * @property hasSearchBar flag that indicates if search bar should be shown * @property userWalletId id of multi-currency wallet */ - data class Params( - val operation: OnrampOperation, - val hasSearchBar: Boolean, - val userWalletId: UserWalletId, - ) + data class Params(val operation: OnrampOperation, val userWalletId: UserWalletId) } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/sell/DefaultSellCryptoComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/sell/DefaultSellCryptoComponent.kt index ffba48a738..f7d63936d1 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/sell/DefaultSellCryptoComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/sell/DefaultSellCryptoComponent.kt @@ -20,11 +20,7 @@ internal class DefaultSellCryptoComponent @AssistedInject constructor( private val selectTokenComponent: OnrampOperationComponent = onrampOperationComponentFactory.create( context = appComponentContext, - params = OnrampOperationComponent.Params( - operation = OnrampOperation.SELL, - hasSearchBar = true, - userWalletId = params.userWalletId, - ), + params = OnrampOperationComponent.Params(operation = OnrampOperation.SELL, userWalletId = params.userWalletId), ) @Composable diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/ExchangeCardUM.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/ExchangeCardUM.kt new file mode 100644 index 0000000000..07c60222b4 --- /dev/null +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/ExchangeCardUM.kt @@ -0,0 +1,63 @@ +package com.tangem.features.onramp.swap.entity + +import com.tangem.core.ui.components.currency.icon.CurrencyIconState +import com.tangem.core.ui.components.token.state.TokenItemState +import com.tangem.core.ui.extensions.TextReference +import com.tangem.features.onramp.impl.R + +/** + * Exchange card UI model + * +[REDACTED_AUTHOR] + */ +internal sealed interface ExchangeCardUM { + + /** Title reference */ + val titleReference: TextReference + + /** Flag that indicates if remove button should be shown */ + val hasRemoveButton: Boolean + + /** Token item state */ + val tokenItemState: TokenItemState + + /** + * Empty state + * + * @property titleReference title reference + * @property onItemClick callback which will be called when an item is clicked + */ + data class Empty( + override val titleReference: TextReference, + val onItemClick: () -> Unit, + ) : ExchangeCardUM { + + override val hasRemoveButton: Boolean = false + + // TODO: [REDACTED_JIRA] + override val tokenItemState: TokenItemState = TokenItemState.Content( + id = "empty", + iconState = CurrencyIconState.Empty(R.drawable.ic_empty_64), + titleState = TokenItemState.TitleState.Content(text = "Choose the Token"), + subtitleState = TokenItemState.SubtitleState.TextContent(value = "You want to Swap"), + fiatAmountState = TokenItemState.FiatAmountState.Content(text = ""), + subtitle2State = TokenItemState.Subtitle2State.TextContent(text = ""), + onItemClick = onItemClick, + onItemLongClick = null, + ) + } + + /** + * Filled + * + * @property titleReference title reference + * @property tokenItemState token item state + */ + data class Filled( + override val titleReference: TextReference, + override val tokenItemState: TokenItemState, + ) : ExchangeCardUM { + + override val hasRemoveButton: Boolean = true + } +} \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/ui/ExchangeCard.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/ui/ExchangeCard.kt new file mode 100644 index 0000000000..4b1ce4c245 --- /dev/null +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/ui/ExchangeCard.kt @@ -0,0 +1,124 @@ +package com.tangem.features.onramp.swap.ui + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.material3.Text +import androidx.compose.material3.ripple +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +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.unit.dp +import com.tangem.core.ui.components.currency.icon.CurrencyIconState +import com.tangem.core.ui.components.marketprice.PriceChangeType +import com.tangem.core.ui.components.rows.NetworkTitle +import com.tangem.core.ui.components.token.TokenItem +import com.tangem.core.ui.components.token.state.TokenItemState +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.features.onramp.impl.R +import com.tangem.features.onramp.swap.entity.ExchangeCardUM + +/** + * Exchange card + * + * @param state state + * @param modifier modifier + * +[REDACTED_AUTHOR] + */ +@Composable +internal fun ExchangeCard(state: ExchangeCardUM, modifier: Modifier = Modifier) { + Column( + modifier = modifier + .fillMaxWidth() + .heightIn(min = 116.dp) + .background( + color = TangemTheme.colors.background.primary, + shape = TangemTheme.shapes.roundedCornersXMedium, + ), + verticalArrangement = Arrangement.SpaceBetween, + ) { + NetworkTitle( + title = { + Text( + text = state.titleReference.resolveReference(), + color = TangemTheme.colors.text.tertiary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = TangemTheme.typography.subtitle2, + ) + }, + action = { + RemoveButton(hasRemoveButton = state.hasRemoveButton, onClick = {}) + }, + ) + + TokenItem(state = state.tokenItemState, isBalanceHidden = false) + } +} + +@Composable +private fun RemoveButton(hasRemoveButton: Boolean, onClick: () -> Unit) { + AnimatedVisibility(visible = hasRemoveButton) { + Text( + text = stringResource(id = R.string.manage_tokens_remove), + modifier = Modifier.clickable( + indication = ripple(bounded = false), + interactionSource = remember { MutableInteractionSource() }, + onClick = onClick, + ), + color = TangemTheme.colors.text.accent, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = TangemTheme.typography.body2, + ) + } +} + +@Preview +@Composable +private fun Preview_ExchangeCard(@PreviewParameter(ExchangeCardUMProvider::class) state: ExchangeCardUM) { + TangemThemePreview { + ExchangeCard(state = state) + } +} + +private class ExchangeCardUMProvider : PreviewParameterProvider { + + override val values: Sequence = sequenceOf( + ExchangeCardUM.Empty( + titleReference = resourceReference(id = R.string.swapping_from_title), + onItemClick = {}, + ), + ExchangeCardUM.Filled( + titleReference = resourceReference(id = R.string.swapping_from_title), + tokenItemState = TokenItemState.Content( + id = "1", + iconState = CurrencyIconState.Locked, + titleState = TokenItemState.TitleState.Content(text = "Bitcoin"), + fiatAmountState = TokenItemState.FiatAmountState.Content(text = "12 368,14 \$"), + subtitle2State = TokenItemState.Subtitle2State.TextContent(text = "0,35853044 BTC"), + subtitleState = TokenItemState.SubtitleState.CryptoPriceContent( + price = "34 496,75 \$", + priceChangePercent = "0,43 %", + type = PriceChangeType.DOWN, + ), + onItemClick = {}, + onItemLongClick = {}, + ), + ), + ) +} \ No newline at end of file