Updated on 2026-08-14
This commit is contained in:
parent
8966e28e29
commit
799a38655e
9 changed files with 733 additions and 67 deletions
|
|
@ -80,6 +80,10 @@
|
||||||
<string name="action_buttons_swap_not_enough_tokens_alert_title">Adicionar tokens</string>
|
<string name="action_buttons_swap_not_enough_tokens_alert_title">Adicionar tokens</string>
|
||||||
<string name="action_buttons_you_want_to_receive">Escolha o token que deseja receber.</string>
|
<string name="action_buttons_you_want_to_receive">Escolha o token que deseja receber.</string>
|
||||||
<string name="action_buttons_you_want_to_swap">Escolha o token que deseja trocar.</string>
|
<string name="action_buttons_you_want_to_swap">Escolha o token que deseja trocar.</string>
|
||||||
|
<string name="add_and_manage_sheet_manage_subtitle">Adicione ao seu portfólio</string>
|
||||||
|
<string name="add_and_manage_sheet_manage_title">Adicionar tokens</string>
|
||||||
|
<string name="add_and_manage_sheet_organize_subtitle">Classificação e agrupamento</string>
|
||||||
|
<string name="add_and_manage_sheet_organize_title">Organizar tokens</string>
|
||||||
<string name="add_custom_token_choose_network">Escolha a rede</string>
|
<string name="add_custom_token_choose_network">Escolha a rede</string>
|
||||||
<string name="add_custom_token_title">Adicionar token personalizado</string>
|
<string name="add_custom_token_title">Adicionar token personalizado</string>
|
||||||
<string name="add_tokens_title">Gerenciar tokens</string>
|
<string name="add_tokens_title">Gerenciar tokens</string>
|
||||||
|
|
@ -584,6 +588,8 @@
|
||||||
<string name="fee_selector_choose_token_description">Escolha qual token usar para pagar a taxa de rede. %s</string>
|
<string name="fee_selector_choose_token_description">Escolha qual token usar para pagar a taxa de rede. %s</string>
|
||||||
<string name="fee_selector_choose_token_title">Escolha o token</string>
|
<string name="fee_selector_choose_token_title">Escolha o token</string>
|
||||||
<string name="feed_market_and_news">Mercado e Notícias</string>
|
<string name="feed_market_and_news">Mercado e Notícias</string>
|
||||||
|
<string name="feed_search_show_all_user_assets">Mostrar tudo</string>
|
||||||
|
<string name="feed_search_show_less_user_assets">Mostrar menos</string>
|
||||||
<string name="feed_trending_now">Em alta agora</string>
|
<string name="feed_trending_now">Em alta agora</string>
|
||||||
<string name="feedback_data_collection_message">As informações a seguir são opcionais. Você pode apagá-las se não quiser compartilhá-las.</string>
|
<string name="feedback_data_collection_message">As informações a seguir são opcionais. Você pode apagá-las se não quiser compartilhá-las.</string>
|
||||||
<string name="feedback_preface_rate_negative">Diga-nos quais funções estão faltando e tentaremos ajudá-lo.</string>
|
<string name="feedback_preface_rate_negative">Diga-nos quais funções estão faltando e tentaremos ajudá-lo.</string>
|
||||||
|
|
@ -719,6 +725,7 @@
|
||||||
<string name="koinos_mana_exceeds_koin_balance_title">Limite de mana</string>
|
<string name="koinos_mana_exceeds_koin_balance_title">Limite de mana</string>
|
||||||
<string name="koinos_mana_level_description">A rede Koinos exige Mana para o pagamento das taxas de rede. Você tem %1$s/%2$s Mana</string>
|
<string name="koinos_mana_level_description">A rede Koinos exige Mana para o pagamento das taxas de rede. Você tem %1$s/%2$s Mana</string>
|
||||||
<string name="koinos_mana_level_title">Nível de mana</string>
|
<string name="koinos_mana_level_title">Nível de mana</string>
|
||||||
|
<string name="main_add_and_manage_tokens">Adicionar e gerenciar</string>
|
||||||
<string name="main_empty_tokens_list_message">Para começar a rastrear seus criptoativos e transações, adicione tokens.</string>
|
<string name="main_empty_tokens_list_message">Para começar a rastrear seus criptoativos e transações, adicione tokens.</string>
|
||||||
<string name="main_manage_tokens">Gerenciar tokens</string>
|
<string name="main_manage_tokens">Gerenciar tokens</string>
|
||||||
<string name="main_qr_scan_hint">Leia o código QR para enviar fundos ou conectar-se a um aplicativo</string>
|
<string name="main_qr_scan_hint">Leia o código QR para enviar fundos ou conectar-se a um aplicativo</string>
|
||||||
|
|
@ -1534,6 +1541,7 @@
|
||||||
<string name="swapping_trade_too_large_title">Negociação em grande escala</string>
|
<string name="swapping_trade_too_large_title">Negociação em grande escala</string>
|
||||||
<string name="tangem_pay_beta_notification_subtitle">Teremos todo o prazer em receber seu feedback.</string>
|
<string name="tangem_pay_beta_notification_subtitle">Teremos todo o prazer em receber seu feedback.</string>
|
||||||
<string name="tangem_pay_beta_notification_title">O Tangem Pay agora está em versão beta.</string>
|
<string name="tangem_pay_beta_notification_title">O Tangem Pay agora está em versão beta.</string>
|
||||||
|
<string name="tangem_pay_card_details_unable_to_rename_card_title">Não foi possível renomear o cartão.</string>
|
||||||
<string name="tangem_pay_card_frozen">Cartão bloqueado</string>
|
<string name="tangem_pay_card_frozen">Cartão bloqueado</string>
|
||||||
<string name="tangem_pay_card_payment">Pagamento com cartão</string>
|
<string name="tangem_pay_card_payment">Pagamento com cartão</string>
|
||||||
<string name="tangem_pay_deposit">Depósito</string>
|
<string name="tangem_pay_deposit">Depósito</string>
|
||||||
|
|
@ -1567,7 +1575,9 @@
|
||||||
<string name="tangem_pay_unfreeze_card_success">Seu cartão foi desbloqueado.</string>
|
<string name="tangem_pay_unfreeze_card_success">Seu cartão foi desbloqueado.</string>
|
||||||
<string name="tangem_pay_withdrawal">Retirada</string>
|
<string name="tangem_pay_withdrawal">Retirada</string>
|
||||||
<string name="tangempay_account_unable_to_use_rooted">Não é possível usar em dispositivos com root.</string>
|
<string name="tangempay_account_unable_to_use_rooted">Não é possível usar em dispositivos com root.</string>
|
||||||
|
<string name="tangempay_available_balance">Saldo disponível</string>
|
||||||
<string name="tangempay_cancel_kyc">Ocultar KYC da tela principal</string>
|
<string name="tangempay_cancel_kyc">Ocultar KYC da tela principal</string>
|
||||||
|
<string name="tangempay_card_default_name">Cartão Tangem Pay 1</string>
|
||||||
<string name="tangempay_card_details_add_funds">Adicionar fundos</string>
|
<string name="tangempay_card_details_add_funds">Adicionar fundos</string>
|
||||||
<string name="tangempay_card_details_add_funds_subtitle">Opções de recarga</string>
|
<string name="tangempay_card_details_add_funds_subtitle">Opções de recarga</string>
|
||||||
<string name="tangempay_card_details_add_to_wallet_button_text">Adicionar ao Google Wallet</string>
|
<string name="tangempay_card_details_add_to_wallet_button_text">Adicionar ao Google Wallet</string>
|
||||||
|
|
@ -1600,10 +1610,13 @@
|
||||||
<string name="tangempay_card_details_receive_description">Compartilhe seu endereço ou mostre o código QR.</string>
|
<string name="tangempay_card_details_receive_description">Compartilhe seu endereço ou mostre o código QR.</string>
|
||||||
<string name="tangempay_card_details_receive_error_description">Problemas técnicos detectados. Tente novamente mais tarde ou entre em contato com o suporte.</string>
|
<string name="tangempay_card_details_receive_error_description">Problemas técnicos detectados. Tente novamente mais tarde ou entre em contato com o suporte.</string>
|
||||||
<string name="tangempay_card_details_receive_error_title">Receber indisponível agora</string>
|
<string name="tangempay_card_details_receive_error_title">Receber indisponível agora</string>
|
||||||
|
<string name="tangempay_card_details_rename_card_invalid_description">Somente letras e números são permitidos.</string>
|
||||||
|
<string name="tangempay_card_details_rename_card_invalid_title">Caracteres inválidos</string>
|
||||||
<string name="tangempay_card_details_reveal_text">Revelar</string>
|
<string name="tangempay_card_details_reveal_text">Revelar</string>
|
||||||
<string name="tangempay_card_details_show_details">Mostrar detalhes</string>
|
<string name="tangempay_card_details_show_details">Mostrar detalhes</string>
|
||||||
<string name="tangempay_card_details_swap_description">Troque qualquer ativo da sua carteira por um cartão.</string>
|
<string name="tangempay_card_details_swap_description">Troque qualquer ativo da sua carteira por um cartão.</string>
|
||||||
<string name="tangempay_card_details_title">Detalhes do cartão</string>
|
<string name="tangempay_card_details_title">Detalhes do cartão</string>
|
||||||
|
<string name="tangempay_card_details_unable_to_rename_card_description">Por favor, tente novamente mais tarde.</string>
|
||||||
<string name="tangempay_card_details_unfreeze_card">Descongelar cartão</string>
|
<string name="tangempay_card_details_unfreeze_card">Descongelar cartão</string>
|
||||||
<string name="tangempay_card_details_view_pin_code_description">Volte ao aplicativo se você se esquecer.</string>
|
<string name="tangempay_card_details_view_pin_code_description">Volte ao aplicativo se você se esquecer.</string>
|
||||||
<string name="tangempay_card_details_view_pin_code_title">Seu código PIN</string>
|
<string name="tangempay_card_details_view_pin_code_title">Seu código PIN</string>
|
||||||
|
|
@ -1611,12 +1624,16 @@
|
||||||
<string name="tangempay_card_details_withdraw_error_title">Retirada indisponível agora</string>
|
<string name="tangempay_card_details_withdraw_error_title">Retirada indisponível agora</string>
|
||||||
<string name="tangempay_card_details_withdraw_in_progress_description">Você não pode iniciar uma troca ou um novo saque até que o atual seja concluído.</string>
|
<string name="tangempay_card_details_withdraw_in_progress_description">Você não pode iniciar uma troca ou um novo saque até que o atual seja concluído.</string>
|
||||||
<string name="tangempay_card_details_withdraw_in_progress_title">Retirada em andamento</string>
|
<string name="tangempay_card_details_withdraw_in_progress_title">Retirada em andamento</string>
|
||||||
|
<string name="tangempay_card_page_settings_title">Configurações do cartão</string>
|
||||||
<string name="tangempay_change_pin_code">Alterar código PIN</string>
|
<string name="tangempay_change_pin_code">Alterar código PIN</string>
|
||||||
<string name="tangempay_come_back_if_forget_pin">Volte ao aplicativo se você se esquecer.</string>
|
<string name="tangempay_come_back_if_forget_pin">Volte ao aplicativo se você se esquecer.</string>
|
||||||
|
<string name="tangempay_digital_card">Cartão digital</string>
|
||||||
<string name="tangempay_factory_settings_warning_title">Entendo que perderei completamente o acesso ao meu cartão Tangem Pay e a todos os fundos nele contidos, sem possibilidade de recuperação.</string>
|
<string name="tangempay_factory_settings_warning_title">Entendo que perderei completamente o acesso ao meu cartão Tangem Pay e a todos os fundos nele contidos, sem possibilidade de recuperação.</string>
|
||||||
<string name="tangempay_failed_to_issue_card">Falha na emissão do cartão</string>
|
<string name="tangempay_failed_to_issue_card">Falha na emissão do cartão</string>
|
||||||
<string name="tangempay_failed_to_issue_card_retry_description">Ocorreu um erro técnico. Tente novamente clicando no botão abaixo.</string>
|
<string name="tangempay_failed_to_issue_card_retry_description">Ocorreu um erro técnico. Tente novamente clicando no botão abaixo.</string>
|
||||||
<string name="tangempay_failed_to_issue_card_support_description">Ocorreu um erro técnico. Por favor, entre em contato com o suporte.</string>
|
<string name="tangempay_failed_to_issue_card_support_description">Ocorreu um erro técnico. Por favor, entre em contato com o suporte.</string>
|
||||||
|
<string name="tangempay_feature_will_be_available_soon">O recurso estará disponível em breve.</string>
|
||||||
|
<string name="tangempay_feature_will_be_available_soon_description">Você poderá emitir cartões adicionais para sua conta de pagamento.</string>
|
||||||
<string name="tangempay_get_banner_description">Obtenha seu cartão virtual Visa Tangem grátis.</string>
|
<string name="tangempay_get_banner_description">Obtenha seu cartão virtual Visa Tangem grátis.</string>
|
||||||
<string name="tangempay_get_tangem_pay">Obtenha o Tangem Pay</string>
|
<string name="tangempay_get_tangem_pay">Obtenha o Tangem Pay</string>
|
||||||
<string name="tangempay_go_to_support">Acesse o Suporte</string>
|
<string name="tangempay_go_to_support">Acesse o Suporte</string>
|
||||||
|
|
@ -1648,6 +1665,7 @@
|
||||||
<string name="tangempay_onboarding_security_description">Uma conta de pagamento separada será criada sem divulgar seus endereços e bens.</string>
|
<string name="tangempay_onboarding_security_description">Uma conta de pagamento separada será criada sem divulgar seus endereços e bens.</string>
|
||||||
<string name="tangempay_onboarding_security_title">Privacidade incomparável</string>
|
<string name="tangempay_onboarding_security_title">Privacidade incomparável</string>
|
||||||
<string name="tangempay_onboarding_title">Obtenha seu cartão Tangem Pay gratuito em minutos.</string>
|
<string name="tangempay_onboarding_title">Obtenha seu cartão Tangem Pay gratuito em minutos.</string>
|
||||||
|
<string name="tangempay_pay_support">Suporte de pagamento</string>
|
||||||
<string name="tangempay_payment_account">Conta de pagamento</string>
|
<string name="tangempay_payment_account">Conta de pagamento</string>
|
||||||
<string name="tangempay_payment_account_sync_needed">A conta de pagamento não está sincronizada.</string>
|
<string name="tangempay_payment_account_sync_needed">A conta de pagamento não está sincronizada.</string>
|
||||||
<string name="tangempay_pin_validation_error_message">PIN inválido: evite sequências ou repetições.</string>
|
<string name="tangempay_pin_validation_error_message">PIN inválido: evite sequências ou repetições.</string>
|
||||||
|
|
@ -1660,6 +1678,7 @@
|
||||||
<string name="tangempay_tangem_visa_card">Use USDC para pagamentos do dia a dia.</string>
|
<string name="tangempay_tangem_visa_card">Use USDC para pagamentos do dia a dia.</string>
|
||||||
<string name="tangempay_temporarily_unavailable">O serviço Tangem Pay está temporariamente inacessível.</string>
|
<string name="tangempay_temporarily_unavailable">O serviço Tangem Pay está temporariamente inacessível.</string>
|
||||||
<string name="tangempay_title">Tangem Pay</string>
|
<string name="tangempay_title">Tangem Pay</string>
|
||||||
|
<string name="tangempay_usdc_on_polygon_network">USDC na rede Polygon</string>
|
||||||
<string name="tangempay_use_tangem_device_to_restore_payment_account">Clique no botão abaixo para restaurar o acesso.</string>
|
<string name="tangempay_use_tangem_device_to_restore_payment_account">Clique no botão abaixo para restaurar o acesso.</string>
|
||||||
<string name="tangempay_withdrawal_note_description">O saldo do seu Polygon em USDC na blockchain é diferente do saldo do seu cartão e é atualizado em até 2 dias úteis após uma compra. Os fundos de compras reembolsadas não serão devolvidos ao seu saldo na blockchain nem estarão disponíveis para saque, mas permanecerão no saldo do seu cartão para compras futuras.</string>
|
<string name="tangempay_withdrawal_note_description">O saldo do seu Polygon em USDC na blockchain é diferente do saldo do seu cartão e é atualizado em até 2 dias úteis após uma compra. Os fundos de compras reembolsadas não serão devolvidos ao seu saldo na blockchain nem estarão disponíveis para saque, mas permanecerão no saldo do seu cartão para compras futuras.</string>
|
||||||
<string name="tangempay_withdrawal_note_title">Observe</string>
|
<string name="tangempay_withdrawal_note_title">Observe</string>
|
||||||
|
|
@ -1690,6 +1709,10 @@
|
||||||
<string name="token_details_staking_block_title">Serviço de staking</string>
|
<string name="token_details_staking_block_title">Serviço de staking</string>
|
||||||
<string name="token_details_token_type_subtitle">%1$s token em %%imagem%% %2$s rede</string>
|
<string name="token_details_token_type_subtitle">%1$s token em %%imagem%% %2$s rede</string>
|
||||||
<string name="token_details_token_type_subtitle_no_standard">Token em %%imagem%% %1$s rede</string>
|
<string name="token_details_token_type_subtitle_no_standard">Token em %%imagem%% %1$s rede</string>
|
||||||
|
<string name="token_details_toolbar_subtitle_network">%s rede</string>
|
||||||
|
<string name="token_details_toolbar_subtitle_standard">%1$s em %2$s rede</string>
|
||||||
|
<string name="token_details_toolbar_title_token_in_account">%1$s em %%imagem%% %2$s</string>
|
||||||
|
<string name="token_details_toolbar_title_token_in_wallet">%1$s em %2$s %%imagem%%</string>
|
||||||
<string name="token_details_unable_hide_alert_message">O %1$s (%2$sO token ) é a principal moeda da plataforma. %3$s rede e não pode ser ocultada enquanto você tiver outros tokens dessa rede na lista</string>
|
<string name="token_details_unable_hide_alert_message">O %1$s (%2$sO token ) é a principal moeda da plataforma. %3$s rede e não pode ser ocultada enquanto você tiver outros tokens dessa rede na lista</string>
|
||||||
<string name="token_details_unable_hide_alert_title">Não foi possível ocultar %s</string>
|
<string name="token_details_unable_hide_alert_title">Não foi possível ocultar %s</string>
|
||||||
<string name="token_market_metrics_no_data">N/A</string>
|
<string name="token_market_metrics_no_data">N/A</string>
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,10 @@
|
||||||
<string name="action_buttons_swap_not_enough_tokens_alert_title">Add tokens</string>
|
<string name="action_buttons_swap_not_enough_tokens_alert_title">Add tokens</string>
|
||||||
<string name="action_buttons_you_want_to_receive">Choose the token you want to receive</string>
|
<string name="action_buttons_you_want_to_receive">Choose the token you want to receive</string>
|
||||||
<string name="action_buttons_you_want_to_swap">Choose the token you want to swap</string>
|
<string name="action_buttons_you_want_to_swap">Choose the token you want to swap</string>
|
||||||
|
<string name="add_and_manage_sheet_manage_subtitle">Add to your portfolio</string>
|
||||||
|
<string name="add_and_manage_sheet_manage_title">Add tokens</string>
|
||||||
|
<string name="add_and_manage_sheet_organize_subtitle">Sorting and grouping</string>
|
||||||
|
<string name="add_and_manage_sheet_organize_title">Organize tokens</string>
|
||||||
<string name="add_custom_token_choose_network">Choose network</string>
|
<string name="add_custom_token_choose_network">Choose network</string>
|
||||||
<string name="add_custom_token_title">Add custom token</string>
|
<string name="add_custom_token_title">Add custom token</string>
|
||||||
<string name="add_tokens_title">Manage tokens</string>
|
<string name="add_tokens_title">Manage tokens</string>
|
||||||
|
|
@ -477,6 +481,11 @@
|
||||||
<string name="domain_receive_assets_onboarding_title">Send funds using only</string>
|
<string name="domain_receive_assets_onboarding_title">Send funds using only</string>
|
||||||
<string name="dynamic_addresses">Dynamic addresses</string>
|
<string name="dynamic_addresses">Dynamic addresses</string>
|
||||||
<string name="dynamic_addresses_custom_token_error_on_addition">Dynamic Addresses is enabled. Custom \"change\" and \"index\" are unavailable.</string>
|
<string name="dynamic_addresses_custom_token_error_on_addition">Dynamic Addresses is enabled. Custom \"change\" and \"index\" are unavailable.</string>
|
||||||
|
<string name="dynamic_addresses_disable_description">You are turning off Dynamic Addresses. Funds will now be received on your fixed address.</string>
|
||||||
|
<string name="dynamic_addresses_disable_fee_description">A network fee applies to consolidate funds into the fixed address.</string>
|
||||||
|
<string name="dynamic_addresses_disable_main_button_title">Disable Dynamic Addresses</string>
|
||||||
|
<string name="dynamic_addresses_disable_title">Disable dynamic addresses</string>
|
||||||
|
<string name="dynamic_addresses_disabled_popup_title">Dynamic addresses disabled</string>
|
||||||
<string name="dynamic_addresses_enabled_toast_title">Dynamic addresses enabled</string>
|
<string name="dynamic_addresses_enabled_toast_title">Dynamic addresses enabled</string>
|
||||||
<string name="dynamic_addresses_enter_features_privacy_description">Use a new address for each transaction to reduce traceability and improve on-chain privacy.</string>
|
<string name="dynamic_addresses_enter_features_privacy_description">Use a new address for each transaction to reduce traceability and improve on-chain privacy.</string>
|
||||||
<string name="dynamic_addresses_enter_features_privacy_title">Enhanced Privacy</string>
|
<string name="dynamic_addresses_enter_features_privacy_title">Enhanced Privacy</string>
|
||||||
|
|
@ -584,6 +593,8 @@
|
||||||
<string name="fee_selector_choose_token_description">Choose which token to use to pay\nthe network fee. %s</string>
|
<string name="fee_selector_choose_token_description">Choose which token to use to pay\nthe network fee. %s</string>
|
||||||
<string name="fee_selector_choose_token_title">Choose token</string>
|
<string name="fee_selector_choose_token_title">Choose token</string>
|
||||||
<string name="feed_market_and_news">Market & News</string>
|
<string name="feed_market_and_news">Market & News</string>
|
||||||
|
<string name="feed_search_show_all_user_assets">Show All</string>
|
||||||
|
<string name="feed_search_show_less_user_assets">Show less</string>
|
||||||
<string name="feed_tangem_ai">Tangem AI</string>
|
<string name="feed_tangem_ai">Tangem AI</string>
|
||||||
<string name="feed_trending_now">Trending Now</string>
|
<string name="feed_trending_now">Trending Now</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_data_collection_message">The following information is optional. You can erase it if you don\'t want to share it.</string>
|
||||||
|
|
@ -720,6 +731,7 @@
|
||||||
<string name="koinos_mana_exceeds_koin_balance_title">Mana limit</string>
|
<string name="koinos_mana_exceeds_koin_balance_title">Mana limit</string>
|
||||||
<string name="koinos_mana_level_description">The Koinos network requires Mana for network fees. You have %1$s/%2$s Mana</string>
|
<string name="koinos_mana_level_description">The Koinos network requires Mana for network fees. You have %1$s/%2$s Mana</string>
|
||||||
<string name="koinos_mana_level_title">Mana level</string>
|
<string name="koinos_mana_level_title">Mana level</string>
|
||||||
|
<string name="main_add_and_manage_tokens">Add & Manage</string>
|
||||||
<string name="main_empty_tokens_list_message">To begin tracking your crypto assets and transactions, add tokens</string>
|
<string name="main_empty_tokens_list_message">To begin tracking your crypto assets and transactions, add tokens</string>
|
||||||
<string name="main_manage_tokens">Manage tokens</string>
|
<string name="main_manage_tokens">Manage tokens</string>
|
||||||
<string name="main_qr_scan_hint">Scan QR code to send funds or connect to an app</string>
|
<string name="main_qr_scan_hint">Scan QR code to send funds or connect to an app</string>
|
||||||
|
|
@ -1569,7 +1581,9 @@
|
||||||
<string name="tangem_pay_unfreeze_card_success">Your card is unfrozen.</string>
|
<string name="tangem_pay_unfreeze_card_success">Your card is unfrozen.</string>
|
||||||
<string name="tangem_pay_withdrawal">Withdrawal</string>
|
<string name="tangem_pay_withdrawal">Withdrawal</string>
|
||||||
<string name="tangempay_account_unable_to_use_rooted">Unable to use on rooted device</string>
|
<string name="tangempay_account_unable_to_use_rooted">Unable to use on rooted device</string>
|
||||||
|
<string name="tangempay_available_balance">Available balance</string>
|
||||||
<string name="tangempay_cancel_kyc">Hide KYC from main screen</string>
|
<string name="tangempay_cancel_kyc">Hide KYC from main screen</string>
|
||||||
|
<string name="tangempay_card_default_name">Tangem Pay Card 1</string>
|
||||||
<string name="tangempay_card_details_add_funds">Add funds</string>
|
<string name="tangempay_card_details_add_funds">Add funds</string>
|
||||||
<string name="tangempay_card_details_add_funds_subtitle">Top-up options</string>
|
<string name="tangempay_card_details_add_funds_subtitle">Top-up options</string>
|
||||||
<string name="tangempay_card_details_add_to_wallet_button_text">Add to Google Wallet</string>
|
<string name="tangempay_card_details_add_to_wallet_button_text">Add to Google Wallet</string>
|
||||||
|
|
@ -1619,10 +1633,13 @@
|
||||||
<string name="tangempay_card_page_settings_title">Card settings</string>
|
<string name="tangempay_card_page_settings_title">Card settings</string>
|
||||||
<string name="tangempay_change_pin_code">Change PIN-code</string>
|
<string name="tangempay_change_pin_code">Change PIN-code</string>
|
||||||
<string name="tangempay_come_back_if_forget_pin">Come back to the app if you forget it.</string>
|
<string name="tangempay_come_back_if_forget_pin">Come back to the app if you forget it.</string>
|
||||||
|
<string name="tangempay_digital_card">Digital card</string>
|
||||||
<string name="tangempay_factory_settings_warning_title">I understand that I will completely lose access to my Tangem Pay Card and all funds on it without the possibility of recovery</string>
|
<string name="tangempay_factory_settings_warning_title">I understand that I will completely lose access to my Tangem Pay Card and all funds on it without the possibility of recovery</string>
|
||||||
<string name="tangempay_failed_to_issue_card">Failed to issue card</string>
|
<string name="tangempay_failed_to_issue_card">Failed to issue card</string>
|
||||||
<string name="tangempay_failed_to_issue_card_retry_description">A technical error has occurred, please try again by clicking the button below.</string>
|
<string name="tangempay_failed_to_issue_card_retry_description">A technical error has occurred, please try again by clicking the button below.</string>
|
||||||
<string name="tangempay_failed_to_issue_card_support_description">A technical error has occurred, please contact support.</string>
|
<string name="tangempay_failed_to_issue_card_support_description">A technical error has occurred, please contact support.</string>
|
||||||
|
<string name="tangempay_feature_will_be_available_soon">Feature will be availible soon</string>
|
||||||
|
<string name="tangempay_feature_will_be_available_soon_description">You will be available to issue additional cards for your payment account</string>
|
||||||
<string name="tangempay_get_banner_description">Get your free Tangem Visa virtual card</string>
|
<string name="tangempay_get_banner_description">Get your free Tangem Visa virtual card</string>
|
||||||
<string name="tangempay_get_tangem_pay">Get Tangem Pay</string>
|
<string name="tangempay_get_tangem_pay">Get Tangem Pay</string>
|
||||||
<string name="tangempay_go_to_support">Go to Support</string>
|
<string name="tangempay_go_to_support">Go to Support</string>
|
||||||
|
|
@ -1654,6 +1671,7 @@
|
||||||
<string name="tangempay_onboarding_security_description">A separate payment account will be created without disclosing your addresses and assets</string>
|
<string name="tangempay_onboarding_security_description">A separate payment account will be created without disclosing your addresses and assets</string>
|
||||||
<string name="tangempay_onboarding_security_title">Unrivaled privacy</string>
|
<string name="tangempay_onboarding_security_title">Unrivaled privacy</string>
|
||||||
<string name="tangempay_onboarding_title">Get your free Tangem Pay Card in minutes</string>
|
<string name="tangempay_onboarding_title">Get your free Tangem Pay Card in minutes</string>
|
||||||
|
<string name="tangempay_pay_support">Pay Support</string>
|
||||||
<string name="tangempay_payment_account">Payment account</string>
|
<string name="tangempay_payment_account">Payment account</string>
|
||||||
<string name="tangempay_payment_account_sync_needed">Payment account is not synced</string>
|
<string name="tangempay_payment_account_sync_needed">Payment account is not synced</string>
|
||||||
<string name="tangempay_pin_validation_error_message">Invalid PIN: avoid sequences or repeats</string>
|
<string name="tangempay_pin_validation_error_message">Invalid PIN: avoid sequences or repeats</string>
|
||||||
|
|
@ -1666,6 +1684,7 @@
|
||||||
<string name="tangempay_tangem_visa_card">Use USDC for everyday payments</string>
|
<string name="tangempay_tangem_visa_card">Use USDC for everyday payments</string>
|
||||||
<string name="tangempay_temporarily_unavailable">Tangem Pay is temporarily unreachable</string>
|
<string name="tangempay_temporarily_unavailable">Tangem Pay is temporarily unreachable</string>
|
||||||
<string name="tangempay_title">Tangem Pay</string>
|
<string name="tangempay_title">Tangem Pay</string>
|
||||||
|
<string name="tangempay_usdc_on_polygon_network">USDC on Polygon network</string>
|
||||||
<string name="tangempay_use_tangem_device_to_restore_payment_account">Click the button below to restore access</string>
|
<string name="tangempay_use_tangem_device_to_restore_payment_account">Click the button below to restore access</string>
|
||||||
<string name="tangempay_withdrawal_note_description">Your on-chain USDC Polygon balance differs from your card balance and updates within 2 business days after a purchase. Funds from refunded purchases won’t be returned your on-chain balance or be available for withdrawal, but will stay on your card balance for purchases.</string>
|
<string name="tangempay_withdrawal_note_description">Your on-chain USDC Polygon balance differs from your card balance and updates within 2 business days after a purchase. Funds from refunded purchases won’t be returned your on-chain balance or be available for withdrawal, but will stay on your card balance for purchases.</string>
|
||||||
<string name="tangempay_withdrawal_note_title">Please note</string>
|
<string name="tangempay_withdrawal_note_title">Please note</string>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
package com.tangem.data.dynamicaddresses
|
package com.tangem.data.dynamicaddresses
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
|
import com.tangem.blockchain.common.AmountType
|
||||||
import com.tangem.blockchain.common.DynamicAddressesManager
|
import com.tangem.blockchain.common.DynamicAddressesManager
|
||||||
import com.tangem.blockchain.common.TransactionData
|
import com.tangem.blockchain.common.TransactionData
|
||||||
import com.tangem.blockchain.common.WalletManager
|
import com.tangem.blockchain.common.WalletManager
|
||||||
import com.tangem.blockchain.common.transaction.Fee
|
import com.tangem.blockchain.common.transaction.Fee
|
||||||
|
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||||
import com.tangem.blockchain.extensions.Result
|
import com.tangem.blockchain.extensions.Result
|
||||||
import com.tangem.domain.dynamicaddresses.repository.ConsolidationRepository
|
import com.tangem.domain.dynamicaddresses.repository.ConsolidationRepository
|
||||||
import com.tangem.domain.models.network.Network
|
import com.tangem.domain.models.network.Network
|
||||||
|
|
@ -33,13 +35,25 @@ internal class DefaultConsolidationRepository(
|
||||||
|
|
||||||
when (val result = dynamicAddressesManager.createConsolidationTransaction(fee)) {
|
when (val result = dynamicAddressesManager.createConsolidationTransaction(fee)) {
|
||||||
is Result.Success -> result.data
|
is Result.Success -> result.data
|
||||||
is Result.Failure -> error("Failed to create consolidation tx: ${result.error}")
|
is Result.Failure -> throw result.error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UnusedParameter")
|
private suspend fun getNormalFee(walletManager: WalletManager): Fee {
|
||||||
private fun getNormalFee(walletManager: WalletManager): Fee {
|
val coinAmount = walletManager.wallet.amounts[AmountType.Coin]
|
||||||
TODO("Fee calculation for consolidation from multiple addresses is not yet implemented")
|
?: error("Coin amount not found")
|
||||||
|
val feeResult = walletManager.getFee(
|
||||||
|
amount = coinAmount,
|
||||||
|
destination = walletManager.wallet.address,
|
||||||
|
)
|
||||||
|
|
||||||
|
return when (feeResult) {
|
||||||
|
is Result.Success -> when (val txFee = feeResult.data) {
|
||||||
|
is TransactionFee.Single -> txFee.normal
|
||||||
|
is TransactionFee.Choosable -> txFee.normal
|
||||||
|
}
|
||||||
|
is Result.Failure -> throw feeResult.error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,42 +1,65 @@
|
||||||
package com.tangem.feature.tokendetails.presentation.tokendetails.model
|
package com.tangem.feature.tokendetails.presentation.tokendetails.model
|
||||||
|
|
||||||
import com.tangem.common.core.TangemSdkError
|
import com.tangem.common.core.TangemSdkError
|
||||||
|
import com.tangem.core.decompose.di.GlobalUiMessageSender
|
||||||
import com.tangem.core.decompose.ui.UiMessageSender
|
import com.tangem.core.decompose.ui.UiMessageSender
|
||||||
import com.tangem.core.res.R
|
import com.tangem.core.res.R
|
||||||
import com.tangem.core.ui.extensions.resourceReference
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
import com.tangem.core.ui.message.SnackbarMessage
|
import com.tangem.core.ui.message.SnackbarMessage
|
||||||
|
import com.tangem.common.ui.amountScreen.utils.getFiatString
|
||||||
|
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||||
|
import com.tangem.domain.dynamicaddresses.CreateConsolidationTransactionUseCase
|
||||||
|
import com.tangem.domain.dynamicaddresses.DisableDynamicAddressesUseCase
|
||||||
import com.tangem.domain.dynamicaddresses.EnableDynamicAddressesError
|
import com.tangem.domain.dynamicaddresses.EnableDynamicAddressesError
|
||||||
import com.tangem.domain.dynamicaddresses.EnableDynamicAddressesUseCase
|
import com.tangem.domain.dynamicaddresses.EnableDynamicAddressesUseCase
|
||||||
import com.tangem.domain.dynamicaddresses.IsXpubDerivedUseCase
|
import com.tangem.domain.dynamicaddresses.IsXpubDerivedUseCase
|
||||||
|
import com.tangem.domain.dynamicaddresses.model.DynamicAddressesStatus
|
||||||
import com.tangem.domain.dynamicaddresses.repository.DynamicAddressesRepository
|
import com.tangem.domain.dynamicaddresses.repository.DynamicAddressesRepository
|
||||||
|
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||||
import com.tangem.domain.models.network.Network
|
import com.tangem.domain.models.network.Network
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWallet
|
||||||
|
import com.tangem.utils.Provider
|
||||||
|
import com.tangem.domain.transaction.error.SendTransactionError
|
||||||
|
import com.tangem.domain.transaction.usecase.GetFeeUseCase
|
||||||
|
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
|
||||||
import com.tangem.domain.wallets.usecase.GetExtendedPublicKeyForCurrencyUseCase
|
import com.tangem.domain.wallets.usecase.GetExtendedPublicKeyForCurrencyUseCase
|
||||||
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.dynamicaddresses.DynamicAddressesBottomSheetConfig
|
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.dynamicaddresses.DynamicAddressesBottomSheetConfig
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import com.tangem.utils.coroutines.runSuspendCatching
|
||||||
import com.tangem.utils.logging.TangemLogger
|
import com.tangem.utils.logging.TangemLogger
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Suppress("LongParameterList")
|
@Suppress("LongParameterList")
|
||||||
internal class DynamicAddressesDelegate(
|
internal class DynamicAddressesDelegate @AssistedInject constructor(
|
||||||
private val enableDynamicAddressesUseCase: EnableDynamicAddressesUseCase,
|
private val enableDynamicAddressesUseCase: EnableDynamicAddressesUseCase,
|
||||||
|
private val disableDynamicAddressesUseCase: DisableDynamicAddressesUseCase,
|
||||||
|
private val createConsolidationTransactionUseCase: CreateConsolidationTransactionUseCase,
|
||||||
|
private val getFeeUseCase: GetFeeUseCase,
|
||||||
|
private val sendTransactionUseCase: SendTransactionUseCase,
|
||||||
private val isXpubDerivedUseCase: IsXpubDerivedUseCase,
|
private val isXpubDerivedUseCase: IsXpubDerivedUseCase,
|
||||||
private val dynamicAddressesRepository: DynamicAddressesRepository,
|
private val dynamicAddressesRepository: DynamicAddressesRepository,
|
||||||
private val getExtendedPublicKeyUseCase: GetExtendedPublicKeyForCurrencyUseCase,
|
private val getExtendedPublicKeyUseCase: GetExtendedPublicKeyForCurrencyUseCase,
|
||||||
private val uiMessageSender: UiMessageSender,
|
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
|
||||||
private val userWalletId: UserWalletId,
|
|
||||||
private val network: Network,
|
|
||||||
private val coroutineScope: CoroutineScope,
|
|
||||||
private val dispatchers: CoroutineDispatcherProvider,
|
private val dispatchers: CoroutineDispatcherProvider,
|
||||||
private val showBottomSheet: () -> Unit,
|
@Assisted private val userWallet: UserWallet,
|
||||||
private val dismissBottomSheet: () -> Unit,
|
@Assisted private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
|
||||||
private val onDynamicAddressesEnabled: () -> Unit,
|
@Assisted private val appCurrencyProvider: Provider<AppCurrency>,
|
||||||
|
@Assisted private val coroutineScope: CoroutineScope,
|
||||||
|
@Assisted("showBottomSheet") private val showBottomSheet: () -> Unit,
|
||||||
|
@Assisted("dismissBottomSheet") private val dismissBottomSheet: () -> Unit,
|
||||||
|
@Assisted("onDynamicAddressesStateChanged") private val onDynamicAddressesStateChanged: () -> Unit,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
private val userWalletId get() = userWallet.walletId
|
||||||
|
|
||||||
private val _bottomSheetConfig = MutableStateFlow<DynamicAddressesBottomSheetConfig>(
|
private val _bottomSheetConfig = MutableStateFlow<DynamicAddressesBottomSheetConfig>(
|
||||||
DynamicAddressesBottomSheetConfig.Enable(
|
DynamicAddressesBottomSheetConfig.Enable(
|
||||||
isCardScanRequired = false,
|
isCardScanRequired = false,
|
||||||
|
|
@ -45,27 +68,45 @@ internal class DynamicAddressesDelegate(
|
||||||
)
|
)
|
||||||
val bottomSheetConfig: StateFlow<DynamicAddressesBottomSheetConfig> = _bottomSheetConfig.asStateFlow()
|
val bottomSheetConfig: StateFlow<DynamicAddressesBottomSheetConfig> = _bottomSheetConfig.asStateFlow()
|
||||||
|
|
||||||
fun onDynamicAddressesClick() {
|
// region Entry point
|
||||||
coroutineScope.launch(dispatchers.main) {
|
|
||||||
val hasConflicts = dynamicAddressesRepository.hasConflictingCustomTokens(userWalletId, network)
|
|
||||||
if (hasConflicts) {
|
|
||||||
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.Unavailable(
|
|
||||||
onGotItClick = dismissBottomSheet,
|
|
||||||
)
|
|
||||||
showBottomSheet()
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
val isCardScanRequired = !isXpubAlreadyDerived()
|
fun onDynamicAddressesClick() {
|
||||||
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.Enable(
|
val network = cryptoCurrencyStatusProvider()?.currency?.network ?: return
|
||||||
isCardScanRequired = isCardScanRequired,
|
coroutineScope.launch(dispatchers.main) {
|
||||||
onEnableClick = ::onEnableClick,
|
val status = dynamicAddressesRepository.getStatus(userWalletId, network).first()
|
||||||
)
|
when (status) {
|
||||||
showBottomSheet()
|
DynamicAddressesStatus.ENABLED,
|
||||||
|
DynamicAddressesStatus.ENABLED_REQUIRES_SETUP,
|
||||||
|
-> onDisableFlow(network)
|
||||||
|
DynamicAddressesStatus.DISABLED -> onEnableFlow(network)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Enable flow
|
||||||
|
|
||||||
|
private suspend fun onEnableFlow(network: Network) {
|
||||||
|
val hasConflicts = dynamicAddressesRepository.hasConflictingCustomTokens(userWalletId, network)
|
||||||
|
if (hasConflicts) {
|
||||||
|
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ConflictingCustomTokens(
|
||||||
|
onDismissClick = dismissBottomSheet,
|
||||||
|
)
|
||||||
|
showBottomSheet()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val isCardScanRequired = !isXpubAlreadyDerived(network)
|
||||||
|
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.Enable(
|
||||||
|
isCardScanRequired = isCardScanRequired,
|
||||||
|
onEnableClick = ::onEnableClick,
|
||||||
|
)
|
||||||
|
showBottomSheet()
|
||||||
|
}
|
||||||
|
|
||||||
private fun onEnableClick() {
|
private fun onEnableClick() {
|
||||||
|
val network = cryptoCurrencyStatusProvider()?.currency?.network ?: return
|
||||||
coroutineScope.launch(dispatchers.main) {
|
coroutineScope.launch(dispatchers.main) {
|
||||||
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.Enable(
|
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.Enable(
|
||||||
isCardScanRequired = false,
|
isCardScanRequired = false,
|
||||||
|
|
@ -80,7 +121,7 @@ internal class DynamicAddressesDelegate(
|
||||||
} else {
|
} else {
|
||||||
TangemLogger.e("Failed to get XPUB: ${error.message}")
|
TangemLogger.e("Failed to get XPUB: ${error.message}")
|
||||||
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ServiceUnavailable(
|
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ServiceUnavailable(
|
||||||
onGotItClick = dismissBottomSheet,
|
onDismissClick = dismissBottomSheet,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return@launch
|
return@launch
|
||||||
|
|
@ -92,21 +133,21 @@ internal class DynamicAddressesDelegate(
|
||||||
ifLeft = { error ->
|
ifLeft = { error ->
|
||||||
when (error) {
|
when (error) {
|
||||||
is EnableDynamicAddressesError.ConflictingCustomTokens -> {
|
is EnableDynamicAddressesError.ConflictingCustomTokens -> {
|
||||||
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.Unavailable(
|
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ConflictingCustomTokens(
|
||||||
onGotItClick = dismissBottomSheet,
|
onDismissClick = dismissBottomSheet,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is EnableDynamicAddressesError.ServiceError -> {
|
is EnableDynamicAddressesError.ServiceError -> {
|
||||||
TangemLogger.e("Failed to enable DA: ${error.cause.message}")
|
TangemLogger.e("Failed to enable DA: ${error.cause.message}")
|
||||||
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ServiceUnavailable(
|
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ServiceUnavailable(
|
||||||
onGotItClick = dismissBottomSheet,
|
onDismissClick = dismissBottomSheet,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ifRight = {
|
ifRight = {
|
||||||
dismissBottomSheet()
|
dismissBottomSheet()
|
||||||
onDynamicAddressesEnabled()
|
onDynamicAddressesStateChanged()
|
||||||
uiMessageSender.send(
|
uiMessageSender.send(
|
||||||
SnackbarMessage(message = resourceReference(R.string.dynamic_addresses_enabled_toast_title)),
|
SnackbarMessage(message = resourceReference(R.string.dynamic_addresses_enabled_toast_title)),
|
||||||
)
|
)
|
||||||
|
|
@ -115,11 +156,208 @@ internal class DynamicAddressesDelegate(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun isXpubAlreadyDerived(): Boolean {
|
// endregion
|
||||||
|
|
||||||
|
// region Disable flow
|
||||||
|
|
||||||
|
private fun onDisableFlow(network: Network) {
|
||||||
|
coroutineScope.launch(dispatchers.main) {
|
||||||
|
disableDynamicAddressesUseCase(userWalletId, network).fold(
|
||||||
|
ifLeft = { error ->
|
||||||
|
TangemLogger.e("Failed to check disable: ${error.message}")
|
||||||
|
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ServiceUnavailable(
|
||||||
|
onDismissClick = dismissBottomSheet,
|
||||||
|
)
|
||||||
|
showBottomSheet()
|
||||||
|
},
|
||||||
|
ifRight = { isConsolidationRequired ->
|
||||||
|
if (!isConsolidationRequired) {
|
||||||
|
showSimpleDisableSheet()
|
||||||
|
} else {
|
||||||
|
showDisableSheetAndLoadFee()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showSimpleDisableSheet() {
|
||||||
|
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.DisableWithoutConsolidation(
|
||||||
|
onDisableClick = ::onSimpleDisableClick,
|
||||||
|
onReadMoreClick = ::onReadMoreClick,
|
||||||
|
)
|
||||||
|
showBottomSheet()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onSimpleDisableClick() {
|
||||||
|
val network = cryptoCurrencyStatusProvider()?.currency?.network ?: return
|
||||||
|
coroutineScope.launch(dispatchers.main) {
|
||||||
|
runSuspendCatching { dynamicAddressesRepository.disable(userWalletId, network) }
|
||||||
|
.onSuccess {
|
||||||
|
dismissBottomSheet()
|
||||||
|
onDynamicAddressesStateChanged()
|
||||||
|
uiMessageSender.send(
|
||||||
|
SnackbarMessage(message = resourceReference(R.string.dynamic_addresses_disabled_popup_title)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.onFailure { e ->
|
||||||
|
TangemLogger.e("Failed to disable DA: ${e.message}")
|
||||||
|
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ServiceUnavailable(
|
||||||
|
onDismissClick = dismissBottomSheet,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showDisableSheetAndLoadFee() {
|
||||||
|
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.DisableWithConsolidation(
|
||||||
|
feeState = DynamicAddressesBottomSheetConfig.DisableFeeState.Loading,
|
||||||
|
onDisableClick = ::onDisableClick,
|
||||||
|
onRefreshFee = ::loadDisableFee,
|
||||||
|
onReadMoreClick = ::onReadMoreClick,
|
||||||
|
)
|
||||||
|
showBottomSheet()
|
||||||
|
loadDisableFee()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadDisableFee() {
|
||||||
|
coroutineScope.launch(dispatchers.main) {
|
||||||
|
_bottomSheetConfig.value = disableWithConsolidationConfig().copy(
|
||||||
|
feeState = DynamicAddressesBottomSheetConfig.DisableFeeState.Loading,
|
||||||
|
)
|
||||||
|
|
||||||
|
val status = cryptoCurrencyStatusProvider()
|
||||||
|
val currency = status?.currency
|
||||||
|
val balance = status?.value?.amount
|
||||||
|
val address = status?.value?.networkAddress?.defaultAddress?.value
|
||||||
|
|
||||||
|
if (currency == null || balance == null || address == null) {
|
||||||
|
_bottomSheetConfig.value = disableWithConsolidationConfig().copy(
|
||||||
|
feeState = DynamicAddressesBottomSheetConfig.DisableFeeState.Error,
|
||||||
|
)
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
getFeeUseCase(
|
||||||
|
amount = balance,
|
||||||
|
destination = address,
|
||||||
|
userWallet = userWallet,
|
||||||
|
cryptoCurrency = currency,
|
||||||
|
).fold(
|
||||||
|
ifLeft = {
|
||||||
|
_bottomSheetConfig.value = disableWithConsolidationConfig().copy(
|
||||||
|
feeState = DynamicAddressesBottomSheetConfig.DisableFeeState.Error,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
ifRight = { txFee ->
|
||||||
|
val fee = txFee.normal
|
||||||
|
val fiatFormatted = getFiatString(
|
||||||
|
value = fee.amount.value,
|
||||||
|
rate = status.value.fiatRate,
|
||||||
|
appCurrency = appCurrencyProvider(),
|
||||||
|
approximate = true,
|
||||||
|
)
|
||||||
|
_bottomSheetConfig.value = disableWithConsolidationConfig().copy(
|
||||||
|
feeState = DynamicAddressesBottomSheetConfig.DisableFeeState.Content(
|
||||||
|
feeSymbol = currency.symbol,
|
||||||
|
fiatFormatted = fiatFormatted,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun disableWithConsolidationConfig(): DynamicAddressesBottomSheetConfig.DisableWithConsolidation {
|
||||||
|
return _bottomSheetConfig.value as? DynamicAddressesBottomSheetConfig.DisableWithConsolidation
|
||||||
|
?: DynamicAddressesBottomSheetConfig.DisableWithConsolidation(
|
||||||
|
onDisableClick = ::onDisableClick,
|
||||||
|
onRefreshFee = ::loadDisableFee,
|
||||||
|
onReadMoreClick = ::onReadMoreClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Replace with actual DA documentation URL
|
||||||
|
private fun onReadMoreClick() {
|
||||||
|
// Stub: open documentation about Dynamic Addresses consolidation
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onDisableClick() {
|
||||||
|
val network = cryptoCurrencyStatusProvider()?.currency?.network ?: return
|
||||||
|
coroutineScope.launch(dispatchers.main) {
|
||||||
|
_bottomSheetConfig.value = disableWithConsolidationConfig().copy(
|
||||||
|
isSending = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
val txData = createConsolidationTransactionUseCase(userWalletId, network).fold(
|
||||||
|
ifLeft = { error ->
|
||||||
|
TangemLogger.e("Failed to create consolidation tx: ${error.message}")
|
||||||
|
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ServiceUnavailable(
|
||||||
|
onDismissClick = dismissBottomSheet,
|
||||||
|
)
|
||||||
|
return@launch
|
||||||
|
},
|
||||||
|
ifRight = { it },
|
||||||
|
)
|
||||||
|
|
||||||
|
sendTransactionUseCase(
|
||||||
|
txData = txData,
|
||||||
|
userWallet = userWallet,
|
||||||
|
network = network,
|
||||||
|
).fold(
|
||||||
|
ifLeft = { error ->
|
||||||
|
if (error is SendTransactionError.UserCancelledError) {
|
||||||
|
dismissBottomSheet()
|
||||||
|
} else {
|
||||||
|
TangemLogger.e("Failed to send consolidation tx: $error")
|
||||||
|
_bottomSheetConfig.value = DynamicAddressesBottomSheetConfig.ServiceUnavailable(
|
||||||
|
onDismissClick = dismissBottomSheet,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ifRight = {
|
||||||
|
try {
|
||||||
|
dynamicAddressesRepository.disable(userWalletId, network)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
TangemLogger.e("Failed to disable DA after consolidation: ${e.message}")
|
||||||
|
}
|
||||||
|
dismissBottomSheet()
|
||||||
|
onDynamicAddressesStateChanged()
|
||||||
|
uiMessageSender.send(
|
||||||
|
SnackbarMessage(
|
||||||
|
message = resourceReference(R.string.dynamic_addresses_disabled_popup_title),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region Common
|
||||||
|
|
||||||
|
private suspend fun isXpubAlreadyDerived(network: Network): Boolean {
|
||||||
return isXpubDerivedUseCase(userWalletId, network)
|
return isXpubDerivedUseCase(userWalletId, network)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isUserCancellation(error: Throwable): Boolean {
|
private fun isUserCancellation(error: Throwable): Boolean {
|
||||||
return error is TangemSdkError.UserCancelled || error.cause is TangemSdkError.UserCancelled
|
return error is TangemSdkError.UserCancelled || error.cause is TangemSdkError.UserCancelled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory {
|
||||||
|
@Suppress("LongParameterList")
|
||||||
|
fun create(
|
||||||
|
userWallet: UserWallet,
|
||||||
|
cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus?>,
|
||||||
|
appCurrencyProvider: Provider<AppCurrency>,
|
||||||
|
coroutineScope: CoroutineScope,
|
||||||
|
@Assisted("showBottomSheet") showBottomSheet: () -> Unit,
|
||||||
|
@Assisted("dismissBottomSheet") dismissBottomSheet: () -> Unit,
|
||||||
|
@Assisted("onDynamicAddressesStateChanged") onDynamicAddressesStateChanged: () -> Unit,
|
||||||
|
): DynamicAddressesDelegate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -11,10 +11,7 @@ import com.tangem.blockchain.common.address.AddressType
|
||||||
import com.tangem.crypto.hdWallet.DerivationPath
|
import com.tangem.crypto.hdWallet.DerivationPath
|
||||||
import com.tangem.domain.dynamicaddresses.DynamicAddressesFeatureToggles
|
import com.tangem.domain.dynamicaddresses.DynamicAddressesFeatureToggles
|
||||||
import com.tangem.domain.dynamicaddresses.DynamicAddressesSupportedBlockchains
|
import com.tangem.domain.dynamicaddresses.DynamicAddressesSupportedBlockchains
|
||||||
import com.tangem.domain.dynamicaddresses.EnableDynamicAddressesUseCase
|
|
||||||
import com.tangem.domain.dynamicaddresses.IsXpubDerivedUseCase
|
|
||||||
import com.tangem.domain.dynamicaddresses.IsXpubSupportedUseCase
|
import com.tangem.domain.dynamicaddresses.IsXpubSupportedUseCase
|
||||||
import com.tangem.domain.dynamicaddresses.repository.DynamicAddressesRepository
|
|
||||||
import com.tangem.common.routing.AppRoute
|
import com.tangem.common.routing.AppRoute
|
||||||
import com.tangem.common.routing.AppRouter
|
import com.tangem.common.routing.AppRouter
|
||||||
import com.tangem.common.ui.bottomsheet.receive.AddressModel
|
import com.tangem.common.ui.bottomsheet.receive.AddressModel
|
||||||
|
|
@ -170,11 +167,9 @@ internal class TokenDetailsModel @Inject constructor(
|
||||||
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
|
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
|
||||||
private val yieldSupplyGetRewardsBalanceUseCase: YieldSupplyGetRewardsBalanceUseCase,
|
private val yieldSupplyGetRewardsBalanceUseCase: YieldSupplyGetRewardsBalanceUseCase,
|
||||||
private val signCloreMessageUseCase: SignCloreMessageUseCase,
|
private val signCloreMessageUseCase: SignCloreMessageUseCase,
|
||||||
private val enableDynamicAddressesUseCase: EnableDynamicAddressesUseCase,
|
|
||||||
private val isXpubSupportedUseCase: IsXpubSupportedUseCase,
|
private val isXpubSupportedUseCase: IsXpubSupportedUseCase,
|
||||||
private val isXpubDerivedUseCase: IsXpubDerivedUseCase,
|
|
||||||
private val dynamicAddressesRepository: DynamicAddressesRepository,
|
|
||||||
private val dynamicAddressesFeatureToggles: DynamicAddressesFeatureToggles,
|
private val dynamicAddressesFeatureToggles: DynamicAddressesFeatureToggles,
|
||||||
|
private val dynamicAddressesDelegateFactory: DynamicAddressesDelegate.Factory,
|
||||||
private val dialogFactory: TokenDetailsDialogFactory,
|
private val dialogFactory: TokenDetailsDialogFactory,
|
||||||
private val userWalletsListRepository: UserWalletsListRepository,
|
private val userWalletsListRepository: UserWalletsListRepository,
|
||||||
private val singleAccountListSupplier: SingleAccountListSupplier,
|
private val singleAccountListSupplier: SingleAccountListSupplier,
|
||||||
|
|
@ -246,21 +241,16 @@ internal class TokenDetailsModel @Inject constructor(
|
||||||
|
|
||||||
// region Dynamic Addresses
|
// region Dynamic Addresses
|
||||||
val dynamicAddressesDelegate by lazy(mode = LazyThreadSafetyMode.NONE) {
|
val dynamicAddressesDelegate by lazy(mode = LazyThreadSafetyMode.NONE) {
|
||||||
DynamicAddressesDelegate(
|
dynamicAddressesDelegateFactory.create(
|
||||||
enableDynamicAddressesUseCase = enableDynamicAddressesUseCase,
|
userWallet = userWallet,
|
||||||
isXpubDerivedUseCase = isXpubDerivedUseCase,
|
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||||
dynamicAddressesRepository = dynamicAddressesRepository,
|
appCurrencyProvider = Provider { selectedAppCurrencyFlow.value },
|
||||||
getExtendedPublicKeyUseCase = getExtendedPublicKeyForCurrencyUseCase,
|
|
||||||
uiMessageSender = uiMessageSender,
|
|
||||||
userWalletId = userWalletId,
|
|
||||||
network = cryptoCurrency.network,
|
|
||||||
coroutineScope = modelScope,
|
coroutineScope = modelScope,
|
||||||
dispatchers = dispatchers,
|
|
||||||
showBottomSheet = {
|
showBottomSheet = {
|
||||||
bottomSheetNavigation.activate(TokenDetailsBottomSheetConfig.DynamicAddresses)
|
bottomSheetNavigation.activate(TokenDetailsBottomSheetConfig.DynamicAddresses)
|
||||||
},
|
},
|
||||||
dismissBottomSheet = bottomSheetNavigation::dismiss,
|
dismissBottomSheet = bottomSheetNavigation::dismiss,
|
||||||
onDynamicAddressesEnabled = ::onDynamicAddressesEnabled,
|
onDynamicAddressesStateChanged = ::onDynamicAddressesStateChanged,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// endregion Dynamic Addresses
|
// endregion Dynamic Addresses
|
||||||
|
|
@ -707,7 +697,7 @@ internal class TokenDetailsModel @Inject constructor(
|
||||||
|
|
||||||
override fun onDynamicAddressesClick() = dynamicAddressesDelegate.onDynamicAddressesClick()
|
override fun onDynamicAddressesClick() = dynamicAddressesDelegate.onDynamicAddressesClick()
|
||||||
|
|
||||||
private fun onDynamicAddressesEnabled() {
|
private fun onDynamicAddressesStateChanged() {
|
||||||
updateTopBarMenu()
|
updateTopBarMenu()
|
||||||
modelScope.launch(dispatchers.main) {
|
modelScope.launch(dispatchers.main) {
|
||||||
cryptoCurrencyBalanceFetcher.invokeAndAwait(userWalletId = userWalletId, currency = cryptoCurrency)
|
cryptoCurrencyBalanceFetcher.invokeAndAwait(userWalletId = userWalletId, currency = cryptoCurrency)
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,14 @@ import androidx.compose.runtime.Composable
|
||||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
|
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
|
||||||
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
|
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
|
||||||
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.core.ui.R as CoreR
|
import com.tangem.core.ui.R as CoreR
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun DynamicAddressesBottomSheet(config: TangemBottomSheetConfig) {
|
internal fun DynamicAddressesBottomSheet(config: TangemBottomSheetConfig) {
|
||||||
TangemModalBottomSheet<DynamicAddressesBottomSheetConfig>(
|
TangemModalBottomSheet<DynamicAddressesBottomSheetConfig>(
|
||||||
config = config,
|
config = config,
|
||||||
|
containerColor = TangemTheme.colors.background.tertiary,
|
||||||
title = {
|
title = {
|
||||||
TangemModalBottomSheetTitle(
|
TangemModalBottomSheetTitle(
|
||||||
endIconRes = CoreR.drawable.ic_close_24,
|
endIconRes = CoreR.drawable.ic_close_24,
|
||||||
|
|
@ -19,10 +21,18 @@ internal fun DynamicAddressesBottomSheet(config: TangemBottomSheetConfig) {
|
||||||
) { content ->
|
) { content ->
|
||||||
when (content) {
|
when (content) {
|
||||||
is DynamicAddressesBottomSheetConfig.Enable -> DynamicAddressesEnableContent(content = content)
|
is DynamicAddressesBottomSheetConfig.Enable -> DynamicAddressesEnableContent(content = content)
|
||||||
is DynamicAddressesBottomSheetConfig.Unavailable -> DynamicAddressesUnavailableContent(content = content)
|
is DynamicAddressesBottomSheetConfig.DisableWithoutConsolidation -> {
|
||||||
is DynamicAddressesBottomSheetConfig.ServiceUnavailable -> DynamicAddressesServiceUnavailableContent(
|
DynamicAddressesDisableWithoutConsolidationContent(content = content)
|
||||||
content = content,
|
}
|
||||||
)
|
is DynamicAddressesBottomSheetConfig.DisableWithConsolidation -> {
|
||||||
|
DynamicAddressesDisableWithConsolidationContent(content = content)
|
||||||
|
}
|
||||||
|
is DynamicAddressesBottomSheetConfig.ConflictingCustomTokens -> {
|
||||||
|
DynamicAddressesConflictingCustomTokensContent(content = content)
|
||||||
|
}
|
||||||
|
is DynamicAddressesBottomSheetConfig.ServiceUnavailable -> {
|
||||||
|
DynamicAddressesServiceUnavailableContent(content = content)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -12,11 +12,33 @@ internal sealed class DynamicAddressesBottomSheetConfig : TangemBottomSheetConfi
|
||||||
val onEnableClick: () -> Unit,
|
val onEnableClick: () -> Unit,
|
||||||
) : DynamicAddressesBottomSheetConfig()
|
) : DynamicAddressesBottomSheetConfig()
|
||||||
|
|
||||||
data class Unavailable(
|
data class DisableWithoutConsolidation(
|
||||||
val onGotItClick: () -> Unit,
|
val onDisableClick: () -> Unit,
|
||||||
|
val onReadMoreClick: () -> Unit,
|
||||||
|
) : DynamicAddressesBottomSheetConfig()
|
||||||
|
|
||||||
|
data class DisableWithConsolidation(
|
||||||
|
val feeState: DisableFeeState = DisableFeeState.Loading,
|
||||||
|
val isSending: Boolean = false,
|
||||||
|
val onDisableClick: () -> Unit,
|
||||||
|
val onRefreshFee: () -> Unit,
|
||||||
|
val onReadMoreClick: () -> Unit,
|
||||||
|
) : DynamicAddressesBottomSheetConfig()
|
||||||
|
|
||||||
|
sealed interface DisableFeeState {
|
||||||
|
data object Loading : DisableFeeState
|
||||||
|
data class Content(
|
||||||
|
val feeSymbol: String,
|
||||||
|
val fiatFormatted: String,
|
||||||
|
) : DisableFeeState
|
||||||
|
data object Error : DisableFeeState
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ConflictingCustomTokens(
|
||||||
|
val onDismissClick: () -> Unit,
|
||||||
) : DynamicAddressesBottomSheetConfig()
|
) : DynamicAddressesBottomSheetConfig()
|
||||||
|
|
||||||
data class ServiceUnavailable(
|
data class ServiceUnavailable(
|
||||||
val onGotItClick: () -> Unit,
|
val onDismissClick: () -> Unit,
|
||||||
) : DynamicAddressesBottomSheetConfig()
|
) : DynamicAddressesBottomSheetConfig()
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.dynamicaddresses
|
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.dynamicaddresses
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
|
@ -14,12 +16,28 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.LinkAnnotation
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.withLink
|
||||||
|
import androidx.compose.ui.text.withStyle
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import com.tangem.core.ui.components.PrimaryButton
|
import com.tangem.core.ui.components.PrimaryButton
|
||||||
|
import com.tangem.core.ui.components.PrimaryButtonIconEnd
|
||||||
|
import com.tangem.core.ui.components.TextShimmer
|
||||||
|
import com.tangem.core.ui.components.audits.AuditLabel
|
||||||
|
import com.tangem.core.ui.components.audits.AuditLabelUM
|
||||||
|
import com.tangem.core.ui.components.notifications.Notification
|
||||||
|
import com.tangem.core.ui.components.notifications.NotificationConfig
|
||||||
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
|
import com.tangem.core.ui.extensions.stringReference
|
||||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||||
import com.tangem.core.ui.res.TangemTheme
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
import com.tangem.core.ui.res.TangemThemePreview
|
||||||
import com.tangem.core.res.R
|
import com.tangem.core.res.R
|
||||||
import com.tangem.core.ui.R as CoreR
|
import com.tangem.core.ui.R as CoreR
|
||||||
|
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.dynamicaddresses.DynamicAddressesBottomSheetConfig.DisableFeeState
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun DynamicAddressesEnableContent(content: DynamicAddressesBottomSheetConfig.Enable) {
|
internal fun DynamicAddressesEnableContent(content: DynamicAddressesBottomSheetConfig.Enable) {
|
||||||
|
|
@ -72,13 +90,13 @@ internal fun DynamicAddressesEnableContent(content: DynamicAddressesBottomSheetC
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing24))
|
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing24))
|
||||||
|
|
||||||
PrimaryButton(
|
PrimaryButtonIconEnd(
|
||||||
text = stringResourceSafe(id = R.string.dynamic_addresses_enter_main_button_title),
|
text = stringResourceSafe(id = R.string.dynamic_addresses_enter_main_button_title),
|
||||||
|
iconResId = if (content.isCardScanRequired) CoreR.drawable.ic_tangem_24 else null,
|
||||||
onClick = content.onEnableClick,
|
onClick = content.onEnableClick,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
showProgress = content.isLoading,
|
showProgress = content.isLoading,
|
||||||
enabled = !content.isLoading,
|
enabled = !content.isLoading,
|
||||||
// TODO add card icon when isCardScanRequired
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing16))
|
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing16))
|
||||||
|
|
@ -86,12 +104,205 @@ internal fun DynamicAddressesEnableContent(content: DynamicAddressesBottomSheetC
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun DynamicAddressesUnavailableContent(content: DynamicAddressesBottomSheetConfig.Unavailable) {
|
internal fun DynamicAddressesDisableWithoutConsolidationContent(
|
||||||
|
content: DynamicAddressesBottomSheetConfig.DisableWithoutConsolidation,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
DisableHeader()
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing24))
|
||||||
|
|
||||||
|
PrimaryButtonIconEnd(
|
||||||
|
text = stringResourceSafe(id = R.string.dynamic_addresses_disable_main_button_title),
|
||||||
|
iconResId = null,
|
||||||
|
onClick = content.onDisableClick,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing16))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun DynamicAddressesDisableWithConsolidationContent(
|
||||||
|
content: DynamicAddressesBottomSheetConfig.DisableWithConsolidation,
|
||||||
|
) {
|
||||||
|
val isConfirmEnabled = content.feeState is DisableFeeState.Content && !content.isSending
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
DisableHeader()
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing16))
|
||||||
|
|
||||||
|
DisableFeeBlock(
|
||||||
|
feeState = content.feeState,
|
||||||
|
onReadMoreClick = content.onReadMoreClick,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (content.feeState is DisableFeeState.Error) {
|
||||||
|
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing12))
|
||||||
|
|
||||||
|
Notification(
|
||||||
|
config = NotificationConfig(
|
||||||
|
title = resourceReference(R.string.send_fee_unreachable_error_title),
|
||||||
|
subtitle = resourceReference(R.string.send_fee_unreachable_error_text),
|
||||||
|
iconResId = CoreR.drawable.ic_alert_24,
|
||||||
|
iconTint = NotificationConfig.IconTint.Warning,
|
||||||
|
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
|
||||||
|
text = resourceReference(R.string.warning_button_refresh),
|
||||||
|
onClick = content.onRefreshFee,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
containerColor = TangemTheme.colors.background.action,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing24))
|
||||||
|
|
||||||
|
PrimaryButtonIconEnd(
|
||||||
|
text = stringResourceSafe(id = R.string.dynamic_addresses_disable_main_button_title),
|
||||||
|
iconResId = CoreR.drawable.ic_tangem_24,
|
||||||
|
onClick = content.onDisableClick,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
showProgress = content.isSending,
|
||||||
|
enabled = isConfirmEnabled,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing16))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun DisableHeader() {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(id = CoreR.drawable.ic_dynamic_addresses_bottomsheet_enable_unavailable),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(TangemTheme.dimens.size44),
|
||||||
|
tint = TangemTheme.colors.icon.attention,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing12))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResourceSafe(id = R.string.dynamic_addresses_disable_title),
|
||||||
|
style = TangemTheme.typography.h3,
|
||||||
|
color = TangemTheme.colors.text.primary1,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing8))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResourceSafe(id = R.string.dynamic_addresses_disable_description),
|
||||||
|
style = TangemTheme.typography.body2,
|
||||||
|
color = TangemTheme.colors.text.secondary,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun DisableFeeBlock(feeState: DisableFeeState, onReadMoreClick: () -> Unit) {
|
||||||
|
Column(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(
|
||||||
|
color = TangemTheme.colors.background.action,
|
||||||
|
shape = TangemTheme.shapes.roundedCornersXMedium,
|
||||||
|
)
|
||||||
|
.padding(TangemTheme.dimens.spacing12),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResourceSafe(id = R.string.common_network_fee_title),
|
||||||
|
style = TangemTheme.typography.body2,
|
||||||
|
color = TangemTheme.colors.text.primary1,
|
||||||
|
)
|
||||||
|
when (feeState) {
|
||||||
|
is DisableFeeState.Loading -> TextShimmer(
|
||||||
|
style = TangemTheme.typography.body2,
|
||||||
|
modifier = Modifier.size(
|
||||||
|
width = TangemTheme.dimens.size80,
|
||||||
|
height = TangemTheme.dimens.spacing16,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
is DisableFeeState.Content -> Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
) {
|
||||||
|
AuditLabel(
|
||||||
|
state = AuditLabelUM(
|
||||||
|
text = stringReference(feeState.feeSymbol),
|
||||||
|
type = AuditLabelUM.Type.General,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = feeState.fiatFormatted,
|
||||||
|
style = TangemTheme.typography.body2,
|
||||||
|
color = TangemTheme.colors.text.tertiary,
|
||||||
|
modifier = Modifier.padding(start = TangemTheme.dimens.spacing4),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is DisableFeeState.Error -> Text(
|
||||||
|
text = "\u2014",
|
||||||
|
style = TangemTheme.typography.body2,
|
||||||
|
color = TangemTheme.colors.text.primary1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing8))
|
||||||
|
DisableFeeDescription(onReadMoreClick = onReadMoreClick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun DisableFeeDescription(onReadMoreClick: () -> Unit) {
|
||||||
|
val readMoreText = stringResourceSafe(id = R.string.common_read_more)
|
||||||
|
val fullText = stringResourceSafe(id = R.string.dynamic_addresses_disable_fee_description)
|
||||||
|
|
||||||
|
val annotatedString = buildAnnotatedString {
|
||||||
|
withStyle(SpanStyle(color = TangemTheme.colors.text.tertiary)) {
|
||||||
|
append(fullText)
|
||||||
|
append(" ")
|
||||||
|
}
|
||||||
|
withLink(
|
||||||
|
link = LinkAnnotation.Clickable(
|
||||||
|
tag = "read_more",
|
||||||
|
linkInteractionListener = { onReadMoreClick() },
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
withStyle(SpanStyle(color = TangemTheme.colors.text.accent)) {
|
||||||
|
append(readMoreText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = annotatedString,
|
||||||
|
style = TangemTheme.typography.caption2,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun DynamicAddressesConflictingCustomTokensContent(
|
||||||
|
content: DynamicAddressesBottomSheetConfig.ConflictingCustomTokens,
|
||||||
|
) {
|
||||||
ErrorContent(
|
ErrorContent(
|
||||||
titleRes = R.string.dynamic_addresses_error_has_custom_token_title,
|
titleRes = R.string.dynamic_addresses_error_has_custom_token_title,
|
||||||
descriptionRes = R.string.dynamic_addresses_error_has_custom_token_description,
|
descriptionRes = R.string.dynamic_addresses_error_has_custom_token_description,
|
||||||
buttonTextRes = R.string.common_got_it,
|
buttonTextRes = R.string.common_got_it,
|
||||||
onButtonClick = content.onGotItClick,
|
onButtonClick = content.onDismissClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,7 +312,7 @@ internal fun DynamicAddressesServiceUnavailableContent(content: DynamicAddresses
|
||||||
titleRes = R.string.dynamic_addresses_error_service_unavailable_title,
|
titleRes = R.string.dynamic_addresses_error_service_unavailable_title,
|
||||||
descriptionRes = R.string.dynamic_addresses_error_service_unavailable_description,
|
descriptionRes = R.string.dynamic_addresses_error_service_unavailable_description,
|
||||||
buttonTextRes = R.string.common_got_it,
|
buttonTextRes = R.string.common_got_it,
|
||||||
onButtonClick = content.onGotItClick,
|
onButtonClick = content.onDismissClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,7 +328,7 @@ private fun ErrorContent(titleRes: Int, descriptionRes: Int, buttonTextRes: Int,
|
||||||
painter = painterResource(id = CoreR.drawable.ic_dynamic_addresses_bottomsheet_enable_unavailable),
|
painter = painterResource(id = CoreR.drawable.ic_dynamic_addresses_bottomsheet_enable_unavailable),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier.size(TangemTheme.dimens.size44),
|
modifier = Modifier.size(TangemTheme.dimens.size44),
|
||||||
tint = TangemTheme.colors.icon.warning,
|
tint = TangemTheme.colors.icon.attention,
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing12))
|
Spacer(modifier = Modifier.height(TangemTheme.dimens.spacing12))
|
||||||
|
|
@ -176,4 +387,143 @@ private fun FeatureItem(iconRes: Int, title: String, description: String) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// region Previews
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
private fun Preview_Enable() {
|
||||||
|
TangemThemePreview {
|
||||||
|
DynamicAddressesEnableContent(
|
||||||
|
content = DynamicAddressesBottomSheetConfig.Enable(
|
||||||
|
isCardScanRequired = false,
|
||||||
|
onEnableClick = {},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
private fun Preview_EnableWithCardScan() {
|
||||||
|
TangemThemePreview {
|
||||||
|
DynamicAddressesEnableContent(
|
||||||
|
content = DynamicAddressesBottomSheetConfig.Enable(
|
||||||
|
isCardScanRequired = true,
|
||||||
|
onEnableClick = {},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
private fun Preview_DisableWithoutConsolidation() {
|
||||||
|
TangemThemePreview {
|
||||||
|
DynamicAddressesDisableWithoutConsolidationContent(
|
||||||
|
content = DynamicAddressesBottomSheetConfig.DisableWithoutConsolidation(
|
||||||
|
onDisableClick = {},
|
||||||
|
onReadMoreClick = {},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
private fun Preview_DisableFeeLoading() {
|
||||||
|
TangemThemePreview {
|
||||||
|
DynamicAddressesDisableWithConsolidationContent(
|
||||||
|
content = DynamicAddressesBottomSheetConfig.DisableWithConsolidation(
|
||||||
|
feeState = DisableFeeState.Loading,
|
||||||
|
onDisableClick = {},
|
||||||
|
onRefreshFee = {},
|
||||||
|
onReadMoreClick = {},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
private fun Preview_DisableFeeLoaded() {
|
||||||
|
TangemThemePreview {
|
||||||
|
DynamicAddressesDisableWithConsolidationContent(
|
||||||
|
content = DynamicAddressesBottomSheetConfig.DisableWithConsolidation(
|
||||||
|
feeState = DisableFeeState.Content(
|
||||||
|
feeSymbol = "BTC",
|
||||||
|
fiatFormatted = "~$0.12",
|
||||||
|
),
|
||||||
|
onDisableClick = {},
|
||||||
|
onRefreshFee = {},
|
||||||
|
onReadMoreClick = {},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
private fun Preview_DisableFeeError() {
|
||||||
|
TangemThemePreview {
|
||||||
|
DynamicAddressesDisableWithConsolidationContent(
|
||||||
|
content = DynamicAddressesBottomSheetConfig.DisableWithConsolidation(
|
||||||
|
feeState = DisableFeeState.Error,
|
||||||
|
onDisableClick = {},
|
||||||
|
onRefreshFee = {},
|
||||||
|
onReadMoreClick = {},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
private fun Preview_DisableSending() {
|
||||||
|
TangemThemePreview {
|
||||||
|
DynamicAddressesDisableWithConsolidationContent(
|
||||||
|
content = DynamicAddressesBottomSheetConfig.DisableWithConsolidation(
|
||||||
|
feeState = DisableFeeState.Content(
|
||||||
|
feeSymbol = "BTC",
|
||||||
|
fiatFormatted = "~$0.12",
|
||||||
|
),
|
||||||
|
isSending = true,
|
||||||
|
onDisableClick = {},
|
||||||
|
onRefreshFee = {},
|
||||||
|
onReadMoreClick = {},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
private fun Preview_ConflictingCustomTokens() {
|
||||||
|
TangemThemePreview {
|
||||||
|
DynamicAddressesConflictingCustomTokensContent(
|
||||||
|
content = DynamicAddressesBottomSheetConfig.ConflictingCustomTokens(onDismissClick = {}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
private fun Preview_ServiceUnavailable() {
|
||||||
|
TangemThemePreview {
|
||||||
|
DynamicAddressesServiceUnavailableContent(
|
||||||
|
content = DynamicAddressesBottomSheetConfig.ServiceUnavailable(onDismissClick = {}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
# https://github.com/tangem/tangem-sdk-android/
|
# https://github.com/tangem/tangem-sdk-android/
|
||||||
# https://github.com/tangem/vico
|
# https://github.com/tangem/vico
|
||||||
|
|
||||||
tangemBlockchainSdk = "develop-1482"
|
tangemBlockchainSdk = "develop-1486"
|
||||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||||
tangemCardSdk = "develop-602"
|
tangemCardSdk = "develop-602"
|
||||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue