Updated on 2026-08-14

This commit is contained in:
Tangem 2023-01-13 22:35:37 +04:00
parent 76f955568e
commit fc8fcfce19
17 changed files with 395 additions and 133 deletions

View file

@ -191,6 +191,7 @@
<string name="common_share">Share</string>
<string name="common_copy">Copy</string>
<string name="common_success">Erfolg</string>
<string name="common_close">Close</string>
<string name="exchange_send_view_header">You Send</string>
<string name="exchange_receive_view_header">You Receive</string>
<string name="swapping_permission_header">Give Permission</string>
@ -209,6 +210,9 @@
<string name="swapping_token_list_other_tokens">Other tokens</string>
<string name="swapping_pending_transaction_title">Waiting</string>
<string name="swapping_pending_transaction_subtitle">Transaction in progress…</string>
<string name="swapping_success_view_title">In progress</string>
<string name="swapping_success_view_explorer_button_title">View in Explorer</string>
<string name="swapping_token_not_available">not available</string>
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Reason: %s. Please try again later.</string>
<string name="referral_error_failed_to_participate">Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support.</string>

View file

@ -191,6 +191,7 @@
<string name="common_share">Share</string>
<string name="common_copy">Copy</string>
<string name="common_success">Avec succès</string>
<string name="common_close">Close</string>
<string name="exchange_send_view_header">You Send</string>
<string name="exchange_receive_view_header">You Receive</string>
<string name="swapping_permission_header">Give Permission</string>
@ -209,6 +210,9 @@
<string name="swapping_token_list_other_tokens">Other tokens</string>
<string name="swapping_pending_transaction_title">Waiting</string>
<string name="swapping_pending_transaction_subtitle">Transaction in progress…</string>
<string name="swapping_success_view_title">In progress</string>
<string name="swapping_success_view_explorer_button_title">View in Explorer</string>
<string name="swapping_token_not_available">not available</string>
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Reason: %s. Please try again later.</string>
<string name="referral_error_failed_to_participate">Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support.</string>

View file

@ -191,6 +191,7 @@
<string name="common_share">Share</string>
<string name="common_copy">Copy</string>
<string name="common_success">Con successo</string>
<string name="common_close">Close</string>
<string name="exchange_send_view_header">You Send</string>
<string name="exchange_receive_view_header">You Receive</string>
<string name="swapping_permission_header">Give Permission</string>
@ -209,6 +210,9 @@
<string name="swapping_token_list_other_tokens">Other tokens</string>
<string name="swapping_pending_transaction_title">Waiting</string>
<string name="swapping_pending_transaction_subtitle">Transaction in progress…</string>
<string name="swapping_success_view_title">In progress</string>
<string name="swapping_success_view_explorer_button_title">View in Explorer</string>
<string name="swapping_token_not_available">not available</string>
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Reason: %s. Please try again later.</string>
<string name="referral_error_failed_to_participate">Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support.</string>

View file

@ -191,6 +191,7 @@
<string name="common_share">Поделиться</string>
<string name="common_copy">Копировать</string>
<string name="common_success">Успешно</string>
<string name="common_close">Закрыть</string>
<string name="exchange_send_view_header">Вы отправляете</string>
<string name="exchange_receive_view_header">Вы получаете</string>
<string name="swapping_permission_header">Дать разрешение</string>
@ -209,6 +210,9 @@
<string name="swapping_token_list_other_tokens">Другие токены</string>
<string name="swapping_pending_transaction_title">Подождите</string>
<string name="swapping_pending_transaction_subtitle">Транзакция в процессе…</string>
<string name="swapping_success_view_title">В процессе</string>
<string name="swapping_success_view_explorer_button_title">Открыть в обозревателе</string>
<string name="swapping_token_not_available">не доступен</string>
<string name="referral_error_failed_to_load_info">Не удалось загрузить информацию по реферальной программе. Пожалуйста, попробуйте позже.</string>
<string name="referral_error_failed_to_load_info_with_reason">Не удалось загрузить информацию по реферальной программе. Код ошибки: %s. Пожалуйста, попробуйте позже.</string>
<string name="referral_error_failed_to_participate">Не удалось обработать вашу заявку на участие. Код ошибки: %s. Пожалуйста, попробуйте позже. Если проблема сохранится, вы можете обратиться в техподдержку.</string>

View file

@ -191,6 +191,7 @@
<string name="common_share">Share</string>
<string name="common_copy">Copy</string>
<string name="common_success">Success</string>
<string name="common_close">Close</string>
<string name="exchange_send_view_header">You Send</string>
<string name="exchange_receive_view_header">You Receive</string>
<string name="swapping_permission_header">Give Permission</string>
@ -209,6 +210,9 @@
<string name="swapping_token_list_other_tokens">Other tokens</string>
<string name="swapping_pending_transaction_title">Waiting</string>
<string name="swapping_pending_transaction_subtitle">Transaction in progress…</string>
<string name="swapping_success_view_title">In progress</string>
<string name="swapping_success_view_explorer_button_title">View in Explorer</string>
<string name="swapping_token_not_available">not available</string>
<string name="referral_error_failed_to_load_info">Failed to load the information about the referral program. Please try again later.</string>
<string name="referral_error_failed_to_load_info_with_reason">Failed to load the information about the referral program. Error code: %s. Please try again later.</string>
<string name="referral_error_failed_to_participate">Your participation request could not be processed. Error code: %s. Please try again later. If the problem persists — feel free to contact our support.</string>

View file

@ -0,0 +1,143 @@
package com.tangem.core.ui.components
import androidx.compose.foundation.layout.padding
import androidx.compose.material.AlertDialog
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R
import com.tangem.core.ui.res.TangemTheme
/**
* Simple alert dialog with a message and 'OK' button
*
* @param message message to show
* @param title title to show (no title if null)
* @param confirmButton title and action for confirm button
* @param dismissButton title and action for dismiss button (no dismiss button if null)
* @param onDismissDialog action to perform when dialog is closed
*
* @see <a href = "https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?node-id=48%3A30&t=izDPAJnDbJTTC0Fp-1"
* >Figma component</a>
*/
@Composable
fun BasicDialog(
modifier: Modifier = Modifier,
message: String,
title: String? = null,
confirmButton: DialogButton,
dismissButton: DialogButton? = null,
onDismissDialog: () -> Unit,
) {
AlertDialog(
text = {
Text(
text = message,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.secondary,
)
},
title = if (title != null) {
{
Text(
text = title,
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
)
}
} else {
null
},
onDismissRequest = onDismissDialog,
confirmButton = {
TextButton(
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
text = confirmButton.title ?: stringResource(id = R.string.common_ok),
onClick = confirmButton.onClick,
)
},
dismissButton = {
if (dismissButton != null) {
TextButton(
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
text = dismissButton.title ?: stringResource(id = R.string.common_cancel),
onClick = dismissButton.onClick,
)
}
},
shape = TangemTheme.shapes.roundedCornersLarge,
modifier = modifier
.padding(TangemTheme.dimens.spacing24),
)
}
data class DialogButton(
val title: String? = null,
val onClick: () -> Unit,
)
@Composable
fun SimpleOkDialog(
modifier: Modifier = Modifier,
message: String,
onDismissDialog: () -> Unit,
) {
BasicDialog(
modifier = modifier,
message = message,
confirmButton = DialogButton(onClick = onDismissDialog),
onDismissDialog = onDismissDialog,
)
}
// region Preview
@Composable
private fun SimpleOkDialogPreview() = SimpleOkDialog(
message = "All protected passwords will be deleted from the " +
"secure storage, you must enter the wallet password to work with the app.",
) {}
@Composable
private fun BasicDialogPreview() = BasicDialog(
message = "All protected passwords will be deleted from the secure storage, you must enter the wallet password to work with the app",
title = "Attention",
confirmButton = DialogButton {},
dismissButton = DialogButton {},
) {}
@Preview(showBackground = true)
@Composable
fun Preview_SimpleOkDialog_InLightTheme() {
TangemTheme(isDark = false) {
SimpleOkDialogPreview()
}
}
@Preview(showBackground = true)
@Composable
fun Preview_BasicDialog_InLightTheme() {
TangemTheme(isDark = false) {
BasicDialogPreview()
}
}
@Preview(showBackground = true)
@Composable
fun Preview_SimpleOkDialog_InDarkTheme() {
TangemTheme(isDark = true) {
SimpleOkDialogPreview()
}
}
@Preview(showBackground = true)
@Composable
fun Preview_BasicDialog_InDarkTheme() {
TangemTheme(isDark = true) {
BasicDialogPreview()
}
}
// endregion Preview

View file

@ -1,5 +1,7 @@
package com.tangem.core.ui.components
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@ -18,6 +20,7 @@ import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
@ -26,25 +29,37 @@ import com.tangem.core.ui.R
import com.tangem.core.ui.res.TangemTheme
/**
* Screen for showing success
* Screen for showing result
*
* @param successMessage
* @param resultMessage message to show
* @param title title to show
* @param resultColor color which will tint the round icon of the result
* @param icon icon to show in the middle of the round icon
* @param secondaryButtonIcon icon to show in the secondary button
* @param secondaryButtonText label of the secondary button
* @param onSecondaryButtonClick action on clicking secondary button
* @param onButtonClick action on clicking "Done" button
*
* @see <a href = "https://www.figma.com/file/Vs6SkVsFnUPsSCNwlnVf5U/Android-%E2%80%93-UI?node-id=1123%3A3863&t=wwR84h5IsMaMsDhq-1"
* >Figma component</a>
*/
@Composable
fun SuccessScreenContent(
fun ResultScreenContent(
modifier: Modifier = Modifier,
successMessage: String,
resultMessage: String,
@StringRes title: Int = R.string.common_success,
resultColor: Color = TangemTheme.colors.icon.accent,
@DrawableRes icon: Int = R.drawable.ic_check_24,
@DrawableRes secondaryButtonIcon: Int? = null,
@StringRes secondaryButtonText: Int? = null,
onSecondaryButtonClick: (() -> Unit)? = null,
onButtonClick: () -> Unit,
) {
Column(
modifier = modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.background(TangemTheme.colors.background.primary)
.background(TangemTheme.colors.background.secondary)
.padding(
horizontal = TangemTheme.dimens.spacing16,
vertical = TangemTheme.dimens.spacing32,
@ -53,10 +68,10 @@ fun SuccessScreenContent(
verticalArrangement = Arrangement.Center,
) {
SpacerHHalf()
SuccessImage()
SuccessImage(resultColor = resultColor, icon = icon)
SpacerH50()
Text(
text = stringResource(id = R.string.common_success),
text = stringResource(id = title),
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
@ -64,15 +79,25 @@ fun SuccessScreenContent(
)
SpacerH12()
Text(
text = successMessage,
text = resultMessage,
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
)
SpacerHHalf()
if (onSecondaryButtonClick != null && secondaryButtonText != null) {
SecondaryButtonForResultScreen(
secondaryButtonText = secondaryButtonText,
secondaryButtonIcon = secondaryButtonIcon,
onSecondaryButtonClick = onSecondaryButtonClick,
modifier = Modifier
.fillMaxWidth(),
)
SpacerH12()
}
PrimaryButton(
text = stringResource(id = R.string.common_done),
text = stringResource(id = R.string.common_close),
modifier = Modifier
.fillMaxWidth(),
onClick = { onButtonClick() },
@ -81,11 +106,15 @@ fun SuccessScreenContent(
}
@Composable
fun SuccessImage() {
fun SuccessImage(
resultColor: Color,
@DrawableRes icon: Int,
) {
Box(
modifier = Modifier
.background(
color = TangemTheme.colors.icon.accent.copy(alpha = 0.2f),
color = (resultColor)
.copy(alpha = 0.2f),
shape = CircleShape,
),
contentAlignment = Alignment.Center,
@ -94,7 +123,7 @@ fun SuccessImage() {
modifier = Modifier
.padding(TangemTheme.dimens.spacing24)
.background(
color = TangemTheme.colors.icon.accent,
color = resultColor,
shape = CircleShape,
)
.height(TangemTheme.dimens.size93)
@ -102,7 +131,7 @@ fun SuccessImage() {
contentAlignment = Alignment.Center,
) {
Icon(
painter = painterResource(id = R.drawable.ic_check_24),
painter = painterResource(id = icon),
contentDescription = null,
tint = TangemTheme.colors.icon.primary2,
modifier = Modifier.size(TangemTheme.dimens.size40),
@ -111,12 +140,37 @@ fun SuccessImage() {
}
}
@Composable
private fun SecondaryButtonForResultScreen(
modifier: Modifier = Modifier,
@StringRes secondaryButtonText: Int,
@DrawableRes secondaryButtonIcon: Int? = null,
onSecondaryButtonClick: (() -> Unit),
) {
if (secondaryButtonIcon != null) {
SecondaryButtonIconLeft(
text = stringResource(id = secondaryButtonText),
icon = painterResource(id = secondaryButtonIcon),
onClick = onSecondaryButtonClick,
modifier = modifier,
)
} else {
SecondaryButton(
text = stringResource(id = secondaryButtonText),
onClick = onSecondaryButtonClick,
modifier = modifier,
)
}
}
// region preview
@Composable
private fun SuccessScreenPreview() {
SuccessScreenContent(
successMessage = "Swap of 1 000 DAI to 1 131,46 MATIC",
ResultScreenContent(
resultMessage = "Swap of 1 000 DAI to 1 131,46 MATIC",
secondaryButtonText = R.string.swapping_success_view_explorer_button_title,
onSecondaryButtonClick = {},
onButtonClick = {},
)
}

View file

@ -28,6 +28,7 @@ fun WarningCard(
modifier: Modifier = Modifier,
title: String,
description: String,
icon: @Composable (() -> Unit)? = null,
) {
WarningCardSurface(
modifier = modifier,
@ -35,6 +36,7 @@ fun WarningCard(
WarningBody(
title = title,
description = description,
icon = icon,
)
},
)
@ -55,12 +57,13 @@ fun ClickableWarningCard(
modifier: Modifier = Modifier,
title: String,
description: String,
icon: @Composable (() -> Unit)? = null,
onClick: () -> Unit,
) {
WarningCardSurface(
modifier = modifier,
content = {
WarningBody(title = title, description = description) {
WarningBody(title = title, description = description, icon = icon) {
SpacerW12()
Icon(
painter = painterResource(id = R.drawable.ic_chevron_right_24),
@ -88,12 +91,13 @@ fun RefreshableWaringCard(
modifier: Modifier = Modifier,
title: String,
description: String,
icon: @Composable (() -> Unit)? = null,
onClick: () -> Unit,
) {
WarningCardSurface(
modifier = modifier,
content = {
WarningBody(title = title, description = description) {
WarningBody(title = title, description = description, icon = icon) {
SpacerW12()
Icon(
painter = painterResource(id = R.drawable.ic_refresh_24),
@ -113,6 +117,7 @@ private fun WarningBody(
modifier: Modifier = Modifier,
title: String,
description: String,
icon: @Composable (() -> Unit)? = null,
additionalContent: @Composable () -> Unit = {},
) {
IconWithTitleAndDescription(
@ -120,7 +125,7 @@ private fun WarningBody(
title = title,
description = description,
additionalContent = additionalContent,
icon = {
icon = icon ?: {
Image(
painter = painterResource(id = R.drawable.img_attention_20),
contentDescription = null,

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000000"
android:pathData="M5,17.59L15.59,7H9V5H19V15H17V8.41L6.41,19L5,17.59Z" />
</vector>

View file

@ -1,8 +1,7 @@
package com.tangem.feature.swap.models
data class SwapSelectTokenStateHolder(
val addedTokens: List<TokenToSelect>,
val otherTokens: List<TokenToSelect>,
val tokens: List<TokenToSelect>,
val onSearchEntered: (String) -> Unit,
val onTokenSelected: (String) -> Unit,
)
@ -12,6 +11,7 @@ data class TokenToSelect(
val name: String,
val symbol: String,
val iconUrl: String,
val available: Boolean = true,
val addedTokenBalanceData: TokenBalanceData? = null,
)

View file

@ -6,8 +6,9 @@ data class SwapStateHolder(
val sendCardData: SwapCardData,
val receiveCardData: SwapCardData,
val networkCurrency: String,
val fee: FeeState = FeeState.Loaded(),
val fee: FeeState = FeeState.Empty,
val warnings: List<SwapWarning> = emptyList(),
val alert: SwapWarning.GenericWarning? = null,
val permissionState: SwapPermissionState = SwapPermissionState.Empty,
val successState: SwapSuccessStateHolder? = null,
@ -29,6 +30,7 @@ data class SwapCardData(
val amountEquivalent: String?,
val tokenIconUrl: String,
val tokenCurrency: String,
val balance: String,
@DrawableRes val networkIconRes: Int? = null,
val canSelectAnotherToken: Boolean = false,
)
@ -40,6 +42,9 @@ data class SwapButton(
)
sealed interface FeeState {
object Empty : FeeState
data class Loaded(
val fee: String = "",
) : FeeState
@ -52,8 +57,6 @@ sealed interface FeeState {
sealed interface TransactionCardType {
data class SendCard(
val balance: String,
val permissionIsGiven: Boolean,
val onAmountChanged: ((String) -> Unit),
) : TransactionCardType

View file

@ -2,4 +2,5 @@ package com.tangem.feature.swap.models
data class SwapSuccessStateHolder(
val message: String,
val onSecondaryButtonClick: () -> Unit,
)

View file

@ -22,12 +22,13 @@ class StateBuilder {
fun createInitialLoadingState(networkCurrency: String, onAmountChanged: (String) -> Unit): SwapStateHolder {
return SwapStateHolder(
sendCardData = SwapCardData(
type = TransactionCardType.SendCard("", true, onAmountChanged),
type = TransactionCardType.SendCard(onAmountChanged),
amount = null,
amountEquivalent = null,
tokenIconUrl = "",
tokenCurrency = "",
canSelectAnotherToken = false,
balance = "",
),
receiveCardData = SwapCardData(
type = TransactionCardType.ReceiveCard(),
@ -36,6 +37,7 @@ class StateBuilder {
tokenIconUrl = "",
tokenCurrency = "",
canSelectAnotherToken = false,
balance = "",
),
fee = FeeState.Loading,
networkCurrency = networkCurrency,
@ -60,6 +62,7 @@ class StateBuilder {
tokenIconUrl = fromToken.logoUrl,
tokenCurrency = fromToken.symbol,
canSelectAnotherToken = mainTokenId != fromToken.id,
balance = "",
),
receiveCardData = SwapCardData(
type = TransactionCardType.ReceiveCard(),
@ -68,6 +71,7 @@ class StateBuilder {
tokenIconUrl = toToken.logoUrl,
tokenCurrency = toToken.symbol,
canSelectAnotherToken = mainTokenId != toToken.id,
balance = "",
),
fee = FeeState.Loading,
swapButton = SwapButton(enabled = false, loading = true, onClick = {}),
@ -83,16 +87,13 @@ class StateBuilder {
): SwapStateHolder {
return uiStateHolder.copy(
sendCardData = SwapCardData(
type = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.SendCard)
.copy(
permissionIsGiven = quoteModel.isAllowedToSpend,
balance = quoteModel.fromTokenWalletBalance,
),
type = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.SendCard),
amount = quoteModel.fromTokenAmount.formatToUIRepresentation(),
amountEquivalent = quoteModel.fromTokenFiatBalance,
tokenIconUrl = uiStateHolder.sendCardData.tokenIconUrl,
tokenCurrency = uiStateHolder.sendCardData.tokenCurrency,
canSelectAnotherToken = uiStateHolder.sendCardData.canSelectAnotherToken,
balance = quoteModel.fromTokenWalletBalance,
),
receiveCardData = SwapCardData(
type = TransactionCardType.ReceiveCard(),
@ -101,6 +102,7 @@ class StateBuilder {
tokenIconUrl = uiStateHolder.receiveCardData.tokenIconUrl,
tokenCurrency = uiStateHolder.receiveCardData.tokenCurrency,
canSelectAnotherToken = uiStateHolder.receiveCardData.canSelectAnotherToken,
balance = quoteModel.toTokenWalletBalance,
),
warnings = if (!quoteModel.isAllowedToSpend) {
listOf(SwapWarning.PermissionNeeded(fromToken.symbol))

View file

@ -1,6 +1,7 @@
package com.tangem.feature.swap.ui
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@ -28,6 +29,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.BasicDialog
import com.tangem.core.ui.components.CardWithIcon
import com.tangem.core.ui.components.ClickableWarningCard
import com.tangem.core.ui.components.Keyboard
@ -133,6 +135,10 @@ internal fun SwapScreenContent(
textAlign = TextAlign.Start,
)
}
if (state.alert != null) {
BasicDialog(message = state.alert.message, onDismissDialog = state.alert.onClick)
}
}
}
@ -147,6 +153,7 @@ private fun MainInfo(state: SwapStateHolder) {
Column {
TransactionCard(
type = state.sendCardData.type,
balance = state.sendCardData.balance,
amount = state.sendCardData.amount,
amountEquivalent = state.sendCardData.amountEquivalent,
tokenIconUrl = state.sendCardData.tokenIconUrl,
@ -157,6 +164,7 @@ private fun MainInfo(state: SwapStateHolder) {
SpacerH16()
TransactionCard(
type = state.receiveCardData.type,
balance = state.receiveCardData.balance,
amount = state.receiveCardData.amount,
amountEquivalent = state.receiveCardData.amountEquivalent,
tokenIconUrl = state.receiveCardData.tokenIconUrl,
@ -203,6 +211,7 @@ private fun FeeItem(feeState: FeeState, currency: String) {
warningText = stringResource(id = R.string.token_details_send_blocked_fee_format, currency),
)
}
is FeeState.Empty -> {}
}
}
@ -227,6 +236,13 @@ private fun SwapWarnings(
id = R.string.swapping_permission_subheader,
warning.tokenCurrency,
),
icon = {
Image(
painter = painterResource(id = com.tangem.core.ui.R.drawable.ic_locked_24),
contentDescription = null,
modifier = Modifier.size(TangemTheme.dimens.size20),
)
},
onClick = onApproveWarningClick,
)
}
@ -252,13 +268,14 @@ private fun SwapWarnings(
// region preview
private val sendCard = SwapCardData(
type = TransactionCardType.SendCard("123", false, {}),
type = TransactionCardType.SendCard {},
amount = "1 000 000",
amountEquivalent = "1 000 000",
tokenIconUrl = "",
tokenCurrency = "DAI",
networkIconRes = R.drawable.img_polygon_22,
canSelectAnotherToken = false,
balance = "123",
)
private val receiveCard = SwapCardData(
@ -269,17 +286,19 @@ private val receiveCard = SwapCardData(
tokenCurrency = "DAI",
networkIconRes = R.drawable.img_polygon_22,
canSelectAnotherToken = true,
balance = "33333",
)
private val state = SwapStateHolder(
sendCardData = sendCard,
receiveCardData = receiveCard,
fee = FeeState.Loaded(fee = "0.155 MATIC (0.14 $)"),
warnings = emptyList(),
warnings = listOf(SwapWarning.PermissionNeeded("DAI")),
networkCurrency = "MATIC",
swapButton = SwapButton(enabled = true, loading = false, onClick = {}),
onRefresh = {}, onBackClicked = {}, onChangeCardsClicked = {},
permissionState = SwapPermissionState.InProgress,
// alert = SwapWarning.GenericWarning("There was an error. Please try again.") {},
)
@Preview

View file

@ -1,9 +1,5 @@
@file:OptIn(ExperimentalFoundationApi::class)
package com.tangem.feature.swap.ui
import androidx.annotation.StringRes
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@ -23,9 +19,10 @@ import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorMatrix
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import coil.compose.SubcomposeAsyncImage
import coil.request.ImageRequest
@ -44,8 +41,8 @@ fun SwapSelectTokenScreen(state: SwapSelectTokenStateHolder, onBack: () -> Unit)
TangemTheme {
Scaffold(
content = {
ListOfTokens(state = state)
content = { padding ->
ListOfTokens(state = state, Modifier.padding(padding))
},
topBar = {
ExpandableSearchView(
@ -61,9 +58,9 @@ fun SwapSelectTokenScreen(state: SwapSelectTokenStateHolder, onBack: () -> Unit)
}
@Composable
private fun ListOfTokens(state: SwapSelectTokenStateHolder) {
private fun ListOfTokens(state: SwapSelectTokenStateHolder, modifier: Modifier = Modifier) {
LazyColumn(
modifier = Modifier
modifier = modifier
.fillMaxWidth()
.background(color = TangemTheme.colors.background.primary)
.padding(
@ -74,27 +71,10 @@ private fun ListOfTokens(state: SwapSelectTokenStateHolder) {
) {
stickyHeader {
Header(title = R.string.swapping_token_list_your_tokens)
}
itemsIndexed(items = state.addedTokens) { index, item ->
itemsIndexed(items = state.tokens) { index, item ->
TokenItem(token = item, onTokenClick = { state.onTokenSelected(item.id) })
if (index != state.addedTokens.lastIndex) Divider(
color = TangemTheme.colors.stroke.primary,
startIndent = TangemTheme.dimens.spacing54,
)
}
stickyHeader {
Header(title = R.string.swapping_token_list_other_tokens)
}
itemsIndexed(items = state.otherTokens) { index, item ->
TokenItem(token = item, onTokenClick = { state.onTokenSelected(item.id) })
if (index != state.otherTokens.lastIndex) Divider(
if (index != state.tokens.lastIndex) Divider(
color = TangemTheme.colors.stroke.primary,
startIndent = TangemTheme.dimens.spacing54,
)
@ -102,21 +82,6 @@ private fun ListOfTokens(state: SwapSelectTokenStateHolder) {
}
}
@Composable
private fun Header(@StringRes title: Int) {
Text(
text = stringResource(id = title).uppercase(),
style = TangemTheme.typography.overline,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier
.fillMaxWidth()
.background(color = TangemTheme.colors.background.primary)
.padding(vertical = TangemTheme.dimens.spacing6),
textAlign = TextAlign.Start,
)
}
@Composable
private fun TokenItem(
token: TokenToSelect,
@ -133,25 +98,10 @@ private fun TokenItem(
.clickable(
onClick = { onTokenClick(token.id) },
),
verticalAlignment = Alignment.CenterVertically,
) {
Box(
modifier = Modifier
.padding(end = TangemTheme.dimens.spacing12)
.align(Alignment.CenterVertically),
) {
val iconModifier = Modifier
.size(TangemTheme.dimens.size40)
SubcomposeAsyncImage(
modifier = iconModifier,
model = ImageRequest.Builder(LocalContext.current)
.data(token.iconUrl)
.crossfade(true)
.build(),
contentDescription = token.id,
loading = { TokenImageShimmer(modifier = iconModifier) },
error = { CurrencyPlaceholderIcon(modifier = iconModifier, id = token.id) },
)
}
TokenIcon(token = token)
Column(
modifier = Modifier
@ -160,7 +110,11 @@ private fun TokenItem(
Text(
text = token.name,
style = TangemTheme.typography.subtitle1,
color = TangemTheme.colors.text.primary1,
color = if (token.available) {
TangemTheme.colors.text.primary1
} else {
TangemTheme.colors.text.tertiary
},
)
SpacerW2()
Text(
@ -172,7 +126,14 @@ private fun TokenItem(
Spacer(modifier = Modifier.weight(1f))
if (token.addedTokenBalanceData != null) {
if (!token.available) {
Text(
text = stringResource(id = R.string.swapping_token_not_available),
style = TangemTheme.typography.caption,
color = TangemTheme.colors.text.tertiary,
)
} else if (token.addedTokenBalanceData != null) {
Column(
horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.Center,
@ -196,6 +157,35 @@ private fun TokenItem(
}
}
@Composable
private fun TokenIcon(token: TokenToSelect) {
Box(
modifier = Modifier
.padding(end = TangemTheme.dimens.spacing12),
) {
val iconModifier = Modifier
.size(TangemTheme.dimens.size40)
val colorFilter = if (!token.available) {
val matrix = ColorMatrix().apply { setToSaturation(0f) }
ColorFilter.colorMatrix(matrix)
} else {
null
}
SubcomposeAsyncImage(
modifier = iconModifier,
model = ImageRequest.Builder(LocalContext.current)
.data(token.iconUrl)
.crossfade(true)
.build(),
contentDescription = token.id,
loading = { TokenImageShimmer(modifier = iconModifier) },
error = { CurrencyPlaceholderIcon(modifier = iconModifier, id = token.id) },
alpha = if (!token.available) 0.7f else 1f,
colorFilter = colorFilter,
)
}
}
@Composable
private fun TokenImageShimmer(
modifier: Modifier = Modifier,
@ -224,12 +214,14 @@ private val token = TokenToSelect(
),
)
private val tokenNotAvailable = token.copy(available = false)
@Preview
@Composable
fun TokenScreenPreview() {
SwapSelectTokenScreen(
state = SwapSelectTokenStateHolder(
listOf(token, token, token), listOf(token, token, token), {}, {},
listOf(token, tokenNotAvailable, token), {}, {},
),
) {

View file

@ -1,9 +1,12 @@
package com.tangem.feature.swap.ui
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import com.tangem.core.ui.components.SuccessScreenContent
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.ResultScreenContent
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.swap.models.SwapSuccessStateHolder
@ -13,10 +16,17 @@ import com.tangem.feature.swap.presentation.R
fun SwapSuccessScreen(state: SwapSuccessStateHolder, onBack: () -> Unit) {
TangemTheme {
Scaffold(
content = {
SuccessScreenContent(
successMessage = state.message,
content = { padding ->
ResultScreenContent(
resultMessage = state.message,
resultColor = TangemTheme.colors.icon.attention,
onButtonClick = onBack,
icon = R.drawable.ic_clock_24,
secondaryButtonIcon = R.drawable.ic_arrow_top_right_24,
onSecondaryButtonClick = state.onSecondaryButtonClick,
secondaryButtonText = R.string.swapping_success_view_explorer_button_title,
title = R.string.swapping_success_view_title,
modifier = Modifier.padding(padding),
)
},
topBar = {
@ -28,4 +38,28 @@ fun SwapSuccessScreen(state: SwapSuccessStateHolder, onBack: () -> Unit) {
},
)
}
}
}
// region preview
private val state = SwapSuccessStateHolder(
message = "Swap of 1 000 DAI to 1 131,46 MATIC",
) {}
@Preview(showBackground = true)
@Composable
fun Preview_Success_InLightTheme() {
TangemTheme(isDark = false) {
SwapSuccessScreen(state) {}
}
}
@Preview(showBackground = true)
@Composable
fun Preview_Success_InDarkTheme() {
TangemTheme(isDark = true) {
SwapSuccessScreen(state) {}
}
}
// endregion preview

View file

@ -56,7 +56,6 @@ import com.tangem.core.ui.components.CurrencyPlaceholderIcon
import com.tangem.core.ui.components.FontSizeRange
import com.tangem.core.ui.components.ResizableText
import com.tangem.core.ui.components.SpacerH4
import com.tangem.core.ui.components.SpacerW12
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.swap.models.TransactionCardType
import com.valentinilk.shimmer.shimmer
@ -65,6 +64,7 @@ import com.valentinilk.shimmer.shimmer
fun TransactionCard(
modifier: Modifier = Modifier,
type: TransactionCardType,
balance: String,
amount: String?,
amountEquivalent: String?,
tokenIconUrl: String,
@ -87,7 +87,7 @@ fun TransactionCard(
horizontalAlignment = Alignment.Start,
) {
Header(type = type)
Header(balance = balance, type = type)
Content(
type = type,
@ -124,6 +124,7 @@ fun TransactionCard(
@Composable
private fun Header(
type: TransactionCardType,
balance: String,
modifier: Modifier = Modifier,
) {
Row(
@ -148,15 +149,13 @@ private fun Header(
style = MaterialTheme.typography.subtitle2,
modifier = Modifier.padding(top = TangemTheme.dimens.spacing12),
)
if (type is TransactionCardType.SendCard) {
Text(
text = stringResource(R.string.common_balance, type.balance),
color = TangemTheme.colors.text.tertiary,
maxLines = 1,
style = MaterialTheme.typography.body2,
modifier = Modifier.padding(top = TangemTheme.dimens.spacing12),
)
}
Text(
text = stringResource(R.string.common_balance, balance),
color = TangemTheme.colors.text.tertiary,
maxLines = 1,
style = MaterialTheme.typography.body2,
modifier = Modifier.padding(top = TangemTheme.dimens.spacing12),
)
}
}
@ -176,27 +175,6 @@ private fun Content(
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.Bottom,
) {
if (type is TransactionCardType.SendCard && !type.permissionIsGiven) {
Box(
modifier = Modifier
.size(TangemTheme.dimens.size56)
.background(
color = TangemTheme.colors.button.secondary,
shape = RoundedCornerShape(TangemTheme.dimens.size12),
),
contentAlignment = Alignment.Center,
) {
Icon(
painter = painterResource(id = R.drawable.ic_locked_24),
contentDescription = null,
tint = TangemTheme.colors.icon.primary1,
)
}
SpacerW12()
}
Column(
modifier = Modifier
.padding(
@ -453,13 +431,14 @@ fun Preview_SwapMainCard_InDarkTheme() {
@Composable
private fun TransactionCardPreview() {
TransactionCard(
type = TransactionCardType.SendCard("123", false, {}),
type = TransactionCardType.SendCard {},
amount = "1 000 000",
amountEquivalent = "1 000 000",
tokenIconUrl = "",
tokenCurrency = "DAI",
networkIconRes = R.drawable.img_polygon_22,
onChangeTokenClick = {},
balance = "123",
)
}