Updated on 2026-08-14
This commit is contained in:
commit
866aaab9ff
15 changed files with 300 additions and 64 deletions
|
|
@ -66,6 +66,7 @@
|
|||
<string name="common_approval">Одобрение</string>
|
||||
<string name="common_attention">Внимание</string>
|
||||
<string name="common_balance">Баланс: %s</string>
|
||||
<string name="common_balance_title">Баланс</string>
|
||||
<string name="common_biometric_authentication">биометрическую аутентификацию</string>
|
||||
<string name="common_biometrics">биометрией</string>
|
||||
<string name="common_buy">Купить</string>
|
||||
|
|
@ -85,9 +86,11 @@
|
|||
<string name="common_enabled">Включено</string>
|
||||
<string name="common_error">Ошибка</string>
|
||||
<string name="common_exchange">Обменять</string>
|
||||
<string name="common_explore_transaction_history">Посмотреть историю транзакций</string>
|
||||
<string name="common_explore">Обозреватель</string>
|
||||
<string name="common_explore_history">Посмотреть историю</string>
|
||||
<string name="common_explore_transaction_history">Посмотреть историю транзакций</string>
|
||||
<string name="common_explorer">Обозреватель</string>
|
||||
<string name="common_fee_label">Комиссия</string>
|
||||
<string name="common_fee_selector_footer">Сетевые комиссии за транзакции используются для поддержки безопасности сети, поощрения валидаторов, выделения ресурсов и определения приоритета транзакции.</string>
|
||||
<string name="common_fee_selector_option_custom">Свое</string>
|
||||
<string name="common_fee_selector_option_fast">Быстро</string>
|
||||
|
|
@ -126,7 +129,6 @@
|
|||
<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>
|
||||
|
|
@ -192,14 +194,18 @@
|
|||
<string name="exchange_tokens_available_tokens_header">Мои токены</string>
|
||||
<string name="exchange_tokens_empty_tokens">У вас нет добавленных токенов. Добавьте токены для обмена</string>
|
||||
<string name="exchange_tokens_unavailable_tokens_header">Недоступен для обмена с %s</string>
|
||||
<string name="express_cex_status_button_title">Статус</string>
|
||||
<string name="express_choose_providers_subtitle">Провайдеры проводят транзакции, обеспечивая плавный и эффективный обмен токенами</string>
|
||||
<string name="express_choose_providers_title">Выберите провайдера</string>
|
||||
<string name="express_exchange_notification_failed_text">Чтобы узнать причину, посетите сайт провайдера</string>
|
||||
<string name="express_exchange_notification_failed_title">Операция не выполнена провайдером</string>
|
||||
<string name="express_exchange_notification_verification_text">Посетите сайт провайдера для проверки</string>
|
||||
<string name="express_exchange_notification_verification_title">Провайдер: требуется верификация</string>
|
||||
<string name="express_fetch_best_rates">Получение наилучших курсов...</string>
|
||||
<string name="express_provider">Провайдер</string>
|
||||
<string name="express_provider_best_rate">Лучший курс</string>
|
||||
<string name="express_provider_min_amount">Доступно с %s</string>
|
||||
<string name="express_provider_not_available">Недоступно для этой пары</string>
|
||||
<string name="express_provider_permission_needed">Требуется разрешение</string>
|
||||
<string name="feedback_data_collection_message">Информация ниже не является обязательной. Вы можете стереть её, если хотите.</string>
|
||||
<string name="feedback_preface_rate_negative">Расскажите, каких функций вам не хватает, и мы постараемся вам помочь.</string>
|
||||
|
|
@ -273,7 +279,6 @@
|
|||
<string name="onboarding_access_codes_doesnt_match">Введенные коды доступа не совпадают</string>
|
||||
<string name="onboarding_alert_message_not_max_backup_cards_added">Вы добавили одну резервную карту. После того, как процесс будет завершен, Вы больше не сможете добавить карт. Если у Вас есть еще одна карта, добавьте ее в резервную копию. Хотите продолжить?</string>
|
||||
<string name="onboarding_backup_exit_warning">Процесс резервного копирования почти завершен. Вы не можете выйти из него сейчас.</string>
|
||||
<string name="common_balance_title">Баланс</string>
|
||||
<string name="onboarding_button_add_backup_card">Добавить резервную карту</string>
|
||||
<string name="onboarding_button_backup_card_format">Сканировать карту #%d</string>
|
||||
<string name="onboarding_button_backup_now">Создать резервную копию</string>
|
||||
|
|
@ -634,12 +639,18 @@
|
|||
<string name="warning_button_like_it">Нравится</string>
|
||||
<string name="warning_button_ok">Понятно!</string>
|
||||
<string name="warning_button_really_cool">Очень круто!</string>
|
||||
<string name="warning_button_refresh">Обновить</string>
|
||||
<string name="warning_demo_mode_message">Вы находитесь в режиме демо</string>
|
||||
<string name="warning_demo_mode_title">Демо режим включен</string>
|
||||
<string name="warning_developer_card_message">Отсканированная вами карта является картой разработчика. Не используйте ее для создания своего кошелька.</string>
|
||||
<string name="warning_developer_card_title">Не для пользователя!</string>
|
||||
<string name="warning_existential_deposit_message">Cеть %1$s использует концепцию экзистенциального депозита. Если баланс вашего счета будет ниже %2$s, то он будет деактивирован, а средства на счете уничтожены.</string>
|
||||
<string name="warning_existential_deposit_title">Для работы с сетью необходим депозит</string>
|
||||
<string name="warning_express_no_exchangeable_coins_description">У вас в списке нет монет доступных для обмена с %s</string>
|
||||
<string name="warning_express_no_exchangeable_coins_title">Нет доступных токенов для обмена</string>
|
||||
<string name="warning_express_refresh_required_title">Cервис временно недоступен</string>
|
||||
<string name="warning_express_too_minimal_amount_description">Пожалуйста, измените сумму для обмена</string>
|
||||
<string name="warning_express_too_minimal_amount_title">Сумма для обмена должна быть не менее %s</string>
|
||||
<string name="warning_failed_to_verify_card_message">Возможно, данная карта - образец или подделка</string>
|
||||
<string name="warning_failed_to_verify_card_title">Ошибка проверки подлинности</string>
|
||||
<string name="warning_low_signatures_message">На этой карте осталось всего %s подписей. Вам следует вывести все ваши средства.</string>
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
<string name="common_approval">Approval</string>
|
||||
<string name="common_attention">Attention</string>
|
||||
<string name="common_balance">Balance: %s</string>
|
||||
<string name="common_balance_title">Balance</string>
|
||||
<string name="common_biometric_authentication">biometric authentication</string>
|
||||
<string name="common_biometrics">biometrics</string>
|
||||
<string name="common_buy">Buy</string>
|
||||
|
|
@ -84,8 +85,10 @@
|
|||
<string name="common_error">Error</string>
|
||||
<string name="common_exchange">Exchange</string>
|
||||
<string name="common_explore">Explore</string>
|
||||
<string name="common_explore_history">Explore history</string>
|
||||
<string name="common_explore_transaction_history">Explore transaction history</string>
|
||||
<string name="common_explorer">Explorer</string>
|
||||
<string name="common_fee_label">Fee</string>
|
||||
<string name="common_fee_selector_footer">Network transaction fees are used to support network security, incentivize validators, allocate resources, and determine transaction priority.</string>
|
||||
<string name="common_fee_selector_option_custom">Custom</string>
|
||||
<string name="common_fee_selector_option_fast">Fast</string>
|
||||
|
|
@ -125,7 +128,6 @@
|
|||
<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>
|
||||
|
|
@ -186,14 +188,16 @@
|
|||
<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_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 don\'t have any added tokens yet. Add tokens via Market to swap</string>
|
||||
<string name="exchange_tokens_unavailable_tokens_header">Unavailable for swap from %s</string>
|
||||
<string name="express_choose_providers_subtitle">Providers facilitate transactions, ensuring smooth and efficient token exchanges</string>
|
||||
<string name="express_choose_providers_title">Choose Provider</string>
|
||||
<string name="exchange_tokens_empty_tokens">You haven\'t added any tokens yet. Add tokens via Market to swap</string>
|
||||
<string name="exchange_tokens_unavailable_tokens_header">Cannot be swapped for %s</string>
|
||||
<string name="express_cex_status_button_title">Status</string>
|
||||
<string name="express_choose_providers_subtitle">Providers facilitate transactions, ensuring smooth and efficient token swaps</string>
|
||||
<string name="express_choose_providers_title">Choose provider</string>
|
||||
<string name="express_estimated_amount">Estimated amount</string>
|
||||
<string name="express_exchange_by">Exchange by %s</string>
|
||||
<string name="express_exchange_notification_failed_text">Visit provider’s website to see why</string>
|
||||
<string name="express_exchange_notification_failed_title">Operation failed by provider</string>
|
||||
<string name="express_exchange_notification_verification_text">Visit provider’s website for verification</string>
|
||||
|
|
@ -216,7 +220,8 @@
|
|||
<string name="express_go_to_provider">Go to provider</string>
|
||||
<string name="express_provider">Provider</string>
|
||||
<string name="express_provider_best_rate">Best rate</string>
|
||||
<string name="express_exchange_by">Exchange by %s</string>
|
||||
<string name="express_provider_min_amount">Available from %s</string>
|
||||
<string name="express_provider_not_available">Unavailable for this pair</string>
|
||||
<string name="express_provider_permission_needed">Permission Needed</string>
|
||||
<string name="feedback_data_collection_message">The following information is optional. You can erase it if you don\'t want to share it.</string>
|
||||
<string name="feedback_preface_rate_negative">Tell us what functions you are missing, and we will try to help you.</string>
|
||||
|
|
@ -291,7 +296,6 @@
|
|||
<string name="onboarding_access_codes_doesnt_match">Entered access code didn\'t match the initial access code</string>
|
||||
<string name="onboarding_alert_message_not_max_backup_cards_added">You\'ve added one backup card. When backup process is finished you can\'t add more backup cards. If you have one more card, add it to backup. Do you like to continue the backup process?</string>
|
||||
<string name="onboarding_backup_exit_warning">The backup process is partly complete. You can\'t exit it now.</string>
|
||||
<string name="common_balance_title">Balance</string>
|
||||
<string name="onboarding_button_add_backup_card">Add a backup card</string>
|
||||
<string name="onboarding_button_backup_card_format">Scan the card #%d</string>
|
||||
<string name="onboarding_button_backup_now">Backup now</string>
|
||||
|
|
@ -437,7 +441,6 @@
|
|||
<string name="send_extras_error_invalid_memo">Invalid Memo. It won\'t be added to the transaction.</string>
|
||||
<string name="send_extras_hint_destination_tag">Tag</string>
|
||||
<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_picker_low">Low</string>
|
||||
<string name="send_fee_picker_normal">Normal</string>
|
||||
|
|
@ -513,7 +516,7 @@
|
|||
<string name="story_meet_title">Meet Tangem</string>
|
||||
<string name="story_web3_description">Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services</string>
|
||||
<string name="story_web3_title">Web 3.0 Compatible</string>
|
||||
<string name="swapping_approve_information_text">Approvals are considered an industry standard across all decentralized exchanges and protect your wallet from being accessed by a smart contract without your permission. By design, smart contracts can\'t access your tokens unless you approve access from your end. By \"unlocking\" your tokens, you are give permission to the 1inch smart contract to spend your assets. The miners of the network are compensated with a gas fee (paid by you) to record this action on the blockchain. Once permission has been granted you will be able to swap your token.</string>
|
||||
<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_generic_error">There was an error. Please try again.</string>
|
||||
|
|
@ -521,25 +524,25 @@
|
|||
<string name="swapping_high_price_impact">High price impact!</string>
|
||||
<string name="swapping_high_price_impact_description">Swapping this amount of selected tokens will cause a significant price impact and reduce your outcome.</string>
|
||||
<string name="swapping_insufficient_funds">Insufficient funds</string>
|
||||
<string name="swapping_not_enough_funds_for_fee">Not enough funds for fee in your %1$s wallet to create a transaction. Top up your %2$s wallet first.</string>
|
||||
<string name="swapping_not_enough_funds_for_fee">Insufficient funds in your %1$s wallet to cover fees. Top up your %2$s wallet first.</string>
|
||||
<string name="swapping_pending_transaction_subtitle">Transaction in progress...</string>
|
||||
<string name="swapping_pending_transaction_title">Waiting</string>
|
||||
<string name="swapping_permission_buttons_approve">Approve</string>
|
||||
<string name="swapping_permission_current_transaction">Current transaction</string>
|
||||
<string name="swapping_permission_fee_footer">The token approval network fee will be charged to confirm that you are the one allowing your token to be used for the exchange.</string>
|
||||
<string name="swapping_permission_fee_footer">The network will charge a token approval fee to verify that you are authorizing the use of your token for the swap.</string>
|
||||
<string name="swapping_permission_header">Give Permission</string>
|
||||
<string name="swapping_permission_policy_type_footer">Specify the approve limit for the selected token</string>
|
||||
<string name="swapping_permission_rows_amount">Amount %s</string>
|
||||
<string name="swapping_permission_rows_spender">Spender</string>
|
||||
<string name="swapping_permission_rows_your_wallet">Your Wallet</string>
|
||||
<string name="swapping_permission_subheader">To continue you need to allow 1inch smart contracts to use your %s</string>
|
||||
<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_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_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 of %s to</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_token_list_other_tokens">Other tokens</string>
|
||||
<string name="swapping_token_list_title">Choose token</string>
|
||||
|
|
@ -661,12 +664,20 @@
|
|||
<string name="warning_button_like_it">Like it</string>
|
||||
<string name="warning_button_ok">Ok, Got it!</string>
|
||||
<string name="warning_button_really_cool">Really cool!</string>
|
||||
<string name="warning_button_refresh">Refresh</string>
|
||||
<string name="warning_demo_mode_message">You are currently in the Demo mode</string>
|
||||
<string name="warning_demo_mode_title">Demo mode active</string>
|
||||
<string name="warning_developer_card_message">The card you scanned is a developer card. Do not use it to create your wallet.</string>
|
||||
<string name="warning_developer_card_title">Not for users!</string>
|
||||
<string name="warning_existential_deposit_message">%1$s network requires an Existential Deposit. If your account drops below %2$s, it will be deactivated, and any remaining funds will be destroyed.</string>
|
||||
<string name="warning_existential_deposit_title">Network requires Existential Deposit</string>
|
||||
<string name="warning_express_no_exchangeable_coins_description">You do not have any %s exchangeable coins in your list</string>
|
||||
<string name="warning_express_no_exchangeable_coins_title">No available tokens to swap</string>
|
||||
<string name="warning_express_not_enough_fee_for_token_tx_description">To make a transaction you need to deposit some %1$s %2$s</string>
|
||||
<string name="warning_express_not_enough_fee_for_token_tx_title">Unable to cover %s fee</string>
|
||||
<string name="warning_express_refresh_required_title">Service temporary unavailable</string>
|
||||
<string name="warning_express_too_minimal_amount_description">Please change the amount to swap</string>
|
||||
<string name="warning_express_too_minimal_amount_title">The amount to swap must be at least %s</string>
|
||||
<string name="warning_failed_to_verify_card_message">This card might be a production sample or counterfeit</string>
|
||||
<string name="warning_failed_to_verify_card_title">Authenticity check failed</string>
|
||||
<string name="warning_low_signatures_message">Only %s signatures are left on this card. You must withdraw all of your funds.</string>
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ internal fun getNetwork(
|
|||
name = blockchain.fullName,
|
||||
isTestnet = blockchain.isTestnet(),
|
||||
derivationPath = getNetworkDerivationPath(blockchain, extraDerivationPath, derivationStyleProvider),
|
||||
currencySymbol = blockchain.currency,
|
||||
standardType = getNetworkStandardType(blockchain),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ data class Network(
|
|||
val id: ID,
|
||||
val backendId: String,
|
||||
val name: String,
|
||||
val currencySymbol: String,
|
||||
val derivationPath: DerivationPath,
|
||||
val isTestnet: Boolean,
|
||||
val standardType: StandardType,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ internal object MockNetworks {
|
|||
name = "Network One",
|
||||
isTestnet = false,
|
||||
standardType = Network.StandardType.ERC20,
|
||||
backendId = "network1",
|
||||
currencySymbol = "ETH",
|
||||
derivationPath = Network.DerivationPath.None,
|
||||
)
|
||||
|
||||
|
|
@ -26,6 +28,8 @@ internal object MockNetworks {
|
|||
name = "Network Two",
|
||||
isTestnet = false,
|
||||
standardType = Network.StandardType.ERC20,
|
||||
backendId = "network1",
|
||||
currencySymbol = "ETH",
|
||||
derivationPath = Network.DerivationPath.None,
|
||||
)
|
||||
|
||||
|
|
@ -34,6 +38,8 @@ internal object MockNetworks {
|
|||
name = "Network Three",
|
||||
isTestnet = false,
|
||||
standardType = Network.StandardType.ERC20,
|
||||
backendId = "network1",
|
||||
currencySymbol = "ETH",
|
||||
derivationPath = Network.DerivationPath.None,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ internal class SendAmountFieldConverter(
|
|||
),
|
||||
placeholder = TextReference.Str(DEFAULT_VALUE),
|
||||
isError = false,
|
||||
error = TextReference.Res(R.string.send_insufficient_funds),
|
||||
error = TextReference.Res(R.string.swapping_insufficient_funds),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -121,6 +121,8 @@ sealed interface SwapWarning {
|
|||
* @property priceImpact in format = 10 (means 10%)
|
||||
*/
|
||||
data class HighPriceImpact(val priceImpact: Int, val notificationConfig: NotificationConfig) : SwapWarning
|
||||
data class TooSmallAmountWarning(val notificationConfig: NotificationConfig) : SwapWarning
|
||||
data class UnableToCoverFeeWarning(val notificationConfig: NotificationConfig) : SwapWarning
|
||||
}
|
||||
|
||||
enum class GenericWarningType {
|
||||
|
|
|
|||
|
|
@ -19,4 +19,5 @@ data class UiActions(
|
|||
val onSelectFeeType: (TxFee) -> Unit,
|
||||
val onProviderClick: (String) -> Unit,
|
||||
val onProviderSelect: (String) -> Unit,
|
||||
val onBuyClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package com.tangem.feature.swap.models.states
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
||||
sealed class ProviderState {
|
||||
|
||||
abstract val onProviderClick: ((String) -> Unit)?
|
||||
|
|
@ -32,7 +34,8 @@ sealed class ProviderState {
|
|||
val name: String,
|
||||
val type: String,
|
||||
val iconUrl: String,
|
||||
val alertText: String,
|
||||
val alertText: TextReference,
|
||||
val selectionType: SelectionType,
|
||||
override val onProviderClick: ((String) -> Unit)? = null,
|
||||
) : ProviderState()
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,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.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.SpacerH24
|
||||
|
|
@ -35,7 +36,7 @@ private fun ChooseFeeBottomSheetContent(content: ChooseFeeBottomSheetConfig) {
|
|||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
Text(
|
||||
text = "Choose fee", // todo replace with strings
|
||||
text = stringResource(R.string.common_fee_selector_title),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier
|
||||
|
|
@ -53,9 +54,7 @@ private fun ChooseFeeBottomSheetContent(content: ChooseFeeBottomSheetConfig) {
|
|||
FeeItemsBlock(content)
|
||||
}
|
||||
Text(
|
||||
text = "Network transaction fees are small charges paid to support network security, incentivize " +
|
||||
"validators," +
|
||||
" allocate resources, and determine transaction priority.", // todo replace with strings
|
||||
text = stringResource(R.string.common_fee_selector_footer),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -10,13 +10,16 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
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.bottomsheets.TangemBottomSheet
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.models.states.ChooseProviderBottomSheetConfig
|
||||
import com.tangem.feature.swap.models.states.ProviderState
|
||||
import com.tangem.feature.swap.presentation.R
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@Composable
|
||||
|
|
@ -32,7 +35,7 @@ private fun ChooseProviderBottomSheetContent(content: ChooseProviderBottomSheetC
|
|||
modifier = Modifier.background(TangemTheme.colors.background.primary),
|
||||
) {
|
||||
Text(
|
||||
text = "Choose provider",
|
||||
text = stringResource(R.string.express_choose_providers_title),
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier
|
||||
|
|
@ -40,7 +43,7 @@ private fun ChooseProviderBottomSheetContent(content: ChooseProviderBottomSheetC
|
|||
.align(Alignment.CenterHorizontally),
|
||||
)
|
||||
Text(
|
||||
text = "Providers facilitate transactions, ensuring smooth and efficient token exchanges",
|
||||
text = stringResource(R.string.express_choose_providers_subtitle),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
modifier = Modifier
|
||||
|
|
@ -99,7 +102,8 @@ private fun ChooseProviderBottomSheet_Preview() {
|
|||
name = "1inch",
|
||||
type = "DEX",
|
||||
iconUrl = "",
|
||||
alertText = "Unavailable",
|
||||
selectionType = ProviderState.SelectionType.SELECT,
|
||||
alertText = stringReference("Unavailable"),
|
||||
),
|
||||
)
|
||||
TangemTheme(isDark = false) {
|
||||
|
|
|
|||
|
|
@ -13,12 +13,15 @@ import androidx.compose.ui.graphics.ColorFilter
|
|||
import androidx.compose.ui.graphics.ColorMatrix
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SpacerH24
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.models.states.ProviderState
|
||||
|
||||
|
|
@ -64,6 +67,7 @@ fun ProviderItem(state: ProviderState, modifier: Modifier = Modifier, isSelected
|
|||
ProviderUnavailableState(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
isSelected = isSelected,
|
||||
)
|
||||
}
|
||||
is ProviderState.Empty -> {
|
||||
|
|
@ -72,6 +76,7 @@ fun ProviderItem(state: ProviderState, modifier: Modifier = Modifier, isSelected
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun ProviderContentState(
|
||||
state: ProviderState.Content,
|
||||
|
|
@ -127,31 +132,42 @@ private fun ProviderContentState(
|
|||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing8),
|
||||
modifier = Modifier.padding(
|
||||
top = TangemTheme.dimens.spacing8,
|
||||
end = TangemTheme.dimens.spacing56,
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = state.rate,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
if (state.percentLowerThenBest != null) {
|
||||
Text(
|
||||
text = "${state.percentLowerThenBest}%", // todo add to strings
|
||||
text = "${state.percentLowerThenBest}%",
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.warning,
|
||||
modifier = Modifier.padding(start = TangemTheme.dimens.spacing4),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ProviderChevron(state = state, isSelected = isSelected)
|
||||
ProviderChevron(selectionType = state.selectionType, isSelected = isSelected)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ProviderUnavailableState(state: ProviderState.Unavailable, modifier: Modifier = Modifier) {
|
||||
private fun ProviderUnavailableState(
|
||||
state: ProviderState.Unavailable,
|
||||
isSelected: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(modifier = modifier.fillMaxWidth()) {
|
||||
Row {
|
||||
val (alpha, colorFilter) = GRAY_SCALE_ALPHA to GrayscaleColorFilter
|
||||
|
|
@ -195,13 +211,15 @@ private fun ProviderUnavailableState(state: ProviderState.Unavailable, modifier:
|
|||
)
|
||||
}
|
||||
Text(
|
||||
text = state.alertText,
|
||||
text = state.alertText.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing8),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ProviderChevron(selectionType = state.selectionType, isSelected = isSelected)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -247,8 +265,8 @@ private fun ProviderLoadingState(modifier: Modifier = Modifier) {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun BoxScope.ProviderChevron(state: ProviderState.Content, isSelected: Boolean) {
|
||||
when (state.selectionType) {
|
||||
private fun BoxScope.ProviderChevron(selectionType: ProviderState.SelectionType, isSelected: Boolean) {
|
||||
when (selectionType) {
|
||||
ProviderState.SelectionType.NONE -> {
|
||||
/* no-op */
|
||||
}
|
||||
|
|
@ -396,7 +414,8 @@ private fun ProviderItem_Unavailable_Preview() {
|
|||
name = "1inch",
|
||||
type = "DEX",
|
||||
iconUrl = "",
|
||||
alertText = "Unavailable",
|
||||
selectionType = ProviderState.SelectionType.SELECT,
|
||||
alertText = stringReference("Unavailable"),
|
||||
)
|
||||
Column {
|
||||
TangemTheme(isDark = false) {
|
||||
|
|
@ -408,5 +427,11 @@ private fun ProviderItem_Unavailable_Preview() {
|
|||
TangemTheme(isDark = true) {
|
||||
ProviderItemBlock(state = state)
|
||||
}
|
||||
|
||||
SpacerH24()
|
||||
|
||||
TangemTheme(isDark = true) {
|
||||
ProviderItem(state = state, isSelected = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,10 +6,7 @@ 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
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
|
@ -123,9 +120,12 @@ internal class StateBuilder(
|
|||
warnings = listOf(
|
||||
SwapWarning.NoAvailableTokensToSwap(
|
||||
notificationConfig = NotificationConfig(
|
||||
title = stringReference("No tokens"),
|
||||
subtitle = stringReference("Swap tokens not available"),
|
||||
iconResId = R.drawable.ic_alert_24,
|
||||
title = resourceReference(R.string.warning_express_no_exchangeable_coins_title),
|
||||
subtitle = resourceReference(
|
||||
id = R.string.warning_express_no_exchangeable_coins_description,
|
||||
formatArgs = wrappedList(fromToken.currency.name),
|
||||
),
|
||||
iconResId = R.drawable.img_attention_20,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
@ -213,6 +213,19 @@ internal class StateBuilder(
|
|||
),
|
||||
)
|
||||
}
|
||||
if (quoteModel.preparedSwapConfigState.isAllowedToSpend &&
|
||||
!quoteModel.preparedSwapConfigState.isFeeEnough &&
|
||||
quoteModel.preparedSwapConfigState.isBalanceEnough
|
||||
) {
|
||||
warnings.add(
|
||||
SwapWarning.UnableToCoverFeeWarning(
|
||||
createUnableToCoverFeeNotificationConfig(
|
||||
fromToken = fromToken,
|
||||
onBuyClick = actions.onBuyClick,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
if (!quoteModel.preparedSwapConfigState.isBalanceEnough) {
|
||||
warnings.add(SwapWarning.InsufficientFunds)
|
||||
}
|
||||
|
|
@ -430,7 +443,7 @@ internal class StateBuilder(
|
|||
|
||||
return FeeItemState.Content(
|
||||
feeType = feeType,
|
||||
title = stringReference("Fee"), // todo replace with string
|
||||
title = resourceReference(R.string.common_fee_label),
|
||||
amountCrypto = fee.feeCryptoFormatted,
|
||||
symbolCrypto = fee.cryptoSymbol,
|
||||
amountFiatFormatted = fee.feeFiatFormatted,
|
||||
|
|
@ -602,17 +615,27 @@ internal class StateBuilder(
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
fun showSelectProviderBottomSheet(
|
||||
uiState: SwapStateHolder,
|
||||
selectedProviderId: String,
|
||||
bestRatedProviderId: String,
|
||||
providersStates: Map<SwapProvider, SwapState>,
|
||||
unavailableProviders: List<SwapProvider>,
|
||||
onDismiss: () -> Unit,
|
||||
): SwapStateHolder {
|
||||
val availableProvidersStates = providersStates.entries.mapNotNull {
|
||||
it.convertToProviderState(bestRatedProviderId, actions.onProviderSelect)
|
||||
}
|
||||
val unavailableProviderStates = unavailableProviders.map {
|
||||
it.convertToUnavailableProviderState(
|
||||
alertText = resourceReference(R.string.express_provider_not_available),
|
||||
selectionType = ProviderState.SelectionType.NONE,
|
||||
)
|
||||
}
|
||||
val config = ChooseProviderBottomSheetConfig(
|
||||
selectedProviderId = selectedProviderId,
|
||||
providers = providersStates.entries
|
||||
.mapNotNull { it.convertToProviderState(actions.onProviderSelect) }
|
||||
.toImmutableList(),
|
||||
providers = (availableProvidersStates + unavailableProviderStates).toImmutableList(),
|
||||
)
|
||||
return uiState.copy(
|
||||
bottomSheetConfig = TangemBottomSheetConfig(
|
||||
|
|
@ -683,7 +706,7 @@ internal class StateBuilder(
|
|||
return listOf(
|
||||
FeeItemState.Content(
|
||||
feeType = this.normalFee.feeType,
|
||||
title = stringReference("Fee"), // todo replace with string
|
||||
title = resourceReference(R.string.common_fee_label),
|
||||
amountCrypto = this.normalFee.feeCryptoFormatted,
|
||||
symbolCrypto = this.normalFee.cryptoSymbol,
|
||||
amountFiatFormatted = this.normalFee.feeFiatFormatted,
|
||||
|
|
@ -692,7 +715,7 @@ internal class StateBuilder(
|
|||
),
|
||||
FeeItemState.Content(
|
||||
feeType = this.priorityFee.feeType,
|
||||
title = stringReference("Fee"), // todo replace with string
|
||||
title = resourceReference(R.string.common_fee_label),
|
||||
amountCrypto = this.priorityFee.feeCryptoFormatted,
|
||||
symbolCrypto = this.priorityFee.cryptoSymbol,
|
||||
amountFiatFormatted = this.priorityFee.feeFiatFormatted,
|
||||
|
|
@ -703,21 +726,28 @@ internal class StateBuilder(
|
|||
}
|
||||
|
||||
private fun Map.Entry<SwapProvider, SwapState>.convertToProviderState(
|
||||
bestRatedProviderId: String,
|
||||
onProviderSelect: (String) -> Unit,
|
||||
): ProviderState? {
|
||||
val provider = this.key
|
||||
return when (val state = this.value) {
|
||||
is SwapState.EmptyAmountState -> null
|
||||
is SwapState.QuotesLoadedState -> provider.convertToContentClickableProviderState(
|
||||
fromTokenInfo = state.fromTokenInfo,
|
||||
toTokenInfo = state.toTokenInfo,
|
||||
is SwapState.QuotesLoadedState -> provider.convertToContentSelectableProviderState(
|
||||
isBestRate = provider.providerId == bestRatedProviderId,
|
||||
state = state,
|
||||
onProviderClick = onProviderSelect,
|
||||
selectionType = ProviderState.SelectionType.SELECT,
|
||||
)
|
||||
is SwapState.SwapError -> null
|
||||
// todo handle error
|
||||
is SwapState.SwapError -> provider.convertToUnavailableProviderState(
|
||||
alertText = resourceReference(R.string.express_provider_min_amount, wrappedList("10")),
|
||||
selectionType = ProviderState.SelectionType.NONE,
|
||||
onProviderClick = onProviderSelect,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region warnings
|
||||
private fun createPermissionNotificationConfig(fromTokenSymbol: String): NotificationConfig {
|
||||
return NotificationConfig(
|
||||
title = resourceReference(R.string.swapping_permission_header),
|
||||
|
|
@ -737,6 +767,28 @@ internal class StateBuilder(
|
|||
)
|
||||
}
|
||||
|
||||
private fun createUnableToCoverFeeNotificationConfig(
|
||||
fromToken: CryptoCurrency,
|
||||
onBuyClick: () -> Unit,
|
||||
): NotificationConfig {
|
||||
return NotificationConfig(
|
||||
title = resourceReference(
|
||||
R.string.warning_express_not_enough_fee_for_token_tx_title,
|
||||
wrappedList(fromToken.network.name),
|
||||
),
|
||||
subtitle = resourceReference(
|
||||
R.string.warning_express_not_enough_fee_for_token_tx_description,
|
||||
wrappedList(fromToken.network.name, fromToken.network.currencySymbol),
|
||||
),
|
||||
iconResId = fromToken.networkIconResId,
|
||||
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||
text = resourceReference(R.string.common_buy_currency, wrappedList(fromToken.name)),
|
||||
onClick = onBuyClick,
|
||||
),
|
||||
)
|
||||
}
|
||||
// end region
|
||||
|
||||
private fun getShortAddressValue(fullAddress: String): String {
|
||||
check(fullAddress.length > ADDRESS_MIN_LENGTH) { "Invalid address" }
|
||||
val firstAddressPart = fullAddress.substring(startIndex = 0, endIndex = ADDRESS_FIRST_PART_LENGTH)
|
||||
|
|
@ -774,6 +826,58 @@ internal class StateBuilder(
|
|||
)
|
||||
}
|
||||
|
||||
private fun SwapProvider.convertToContentSelectableProviderState(
|
||||
isBestRate: Boolean,
|
||||
state: SwapState.QuotesLoadedState,
|
||||
selectionType: ProviderState.SelectionType,
|
||||
onProviderClick: (String) -> Unit,
|
||||
): ProviderState {
|
||||
val fromTokenInfo = state.fromTokenInfo
|
||||
val toTokenInfo = state.toTokenInfo
|
||||
val rate = toTokenInfo.tokenAmount.value.divide(
|
||||
fromTokenInfo.tokenAmount.value,
|
||||
toTokenInfo.cryptoCurrencyStatus.currency.decimals,
|
||||
RoundingMode.HALF_UP,
|
||||
)
|
||||
val fromCurrencySymbol = fromTokenInfo.cryptoCurrencyStatus.currency.symbol
|
||||
val toCurrencySymbol = toTokenInfo.cryptoCurrencyStatus.currency.symbol
|
||||
val rateString = "1 $fromCurrencySymbol ≈ $rate $toCurrencySymbol"
|
||||
val additionalBadge = if (state.permissionState is PermissionDataState.PermissionReadyForRequest) {
|
||||
ProviderState.AdditionalBadge.PermissionRequired
|
||||
} else if (isBestRate) {
|
||||
ProviderState.AdditionalBadge.BestTrade
|
||||
} else {
|
||||
ProviderState.AdditionalBadge.Empty
|
||||
}
|
||||
return ProviderState.Content(
|
||||
id = this.providerId,
|
||||
name = this.name,
|
||||
iconUrl = this.imageLarge,
|
||||
type = this.type.toString(),
|
||||
rate = rateString,
|
||||
additionalBadge = additionalBadge,
|
||||
selectionType = selectionType,
|
||||
percentLowerThenBest = null,
|
||||
onProviderClick = onProviderClick,
|
||||
)
|
||||
}
|
||||
|
||||
private fun SwapProvider.convertToUnavailableProviderState(
|
||||
alertText: TextReference,
|
||||
selectionType: ProviderState.SelectionType,
|
||||
onProviderClick: ((String) -> Unit)? = null,
|
||||
): ProviderState {
|
||||
return ProviderState.Unavailable(
|
||||
id = this.providerId,
|
||||
name = this.name,
|
||||
iconUrl = this.imageLarge,
|
||||
type = this.type.toString(),
|
||||
selectionType = selectionType,
|
||||
alertText = alertText,
|
||||
onProviderClick = onProviderClick,
|
||||
)
|
||||
}
|
||||
|
||||
private fun CryptoCurrencyStatus.getFormattedAmount(): String {
|
||||
val amount = value.amount ?: return UNKNOWN_AMOUNT_SIGN
|
||||
|
||||
|
|
|
|||
|
|
@ -256,6 +256,7 @@ private fun SwapWarnings(warnings: List<SwapWarning>) {
|
|||
is SwapWarning.HighPriceImpact -> {
|
||||
Notification(
|
||||
config = warning.notificationConfig,
|
||||
iconTint = TangemTheme.colors.icon.warning,
|
||||
)
|
||||
}
|
||||
is SwapWarning.PermissionNeeded -> {
|
||||
|
|
@ -282,6 +283,17 @@ private fun SwapWarnings(warnings: List<SwapWarning>) {
|
|||
config = warning.notificationConfig,
|
||||
)
|
||||
}
|
||||
is SwapWarning.TooSmallAmountWarning -> {
|
||||
Notification(
|
||||
config = warning.notificationConfig,
|
||||
iconTint = TangemTheme.colors.icon.warning,
|
||||
)
|
||||
}
|
||||
is SwapWarning.UnableToCoverFeeWarning -> {
|
||||
Notification(
|
||||
config = warning.notificationConfig,
|
||||
)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
SpacerH8()
|
||||
|
|
@ -379,7 +391,7 @@ private val state = SwapStateHolder(
|
|||
notificationConfig = NotificationConfig(
|
||||
title = stringReference("No tokens"),
|
||||
subtitle = stringReference("Swap tokens not available"),
|
||||
iconResId = R.drawable.ic_alert_24,
|
||||
iconResId = R.drawable.img_attention_20,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -30,11 +30,14 @@ import com.tangem.feature.swap.ui.StateBuilder
|
|||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.Debouncer
|
||||
import com.tangem.utils.coroutines.runCatching
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import java.text.DecimalFormat
|
||||
import java.text.NumberFormat
|
||||
import java.util.Locale
|
||||
|
|
@ -286,29 +289,62 @@ internal class SwapViewModel @Inject constructor(
|
|||
}
|
||||
is SwapState.SwapError -> {
|
||||
Timber.e("SwapError when loading quotes ${state.error}")
|
||||
// todo handle when change token and error
|
||||
uiState = stateBuilder.mapError(uiState, state.error) { startLoadingQuotesFromLastState() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateLoadedQuotes(state: Map<SwapProvider, SwapState>): Pair<SwapProvider, SwapState> {
|
||||
val selectedSwapProvider = selectProvider(state)
|
||||
val nonEmptyStates = state.filter { it.value !is SwapState.EmptyAmountState }
|
||||
val selectedSwapProvider = if (nonEmptyStates.isNotEmpty()) {
|
||||
selectProvider(state)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
dataState = dataState.copy(
|
||||
selectedProvider = selectedSwapProvider,
|
||||
lastLoadedSwapStates = state,
|
||||
)
|
||||
return state.entries.first { it.key == selectedSwapProvider }.toPair()
|
||||
selectedSwapProvider?.let {
|
||||
return state.entries.first { it.key == selectedSwapProvider }.toPair()
|
||||
}
|
||||
return state.entries.first().toPair()
|
||||
}
|
||||
|
||||
private fun selectProvider(state: Map<SwapProvider, SwapState>): SwapProvider {
|
||||
val currentSelected = dataState.selectedProvider
|
||||
return if (currentSelected != null && state.keys.contains(currentSelected)) {
|
||||
currentSelected
|
||||
val stateSuccess = state
|
||||
.filter { it.value is SwapState.QuotesLoadedState }
|
||||
.mapValues { it.value as SwapState.QuotesLoadedState }
|
||||
return if (stateSuccess.isNotEmpty()) {
|
||||
val currentSelected = dataState.selectedProvider
|
||||
if (currentSelected != null && state.keys.contains(currentSelected)) {
|
||||
currentSelected
|
||||
} else {
|
||||
findBestQuoteProvider(stateSuccess) ?: stateSuccess.keys.first()
|
||||
}
|
||||
} else {
|
||||
state.keys.first()
|
||||
}
|
||||
}
|
||||
|
||||
private fun findBestQuoteProvider(state: Map<SwapProvider, SwapState.QuotesLoadedState>): SwapProvider? {
|
||||
// finding best quotes
|
||||
return state.mapValues {
|
||||
if (!it.value.fromTokenInfo.amountFiat.isNullOrZero() &&
|
||||
!it.value.toTokenInfo.amountFiat.isNullOrZero()
|
||||
) {
|
||||
it.value.fromTokenInfo.amountFiat.divide(
|
||||
it.value.toTokenInfo.amountFiat,
|
||||
it.value.toTokenInfo.cryptoCurrencyStatus.currency.decimals,
|
||||
RoundingMode.HALF_UP,
|
||||
)
|
||||
} else {
|
||||
BigDecimal.ZERO
|
||||
}
|
||||
}.minByOrNull { it.value }?.key
|
||||
}
|
||||
|
||||
private fun fillLoadedDataState(
|
||||
state: SwapState.QuotesLoadedState,
|
||||
permissionState: PermissionDataState,
|
||||
|
|
@ -477,7 +513,6 @@ internal class SwapViewModel @Inject constructor(
|
|||
|
||||
private fun onTokenSelect(id: String) {
|
||||
val tokens = dataState.tokensDataState ?: return
|
||||
|
||||
val foundToken = if (isOrderReversed) {
|
||||
tokens.fromGroup.available.firstOrNull {
|
||||
it.currencyStatus.currency.id.value == id
|
||||
|
|
@ -504,6 +539,7 @@ internal class SwapViewModel @Inject constructor(
|
|||
dataState = dataState.copy(
|
||||
fromCryptoCurrency = fromToken,
|
||||
toCryptoCurrency = toToken,
|
||||
selectedProvider = null,
|
||||
)
|
||||
startLoadingQuotes(
|
||||
fromToken = fromToken,
|
||||
|
|
@ -674,10 +710,17 @@ internal class SwapViewModel @Inject constructor(
|
|||
setupLoadedState(selectedProvider, updatedState, fromToken)
|
||||
}
|
||||
},
|
||||
onProviderClick = {
|
||||
onProviderClick = { providerId ->
|
||||
val states = dataState.lastLoadedSwapStates
|
||||
.filter { it.value is SwapState.QuotesLoadedState }
|
||||
.mapValues { it.value as SwapState.QuotesLoadedState }
|
||||
val bestRatedProviderId = findBestQuoteProvider(states)?.providerId ?: providerId
|
||||
val unavailableProviders = getUnavailableProvidersFor(dataState.lastLoadedSwapStates)
|
||||
uiState = stateBuilder.showSelectProviderBottomSheet(
|
||||
uiState = uiState,
|
||||
selectedProviderId = it,
|
||||
selectedProviderId = providerId,
|
||||
bestRatedProviderId = bestRatedProviderId,
|
||||
unavailableProviders = unavailableProviders,
|
||||
providersStates = dataState.lastLoadedSwapStates,
|
||||
) { uiState = stateBuilder.dismissBottomSheet(uiState) }
|
||||
},
|
||||
|
|
@ -694,6 +737,7 @@ internal class SwapViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
},
|
||||
onBuyClick = {},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -735,6 +779,18 @@ internal class SwapViewModel @Inject constructor(
|
|||
return groupToFind.available.find { idToFind == it.currencyStatus.currency.id.value }?.providers ?: emptyList()
|
||||
}
|
||||
|
||||
private fun getAllProviders(): List<SwapProvider> {
|
||||
dataState.tokensDataState?.let { data ->
|
||||
val allProviders =
|
||||
data.fromGroup.available.flatMap { it.providers } + data.toGroup.available.flatMap { it.providers }
|
||||
return allProviders.distinct()
|
||||
} ?: return emptyList()
|
||||
}
|
||||
|
||||
private fun getUnavailableProvidersFor(state: Map<SwapProvider, SwapState>): List<SwapProvider> {
|
||||
return getAllProviders().filterNot { it in state }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val loggingTag = "SwapViewModel"
|
||||
private const val INITIAL_AMOUNT = ""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue