diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt index fd3364e729..c7aa7ddff0 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt @@ -141,6 +141,7 @@ internal sealed class SendStates { val transactionDate: Long, val txUrl: String, val ignoreAmountReduce: Boolean, + val reduceAmountBy: BigDecimal?, val isFromConfirmation: Boolean, val showTapHelp: Boolean, val notifications: ImmutableList, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt index e91f30356f..6ff84cf35d 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt @@ -6,6 +6,7 @@ import com.tangem.features.send.impl.presentation.state.StateRouter import com.tangem.features.send.impl.presentation.state.fields.SendAmountFieldChangeConverter import com.tangem.features.send.impl.presentation.state.fields.SendAmountFieldMaxAmountConverter import com.tangem.utils.Provider +import java.math.BigDecimal /** * Factory to produce amount state for [SendUiState] @@ -44,9 +45,18 @@ internal class AmountStateFactory( currentStateProvider = currentStateProvider, ) } + private val amountReducedConverter by lazy { + SendAmountReducedConverter( + stateRouterProvider = stateRouterProvider, + currentStateProvider = currentStateProvider, + cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + ) + } fun getOnAmountValueChange(value: String) = amountFieldChangeConverter.convert(value) + fun getOnAmountReducedState(reduceAmountBy: BigDecimal) = amountReducedConverter.convert(reduceAmountBy) + fun getOnMaxAmountClick(): SendUiState { return amountFieldMaxAmountConverter.convert(Unit) } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountUtils.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountUtils.kt new file mode 100644 index 0000000000..3921429828 --- /dev/null +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountUtils.kt @@ -0,0 +1,59 @@ +package com.tangem.features.send.impl.presentation.state.amount + +import androidx.compose.ui.text.input.ImeAction +import com.tangem.common.extensions.isZero +import com.tangem.core.ui.utils.parseBigDecimal +import com.tangem.core.ui.utils.parseToBigDecimal +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.features.send.impl.presentation.state.fields.SendTextField +import java.math.BigDecimal +import java.math.RoundingMode + +internal fun String.getCryptoValue(fiatRate: BigDecimal?, isFiatValue: Boolean, decimals: Int): String { + return if (isFiatValue && fiatRate != null) { + parseToBigDecimal(decimals).divide(fiatRate, decimals, RoundingMode.DOWN) + .parseBigDecimal(decimals) + } else { + this + } +} + +internal fun String.getFiatValue( + fiatRate: BigDecimal?, + isFiatValue: Boolean, + decimals: Int, +): Pair { + return if (fiatRate != null) { + val fiatValue = if (!isFiatValue) { + parseToBigDecimal(decimals).multiply(fiatRate).parseBigDecimal(decimals) + } else { + this + } + val decimalFiatValue = fiatValue.parseToBigDecimal(decimals) + fiatValue to decimalFiatValue + } else { + "" to null + } +} + +internal fun String.checkExceedBalance( + cryptoCurrencyStatus: CryptoCurrencyStatus, + amountTextField: SendTextField.AmountField, +): Boolean { + val currencyCryptoAmount = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO + val currencyFiatAmount = cryptoCurrencyStatus.value.fiatAmount ?: BigDecimal.ZERO + val fiatDecimal = parseToBigDecimal(amountTextField.fiatAmount.decimals) + val cryptoDecimal = parseToBigDecimal(amountTextField.cryptoAmount.decimals) + return if (amountTextField.isFiatValue) { + fiatDecimal > currencyFiatAmount + } else { + cryptoDecimal > currencyCryptoAmount + } +} + +internal fun getKeyboardAction(isExceedBalance: Boolean, decimalCryptoValue: BigDecimal) = + if (!isExceedBalance && !decimalCryptoValue.isZero()) { + ImeAction.Done + } else { + ImeAction.None + } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReducedConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReducedConverter.kt new file mode 100644 index 0000000000..d624608b14 --- /dev/null +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReducedConverter.kt @@ -0,0 +1,62 @@ +package com.tangem.features.send.impl.presentation.state.amount + +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.ui.text.input.KeyboardType +import com.tangem.common.extensions.isZero +import com.tangem.core.ui.utils.parseBigDecimal +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.features.send.impl.presentation.state.SendUiState +import com.tangem.features.send.impl.presentation.state.StateRouter +import com.tangem.utils.Provider +import com.tangem.utils.converter.Converter +import com.tangem.utils.isNullOrZero +import java.math.BigDecimal + +internal class SendAmountReducedConverter( + private val stateRouterProvider: Provider, + private val currentStateProvider: Provider, + private val cryptoCurrencyStatusProvider: Provider, +) : Converter { + override fun convert(value: BigDecimal): SendUiState { + val state = currentStateProvider() + val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() + val isEditState = stateRouterProvider().isEditState + val amountState = state.getAmountState(isEditState) ?: return state + val amountTextField = amountState.amountTextField + val cryptoDecimals = amountTextField.cryptoAmount.decimals + val fiatDecimals = amountTextField.fiatAmount.decimals + val amountValue = amountState.amountTextField.cryptoAmount.value ?: return state + + val decimalCryptoValue = amountValue.minus(value) + val cryptoValue = decimalCryptoValue.parseBigDecimal(cryptoDecimals) + val (fiatValue, decimalFiatValue) = cryptoValue.getFiatValue( + fiatRate = cryptoCurrencyStatus.value.fiatRate, + isFiatValue = false, + decimals = fiatDecimals, + ) + + val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue + val isExceedBalance = checkValue.checkExceedBalance(cryptoCurrencyStatus, amountTextField) + val isZero = if (amountTextField.isFiatValue) decimalFiatValue.isNullOrZero() else decimalCryptoValue.isZero() + return state.copyWrapped( + isEditState = isEditState, + sendState = state.sendState?.copy( + reduceAmountBy = value, + ), + amountState = amountState.copy( + isPrimaryButtonEnabled = !isExceedBalance && !isZero, + amountTextField = amountTextField.copy( + value = cryptoValue, + fiatValue = fiatValue, + isError = isExceedBalance, + cryptoAmount = amountTextField.cryptoAmount.copy(value = decimalCryptoValue), + fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue), + keyboardOptions = KeyboardOptions( + imeAction = getKeyboardAction(isExceedBalance, decimalCryptoValue), + keyboardType = KeyboardType.Number, + ), + ), + ), + ) + } +} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendConfirmStateConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendConfirmStateConverter.kt index 7935da04c8..254a979807 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendConfirmStateConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendConfirmStateConverter.kt @@ -16,6 +16,7 @@ internal class SendConfirmStateConverter( transactionDate = 0L, txUrl = "", ignoreAmountReduce = false, + reduceAmountBy = null, isFromConfirmation = true, showTapHelp = isTapHelpPreviewEnabledProvider(), notifications = persistentListOf(), diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt index 1de2ea58fb..c98336636c 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt @@ -93,6 +93,7 @@ internal class SendNotificationFactory( return state.copy( sendState = sendState.copy( ignoreAmountReduce = isIgnored, + reduceAmountBy = if (isIgnored) null else sendState.reduceAmountBy, notifications = updatedNotifications.toImmutableList(), ), ) @@ -225,8 +226,7 @@ internal class SendNotificationFactory( SendNotification.Warning.HighFeeError( amount = threshold.toPlainString(), onConfirmClick = { - val reduceTo = sendAmount.minus(threshold) - clickIntents.onAmountReduceClick(reduceTo, SendNotification.Warning.HighFeeError::class.java) + clickIntents.onAmountReduceClick(threshold, SendNotification.Warning.HighFeeError::class.java) }, onCloseClick = { clickIntents.onNotificationCancel(SendNotification.Warning.HighFeeError::class.java) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt index c7f5a024df..b06d42a23d 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeCalculation.kt @@ -1,13 +1,10 @@ package com.tangem.features.send.impl.presentation.state.fee -import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.common.extensions.isZero import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal -import com.tangem.domain.common.extensions.minimalAmount import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.lib.crypto.BlockchainUtils import java.math.BigDecimal import java.math.RoundingMode @@ -19,6 +16,7 @@ internal fun checkAndCalculateSubtractedAmount( cryptoCurrencyStatus: CryptoCurrencyStatus, amountValue: BigDecimal, feeValue: BigDecimal, + reduceAmountBy: BigDecimal?, ): BigDecimal { val balance = cryptoCurrencyStatus.value.amount ?: return amountValue val isFeeCoverage = checkFeeCoverage( @@ -27,12 +25,17 @@ internal fun checkAndCalculateSubtractedAmount( amountValue = amountValue, feeValue = feeValue, ) - return calculateSubtractedAmount( + val subtractedAmount = calculateSubtractedAmount( isFeeCoverage = isFeeCoverage, cryptoCurrencyStatus = cryptoCurrencyStatus, amountValue = amountValue, feeValue = feeValue, ) + return if (reduceAmountBy != null) { + subtractedAmount.minus(reduceAmountBy) + } else { + subtractedAmount + } } /** @@ -59,12 +62,7 @@ internal fun calculateSubtractedAmount( ): BigDecimal { val balance = cryptoCurrencyStatus.value.amount ?: return amountValue return if (isFeeCoverage) { - var subtractedValue = minOf(amountValue, balance.minus(feeValue)) - if (BlockchainUtils.isTezos(cryptoCurrencyStatus.currency.network.id.value)) { - val threshold = Blockchain.Tezos.minimalAmount() - subtractedValue = -threshold - } - subtractedValue + minOf(amountValue, balance.minus(feeValue)) } else { amountValue } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt index 86d87bf3cc..3958e3b64a 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt @@ -1,19 +1,20 @@ package com.tangem.features.send.impl.presentation.state.fields import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import com.tangem.common.extensions.isZero -import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.state.StateRouter +import com.tangem.features.send.impl.presentation.state.amount.checkExceedBalance +import com.tangem.features.send.impl.presentation.state.amount.getCryptoValue +import com.tangem.features.send.impl.presentation.state.amount.getFiatValue +import com.tangem.features.send.impl.presentation.state.amount.getKeyboardAction import com.tangem.utils.Provider import com.tangem.utils.converter.Converter import com.tangem.utils.isNullOrZero import java.math.BigDecimal -import java.math.RoundingMode internal class SendAmountFieldChangeConverter( private val stateRouterProvider: Provider, @@ -22,6 +23,7 @@ internal class SendAmountFieldChangeConverter( ) : Converter { override fun convert(value: String): SendUiState { val state = currentStateProvider() + val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() val isEditState = stateRouterProvider().isEditState val amountState = state.getAmountState(isEditState) ?: return state val amountTextField = amountState.amountTextField @@ -31,15 +33,20 @@ internal class SendAmountFieldChangeConverter( val fiatDecimals = amountTextField.fiatAmount.decimals val trimmedValue = value.trim() - val cryptoValue = trimmedValue.getCryptoValue(amountTextField.isFiatValue, cryptoDecimals) + val cryptoValue = trimmedValue.getCryptoValue( + fiatRate = cryptoCurrencyStatus.value.fiatRate, + isFiatValue = amountTextField.isFiatValue, + decimals = cryptoDecimals, + ) val decimalCryptoValue = cryptoValue.parseToBigDecimal(cryptoDecimals) val (fiatValue, decimalFiatValue) = trimmedValue.getFiatValue( + fiatRate = cryptoCurrencyStatus.value.fiatRate, isFiatValue = amountTextField.isFiatValue, decimals = fiatDecimals, ) val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue - val isExceedBalance = checkValue.checkExceedBalance(amountTextField) + val isExceedBalance = checkValue.checkExceedBalance(cryptoCurrencyStatus, amountTextField) val isZero = if (amountTextField.isFiatValue) decimalFiatValue.isNullOrZero() else decimalCryptoValue.isZero() return state.copyWrapped( isEditState = isEditState, @@ -60,32 +67,6 @@ internal class SendAmountFieldChangeConverter( ) } - private fun String.getCryptoValue(isFiatValue: Boolean, decimals: Int): String { - val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() - val fiatRate = cryptoCurrencyStatus.value.fiatRate - return if (isFiatValue && fiatRate != null) { - parseToBigDecimal(decimals).divide(fiatRate, decimals, RoundingMode.DOWN) - .parseBigDecimal(decimals) - } else { - this - } - } - - private fun String.getFiatValue(isFiatValue: Boolean, decimals: Int): Pair { - val fiatRate = cryptoCurrencyStatusProvider().value.fiatRate - return if (fiatRate != null) { - val fiatValue = if (!isFiatValue) { - parseToBigDecimal(decimals).multiply(fiatRate).parseBigDecimal(decimals) - } else { - this - } - val decimalFiatValue = fiatValue.parseToBigDecimal(decimals) - fiatValue to decimalFiatValue - } else { - "" to null - } - } - private fun SendUiState.emptyState(): SendUiState { val isEditState = stateRouterProvider().isEditState val amountState = getAmountState(isEditState) ?: return this @@ -104,24 +85,4 @@ internal class SendAmountFieldChangeConverter( ), ) } - - private fun String.checkExceedBalance(amountTextField: SendTextField.AmountField): Boolean { - val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() - val currencyCryptoAmount = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO - val currencyFiatAmount = cryptoCurrencyStatus.value.fiatAmount ?: BigDecimal.ZERO - val fiatDecimal = parseToBigDecimal(amountTextField.fiatAmount.decimals) - val cryptoDecimal = parseToBigDecimal(amountTextField.cryptoAmount.decimals) - return if (amountTextField.isFiatValue) { - fiatDecimal > currencyFiatAmount - } else { - cryptoDecimal > currencyCryptoAmount - } - } - - private fun getKeyboardAction(isExceedBalance: Boolean, decimalCryptoValue: BigDecimal) = - if (!isExceedBalance && !decimalCryptoValue.isZero()) { - ImeAction.Done - } else { - ImeAction.None - } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/SendClickIntentsStub.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/SendClickIntentsStub.kt index 84a2a7b90d..4f05dd6c30 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/SendClickIntentsStub.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/previewdata/SendClickIntentsStub.kt @@ -60,7 +60,7 @@ internal object SendClickIntentsStub : SendClickIntents { override fun onShareClick() {} - override fun onAmountReduceClick(reducedAmount: BigDecimal, clazz: Class) {} + override fun onAmountReduceClick(reduceAmountBy: BigDecimal, clazz: Class) {} override fun onNotificationCancel(clazz: Class) {} } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendClickIntents.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendClickIntents.kt index 76a22a3b24..61c33cb023 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendClickIntents.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendClickIntents.kt @@ -67,7 +67,7 @@ internal interface SendClickIntents { fun onShareClick() - fun onAmountReduceClick(reducedAmount: BigDecimal, clazz: Class) + fun onAmountReduceClick(reduceAmountBy: BigDecimal, clazz: Class) fun onNotificationCancel(clazz: Class) // endregion diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt index eca7088154..0273e12490 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt @@ -797,8 +797,8 @@ internal class SendViewModel @Inject constructor( analyticsEventHandler.send(SendAnalyticEvents.ShareButtonClicked) } - override fun onAmountReduceClick(reducedAmount: BigDecimal, clazz: Class) { - uiState = amountStateFactory.getOnAmountValueChange(reducedAmount.parseBigDecimal(cryptoCurrency.decimals)) + override fun onAmountReduceClick(reduceAmountBy: BigDecimal, clazz: Class) { + uiState = amountStateFactory.getOnAmountReducedState(reduceAmountBy) uiState = sendNotificationFactory.dismissNotificationState(clazz) feeReload() } @@ -820,6 +820,7 @@ internal class SendViewModel @Inject constructor( cryptoCurrencyStatus = cryptoCurrencyStatus, amountValue = amountValue, feeValue = feeValue, + reduceAmountBy = uiState.sendState?.reduceAmountBy, ) viewModelScope.launch(dispatchers.main) {