Updated on 2026-08-14
This commit is contained in:
parent
bf138e432b
commit
b7c6ea61a6
12 changed files with 112 additions and 57 deletions
|
|
@ -50,6 +50,14 @@ internal sealed class SendAlertState {
|
|||
override val message: TextReference = resourceReference(id = R.string.send_notification_high_fee_title)
|
||||
}
|
||||
|
||||
data class FeeTooLow(
|
||||
override val onConfirmClick: () -> Unit,
|
||||
) : SendAlertState() {
|
||||
override val title: TextReference? = null
|
||||
override val message: TextReference = resourceReference(id = R.string.send_alert_fee_too_low_text)
|
||||
override val confirmButtonText: TextReference = resourceReference(R.string.common_continue)
|
||||
}
|
||||
|
||||
data class FeeCoverage(
|
||||
override val onConfirmClick: (() -> Unit),
|
||||
) : SendAlertState() {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.send.impl.presentation.state
|
|||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.event.triggeredEvent
|
||||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.transaction.error.SendTransactionError
|
||||
import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState
|
||||
import com.tangem.features.send.impl.presentation.state.fee.FeeStateFactory
|
||||
|
|
@ -88,6 +89,29 @@ internal class SendEventStateFactory(
|
|||
}
|
||||
}
|
||||
|
||||
fun getFeeTooLowAlert(onConsume: () -> Unit): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val feeSelectorState = state.feeState?.feeSelectorState as? FeeSelectorState.Content ?: return state
|
||||
val multipleFees = feeSelectorState.fees as? TransactionFee.Choosable ?: return state
|
||||
val minimumValue = multipleFees.minimum.amount.value ?: return state
|
||||
val customAmount = feeSelectorState.customValues.firstOrNull() ?: return state
|
||||
val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals)
|
||||
|
||||
val isFeeTooLow = feeSelectorState.selectedFee == FeeType.Custom && minimumValue > customValue
|
||||
if (!isFeeTooLow) return state
|
||||
|
||||
return state.copy(
|
||||
event = triggeredEvent(
|
||||
data = SendEvent.ShowAlert(
|
||||
SendAlertState.FeeTooLow(
|
||||
onConfirmClick = clickIntents::showSend,
|
||||
),
|
||||
),
|
||||
onConsume = onConsume,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun getGenericErrorState(error: Throwable? = null, onConsume: () -> Unit): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
return state.copy(
|
||||
|
|
|
|||
|
|
@ -12,12 +12,14 @@ internal sealed class SendNotification(val config: NotificationConfig) {
|
|||
title: TextReference,
|
||||
subtitle: TextReference,
|
||||
buttonState: NotificationConfig.ButtonsState? = null,
|
||||
onCloseClick: (() -> Unit)? = null,
|
||||
) : SendNotification(
|
||||
config = NotificationConfig(
|
||||
title = title,
|
||||
subtitle = subtitle,
|
||||
iconResId = R.drawable.ic_alert_24,
|
||||
buttonsState = buttonState,
|
||||
onCloseClick = onCloseClick,
|
||||
),
|
||||
) {
|
||||
|
||||
|
|
@ -45,12 +47,17 @@ internal sealed class SendNotification(val config: NotificationConfig) {
|
|||
val cryptoCurrency: String,
|
||||
val utxoLimit: String,
|
||||
val amountLimit: String,
|
||||
val onConfirmClick: () -> Unit,
|
||||
) : Error(
|
||||
title = resourceReference(R.string.send_notifiaction_transaction_limit_title),
|
||||
subtitle = resourceReference(
|
||||
R.string.send_notifiaction_transaction_limit_text,
|
||||
R.string.send_notification_transaction_limit_text,
|
||||
wrappedList(cryptoCurrency, utxoLimit, amountLimit),
|
||||
),
|
||||
buttonState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
|
||||
text = resourceReference(R.string.send_notification_reduce_to, wrappedList(amountLimit)),
|
||||
onClick = onConfirmClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -58,27 +65,28 @@ internal sealed class SendNotification(val config: NotificationConfig) {
|
|||
title: TextReference,
|
||||
subtitle: TextReference,
|
||||
buttonsState: NotificationConfig.ButtonsState? = null,
|
||||
onCloseClick: (() -> Unit)? = null,
|
||||
) : SendNotification(
|
||||
config = NotificationConfig(
|
||||
title = title,
|
||||
subtitle = subtitle,
|
||||
iconResId = R.drawable.img_attention_20,
|
||||
buttonsState = buttonsState,
|
||||
onCloseClick = onCloseClick,
|
||||
),
|
||||
) {
|
||||
data class HighFeeError(
|
||||
val amount: String,
|
||||
val onConfirmClick: () -> Unit,
|
||||
val onDismissClick: () -> Unit,
|
||||
val onCloseClick: () -> Unit,
|
||||
) : Warning(
|
||||
title = resourceReference(R.string.send_notification_high_fee_title),
|
||||
subtitle = resourceReference(R.string.send_notification_high_fee_text, wrappedList(amount)),
|
||||
buttonsState = NotificationConfig.ButtonsState.PairButtonsConfig(
|
||||
primaryText = resourceReference(R.string.send_notification_fee_too_high_accept, wrappedList(amount)),
|
||||
onPrimaryClick = onConfirmClick,
|
||||
secondaryText = resourceReference(R.string.send_notification_fee_too_high_ignore),
|
||||
onSecondaryClick = onDismissClick,
|
||||
buttonsState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
|
||||
text = resourceReference(R.string.send_notification_reduce_by, wrappedList(amount)),
|
||||
onClick = onConfirmClick,
|
||||
),
|
||||
onCloseClick = onCloseClick,
|
||||
)
|
||||
|
||||
data class ExistentialDeposit(val deposit: String) : Warning(
|
||||
|
|
@ -96,5 +104,10 @@ internal sealed class SendNotification(val config: NotificationConfig) {
|
|||
formatArgs = wrappedList(amountReducedBy, amountReduced),
|
||||
),
|
||||
)
|
||||
|
||||
data object FeeTooLow : Warning(
|
||||
title = resourceReference(id = R.string.send_notification_transaction_delay_title),
|
||||
subtitle = resourceReference(id = R.string.send_notification_transaction_delay_text),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,15 @@
|
|||
package com.tangem.features.send.impl.presentation.state
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
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.repository.CurrencyChecksRepository
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
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.viewmodel.SendClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.isNullOrZero
|
||||
|
|
@ -48,13 +52,16 @@ internal class SendNotificationFactory(
|
|||
addFeeCoverageNotification(sendState.isSubtract, sendAmount)
|
||||
addExistentialWarningNotification(feeAmount, sendAmount)
|
||||
addHighFeeWarningNotification(sendAmount, sendState.ignoreAmountReduce)
|
||||
addTooLowNotification(feeState)
|
||||
}.toImmutableList()
|
||||
}
|
||||
|
||||
fun dismissHighFeeWarningState(): SendUiState {
|
||||
fun dismissNotificationState(clazz: Class<out SendNotification>): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val sendState = state.sendState
|
||||
val updatedNotifications = sendState.notifications.filterNot { it is SendNotification.Warning.HighFeeError }
|
||||
val notificationsToRemove = sendState.notifications.filterIsInstance(clazz)
|
||||
val updatedNotifications = sendState.notifications.toMutableList()
|
||||
updatedNotifications.removeAll(notificationsToRemove)
|
||||
return state.copy(
|
||||
sendState = sendState.copy(
|
||||
ignoreAmountReduce = true,
|
||||
|
|
@ -162,6 +169,13 @@ internal class SendNotificationFactory(
|
|||
cryptoAmount = utxoLimit.maxAmount,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
),
|
||||
onConfirmClick = {
|
||||
val reduceTo = utxoLimit.maxAmount.toPlainString()
|
||||
clickIntents.onAmountReduceClick(
|
||||
reduceTo,
|
||||
SendNotification.Error.TransactionLimitError::class.java,
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -207,9 +221,11 @@ internal class SendNotificationFactory(
|
|||
amount = TEZOS_FEE_THRESHOLD.toPlainString(),
|
||||
onConfirmClick = {
|
||||
val reduceTo = sendAmount.minus(TEZOS_FEE_THRESHOLD).toPlainString()
|
||||
clickIntents.onAmountReduceClick(reduceTo)
|
||||
clickIntents.onAmountReduceClick(reduceTo, SendNotification.Warning.HighFeeError::class.java)
|
||||
},
|
||||
onCloseClick = {
|
||||
clickIntents.onNotificationCancel(SendNotification.Warning.HighFeeError::class.java)
|
||||
},
|
||||
onDismissClick = clickIntents::onAmountReduceIgnoreClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -261,6 +277,17 @@ internal class SendNotificationFactory(
|
|||
}
|
||||
}
|
||||
|
||||
private fun MutableList<SendNotification>.addTooLowNotification(feeState: SendStates.FeeState) {
|
||||
val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return
|
||||
val multipleFees = feeSelectorState.fees as? TransactionFee.Choosable ?: return
|
||||
val minimumValue = multipleFees.minimum.amount.value ?: return
|
||||
val customAmount = feeSelectorState.customValues.firstOrNull() ?: return
|
||||
val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals)
|
||||
if (feeSelectorState.selectedFee == FeeType.Custom && minimumValue > customValue) {
|
||||
add(SendNotification.Warning.FeeTooLow)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val CARDANO_MINIMUM = "1"
|
||||
private const val DOGECOIN_MINIMUM = "0.01"
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ internal class FeeNotificationFactory(
|
|||
is FeeSelectorState.Content -> {
|
||||
val customFee = feeSelectorState.customValues
|
||||
val selectedFee = feeSelectorState.selectedFee
|
||||
addTooLowNotification(feeSelectorState.fees, selectedFee, customFee)
|
||||
addTooHighNotification(feeSelectorState.fees, selectedFee, customFee)
|
||||
addExceedsBalanceNotification(feeState.fee)
|
||||
}
|
||||
|
|
@ -61,20 +60,6 @@ internal class FeeNotificationFactory(
|
|||
}
|
||||
}
|
||||
|
||||
private fun MutableList<SendFeeNotification>.addTooLowNotification(
|
||||
transactionFee: TransactionFee,
|
||||
selectedFee: FeeType,
|
||||
customFee: List<SendTextField.CustomFee>,
|
||||
) {
|
||||
val multipleFees = transactionFee as? TransactionFee.Choosable ?: return
|
||||
val minimumValue = multipleFees.minimum.amount.value ?: return
|
||||
val customAmount = customFee.firstOrNull() ?: return
|
||||
val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals)
|
||||
if (selectedFee == FeeType.Custom && minimumValue > customValue) {
|
||||
add(SendFeeNotification.Warning.TooLow)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<SendFeeNotification>.addTooHighNotification(
|
||||
transactionFee: TransactionFee,
|
||||
selectedFee: FeeType,
|
||||
|
|
|
|||
|
|
@ -20,11 +20,6 @@ sealed class SendFeeNotification(val config: NotificationConfig) {
|
|||
buttonsState = buttonsState,
|
||||
),
|
||||
) {
|
||||
object TooLow : Warning(
|
||||
title = resourceReference(id = R.string.send_notification_transaction_delay_title),
|
||||
subtitle = resourceReference(id = R.string.send_notification_transaction_delay_text),
|
||||
)
|
||||
|
||||
data class TooHigh(
|
||||
val value: String,
|
||||
) : Warning(
|
||||
|
|
|
|||
|
|
@ -74,10 +74,7 @@ private fun LazyListScope.middleNotifications(
|
|||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
notifications(
|
||||
configs = configs.filter {
|
||||
it is SendFeeNotification.Warning.TooLow ||
|
||||
it is SendFeeNotification.Warning.TooHigh
|
||||
}.toImmutableList(),
|
||||
configs = configs.filterIsInstance<SendFeeNotification.Warning.TooHigh>().toImmutableList(),
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,10 +117,7 @@ internal fun SendSpeedSelector(
|
|||
visible = fees.normal is Fee.Ethereum,
|
||||
label = "Custom fee appearance animation",
|
||||
) {
|
||||
val showWarning = state.notifications.any {
|
||||
it is SendFeeNotification.Warning.TooHigh ||
|
||||
it is SendFeeNotification.Warning.TooLow
|
||||
}
|
||||
val showWarning = state.notifications.any { it is SendFeeNotification.Warning.TooHigh }
|
||||
SendSpeedSelectorItem(
|
||||
titleRes = R.string.common_fee_selector_option_custom,
|
||||
iconRes = R.drawable.ic_edit_24,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.features.send.impl.presentation.viewmodel
|
|||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.send.impl.presentation.analytics.EnterAddressSource
|
||||
import com.tangem.features.send.impl.presentation.state.SendNotification
|
||||
import com.tangem.features.send.impl.presentation.state.fee.FeeType
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
|
|
@ -57,12 +58,14 @@ internal interface SendClickIntents {
|
|||
|
||||
fun showFee()
|
||||
|
||||
fun showSend()
|
||||
|
||||
fun onExploreClick()
|
||||
|
||||
fun onShareClick()
|
||||
|
||||
fun onAmountReduceClick(reducedAmount: String)
|
||||
fun onAmountReduceClick(reducedAmount: String, clazz: Class<out SendNotification>)
|
||||
|
||||
fun onAmountReduceIgnoreClick()
|
||||
fun onNotificationCancel(clazz: Class<out SendNotification>)
|
||||
// endregion
|
||||
}
|
||||
|
|
@ -394,6 +394,9 @@ internal class SendViewModel @Inject constructor(
|
|||
val currentState = stateRouter.currentState.value
|
||||
val isCurrentFee = currentState.type == SendUiStateType.Fee
|
||||
if (isCurrentFee) {
|
||||
uiState = eventStateFactory.getFeeTooLowAlert(
|
||||
onConsume = { uiState = eventStateFactory.onConsumeEventState() },
|
||||
)
|
||||
val isFeeCoverage = checkFeeCoverage(uiState, cryptoCurrencyStatus)
|
||||
if (isAmountSubtractAvailable && isFeeCoverage) {
|
||||
uiState = eventStateFactory.getFeeCoverageAlert(
|
||||
|
|
@ -606,6 +609,10 @@ internal class SendViewModel @Inject constructor(
|
|||
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Fee))
|
||||
}
|
||||
|
||||
override fun showSend() {
|
||||
stateRouter.showSend()
|
||||
}
|
||||
|
||||
override fun onExploreClick() {
|
||||
analyticsEventHandler.send(SendAnalyticEvents.ExploreButtonClicked)
|
||||
innerRouter.openUrl(uiState.sendState.txUrl)
|
||||
|
|
@ -615,14 +622,14 @@ internal class SendViewModel @Inject constructor(
|
|||
analyticsEventHandler.send(SendAnalyticEvents.ShareButtonClicked)
|
||||
}
|
||||
|
||||
override fun onAmountReduceClick(reducedAmount: String) {
|
||||
override fun onAmountReduceClick(reducedAmount: String, clazz: Class<out SendNotification>) {
|
||||
uiState = amountStateFactory.getOnAmountValueChange(reducedAmount)
|
||||
uiState = sendNotificationFactory.dismissHighFeeWarningState()
|
||||
uiState = sendNotificationFactory.dismissNotificationState(clazz)
|
||||
loadFee()
|
||||
}
|
||||
|
||||
override fun onAmountReduceIgnoreClick() {
|
||||
uiState = sendNotificationFactory.dismissHighFeeWarningState()
|
||||
override fun onNotificationCancel(clazz: Class<out SendNotification>) {
|
||||
uiState = sendNotificationFactory.dismissNotificationState(clazz)
|
||||
}
|
||||
|
||||
private fun verifyAndSendTransaction() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue