Updated on 2026-08-14
This commit is contained in:
parent
1e19ce06b8
commit
5ce498df75
15 changed files with 222 additions and 237 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
<string name="common_save_changes">Änderungen speichern</string>
|
||||
<string name="common_send">Absenden</string>
|
||||
<string name="common_success">Erfolg</string>
|
||||
<string name="common_fee_label">Gebühr</string>
|
||||
<string name="details_manage_security_access_code">Zugangscode</string>
|
||||
<string name="details_manage_security_access_code_description">Sie müssen den richtigen Zugangscode eingeben, bevor Sie die Karte scannen.</string>
|
||||
<string name="details_manage_security_long_tap">Langes Tippen</string>
|
||||
|
|
@ -40,7 +41,6 @@
|
|||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
<string name="send_extras_hint_memo">Memo</string>
|
||||
<string name="send_fee_include_description">inkl. Gebühr</string>
|
||||
<string name="send_fee_label">Gebühr</string>
|
||||
<string name="send_fee_picker_low">Niedrig</string>
|
||||
<string name="send_fee_picker_normal">Normal</string>
|
||||
<string name="send_fee_picker_priority">Priorität</string>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
<string name="common_save_changes">Sauvegarder les modifications</string>
|
||||
<string name="common_send">Envoyer</string>
|
||||
<string name="common_success">Avec succès</string>
|
||||
<string name="common_fee_label">Commissions</string>
|
||||
<string name="details_manage_security_access_code">Code d\'accès</string>
|
||||
<string name="details_manage_security_access_code_description">Vous devrez entrer le mot de passe correct avant de scanner la carte</string>
|
||||
<string name="details_manage_security_long_tap">Tenez la carte fermement</string>
|
||||
|
|
@ -40,7 +41,6 @@
|
|||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
<string name="send_extras_hint_memo">Memo</string>
|
||||
<string name="send_fee_include_description">Inclure les commissions</string>
|
||||
<string name="send_fee_label">Commissions</string>
|
||||
<string name="send_fee_picker_low">Bas</string>
|
||||
<string name="send_fee_picker_normal">Normal</string>
|
||||
<string name="send_fee_picker_priority">Priorité</string>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
<string name="common_save_changes">Mantieni le modifiche</string>
|
||||
<string name="common_send">Invia</string>
|
||||
<string name="common_success">Con successo</string>
|
||||
<string name="common_fee_label">Commissione</string>
|
||||
<string name="details_manage_security_access_code">Codice di accesso</string>
|
||||
<string name="details_manage_security_access_code_description">Prima di scansionare la carta sarà necessario inserire il codice di accesso corretto</string>
|
||||
<string name="details_manage_security_long_tap">Mantenimento della carta</string>
|
||||
|
|
@ -40,7 +41,6 @@
|
|||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
<string name="send_extras_hint_memo">Memo</string>
|
||||
<string name="send_fee_include_description">Includi commissione</string>
|
||||
<string name="send_fee_label">Commissione</string>
|
||||
<string name="send_fee_picker_low">Insufficiente</string>
|
||||
<string name="send_fee_picker_normal">Normale</string>
|
||||
<string name="send_fee_picker_priority">Prioritario</string>
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@
|
|||
<string name="common_submit">Отправить</string>
|
||||
<string name="common_success">Успешно</string>
|
||||
<string name="common_swap">Обмен</string>
|
||||
<string name="common_fee_label">Комиссия</string>
|
||||
<string name="common_terms_and_conditions">условия участия</string>
|
||||
<string name="common_transaction_failed">Ошибка транзакции</string>
|
||||
<string name="common_transactions">Транзакции</string>
|
||||
|
|
@ -423,7 +424,6 @@
|
|||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
<string name="send_extras_hint_memo">Memo</string>
|
||||
<string name="send_fee_include_description">Включая комиссию</string>
|
||||
<string name="send_fee_label">Комиссия</string>
|
||||
<string name="send_fee_picker_low">Низкая</string>
|
||||
<string name="send_fee_picker_normal">Нормальная</string>
|
||||
<string name="send_fee_picker_priority">Приоритетная</string>
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@
|
|||
<string name="common_submit">提交</string>
|
||||
<string name="common_success">成功</string>
|
||||
<string name="common_swap">交換</string>
|
||||
<string name="common_fee_label">費用</string>
|
||||
<string name="common_terms_and_conditions">條款和條件</string>
|
||||
<string name="common_transactions">交易</string>
|
||||
<string name="common_understand">我了解</string>
|
||||
|
|
@ -294,7 +295,6 @@
|
|||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
<string name="send_extras_hint_memo">Memo</string>
|
||||
<string name="send_fee_include_description">包含費用</string>
|
||||
<string name="send_fee_label">費用</string>
|
||||
<string name="send_fee_picker_low">低</string>
|
||||
<string name="send_fee_picker_normal">正常</string>
|
||||
<string name="send_fee_picker_priority">優先</string>
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@
|
|||
<string name="common_submit">Submit</string>
|
||||
<string name="common_success">Success</string>
|
||||
<string name="common_swap">Swap</string>
|
||||
<string name="common_fee_label">Fee</string>
|
||||
<string name="common_terms_and_conditions">terms and conditions</string>
|
||||
<string name="common_transaction_failed">Transaction failed</string>
|
||||
<string name="common_transactions">Transactions</string>
|
||||
|
|
@ -438,7 +439,6 @@
|
|||
<string name="send_extras_hint_memo">Memo</string>
|
||||
<string name="send_insufficient_funds">Insufficient funds for transfer</string>
|
||||
<string name="send_fee_include_description">Include fee</string>
|
||||
<string name="send_fee_label">Fee</string>
|
||||
<string name="send_fee_picker_low">Low</string>
|
||||
<string name="send_fee_picker_normal">Normal</string>
|
||||
<string name="send_fee_picker_priority">Priority</string>
|
||||
|
|
@ -536,6 +536,8 @@
|
|||
<string name="swapping_permission_unlimited">Unlimited</string>
|
||||
<string name="swapping_success_view_explorer_button_title">View in Explorer</string>
|
||||
<string name="swapping_success_view_title">In progress</string>
|
||||
<string name="swapping_success_from_title">You swap</string>
|
||||
<string name="swapping_success_to_title">You receive</string>
|
||||
<string name="swapping_swap_action">Swap</string>
|
||||
<string name="swapping_swap_of_to">Swap of %s to</string>
|
||||
<string name="swapping_tangem_fee_disclaimer">Quotes include an additional Tangem commission of %s. This helps us deliver a top-of-the-line product.</string>
|
||||
|
|
|
|||
|
|
@ -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 <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 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
|
||||
|
|
@ -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?,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -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<AppCurrency>,
|
||||
) {
|
||||
|
||||
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,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue