Updated on 2026-08-14

This commit is contained in:
Tangem 2024-11-27 15:23:46 +05:00
parent d0d9fcfef8
commit d772beda45
11 changed files with 90 additions and 41 deletions

View file

@ -4,6 +4,7 @@ import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.feature.swap.domain.models.SwapAmount
import com.tangem.feature.swap.domain.models.ui.TxFee
import java.math.BigDecimal
data class UiActions(
val onSearchEntered: (String) -> Unit,
@ -15,8 +16,8 @@ data class UiActions(
val onChangeCardsClicked: () -> Unit,
val onBackClicked: () -> Unit,
val onMaxAmountSelected: () -> Unit,
val onReduceAmount: (SwapAmount) -> Unit,
val onLeaveExistentialDeposit: (SwapAmount) -> Unit,
val onReduceToAmount: (SwapAmount) -> Unit,
val onReduceByAmount: (SwapAmount, reduceBy: BigDecimal) -> Unit,
val openPermissionBottomSheet: () -> Unit,
val onChangeApproveType: (ApproveType) -> Unit,
// region new actions

View file

@ -134,10 +134,11 @@ internal class SwapNotificationsFactory(
) {
val fromCurrencyStatus = quoteModel.fromTokenInfo.cryptoCurrencyStatus
val includeFeeInAmount = quoteModel.preparedSwapConfigState.includeFeeInAmount
val amount = quoteModel.fromTokenInfo.tokenAmount
val amountToRequest = if (includeFeeInAmount is IncludeFeeInAmount.Included) {
includeFeeInAmount.amountSubtractFee
} else {
quoteModel.fromTokenInfo.tokenAmount
amount
}
val fee = when (val feeState = quoteModel.txFee) {
TxFeeState.Empty -> null
@ -154,13 +155,12 @@ internal class SwapNotificationsFactory(
addExistentialWarningNotification(
existentialDeposit = quoteModel.currencyCheck?.existentialDeposit,
feeAmount = fee?.feeValue.orZero(),
receivedAmount = amountToRequest.value,
sendingAmount = amount.value,
cryptoCurrencyStatus = fromCurrencyStatus,
onReduceClick = { _, reduceByDiff, _ ->
actions.onLeaveExistentialDeposit(
amountToRequest.copy(
value = amountToRequest.value.minus(reduceByDiff).minus(fee?.feeValue.orZero()),
),
onReduceClick = { reduceBy, reduceByDiff, _ ->
actions.onReduceByAmount(
amount.copy(value = amount.value.minus(reduceByDiff)),
reduceBy,
)
},
)
@ -170,7 +170,7 @@ internal class SwapNotificationsFactory(
cryptoCurrency = fromCurrencyStatus.currency,
minAdaValue = quoteModel.minAdaValue,
onReduceClick = { reduceTo, _ ->
actions.onLeaveExistentialDeposit(amountToRequest.copy(value = reduceTo))
actions.onReduceToAmount(amount.copy(value = reduceTo))
},
)
if (!isCardano) {
@ -191,13 +191,13 @@ internal class SwapNotificationsFactory(
addReduceAmountNotification(
cryptoCurrencyStatus = fromCurrencyStatus,
fromAmount = quoteModel.fromTokenInfo.tokenAmount,
onReduceAmount = actions.onReduceAmount,
onReduceByAmount = actions.onReduceByAmount,
)
addTransactionLimitErrorNotification(
utxoLimit = quoteModel.currencyCheck?.utxoAmountLimit,
cryptoCurrency = fromCurrencyStatus.currency,
onReduceClick = { reduceTo, _ ->
actions.onReduceAmount(amountToRequest.copy(value = reduceTo))
actions.onReduceToAmount(amountToRequest.copy(value = reduceTo))
},
)
}
@ -294,7 +294,7 @@ internal class SwapNotificationsFactory(
private fun MutableList<NotificationUM>.addReduceAmountNotification(
cryptoCurrencyStatus: CryptoCurrencyStatus,
fromAmount: SwapAmount,
onReduceAmount: (SwapAmount) -> Unit,
onReduceByAmount: (SwapAmount, BigDecimal) -> Unit,
) {
val isTezos = isTezos(cryptoCurrencyStatus.currency.network.id.value)
val balance = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO
@ -309,7 +309,7 @@ internal class SwapNotificationsFactory(
val patchedAmount = fromAmount.copy(
value = fromAmount.value - threshold,
)
onReduceAmount(patchedAmount)
onReduceByAmount(patchedAmount, threshold)
},
),
)

View file

@ -7,6 +7,7 @@ import com.tangem.feature.swap.domain.models.ui.RequestApproveStateData
import com.tangem.feature.swap.domain.models.ui.SwapState
import com.tangem.feature.swap.domain.models.ui.TokensDataStateExpress
import com.tangem.feature.swap.domain.models.ui.TxFee
import java.math.BigDecimal
data class SwapProcessDataState(
// Initial network id
@ -15,6 +16,7 @@ data class SwapProcessDataState(
val feePaidCryptoCurrency: CryptoCurrencyStatus? = null,
// Amount from input
val amount: String? = null,
val reduceBalanceBy: BigDecimal = BigDecimal.ZERO,
val approveDataModel: RequestApproveStateData? = null,
val swapDataModel: SwapDataModel? = null,
val selectedFee: TxFee? = null,

View file

@ -25,11 +25,7 @@ import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.GetCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase
import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.Network
@ -144,6 +140,7 @@ internal class SwapViewModel @Inject constructor(
// shows currency order (direct - swap initial to selected, reversed = selected to initial)
private var isOrderReversed = false
private val lastAmount = mutableStateOf(INITIAL_AMOUNT)
private val lastReducedBalanceBy = mutableStateOf(BigDecimal.ZERO)
private var swapRouter: SwapRouter by Delegates.notNull()
private val isUserResolvableError: (SwapState) -> Boolean = {
@ -304,6 +301,7 @@ internal class SwapViewModel @Inject constructor(
fromToken = fromCurrencyStatus,
toToken = toCurrencyStatus,
amount = lastAmount.value,
reduceBalanceBy = lastReducedBalanceBy.value,
toProvidersList = findSwapProviders(fromCurrencyStatus, toCurrencyStatus),
)
}
@ -321,6 +319,7 @@ internal class SwapViewModel @Inject constructor(
fromToken: CryptoCurrencyStatus,
toToken: CryptoCurrencyStatus,
amount: String,
reduceBalanceBy: BigDecimal,
toProvidersList: List<SwapProvider>,
isSilent: Boolean = false,
) {
@ -339,6 +338,7 @@ internal class SwapViewModel @Inject constructor(
fromToken = fromToken,
toToken = toToken,
amount = amount,
reduceBalanceBy = reduceBalanceBy,
toProvidersList = toProvidersList,
),
)
@ -354,6 +354,7 @@ internal class SwapViewModel @Inject constructor(
toToken = toCurrency,
amount = amount,
isSilent = isSilent,
reduceBalanceBy = dataState.reduceBalanceBy,
toProvidersList = findSwapProviders(fromCurrency, toCurrency),
)
}
@ -363,6 +364,7 @@ internal class SwapViewModel @Inject constructor(
fromToken: CryptoCurrencyStatus,
toToken: CryptoCurrencyStatus,
amount: String,
reduceBalanceBy: BigDecimal,
toProvidersList: List<SwapProvider>,
): PeriodicTask<Map<SwapProvider, SwapState>> {
return PeriodicTask(
@ -372,6 +374,7 @@ internal class SwapViewModel @Inject constructor(
runCatching(dispatchers.io) {
dataState = dataState.copy(
amount = amount,
reduceBalanceBy = reduceBalanceBy,
swapDataModel = null,
approveDataModel = null,
)
@ -380,6 +383,7 @@ internal class SwapViewModel @Inject constructor(
toToken = toToken,
providers = toProvidersList,
amountToSwap = amount,
reduceBalanceBy = reduceBalanceBy,
selectedFee = dataState.selectedFee?.feeType ?: FeeType.NORMAL,
)
}
@ -817,6 +821,7 @@ internal class SwapViewModel @Inject constructor(
fromToken = fromToken,
toToken = toToken,
amount = lastAmount.value,
reduceBalanceBy = lastReducedBalanceBy.value,
toProvidersList = findSwapProviders(fromToken, toToken),
)
swapRouter.openScreen(SwapNavScreen.Main)
@ -886,6 +891,7 @@ internal class SwapViewModel @Inject constructor(
).getOrNull()
val decimals = newFromToken.currency.decimals
lastAmount.value = cutAmountWithDecimals(decimals, lastAmount.value)
lastReducedBalanceBy.value = BigDecimal.ZERO
uiState = stateBuilder.updateSwapAmount(
uiState = uiState,
amountFormatted = inputNumberFormatter.formatWithThousands(lastAmount.value, decimals),
@ -897,13 +903,18 @@ internal class SwapViewModel @Inject constructor(
fromToken = newFromToken,
toToken = newToToken,
amount = lastAmount.value,
reduceBalanceBy = lastReducedBalanceBy.value,
toProvidersList = findSwapProviders(newFromToken, newToToken),
)
}
}
}
private fun onAmountChanged(value: String) {
private fun onAmountChanged(
value: String,
forceQuotesUpdate: Boolean = false,
reduceBalanceBy: BigDecimal = BigDecimal.ZERO,
) {
viewModelScope.launch {
val fromToken = dataState.fromCryptoCurrency
val toToken = dataState.toCryptoCurrency
@ -915,6 +926,7 @@ internal class SwapViewModel @Inject constructor(
fromToken,
).getOrNull()
lastAmount.value = cutValue
lastReducedBalanceBy.value = reduceBalanceBy
uiState = stateBuilder.updateSwapAmount(
uiState = uiState,
amountFormatted = inputNumberFormatter.formatWithThousands(cutValue, decimals),
@ -928,11 +940,12 @@ internal class SwapViewModel @Inject constructor(
isAmountChangedByUser = true
}
amountDebouncer.debounce(viewModelScope, DEBOUNCE_AMOUNT_DELAY) {
amountDebouncer.debounce(viewModelScope, DEBOUNCE_AMOUNT_DELAY, forceUpdate = forceQuotesUpdate) {
startLoadingQuotes(
fromToken = fromToken,
toToken = toToken,
amount = lastAmount.value,
reduceBalanceBy = lastReducedBalanceBy.value,
toProvidersList = findSwapProviders(fromToken, toToken),
)
}
@ -948,12 +961,12 @@ internal class SwapViewModel @Inject constructor(
}
}
private fun onReduceAmountClicked(newAmount: SwapAmount) {
onAmountChanged(newAmount.formatToUIRepresentation())
}
private fun onLeaveExistentialDepositClicked(newAmount: SwapAmount) {
onAmountChanged(newAmount.formatToUIRepresentation())
private fun onReduceAmountClicked(newAmount: SwapAmount, reduceBalanceBy: BigDecimal = BigDecimal.ZERO) {
onAmountChanged(
value = newAmount.formatToUIRepresentation(),
forceQuotesUpdate = true,
reduceBalanceBy = reduceBalanceBy,
)
}
private fun onAmountSelected(selected: Boolean) {
@ -1014,8 +1027,8 @@ internal class SwapViewModel @Inject constructor(
onSearchEntered("")
},
onMaxAmountSelected = ::onMaxAmountClicked,
onReduceAmount = ::onReduceAmountClicked,
onLeaveExistentialDeposit = ::onLeaveExistentialDepositClicked,
onReduceToAmount = ::onReduceAmountClicked,
onReduceByAmount = ::onReduceAmountClicked,
openPermissionBottomSheet = {
singleTaskScheduler.cancelTask()
analyticsEventHandler.send(SwapEvents.ButtonGivePermissionClicked)
@ -1054,6 +1067,7 @@ internal class SwapViewModel @Inject constructor(
selectedFee = it.feeType,
fromToken = fromToken,
amountToSwap = amountToSwap,
reduceBalanceBy = dataState.reduceBalanceBy,
)
setupLoadedState(selectedProvider, updatedState, fromToken)
}