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 = {}),