Updated on 2026-08-14

This commit is contained in:
Tangem 2023-12-26 15:28:35 +03:00
parent c7aabfa5d3
commit c626a0c35f
17 changed files with 61 additions and 38 deletions

View file

@ -189,8 +189,6 @@
<string name="disclaimer_title">Условия использования</string>
<string name="error_update_app">К сожалению, текущая версия приложения не готова к работе с этой картой, проверьте наличие обновлений</string>
<string name="error_wrong_wallet_tapped">Вы использовали карту от другого кошелька. Приложите карту, связанную с этим кошельком.</string>
<string name="exchange_receive_view_header">Вы получаете</string>
<string name="exchange_send_view_header">Вы отправляете</string>
<string name="exchange_tokens_available_tokens_header">Мои токены</string>
<string name="exchange_tokens_empty_tokens">У вас нет добавленных токенов. Добавьте токены для обмена</string>
<string name="express_token_list_empty_search">Токены не найдены. Пожалуйста, попробуйте другой запрос</string>
@ -524,6 +522,7 @@
<string name="swapping_approve_information_text">Подтверждения считаются отраслевым стандартом для всех децентрализованных бирж и защищают ваш кошелек от доступа со стороны смарт-контракта без вашего разрешения. По замыслу смарт-контракты не могут получить доступ к вашим токенам, если вы не одобрите доступ со своей стороны. «Разблокируя» свои токены, вы даете смарт-контракту 1inch разрешение тратить ваши активы. Майнеры сети получают компенсацию за газ (оплачиваемый вами) за запись этого действия в блокчейне. Как только разрешение будет предоставлено, вы сможете обменять свой токен.</string>
<string name="swapping_approve_information_title">Подтвердить</string>
<string name="swapping_error_wrapper">Ошибка: %s</string>
<string name="swapping_from_title">Вы отправляете</string>
<string name="swapping_generic_error">Произошла ошибка. Пожалуйста, попробуйте еще раз.</string>
<string name="swapping_give_permission">Дать разрешение</string>
<string name="swapping_high_price_impact">Сильные колебания цены!</string>
@ -542,13 +541,12 @@
<string name="swapping_permission_rows_your_wallet">Ваш кошелек</string>
<string name="swapping_permission_subheader">Чтобы продолжить, вам нужно разрешить смарт-контракту 1inch использовать ваш %s</string>
<string name="swapping_permission_unlimited">Безлимитно</string>
<string name="swapping_success_from_title">Вы отправляете</string>
<string name="swapping_success_to_title">Вы получите</string>
<string name="swapping_success_view_explorer_button_title">Открыть в обозревателе</string>
<string name="swapping_success_view_title">В процессе</string>
<string name="swapping_swap_action">Обменять</string>
<string name="swapping_swap_of_to">Обмен %s на</string>
<string name="swapping_tangem_fee_disclaimer">Котировки включают дополнительную комиссию Tangem в размере %s. Это помогает нам предоставлять первоклассный продукт.</string>
<string name="swapping_to_title">Вы получите</string>
<string name="swapping_token_list_other_tokens">Другие токены</string>
<string name="swapping_token_list_title">Выберите токен</string>
<string name="swapping_token_list_your_tokens">Ваши токены</string>

View file

@ -137,8 +137,6 @@
<string name="disclaimer_title">服務條款</string>
<string name="error_update_app">糟糕,當前版本的應用程序無法使用此卡,請檢查更新</string>
<string name="error_wrong_wallet_tapped">您使用了另一個錢包中的卡。點按與此錢包關聯的卡片</string>
<string name="exchange_receive_view_header">您收到</string>
<string name="exchange_send_view_header">您發送</string>
<string name="feedback_data_collection_message">以下信息是可選的。如果不想共享,可以將其刪除</string>
<string name="feedback_preface_rate_negative">告訴我們您缺少哪些功能,我們會盡力幫助您</string>
<string name="feedback_preface_scan_failed">請告訴我們你有什麼卡</string>

View file

@ -188,8 +188,6 @@
<string name="disclaimer_title">Terms of Service</string>
<string name="error_update_app">Oops, the current version of the application is not ready to work with this card, please check for updates.</string>
<string name="error_wrong_wallet_tapped">You have used a card from another wallet. Tap the card associated with this wallet</string>
<string name="exchange_receive_view_header">You receive</string>
<string name="exchange_send_view_header">You send</string>
<string name="exchange_tokens_available_tokens_header">My tokens</string>
<string name="exchange_tokens_empty_tokens">You haven\'t added any tokens yet. Add tokens via Market to swap</string>
<string name="express_token_list_empty_search">No tokens found. Please try another request</string>
@ -535,6 +533,7 @@
<string name="swapping_approve_information_text">All decentralized exchanges require approvals to prevent smart contracts from accessing your wallet without your permission. By design, smart contracts can\'t access your tokens unless you approve. By \"unlocking\" your tokens, you authorize the 1-inch smart contract to spend them. The network\'s miners receive a gas fee (paid by you) to record this action on the blockchain. You can swap your token after giving approval.</string>
<string name="swapping_approve_information_title">Approve</string>
<string name="swapping_error_wrapper">Error: %s</string>
<string name="swapping_from_title">You swap</string>
<string name="swapping_generic_error">There was an error. Please try again.</string>
<string name="swapping_give_permission">Give Permission</string>
<string name="swapping_high_price_impact">High price impact!</string>
@ -553,13 +552,12 @@
<string name="swapping_permission_rows_your_wallet">Your wallet</string>
<string name="swapping_permission_subheader">To continue, grant 1inch smart contracts permission to use your %s</string>
<string name="swapping_permission_unlimited">Unlimited</string>
<string name="swapping_success_from_title">You swap</string>
<string name="swapping_success_to_title">You receive</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_swap_action">Swap</string>
<string name="swapping_swap_of_to">Swap %s for</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>
<string name="swapping_to_title">You receive</string>
<string name="swapping_token_list_other_tokens">Other tokens</string>
<string name="swapping_token_list_title">Choose token</string>
<string name="swapping_token_list_your_tokens">Your tokens</string>

View file

@ -22,7 +22,13 @@ object BigDecimalFormatter {
roundingMode = RoundingMode.DOWN
}
return formatter.format(cryptoAmount) + "\u2009$cryptoCurrency"
return formatter.format(cryptoAmount).let {
if (cryptoCurrency.isEmpty()) {
it
} else {
it + "\u2009$cryptoCurrency"
}
}
}
fun formatCryptoAmount(cryptoAmount: BigDecimal?, cryptoCurrency: CryptoCurrency): String {

View file

@ -8,7 +8,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.feature.swap.domain.models.domain.CryptoCurrencySwapInfo
import com.tangem.feature.swap.domain.models.ui.CurrenciesGroup
import com.tangem.feature.swap.models.CurrenciesGroupWithFromCurrency
import com.tangem.feature.swap.models.SwapSelectTokenStateHolder
import com.tangem.feature.swap.models.TokenBalanceData
import com.tangem.feature.swap.models.TokenToSelectState
@ -21,17 +21,21 @@ class TokensDataConverter(
private val onTokenSelected: (String) -> Unit,
private val isBalanceHiddenProvider: Provider<Boolean>,
private val appCurrencyProvider: Provider<AppCurrency>,
) : Converter<CurrenciesGroup, SwapSelectTokenStateHolder> {
) : Converter<CurrenciesGroupWithFromCurrency, SwapSelectTokenStateHolder> {
override fun convert(value: CurrenciesGroup): SwapSelectTokenStateHolder {
override fun convert(value: CurrenciesGroupWithFromCurrency): SwapSelectTokenStateHolder {
val group = value.group
val availableTitle = TokenToSelectState.Title(
resourceReference(R.string.exchange_tokens_available_tokens_header),
)
val unavailableTitle = TokenToSelectState.Title(
resourceReference(R.string.exchange_tokens_available_tokens_header),
resourceReference(
R.string.exchange_tokens_unavailable_tokens_header,
wrappedList(value.fromCurrency.name),
),
)
return SwapSelectTokenStateHolder(
availableTokens = value.available.map { tokenWithBalanceToTokenToSelect(it, true) }
availableTokens = group.available.map { tokenWithBalanceToTokenToSelect(it, true) }
.toMutableList()
.apply {
if (this.isNotEmpty()) {
@ -39,7 +43,7 @@ class TokensDataConverter(
}
}
.toImmutableList(),
unavailableTokens = value.unavailable.map { tokenWithBalanceToTokenToSelect(it, false) }
unavailableTokens = group.unavailable.map { tokenWithBalanceToTokenToSelect(it, false) }
.toMutableList()
.apply {
if (this.isNotEmpty()) {
@ -49,7 +53,7 @@ class TokensDataConverter(
.toImmutableList(),
onSearchEntered = onSearchEntered,
onTokenSelected = onTokenSelected,
afterSearch = value.afterSearch,
afterSearch = group.afterSearch,
)
}

View file

@ -0,0 +1,9 @@
package com.tangem.feature.swap.models
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.feature.swap.domain.models.ui.CurrenciesGroup
data class CurrenciesGroupWithFromCurrency(
val group: CurrenciesGroup,
val fromCurrency: CryptoCurrency,
)

View file

@ -105,12 +105,12 @@ sealed interface TransactionCardType {
data class Inputtable(
val onAmountChanged: ((String) -> Unit),
val onFocusChanged: ((Boolean) -> Unit),
@StringRes override val headerResId: Int = R.string.exchange_send_view_header,
@StringRes override val headerResId: Int = R.string.swapping_from_title,
) : TransactionCardType
data class ReadOnly(
val highPriceImpact: String? = null,
@StringRes override val headerResId: Int = R.string.exchange_receive_view_header,
@StringRes override val headerResId: Int = R.string.swapping_to_title,
) : TransactionCardType
}

View file

@ -34,6 +34,7 @@ fun ChooseProviderBottomSheet(config: TangemBottomSheetConfig) {
}
}
@Suppress("LongMethod")
@Composable
private fun ChooseProviderBottomSheetContent(content: ChooseProviderBottomSheetConfig) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {

View file

@ -107,7 +107,7 @@ internal class StateBuilder(
isNotNativeToken = uiStateHolder.sendCardData.isNotNativeToken,
tokenCurrency = uiStateHolder.sendCardData.tokenCurrency,
canSelectAnotherToken = uiStateHolder.sendCardData.canSelectAnotherToken,
balance = fromToken.getFormattedAmount(),
balance = fromToken.getFormattedAmount(isNeedSymbol = false),
networkIconRes = getActiveIconRes(fromToken.currency.network.id.value),
isBalanceHidden = isBalanceHiddenProvider(),
),
@ -227,7 +227,7 @@ internal class StateBuilder(
tokenCurrency = uiStateHolder.sendCardData.tokenCurrency,
canSelectAnotherToken = uiStateHolder.sendCardData.canSelectAnotherToken,
networkIconRes = uiStateHolder.sendCardData.networkIconRes,
balance = fromCurrencyStatus.getFormattedAmount(),
balance = fromCurrencyStatus.getFormattedAmount(isNeedSymbol = false),
isBalanceHidden = isBalanceHiddenProvider(),
),
receiveCardData = SwapCardState.SwapCardData(
@ -241,7 +241,7 @@ internal class StateBuilder(
tokenCurrency = uiStateHolder.receiveCardData.tokenCurrency,
canSelectAnotherToken = uiStateHolder.receiveCardData.canSelectAnotherToken,
networkIconRes = uiStateHolder.receiveCardData.networkIconRes,
balance = toCurrencyStatus.getFormattedAmount(),
balance = toCurrencyStatus.getFormattedAmount(isNeedSymbol = false),
isBalanceHidden = isBalanceHiddenProvider(),
),
networkCurrency = quoteModel.networkCurrency,
@ -437,7 +437,7 @@ internal class StateBuilder(
tokenCurrency = uiStateHolder.receiveCardData.tokenCurrency,
canSelectAnotherToken = uiStateHolder.receiveCardData.canSelectAnotherToken,
networkIconRes = uiStateHolder.receiveCardData.networkIconRes,
balance = toToken.getFormattedAmount(),
balance = toToken.getFormattedAmount(isNeedSymbol = false),
isBalanceHidden = isBalanceHiddenProvider(),
)
} ?: SwapCardState.Empty(
@ -451,6 +451,7 @@ internal class StateBuilder(
return uiStateHolder.copy(
sendCardData = uiStateHolder.sendCardData.copy(
amountEquivalent = getFormattedFiatAmount(fromToken.amountFiat),
balance = fromToken.cryptoCurrencyStatus.getFormattedAmount(isNeedSymbol = false),
),
receiveCardData = receiveCardData,
warnings = warnings,
@ -589,10 +590,17 @@ internal class StateBuilder(
)
}
fun addTokensToState(uiState: SwapStateHolder, tokensDataState: CurrenciesGroup): SwapStateHolder {
fun addTokensToState(
uiState: SwapStateHolder,
fromToken: CryptoCurrency,
tokensDataState: CurrenciesGroup,
): SwapStateHolder {
return uiState.copy(
selectTokenState = tokensDataConverter.convert(
value = tokensDataState,
value = CurrenciesGroupWithFromCurrency(
fromCurrency = fromToken,
group = tokensDataState,
),
),
)
}
@ -1220,10 +1228,10 @@ internal class StateBuilder(
)
}
private fun CryptoCurrencyStatus.getFormattedAmount(): String {
private fun CryptoCurrencyStatus.getFormattedAmount(isNeedSymbol: Boolean): String {
val amount = value.amount ?: return UNKNOWN_AMOUNT_SIGN
return BigDecimalFormatter.formatCryptoAmount(amount, currency.symbol, currency.decimals)
val symbol = if (isNeedSymbol) currency.symbol else ""
return BigDecimalFormatter.formatCryptoAmount(amount, symbol, currency.decimals)
}
@Suppress("UnusedPrivateMember")

View file

@ -413,7 +413,7 @@ private fun MainButton(state: SwapStateHolder, onPermissionWarningClick: () -> U
else -> {
PrimaryButtonIconEnd(
modifier = Modifier.fillMaxWidth(),
text = stringResource(id = R.string.common_swap),
text = stringResource(id = R.string.swapping_swap_action),
iconResId = R.drawable.ic_tangem_24,
enabled = state.swapButton.enabled,
onClick = state.swapButton.onClick,

View file

@ -180,6 +180,7 @@ private fun TitleHeader(item: TokenToSelectState.Title, modifier: Modifier = Mod
Text(
text = item.title.resolveReference().uppercase(),
style = TangemTheme.typography.overline,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier
.padding(
top = TangemTheme.dimens.spacing16,

View file

@ -60,7 +60,7 @@ private fun SwapSuccessScreenContent(state: SwapSuccessStateHolder, padding: Pad
TransactionDoneTitle(titleRes = R.string.swapping_success_view_title, date = state.timestamp)
SpacerH16()
InputRowImage(
title = TextReference.Res(R.string.swapping_success_from_title),
title = TextReference.Res(R.string.swapping_from_title),
subtitle = state.fromTokenAmount,
caption = state.fromTokenFiatAmount,
tokenIconState = state.fromTokenIconState ?: TokenIconState.Loading,
@ -70,7 +70,7 @@ private fun SwapSuccessScreenContent(state: SwapSuccessStateHolder, padding: Pad
)
SpacerH16()
InputRowImage(
title = TextReference.Res(R.string.swapping_success_to_title),
title = TextReference.Res(R.string.swapping_to_title),
subtitle = state.toTokenAmount,
caption = state.toTokenFiatAmount,
tokenIconState = state.toTokenIconState ?: TokenIconState.Loading,

View file

@ -216,11 +216,12 @@ internal class SwapViewModel @Inject constructor(
}
}
private fun updateTokensState(dataState: TokensDataStateExpress) {
val tokensDataState = if (isOrderReversed) dataState.fromGroup else dataState.toGroup
private fun updateTokensState(tokenDataState: TokensDataStateExpress) {
val tokensDataState = if (isOrderReversed) tokenDataState.fromGroup else tokenDataState.toGroup
uiState = stateBuilder.addTokensToState(
uiState = uiState,
tokensDataState = tokensDataState,
fromToken = dataState.fromCryptoCurrency?.currency ?: initialCryptoCurrency,
)
}

View file

@ -78,7 +78,7 @@ internal sealed class TokenDetailsActionButton(val config: ActionButtonConfig) {
*/
data class Swap(val enabled: Boolean, override val onClick: () -> Unit) : TokenDetailsActionButton(
config = ActionButtonConfig(
text = TextReference.Res(id = R.string.common_swap),
text = TextReference.Res(id = R.string.common_exchange),
iconResId = R.drawable.ic_exchange_vertical_24,
onClick = onClick,
enabled = enabled,

View file

@ -125,7 +125,6 @@ internal class TokenDetailsViewModel @Inject constructor(
clickIntents = this,
appCurrencyProvider = Provider { selectedAppCurrencyFlow.value },
analyticsEventsHandlerProvider = Provider { analyticsEventsHandler },
currentStateProvider = Provider { uiState },
userWalletId = userWalletId,
cryptoCurrency = cryptoCurrency,
)

View file

@ -88,7 +88,7 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
*/
data class Swap(override val enabled: Boolean, override val onClick: () -> Unit) : WalletManageButton(
config = ActionButtonConfig(
text = TextReference.Res(id = R.string.common_swap),
text = TextReference.Res(id = R.string.common_exchange),
iconResId = R.drawable.ic_exchange_vertical_24,
onClick = onClick,
enabled = enabled,

View file

@ -78,7 +78,7 @@ internal class TokenActionsProvider(
action = { clickIntents.onMultiCurrencySendClick(cryptoCurrencyStatus) }
}
is TokenActionsState.ActionState.Swap -> {
title = resourceReference(R.string.common_swap)
title = resourceReference(R.string.common_exchange)
icon = R.drawable.ic_exchange_horizontal_24
action = { clickIntents.onSwapClick(cryptoCurrencyStatus) }
}