Updated on 2026-08-14
This commit is contained in:
commit
7575df8bf6
22 changed files with 123 additions and 233 deletions
|
|
@ -92,7 +92,6 @@
|
||||||
<string name="common_explorer">Обозреватель</string>
|
<string name="common_explorer">Обозреватель</string>
|
||||||
<string name="common_fee_label">Комиссия</string>
|
<string name="common_fee_label">Комиссия</string>
|
||||||
<string name="common_fee_selector_footer">Сетевые комиссии – это плата пользователя за обработку и подтверждение транзакций. Размер комиссии зависит от нагрузки на сеть, объема транзакции и приоритета исполнения. %s</string>
|
<string name="common_fee_selector_footer">Сетевые комиссии – это плата пользователя за обработку и подтверждение транзакций. Размер комиссии зависит от нагрузки на сеть, объема транзакции и приоритета исполнения. %s</string>
|
||||||
<string name="common_fee_selector_link_description">Подробнее</string>
|
|
||||||
<string name="common_fee_selector_option_custom">Свое</string>
|
<string name="common_fee_selector_option_custom">Свое</string>
|
||||||
<string name="common_fee_selector_option_fast">Быстро</string>
|
<string name="common_fee_selector_option_fast">Быстро</string>
|
||||||
<string name="common_fee_selector_option_market">По рынку</string>
|
<string name="common_fee_selector_option_market">По рынку</string>
|
||||||
|
|
@ -111,6 +110,7 @@
|
||||||
<string name="common_ok">OK</string>
|
<string name="common_ok">OK</string>
|
||||||
<string name="common_origin_card">Основная карта</string>
|
<string name="common_origin_card">Основная карта</string>
|
||||||
<string name="common_paste">Вставить</string>
|
<string name="common_paste">Вставить</string>
|
||||||
|
<string name="common_read_more">Подробнее</string>
|
||||||
<string name="common_receive">Получить</string>
|
<string name="common_receive">Получить</string>
|
||||||
<string name="common_reject">Отклонить</string>
|
<string name="common_reject">Отклонить</string>
|
||||||
<string name="common_reload">Перезагрузить</string>
|
<string name="common_reload">Перезагрузить</string>
|
||||||
|
|
@ -457,6 +457,8 @@
|
||||||
<string name="send_alert_button_request_support">Поддержка</string>
|
<string name="send_alert_button_request_support">Поддержка</string>
|
||||||
<string name="send_alert_transaction_failed_title">Транзакция не выполнена</string>
|
<string name="send_alert_transaction_failed_title">Транзакция не выполнена</string>
|
||||||
<string name="send_alert_transaction_failed_text">Причина: %1$s\nКод: %2$s</string>
|
<string name="send_alert_transaction_failed_text">Причина: %1$s\nКод: %2$s</string>
|
||||||
|
<string name="send_alert_fee_coverage_title">Недостаточно средств для покрытия комиссии сети. Вычесть комиссию из отправляемой сумму?</string>
|
||||||
|
<string name="send_alert_fee_coverage_subract_text">Вычесть</string>
|
||||||
<string name="send_date_format">%1$s в %2$s</string>
|
<string name="send_date_format">%1$s в %2$s</string>
|
||||||
<string name="send_destination_hint_address">Адрес</string>
|
<string name="send_destination_hint_address">Адрес</string>
|
||||||
<string name="send_destination_tag_field">Код назначения</string>
|
<string name="send_destination_tag_field">Код назначения</string>
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,6 @@
|
||||||
<string name="common_explorer">Explorer</string>
|
<string name="common_explorer">Explorer</string>
|
||||||
<string name="common_fee_label">Fee</string>
|
<string name="common_fee_label">Fee</string>
|
||||||
<string name="common_fee_selector_footer">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</string>
|
<string name="common_fee_selector_footer">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</string>
|
||||||
<string name="common_fee_selector_link_description">Read more</string>
|
|
||||||
<string name="common_fee_selector_option_custom">Custom</string>
|
<string name="common_fee_selector_option_custom">Custom</string>
|
||||||
<string name="common_fee_selector_option_fast">Fast</string>
|
<string name="common_fee_selector_option_fast">Fast</string>
|
||||||
<string name="common_fee_selector_option_market">Market</string>
|
<string name="common_fee_selector_option_market">Market</string>
|
||||||
|
|
@ -110,6 +109,7 @@
|
||||||
<string name="common_ok">OK</string>
|
<string name="common_ok">OK</string>
|
||||||
<string name="common_origin_card">Primary Card</string>
|
<string name="common_origin_card">Primary Card</string>
|
||||||
<string name="common_paste">Paste</string>
|
<string name="common_paste">Paste</string>
|
||||||
|
<string name="common_read_more">Read more</string>
|
||||||
<string name="common_receive">Receive</string>
|
<string name="common_receive">Receive</string>
|
||||||
<string name="common_reject">Reject</string>
|
<string name="common_reject">Reject</string>
|
||||||
<string name="common_reload">Reload</string>
|
<string name="common_reload">Reload</string>
|
||||||
|
|
@ -459,6 +459,8 @@
|
||||||
<string name="send_alert_button_request_support">Support</string>
|
<string name="send_alert_button_request_support">Support</string>
|
||||||
<string name="send_alert_transaction_failed_title">The transaction is not completed</string>
|
<string name="send_alert_transaction_failed_title">The transaction is not completed</string>
|
||||||
<string name="send_alert_transaction_failed_text">Reason: %1$s\nCode: %2$s</string>
|
<string name="send_alert_transaction_failed_text">Reason: %1$s\nCode: %2$s</string>
|
||||||
|
<string name="send_alert_fee_coverage_title">Недостаточно средств для покрытия комиссии сети. Вычесть комиссию из отправляемой сумму?</string>
|
||||||
|
<string name="send_alert_fee_coverage_subract_text">Вычесть</string>
|
||||||
<string name="send_confirm_label">Confirm</string>
|
<string name="send_confirm_label">Confirm</string>
|
||||||
<string name="send_date_format">%1$s at %2$s</string>
|
<string name="send_date_format">%1$s at %2$s</string>
|
||||||
<string name="send_destination_hint_address">Address</string>
|
<string name="send_destination_hint_address">Address</string>
|
||||||
|
|
@ -486,7 +488,7 @@
|
||||||
<string name="send_max_fee_footer">Maximum fee amount</string>
|
<string name="send_max_fee_footer">Maximum fee amount</string>
|
||||||
<string name="send_memo_destination_tag_error">Numbers only for Destination Tag</string>
|
<string name="send_memo_destination_tag_error">Numbers only for Destination Tag</string>
|
||||||
<string name="common_network_fee_title">Network fee</string>
|
<string name="common_network_fee_title">Network fee</string>
|
||||||
<string name="send_network_fee_warning_content">Sending amount will be reduced to cover the selected fee level</string>
|
<string name="send_network_fee_warning_content">Sending amount will be reduced by %1$s to cover the selected commission level. The recipient will get %2$s.</string>
|
||||||
<string name="send_network_fee_warning_title">Network fee coverage</string>
|
<string name="send_network_fee_warning_title">Network fee coverage</string>
|
||||||
<string name="send_notification_exceed_balance_text">Insufficient funds for the transfer, as the total of the fee and transfer amount exceeds the existing balance</string>
|
<string name="send_notification_exceed_balance_text">Insufficient funds for the transfer, as the total of the fee and transfer amount exceeds the existing balance</string>
|
||||||
<string name="send_notification_exceed_balance_title">Total exceeds balance</string>
|
<string name="send_notification_exceed_balance_title">Total exceeds balance</string>
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,6 @@ internal class SendOnNextScreenAnalyticSender(
|
||||||
}
|
}
|
||||||
analyticsEventHandler.send(SendAnalyticEvents.SelectedFee(selectedFee.name))
|
analyticsEventHandler.send(SendAnalyticEvents.SelectedFee(selectedFee.name))
|
||||||
}
|
}
|
||||||
if (feeState.isSubtract) {
|
|
||||||
analyticsEventHandler.send(SendAnalyticEvents.SubtractFromAmount)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SendUiStateType.Amount -> {
|
SendUiStateType.Amount -> {
|
||||||
val isFiatSelected = state.amountState?.amountTextField?.isFiatValue ?: return
|
val isFiatSelected = state.amountState?.amountTextField?.isFiatValue ?: return
|
||||||
|
|
|
||||||
|
|
@ -45,11 +45,20 @@ internal sealed class SendAlertState {
|
||||||
override val message: TextReference = resourceReference(id = R.string.warning_demo_mode_message)
|
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 title: TextReference? = null
|
||||||
override val message: TextReference = resourceReference(id = R.string.send_notification_high_fee_title)
|
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() {
|
data class ReserveAmount(val amount: String) : SendAlertState() {
|
||||||
override val title: TextReference =
|
override val title: TextReference =
|
||||||
resourceReference(id = R.string.send_notification_invalid_reserve_amount_title, wrappedList(amount))
|
resourceReference(id = R.string.send_notification_invalid_reserve_amount_title, wrappedList(amount))
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
fun getFeeUpdatedAlert(fee: TransactionFee, onConsume: () -> Unit, onFeeNotIncreased: () -> Unit): SendUiState {
|
||||||
val state = currentStateProvider()
|
val state = currentStateProvider()
|
||||||
val feeSelector = state.feeState?.feeSelectorState as? FeeSelectorState.Content ?: return state
|
val feeSelector = state.feeState?.feeSelectorState as? FeeSelectorState.Content ?: return state
|
||||||
|
|
|
||||||
|
|
@ -85,5 +85,16 @@ internal sealed class SendNotification(val config: NotificationConfig) {
|
||||||
title = resourceReference(R.string.send_notification_existential_deposit_title),
|
title = resourceReference(R.string.send_notification_existential_deposit_title),
|
||||||
subtitle = resourceReference(R.string.send_notification_existential_deposit_text, wrappedList(deposit)),
|
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),
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -30,20 +30,22 @@ internal class SendNotificationFactory(
|
||||||
.filter { it.type == SendUiStateType.Send }
|
.filter { it.type == SendUiStateType.Send }
|
||||||
.map {
|
.map {
|
||||||
val state = currentStateProvider()
|
val state = currentStateProvider()
|
||||||
|
val sendState = state.sendState
|
||||||
val feeState = state.feeState ?: return@map persistentListOf()
|
val feeState = state.feeState ?: return@map persistentListOf()
|
||||||
val feeAmount = feeState.fee?.amount?.value ?: BigDecimal.ZERO
|
val feeAmount = feeState.fee?.amount?.value ?: BigDecimal.ZERO
|
||||||
val amountValue = state.amountState?.amountTextField?.value?.toBigDecimalOrNull() ?: BigDecimal.ZERO
|
val amountValue = state.amountState?.amountTextField?.cryptoAmount?.value ?: BigDecimal.ZERO
|
||||||
val sendAmount = if (feeState.isSubtract) feeState.receivedAmountValue else amountValue
|
val sendAmount = if (sendState.isSubtract) amountValue.minus(feeAmount) else amountValue
|
||||||
buildList {
|
buildList {
|
||||||
// errors
|
// errors
|
||||||
addExceedBalanceNotification(feeAmount, sendAmount)
|
addExceedBalanceNotification(feeAmount, sendAmount)
|
||||||
addInvalidAmountNotification(feeState.isSubtract, sendAmount)
|
addInvalidAmountNotification(sendState.isSubtract, sendAmount)
|
||||||
addMinimumAmountErrorNotification(feeAmount, sendAmount)
|
addMinimumAmountErrorNotification(feeAmount, sendAmount)
|
||||||
addDustWarningNotification(feeAmount, sendAmount)
|
addDustWarningNotification(feeAmount, sendAmount)
|
||||||
addTransactionLimitErrorNotification(feeAmount, sendAmount)
|
addTransactionLimitErrorNotification(feeAmount, sendAmount)
|
||||||
// warnings
|
// warnings
|
||||||
|
addFeeCoverageNotification(sendState.isSubtract, sendAmount)
|
||||||
addExistentialWarningNotification(feeAmount, sendAmount)
|
addExistentialWarningNotification(feeAmount, sendAmount)
|
||||||
addHighFeeWarningNotification(amountValue, state.sendState.ignoreAmountReduce)
|
addHighFeeWarningNotification(sendAmount, sendState.ignoreAmountReduce)
|
||||||
}.toImmutableList()
|
}.toImmutableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,6 +236,29 @@ internal class SendNotificationFactory(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun MutableList<SendNotification>.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 {
|
companion object {
|
||||||
private const val CARDANO_MINIMUM = "1"
|
private const val CARDANO_MINIMUM = "1"
|
||||||
private const val DOGECOIN_MINIMUM = "0.01"
|
private const val DOGECOIN_MINIMUM = "0.01"
|
||||||
|
|
|
||||||
|
|
@ -223,6 +223,13 @@ internal class SendStateFactory(
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
//region send
|
//region send
|
||||||
|
fun onSubtractSelect(isSubtract: Boolean): SendUiState {
|
||||||
|
val state = currentStateProvider()
|
||||||
|
return state.copy(
|
||||||
|
sendState = state.sendState.copy(isSubtract = isSubtract),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun getSendingStateUpdate(isSending: Boolean): SendUiState {
|
fun getSendingStateUpdate(isSending: Boolean): SendUiState {
|
||||||
val state = currentStateProvider()
|
val state = currentStateProvider()
|
||||||
return state.copy(sendState = state.sendState.copy(isSending = isSending))
|
return state.copy(sendState = state.sendState.copy(isSending = isSending))
|
||||||
|
|
|
||||||
|
|
@ -75,12 +75,7 @@ internal sealed class SendStates {
|
||||||
override val type: SendUiStateType = SendUiStateType.Fee,
|
override val type: SendUiStateType = SendUiStateType.Fee,
|
||||||
override val isPrimaryButtonEnabled: Boolean = false,
|
override val isPrimaryButtonEnabled: Boolean = false,
|
||||||
val feeSelectorState: FeeSelectorState,
|
val feeSelectorState: FeeSelectorState,
|
||||||
val isSubtractAvailable: Boolean,
|
|
||||||
val isSubtract: Boolean,
|
|
||||||
val isUserSubtracted: Boolean,
|
|
||||||
val fee: Fee?,
|
val fee: Fee?,
|
||||||
val receivedAmountValue: BigDecimal,
|
|
||||||
val receivedAmount: String,
|
|
||||||
val rate: BigDecimal?,
|
val rate: BigDecimal?,
|
||||||
val appCurrency: AppCurrency,
|
val appCurrency: AppCurrency,
|
||||||
val isFeeApproximate: Boolean,
|
val isFeeApproximate: Boolean,
|
||||||
|
|
@ -94,6 +89,7 @@ internal sealed class SendStates {
|
||||||
override val isPrimaryButtonEnabled: Boolean = true,
|
override val isPrimaryButtonEnabled: Boolean = true,
|
||||||
val isSending: Boolean = false,
|
val isSending: Boolean = false,
|
||||||
val isSuccess: Boolean = false,
|
val isSuccess: Boolean = false,
|
||||||
|
val isSubtract: Boolean = false,
|
||||||
val transactionDate: Long = 0L,
|
val transactionDate: Long = 0L,
|
||||||
val txUrl: String = "",
|
val txUrl: String = "",
|
||||||
val ignoreAmountReduce: Boolean = false,
|
val ignoreAmountReduce: Boolean = false,
|
||||||
|
|
|
||||||
|
|
@ -119,12 +119,12 @@ internal class StateRouter(
|
||||||
mutableCurrentState.update { SendUiCurrentScreen(SendUiStateType.Fee, isFromConfirmation) }
|
mutableCurrentState.update { SendUiCurrentScreen(SendUiStateType.Fee, isFromConfirmation) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun continueToSend(show: () -> Unit) {
|
fun showSend() {
|
||||||
if (currentState.value.isFromConfirmation) showSend() else show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showSend() {
|
|
||||||
analyticsEventsHandler.send(SendAnalyticEvents.ConfirmationScreenOpened)
|
analyticsEventsHandler.send(SendAnalyticEvents.ConfirmationScreenOpened)
|
||||||
mutableCurrentState.update { SendUiCurrentScreen(SendUiStateType.Send, isFromConfirmation = false) }
|
mutableCurrentState.update { SendUiCurrentScreen(SendUiStateType.Send, isFromConfirmation = false) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun continueToSend(show: () -> Unit) {
|
||||||
|
if (currentState.value.isFromConfirmation) showSend() else show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
package com.tangem.features.send.impl.presentation.state.fee
|
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 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 {
|
internal fun checkFeeCoverage(state: SendUiState, cryptoCurrencyStatus: CryptoCurrencyStatus): Boolean {
|
||||||
val amountValue = state.amountState?.amountTextField?.cryptoAmount?.value ?: BigDecimal.ZERO
|
val balance = cryptoCurrencyStatus.value.amount ?: return false
|
||||||
val fee = feeAmount.amount.value ?: return BigDecimal.ZERO
|
val fee = state.feeState?.fee?.amount?.value ?: return false
|
||||||
return amountValue.minus(fee)
|
val amount = state.amountState?.amountTextField?.cryptoAmount?.value ?: return false
|
||||||
|
return balance <= amount + fee
|
||||||
}
|
}
|
||||||
|
|
@ -9,7 +9,6 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
|
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
|
||||||
import com.tangem.domain.wallets.models.UserWallet
|
import com.tangem.domain.wallets.models.UserWallet
|
||||||
import com.tangem.features.send.impl.R
|
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.SendUiState
|
||||||
import com.tangem.features.send.impl.presentation.state.SendUiStateType
|
import com.tangem.features.send.impl.presentation.state.SendUiStateType
|
||||||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||||
|
|
@ -47,7 +46,6 @@ internal class FeeNotificationFactory(
|
||||||
val selectedFee = feeSelectorState.selectedFee
|
val selectedFee = feeSelectorState.selectedFee
|
||||||
addTooLowNotification(feeSelectorState.fees, selectedFee, customFee)
|
addTooLowNotification(feeSelectorState.fees, selectedFee, customFee)
|
||||||
addTooHighNotification(feeSelectorState.fees, selectedFee, customFee)
|
addTooHighNotification(feeSelectorState.fees, selectedFee, customFee)
|
||||||
addFeeCoverageNotification(feeState, state.amountState)
|
|
||||||
addExceedsBalanceNotification(feeState.fee)
|
addExceedsBalanceNotification(feeState.fee)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -89,20 +87,6 @@ internal class FeeNotificationFactory(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MutableList<SendFeeNotification>.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<SendFeeNotification>.addExceedsBalanceNotification(fee: Fee?) {
|
private suspend fun MutableList<SendFeeNotification>.addExceedsBalanceNotification(fee: Fee?) {
|
||||||
val feeValue = fee?.amount?.value ?: BigDecimal.ZERO
|
val feeValue = fee?.amount?.value ?: BigDecimal.ZERO
|
||||||
val userWalletId = userWalletProvider().walletId
|
val userWalletId = userWalletProvider().walletId
|
||||||
|
|
|
||||||
|
|
@ -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.Fee
|
||||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||||
import com.tangem.common.extensions.isZero
|
import com.tangem.common.extensions.isZero
|
||||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
|
||||||
import com.tangem.core.ui.utils.parseToBigDecimal
|
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
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 com.tangem.utils.Provider
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import java.math.BigDecimal
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory to produce fee state for [SendUiState]
|
* Factory to produce fee state for [SendUiState]
|
||||||
|
|
@ -22,7 +20,6 @@ import java.math.BigDecimal
|
||||||
internal class FeeStateFactory(
|
internal class FeeStateFactory(
|
||||||
private val clickIntents: SendClickIntents,
|
private val clickIntents: SendClickIntents,
|
||||||
private val currentStateProvider: Provider<SendUiState>,
|
private val currentStateProvider: Provider<SendUiState>,
|
||||||
private val coinCryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
|
||||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||||
private val isFeeApproximateUseCase: IsFeeApproximateUseCase,
|
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 state = currentStateProvider()
|
||||||
val balance = coinCryptoCurrencyStatusProvider().value.amount ?: BigDecimal.ZERO
|
|
||||||
val feeState = state.feeState ?: return state
|
val feeState = state.feeState ?: return state
|
||||||
val feeSelectorState = (feeState.feeSelectorState as? FeeSelectorState.Content)?.copy(
|
val feeSelectorState = (feeState.feeSelectorState as? FeeSelectorState.Content)?.copy(
|
||||||
fees = fees,
|
fees = fees,
|
||||||
|
|
@ -68,43 +64,15 @@ internal class FeeStateFactory(
|
||||||
)
|
)
|
||||||
|
|
||||||
val fee = feeConverter.convert(feeSelectorState)
|
val fee = feeConverter.convert(feeSelectorState)
|
||||||
val receivedAmount = calculateReceiveAmount(state, fee)
|
|
||||||
return state.copy(
|
return state.copy(
|
||||||
feeState = feeState.copy(
|
feeState = feeState.copy(
|
||||||
isSubtractAvailable = isSubtractAvailable,
|
|
||||||
feeSelectorState = feeSelectorState,
|
feeSelectorState = feeSelectorState,
|
||||||
fee = fee,
|
fee = fee,
|
||||||
receivedAmountValue = receivedAmount,
|
|
||||||
receivedAmount = getFormattedValue(receivedAmount),
|
|
||||||
isSubtract = isSubtractAvailable && checkAutoSubtract(state, fee, balance),
|
|
||||||
isFeeApproximate = isFeeApproximate(fee),
|
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 {
|
fun onFeeOnErrorState(): SendUiState {
|
||||||
val state = currentStateProvider()
|
val state = currentStateProvider()
|
||||||
return state.copy(
|
return state.copy(
|
||||||
|
|
@ -118,18 +86,13 @@ internal class FeeStateFactory(
|
||||||
val state = currentStateProvider()
|
val state = currentStateProvider()
|
||||||
val feeState = state.feeState ?: return state
|
val feeState = state.feeState ?: return state
|
||||||
val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: 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 updatedFeeSelectorState = feeSelectorState.copy(selectedFee = feeType)
|
||||||
val fee = feeConverter.convert(updatedFeeSelectorState)
|
val fee = feeConverter.convert(updatedFeeSelectorState)
|
||||||
val receivedAmount = calculateReceiveAmount(state, fee)
|
|
||||||
return state.copy(
|
return state.copy(
|
||||||
feeState = feeState.copy(
|
feeState = feeState.copy(
|
||||||
fee = fee,
|
fee = fee,
|
||||||
feeSelectorState = updatedFeeSelectorState,
|
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 feeState = state.feeState ?: return state
|
||||||
val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return state
|
val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return state
|
||||||
val updatedFeeSelectorState = customFeeFieldConverter.onValueChange(feeSelectorState, index, value)
|
val updatedFeeSelectorState = customFeeFieldConverter.onValueChange(feeSelectorState, index, value)
|
||||||
val balance = coinCryptoCurrencyStatusProvider().value.amount ?: BigDecimal.ZERO
|
|
||||||
|
|
||||||
val fee = feeConverter.convert(updatedFeeSelectorState)
|
val fee = feeConverter.convert(updatedFeeSelectorState)
|
||||||
val receivedAmount = calculateReceiveAmount(state, fee)
|
|
||||||
return state.copy(
|
return state.copy(
|
||||||
feeState = feeState.copy(
|
feeState = feeState.copy(
|
||||||
feeSelectorState = updatedFeeSelectorState,
|
feeSelectorState = updatedFeeSelectorState,
|
||||||
fee = fee,
|
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 {
|
): Boolean {
|
||||||
val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return false
|
val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return false
|
||||||
val customValue = feeSelectorState.customValues.firstOrNull()
|
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 isNotCustom = feeSelectorState.selectedFee != FeeType.Custom
|
||||||
val isNotEmptyCustom = if (customValue != null) {
|
val isNotEmptyCustom = if (customValue != null) {
|
||||||
|
|
@ -198,24 +136,8 @@ internal class FeeStateFactory(
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
val noErrors = notifications.none { it is SendFeeNotification.Error }
|
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)
|
return noErrors && (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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isFeeApproximate(fee: Fee): Boolean {
|
private fun isFeeApproximate(fee: Fee): Boolean {
|
||||||
|
|
@ -225,13 +147,4 @@ internal class FeeStateFactory(
|
||||||
amountType = fee.amount.type,
|
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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -32,11 +32,6 @@ sealed class SendFeeNotification(val config: NotificationConfig) {
|
||||||
subtitle = resourceReference(id = R.string.send_notification_fee_too_high_text, wrappedList(value)),
|
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(
|
data class NetworkFeeUnreachable(val onRefresh: () -> Unit) : Warning(
|
||||||
title = resourceReference(R.string.send_fee_unreachable_error_title),
|
title = resourceReference(R.string.send_fee_unreachable_error_title),
|
||||||
subtitle = resourceReference(R.string.send_fee_unreachable_error_text),
|
subtitle = resourceReference(R.string.send_fee_unreachable_error_text),
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import com.tangem.features.send.impl.presentation.state.SendStates
|
||||||
import com.tangem.utils.Provider
|
import com.tangem.utils.Provider
|
||||||
import com.tangem.utils.converter.Converter
|
import com.tangem.utils.converter.Converter
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import java.math.BigDecimal
|
|
||||||
|
|
||||||
internal class SendFeeStateConverter(
|
internal class SendFeeStateConverter(
|
||||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||||
|
|
@ -17,12 +16,7 @@ internal class SendFeeStateConverter(
|
||||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||||
return SendStates.FeeState(
|
return SendStates.FeeState(
|
||||||
feeSelectorState = FeeSelectorState.Loading,
|
feeSelectorState = FeeSelectorState.Loading,
|
||||||
isSubtractAvailable = false,
|
|
||||||
isSubtract = false,
|
|
||||||
isUserSubtracted = false,
|
|
||||||
fee = null,
|
fee = null,
|
||||||
receivedAmountValue = BigDecimal.ZERO,
|
|
||||||
receivedAmount = "",
|
|
||||||
notifications = persistentListOf(),
|
notifications = persistentListOf(),
|
||||||
rate = cryptoCurrencyStatus.value.fiatRate,
|
rate = cryptoCurrencyStatus.value.fiatRate,
|
||||||
appCurrency = appCurrencyProvider(),
|
appCurrency = appCurrencyProvider(),
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||||
import com.tangem.common.extensions.isZero
|
import com.tangem.common.extensions.isZero
|
||||||
import com.tangem.core.ui.utils.parseBigDecimal
|
import com.tangem.core.ui.utils.parseBigDecimal
|
||||||
import com.tangem.core.ui.utils.parseToBigDecimal
|
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
|
||||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||||
import com.tangem.utils.Provider
|
import com.tangem.utils.Provider
|
||||||
|
|
@ -22,7 +21,6 @@ internal class SendAmountFieldChangeConverter(
|
||||||
val state = currentStateProvider()
|
val state = currentStateProvider()
|
||||||
val amountState = state.amountState ?: return state
|
val amountState = state.amountState ?: return state
|
||||||
val amountTextField = amountState.amountTextField
|
val amountTextField = amountState.amountTextField
|
||||||
val feeState = state.feeState ?: return state
|
|
||||||
|
|
||||||
if (value.isEmpty()) return state.emptyState()
|
if (value.isEmpty()) return state.emptyState()
|
||||||
val cryptoDecimals = amountTextField.cryptoAmount.decimals
|
val cryptoDecimals = amountTextField.cryptoAmount.decimals
|
||||||
|
|
@ -36,7 +34,6 @@ internal class SendAmountFieldChangeConverter(
|
||||||
|
|
||||||
val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue
|
val checkValue = if (amountTextField.isFiatValue) fiatValue else cryptoValue
|
||||||
val isExceedBalance = checkValue.checkExceedBalance(amountTextField)
|
val isExceedBalance = checkValue.checkExceedBalance(amountTextField)
|
||||||
val isMaxAmount = checkValue.checkMaxAmount(amountTextField)
|
|
||||||
return state.copy(
|
return state.copy(
|
||||||
amountState = amountState.copy(
|
amountState = amountState.copy(
|
||||||
isPrimaryButtonEnabled = !isExceedBalance,
|
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()
|
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -20,7 +20,6 @@ internal class SendAmountFieldMaxAmountConverter(
|
||||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||||
val amountState = state.amountState ?: return state
|
val amountState = state.amountState ?: return state
|
||||||
val amountTextField = amountState.amountTextField
|
val amountTextField = amountState.amountTextField
|
||||||
val feeState = state.feeState ?: return state
|
|
||||||
|
|
||||||
val cryptoDecimals = amountTextField.cryptoAmount.decimals
|
val cryptoDecimals = amountTextField.cryptoAmount.decimals
|
||||||
val fiatDecimals = amountTextField.fiatAmount.decimals
|
val fiatDecimals = amountTextField.fiatAmount.decimals
|
||||||
|
|
@ -47,9 +46,6 @@ internal class SendAmountFieldMaxAmountConverter(
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
feeState = feeState.copy(
|
|
||||||
isSubtract = true,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -15,7 +15,6 @@ import com.tangem.core.ui.components.notifications.Notification
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.features.send.impl.presentation.state.SendStates
|
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.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.state.fee.SendFeeNotification
|
||||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
@ -41,8 +40,6 @@ internal fun SendSpeedAndFeeContent(state: SendStates.FeeState?, clickIntents: S
|
||||||
topNotifications(notifications)
|
topNotifications(notifications)
|
||||||
customFee(feeSendState)
|
customFee(feeSendState)
|
||||||
middleNotifications(notifications)
|
middleNotifications(notifications)
|
||||||
subtractButton(state, clickIntents)
|
|
||||||
bottomNotifications(notifications)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,17 +82,6 @@ private fun LazyListScope.middleNotifications(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun LazyListScope.bottomNotifications(
|
|
||||||
configs: ImmutableList<SendFeeNotification>,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
notifications(
|
|
||||||
configs = configs.filterIsInstance<SendFeeNotification.Warning.NetworkCoverage>().toImmutableList(),
|
|
||||||
isLast = true,
|
|
||||||
modifier = modifier,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
private fun LazyListScope.notifications(
|
private fun LazyListScope.notifications(
|
||||||
configs: ImmutableList<SendFeeNotification>,
|
configs: ImmutableList<SendFeeNotification>,
|
||||||
|
|
@ -160,35 +146,3 @@ 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(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -154,7 +154,7 @@ internal fun SendSpeedSelector(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun FooterText(onReadMoreClick: () -> Unit) {
|
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 fullString = stringResource(R.string.common_fee_selector_footer, linkText)
|
||||||
val linkTextPosition = fullString.length - linkText.length
|
val linkTextPosition = fullString.length - linkText.length
|
||||||
val defaultStyle = TangemTheme.colors.text.tertiary
|
val defaultStyle = TangemTheme.colors.text.tertiary
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ internal interface SendClickIntents {
|
||||||
|
|
||||||
fun onCustomFeeValueChange(index: Int, value: String)
|
fun onCustomFeeValueChange(index: Int, value: String)
|
||||||
|
|
||||||
fun onSubtractSelect(value: Boolean)
|
fun onSubtractSelect()
|
||||||
|
|
||||||
fun onReadMoreClick()
|
fun onReadMoreClick()
|
||||||
// endregion
|
// endregion
|
||||||
|
|
|
||||||
|
|
@ -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.*
|
||||||
import com.tangem.features.send.impl.presentation.state.amount.AmountStateFactory
|
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.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.FeeStateFactory
|
||||||
import com.tangem.features.send.impl.presentation.state.fee.FeeType
|
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.lib.crypto.BlockchainUtils
|
||||||
import com.tangem.utils.Provider
|
import com.tangem.utils.Provider
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
|
@ -126,7 +126,6 @@ internal class SendViewModel @Inject constructor(
|
||||||
private val feeStateFactory = FeeStateFactory(
|
private val feeStateFactory = FeeStateFactory(
|
||||||
clickIntents = this,
|
clickIntents = this,
|
||||||
currentStateProvider = Provider { uiState },
|
currentStateProvider = Provider { uiState },
|
||||||
coinCryptoCurrencyStatusProvider = Provider { coinCryptoCurrencyStatus },
|
|
||||||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||||
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
|
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
|
||||||
isFeeApproximateUseCase = isFeeApproximateUseCase,
|
isFeeApproximateUseCase = isFeeApproximateUseCase,
|
||||||
|
|
@ -411,6 +410,20 @@ internal class SendViewModel @Inject constructor(
|
||||||
override fun popBackStack() = stateRouter.popBackStack()
|
override fun popBackStack() = stateRouter.popBackStack()
|
||||||
override fun onBackClick() = stateRouter.onBackClick(uiState.sendState.isSuccess)
|
override fun onBackClick() = stateRouter.onBackClick(uiState.sendState.isSuccess)
|
||||||
override fun onNextClick() {
|
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()
|
val prevScreen = stateRouter.onNextClick()
|
||||||
sendOnNextScreenAnalyticSender.send(prevScreen, uiState)
|
sendOnNextScreenAnalyticSender.send(prevScreen, uiState)
|
||||||
}
|
}
|
||||||
|
|
@ -520,9 +533,9 @@ internal class SendViewModel @Inject constructor(
|
||||||
updateFeeNotifications()
|
updateFeeNotifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSubtractSelect(value: Boolean) {
|
override fun onSubtractSelect() {
|
||||||
uiState = feeStateFactory.onSubtractSelect(value)
|
uiState = stateFactory.onSubtractSelect(true)
|
||||||
updateFeeNotifications()
|
stateRouter.showSend()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReadMoreClick() {
|
override fun onReadMoreClick() {
|
||||||
|
|
@ -539,12 +552,8 @@ internal class SendViewModel @Inject constructor(
|
||||||
viewModelScope.launch(dispatchers.main) {
|
viewModelScope.launch(dispatchers.main) {
|
||||||
uiState = feeStateFactory.onFeeOnLoadingState()
|
uiState = feeStateFactory.onFeeOnLoadingState()
|
||||||
uiState = callFeeUseCase()?.fold(
|
uiState = callFeeUseCase()?.fold(
|
||||||
ifRight = { fees ->
|
ifRight = feeStateFactory::onFeeOnLoadedState,
|
||||||
feeStateFactory.onFeeOnLoadedState(fees, isAmountSubtractAvailable)
|
ifLeft = { feeStateFactory.onFeeOnErrorState() },
|
||||||
},
|
|
||||||
ifLeft = {
|
|
||||||
feeStateFactory.onFeeOnErrorState()
|
|
||||||
},
|
|
||||||
) ?: feeStateFactory.onFeeOnErrorState()
|
) ?: feeStateFactory.onFeeOnErrorState()
|
||||||
updateFeeNotifications()
|
updateFeeNotifications()
|
||||||
}.saveIn(feeJobHolder)
|
}.saveIn(feeJobHolder)
|
||||||
|
|
@ -587,16 +596,19 @@ internal class SendViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showAmount() {
|
override fun showAmount() {
|
||||||
|
uiState = stateFactory.onSubtractSelect(false)
|
||||||
stateRouter.showAmount(isFromConfirmation = true)
|
stateRouter.showAmount(isFromConfirmation = true)
|
||||||
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Amount))
|
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Amount))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showRecipient() {
|
override fun showRecipient() {
|
||||||
|
uiState = stateFactory.onSubtractSelect(false)
|
||||||
stateRouter.showRecipient(isFromConfirmation = true)
|
stateRouter.showRecipient(isFromConfirmation = true)
|
||||||
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Address))
|
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Address))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showFee() {
|
override fun showFee() {
|
||||||
|
uiState = stateFactory.onSubtractSelect(false)
|
||||||
stateRouter.showFee(isFromConfirmation = true)
|
stateRouter.showFee(isFromConfirmation = true)
|
||||||
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Fee))
|
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Fee))
|
||||||
}
|
}
|
||||||
|
|
@ -623,12 +635,12 @@ internal class SendViewModel @Inject constructor(
|
||||||
private fun verifyAndSendTransaction() {
|
private fun verifyAndSendTransaction() {
|
||||||
val recipient = uiState.recipientState?.addressTextField?.value ?: return
|
val recipient = uiState.recipientState?.addressTextField?.value ?: return
|
||||||
val feeState = uiState.feeState ?: 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 memo = uiState.recipientState?.memoTextField?.value
|
||||||
val fee = feeStateFactory.feeConverter.convert(feeSelectorState)
|
|
||||||
val amountValue = uiState.amountState?.amountTextField?.cryptoAmount?.value ?: return
|
val amountValue = uiState.amountState?.amountTextField?.cryptoAmount?.value ?: return
|
||||||
val amountToSend = if (feeState.isSubtract && isAmountSubtractAvailable) {
|
val amountToSend = if (uiState.sendState.isSubtract && isAmountSubtractAvailable) {
|
||||||
feeState.receivedAmountValue
|
val feeValue = fee.amount.value ?: return
|
||||||
|
amountValue.minus(feeValue)
|
||||||
} else {
|
} else {
|
||||||
amountValue
|
amountValue
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1119,7 +1119,7 @@ internal class StateBuilder(
|
||||||
},
|
},
|
||||||
readMoreUrl = buildReadMoreUrl(),
|
readMoreUrl = buildReadMoreUrl(),
|
||||||
feeItems = txFeeState.toFeeItemState(),
|
feeItems = txFeeState.toFeeItemState(),
|
||||||
readMore = resourceReference(R.string.common_fee_selector_link_description),
|
readMore = resourceReference(R.string.common_read_more),
|
||||||
onReadMoreClick = actions.onFeeReadMoreClick,
|
onReadMoreClick = actions.onFeeReadMoreClick,
|
||||||
)
|
)
|
||||||
return uiState.copy(
|
return uiState.copy(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue