From eadc650d266cddf234fbc4efab52fad293a6f501 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 15 Feb 2024 10:08:56 +0100 Subject: [PATCH] Updated on 2026-08-14 --- core/res/src/main/res/values-ru/strings.xml | 3 + core/res/src/main/res/values/strings.xml | 3 + .../swap/domain/models/ui/SwapState.kt | 4 +- .../feature/swap/models/SwapStateHolder.kt | 3 +- .../tangem/feature/swap/ui/StateBuilder.kt | 24 +++- .../feature/swap/ui/SwapScreenContent.kt | 7 +- .../tangem/feature/swap/ui/TransactionCard.kt | 114 ++++++++++++++---- .../swap/viewmodels/SwapProcessDataState.kt | 2 +- .../feature/swap/viewmodels/SwapViewModel.kt | 6 +- 9 files changed, 133 insertions(+), 33 deletions(-) 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)