Updated on 2026-08-14
This commit is contained in:
parent
7e3556f0df
commit
a8185c2498
17 changed files with 228 additions and 75 deletions
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue