Updated on 2026-08-14
This commit is contained in:
parent
3e7825b2e3
commit
eadc650d26
9 changed files with 133 additions and 33 deletions
|
|
@ -536,6 +536,9 @@
|
|||
<string name="swapping_give_permission">Дать разрешение</string>
|
||||
<string name="swapping_high_price_impact_description">Обмен этой суммы выбранных токенов может вызвать значительные колебания цены и уменьшить получаемую сумму.</string>
|
||||
<string name="swapping_insufficient_funds">Недостаточно средств</string>
|
||||
<string name="swapping_alert_title">Комиссии</string>
|
||||
<string name="swapping_alert_cex_description">В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %s от биржи обратно на адрес пользователя</string>
|
||||
<string name="swapping_alert_dex_description">В сумму включена комиссия провайдера сервиса.</string>
|
||||
<string name="swapping_not_enough_funds_for_fee">Недостаточно средств для оплаты комиссии на вашем %1$s кошельке для создания транзакции. Сначала пополните свой %2$s кошелек.</string>
|
||||
<string name="swapping_pending_transaction_subtitle">Транзакция в процессе…</string>
|
||||
<string name="swapping_pending_transaction_title">Подождите</string>
|
||||
|
|
|
|||
|
|
@ -549,6 +549,9 @@
|
|||
<string name="swapping_give_permission">Give Permission</string>
|
||||
<string name="swapping_high_price_impact_description">Swapping this amount of selected tokens will cause a significant price impact and reduce your outcome.</string>
|
||||
<string name="swapping_insufficient_funds">Insufficient funds</string>
|
||||
<string name="swapping_alert_title">Fees</string>
|
||||
<string name="swapping_alert_cex_description">The amount includes:\n• service provider\'s fee\n• network fee for sending %s from the exchange back to the user\'s address.</string>
|
||||
<string name="swapping_alert_dex_description">The amount includes the service provider\'s fee.</string>
|
||||
<string name="swapping_not_enough_funds_for_fee">Insufficient funds in your %1$s wallet to cover fees. Top up your %2$s wallet first.</string>
|
||||
<string name="swapping_pending_transaction_subtitle">Transaction in progress...</string>
|
||||
<string name="swapping_pending_transaction_title">Waiting</string>
|
||||
|
|
|
|||
|
|
@ -42,10 +42,12 @@ sealed class PriceImpact {
|
|||
|
||||
abstract val value: Float
|
||||
|
||||
fun getIntPercentValue() = (value * HUNDRED_PERCENTS).toInt()
|
||||
data class Empty(override val value: Float = 0f) : PriceImpact()
|
||||
|
||||
data class Value(override val value: Float) : PriceImpact()
|
||||
|
||||
fun getIntPercentValue() = (value * HUNDRED_PERCENTS).toInt()
|
||||
|
||||
companion object {
|
||||
private const val HUNDRED_PERCENTS = 100
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ sealed interface TransactionCardType {
|
|||
) : TransactionCardType
|
||||
|
||||
data class ReadOnly(
|
||||
val showWarning: Boolean = false,
|
||||
val onWarningClick: (() -> Unit)? = null,
|
||||
@StringRes override val headerResId: Int = R.string.swapping_to_title,
|
||||
) : TransactionCardType
|
||||
|
|
@ -105,12 +106,12 @@ sealed interface SwapWarning {
|
|||
object InsufficientFunds : SwapWarning
|
||||
data class NoAvailableTokensToSwap(val notificationConfig: NotificationConfig) : SwapWarning
|
||||
data class GenericWarning(
|
||||
val title: TextReference? = null,
|
||||
val message: TextReference? = null,
|
||||
val type: GenericWarningType = GenericWarningType.OTHER,
|
||||
val shouldWrapMessage: Boolean = false,
|
||||
val onClick: () -> Unit,
|
||||
) : SwapWarning
|
||||
// data class RateExpired(val onClick: () -> Unit) : SwapWarning
|
||||
data class GeneralError(val notificationConfig: NotificationConfig) : SwapWarning
|
||||
data class UnableToCoverFeeWarning(val notificationConfig: NotificationConfig) : SwapWarning
|
||||
data class GeneralWarning(val notificationConfig: NotificationConfig) : SwapWarning
|
||||
|
|
|
|||
|
|
@ -233,7 +233,10 @@ internal class StateBuilder(
|
|||
isBalanceHidden = isBalanceHiddenProvider(),
|
||||
),
|
||||
receiveCardData = SwapCardState.SwapCardData(
|
||||
type = TransactionCardType.ReadOnly(actions.onReceiveCardWarningClick),
|
||||
type = TransactionCardType.ReadOnly(
|
||||
showWarning = true,
|
||||
actions.onReceiveCardWarningClick,
|
||||
),
|
||||
amountTextFieldValue = TextFieldValue(quoteModel.toTokenInfo.tokenAmount.formatToUIRepresentation()),
|
||||
amountEquivalent = getFormattedFiatAmount(quoteModel.toTokenInfo.amountFiat),
|
||||
token = toCurrencyStatus,
|
||||
|
|
@ -852,17 +855,30 @@ internal class StateBuilder(
|
|||
)
|
||||
}
|
||||
|
||||
fun createImpactAlert(
|
||||
fun createAlert(
|
||||
uiState: SwapStateHolder,
|
||||
isPriceImpact: Boolean,
|
||||
token: String,
|
||||
providerType: ExchangeProviderType,
|
||||
onAlertClick: () -> Unit,
|
||||
): SwapStateHolder {
|
||||
val message = when (providerType) {
|
||||
ExchangeProviderType.CEX -> resourceReference(R.string.express_cex_fee_explanation)
|
||||
ExchangeProviderType.DEX -> resourceReference(R.string.swapping_high_price_impact_description)
|
||||
ExchangeProviderType.CEX -> resourceReference(R.string.swapping_alert_cex_description, wrappedList(token))
|
||||
ExchangeProviderType.DEX -> {
|
||||
val refs = buildList {
|
||||
if (isPriceImpact) {
|
||||
add(resourceReference(R.string.swapping_high_price_impact_description))
|
||||
add(stringReference("\n\n"))
|
||||
}
|
||||
add(resourceReference(R.string.swapping_alert_dex_description))
|
||||
}
|
||||
|
||||
combinedReference(refs.toWrappedList())
|
||||
}
|
||||
}
|
||||
return uiState.copy(
|
||||
alert = SwapWarning.GenericWarning(
|
||||
title = resourceReference(R.string.swapping_alert_title),
|
||||
message = message,
|
||||
onClick = onAlertClick,
|
||||
type = GenericWarningType.OTHER,
|
||||
|
|
|
|||
|
|
@ -111,8 +111,13 @@ internal fun SwapScreenContent(state: SwapStateHolder, modifier: Modifier = Modi
|
|||
} else {
|
||||
state.alert.message?.resolveReference() ?: stringResource(id = R.string.common_unknown_error)
|
||||
}
|
||||
SimpleOkDialog(
|
||||
BasicDialog(
|
||||
title = state.alert.title?.resolveReference(),
|
||||
message = message,
|
||||
confirmButton = DialogButton(
|
||||
title = stringResource(id = R.string.common_ok),
|
||||
onClick = state.alert.onClick,
|
||||
),
|
||||
onDismissDialog = state.alert.onClick,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -274,29 +274,49 @@ private fun Content(
|
|||
SpacerH8()
|
||||
|
||||
if (amountEquivalent != null) {
|
||||
if (type is TransactionCardType.ReadOnly && priceImpact !is PriceImpact.Empty) {
|
||||
if (type is TransactionCardType.ReadOnly) {
|
||||
Row {
|
||||
Text(
|
||||
text = makePriceImpactBalanceWarning(amountEquivalent, priceImpact.getIntPercentValue()),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.body2,
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minHeight = TangemTheme.dimens.size20)
|
||||
.align(Alignment.CenterVertically),
|
||||
)
|
||||
SpacerW4()
|
||||
IconButton(
|
||||
onClick = {
|
||||
type.onWarningClick?.invoke()
|
||||
},
|
||||
modifier = Modifier.size(size = TangemTheme.dimens.size20),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_alert_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.attention,
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
if (priceImpact is PriceImpact.Value) {
|
||||
Text(
|
||||
text = makePriceImpactBalanceWarning(
|
||||
amountEquivalent,
|
||||
priceImpact.getIntPercentValue(),
|
||||
),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.body2,
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minHeight = TangemTheme.dimens.size20)
|
||||
.align(Alignment.CenterVertically),
|
||||
)
|
||||
} else {
|
||||
AnimatedContent(targetState = amountEquivalent, label = "") {
|
||||
Text(
|
||||
text = it,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.body2,
|
||||
modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size20),
|
||||
)
|
||||
}
|
||||
}
|
||||
if (type.showWarning) {
|
||||
SpacerW4()
|
||||
IconButton(
|
||||
onClick = {
|
||||
type.onWarningClick?.invoke()
|
||||
},
|
||||
modifier = Modifier.size(size = TangemTheme.dimens.size20),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_alert_24),
|
||||
contentDescription = null,
|
||||
tint = if (priceImpact is PriceImpact.Value) {
|
||||
TangemTheme.colors.text.attention
|
||||
} else {
|
||||
TangemTheme.colors.text.tertiary
|
||||
},
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -471,22 +491,52 @@ private fun makePriceImpactBalanceWarning(value: String, priceImpactPercents: In
|
|||
|
||||
@Preview(widthDp = 328, heightDp = 116, showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_SwapMainCard_InLightTheme() {
|
||||
private fun Preview_TransactionCard_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
TransactionCardPreview()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 116, showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_TransactionCardWithPriceImpact_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
TransactionCardPreviewWithPriceImpact()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 116, showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_SwapMainCard_InDarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
private fun Preview_TransactionCardWithoutPriceImpact_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
TransactionCardPreviewWithoutPriceImpact()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 116, showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_TransactionCard_InDarkTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
TransactionCardPreview()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 116, showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_TransactionCardWithPriceImpact_InDarkTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
TransactionCardPreviewWithPriceImpact()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 116, showBackground = true)
|
||||
@Composable
|
||||
private fun Preview_TransactionCardWithoutPriceImpact_InDarkTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
TransactionCardPreviewWithoutPriceImpact()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TransactionCardPreview() {
|
||||
TransactionCard(
|
||||
|
|
@ -518,4 +568,20 @@ private fun TransactionCardPreviewWithPriceImpact() {
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Suppress("MagicNumber")
|
||||
private fun TransactionCardPreviewWithoutPriceImpact() {
|
||||
TransactionCard(
|
||||
type = TransactionCardType.ReadOnly(),
|
||||
amountEquivalent = "1 000 000",
|
||||
tokenIconUrl = "",
|
||||
tokenCurrency = "DAI",
|
||||
networkIconRes = R.drawable.img_polygon_22,
|
||||
onChangeTokenClick = {},
|
||||
balance = "123",
|
||||
textFieldValue = TextFieldValue(),
|
||||
priceImpact = PriceImpact.Empty(),
|
||||
)
|
||||
}
|
||||
|
||||
// endregion preview
|
||||
|
|
@ -18,7 +18,7 @@ data class SwapProcessDataState(
|
|||
val approveDataModel: RequestApproveStateData? = null,
|
||||
val approveType: ApproveType? = null,
|
||||
val swapDataModel: SwapDataModel? = null,
|
||||
val selectedFee: TxFee? = null, // todo
|
||||
val selectedFee: TxFee? = null,
|
||||
val tokensDataState: TokensDataStateExpress? = null,
|
||||
val selectedProvider: SwapProvider? = null,
|
||||
val lastLoadedSwapStates: Map<SwapProvider, SwapState> = emptyMap(),
|
||||
|
|
|
|||
|
|
@ -879,8 +879,12 @@ internal class SwapViewModel @Inject constructor(
|
|||
},
|
||||
onReceiveCardWarningClick = {
|
||||
val selectedProvider = dataState.selectedProvider ?: return@UiActions
|
||||
uiState = stateBuilder.createImpactAlert(
|
||||
val currencySymbol = dataState.toCryptoCurrency?.currency?.symbol ?: return@UiActions
|
||||
val isPriceImpact = uiState.priceImpact is PriceImpact.Value
|
||||
uiState = stateBuilder.createAlert(
|
||||
uiState = uiState,
|
||||
isPriceImpact = isPriceImpact,
|
||||
token = currencySymbol,
|
||||
providerType = selectedProvider.type,
|
||||
) {
|
||||
uiState = stateBuilder.clearAlert(uiState)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue