Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-08 12:30:19 +04:00
parent a324c914e5
commit 94579f4af8
5 changed files with 138 additions and 48 deletions

View file

@ -3,18 +3,13 @@ package com.tangem.common.ui.amountScreen.converters.field
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import com.tangem.common.ui.R
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
import com.tangem.common.ui.amountScreen.utils.checkExceedBalance
import com.tangem.common.ui.amountScreen.utils.getAmountValidationError
import com.tangem.common.ui.amountScreen.utils.getCryptoValue
import com.tangem.common.ui.amountScreen.utils.getFiatValue
import com.tangem.common.ui.amountScreen.utils.getKeyboardAction
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.utils.parseToBigDecimal
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.utils.isNullOrZero
@ -72,20 +67,12 @@ class AmountFieldChangeTransformer(
value = cryptoValue,
fiatValue = fiatValue,
isError = isCheckFailed,
error = when {
isExceedBalance -> resourceReference(R.string.common_insufficient_balance)
isLessThanMinimumIfProvided -> {
val minimumAmount = minimumTransactionAmount.amount.format {
crypto(cryptoCurrencyStatus.currency)
}
resourceReference(
R.string.transfer_notification_invalid_minimum_transaction_amount_text,
wrappedList(minimumAmount, minimumAmount),
)
}
else -> TextReference.EMPTY
},
error = getAmountValidationError(
isExceedBalance = isExceedBalance,
isLessThanMinimum = isLessThanMinimumIfProvided,
minimumTransactionAmount = minimumTransactionAmount,
cryptoCurrency = cryptoCurrencyStatus.currency,
),
cryptoAmount = amountTextField.cryptoAmount.copy(value = decimalCryptoValue),
fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue),
keyboardOptions = KeyboardOptions(

View file

@ -0,0 +1,92 @@
package com.tangem.common.ui.amountScreen.converters.field
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
import com.tangem.common.ui.amountScreen.utils.getAmountValidationError
import com.tangem.common.ui.amountScreen.utils.getKeyboardAction
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.utils.isNullOrZero
import com.tangem.utils.transformer.Transformer
import java.math.BigDecimal
import java.math.RoundingMode
/**
* @property cryptoAmount the crypto amount to set, or `null` to clear the field
* @property maxEnterAmount maximum allowed amount boundary (for balance validation)
* @property minimumTransactionAmount minimum allowed amount boundary (optional)
*/
class AmountFieldQuoteTransformer(
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
private val cryptoAmount: BigDecimal?,
private val maxEnterAmount: EnterAmountBoundary,
private val minimumTransactionAmount: EnterAmountBoundary?,
) : Transformer<AmountState> {
override fun transform(prevState: AmountState): AmountState {
if (prevState !is AmountState.Data) return prevState
if (cryptoAmount == null) return prevState.emptyState(maxEnterAmount.fiatRate)
val textField = prevState.amountTextField
val cryptoDecimals = textField.cryptoAmount.decimals
val fiatDecimals = textField.fiatAmount.decimals
val fiatRate = maxEnterAmount.fiatRate
val cryptoString = cryptoAmount.parseBigDecimal(cryptoDecimals)
val fiatValue = fiatRate?.let { cryptoAmount.multiply(it).setScale(fiatDecimals, RoundingMode.HALF_UP) }
val fiatString = fiatValue?.parseBigDecimal(fiatDecimals).orEmpty()
val isExceedBalance = if (textField.isFiatValue) {
val maxFiat = maxEnterAmount.fiatAmount
maxFiat != null && fiatValue != null && fiatValue > maxFiat
} else {
val maxCrypto = maxEnterAmount.amount
maxCrypto != null && cryptoAmount > maxCrypto
}
val isLessThanMinimum = minimumTransactionAmount?.amount?.let { cryptoAmount < it } == true
val isZero = cryptoAmount.isNullOrZero()
val isCheckFailed = isExceedBalance || isLessThanMinimum
return prevState.copy(
isPrimaryButtonEnabled = !isZero && !isCheckFailed,
reduceAmountBy = BigDecimal.ZERO,
amountTextField = textField.copy(
value = cryptoString,
fiatValue = fiatString,
cryptoAmount = textField.cryptoAmount.copy(value = cryptoAmount),
fiatAmount = textField.fiatAmount.copy(value = fiatValue),
isError = isCheckFailed,
error = getAmountValidationError(
isExceedBalance = isExceedBalance,
isLessThanMinimum = isLessThanMinimum,
minimumTransactionAmount = minimumTransactionAmount,
cryptoCurrency = cryptoCurrencyStatus.currency,
),
keyboardOptions = KeyboardOptions(
imeAction = getKeyboardAction(isCheckFailed, cryptoAmount),
keyboardType = KeyboardType.Number,
),
),
)
}
private fun AmountState.Data.emptyState(fiatRate: BigDecimal?): AmountState.Data {
return copy(
isPrimaryButtonEnabled = false,
reduceAmountBy = BigDecimal.ZERO,
amountTextField = amountTextField.copy(
value = "",
fiatValue = "",
cryptoAmount = amountTextField.cryptoAmount.copy(value = BigDecimal.ZERO),
fiatAmount = amountTextField.fiatAmount.copy(value = if (fiatRate != null) BigDecimal.ZERO else null),
isError = false,
keyboardOptions = KeyboardOptions(
imeAction = ImeAction.None,
keyboardType = KeyboardType.Number,
),
),
)
}
}

View file

@ -2,15 +2,10 @@ package com.tangem.common.ui.amountScreen.converters.field
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.text.input.KeyboardType
import com.tangem.common.ui.R
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
import com.tangem.common.ui.amountScreen.utils.getAmountValidationError
import com.tangem.common.ui.amountScreen.utils.getKeyboardAction
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.utils.extensions.isZero
@ -52,16 +47,12 @@ class AmountFieldSetMaxAmountTransformer(
value = cryptoValue,
fiatValue = fiatValue,
isError = isLessThanMinimumIfProvided,
error = when {
isLessThanMinimumIfProvided -> {
val minimumAmount = minAmount.amount.format { crypto(cryptoCurrencyStatus.currency) }
resourceReference(
R.string.transfer_notification_invalid_minimum_transaction_amount_text,
wrappedList(minimumAmount, minimumAmount),
)
}
else -> TextReference.EMPTY
},
error = getAmountValidationError(
isExceedBalance = false,
isLessThanMinimum = isLessThanMinimumIfProvided,
minimumTransactionAmount = minAmount,
cryptoCurrency = cryptoCurrencyStatus.currency,
),
cryptoAmount = amountTextField.cryptoAmount.copy(value = decimalCryptoValue),
fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue),
keyboardOptions = KeyboardOptions(

View file

@ -1,10 +1,17 @@
package com.tangem.common.ui.amountScreen.utils
import androidx.compose.ui.text.input.ImeAction
import com.tangem.common.ui.R
import com.tangem.common.ui.amountScreen.models.AmountFieldModel
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.core.ui.utils.parseToBigDecimal
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.utils.extensions.isZero
import java.math.BigDecimal
import java.math.RoundingMode
@ -56,4 +63,21 @@ internal fun getKeyboardAction(isCheckFailed: Boolean, decimalCryptoValue: BigDe
ImeAction.Done
} else {
ImeAction.None
}
}
internal fun getAmountValidationError(
isExceedBalance: Boolean,
isLessThanMinimum: Boolean,
minimumTransactionAmount: EnterAmountBoundary?,
cryptoCurrency: CryptoCurrency,
): TextReference = when {
isExceedBalance -> resourceReference(R.string.common_insufficient_balance)
isLessThanMinimum -> {
val minimumAmount = minimumTransactionAmount?.amount.format { crypto(cryptoCurrency) }
resourceReference(
R.string.transfer_notification_invalid_minimum_transaction_amount_text,
wrappedList(minimumAmount, minimumAmount),
)
}
else -> TextReference.EMPTY
}

View file

@ -1,11 +1,10 @@
package com.tangem.features.swap.v2.impl.amount.model.transformers
import com.tangem.common.ui.amountScreen.converters.field.AmountFieldChangeTransformer
import com.tangem.common.ui.amountScreen.converters.field.AmountFieldQuoteTransformer
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.swap.models.SwapAmountType
import com.tangem.features.swap.v2.impl.R
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM
@ -93,13 +92,11 @@ internal class SwapAmountSelectQuoteTransformer(
isAmountEmpty = false,
displayAmount = fromAmount,
).copy(
amountField = AmountFieldChangeTransformer(
amountField = AmountFieldQuoteTransformer(
cryptoCurrencyStatus = prevState.primaryCryptoCurrencyStatus,
cryptoAmount = fromAmount,
maxEnterAmount = primaryMaximumAmountBoundary,
minimumTransactionAmount = primaryMinimumAmountBoundary,
value = fromAmount.parseBigDecimal(
prevState.primaryCryptoCurrencyStatus.currency.decimals,
),
).transform(fromField.amountField),
)
} ?: prevState.primaryAmount
@ -123,11 +120,11 @@ internal class SwapAmountSelectQuoteTransformer(
cryptoCurrencyStatus = prevState.primaryCryptoCurrencyStatus,
isAmountEmpty = true,
).copy(
amountField = AmountFieldChangeTransformer(
amountField = AmountFieldQuoteTransformer(
cryptoCurrencyStatus = prevState.primaryCryptoCurrencyStatus,
cryptoAmount = null,
maxEnterAmount = primaryMaximumAmountBoundary,
minimumTransactionAmount = primaryMinimumAmountBoundary,
value = "",
).transform(primarySwapAmountField.amountField),
)
}
@ -163,12 +160,11 @@ internal class SwapAmountSelectQuoteTransformer(
val transformedSecondaryAmountField = if (isSecondarySelected && toAmount == null) {
secondarySwapAmountField.amountField
} else {
AmountFieldChangeTransformer(
AmountFieldQuoteTransformer(
cryptoCurrencyStatus = prevState.secondaryCryptoCurrencyStatus,
cryptoAmount = toAmount,
maxEnterAmount = secondaryMaximumAmountBoundary,
minimumTransactionAmount = secondaryMinimumAmountBoundary,
value = toAmount?.parseBigDecimal(prevState.secondaryCryptoCurrencyStatus.currency.decimals)
.orEmpty(),
).transform(secondarySwapAmountField.amountField)
}
val insufficientFundsError = if (isSecondarySelected && fromAmount != null) {