diff --git a/app/src/main/res/layout/layout_send_receipt.xml b/app/src/main/res/layout/layout_send_receipt.xml index 4a8fab6816..97e7afbb0f 100644 --- a/app/src/main/res/layout/layout_send_receipt.xml +++ b/app/src/main/res/layout/layout_send_receipt.xml @@ -33,7 +33,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dp" - android:text="@string/send_fee_label" + android:text="@string/common_fee_label" android:textColor="@color/text_tertiary" android:textStyle="bold" app:layout_constraintStart_toStartOf="parent" diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index 3bc3defd6d..ddbf073872 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -13,6 +13,7 @@ Änderungen speichern Absenden Erfolg + Gebühr Zugangscode Sie müssen den richtigen Zugangscode eingeben, bevor Sie die Karte scannen. Langes Tippen @@ -40,7 +41,6 @@ Tag Memo inkl. Gebühr - Gebühr Niedrig Normal Priorität diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index 097c597e6e..9ac49cb6d8 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -13,6 +13,7 @@ Sauvegarder les modifications Envoyer Avec succès + Commissions Code d\'accès Vous devrez entrer le mot de passe correct avant de scanner la carte Tenez la carte fermement @@ -40,7 +41,6 @@ Tag Memo Inclure les commissions - Commissions Bas Normal Priorité diff --git a/core/res/src/main/res/values-it/strings.xml b/core/res/src/main/res/values-it/strings.xml index aa5a2b3d47..f673e3bf38 100644 --- a/core/res/src/main/res/values-it/strings.xml +++ b/core/res/src/main/res/values-it/strings.xml @@ -13,6 +13,7 @@ Mantieni le modifiche Invia Con successo + Commissione Codice di accesso Prima di scansionare la carta sarà necessario inserire il codice di accesso corretto Mantenimento della carta @@ -40,7 +41,6 @@ Tag Memo Includi commissione - Commissione Insufficiente Normale Prioritario diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 3473a16427..77fd07304f 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -126,6 +126,7 @@ Отправить Успешно Обмен + Комиссия условия участия Ошибка транзакции Транзакции @@ -423,7 +424,6 @@ Tag Memo Включая комиссию - Комиссия Низкая Нормальная Приоритетная 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 0b9c4e90a9..f595e68773 100644 --- a/core/res/src/main/res/values-zh-rTW/strings.xml +++ b/core/res/src/main/res/values-zh-rTW/strings.xml @@ -90,6 +90,7 @@ 提交 成功 交換 + 費用 條款和條件 交易 我了解 @@ -294,7 +295,6 @@ Tag Memo 包含費用 - 費用 正常 優先 diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index e88c410260..ffc1858e33 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -125,6 +125,7 @@ Submit Success Swap + Fee terms and conditions Transaction failed Transactions @@ -438,7 +439,6 @@ Memo Insufficient funds for transfer Include fee - Fee Low Normal Priority @@ -536,6 +536,8 @@ Unlimited View in Explorer In progress + You swap + You receive Swap Swap of %s to Quotes include an additional Tangem commission of %s. This helps us deliver a top-of-the-line product. 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 deleted file mode 100644 index c5c3feabc1..0000000000 --- a/core/ui/src/main/java/com/tangem/core/ui/components/SuccessScreen.kt +++ /dev/null @@ -1,181 +0,0 @@ -package com.tangem.core.ui.components - -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.Icon -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.AnnotatedString -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.R -import com.tangem.core.ui.res.TangemTheme - -/** - * Screen for showing result - * - * @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 ResultScreenContent( - resultMessage: AnnotatedString, - onButtonClick: () -> Unit, - modifier: Modifier = Modifier, - @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, -) { - Column( - modifier = modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .background(TangemTheme.colors.background.secondary) - .padding( - horizontal = TangemTheme.dimens.spacing16, - vertical = TangemTheme.dimens.spacing32, - ), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center, - ) { - SpacerHHalf() - SuccessImage(resultColor = resultColor, icon = icon) - SpacerH50() - Text( - text = stringResource(id = title), - style = TangemTheme.typography.h2, - color = TangemTheme.colors.text.primary1, - textAlign = TextAlign.Center, - modifier = Modifier.fillMaxWidth(), - ) - SpacerH12() - Text( - 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, - ) - SpacerH12() - } - PrimaryButton( - text = stringResource(id = R.string.common_close), - modifier = Modifier - .fillMaxWidth(), - onClick = { onButtonClick() }, - ) - } -} - -@Composable -fun SuccessImage(resultColor: Color, @DrawableRes icon: Int) { - Box( - modifier = Modifier - .background( - color = resultColor.copy(alpha = 0.2f), - shape = CircleShape, - ), - contentAlignment = Alignment.Center, - ) { - Box( - modifier = Modifier - .padding(TangemTheme.dimens.spacing24) - .background( - color = resultColor, - shape = CircleShape, - ) - .height(TangemTheme.dimens.size93) - .width(TangemTheme.dimens.size93), - contentAlignment = Alignment.Center, - ) { - Icon( - painter = painterResource(id = icon), - contentDescription = null, - tint = TangemTheme.colors.icon.primary2, - modifier = Modifier.size(TangemTheme.dimens.size40), - ) - } - } -} - -@Composable -private fun SecondaryButtonForResultScreen( - @StringRes secondaryButtonText: Int, - onSecondaryButtonClick: () -> Unit, - @DrawableRes secondaryButtonIcon: Int? = null, -) { - if (secondaryButtonIcon != null) { - SecondaryButtonIconStart( - text = stringResource(id = secondaryButtonText), - iconResId = secondaryButtonIcon, - onClick = onSecondaryButtonClick, - modifier = Modifier.fillMaxWidth(), - ) - } else { - SecondaryButton( - text = stringResource(id = secondaryButtonText), - onClick = onSecondaryButtonClick, - modifier = Modifier.fillMaxWidth(), - ) - } -} - -// region preview - -@Composable -private fun SuccessScreenPreview() { - ResultScreenContent( - resultMessage = AnnotatedString("Swap of 1 000 DAI to 1 131,46 MATIC"), - secondaryButtonText = R.string.swapping_success_view_explorer_button_title, - onSecondaryButtonClick = {}, - onButtonClick = {}, - ) -} - -@Preview(showBackground = true) -@Composable -private fun Preview_SuccessScreenContent_InLightTheme() { - TangemTheme(isDark = false) { - SuccessScreenPreview() - } -} - -@Preview(showBackground = true) -@Composable -private fun Preview_SuccessScreenContent_InDarkTheme() { - TangemTheme(isDark = true) { - SuccessScreenPreview() - } -} - -// endregion preview \ No newline at end of file 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 a685079175..21724f8e9e 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 @@ -5,6 +5,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.notifications.NotificationConfig import com.tangem.core.ui.components.states.Item import com.tangem.core.ui.components.states.SelectableItemsState +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.feature.swap.domain.models.ui.TxFee import com.tangem.feature.swap.models.states.FeeItemState import com.tangem.feature.swap.models.states.ProviderState @@ -44,6 +45,7 @@ sealed class SwapCardState { data class SwapCardData( val type: TransactionCardType, val amountEquivalent: String?, + val token: CryptoCurrencyStatus?, val coinId: String?, val amountTextFieldValue: TextFieldValue?, val tokenIconUrl: String?, 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 56dc3525a8..640c4809d3 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 @@ -1,7 +1,20 @@ package com.tangem.feature.swap.models +import com.tangem.core.ui.components.currency.tokenicon.TokenIconState +import com.tangem.core.ui.extensions.TextReference +import com.tangem.feature.swap.domain.models.domain.SwapProvider + data class SwapSuccessStateHolder( - val fromTokenAmount: String, - val toTokenAmount: String, + val timestamp: Long, + val txUrl: String, + val fee: TextReference, + val rate: TextReference, + val selectedProvider: SwapProvider, + val fromTokenAmount: TextReference, + val toTokenAmount: TextReference, + val fromTokenFiatAmount: TextReference, + val toTokenFiatAmount: TextReference, + val fromTokenIconState: TokenIconState?, + val toTokenIconState: TokenIconState?, 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 b6a1ec961e..6e30057199 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 @@ -4,6 +4,7 @@ import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import com.tangem.common.Provider import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.currency.tokenicon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.components.notifications.NotificationConfig import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference @@ -22,6 +23,7 @@ import com.tangem.feature.swap.domain.models.ui.* import com.tangem.feature.swap.models.* import com.tangem.feature.swap.models.states.* import com.tangem.feature.swap.presentation.R +import com.tangem.feature.swap.viewmodels.SwapProcessDataState import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import java.math.BigDecimal @@ -37,6 +39,8 @@ internal class StateBuilder( private val appCurrencyProvider: Provider, ) { + private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter) + private val tokensDataConverter = TokensDataConverter( onSearchEntered = actions.onSearchEntered, onTokenSelected = actions.onTokenSelected, @@ -52,6 +56,7 @@ internal class StateBuilder( type = TransactionCardType.SendCard(actions.onAmountChanged, actions.onAmountSelected), amountEquivalent = null, amountTextFieldValue = null, + token = null, tokenIconUrl = initialCurrency.iconUrl, tokenCurrency = initialCurrency.symbol, coinId = initialCurrency.network.backendId, @@ -65,6 +70,7 @@ internal class StateBuilder( amountEquivalent = null, tokenIconUrl = "", tokenCurrency = "", + token = null, amountTextFieldValue = null, canSelectAnotherToken = false, balance = "", @@ -98,6 +104,7 @@ internal class StateBuilder( text = "0", ), amountEquivalent = "0 ${appCurrencyProvider.invoke().symbol}", + token = fromToken, tokenIconUrl = uiStateHolder.sendCardData.tokenIconUrl, coinId = uiStateHolder.sendCardData.coinId, isNotNativeToken = uiStateHolder.sendCardData.isNotNativeToken, @@ -148,6 +155,7 @@ internal class StateBuilder( type = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.SendCard), amountTextFieldValue = uiStateHolder.sendCardData.amountTextFieldValue, amountEquivalent = null, + token = uiStateHolder.sendCardData.token, tokenIconUrl = fromToken.iconUrl, tokenCurrency = fromToken.symbol, coinId = fromToken.network.backendId, @@ -160,6 +168,7 @@ internal class StateBuilder( type = TransactionCardType.ReceiveCard(), amountTextFieldValue = null, amountEquivalent = null, + token = uiStateHolder.receiveCardData.token, tokenIconUrl = toToken.iconUrl, tokenCurrency = toToken.symbol, coinId = toToken.network.backendId, @@ -225,6 +234,7 @@ internal class StateBuilder( type = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.SendCard), amountTextFieldValue = uiStateHolder.sendCardData.amountTextFieldValue, amountEquivalent = getFormattedFiatAmount(quoteModel.fromTokenInfo.amountFiat), + token = fromCurrencyStatus, tokenIconUrl = uiStateHolder.sendCardData.tokenIconUrl, coinId = fromCurrencyStatus.currency.network.backendId, isNotNativeToken = uiStateHolder.sendCardData.isNotNativeToken, @@ -237,6 +247,7 @@ internal class StateBuilder( type = TransactionCardType.ReceiveCard(), amountTextFieldValue = TextFieldValue(quoteModel.toTokenInfo.tokenAmount.formatToUIRepresentation()), amountEquivalent = getFormattedFiatAmount(quoteModel.toTokenInfo.amountFiat), + token = toCurrencyStatus, tokenIconUrl = uiStateHolder.receiveCardData.tokenIconUrl, coinId = toCurrencyStatus.currency.network.backendId, isNotNativeToken = uiStateHolder.receiveCardData.isNotNativeToken, @@ -282,6 +293,7 @@ internal class StateBuilder( type = requireNotNull(uiStateHolder.sendCardData.type as? TransactionCardType.SendCard), amountTextFieldValue = uiStateHolder.sendCardData.amountTextFieldValue, amountEquivalent = emptyAmountState.zeroAmountEquivalent, + token = uiStateHolder.sendCardData.token, tokenIconUrl = uiStateHolder.sendCardData.tokenIconUrl, coinId = uiStateHolder.sendCardData.coinId, isNotNativeToken = uiStateHolder.sendCardData.isNotNativeToken, @@ -294,6 +306,7 @@ internal class StateBuilder( type = TransactionCardType.ReceiveCard(), amountTextFieldValue = TextFieldValue("0"), amountEquivalent = emptyAmountState.zeroAmountEquivalent, + token = uiStateHolder.receiveCardData.token, tokenIconUrl = uiStateHolder.receiveCardData.tokenIconUrl, coinId = uiStateHolder.receiveCardData.coinId, isNotNativeToken = uiStateHolder.receiveCardData.isNotNativeToken, @@ -434,12 +447,38 @@ internal class StateBuilder( fun createSuccessState( uiState: SwapStateHolder, txState: TxState.TxSent, + dataState: SwapProcessDataState, + txUrl: String, onSecondaryBtnClick: () -> Unit, ): SwapStateHolder { + val fromToken = requireNotNull(uiState.sendCardData as? SwapCardState.SwapCardData) + val toToken = requireNotNull(uiState.receiveCardData as? SwapCardState.SwapCardData) + val fromTokenIconState = fromToken.token?.let(iconStateConverter::convert) + val toTokenIconState = toToken.token?.let(iconStateConverter::convert) + val fee = uiState.fee as? FeeItemState.Content ?: return uiState + val fromCryptoCurrencyStatus = requireNotNull(fromToken.token) + val toCryptoCurrencyStatus = requireNotNull(toToken.token) + val rate = txState.toAmount?.toBigDecimal()?.divide( + txState.fromAmount?.toBigDecimal(), + toCryptoCurrencyStatus.currency.decimals, + RoundingMode.HALF_UP, + ) + val fromCurrencySymbol = fromCryptoCurrencyStatus.currency.symbol + val toCurrencySymbol = toCryptoCurrencyStatus.currency.symbol + return uiState.copy( successState = SwapSuccessStateHolder( - fromTokenAmount = txState.fromAmount ?: "", - toTokenAmount = txState.toAmount ?: "", + timestamp = System.currentTimeMillis(), + txUrl = txUrl, + selectedProvider = requireNotNull(dataState.selectedProvider), + fee = TextReference.Str("${fee.amountCrypto} ${fee.symbolCrypto} (${fee.amountFiatFormatted})"), + rate = TextReference.Str("1 $fromCurrencySymbol ≈ $rate $toCurrencySymbol"), + fromTokenAmount = TextReference.Str("${txState.fromAmount.orEmpty()} ${fromToken.tokenCurrency}}"), + toTokenAmount = TextReference.Str("${txState.toAmount.orEmpty()} ${toToken.tokenCurrency}}"), + fromTokenFiatAmount = TextReference.Str(fromToken.amountEquivalent.orEmpty()), + toTokenFiatAmount = TextReference.Str(toToken.amountEquivalent.orEmpty()), + fromTokenIconState = fromTokenIconState, + toTokenIconState = toTokenIconState, onSecondaryButtonClick = onSecondaryBtnClick, ), ) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheet.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheet.kt index 724c3e44d8..39ecc48a11 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheet.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapPermissionBottomSheet.kt @@ -272,7 +272,7 @@ private fun DropdownSelector( @Composable private fun FeeItem(fee: String) { InformationItem( - subtitle = stringResource(id = R.string.send_fee_label), + subtitle = stringResource(id = R.string.common_fee_label), value = fee, ) } 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 8d81d961db..bf382cbc41 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 @@ -336,6 +336,7 @@ private val sendCard = SwapCardState.SwapCardData( canSelectAnotherToken = false, balance = "123", coinId = "", + token = null, isBalanceHidden = false, ) @@ -349,6 +350,7 @@ private val receiveCard = SwapCardState.SwapCardData( canSelectAnotherToken = true, balance = "33333", coinId = "", + token = null, isBalanceHidden = false, ) 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 a05fbdb29b..909239af13 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,18 +1,29 @@ package com.tangem.feature.swap.ui -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.systemBarsPadding +import androidx.annotation.StringRes +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* import androidx.compose.material.Scaffold import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.components.ResultScreenContent +import com.tangem.core.ui.components.* import com.tangem.core.ui.components.appbar.AppBarWithBackButton +import com.tangem.core.ui.components.currency.tokenicon.TokenIconState +import com.tangem.core.ui.components.inputrow.InputRowBestRate +import com.tangem.core.ui.components.inputrow.InputRowDefault +import com.tangem.core.ui.components.inputrow.InputRowImage +import com.tangem.core.ui.components.transactions.TransactionDoneTitle +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.shareText import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.swap.domain.models.domain.ExchangeProviderType +import com.tangem.feature.swap.domain.models.domain.SwapProvider import com.tangem.feature.swap.models.SwapSuccessStateHolder import com.tangem.feature.swap.presentation.R @@ -21,41 +32,117 @@ fun SwapSuccessScreen(state: SwapSuccessStateHolder, onBack: () -> Unit) { Scaffold( modifier = Modifier.systemBarsPadding(), content = { padding -> - ResultScreenContent( - resultMessage = makeSuccessMessage( - fromTokenAmount = state.fromTokenAmount, - toTokenAmount = state.toTokenAmount, - ), - 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), - ) + SwapSuccessScreenContent(padding = padding, state = state) }, topBar = { AppBarWithBackButton( - text = stringResource(R.string.common_swap), onBackClick = onBack, iconRes = R.drawable.ic_close_24, ) }, + bottomBar = { + SwapSuccessScreenButtons( + textRes = R.string.common_close, + txUrl = state.txUrl, + onExploreClick = state.onSecondaryButtonClick, + onDoneClick = onBack, + ) + }, ) } @Composable -private fun makeSuccessMessage(fromTokenAmount: String, toTokenAmount: String): AnnotatedString { - val swapToString = stringResource(id = R.string.swapping_swap_of_to, fromTokenAmount) - val message = "$swapToString\n$toTokenAmount" - return buildAnnotatedString { - append(message) - addStyle( - style = SpanStyle(color = TangemTheme.colors.text.accent), - start = message.indexOf(toTokenAmount), - end = message.length, +private fun SwapSuccessScreenContent(state: SwapSuccessStateHolder, padding: PaddingValues) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(padding) + .background(TangemTheme.colors.background.secondary) + .padding(horizontal = TangemTheme.dimens.spacing16), + ) { + TransactionDoneTitle(titleRes = R.string.swapping_success_view_title, date = 0L) + SpacerH16() + InputRowImage( + title = TextReference.Res(R.string.swapping_success_from_title), + subtitle = state.fromTokenAmount, + caption = state.fromTokenFiatAmount, + tokenIconState = state.fromTokenIconState ?: TokenIconState.Loading, + modifier = Modifier + .clip(TangemTheme.shapes.roundedCornersXMedium) + .background(TangemTheme.colors.background.action), + ) + SpacerH16() + InputRowImage( + title = TextReference.Res(R.string.swapping_success_to_title), + subtitle = state.toTokenAmount, + caption = state.toTokenFiatAmount, + tokenIconState = state.toTokenIconState ?: TokenIconState.Loading, + modifier = Modifier + .clip(TangemTheme.shapes.roundedCornersXMedium) + .background(TangemTheme.colors.background.action), + ) + SpacerH16() + InputRowBestRate( + imageUrl = state.selectedProvider.imageLarge, + title = TextReference.Str(state.selectedProvider.name), + titleExtra = TextReference.Str(state.selectedProvider.type.name), + subtitle = state.rate, + modifier = Modifier + .clip(TangemTheme.shapes.roundedCornersXMedium) + .background(TangemTheme.colors.background.action), + ) + SpacerH16() + InputRowDefault( + title = TextReference.Res(R.string.common_fee_label), + text = state.fee, + modifier = Modifier + .clip(TangemTheme.shapes.roundedCornersXMedium) + .background(TangemTheme.colors.background.action), + ) + } +} + +@Composable +private fun SwapSuccessScreenButtons( + @StringRes textRes: Int, + txUrl: String, + onExploreClick: () -> Unit, + onDoneClick: () -> Unit, +) { + val hapticFeedback = LocalHapticFeedback.current + val context = LocalContext.current + + Column( + modifier = Modifier + .background(TangemTheme.colors.background.secondary) + .padding(TangemTheme.dimens.spacing16), + ) { + if (txUrl.isNotBlank()) { + Row { + SecondaryButtonIconStart( + text = stringResource(id = com.tangem.core.ui.R.string.common_explore), + iconResId = com.tangem.core.ui.R.drawable.ic_web_24, + onClick = onExploreClick, + modifier = Modifier.weight(1f), + ) + SpacerW12() + SecondaryButtonIconStart( + text = stringResource(id = com.tangem.core.ui.R.string.common_share), + iconResId = com.tangem.core.ui.R.drawable.ic_share_24, + onClick = { + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + context.shareText(txUrl) + }, + modifier = Modifier.weight(1f), + ) + } + SpacerH12() + } + PrimaryButton( + text = stringResource(id = textRes), + enabled = true, + onClick = onDoneClick, + modifier = Modifier.fillMaxWidth(), ) } } @@ -63,9 +150,25 @@ private fun makeSuccessMessage(fromTokenAmount: String, toTokenAmount: String): // region preview private val state = SwapSuccessStateHolder( - fromTokenAmount = "1 000 DAI", - toTokenAmount = "1 131,46 MATIC", -) {} + timestamp = 0L, + txUrl = "https://www.google.com/#q=nam", + fee = TextReference.Str("1 000 DAI ~ 1 000 MATIC"), + selectedProvider = SwapProvider( + providerId = "1inch", + rateTypes = listOf(), + name = "1inch", + type = ExchangeProviderType.DEX, + imageLarge = "", + ), + fromTokenAmount = TextReference.Str("1 000 DAI"), + toTokenAmount = TextReference.Str("1 000 MATIC"), + fromTokenFiatAmount = TextReference.Str("1 000 $"), + toTokenFiatAmount = TextReference.Str("1 000 $"), + fromTokenIconState = TokenIconState.Loading, + toTokenIconState = TokenIconState.Loading, + rate = TextReference.Str("1 000 DAI ~ 1 000 MATIC"), + onSecondaryButtonClick = {}, +) @Preview(showBackground = true) @Composable diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index 41287a8bc6..76e960107b 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -351,17 +351,22 @@ internal class SwapViewModel @Inject constructor( .onSuccess { when (it) { is TxState.TxSent -> { - uiState = stateBuilder.createSuccessState(uiState, it) { - val txHash = it.txAddress - if (txHash.isNotEmpty()) { - swapRouter.openUrl( - blockchainInteractor.getExplorerTransactionLink( - networkId = dataState.networkId, - txAddress = it.txAddress, - ), - ) - } - } + val url = blockchainInteractor.getExplorerTransactionLink( + networkId = dataState.networkId, + txAddress = it.txAddress, + ) + uiState = stateBuilder.createSuccessState( + uiState = uiState, + txState = it, + dataState = dataState, + txUrl = url, + onSecondaryBtnClick = { + val txHash = it.txAddress + if (txHash.isNotEmpty()) { + swapRouter.openUrl(url) + } + }, + ) analyticsEventHandler.send(SwapEvents.SwapInProgressScreen) swapRouter.openScreen(SwapNavScreen.Success) }