Updated on 2026-08-14

This commit is contained in:
Tangem 2025-08-14 12:31:28 +05:00
commit 967f8b2ea0
17 changed files with 247 additions and 147 deletions

View file

@ -134,7 +134,7 @@ private fun AddressWithMemoBlock(
Column(modifier = Modifier.weight(1f)) {
Text(
text = address.value,
style = TangemTheme.typography.body2,
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.primary1,
)
val blockchainAddress = address.briefBlockchainAddress

View file

@ -8,6 +8,7 @@ import com.tangem.core.ui.components.atoms.text.TextEllipsis
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
@ -16,6 +17,7 @@ import com.tangem.domain.swap.models.SwapDirection
import com.tangem.features.swap.v2.impl.R
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType
import com.tangem.utils.StringsSigns.DOT
internal class SwapAmountFieldConverter(
private val swapDirection: SwapDirection,
@ -60,15 +62,26 @@ internal class SwapAmountFieldConverter(
}
private fun getSubtitle(selectedType: SwapAmountType, cryptoCurrencyStatus: CryptoCurrencyStatus) = when {
selectedType.isEnteringField() -> resourceReference(
R.string.common_balance,
wrappedList(
selectedType.isEnteringField() -> combinedReference(
stringReference(
cryptoCurrencyStatus.value.amount.format {
crypto(cryptoCurrency = cryptoCurrencyStatus.currency)
},
),
stringReference(value = " $DOT "),
stringReference(
cryptoCurrencyStatus.value.fiatAmount.format {
fiat(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
)
},
),
).orMaskWithStars(isBalanceHidden)
selectedType.isViewingField() -> resourceReference(R.string.send_with_swap_recipient_amount_text)
selectedType.isViewingField() -> resourceReference(
R.string.send_with_swap_recipient_get_amount,
)
else -> TextReference.Companion.EMPTY
}

View file

@ -154,10 +154,7 @@ private fun SwapAmountDivider(modifier: Modifier = Modifier) {
style = TangemTheme.typography.caption1,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier
.background(TangemTheme.colors.stroke.primary, RoundedCornerShape(32.dp))
.padding(1.dp)
.background(TangemTheme.colors.text.primary2, RoundedCornerShape(32.dp)) // workaround
.background(TangemTheme.colors.icon.informative.copy(alpha = 0.1f), RoundedCornerShape(32.dp))
.background(TangemTheme.colors.button.secondary, RoundedCornerShape(32.dp))
.padding(horizontal = 11.dp, vertical = 5.dp)
.align(Alignment.Center),
)

View file

@ -3,8 +3,6 @@ package com.tangem.features.swap.v2.impl.amount.ui
import android.content.res.Configuration
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@ -26,7 +24,7 @@ import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.ui.AmountFieldV2
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.SpacerH2
import com.tangem.core.ui.components.SpacerWMax
import com.tangem.core.ui.components.TextShimmer
import com.tangem.core.ui.components.atoms.text.EllipsisText
@ -38,7 +36,6 @@ import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.domain.express.models.ExpressRateType
import com.tangem.domain.swap.models.SwapDirection
import com.tangem.features.swap.v2.impl.R
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType
@ -54,13 +51,17 @@ internal fun SwapAmountContent(
clickIntents: SwapAmountClickIntents,
modifier: Modifier = Modifier,
) {
val swapAmountContent = amountUM as? SwapAmountUM.Content
val isFixedRate = swapAmountContent?.swapRateType == ExpressRateType.Fixed
ConstraintLayout(
modifier = modifier,
) {
val (amountFromRef, amountToRef, middleButtonRef) = createRefs()
SwapAmountBlock(
amountUM = amountUM,
amountFieldUM = amountUM.primaryAmount,
selectedAmountType = amountUM.selectedAmountType,
selectedQuote = swapAmountContent?.selectedQuote,
isFixedRate = isFixedRate,
clickIntents = clickIntents,
modifier = Modifier.constrainAs(amountFromRef) {
top.linkTo(parent.top)
@ -69,8 +70,10 @@ internal fun SwapAmountContent(
},
)
SwapAmountBlock(
amountUM = amountUM,
amountFieldUM = amountUM.secondaryAmount,
selectedAmountType = amountUM.selectedAmountType,
selectedQuote = swapAmountContent?.selectedQuote,
isFixedRate = isFixedRate,
clickIntents = clickIntents,
modifier = Modifier.constrainAs(amountToRef) {
top.linkTo(amountFromRef.bottom, 8.dp)
@ -100,7 +103,7 @@ private fun SwapAmountBlockSeparator(onClick: () -> Unit, modifier: Modifier = M
modifier = modifier
.heightIn(max = 28.dp)
.clip(RoundedCornerShape(32.dp))
.background(TangemTheme.colors.background.secondary)
.background(TangemTheme.colors.button.secondary)
.clickable(onClick = onClick)
.padding(vertical = 6.dp, horizontal = 12.dp),
) {
@ -109,10 +112,6 @@ private fun SwapAmountBlockSeparator(onClick: () -> Unit, modifier: Modifier = M
style = TangemTheme.typography.caption1,
color = TangemTheme.colors.text.tertiary,
)
VerticalDivider(
thickness = 1.dp,
color = TangemTheme.colors.icon.inactive,
)
Icon(
painter = rememberVectorPainter(
ImageVector.vectorResource(R.drawable.ic_close_24),
@ -126,11 +125,14 @@ private fun SwapAmountBlockSeparator(onClick: () -> Unit, modifier: Modifier = M
@Composable
private fun SwapAmountBlock(
amountUM: SwapAmountUM,
amountFieldUM: SwapAmountFieldUM,
selectedQuote: SwapQuoteUM?,
selectedAmountType: SwapAmountType,
isFixedRate: Boolean,
clickIntents: SwapAmountClickIntents,
modifier: Modifier = Modifier,
) {
val isSelectedAmountType = selectedAmountType == amountFieldUM.amountType
Column(
modifier = modifier
.padding(horizontal = 16.dp)
@ -138,7 +140,7 @@ private fun SwapAmountBlock(
.fillMaxWidth()
.background(TangemTheme.colors.background.action),
) {
AnimatedVisibility(amountUM.selectedAmountType == amountFieldUM.amountType) {
AnimatedVisibility(isSelectedAmountType) {
Box {
SwapAmountEditBlock(
amountFieldUM = amountFieldUM,
@ -157,8 +159,10 @@ private fun SwapAmountBlock(
}
}
SwapAmountInfo(
amountUM = amountUM,
amountFieldUM = amountFieldUM,
selectedQuote = selectedQuote,
isSelectedAmountType = isSelectedAmountType,
isFixedRate = isFixedRate,
onExpandEditField = clickIntents::onExpandEditField,
onSelectTokenClick = clickIntents::onSelectTokenClick,
onMaxAmountClick = clickIntents::onMaxValueClick,
@ -187,7 +191,7 @@ private fun SwapAmountEditBlock(
} else {
Text(
text = (amountFieldUM.amountField as AmountState.Data).title.resolveReference(),
style = TangemTheme.typography.caption2,
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.tertiary,
)
}
@ -201,10 +205,13 @@ private fun SwapAmountEditBlock(
}
}
@Suppress("LongParameterList")
@Composable
private fun SwapAmountInfo(
amountUM: SwapAmountUM,
amountFieldUM: SwapAmountFieldUM,
selectedQuote: SwapQuoteUM?,
isSelectedAmountType: Boolean,
isFixedRate: Boolean,
onExpandEditField: (SwapAmountType) -> Unit,
onMaxAmountClick: () -> Unit,
onSelectTokenClick: () -> Unit,
@ -220,7 +227,7 @@ private fun SwapAmountInfo(
indication = ripple(),
enabled = (amountFieldUM as? SwapAmountFieldUM.Content)?.isClickEnabled == true,
onClick = {
if ((amountUM as? SwapAmountUM.Content)?.swapRateType == ExpressRateType.Fixed) {
if (isFixedRate) {
onExpandEditField(amountFieldUM.amountType)
} else {
onSelectTokenClick()
@ -237,107 +244,85 @@ private fun SwapAmountInfo(
bottom = 16.dp,
),
)
SwapAmountInfoMain(amountFieldUM = amountFieldUM)
SwapAmountInfoMain(
amountFieldUM = amountFieldUM,
selectedQuote = selectedQuote,
isSelectedAmountType = isSelectedAmountType,
)
SpacerWMax()
AnimatedContent(
amountFieldUM,
) { wrappedFieldAmountUM ->
if (amountUM is SwapAmountUM.Content) {
SwapAmountInfoExtra(
amountUM = amountUM,
amountFieldUM = wrappedFieldAmountUM,
onMaxAmountClick = onMaxAmountClick,
targetState = isSelectedAmountType,
) { isSelected ->
if (isSelected) {
AmountMaxButton(onMaxAmountClick)
} else {
SwapAmountInfoQuote(
quoteUM = selectedQuote,
isFixedRate = isFixedRate,
onSelectTokenClick = onSelectTokenClick,
)
} else {
RectangleShimmer()
}
}
}
}
@Composable
private fun SwapAmountInfoMain(amountFieldUM: SwapAmountFieldUM, modifier: Modifier = Modifier) {
AnimatedContent(
targetState = amountFieldUM is SwapAmountFieldUM.Content,
private fun SwapAmountInfoMain(
amountFieldUM: SwapAmountFieldUM,
selectedQuote: SwapQuoteUM?,
isSelectedAmountType: Boolean,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier,
) { isContent ->
if (isContent && amountFieldUM is SwapAmountFieldUM.Content) {
Column(
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
) {
AnimatedContent(amountFieldUM is SwapAmountFieldUM.Content) { isContent ->
if (isContent && amountFieldUM is SwapAmountFieldUM.Content) {
Text(
text = amountFieldUM.title.resolveReference(),
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.primary1,
maxLines = 1,
)
} else {
TextShimmer(
style = TangemTheme.typography.subtitle2,
modifier = Modifier.width(56.dp),
)
}
}
AnimatedContent(isSelectedAmountType) { isSelected ->
if (isSelected && amountFieldUM is SwapAmountFieldUM.Content) {
SpacerH2()
EllipsisText(
text = amountFieldUM.subtitle.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
ellipsis = amountFieldUM.subtitleEllipsis,
)
}
} else {
Column(
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
TextShimmer(
style = TangemTheme.typography.subtitle2,
modifier = Modifier.width(56.dp),
)
TextShimmer(
style = TangemTheme.typography.caption2,
modifier = Modifier.width(72.dp),
)
}
}
}
}
@Composable
private fun SwapAmountInfoExtra(
amountUM: SwapAmountUM.Content,
amountFieldUM: SwapAmountFieldUM,
onMaxAmountClick: () -> Unit,
onSelectTokenClick: () -> Unit,
) {
when (amountFieldUM.amountType) {
SwapAmountType.From -> when (amountUM.swapDirection) {
SwapDirection.Direct -> {
AnimatedVisibility(
visible = amountUM.selectedAmountType == amountFieldUM.amountType,
enter = fadeIn(),
exit = fadeOut(),
) {
AmountMaxButton(onMaxAmountClick)
}
}
SwapDirection.Reverse -> {
SwapAmountInfoQuote(
quoteUM = amountUM.selectedQuote,
swapRateType = amountUM.swapRateType,
onSelectTokenClick = onSelectTokenClick,
)
}
}
SwapAmountType.To -> when (amountUM.swapDirection) {
SwapDirection.Direct -> {
SwapAmountInfoQuote(
quoteUM = amountUM.selectedQuote,
swapRateType = amountUM.swapRateType,
onSelectTokenClick = onSelectTokenClick,
)
}
SwapDirection.Reverse -> {
AnimatedVisibility(
visible = amountUM.selectedAmountType == amountFieldUM.amountType,
enter = fadeIn(),
exit = fadeOut(),
) {
AmountMaxButton(onMaxAmountClick)
} else {
AnimatedContent(selectedQuote) { quote ->
when (quote) {
is SwapQuoteUM.Content -> {
SpacerH2()
EllipsisText(
text = stringResourceSafe(
R.string.send_with_swap_recipient_get_amount,
quote.quoteAmountValue.resolveReference(),
),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
}
SwapQuoteUM.Loading -> {
SpacerH2()
TextShimmer(
style = TangemTheme.typography.caption2,
modifier = Modifier.width(72.dp),
)
}
else -> Unit
}
}
}
}
@ -353,7 +338,7 @@ private fun AmountMaxButton(onMaxAmountClick: () -> Unit) {
modifier = Modifier
.padding(end = 16.dp)
.clip(RoundedCornerShape(16.dp))
.background(TangemTheme.colors.background.secondary)
.background(TangemTheme.colors.button.secondary)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = ripple(),
@ -364,11 +349,11 @@ private fun AmountMaxButton(onMaxAmountClick: () -> Unit) {
}
@Composable
private fun SwapAmountInfoQuote(quoteUM: SwapQuoteUM, swapRateType: ExpressRateType, onSelectTokenClick: () -> Unit) {
private fun SwapAmountInfoQuote(quoteUM: SwapQuoteUM?, isFixedRate: Boolean, onSelectTokenClick: () -> Unit) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.conditionalCompose(
condition = swapRateType == ExpressRateType.Fixed,
condition = isFixedRate,
modifier = {
clickable(
interactionSource = remember { MutableInteractionSource() },
@ -378,29 +363,20 @@ private fun SwapAmountInfoQuote(quoteUM: SwapQuoteUM, swapRateType: ExpressRateT
},
),
) {
when (quoteUM) {
is SwapQuoteUM.Content -> EllipsisText(
text = quoteUM.quoteAmountValue.resolveReference(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier.padding(end = 2.dp),
)
is SwapQuoteUM.Error,
is SwapQuoteUM.Empty,
-> Box(modifier = Modifier.padding(start = 16.dp))
is SwapQuoteUM.Loading -> CircularProgressIndicator(
color = TangemTheme.colors.icon.inactive,
modifier = Modifier
.padding(end = 4.dp)
.size(20.dp),
)
is SwapQuoteUM.Allowance -> Text(
text = "ALLOWANCE NOT IMPLEMENTED",
)
AnimatedContent(
quoteUM,
) { quote ->
when (quote) {
is SwapQuoteUM.Loading -> CircularProgressIndicator(
color = TangemTheme.colors.icon.inactive,
modifier = Modifier
.padding(end = 4.dp)
.size(20.dp),
)
else -> Box(modifier = Modifier.padding(start = 16.dp))
}
}
Icon(
painter = rememberVectorPainter(
ImageVector.vectorResource(R.drawable.ic_chevron_24),