Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-01 18:11:11 +05:00
parent bdbe12ecf0
commit d83a31a9b0
17 changed files with 159 additions and 92 deletions

View file

@ -178,7 +178,7 @@ internal class SendAmountModel @Inject constructor(
private fun initialState() {
if (uiState.value is AmountState.Empty && userWallet != null) {
val isSingleWallet = getWalletsUseCase.invokeSync().size == 1
val isOnlyOneWallet = getWalletsUseCase.invokeSync().size == 1
_uiState.update {
AmountStateConverterV2(
clickIntents = this,
@ -190,7 +190,7 @@ internal class SendAmountModel @Inject constructor(
isBalanceHidden = params.isBalanceHidingFlow.value,
).convert(
AmountParameters(
title = if (isSingleWallet) {
title = if (isOnlyOneWallet) {
resourceReference(R.string.send_from_title)
} else {
resourceReference(

View file

@ -307,7 +307,8 @@ class SendConfirmNotificationsTransformersComparisonTest {
isRedesignEnabled = false,
title = mockk(relaxed = true),
availableBalance = mockk(relaxed = true),
availableBalanceShort = mockk(relaxed = true),
availableBalanceCrypto = mockk(relaxed = true),
availableBalanceFiat = mockk(relaxed = true),
tokenName = mockk(relaxed = true),
tokenIconState = mockk(relaxed = true),
segmentedButtonConfig = persistentListOf(),

View file

@ -3,26 +3,26 @@ package com.tangem.features.send.v2.send.confirm.model.transformers
import com.google.common.truth.Truth.assertThat
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.models.AmountFieldModel
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
import com.tangem.features.send.v2.api.entity.CustomFeeFieldUM
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import java.math.BigDecimal
import java.math.BigInteger
import java.util.Locale
import org.junit.jupiter.api.BeforeAll
class SendConfirmationNotificationsTransformerTest {
@ -205,7 +205,8 @@ class SendConfirmationNotificationsTransformerTest {
isRedesignEnabled = false,
title = mockk(relaxed = true),
availableBalance = mockk(relaxed = true),
availableBalanceShort = mockk(relaxed = true),
availableBalanceCrypto = mockk(relaxed = true),
availableBalanceFiat = mockk(relaxed = true),
tokenName = mockk(relaxed = true),
tokenIconState = mockk(relaxed = true),
segmentedButtonConfig = persistentListOf(),

View file

@ -204,7 +204,8 @@ class SendConfirmationNotificationsTransformerV2Test {
isRedesignEnabled = false,
title = mockk(relaxed = true),
availableBalance = mockk(relaxed = true),
availableBalanceShort = mockk(relaxed = true),
availableBalanceCrypto = mockk(relaxed = true),
availableBalanceFiat = mockk(relaxed = true),
tokenName = mockk(relaxed = true),
tokenIconState = mockk(relaxed = true),
segmentedButtonConfig = persistentListOf(),

View file

@ -84,8 +84,10 @@ sealed class SwapAmountFieldUM {
override val amountField: AmountState,
val priceImpact: TextReference?,
val title: TextReference,
val subtitle: TextReference,
val subtitleEllipsis: TextEllipsis,
val subtitleLeft: TextReference,
val subtitleRight: TextReference,
val subtitleEllipsisLeft: TextEllipsis,
val subtitleEllipsisRight: TextEllipsis,
val isClickEnabled: Boolean,
) : SwapAmountFieldUM()
}

View file

@ -331,11 +331,11 @@ internal class SwapAmountModel @Inject constructor(
private fun subscribeOnBalanceHiddenUpdates() {
params.isBalanceHidingFlow.onEach { isHidden ->
val isSingleWallet = getWalletsUseCase.invokeSync().size == 1
val isOnlyOneWallet = getWalletsUseCase.invokeSync().size == 1
uiState.transformerUpdate(
SwapAmountBalanceHiddenTransformer(
isBalanceHidden = isHidden,
isSingleWallet = isSingleWallet,
isSingleWallet = isOnlyOneWallet,
userWallet = userWallet,
appCurrency = appCurrency,
swapDirection = swapDirection,
@ -351,7 +351,7 @@ internal class SwapAmountModel @Inject constructor(
val primaryCryptoCurrencyStatus = (uiState.value as? SwapAmountUM.Content)?.primaryCryptoCurrencyStatus
if (primaryCryptoCurrencyStatus != null) {
val isSingleWallet = getWalletsUseCase.invokeSync().size == 1
val isOnlyOneWallet = getWalletsUseCase.invokeSync().size == 1
uiState.transformerUpdate(
SwapAmountPrimaryReadyStateTransformer(
userWallet = userWallet,
@ -361,7 +361,7 @@ internal class SwapAmountModel @Inject constructor(
clickIntents = this,
isBalanceHidden = params.isBalanceHidingFlow.value,
showBestRateAnimation = showBestRateAnimation,
isSingleWallet = isSingleWallet,
isSingleWallet = isOnlyOneWallet,
),
)
}
@ -387,7 +387,7 @@ internal class SwapAmountModel @Inject constructor(
),
)
} else {
val isSingleWallet = getWalletsUseCase.invokeSync().size == 1
val isOnlyOneWallet = getWalletsUseCase.invokeSync().size == 1
uiState.transformerUpdate(
SwapAmountPrimaryReadyStateTransformer(
userWallet = userWallet,
@ -397,7 +397,7 @@ internal class SwapAmountModel @Inject constructor(
clickIntents = this,
isBalanceHidden = params.isBalanceHidingFlow.value,
showBestRateAnimation = showBestRateAnimation,
isSingleWallet = isSingleWallet,
isSingleWallet = isOnlyOneWallet,
),
)
}
@ -495,7 +495,7 @@ internal class SwapAmountModel @Inject constructor(
val primaryStatus = (uiState.value as? SwapAmountUM.Content)?.primaryCryptoCurrencyStatus
if (secondaryStatus != null && primaryStatus != null) {
initCurrencies(primaryStatus, secondaryStatus)
val isSingleWallet = getWalletsUseCase.invokeSync().size == 1
val isOnlyOneWallet = getWalletsUseCase.invokeSync().size == 1
uiState.transformerUpdate(
SwapAmountSecondaryReadyStateTransformer(
userWallet = userWallet,
@ -507,7 +507,7 @@ internal class SwapAmountModel @Inject constructor(
clickIntents = this@SwapAmountModel,
isBalanceHidden = params.isBalanceHidingFlow.value,
showBestRateAnimation = showBestRateAnimation,
isSingleWallet = isSingleWallet,
isSingleWallet = isOnlyOneWallet,
),
)
startLoadingQuotesTask(isSilentReload = false)

View file

@ -35,11 +35,13 @@ internal class SwapAmountFieldConverter(
return SwapAmountFieldUM.Content(
amountType = selectedType,
title = stringReference(cryptoCurrencyStatus.currency.name),
subtitle = getSubtitle(selectedType = selectedType, cryptoCurrencyStatus = cryptoCurrencyStatus),
subtitleEllipsis = getSubtitleEllipsis(
subtitleLeft = getSubtitleLeft(selectedType = selectedType, cryptoCurrencyStatus = cryptoCurrencyStatus),
subtitleEllipsisLeft = getSubtitleEllipsisLeft(
selectedType = selectedType,
cryptoCurrencyStatus = cryptoCurrencyStatus,
),
subtitleRight = getSubtitleRight(selectedType = selectedType, cryptoCurrencyStatus = cryptoCurrencyStatus),
subtitleEllipsisRight = TextEllipsis.OffsetEnd(appCurrency.symbol.length),
priceImpact = null,
isClickEnabled = selectedType.isViewingField(),
amountField = AmountStateConverterV2(
@ -67,36 +69,44 @@ internal class SwapAmountFieldConverter(
)
}
private fun getSubtitle(selectedType: SwapAmountType, cryptoCurrencyStatus: CryptoCurrencyStatus) = when {
private fun getSubtitleLeft(selectedType: SwapAmountType, cryptoCurrencyStatus: CryptoCurrencyStatus) = when {
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_get_amount,
)
selectedType.isViewingField() -> resourceReference(R.string.send_with_swap_recipient_get_amount)
else -> TextReference.Companion.EMPTY
}
private fun getSubtitleEllipsis(selectedType: SwapAmountType, cryptoCurrencyStatus: CryptoCurrencyStatus) = when {
selectedType.isEnteringField() -> TextEllipsis.OffsetEnd(cryptoCurrencyStatus.currency.symbol.length)
selectedType.isViewingField() -> TextEllipsis.End
else -> TextEllipsis.End
private fun getSubtitleRight(selectedType: SwapAmountType, cryptoCurrencyStatus: CryptoCurrencyStatus) = when {
selectedType.isEnteringField() -> if (isBalanceHidden) {
TextReference.EMPTY
} else {
combinedReference(
stringReference(value = " $DOT "),
stringReference(
cryptoCurrencyStatus.value.fiatAmount.format {
fiat(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
)
},
),
)
}
else -> TextReference.EMPTY
}
private fun getSubtitleEllipsisLeft(selectedType: SwapAmountType, cryptoCurrencyStatus: CryptoCurrencyStatus) =
when {
selectedType.isEnteringField() -> TextEllipsis.OffsetEnd(cryptoCurrencyStatus.currency.symbol.length)
selectedType.isViewingField() -> TextEllipsis.End
else -> TextEllipsis.End
}
private fun SwapAmountType.isEnteringField(): Boolean {
return this == SwapAmountType.From && swapDirection == SwapDirection.Direct ||
this == SwapAmountType.To && swapDirection == SwapDirection.Reverse

View file

@ -128,7 +128,7 @@ private fun ConstraintLayoutScope.SwapAmountBlock(
amountState = (amountUM.secondaryAmount.amountField as? AmountState.Data)?.copy(
title = resourceReference(R.string.send_with_swap_recipient_amount_title),
availableBalance = TextReference.EMPTY,
availableBalanceShort = TextReference.EMPTY,
availableBalanceCrypto = TextReference.EMPTY,
) ?: amountUM.secondaryAmount.amountField,
isClickDisabled = true,
isEditingDisabled = false,

View file

@ -25,7 +25,6 @@ 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.SpacerH2
import com.tangem.core.ui.components.SpacerWMax
import com.tangem.core.ui.components.TextShimmer
import com.tangem.core.ui.components.atoms.text.EllipsisText
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
@ -248,8 +247,8 @@ private fun SwapAmountInfo(
amountFieldUM = amountFieldUM,
selectedQuote = selectedQuote,
isSelectedAmountType = isSelectedAmountType,
modifier = Modifier.weight(1f),
)
SpacerWMax()
AnimatedContent(
targetState = isSelectedAmountType,
) { isSelected ->
@ -294,12 +293,21 @@ private fun SwapAmountInfoMain(
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,
)
Row {
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,
)
}
} else {
AnimatedContent(selectedQuote) { quote ->
when (quote) {

View file

@ -17,6 +17,7 @@ import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType
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
@ -100,10 +101,12 @@ internal data object SwapAmountContentPreview {
availableBalance = stringReference("Balance: 100 BTC"),
),
title = stringReference("Tether"),
subtitle = stringReference("Balance: 100 BTC"),
subtitleLeft = stringReference("11 101,123123456 BTC"),
subtitleRight = stringReference(" ${StringsSigns.DOT} 1 212,12 $"),
priceImpact = null,
isClickEnabled = false,
subtitleEllipsis = TextEllipsis.OffsetEnd(3),
subtitleEllipsisLeft = TextEllipsis.OffsetEnd(3),
subtitleEllipsisRight = TextEllipsis.OffsetEnd(1),
),
secondaryAmount = SwapAmountFieldUM.Content(
amountType = SwapAmountType.To,
@ -113,9 +116,11 @@ internal data object SwapAmountContentPreview {
),
title = stringReference("Shiba Inu"),
priceImpact = stringReference("(-10%)"),
subtitle = TextReference.EMPTY,
subtitleLeft = TextReference.EMPTY,
subtitleRight = TextReference.EMPTY,
isClickEnabled = false,
subtitleEllipsis = TextEllipsis.OffsetEnd(3),
subtitleEllipsisLeft = TextEllipsis.End,
subtitleEllipsisRight = TextEllipsis.End,
),
appCurrency = AppCurrency.Default,
swapDirection = SwapDirection.Direct,