Updated on 2026-08-14

This commit is contained in:
Tangem 2026-05-08 16:11:17 +02:00
parent d895fe63b7
commit b01ede005a
8 changed files with 168 additions and 54 deletions

View file

@ -87,6 +87,7 @@ sealed class SwapAmountFieldUM {
val subtitleEllipsisRight: TextEllipsis,
val isClickEnabled: Boolean,
val shouldShowApproximatePrefix: Boolean,
val sendSubtitle: SendSubtitleUM? = null,
) : SwapAmountFieldUM()
}
@ -119,4 +120,11 @@ data class PriceImpact(
type = Type.NONE,
)
}
}
}
@Immutable
data class SendSubtitleUM(
val label: TextReference,
val value: TextReference,
val valueEllipsis: TextEllipsis,
)

View file

@ -60,6 +60,7 @@ internal class SwapAmountFieldConverter(
subtitleEllipsisLeft = subtitles.subtitleEllipsisLeft,
subtitleRight = subtitles.subtitleRight,
subtitleEllipsisRight = subtitles.subtitleEllipsisRight,
sendSubtitle = subtitles.sendSubtitle,
isClickEnabled = true,
shouldShowApproximatePrefix = showApproximatePrefix,
amountField = AmountStateConverter(

View file

@ -58,6 +58,7 @@ internal class SwapAmountUpdateSubtitleConverter(
subtitleEllipsisLeft = subtitles.subtitleEllipsisLeft,
subtitleRight = subtitles.subtitleRight,
subtitleEllipsisRight = subtitles.subtitleEllipsisRight,
sendSubtitle = subtitles.sendSubtitle,
)
}
}

View file

@ -6,17 +6,17 @@ import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.features.swap.v2.impl.R
import com.tangem.utils.StringsSigns.DOT
import com.tangem.features.swap.v2.impl.amount.entity.SendSubtitleUM
import java.math.BigDecimal
/**
* Computes all subtitle fields for the **From** (primary) field.
*
* | State | subtitleLeft | subtitleRight | ellipsisLeft |
* |--------------------------------|-----------------------------------|-----------------------------------|------------------------|
* | Entering (float), any | "Balance: " (empty param) | "{balance}" masked | OffsetEnd(symbol) |
* | Viewing (fixed), empty | "Balance: " (empty param) | "{balance}" masked (crypto only) | End |
* | Viewing (fixed), not empty | "{balance}" masked | "• Send {displayStr}" masked | OffsetEnd(symbol) |
* | State | subtitleLeft | subtitleRight | sendSubtitle |
* |--------------------------------|-----------------------------------|---------------|-------------------------|
* | Entering (float), any | "Balance: {balance}" masked | EMPTY | null |
* | Viewing (fixed), empty | "Balance: {balance}" masked | EMPTY | null |
* | Viewing (fixed), not empty | "Balance: {balance}" masked | EMPTY | SendSubtitleUM(...) |
*/
internal object SwapFromSubtitleConverter {
@ -35,40 +35,27 @@ internal object SwapFromSubtitleConverter {
crypto(cryptoCurrency = cryptoCurrencyStatus.currency)
} ?: balance
val subtitleLeft: TextReference
val subtitleRight: TextReference
val ellipsisLeft: TextEllipsis
val subtitleLeft = combinedReference(
resourceReference(R.string.common_balance, wrappedList("")),
stringReference(balance).orMaskWithStars(isBalanceHidden),
)
when {
isEntering -> {
subtitleLeft = resourceReference(R.string.common_balance, wrappedList(""))
subtitleRight = combinedReference(stringReference(balance))
.orMaskWithStars(isBalanceHidden)
ellipsisLeft = TextEllipsis.OffsetEnd(symbol.length)
}
!isEntering && isAmountEmpty -> {
subtitleLeft = resourceReference(R.string.common_balance, wrappedList(""))
subtitleRight = combinedReference(stringReference(balance))
.orMaskWithStars(isBalanceHidden)
ellipsisLeft = TextEllipsis.End
}
else -> {
subtitleLeft = stringReference(balance)
.orMaskWithStars(isBalanceHidden)
subtitleRight = combinedReference(
stringReference("$DOT "),
resourceReference(R.string.common_send),
stringReference(" $displayStr"),
).orMaskWithStars(isBalanceHidden)
ellipsisLeft = TextEllipsis.OffsetEnd(symbol.length)
}
val sendSubtitle = if (!isEntering && !isAmountEmpty) {
SendSubtitleUM(
label = resourceReference(R.string.common_send_colon),
value = stringReference(displayStr).orMaskWithStars(isBalanceHidden),
valueEllipsis = TextEllipsis.OffsetEnd(symbol.length),
)
} else {
null
}
return SwapSubtitleResult(
subtitleLeft = subtitleLeft,
subtitleRight = subtitleRight,
subtitleEllipsisLeft = ellipsisLeft,
subtitleRight = TextReference.EMPTY,
subtitleEllipsisLeft = TextEllipsis.OffsetEnd(symbol.length),
subtitleEllipsisRight = TextEllipsis.OffsetEnd(symbol.length),
sendSubtitle = sendSubtitle,
)
}
}

View file

@ -2,10 +2,12 @@ package com.tangem.features.swap.v2.impl.amount.model.converter
import com.tangem.core.ui.components.atoms.text.TextEllipsis
import com.tangem.core.ui.extensions.TextReference
import com.tangem.features.swap.v2.impl.amount.entity.SendSubtitleUM
internal data class SwapSubtitleResult(
val subtitleLeft: TextReference,
val subtitleRight: TextReference,
val subtitleEllipsisLeft: TextEllipsis,
val subtitleEllipsisRight: TextEllipsis,
val sendSubtitle: SendSubtitleUM? = null,
)

View file

@ -365,23 +365,45 @@ private fun SwapAmountInfoMain(
private fun SwapAmountSubtitle(amountFieldUM: SwapAmountFieldUM) {
SpacerH2()
if (amountFieldUM is SwapAmountFieldUM.Content) {
SpacerH2()
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
Column(
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
EllipsisText(
text = amountFieldUM.subtitleLeft.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
ellipsis = amountFieldUM.subtitleEllipsisLeft,
modifier = Modifier.weight(1f, fill = false),
)
EllipsisText(
text = amountFieldUM.subtitleRight.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
ellipsis = amountFieldUM.subtitleEllipsisRight,
)
SpacerH2()
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
EllipsisText(
text = amountFieldUM.subtitleLeft.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
ellipsis = amountFieldUM.subtitleEllipsisLeft,
modifier = Modifier.weight(1f, fill = false),
)
EllipsisText(
text = amountFieldUM.subtitleRight.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
ellipsis = amountFieldUM.subtitleEllipsisRight,
)
}
if (amountFieldUM.sendSubtitle != null) {
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
text = amountFieldUM.sendSubtitle.label.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
)
EllipsisText(
text = amountFieldUM.sendSubtitle.value.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.primary1,
ellipsis = amountFieldUM.sendSubtitle.valueEllipsis,
modifier = Modifier.weight(1f, fill = false),
)
}
}
}
} else {
SpacerH2()

View file

@ -16,10 +16,10 @@ import com.tangem.domain.swap.models.SwapAmountType
import com.tangem.domain.swap.models.SwapCurrencies
import com.tangem.domain.swap.models.SwapDirection
import com.tangem.domain.swap.models.SwapRateMode
import com.tangem.features.swap.v2.impl.amount.entity.SendSubtitleUM
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
import com.tangem.utils.StringsSigns
import kotlinx.collections.immutable.persistentListOf
import java.math.BigDecimal
@ -103,12 +103,17 @@ internal data object SwapAmountContentPreview {
amountType = SwapAmountType.From,
amountField = AmountStatePreviewData.amountState,
title = stringReference("Tether"),
subtitleLeft = stringReference("11 101,123123456 BTC"),
subtitleRight = stringReference(" ${StringsSigns.DOT} 1 212,12 $"),
subtitleLeft = stringReference("Balance: 11 101,123123456 BTC"),
subtitleRight = TextReference.EMPTY,
isClickEnabled = false,
subtitleEllipsisLeft = TextEllipsis.OffsetEnd(3),
subtitleEllipsisRight = TextEllipsis.OffsetEnd(1),
shouldShowApproximatePrefix = false,
sendSubtitle = SendSubtitleUM(
label = stringReference("Send:"),
value = stringReference("1 212,12 BTC"),
valueEllipsis = TextEllipsis.OffsetEnd(3),
),
),
secondaryAmount = SwapAmountFieldUM.Content(
amountType = SwapAmountType.To,

View file

@ -0,0 +1,88 @@
package com.tangem.features.swap.v2.impl.amount.model.converter
import com.google.common.truth.Truth.assertThat
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import io.mockk.every
import io.mockk.mockk
import org.junit.Test
import java.math.BigDecimal
internal class SwapFromSubtitleConverterTest {
@Test
fun `GIVEN isEntering true WHEN convert THEN subtitleRight is EMPTY and sendSubtitle is null`() {
val cryptoCurrencyStatus = mockk<CryptoCurrencyStatus>(relaxed = true)
every { cryptoCurrencyStatus.currency.symbol } returns "ETH"
every { cryptoCurrencyStatus.currency.decimals } returns 8
every { cryptoCurrencyStatus.value.amount } returns BigDecimal("1.5")
val result = SwapFromSubtitleConverter.convert(
cryptoCurrencyStatus = cryptoCurrencyStatus,
isBalanceHidden = false,
isEntering = true,
isAmountEmpty = false,
displayAmount = BigDecimal("0.5"),
)
assertThat(result.subtitleRight).isEqualTo(TextReference.EMPTY)
assertThat(result.sendSubtitle).isNull()
}
@Test
fun `GIVEN isEntering false and isAmountEmpty true WHEN convert THEN subtitleRight is EMPTY and sendSubtitle is null`() {
val cryptoCurrencyStatus = mockk<CryptoCurrencyStatus>(relaxed = true)
every { cryptoCurrencyStatus.currency.symbol } returns "ETH"
every { cryptoCurrencyStatus.currency.decimals } returns 8
every { cryptoCurrencyStatus.value.amount } returns BigDecimal("1.5")
val result = SwapFromSubtitleConverter.convert(
cryptoCurrencyStatus = cryptoCurrencyStatus,
isBalanceHidden = false,
isEntering = false,
isAmountEmpty = true,
displayAmount = null,
)
assertThat(result.subtitleRight).isEqualTo(TextReference.EMPTY)
assertThat(result.sendSubtitle).isNull()
}
@Test
fun `GIVEN isEntering false and isAmountEmpty false WHEN convert THEN subtitleRight is EMPTY and sendSubtitle is not null`() {
val cryptoCurrencyStatus = mockk<CryptoCurrencyStatus>(relaxed = true)
every { cryptoCurrencyStatus.currency.symbol } returns "ETH"
every { cryptoCurrencyStatus.currency.decimals } returns 8
every { cryptoCurrencyStatus.value.amount } returns BigDecimal("1.5")
val result = SwapFromSubtitleConverter.convert(
cryptoCurrencyStatus = cryptoCurrencyStatus,
isBalanceHidden = false,
isEntering = false,
isAmountEmpty = false,
displayAmount = BigDecimal("0.5"),
)
assertThat(result.subtitleRight).isEqualTo(TextReference.EMPTY)
assertThat(result.sendSubtitle).isNotNull()
}
@Test
fun `GIVEN isBalanceHidden true and has amount WHEN convert THEN sendSubtitle is not null`() {
val cryptoCurrencyStatus = mockk<CryptoCurrencyStatus>(relaxed = true)
every { cryptoCurrencyStatus.currency.symbol } returns "ETH"
every { cryptoCurrencyStatus.currency.decimals } returns 8
every { cryptoCurrencyStatus.value.amount } returns BigDecimal("1.5")
val result = SwapFromSubtitleConverter.convert(
cryptoCurrencyStatus = cryptoCurrencyStatus,
isBalanceHidden = true,
isEntering = false,
isAmountEmpty = false,
displayAmount = BigDecimal("0.5"),
)
assertThat(result.subtitleRight).isEqualTo(TextReference.EMPTY)
assertThat(result.sendSubtitle).isNotNull()
}
}