Updated on 2026-08-14

This commit is contained in:
Tangem 2024-04-11 19:59:05 +05:00
parent 1264ada90d
commit 08ddee9acc
9 changed files with 73 additions and 76 deletions

View file

@ -58,6 +58,16 @@ internal sealed class SendAlertState {
override val confirmButtonText: TextReference = resourceReference(R.string.common_continue)
}
data class FeeTooHigh(
val times: String,
override val onConfirmClick: () -> Unit,
) : SendAlertState() {
override val title: TextReference? = null
override val message: TextReference =
resourceReference(id = R.string.send_alert_fee_too_high_text, wrappedList(times))
override val confirmButtonText: TextReference = resourceReference(R.string.common_continue)
}
data class FeeCoverage(
val amount: String,
override val onConfirmClick: (() -> Unit),

View file

@ -115,6 +115,20 @@ internal class SendEventStateFactory(
)
}
fun getFeeTooHighAlert(diff: String, onConsume: () -> Unit): SendUiState {
return currentStateProvider().copy(
event = triggeredEvent(
data = SendEvent.ShowAlert(
SendAlertState.FeeTooHigh(
onConfirmClick = clickIntents::showSend,
times = diff,
),
),
onConsume = onConsume,
),
)
}
fun getGenericErrorState(error: Throwable? = null, onConsume: () -> Unit): SendUiState {
val state = currentStateProvider()
return state.copy(

View file

@ -30,6 +30,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import java.math.BigDecimal
import java.math.BigInteger
@Suppress("LongParameterList")
internal class SendNotificationFactory(
@ -64,6 +65,7 @@ internal class SendNotificationFactory(
// warnings
addExistentialWarningNotification(feeAmount, amountValue)
addHighFeeWarningNotification(feeAmount, amountValue, sendState.ignoreAmountReduce)
addTooHighNotification(feeState.feeSelectorState)
addTooLowNotification(feeState)
}.toImmutableList()
}
@ -279,6 +281,18 @@ internal class SendNotificationFactory(
}
}
private fun MutableList<SendNotification>.addTooHighNotification(feeSelectorState: FeeSelectorState) {
if (feeSelectorState !is FeeSelectorState.Content) return
val multipleFees = feeSelectorState.fees as? TransactionFee.Choosable ?: return
val highValue = multipleFees.priority.amount.value ?: return
val customAmount = feeSelectorState.customValues.firstOrNull() ?: return
val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals)
val diff = (customValue / highValue).toBigInteger()
if (feeSelectorState.selectedFee == FeeType.Custom && diff > FEE_MAX_DIFF) {
add(SendNotification.Warning.TooHigh(diff.toString()))
}
}
private suspend fun MutableList<SendNotification>.addExceedsBalanceNotification(fee: Fee?) {
val feeValue = fee?.amount?.value ?: BigDecimal.ZERO
val userWalletId = userWalletProvider().walletId
@ -359,5 +373,6 @@ internal class SendNotificationFactory(
companion object {
private const val DOGECOIN_MINIMUM = "0.01"
private val TEZOS_FEE_THRESHOLD = BigDecimal("0.01")
internal val FEE_MAX_DIFF = BigInteger("5")
}
}

View file

@ -4,6 +4,7 @@ import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.core.ui.utils.parseToBigDecimal
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.impl.presentation.state.SendUiState
import com.tangem.features.send.impl.presentation.state.confirm.SendNotificationFactory
/**
* Check if sending amount with fee is greater than balance
@ -26,4 +27,19 @@ internal fun checkIfFeeTooLow(state: SendUiState): Boolean {
val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals)
return feeSelectorState.selectedFee == FeeType.Custom && minimumValue > customValue
}
/**
* Check if custom fee is too high
*/
internal fun checkIfFeeTooHigh(state: SendUiState, onShow: (String) -> Unit): Boolean {
val feeSelectorState = state.feeState?.feeSelectorState as? FeeSelectorState.Content ?: return false
val multipleFees = feeSelectorState.fees as? TransactionFee.Choosable ?: return false
val highValue = multipleFees.priority.amount.value ?: return false
val customAmount = feeSelectorState.customValues.firstOrNull() ?: return false
val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals)
val diff = (customValue / highValue).toBigInteger()
val isShow = feeSelectorState.selectedFee == FeeType.Custom && diff > SendNotificationFactory.FEE_MAX_DIFF
if (isShow) onShow(diff.toString())
return isShow
}

View file

@ -1,20 +1,15 @@
package com.tangem.features.send.impl.presentation.state.fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.core.ui.utils.parseToBigDecimal
import com.tangem.features.send.impl.presentation.state.SendNotification
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.StateRouter
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
import com.tangem.features.send.impl.presentation.state.SendNotification
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
import com.tangem.utils.Provider
import com.tangem.utils.toFormattedString
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import java.math.BigDecimal
@Suppress("LongParameterList")
internal class FeeNotificationFactory(
@ -29,16 +24,7 @@ internal class FeeNotificationFactory(
val state = currentStateProvider()
val feeState = state.feeState ?: return@map persistentListOf()
buildList {
when (val feeSelectorState = feeState.feeSelectorState) {
FeeSelectorState.Error -> {
addFeeUnreachableNotification(feeSelectorState)
}
is FeeSelectorState.Content -> {
val customFee = feeSelectorState.customValues
val selectedFee = feeSelectorState.selectedFee
addTooHighNotification(feeSelectorState.fees, selectedFee, customFee)
}
}
addFeeUnreachableNotification(feeState.feeSelectorState)
}.toImmutableList()
}
@ -47,24 +33,4 @@ internal class FeeNotificationFactory(
add(SendNotification.Warning.NetworkFeeUnreachable(clickIntents::feeReload))
}
}
private fun MutableList<SendNotification>.addTooHighNotification(
transactionFee: TransactionFee,
selectedFee: FeeType,
customFee: List<SendTextField.CustomFee>,
) {
val multipleFees = transactionFee as? TransactionFee.Choosable ?: return
val highValue = multipleFees.priority.amount.value ?: return
val customAmount = customFee.firstOrNull() ?: return
val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals)
val diff = customValue / highValue
if (selectedFee == FeeType.Custom && diff > FEE_MAX_DIFF) {
add(SendNotification.Warning.TooHigh(diff.toFormattedString(HIGH_FEE_DIFF_DECIMALS)))
}
}
companion object {
private val FEE_MAX_DIFF = BigDecimal(5)
private const val HIGH_FEE_DIFF_DECIMALS = 0
}
}

View file

@ -3,7 +3,6 @@ package com.tangem.features.send.impl.presentation.ui.fee
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.animation.*
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
@ -12,15 +11,12 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.core.ui.components.SpacerWMax
import com.tangem.core.ui.components.rows.SelectorRowItem
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.features.send.impl.R
import com.tangem.features.send.impl.presentation.state.SendNotification
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
@ -64,11 +60,6 @@ internal fun SendSpeedSelectorItem(
showDivider = showDivider,
)
SendSpeedSelectorItemError(isError = feeSelectorState is FeeSelectorState.Error)
if (feeType == FeeType.Custom) {
val showWarning = state.notifications.any { it is SendNotification.Warning.TooHigh }
WarningIcon(showWarning = showWarning)
}
}
}
}
@ -97,29 +88,6 @@ private fun SendSpeedSelectorItemError(isError: Boolean) {
}
}
@Composable
private fun WarningIcon(showWarning: Boolean = false) {
Row {
SpacerWMax()
AnimatedVisibility(
visible = showWarning,
label = "Custom fee warning indicator",
enter = fadeIn(),
exit = fadeOut(),
) {
Image(
painter = painterResource(R.drawable.ic_alert_triangle_20),
contentDescription = null,
modifier = Modifier
.padding(
vertical = TangemTheme.dimens.spacing12,
horizontal = TangemTheme.dimens.spacing14,
),
)
}
}
}
private fun FeeSelectorState.Content.getAmount(feeType: FeeType): Amount? {
val choosableFees = fees as? TransactionFee.Choosable
return when (feeType) {

View file

@ -525,7 +525,15 @@ internal class SendViewModel @Inject constructor(
)
return true
}
return false
return checkIfFeeTooHigh(
state = uiState,
onShow = { diff ->
uiState = eventStateFactory.getFeeTooHighAlert(
diff = diff,
onConsume = { uiState = eventStateFactory.onConsumeEventState() },
)
},
)
}
private fun onFeeCoverageAlert(): Boolean {