From 9e535d8240a6cb39a5e496d27a7f7e16067fd349 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 15 Feb 2023 12:23:50 +0300 Subject: [PATCH] Updated on 2026-08-14 --- core/res/src/main/res/values-de/strings.xml | 1 + core/res/src/main/res/values-fr/strings.xml | 1 + core/res/src/main/res/values-it/strings.xml | 1 + core/res/src/main/res/values-ru/strings.xml | 1 + .../src/main/res/values-zh-rTW/strings.xml | 1 + core/res/src/main/res/values/strings.xml | 1 + .../com/tangem/core/ui/components/Cards.kt | 186 +++++++++++++++--- .../main/res/drawable/ic_chevron_up_24.xml | 9 + core/ui/src/main/res/drawable/ic_heart_24.xml | 9 + .../feature/swap/domain/SwapInteractorImpl.kt | 5 + .../swap/domain/models/ui/SwapState.kt | 3 +- .../feature/swap/models/SwapStateHolder.kt | 11 +- .../tangem/feature/swap/ui/StateBuilder.kt | 4 +- .../feature/swap/ui/SwapScreenContent.kt | 20 +- 14 files changed, 212 insertions(+), 41 deletions(-) create mode 100644 core/ui/src/main/res/drawable/ic_chevron_up_24.xml create mode 100644 core/ui/src/main/res/drawable/ic_heart_24.xml diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index 391645934b..6dfe9b86ab 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -350,6 +350,7 @@ In progress Swap Swap of %s to + Additionally, quotes include Tangem fee of %s. This helps us deliver a top-of-the-line product. Other tokens Choose token Your tokens diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index 5cd98f4603..58fc19b1e4 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -350,6 +350,7 @@ In progress Swap Swap of %s to + Additionally, quotes include Tangem fee of %s. This helps us deliver a top-of-the-line product. Other tokens Choose token Your tokens diff --git a/core/res/src/main/res/values-it/strings.xml b/core/res/src/main/res/values-it/strings.xml index eeb90417f3..225e2544a9 100644 --- a/core/res/src/main/res/values-it/strings.xml +++ b/core/res/src/main/res/values-it/strings.xml @@ -350,6 +350,7 @@ In progress Swap Swap of %s to + Additionally, quotes include Tangem fee of %s. This helps us deliver a top-of-the-line product. Other tokens Choose token Your tokens diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index a185cbd9f0..47116aa82e 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -350,6 +350,7 @@ В процессе Обмен Обмен %s на + Кроме того, котировки включают комиссию Tangem в размере %s. Это помогает нам предоставлять первоклассный продукт. Другие токены Выберите токен Ваши токены diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml index 355e8b4cd2..6699c46a3c 100644 --- a/core/res/src/main/res/values-zh-rTW/strings.xml +++ b/core/res/src/main/res/values-zh-rTW/strings.xml @@ -350,6 +350,7 @@ 進行中 交換 交易 %s 至 + Additionally, quotes include Tangem fee of %s. This helps us deliver a top-of-the-line product. 其他代幣 選擇代幣 您的代幣 diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 679519b741..fcaa91827d 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -350,6 +350,7 @@ In progress Swap Swap of %s to + Additionally, quotes include Tangem fee of %s. This helps us deliver a top-of-the-line product. Other tokens Choose token Your tokens diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Cards.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Cards.kt index dd8efe92a9..0fc92ad614 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/Cards.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/Cards.kt @@ -2,6 +2,7 @@ package com.tangem.core.ui.components import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -20,10 +21,13 @@ import androidx.compose.material.Icon import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp import com.tangem.core.ui.R import com.tangem.core.ui.res.TangemTheme import com.valentinilk.shimmer.shimmer @@ -53,6 +57,39 @@ fun SmallInfoCard( startText = startText, endText = endText, isLoading = isLoading, + disclaimer = null, + ) + } +} + +/** + * Small card with text information attached to the edges + * + * @param startText text information attached to the left edge + * @param endText text information attached to the right edge + * @param disclaimer closable description text + * @param isLoading if true, shimmer is shown instead of [startText] and [endText] + * + * @see Figma component + */ +@Composable +fun SmallInfoCardWithDisclaimer( + startText: String, + endText: String, + disclaimer: String, + isLoading: Boolean = false, +) { + Surface( + shape = RoundedCornerShape(TangemTheme.dimens.radius12), + color = TangemTheme.colors.background.primary, + elevation = TangemTheme.dimens.elevation2, + ) { + CardInfoBox( + startText = startText, + endText = endText, + isLoading = isLoading, + disclaimer = disclaimer, ) } } @@ -63,6 +100,7 @@ fun SmallInfoCard( * @param startText text information attached to the left edge * @param endText text information attached to the right edge * @param warningText text of the warning + * @param disclaimer closable description text * * @see Figma component @@ -72,6 +110,7 @@ fun SmallInfoCardWithWarning( startText: String, endText: String, warningText: String, + disclaimer: String? = null, ) { Surface( shape = RoundedCornerShape(TangemTheme.dimens.size12), @@ -83,7 +122,7 @@ fun SmallInfoCardWithWarning( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { - CardInfoBox(startText = startText, endText = endText) + CardInfoBox(startText = startText, endText = endText, disclaimer = disclaimer) Divider( color = TangemTheme.colors.stroke.primary, @@ -238,44 +277,117 @@ fun IconWithTitleAndDescription( private fun CardInfoBox( startText: String, endText: String, + disclaimer: String?, isLoading: Boolean = false, ) { + val isOpened = remember { mutableStateOf(false) } + Column { + Row( + modifier = Modifier + .fillMaxWidth() + .heightIn(TangemTheme.dimens.size48) + .clickable { isOpened.value = !isOpened.value } + .padding( + horizontal = TangemTheme.dimens.spacing16, + vertical = TangemTheme.dimens.spacing12, + ), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = startText, + color = TangemTheme.colors.text.tertiary, + maxLines = 1, + style = TangemTheme.typography.subtitle2, + ) + + if (isLoading) { + ShimmerItem(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12) + } else { + Row { + Text( + text = endText, + color = TangemTheme.colors.text.primary1, + maxLines = 1, + style = TangemTheme.typography.body2, + ) + if (disclaimer != null) { + val chevronIcon = if (isOpened.value) { + painterResource(id = R.drawable.ic_chevron_up_24) + } else { + painterResource(id = R.drawable.ic_chevron_24) + } + Icon( + modifier = Modifier.size(TangemTheme.dimens.size20), + painter = chevronIcon, + tint = TangemTheme.colors.icon.primary1, + contentDescription = null, + ) + } + } + } + } + if (isOpened.value && disclaimer != null) { + DisclaimerItem(disclaimer) + } + } +} + +@Composable +private fun ShimmerItem(width: Dp, height: Dp) { + Box( + modifier = Modifier + .size(width = width, height = height) + .shimmer() + .background( + color = TangemTheme.colors.button.secondary, + shape = RoundedCornerShape(TangemTheme.dimens.radius3), + ), + ) +} + +@Composable +private fun DisclaimerItem(disclaimer: String) { + Divider( + color = TangemTheme.colors.stroke.primary, + thickness = TangemTheme.dimens.size0_5, + modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing12), + ) + Row( modifier = Modifier .fillMaxWidth() - .heightIn(TangemTheme.dimens.size48) .padding( - horizontal = TangemTheme.dimens.spacing16, - vertical = TangemTheme.dimens.spacing12, + top = TangemTheme.dimens.spacing14, + bottom = TangemTheme.dimens.spacing16, + start = TangemTheme.dimens.spacing16, + end = TangemTheme.dimens.spacing16, ), - horizontalArrangement = Arrangement.SpaceBetween, + horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { - Text( - text = startText, - color = TangemTheme.colors.text.tertiary, - maxLines = 1, - style = TangemTheme.typography.subtitle2, - ) - - if (isLoading) { - Box( - modifier = Modifier - .size(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12) - .shimmer() - .background( - color = TangemTheme.colors.button.secondary, - shape = RoundedCornerShape(TangemTheme.dimens.radius3), - ), - ) - } else { - Text( - text = endText, - color = TangemTheme.colors.text.primary1, - maxLines = 1, - style = TangemTheme.typography.body2, + Box( + modifier = Modifier + .background( + color = TangemTheme.colors.background.secondary, + shape = CircleShape, + ) + .height(TangemTheme.dimens.size40) + .width(TangemTheme.dimens.size40), + contentAlignment = Alignment.Center, + ) { + Icon( + painter = painterResource(id = R.drawable.ic_heart_24), + tint = TangemTheme.colors.icon.secondary, + contentDescription = null, ) } + SpacerW12() + Text( + text = disclaimer, + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.body2, + ) } } @@ -287,7 +399,19 @@ private fun CardInfoBox( @Composable private fun CardsPreview() { Column(modifier = Modifier.fillMaxWidth()) { - SmallInfoCard(startText = "Balance", endText = "0.4405434 BTC") + SmallInfoCard( + startText = "Balance", + endText = "0.4405434 BTC", + ) + + SpacerH32() + + SmallInfoCardWithDisclaimer( + startText = "Balance", + endText = "0.4405434 BTC", + disclaimer = "Not enough funds for fee on your Polygon wallet to create a transaction." + + "Top up your Polygon wallet first.", + ) SpacerH32() @@ -296,6 +420,8 @@ private fun CardsPreview() { endText = "0.4405434 BTC", warningText = "Not enough funds for fee on your Polygon wallet to create a transaction. " + "Top up your Polygon wallet first.", + disclaimer = "Not enough funds for fee on your Polygon wallet to create a transaction." + + "Top up your Polygon wallet first.", ) SpacerH32() @@ -335,6 +461,7 @@ private fun Preview_InfoCardWithWarning_InDarkTheme() { private fun Preview_SimpleInfoCard_InLightTheme() { TangemTheme(isDark = false) { SmallInfoCard(startText = "Balance", endText = "0.4405434 BTC") + SmallInfoCardWithDisclaimer(startText = "Balance", endText = "0.4405434 BTC", disclaimer = "test") } } @@ -343,6 +470,7 @@ private fun Preview_SimpleInfoCard_InLightTheme() { private fun Preview_SimpleInfoCard_InDarkTheme() { TangemTheme(isDark = true) { SmallInfoCard(startText = "Balance", endText = "0.4405434 BTC") + SmallInfoCardWithDisclaimer(startText = "Balance", endText = "0.4405434 BTC", disclaimer = "test") } } // endregion Preview \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/ic_chevron_up_24.xml b/core/ui/src/main/res/drawable/ic_chevron_up_24.xml new file mode 100644 index 0000000000..5433e8ef45 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_chevron_up_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/ui/src/main/res/drawable/ic_heart_24.xml b/core/ui/src/main/res/drawable/ic_heart_24.xml new file mode 100644 index 0000000000..7fe31e531e --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_heart_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt index 25f533f4b1..381ba9784e 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt @@ -239,6 +239,10 @@ internal class SwapInteractorImpl @Inject constructor( return ONE_INCH_SUPPORTED_NETWORKS.contains(networkId) } + private fun getTangemFee(): Double { + return repository.getTangemFee() + } + fun getTokenDecimals(token: Currency): Int { return if (token is Currency.NonNativeToken) { token.decimalCount @@ -495,6 +499,7 @@ internal class SwapInteractorImpl @Inject constructor( networkCurrency = userWalletManager.getNetworkCurrency(networkId), preparedSwapConfigState = preparedSwapConfigState, swapDataModel = swapDataModel, + tangemFee = getTangemFee(), ) } diff --git a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt index 271e979b8f..879dc1f124 100644 --- a/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt +++ b/features/swap/domain/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt @@ -16,12 +16,13 @@ sealed interface SwapState { val preparedSwapConfigState: PreparedSwapConfigState, val permissionState: PermissionDataState = PermissionDataState.Empty, val swapDataModel: SwapDataModel? = null, + val tangemFee: Double, ) : SwapState data class EmptyAmountState( val fromTokenWalletBalance: String, val toTokenWalletBalance: String, - val zeroAmountEquivalent: String + val zeroAmountEquivalent: String, ) : SwapState data class SwapError(val error: DataError) : SwapState 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 6c431cae0a..81d124d753 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 @@ -48,17 +48,18 @@ data class SwapButton( val onClick: () -> Unit, ) -sealed interface FeeState { +sealed class FeeState(open val tangemFee: Double) { - object Empty : FeeState + object Empty : FeeState(0.0) data class Loaded( + override val tangemFee: Double, val fee: String = "", - ) : FeeState + ) : FeeState(tangemFee) - object Loading : FeeState + object Loading : FeeState(0.0) - data class NotEnoughFundsWarning(val fee: String) : FeeState + data class NotEnoughFundsWarning(override val tangemFee: Double, val fee: String) : FeeState(tangemFee) } sealed interface TransactionCardType { 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 9eac1b9c92..08ae435304 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 @@ -125,9 +125,9 @@ internal class StateBuilder(val actions: UiActions) { warnings.add(SwapWarning.InsufficientFunds) } val feeState = if (quoteModel.preparedSwapConfigState.isFeeEnough) { - FeeState.Loaded(quoteModel.fee) + FeeState.Loaded(tangemFee = quoteModel.tangemFee, fee = quoteModel.fee) } else { - FeeState.NotEnoughFundsWarning(quoteModel.fee) + FeeState.NotEnoughFundsWarning(tangemFee = quoteModel.tangemFee, fee = quoteModel.fee) } return uiStateHolder.copy( sendCardData = SwapCardData( 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 a47d9eeac3..6ae609bf90 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 @@ -37,7 +37,7 @@ import com.tangem.core.ui.components.PrimaryButton import com.tangem.core.ui.components.PrimaryButtonIconRight import com.tangem.core.ui.components.RefreshableWaringCard import com.tangem.core.ui.components.SimpleOkDialog -import com.tangem.core.ui.components.SmallInfoCard +import com.tangem.core.ui.components.SmallInfoCardWithDisclaimer import com.tangem.core.ui.components.SmallInfoCardWithWarning import com.tangem.core.ui.components.WarningCard import com.tangem.core.ui.components.appbar.AppBarWithBackButton @@ -244,17 +244,29 @@ private fun SwapButton(state: SwapStateHolder, modifier: Modifier = Modifier) { @Composable private fun FeeItem(feeState: FeeState, currency: String) { val titleString = stringResource(id = R.string.send_fee_label) + val disclaimer = stringResource(id = R.string.swapping_tangem_fee_disclaimer, "${feeState.tangemFee}%") when (feeState) { is FeeState.Loaded -> { - SmallInfoCard(startText = titleString, endText = feeState.fee, isLoading = false) + SmallInfoCardWithDisclaimer( + startText = titleString, + endText = feeState.fee, + disclaimer = disclaimer, + isLoading = false, + ) } FeeState.Loading -> { - SmallInfoCard(startText = titleString, endText = "", isLoading = true) + SmallInfoCardWithDisclaimer( + startText = titleString, + endText = "", + disclaimer = disclaimer, + isLoading = true, + ) } is FeeState.NotEnoughFundsWarning -> { SmallInfoCardWithWarning( startText = titleString, endText = feeState.fee, + disclaimer = disclaimer, warningText = stringResource( id = R.string.swapping_not_enough_funds_for_fee, currency, @@ -387,7 +399,7 @@ private val state = SwapStateHolder( networkId = "ethereum", sendCardData = sendCard, receiveCardData = receiveCard, - fee = FeeState.Loaded(fee = "0.155 MATIC (0.14 $)"), + fee = FeeState.Loaded(fee = "0.155 MATIC (0.14 $)", tangemFee = 0.0), warnings = listOf(SwapWarning.PermissionNeeded("DAI")), networkCurrency = "MATIC", swapButton = SwapButton(enabled = true, loading = false, onClick = {}),