diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 007f6d05eb..e52b26c31c 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -536,6 +536,9 @@
Дать разрешение
Обмен этой суммы выбранных токенов может вызвать значительные колебания цены и уменьшить получаемую сумму.
Недостаточно средств
+ Комиссии
+ В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %s от биржи обратно на адрес пользователя
+ В сумму включена комиссия провайдера сервиса.
Недостаточно средств для оплаты комиссии на вашем %1$s кошельке для создания транзакции. Сначала пополните свой %2$s кошелек.
Транзакция в процессе…
Подождите
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 0040f9b3b0..80715ad6f9 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -549,6 +549,9 @@
Give Permission
Swapping this amount of selected tokens will cause a significant price impact and reduce your outcome.
Insufficient funds
+ Fees
+ The amount includes:\n• service provider\'s fee\n• network fee for sending %s from the exchange back to the user\'s address.
+ The amount includes the service provider\'s fee.
Insufficient funds in your %1$s wallet to cover fees. Top up your %2$s wallet first.
Transaction in progress...
Waiting
diff --git a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt
index 264f39582b..1fae322ed1 100644
--- a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt
+++ b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt
@@ -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
}
diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt
index 4e77ff7dc8..71df4eb5d3 100644
--- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt
+++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt
@@ -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
diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt
index d6a880f213..38aee5e9fb 100644
--- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt
+++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt
@@ -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,
diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt
index f58b501863..4f2a3fefa1 100644
--- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt
+++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt
@@ -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,
)
}
diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt
index 038465b6d3..9e7a5f8b2d 100644
--- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt
+++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt
@@ -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
\ No newline at end of file
diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapProcessDataState.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapProcessDataState.kt
index 7623970edf..aada4b7d2b 100644
--- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapProcessDataState.kt
+++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapProcessDataState.kt
@@ -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 = emptyMap(),
diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt
index ace4cb9006..52e89386a3 100644
--- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt
+++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt
@@ -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)