Updated on 2026-08-14

This commit is contained in:
Tangem 2024-10-30 18:24:10 +04:00
parent 45273dbc9f
commit f3ee1dd9a9
10 changed files with 225 additions and 17 deletions

View file

@ -53,6 +53,7 @@ internal fun ContentIcon(
)
CurrencyIconState.Loading,
CurrencyIconState.Locked,
is CurrencyIconState.Empty,
-> Unit
}
}

View file

@ -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,

View file

@ -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
}
}

View file

@ -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

View file

@ -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,
),

View file

@ -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)
}

View file

@ -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

View file

@ -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
}
}

View file

@ -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<ExchangeCardUM> {
override val values: Sequence<ExchangeCardUM> = 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 = {},
),
),
)
}