diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt index 521245b940..2713162e91 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import com.tangem.core.ui.components.fields.visualtransformations.AmountVisualTransformation import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.utils.* +import java.math.BigDecimal import java.text.DecimalFormat /** @@ -123,32 +124,49 @@ fun AmountTextField( private fun prepareEnter(oldValue: String, newValue: String, decimalFormat: DecimalFormat, decimals: Int): String { val decimalSymbol = decimalFormat.decimalFormatSymbols.decimalSeparator return if (decimalFormat.isValidSymbols(newValue)) { - val parsedValue = newValue.parseBigDecimalOrNull()?.toPlainString() - ?: if (newValue.isBlank()) "" else oldValue - val replacedWithSymbol = if (parsedValue.findLast { it != decimalSymbol } != null) { - when { - parsedValue.findLast { it == COMMA_SEPARATOR } != null -> { - parsedValue.replace(COMMA_SEPARATOR, decimalSymbol) - } - parsedValue.findLast { it == POINT_SEPARATOR } != null -> { - parsedValue.replace(POINT_SEPARATOR, decimalSymbol) - } - else -> parsedValue - } - } else { - parsedValue - } - val joinedSymbol = if (newValue.endsWith(COMMA_SEPARATOR) || newValue.endsWith(POINT_SEPARATOR)) { - replacedWithSymbol.plus(decimalSymbol) - } else { - replacedWithSymbol - } - decimalFormat.getValidatedNumberWithFixedDecimals(joinedSymbol, decimals) + val parsedDecimal = newValue.parseBigDecimalOrNull() + val parsedValue = parsedDecimal?.toPlainString() ?: if (newValue.isBlank()) "" else oldValue + + val replacedWithSymbol = parsedValue.replaceDecimalSymbol(decimalSymbol) + val joinedSymbol = replacedWithSymbol.preserveDecimalSymbol(newValue, decimalSymbol) + val withPreservedZeros = joinedSymbol.preserveTrailingZeros(newValue, parsedDecimal, decimalSymbol) + decimalFormat.getValidatedNumberWithFixedDecimals(withPreservedZeros, decimals) } else { oldValue } } +private fun String.replaceDecimalSymbol(decimalSymbol: Char) = if (this.findLast { it != decimalSymbol } != null) { + when { + this.findLast { it == COMMA_SEPARATOR } != null -> { + this.replace(COMMA_SEPARATOR, decimalSymbol) + } + this.findLast { it == POINT_SEPARATOR } != null -> { + this.replace(POINT_SEPARATOR, decimalSymbol) + } + else -> this + } +} else { + this +} + +private fun String.preserveDecimalSymbol(newValue: String, decimalSymbol: Char) = if ( + newValue.endsWith(COMMA_SEPARATOR) || newValue.endsWith(POINT_SEPARATOR) +) { + this.plus(decimalSymbol) +} else { + this +} + +private fun String.preserveTrailingZeros(newValue: String, parsedDecimal: BigDecimal?, decimalSymbol: Char): String { + val trailingZeros = newValue.split(decimalSymbol).getOrNull(1)?.takeLastWhile { it == '0' }.orEmpty() + return if (parsedDecimal?.scale() == 0 && trailingZeros.isNotEmpty()) { + "$this$decimalSymbol$trailingZeros" + } else { + this.plus(trailingZeros) + } +} + private fun DecimalFormat.isValidSymbols(text: String): Boolean { return checkDecimalSeparatorDuplicate(text) } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt index 631cbdf100..f2ea95f678 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt @@ -296,6 +296,7 @@ internal class SendStateFactory( balance = balance, amountValue = amountValue, feeValue = feeValue, + reduceAmountBy = state.sendState?.reduceAmountBy, ), ) } @@ -331,6 +332,12 @@ internal class SendStateFactory( fun getSendNotificationState(notifications: ImmutableList): SendUiState { val state = currentStateProvider() val sendState = state.sendState ?: return state + val reducedBy = sendState.reduceAmountBy.takeIf { + notifications.none { + it is SendNotification.Error.ExistentialDeposit || + it is SendNotification.Error.TransactionLimitError + } + } return state.copy( sendState = sendState.copy( isPrimaryButtonEnabled = isPrimaryButtonEnabled( @@ -338,6 +345,7 @@ internal class SendStateFactory( isSending = sendState.isSending, notifications = notifications, ), + reduceAmountBy = reducedBy, notifications = notifications, showTapHelp = sendState.showTapHelp && notifications.isEmpty(), ), 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 84adf93793..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 @@ -142,7 +142,6 @@ internal sealed class SendStates { val txUrl: String, val ignoreAmountReduce: Boolean, val reduceAmountBy: BigDecimal?, - val reduceAmountTo: 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/SendAmountReduceToConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceToConverter.kt index 5b40022bbb..76fd7e2654 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceToConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountReduceToConverter.kt @@ -38,9 +38,6 @@ internal class SendAmountReduceToConverter( val isZero = if (amountTextField.isFiatValue) decimalFiatValue.isNullOrZero() else value.isZero() return state.copyWrapped( isEditState = isEditState, - sendState = state.sendState?.copy( - reduceAmountBy = value, - ), amountState = amountState.copy( isPrimaryButtonEnabled = !isExceedBalance && !isZero, amountTextField = amountTextField.copy( 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 6e6e67132b..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 @@ -17,7 +17,6 @@ internal class SendConfirmStateConverter( txUrl = "", ignoreAmountReduce = false, reduceAmountBy = null, - reduceAmountTo = 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 ad764dbb71..f6a100ff4e 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 @@ -60,18 +60,20 @@ internal class SendNotificationFactory( val amountValue = amountState.amountTextField.cryptoAmount.value ?: BigDecimal.ZERO val feeValue = feeState.fee?.amount?.value ?: BigDecimal.ZERO + val reduceAmountBy = sendState.reduceAmountBy ?: BigDecimal.ZERO val isFeeCoverage = checkFeeCoverage( isSubtractAvailable = isSubtractAvailableProvider(), balance = balance, amountValue = amountValue, feeValue = feeValue, + reduceAmountBy = reduceAmountBy, ) val sendingAmount = checkAndCalculateSubtractedAmount( isAmountSubtractAvailable = isFeeCoverage, cryptoCurrencyStatus = cryptoCurrencyStatusProvider(), amountValue = amountValue, feeValue = feeValue, - reduceAmountBy = sendState.reduceAmountBy, + reduceAmountBy = reduceAmountBy, ) buildList { // errors @@ -211,7 +213,7 @@ internal class SendNotificationFactory( ), onConfirmClick = { clickIntents.onAmountReduceClick( - reduceAmountBy = currencyDeposit, + reduceAmountBy = currencyDeposit.minus(diff), clazz = SendNotification.Error.ExistentialDeposit::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 a8a93bcd32..b500af1923 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 @@ -16,7 +16,7 @@ internal fun checkAndCalculateSubtractedAmount( cryptoCurrencyStatus: CryptoCurrencyStatus, amountValue: BigDecimal, feeValue: BigDecimal, - reduceAmountBy: BigDecimal?, + reduceAmountBy: BigDecimal, ): BigDecimal { val balance = cryptoCurrencyStatus.value.amount ?: return amountValue val isFeeCoverage = checkFeeCoverage( @@ -24,17 +24,12 @@ internal fun checkAndCalculateSubtractedAmount( balance = balance, amountValue = amountValue, feeValue = feeValue, + reduceAmountBy = reduceAmountBy, ) - val subtractedAmount = calculateSubtractedAmount( - isFeeCoverage = isFeeCoverage, - cryptoCurrencyStatus = cryptoCurrencyStatus, - amountValue = amountValue, - feeValue = feeValue, - ) - return if (reduceAmountBy != null) { - subtractedAmount.minus(reduceAmountBy) + return if (isFeeCoverage) { + balance.minus(reduceAmountBy).minus(feeValue) } else { - subtractedAmount + amountValue.minus(reduceAmountBy) } } @@ -46,26 +41,11 @@ internal fun checkFeeCoverage( balance: BigDecimal, amountValue: BigDecimal, feeValue: BigDecimal, + reduceAmountBy: BigDecimal?, ): Boolean { if (!isSubtractAvailable) return false - return balance < amountValue + feeValue && balance > feeValue && balance >= amountValue -} - -/** - * Calculates subtracted amount - */ -private fun calculateSubtractedAmount( - isFeeCoverage: Boolean, - cryptoCurrencyStatus: CryptoCurrencyStatus, - amountValue: BigDecimal, - feeValue: BigDecimal, -): BigDecimal { - val balance = cryptoCurrencyStatus.value.amount ?: return amountValue - return if (isFeeCoverage) { - minOf(amountValue, balance.minus(feeValue)) - } else { - amountValue - } + val amountWithReduced = (reduceAmountBy ?: BigDecimal.ZERO) + amountValue + return balance < amountWithReduced + feeValue && balance > feeValue && balance >= amountWithReduced } /** 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 a74e2df9ab..078ff442a9 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 @@ -823,7 +823,7 @@ internal class SendViewModel @Inject constructor( cryptoCurrencyStatus = cryptoCurrencyStatus, amountValue = amountValue, feeValue = feeValue, - reduceAmountBy = uiState.sendState?.reduceAmountBy, + reduceAmountBy = uiState.sendState?.reduceAmountBy ?: BigDecimal.ZERO, ) viewModelScope.launch(dispatchers.main) {