From fc8fcfce192a96ca50aa53054284b821650c6bb3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 13 Jan 2023 22:35:37 +0400 Subject: [PATCH] Updated on 2026-08-14 --- core/res/src/main/res/values-de/strings.xml | 4 + core/res/src/main/res/values-fr/strings.xml | 4 + core/res/src/main/res/values-it/strings.xml | 4 + core/res/src/main/res/values-ru/strings.xml | 4 + core/res/src/main/res/values/strings.xml | 4 + .../com/tangem/core/ui/components/Dialogs.kt | 143 ++++++++++++++++++ .../core/ui/components/SuccessScreen.kt | 84 ++++++++-- .../com/tangem/core/ui/components/Warnings.kt | 11 +- .../res/drawable/ic_arrow_top_right_24.xml | 10 ++ .../swap/models/SwapSelectTokenStateHolder.kt | 4 +- .../feature/swap/models/SwapStateHolder.kt | 9 +- .../swap/models/SwapSuccessStateHolder.kt | 1 + .../tangem/feature/swap/ui/StateBuilder.kt | 14 +- .../feature/swap/ui/SwapScreenContent.kt | 23 ++- .../feature/swap/ui/SwapSelectTokenScreen.kt | 120 +++++++-------- .../feature/swap/ui/SwapSuccessScreen.kt | 44 +++++- .../tangem/feature/swap/ui/TransactionCard.kt | 45 ++---- 17 files changed, 395 insertions(+), 133 deletions(-) create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/Dialogs.kt create mode 100644 core/ui/src/main/res/drawable/ic_arrow_top_right_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 e81c23629f..e62a78ccb6 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -191,6 +191,7 @@ Share Copy Erfolg + Close You Send You Receive Give Permission @@ -209,6 +210,9 @@ Other tokens Waiting Transaction in progress… + In progress + View in Explorer + not available Failed to load the information about the referral program. Please try again later. Failed to load the information about the referral program. Reason: %s. Please try again later. Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support. diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index 7fa8a61c7d..ada856b314 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -191,6 +191,7 @@ Share Copy Avec succès + Close You Send You Receive Give Permission @@ -209,6 +210,9 @@ Other tokens Waiting Transaction in progress… + In progress + View in Explorer + not available Failed to load the information about the referral program. Please try again later. Failed to load the information about the referral program. Reason: %s. Please try again later. Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support. diff --git a/core/res/src/main/res/values-it/strings.xml b/core/res/src/main/res/values-it/strings.xml index d183fa7130..d1e4f054a7 100644 --- a/core/res/src/main/res/values-it/strings.xml +++ b/core/res/src/main/res/values-it/strings.xml @@ -191,6 +191,7 @@ Share Copy Con successo + Close You Send You Receive Give Permission @@ -209,6 +210,9 @@ Other tokens Waiting Transaction in progress… + In progress + View in Explorer + not available Failed to load the information about the referral program. Please try again later. Failed to load the information about the referral program. Reason: %s. Please try again later. Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support. diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 1301436efc..9fa9a20bee 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -191,6 +191,7 @@ Поделиться Копировать Успешно + Закрыть Вы отправляете Вы получаете Дать разрешение @@ -209,6 +210,9 @@ Другие токены Подождите Транзакция в процессе… + В процессе + Открыть в обозревателе + не доступен Не удалось загрузить информацию по реферальной программе. Пожалуйста, попробуйте позже. Не удалось загрузить информацию по реферальной программе. Код ошибки: %s. Пожалуйста, попробуйте позже. Не удалось обработать вашу заявку на участие. Код ошибки: %s. Пожалуйста, попробуйте позже. Если проблема сохранится, вы можете обратиться в техподдержку. diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 0824f29b0f..8a2711bb79 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -191,6 +191,7 @@ Share Copy Success + Close You Send You Receive Give Permission @@ -209,6 +210,9 @@ Other tokens Waiting Transaction in progress… + In progress + View in Explorer + not available Failed to load the information about the referral program. Please try again later. Failed to load the information about the referral program. Error code: %s. Please try again later. Your participation request could not be processed. Error code: %s. Please try again later. If the problem persists — feel free to contact our support. diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Dialogs.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Dialogs.kt new file mode 100644 index 0000000000..b9f323d670 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/Dialogs.kt @@ -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 Figma component + */ +@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 \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/SuccessScreen.kt b/core/ui/src/main/java/com/tangem/core/ui/components/SuccessScreen.kt index 9464d4355c..c87490b133 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/SuccessScreen.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/SuccessScreen.kt @@ -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 Figma component */ @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 = {}, ) } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Warnings.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Warnings.kt index 86a1085ebf..0b0968581c 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/Warnings.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/Warnings.kt @@ -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, diff --git a/core/ui/src/main/res/drawable/ic_arrow_top_right_24.xml b/core/ui/src/main/res/drawable/ic_arrow_top_right_24.xml new file mode 100644 index 0000000000..e23e12a295 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_arrow_top_right_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt index 4510f9ece6..cf80940a59 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSelectTokenStateHolder.kt @@ -1,8 +1,7 @@ package com.tangem.feature.swap.models data class SwapSelectTokenStateHolder( - val addedTokens: List, - val otherTokens: List, + val tokens: List, 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, ) 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 3f78f0a311..391a4fdfc4 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 @@ -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 = 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 diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSuccessStateHolder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSuccessStateHolder.kt index 9fad250c3a..5b6a026b1f 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSuccessStateHolder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapSuccessStateHolder.kt @@ -2,4 +2,5 @@ package com.tangem.feature.swap.models data class SwapSuccessStateHolder( val message: String, + val onSecondaryButtonClick: () -> Unit, ) \ No newline at end of file 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 33bd25c82d..5e91941a56 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 @@ -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)) 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 a0475d02a6..0d7242fde3 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 @@ -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 diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt index f8be3fa3c2..2b2ad1c321 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt @@ -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), {}, {}, ), ) { diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt index 2311562db4..f2bc335b92 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSuccessScreen.kt @@ -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) { }, ) } -} \ No newline at end of file +} + +// 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 \ No newline at end of file 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 f5448c3386..ff81cb50e9 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 @@ -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", ) }