Updated on 2026-08-14

This commit is contained in:
Tangem 2024-09-13 20:56:48 +05:00
parent 7e3556f0df
commit a8185c2498
17 changed files with 228 additions and 75 deletions

View file

@ -9,15 +9,9 @@ import com.tangem.utils.transformer.Transformer
internal class AmountChangeStateTransformer(
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
private val value: String,
yield: Yield,
private val yield: Yield,
) : Transformer<StakingUiState> {
private val amountRequirementStateTransformer = AmountRequirementStateTransformer(
cryptoCurrencyStatus,
yield,
value,
)
override fun transform(prevState: StakingUiState): StakingUiState {
val updatedAmountState = AmountFieldChangeTransformer(
cryptoCurrencyStatus,
@ -25,7 +19,11 @@ internal class AmountChangeStateTransformer(
).transform(prevState.amountState)
return prevState.copy(
amountState = amountRequirementStateTransformer.transform(updatedAmountState),
amountState = AmountRequirementStateTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatus,
yield = yield,
actionType = prevState.actionType,
).transform(updatedAmountState),
)
}
}

View file

@ -1,7 +1,6 @@
package com.tangem.features.staking.impl.presentation.state.transformers.amount
import com.tangem.common.ui.amountScreen.converters.field.AmountFieldMaxAmountTransformer
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.staking.impl.presentation.state.StakingUiState
@ -9,21 +8,17 @@ import com.tangem.utils.transformer.Transformer
internal class AmountMaxValueStateTransformer(
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
yield: Yield,
private val yield: Yield,
) : Transformer<StakingUiState> {
private val amountRequirementStateTransformer = AmountRequirementStateTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatus,
yield = yield,
value = cryptoCurrencyStatus.value.amount
?.parseBigDecimal(cryptoCurrencyStatus.currency.decimals)
.orEmpty(),
)
override fun transform(prevState: StakingUiState): StakingUiState {
val updatedAmountState = AmountFieldMaxAmountTransformer(cryptoCurrencyStatus).transform(prevState.amountState)
return prevState.copy(
amountState = amountRequirementStateTransformer.transform(updatedAmountState),
amountState = AmountRequirementStateTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatus,
yield = yield,
actionType = prevState.actionType,
).transform(updatedAmountState),
)
}
}

View file

@ -3,60 +3,91 @@ package com.tangem.features.staking.impl.presentation.state.transformers.amount
import androidx.compose.ui.text.input.ImeAction
import com.tangem.common.extensions.isZero
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.isNullOrEmpty
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.core.ui.utils.parseToBigDecimal
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.staking.model.stakekit.AddressArgument
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.staking.impl.R
import com.tangem.lib.crypto.BlockchainUtils.isTron
import com.tangem.utils.transformer.Transformer
import java.math.BigDecimal
import java.math.RoundingMode
internal class AmountRequirementStateTransformer(
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
private val yield: Yield,
private val value: String,
private val actionType: StakingActionCommonType,
) : Transformer<AmountState> {
override fun transform(prevState: AmountState): AmountState {
val amountRequirements = yield.args.enter.args[Yield.Args.ArgType.AMOUNT]
return if (prevState !is AmountState.Data || amountRequirements == null) {
prevState
} else {
updateWithError(prevState, amountRequirements)
}
}
private fun updateWithError(prevState: AmountState.Data, amountRequirements: AddressArgument): AmountState {
val isRequirementError = isRequirementError(prevState, amountRequirements)
return if (isRequirementError) {
prevState.copy(
isPrimaryButtonEnabled = false,
amountTextField = prevState.amountTextField.copy(
isError = true,
error = resourceReference(
R.string.staking_amount_requirement_error,
wrappedList(
BigDecimalFormatter.formatCryptoAmount(
amountRequirements.minimum,
cryptoCurrencyStatus.currency.symbol,
cryptoCurrencyStatus.currency.decimals,
),
),
),
keyboardOptions = prevState.amountTextField.keyboardOptions.copy(
imeAction = ImeAction.None,
),
),
return if (prevState is AmountState.Data && amountRequirements != null) {
updateWithError(
prevState,
actionType,
amountRequirements,
)
} else {
prevState
}
}
private fun updateWithError(
amountState: AmountState.Data,
actionType: StakingActionCommonType,
amountRequirements: AddressArgument,
): AmountState {
val isRequirementError = isRequirementError(amountState, amountRequirements)
val isIntegerOnlyError = isIntegerOnlyError(amountState, actionType)
val cryptoAmount = amountState.amountTextField.cryptoAmount
val roundedDownCrypto = cryptoAmount.value?.setScale(0, RoundingMode.DOWN)
val value = roundedDownCrypto?.parseBigDecimal(0).orEmpty()
val errorText = when {
amountState.amountTextField.isError -> amountState.amountTextField.error
isRequirementError -> resourceReference(
R.string.staking_amount_requirement_error,
wrappedList(
BigDecimalFormatter.formatCryptoAmount(
amountRequirements.minimum,
cryptoCurrencyStatus.currency.symbol,
cryptoCurrencyStatus.currency.decimals,
),
),
)
isIntegerOnlyError -> resourceReference(
R.string.staking_amount_tron_integer_error,
wrappedList(value),
)
else -> TextReference.EMPTY
}
val isError = amountState.amountTextField.isError || isRequirementError
return if (!errorText.isNullOrEmpty()) {
amountState.copy(
isPrimaryButtonEnabled = !isError,
amountTextField = amountState.amountTextField.copy(
isError = isError,
isWarning = isIntegerOnlyError,
error = errorText,
keyboardOptions = amountState.amountTextField.keyboardOptions.copy(
imeAction = ImeAction.None,
),
),
)
} else {
amountState
}
}
private fun isRequirementError(prevState: AmountState.Data, amountRequirements: AddressArgument): Boolean {
val amountDecimal = value.parseToBigDecimal(cryptoCurrencyStatus.currency.decimals)
val amountDecimal = prevState.amountTextField.cryptoAmount.value ?: return false
val isAlreadyErrorState = prevState.amountTextField.isError
val isAmountRequired = amountRequirements.required
@ -67,4 +98,20 @@ internal class AmountRequirementStateTransformer(
return !isAmountZero && isAmountRequired && isExceedsRequirements && !isAlreadyErrorState
}
private fun isIntegerOnlyError(amountState: AmountState.Data, actionType: StakingActionCommonType): Boolean {
val cryptoAmountValue = amountState.amountTextField.cryptoAmount.value ?: return false
val isEnter = actionType == StakingActionCommonType.ENTER
val isTron = isTron(cryptoCurrencyStatus.currency.network.id.value)
val isIntegerOnly = cryptoAmountValue.isZero() || cryptoAmountValue.remainder(BigDecimal.ONE).isZero()
return isEnter && isTron && !isIntegerOnly
}
data class Data(
val amountState: AmountState,
val actionType: StakingActionCommonType,
)
}

View file

@ -0,0 +1,40 @@
package com.tangem.features.staking.impl.presentation.state.transformers.amount
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.staking.impl.presentation.state.StakingUiState
import com.tangem.utils.transformer.Transformer
import java.math.RoundingMode
internal class AmountRoundToIntegerTransformer(
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
) : Transformer<StakingUiState> {
override fun transform(prevState: StakingUiState): StakingUiState {
val amountState = prevState.amountState as? AmountState.Data ?: return prevState
val amountTextField = amountState.amountTextField
if (amountTextField.value.isEmpty()) return prevState
val cryptoAmount = amountState.amountTextField.cryptoAmount
val fiatAmount = amountState.amountTextField.fiatAmount
val fiatDecimals = fiatAmount.decimals
val roundedDownCrypto = cryptoAmount.value?.setScale(0, RoundingMode.DOWN)
val roundedDownFiat = roundedDownCrypto?.multiply(cryptoCurrencyStatus.value.fiatRate)
val value = roundedDownCrypto?.parseBigDecimal(0).orEmpty()
val fiatValue = roundedDownFiat?.parseBigDecimal(fiatDecimals, RoundingMode.HALF_UP).orEmpty()
return prevState.copy(
amountState = amountState.copy(
amountTextField = amountState.amountTextField.copy(
cryptoAmount = cryptoAmount.copy(value = roundedDownCrypto),
fiatAmount = fiatAmount.copy(value = roundedDownFiat),
value = value,
fiatValue = fiatValue,
isWarning = false,
),
),
)
}
}

View file

@ -1,7 +1,10 @@
package com.tangem.features.staking.impl.presentation.state.utils
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.lib.crypto.BlockchainUtils.isTron
import java.math.BigDecimal
import java.math.MathContext
import java.math.RoundingMode
/**
* Check and calculates subtracted amount
@ -14,15 +17,21 @@ internal fun checkAndCalculateSubtractedAmount(
reduceAmountBy: BigDecimal,
): BigDecimal {
val balance = cryptoCurrencyStatus.value.amount ?: return amountValue
val isTron = isTron(cryptoCurrencyStatus.currency.network.id.value)
val feeValueRounded = if (isTron) {
feeValue.round(MathContext(0, RoundingMode.UP))
} else {
feeValue
}
val isFeeCoverage = checkFeeCoverage(
isSubtractAvailable = isAmountSubtractAvailable,
balance = balance,
amountValue = amountValue,
feeValue = feeValue,
feeValue = feeValueRounded,
reduceAmountBy = reduceAmountBy,
)
return if (isFeeCoverage) {
balance.minus(reduceAmountBy).minus(feeValue)
balance.minus(reduceAmountBy).minus(feeValueRounded)
} else {
amountValue
}

View file

@ -197,7 +197,17 @@ internal class StakingViewModel @Inject constructor(
stateController.update(SetConfirmationStateLoadingTransformer(yield, appCurrency))
onRefreshSwipe(isRefreshing = false)
}
isAssentState() -> getFee(pendingAction)
isAssentState() -> {
getFee(pendingAction)
val amountState = value.amountState as? AmountState.Data
if (amountState?.amountTextField?.isWarning == true) {
stateController.update(
AmountRoundToIntegerTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatus,
),
)
}
}
}
}