diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 7ed29ac733..45b3948014 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -92,7 +92,6 @@ Обозреватель Комиссия Сетевые комиссии – это плата пользователя за обработку и подтверждение транзакций. Размер комиссии зависит от нагрузки на сеть, объема транзакции и приоритета исполнения. %s - Подробнее Свое Быстро По рынку @@ -111,6 +110,7 @@ OK Основная карта Вставить + Подробнее Получить Отклонить Перезагрузить @@ -457,6 +457,8 @@ Поддержка Транзакция не выполнена Причина: %1$s\nКод: %2$s + Недостаточно средств для покрытия комиссии сети. Вычесть комиссию из отправляемой сумму? + Вычесть %1$s в %2$s Адрес Код назначения diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 6189bc5cbf..246e7b197e 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -90,7 +90,6 @@ Explorer Fee Network fees are charges users pay to process and confirm transactions. The fee amount can be affected by network congestion, transaction size, and execution priority. %s - Read more Custom Fast Market @@ -110,6 +109,7 @@ OK Primary Card Paste + Read more Receive Reject Reload @@ -459,6 +459,8 @@ Support The transaction is not completed Reason: %1$s\nCode: %2$s + Недостаточно средств для покрытия комиссии сети. Вычесть комиссию из отправляемой сумму? + Вычесть Confirm %1$s at %2$s Address @@ -486,7 +488,7 @@ Maximum fee amount Numbers only for Destination Tag Network fee - Sending amount will be reduced to cover the selected fee level + Sending amount will be reduced by %1$s to cover the selected commission level. The recipient will get %2$s. Network fee coverage Insufficient funds for the transfer, as the total of the fee and transfer amount exceeds the existing balance Total exceeds balance diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/utils/SendOnNextScreenAnalyticSender.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/utils/SendOnNextScreenAnalyticSender.kt index e3f5666a1c..4fc8d719af 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/utils/SendOnNextScreenAnalyticSender.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/utils/SendOnNextScreenAnalyticSender.kt @@ -23,9 +23,6 @@ internal class SendOnNextScreenAnalyticSender( } analyticsEventHandler.send(SendAnalyticEvents.SelectedFee(selectedFee.name)) } - if (feeState.isSubtract) { - analyticsEventHandler.send(SendAnalyticEvents.SubtractFromAmount) - } } SendUiStateType.Amount -> { val isFiatSelected = state.amountState?.amountTextField?.isFiatValue ?: return diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendAlertState.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendAlertState.kt index 672ceb39d5..ce176c7174 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendAlertState.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendAlertState.kt @@ -45,11 +45,20 @@ internal sealed class SendAlertState { override val message: TextReference = resourceReference(id = R.string.warning_demo_mode_message) } - object FeeIncreased : SendAlertState() { + data object FeeIncreased : SendAlertState() { override val title: TextReference? = null override val message: TextReference = resourceReference(id = R.string.send_notification_high_fee_title) } + data class FeeCoverage( + override val onConfirmClick: (() -> Unit), + ) : SendAlertState() { + override val title: TextReference? = null + override val message: TextReference = resourceReference(id = R.string.send_alert_fee_coverage_title) + override val confirmButtonText: TextReference = + resourceReference(id = R.string.send_alert_fee_coverage_subract_text) + } + data class ReserveAmount(val amount: String) : SendAlertState() { override val title: TextReference = resourceReference(id = R.string.send_notification_invalid_reserve_amount_title, wrappedList(amount)) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt index 23c9aec225..1bfe20a62b 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt @@ -43,6 +43,20 @@ internal class SendEventStateFactory( ) } + fun getFeeCoverageAlert(onConsume: () -> Unit): SendUiState { + val state = currentStateProvider() + return state.copy( + event = triggeredEvent( + data = SendEvent.ShowAlert( + SendAlertState.FeeCoverage( + onConfirmClick = clickIntents::onSubtractSelect, + ), + ), + onConsume = onConsume, + ), + ) + } + fun getFeeUpdatedAlert(fee: TransactionFee, onConsume: () -> Unit, onFeeNotIncreased: () -> Unit): SendUiState { val state = currentStateProvider() val feeSelector = state.feeState?.feeSelectorState as? FeeSelectorState.Content ?: return state diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotification.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotification.kt index cdbd005127..4ab6952836 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotification.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotification.kt @@ -85,5 +85,16 @@ internal sealed class SendNotification(val config: NotificationConfig) { title = resourceReference(R.string.send_notification_existential_deposit_title), subtitle = resourceReference(R.string.send_notification_existential_deposit_text, wrappedList(deposit)), ) + + data class NetworkCoverage( + val amountReducedBy: String, + val amountReduced: String, + ) : Warning( + title = resourceReference(id = R.string.send_network_fee_warning_title), + subtitle = resourceReference( + id = R.string.send_network_fee_warning_content, + formatArgs = wrappedList(amountReducedBy, amountReduced), + ), + ) } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotificationFactory.kt index cdfb8fc271..6f3021a2b5 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotificationFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotificationFactory.kt @@ -30,20 +30,22 @@ internal class SendNotificationFactory( .filter { it.type == SendUiStateType.Send } .map { val state = currentStateProvider() + val sendState = state.sendState val feeState = state.feeState ?: return@map persistentListOf() val feeAmount = feeState.fee?.amount?.value ?: BigDecimal.ZERO - val amountValue = state.amountState?.amountTextField?.value?.toBigDecimalOrNull() ?: BigDecimal.ZERO - val sendAmount = if (feeState.isSubtract) feeState.receivedAmountValue else amountValue + val amountValue = state.amountState?.amountTextField?.cryptoAmount?.value ?: BigDecimal.ZERO + val sendAmount = if (sendState.isSubtract) amountValue.minus(feeAmount) else amountValue buildList { // errors addExceedBalanceNotification(feeAmount, sendAmount) - addInvalidAmountNotification(feeState.isSubtract, sendAmount) + addInvalidAmountNotification(sendState.isSubtract, sendAmount) addMinimumAmountErrorNotification(feeAmount, sendAmount) addDustWarningNotification(feeAmount, sendAmount) addTransactionLimitErrorNotification(feeAmount, sendAmount) // warnings + addFeeCoverageNotification(sendState.isSubtract, sendAmount) addExistentialWarningNotification(feeAmount, sendAmount) - addHighFeeWarningNotification(amountValue, state.sendState.ignoreAmountReduce) + addHighFeeWarningNotification(sendAmount, sendState.ignoreAmountReduce) }.toImmutableList() } @@ -234,6 +236,29 @@ internal class SendNotificationFactory( } } + private fun MutableList.addFeeCoverageNotification( + isSubtract: Boolean, + amountValue: BigDecimal, + ) { + val state = currentStateProvider() + val cryptoCurrency = cryptoCurrencyStatusProvider().currency + val feeAmount = state.feeState?.fee?.amount?.value ?: BigDecimal.ZERO + + val amountReducedValue = amountValue.minus(feeAmount) + val amountReducedByValue = amountValue.minus(amountReducedValue) + val amountReducedBy = BigDecimalFormatter.formatCryptoAmount( + cryptoAmount = amountReducedByValue, + cryptoCurrency = cryptoCurrency, + ) + val amountReduced = BigDecimalFormatter.formatCryptoAmount( + cryptoAmount = amountReducedValue, + cryptoCurrency = cryptoCurrency, + ) + if (isSubtract) { + add(SendNotification.Warning.NetworkCoverage(amountReducedBy, amountReduced)) + } + } + companion object { private const val CARDANO_MINIMUM = "1" private const val DOGECOIN_MINIMUM = "0.01" 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 bbace2d0ac..10b562c4c5 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 @@ -223,6 +223,13 @@ internal class SendStateFactory( //endregion //region send + fun onSubtractSelect(isSubtract: Boolean): SendUiState { + val state = currentStateProvider() + return state.copy( + sendState = state.sendState.copy(isSubtract = isSubtract), + ) + } + fun getSendingStateUpdate(isSending: Boolean): SendUiState { val state = currentStateProvider() return state.copy(sendState = state.sendState.copy(isSending = isSending)) 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 9f1c13016c..8611ded493 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 @@ -75,12 +75,7 @@ internal sealed class SendStates { override val type: SendUiStateType = SendUiStateType.Fee, override val isPrimaryButtonEnabled: Boolean = false, val feeSelectorState: FeeSelectorState, - val isSubtractAvailable: Boolean, - val isSubtract: Boolean, - val isUserSubtracted: Boolean, val fee: Fee?, - val receivedAmountValue: BigDecimal, - val receivedAmount: String, val rate: BigDecimal?, val appCurrency: AppCurrency, val isFeeApproximate: Boolean, @@ -94,6 +89,7 @@ internal sealed class SendStates { override val isPrimaryButtonEnabled: Boolean = true, val isSending: Boolean = false, val isSuccess: Boolean = false, + val isSubtract: Boolean = false, val transactionDate: Long = 0L, val txUrl: String = "", val ignoreAmountReduce: Boolean = false, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt index 21a6c3e9db..a9c6451ab9 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt @@ -119,12 +119,12 @@ internal class StateRouter( mutableCurrentState.update { SendUiCurrentScreen(SendUiStateType.Fee, isFromConfirmation) } } - private fun continueToSend(show: () -> Unit) { - if (currentState.value.isFromConfirmation) showSend() else show() - } - - private fun showSend() { + fun showSend() { analyticsEventsHandler.send(SendAnalyticEvents.ConfirmationScreenOpened) mutableCurrentState.update { SendUiCurrentScreen(SendUiStateType.Send, isFromConfirmation = false) } } + + private fun continueToSend(show: () -> Unit) { + if (currentState.value.isFromConfirmation) showSend() else show() + } } \ No newline at end of file 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 16d58d39a6..b9a182691f 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,14 +1,14 @@ package com.tangem.features.send.impl.presentation.state.fee -import com.tangem.blockchain.common.transaction.Fee +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState -import java.math.BigDecimal /** - * Calculate receiving amount when fee is subtracted from sending amount + * Check if sending amount with fee is greater than balance */ -internal fun calculateReceiveAmount(state: SendUiState, feeAmount: Fee): BigDecimal { - val amountValue = state.amountState?.amountTextField?.cryptoAmount?.value ?: BigDecimal.ZERO - val fee = feeAmount.amount.value ?: return BigDecimal.ZERO - return amountValue.minus(fee) +internal fun checkFeeCoverage(state: SendUiState, cryptoCurrencyStatus: CryptoCurrencyStatus): Boolean { + val balance = cryptoCurrencyStatus.value.amount ?: return false + val fee = state.feeState?.fee?.amount?.value ?: return false + val amount = state.amountState?.amountTextField?.cryptoAmount?.value ?: return false + return balance <= amount + fee } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt index 3581ba0610..47b85049a3 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt @@ -9,7 +9,6 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.domain.wallets.models.UserWallet import com.tangem.features.send.impl.R -import com.tangem.features.send.impl.presentation.state.SendStates import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.state.SendUiStateType import com.tangem.features.send.impl.presentation.state.fields.SendTextField @@ -47,7 +46,6 @@ internal class FeeNotificationFactory( val selectedFee = feeSelectorState.selectedFee addTooLowNotification(feeSelectorState.fees, selectedFee, customFee) addTooHighNotification(feeSelectorState.fees, selectedFee, customFee) - addFeeCoverageNotification(feeState, state.amountState) addExceedsBalanceNotification(feeState.fee) } } @@ -89,20 +87,6 @@ internal class FeeNotificationFactory( } } - private fun MutableList.addFeeCoverageNotification( - feeState: SendStates.FeeState, - amountState: SendStates.AmountState?, - ) { - if (!feeState.isSubtractAvailable) return - - val cryptoAmount = coinCryptoCurrencyStatusProvider().value.amount ?: return - val feeValue = feeState.fee?.amount?.value ?: return - val value = amountState?.amountTextField?.cryptoAmount?.value ?: return - if (cryptoAmount <= value + feeValue && feeState.isSubtract && !feeState.isUserSubtracted) { - add(SendFeeNotification.Warning.NetworkCoverage) - } - } - private suspend fun MutableList.addExceedsBalanceNotification(fee: Fee?) { val feeValue = fee?.amount?.value ?: BigDecimal.ZERO val userWalletId = userWalletProvider().walletId diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt index b8f99a5511..d928d3c091 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt @@ -3,7 +3,6 @@ package com.tangem.features.send.impl.presentation.state.fee import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.common.extensions.isZero -import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus @@ -14,7 +13,6 @@ import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import com.tangem.utils.Provider import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -import java.math.BigDecimal /** * Factory to produce fee state for [SendUiState] @@ -22,7 +20,6 @@ import java.math.BigDecimal internal class FeeStateFactory( private val clickIntents: SendClickIntents, private val currentStateProvider: Provider, - private val coinCryptoCurrencyStatusProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, private val appCurrencyProvider: Provider, private val isFeeApproximateUseCase: IsFeeApproximateUseCase, @@ -55,9 +52,8 @@ internal class FeeStateFactory( ) } - fun onFeeOnLoadedState(fees: TransactionFee, isSubtractAvailable: Boolean): SendUiState { + fun onFeeOnLoadedState(fees: TransactionFee): SendUiState { val state = currentStateProvider() - val balance = coinCryptoCurrencyStatusProvider().value.amount ?: BigDecimal.ZERO val feeState = state.feeState ?: return state val feeSelectorState = (feeState.feeSelectorState as? FeeSelectorState.Content)?.copy( fees = fees, @@ -68,43 +64,15 @@ internal class FeeStateFactory( ) val fee = feeConverter.convert(feeSelectorState) - val receivedAmount = calculateReceiveAmount(state, fee) return state.copy( feeState = feeState.copy( - isSubtractAvailable = isSubtractAvailable, feeSelectorState = feeSelectorState, fee = fee, - receivedAmountValue = receivedAmount, - receivedAmount = getFormattedValue(receivedAmount), - isSubtract = isSubtractAvailable && checkAutoSubtract(state, fee, balance), isFeeApproximate = isFeeApproximate(fee), ), ) } - fun onFeeOnLoadedState(fees: TransactionFee): SendUiState { - val state = currentStateProvider() - val balance = coinCryptoCurrencyStatusProvider().value.amount ?: BigDecimal.ZERO - val feeState = state.feeState ?: return state - val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return state - - val updatedFeeSelector = feeSelectorState.copy( - fees = fees, - customValues = customFeeFieldConverter.convert(fees.normal), - ) - val fee = feeConverter.convert(updatedFeeSelector) - val receivedAmount = calculateReceiveAmount(state, fee) - return state.copy( - feeState = feeState.copy( - feeSelectorState = updatedFeeSelector, - fee = fee, - receivedAmountValue = receivedAmount, - receivedAmount = getFormattedValue(receivedAmount), - isSubtract = checkAutoSubtract(state, fee, balance), - ), - ) - } - fun onFeeOnErrorState(): SendUiState { val state = currentStateProvider() return state.copy( @@ -118,18 +86,13 @@ internal class FeeStateFactory( val state = currentStateProvider() val feeState = state.feeState ?: return state val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return state - val balance = coinCryptoCurrencyStatusProvider().value.amount ?: BigDecimal.ZERO val updatedFeeSelectorState = feeSelectorState.copy(selectedFee = feeType) val fee = feeConverter.convert(updatedFeeSelectorState) - val receivedAmount = calculateReceiveAmount(state, fee) return state.copy( feeState = feeState.copy( fee = fee, feeSelectorState = updatedFeeSelectorState, - receivedAmountValue = receivedAmount, - receivedAmount = getFormattedValue(receivedAmount), - isSubtract = checkAutoSubtract(state, fee, balance), ), ) } @@ -139,34 +102,12 @@ internal class FeeStateFactory( val feeState = state.feeState ?: return state val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return state val updatedFeeSelectorState = customFeeFieldConverter.onValueChange(feeSelectorState, index, value) - val balance = coinCryptoCurrencyStatusProvider().value.amount ?: BigDecimal.ZERO val fee = feeConverter.convert(updatedFeeSelectorState) - val receivedAmount = calculateReceiveAmount(state, fee) return state.copy( feeState = feeState.copy( feeSelectorState = updatedFeeSelectorState, fee = fee, - receivedAmountValue = receivedAmount, - receivedAmount = getFormattedValue(receivedAmount), - isSubtract = checkAutoSubtract(state, fee, balance), - ), - ) - } - - fun onSubtractSelect(value: Boolean): SendUiState { - val state = currentStateProvider() - val feeState = state.feeState ?: return state - val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return state - val fee = feeConverter.convert(feeSelectorState) - val receivedAmount = calculateReceiveAmount(state, fee) - return state.copy( - feeState = feeState.copy( - isSubtract = value, - isUserSubtracted = true, - receivedAmountValue = receivedAmount, - receivedAmount = getFormattedValue(receivedAmount), - fee = fee, ), ) } @@ -187,9 +128,6 @@ internal class FeeStateFactory( ): Boolean { val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return false val customValue = feeSelectorState.customValues.firstOrNull() - val balance = coinCryptoCurrencyStatusProvider().value.amount ?: BigDecimal.ZERO - val fee = feeConverter.convert(feeSelectorState) - val feeValue = fee.amount.value ?: BigDecimal.ZERO val isNotCustom = feeSelectorState.selectedFee != FeeType.Custom val isNotEmptyCustom = if (customValue != null) { @@ -198,24 +136,8 @@ internal class FeeStateFactory( false } val noErrors = notifications.none { it is SendFeeNotification.Error } - val isSubtractRequired = when { - !feeState.isSubtractAvailable -> true // current currency is not fee currency - feeValue + feeState.receivedAmountValue >= balance -> feeState.isSubtract - else -> feeValue + feeState.receivedAmountValue <= balance - } - return noErrors && isSubtractRequired && (isNotEmptyCustom || isNotCustom) - } - - private fun checkAutoSubtract(state: SendUiState, fee: Fee, balance: BigDecimal): Boolean { - val feeState = state.feeState ?: return false - val amountValue = state.amountState?.amountTextField?.cryptoAmount?.value ?: BigDecimal.ZERO - val feeAmount = fee.amount.value ?: BigDecimal.ZERO - return if (feeState.isUserSubtracted) { - feeState.isSubtract - } else { - amountValue + feeAmount >= balance - } + return noErrors && (isNotEmptyCustom || isNotCustom) } private fun isFeeApproximate(fee: Fee): Boolean { @@ -225,13 +147,4 @@ internal class FeeStateFactory( amountType = fee.amount.type, ) } - - private fun getFormattedValue(value: BigDecimal): String { - val cryptoCurrency = cryptoCurrencyStatusProvider().currency - return BigDecimalFormatter.formatCryptoAmount( - cryptoAmount = value, - cryptoCurrency = cryptoCurrency.symbol, - decimals = cryptoCurrency.decimals, - ) - } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeNotification.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeNotification.kt index f895fb4921..56add5cfe5 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeNotification.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeNotification.kt @@ -32,11 +32,6 @@ sealed class SendFeeNotification(val config: NotificationConfig) { subtitle = resourceReference(id = R.string.send_notification_fee_too_high_text, wrappedList(value)), ) - object NetworkCoverage : Warning( - title = resourceReference(id = R.string.send_network_fee_warning_title), - subtitle = resourceReference(id = R.string.send_network_fee_warning_content), - ) - data class NetworkFeeUnreachable(val onRefresh: () -> Unit) : Warning( title = resourceReference(R.string.send_fee_unreachable_error_title), subtitle = resourceReference(R.string.send_fee_unreachable_error_text), diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeStateConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeStateConverter.kt index 1dbee6b3fd..2659080a08 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeStateConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeStateConverter.kt @@ -6,7 +6,6 @@ import com.tangem.features.send.impl.presentation.state.SendStates import com.tangem.utils.Provider import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.persistentListOf -import java.math.BigDecimal internal class SendFeeStateConverter( private val appCurrencyProvider: Provider, @@ -17,12 +16,7 @@ internal class SendFeeStateConverter( val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() return SendStates.FeeState( feeSelectorState = FeeSelectorState.Loading, - isSubtractAvailable = false, - isSubtract = false, - isUserSubtracted = false, fee = null, - receivedAmountValue = BigDecimal.ZERO, - receivedAmount = "", notifications = persistentListOf(), rate = cryptoCurrencyStatus.value.fiatRate, appCurrency = appCurrencyProvider(), 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 630a026952..1b2a4d56f6 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 @@ -6,7 +6,6 @@ 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.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.utils.Provider @@ -22,7 +21,6 @@ internal class SendAmountFieldChangeConverter( val state = currentStateProvider() val amountState = state.amountState ?: return state val amountTextField = amountState.amountTextField - val feeState = state.feeState ?: return state if (value.isEmpty()) return state.emptyState() val cryptoDecimals = amountTextField.cryptoAmount.decimals @@ -36,7 +34,6 @@ internal class SendAmountFieldChangeConverter( val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue val isExceedBalance = checkValue.checkExceedBalance(amountTextField) - val isMaxAmount = checkValue.checkMaxAmount(amountTextField) return state.copy( amountState = amountState.copy( isPrimaryButtonEnabled = !isExceedBalance, @@ -52,9 +49,6 @@ internal class SendAmountFieldChangeConverter( ), ), ), - feeState = feeState.copy( - isSubtract = isMaxAmount, - ), ) } @@ -104,19 +98,4 @@ internal class SendAmountFieldChangeConverter( cryptoDecimal > currencyCryptoAmount || cryptoDecimal.isZero() } } - - private fun String.checkMaxAmount(amountTextField: SendTextField.AmountField): Boolean { - val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() - - // If current currency is Token - if (cryptoCurrencyStatus.currency is CryptoCurrency.Token) return false - - val currencyCryptoAmount = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO - val currencyFiatAmount = cryptoCurrencyStatus.value.fiatAmount ?: BigDecimal.ZERO - return if (amountTextField.isFiatValue) { - parseToBigDecimal(amountTextField.fiatAmount.decimals) == currencyFiatAmount - } else { - parseToBigDecimal(amountTextField.cryptoAmount.decimals) == currencyCryptoAmount - } - } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt index f6875731d7..b29459e757 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt @@ -20,7 +20,6 @@ internal class SendAmountFieldMaxAmountConverter( val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() val amountState = state.amountState ?: return state val amountTextField = amountState.amountTextField - val feeState = state.feeState ?: return state val cryptoDecimals = amountTextField.cryptoAmount.decimals val fiatDecimals = amountTextField.fiatAmount.decimals @@ -47,9 +46,6 @@ internal class SendAmountFieldMaxAmountConverter( ), ), ), - feeState = feeState.copy( - isSubtract = true, - ), ) } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedAndFeeContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedAndFeeContent.kt index 95a469975a..5acd6b57b1 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedAndFeeContent.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedAndFeeContent.kt @@ -15,7 +15,6 @@ import com.tangem.core.ui.components.notifications.Notification import com.tangem.core.ui.res.TangemTheme import com.tangem.features.send.impl.presentation.state.SendStates import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState -import com.tangem.features.send.impl.presentation.state.fee.FeeType import com.tangem.features.send.impl.presentation.state.fee.SendFeeNotification import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import kotlinx.collections.immutable.ImmutableList @@ -41,8 +40,6 @@ internal fun SendSpeedAndFeeContent(state: SendStates.FeeState?, clickIntents: S topNotifications(notifications) customFee(feeSendState) middleNotifications(notifications) - subtractButton(state, clickIntents) - bottomNotifications(notifications) } } @@ -85,17 +82,6 @@ private fun LazyListScope.middleNotifications( ) } -private fun LazyListScope.bottomNotifications( - configs: ImmutableList, - modifier: Modifier = Modifier, -) { - notifications( - configs = configs.filterIsInstance().toImmutableList(), - isLast = true, - modifier = modifier, - ) -} - @OptIn(ExperimentalFoundationApi::class) private fun LazyListScope.notifications( configs: ImmutableList, @@ -159,36 +145,4 @@ internal fun LazyListScope.customFee(feeSendState: FeeSelectorState, modifier: M } } } -} - -@OptIn(ExperimentalFoundationApi::class) -internal fun LazyListScope.subtractButton( - state: SendStates.FeeState, - clickIntents: SendClickIntents, - modifier: Modifier = Modifier, -) { - val receivedAmount = state.receivedAmount - val isSubtract = state.isSubtract - val isSubtractAvailable = state.isSubtractAvailable - val feeSendState = state.feeSelectorState - if (isSubtractAvailable) { - item { - val feeStateContent = feeSendState as? FeeSelectorState.Content - val isCustomAvailable = feeStateContent?.customValues.isNullOrEmpty().not() - val isCustomSelected = feeStateContent?.selectedFee == FeeType.Custom - val topPadding = if (isCustomSelected && isCustomAvailable) { - TangemTheme.dimens.spacing12 - } else { - TangemTheme.dimens.spacing20 - } - SendSpeedSubtract( - receivingAmount = receivedAmount, - isSubtract = isSubtract, - onSelectClick = clickIntents::onSubtractSelect, - modifier = modifier - .padding(top = topPadding) - .animateItemPlacement(), - ) - } - } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt index 611c29dd70..4d13b7d7a5 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt @@ -154,7 +154,7 @@ internal fun SendSpeedSelector( @Composable private fun FooterText(onReadMoreClick: () -> Unit) { - val linkText = stringResource(R.string.common_fee_selector_link_description) + val linkText = stringResource(R.string.common_read_more) val fullString = stringResource(R.string.common_fee_selector_footer, linkText) val linkTextPosition = fullString.length - linkText.length val defaultStyle = TangemTheme.colors.text.tertiary 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 8d409188fd..6c66189f98 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 @@ -43,7 +43,7 @@ internal interface SendClickIntents { fun onCustomFeeValueChange(index: Int, value: String) - fun onSubtractSelect(value: Boolean) + fun onSubtractSelect() fun onReadMoreClick() // 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 524d95dada..c9969641c5 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 @@ -45,9 +45,9 @@ import com.tangem.features.send.impl.presentation.domain.AvailableWallet import com.tangem.features.send.impl.presentation.state.* import com.tangem.features.send.impl.presentation.state.amount.AmountStateFactory import com.tangem.features.send.impl.presentation.state.fee.FeeNotificationFactory -import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState import com.tangem.features.send.impl.presentation.state.fee.FeeStateFactory import com.tangem.features.send.impl.presentation.state.fee.FeeType +import com.tangem.features.send.impl.presentation.state.fee.checkFeeCoverage import com.tangem.lib.crypto.BlockchainUtils import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -126,7 +126,6 @@ internal class SendViewModel @Inject constructor( private val feeStateFactory = FeeStateFactory( clickIntents = this, currentStateProvider = Provider { uiState }, - coinCryptoCurrencyStatusProvider = Provider { coinCryptoCurrencyStatus }, cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, appCurrencyProvider = Provider(selectedAppCurrencyFlow::value), isFeeApproximateUseCase = isFeeApproximateUseCase, @@ -411,6 +410,20 @@ internal class SendViewModel @Inject constructor( override fun popBackStack() = stateRouter.popBackStack() override fun onBackClick() = stateRouter.onBackClick(uiState.sendState.isSuccess) override fun onNextClick() { + val currentState = stateRouter.currentState.value + val isCurrentFee = currentState.type == SendUiStateType.Fee + if (isCurrentFee) { + val isFeeCoverage = checkFeeCoverage(uiState, cryptoCurrencyStatus) + if (isAmountSubtractAvailable && isFeeCoverage) { + uiState = eventStateFactory.getFeeCoverageAlert( + onConsume = { uiState = eventStateFactory.onConsumeEventState() }, + ) + return + } else { + uiState = stateFactory.onSubtractSelect(false) + } + } + val prevScreen = stateRouter.onNextClick() sendOnNextScreenAnalyticSender.send(prevScreen, uiState) } @@ -520,9 +533,9 @@ internal class SendViewModel @Inject constructor( updateFeeNotifications() } - override fun onSubtractSelect(value: Boolean) { - uiState = feeStateFactory.onSubtractSelect(value) - updateFeeNotifications() + override fun onSubtractSelect() { + uiState = stateFactory.onSubtractSelect(true) + stateRouter.showSend() } override fun onReadMoreClick() { @@ -539,12 +552,8 @@ internal class SendViewModel @Inject constructor( viewModelScope.launch(dispatchers.main) { uiState = feeStateFactory.onFeeOnLoadingState() uiState = callFeeUseCase()?.fold( - ifRight = { fees -> - feeStateFactory.onFeeOnLoadedState(fees, isAmountSubtractAvailable) - }, - ifLeft = { - feeStateFactory.onFeeOnErrorState() - }, + ifRight = feeStateFactory::onFeeOnLoadedState, + ifLeft = { feeStateFactory.onFeeOnErrorState() }, ) ?: feeStateFactory.onFeeOnErrorState() updateFeeNotifications() }.saveIn(feeJobHolder) @@ -587,16 +596,19 @@ internal class SendViewModel @Inject constructor( } override fun showAmount() { + uiState = stateFactory.onSubtractSelect(false) stateRouter.showAmount(isFromConfirmation = true) analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Amount)) } override fun showRecipient() { + uiState = stateFactory.onSubtractSelect(false) stateRouter.showRecipient(isFromConfirmation = true) analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Address)) } override fun showFee() { + uiState = stateFactory.onSubtractSelect(false) stateRouter.showFee(isFromConfirmation = true) analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Fee)) } @@ -623,12 +635,12 @@ internal class SendViewModel @Inject constructor( private fun verifyAndSendTransaction() { val recipient = uiState.recipientState?.addressTextField?.value ?: return val feeState = uiState.feeState ?: return - val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return + val fee = feeState.fee ?: return val memo = uiState.recipientState?.memoTextField?.value - val fee = feeStateFactory.feeConverter.convert(feeSelectorState) val amountValue = uiState.amountState?.amountTextField?.cryptoAmount?.value ?: return - val amountToSend = if (feeState.isSubtract && isAmountSubtractAvailable) { - feeState.receivedAmountValue + val amountToSend = if (uiState.sendState.isSubtract && isAmountSubtractAvailable) { + val feeValue = fee.amount.value ?: return + amountValue.minus(feeValue) } else { amountValue } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index 49d98a51dd..0da7c077d1 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -1119,7 +1119,7 @@ internal class StateBuilder( }, readMoreUrl = buildReadMoreUrl(), feeItems = txFeeState.toFeeItemState(), - readMore = resourceReference(R.string.common_fee_selector_link_description), + readMore = resourceReference(R.string.common_read_more), onReadMoreClick = actions.onFeeReadMoreClick, ) return uiState.copy(