Updated on 2026-08-14

This commit is contained in:
Tangem 2023-02-15 12:23:50 +03:00
parent 8a9008dbe8
commit 9e535d8240
14 changed files with 212 additions and 41 deletions

View file

@ -350,6 +350,7 @@
<string name="swapping_success_view_title">In progress</string>
<string name="swapping_swap">Swap</string>
<string name="swapping_swap_of_to">Swap of %s to</string>
<string name="swapping_tangem_fee_disclaimer">Additionally, quotes include Tangem fee of %s. This helps us deliver a top-of-the-line product.</string>
<string name="swapping_token_list_other_tokens">Other tokens</string>
<string name="swapping_token_list_title">Choose token</string>
<string name="swapping_token_list_your_tokens">Your tokens</string>

View file

@ -350,6 +350,7 @@
<string name="swapping_success_view_title">In progress</string>
<string name="swapping_swap">Swap</string>
<string name="swapping_swap_of_to">Swap of %s to</string>
<string name="swapping_tangem_fee_disclaimer">Additionally, quotes include Tangem fee of %s. This helps us deliver a top-of-the-line product.</string>
<string name="swapping_token_list_other_tokens">Other tokens</string>
<string name="swapping_token_list_title">Choose token</string>
<string name="swapping_token_list_your_tokens">Your tokens</string>

View file

@ -350,6 +350,7 @@
<string name="swapping_success_view_title">In progress</string>
<string name="swapping_swap">Swap</string>
<string name="swapping_swap_of_to">Swap of %s to</string>
<string name="swapping_tangem_fee_disclaimer">Additionally, quotes include Tangem fee of %s. This helps us deliver a top-of-the-line product.</string>
<string name="swapping_token_list_other_tokens">Other tokens</string>
<string name="swapping_token_list_title">Choose token</string>
<string name="swapping_token_list_your_tokens">Your tokens</string>

View file

@ -350,6 +350,7 @@
<string name="swapping_success_view_title">В процессе</string>
<string name="swapping_swap">Обмен</string>
<string name="swapping_swap_of_to">Обмен %s на</string>
<string name="swapping_tangem_fee_disclaimer">Кроме того, котировки включают комиссию Tangem в размере %s. Это помогает нам предоставлять первоклассный продукт.</string>
<string name="swapping_token_list_other_tokens">Другие токены</string>
<string name="swapping_token_list_title">Выберите токен</string>
<string name="swapping_token_list_your_tokens">Ваши токены</string>

View file

@ -350,6 +350,7 @@
<string name="swapping_success_view_title">進行中</string>
<string name="swapping_swap">交換</string>
<string name="swapping_swap_of_to">交易 %s 至</string>
<string name="swapping_tangem_fee_disclaimer">Additionally, quotes include Tangem fee of %s. This helps us deliver a top-of-the-line product.</string>
<string name="swapping_token_list_other_tokens">其他代幣</string>
<string name="swapping_token_list_title">選擇代幣</string>
<string name="swapping_token_list_your_tokens">您的代幣</string>

View file

@ -350,6 +350,7 @@
<string name="swapping_success_view_title">In progress</string>
<string name="swapping_swap">Swap</string>
<string name="swapping_swap_of_to">Swap of %s to</string>
<string name="swapping_tangem_fee_disclaimer">Additionally, quotes include Tangem fee of %s. This helps us deliver a top-of-the-line product.</string>
<string name="swapping_token_list_other_tokens">Other tokens</string>
<string name="swapping_token_list_title">Choose token</string>
<string name="swapping_token_list_your_tokens">Your tokens</string>

View file

@ -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 <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=1007%3A709&t=X19ZWlYP0rnmdcDO-4"
* >Figma component</a>
*/
@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 <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=515%3A1541&t=qYGQJdtf1eeUPdkR-1"
* >Figma component</a>
@ -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

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M7.41,16L12,11.41L16.59,16L18,14.58L12,8.58L6,14.58L7.41,16Z"
android:fillColor="#000000"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z"
android:fillColor="#000000"/>
</vector>

View file

@ -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(),
)
}

View file

@ -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

View file

@ -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 {

View file

@ -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(

View file

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