Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-22 13:17:34 +01:00
parent a96fb4b2f4
commit 721115e64e
9 changed files with 160 additions and 56 deletions

View file

@ -274,6 +274,10 @@
<item quantity="one">day</item> <item quantity="one">day</item>
<item quantity="other">days</item> <item quantity="other">days</item>
</plurals> </plurals>
<plurals name="common_days_time_ago">
<item quantity="one">%d day ago</item>
<item quantity="other">%d days ago</item>
</plurals>
<string name="common_delete">Delete</string> <string name="common_delete">Delete</string>
<string name="common_disable">Disable</string> <string name="common_disable">Disable</string>
<string name="common_disabled">Disabled</string> <string name="common_disabled">Disabled</string>
@ -308,6 +312,10 @@
<string name="common_hide">Hide</string> <string name="common_hide">Hide</string>
<string name="common_hold_to">Hold to %s</string> <string name="common_hold_to">Hold to %s</string>
<string name="common_hour">hour</string> <string name="common_hour">hour</string>
<plurals name="common_hours_time_ago">
<item quantity="one">%dh ago</item>
<item quantity="other">%dh ago</item>
</plurals>
<string name="common_import">Import</string> <string name="common_import">Import</string>
<string name="common_in_progress">In progress</string> <string name="common_in_progress">In progress</string>
<string name="common_insufficient_balance">Insufficient balance</string> <string name="common_insufficient_balance">Insufficient balance</string>
@ -318,6 +326,10 @@
<string name="common_locked">Locked</string> <string name="common_locked">Locked</string>
<string name="common_locked_wallets">Locked Wallets</string> <string name="common_locked_wallets">Locked Wallets</string>
<string name="common_main_network">Main network</string> <string name="common_main_network">Main network</string>
<plurals name="common_minutes_time_ago">
<item quantity="one">%d minute ago</item>
<item quantity="other">%d minutes ago</item>
</plurals>
<string name="common_month">month</string> <string name="common_month">month</string>
<string name="common_network_fee_title">Network fee</string> <string name="common_network_fee_title">Network fee</string>
<string name="common_network_fee_warning_content">Amount sent will be reduced by %1$s (%2$s) to cover the selected fee level</string> <string name="common_network_fee_warning_content">Amount sent will be reduced by %1$s (%2$s) to cover the selected fee level</string>
@ -487,7 +499,6 @@
<string name="dynamic_addresses_disable_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_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_receive_badge">Dynamic address</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>
<string name="dynamic_addresses_enter_features_receving_description">Easily receive funds in UTXO-based networks with automatic address generation — no manual address management required.</string> <string name="dynamic_addresses_enter_features_receving_description">Easily receive funds in UTXO-based networks with automatic address generation — no manual address management required.</string>
@ -498,6 +509,9 @@
<string name="dynamic_addresses_error_has_custom_token_title">Dynamic Addresses Unavailable</string> <string name="dynamic_addresses_error_has_custom_token_title">Dynamic Addresses Unavailable</string>
<string name="dynamic_addresses_error_service_unavailable_description">We cant connect to the provider right now. Please try again later.</string> <string name="dynamic_addresses_error_service_unavailable_description">We cant connect to the provider right now. Please try again later.</string>
<string name="dynamic_addresses_error_service_unavailable_title">Service unavailable. Please try again.</string> <string name="dynamic_addresses_error_service_unavailable_title">Service unavailable. Please try again.</string>
<string name="dynamic_addresses_notification_funds_found_description">Funds were found on additional addresses. Enable Dynamic Addresses to access them.</string>
<string name="dynamic_addresses_notification_funds_found_title">Funds found on additional addresses</string>
<string name="dynamic_addresses_receive_badge">Dynamic address</string>
<string name="earn_best_opportunities">Best opportunities</string> <string name="earn_best_opportunities">Best opportunities</string>
<string name="earn_clear_filter">Clear filter</string> <string name="earn_clear_filter">Clear filter</string>
<string name="earn_empty">The list is temporarily empty as its being refreshed. Check back in a moment.</string> <string name="earn_empty">The list is temporarily empty as its being refreshed. Check back in a moment.</string>
@ -569,6 +583,7 @@
<string name="express_provider">Provider</string> <string name="express_provider">Provider</string>
<string name="express_provider_best_rate">Best rate</string> <string name="express_provider_best_rate">Best rate</string>
<string name="express_provider_fca_warning_list">FCA Warning List</string> <string name="express_provider_fca_warning_list">FCA Warning List</string>
<string name="express_provider_fixed_rate_is_unavailable">Fixed rate is unavailable</string>
<string name="express_provider_great_rate">Competitive rate</string> <string name="express_provider_great_rate">Competitive rate</string>
<string name="express_provider_in_fca_warning_list">Provider in FCA warning list</string> <string name="express_provider_in_fca_warning_list">Provider in FCA warning list</string>
<string name="express_provider_max_amount">Available up to %s</string> <string name="express_provider_max_amount">Available up to %s</string>
@ -781,6 +796,7 @@
<string name="markets_loading_no_data_title">No data</string> <string name="markets_loading_no_data_title">No data</string>
<string name="markets_portfolio_block_add_token_title">**Add to your portfolio** to start buying, exchanging or receiving this asset</string> <string name="markets_portfolio_block_add_token_title">**Add to your portfolio** to start buying, exchanging or receiving this asset</string>
<string name="markets_portfolio_block_subtitle">In your portfolio</string> <string name="markets_portfolio_block_subtitle">In your portfolio</string>
<string name="markets_portfolio_block_title">Your portfolio</string>
<string name="markets_pulse_common_title">Market Pulse</string> <string name="markets_pulse_common_title">Market Pulse</string>
<string name="markets_quick_actions">Quick actions</string> <string name="markets_quick_actions">Quick actions</string>
<string name="markets_search_clear_all_hints">Clear all</string> <string name="markets_search_clear_all_hints">Clear all</string>
@ -1020,7 +1036,9 @@
<string name="onboarding_getting_started">Getting started</string> <string name="onboarding_getting_started">Getting started</string>
<string name="onboarding_linking_error_card_with_wallets">Another wallet has already been created on the card you\'re trying to add. If you have funds in this wallet, please withdraw it and then reset this card and add it as a backup.</string> <string name="onboarding_linking_error_card_with_wallets">Another wallet has already been created on the card you\'re trying to add. If you have funds in this wallet, please withdraw it and then reset this card and add it as a backup.</string>
<string name="onboarding_navbar_save_wallet">Save your wallet</string> <string name="onboarding_navbar_save_wallet">Save your wallet</string>
<string name="onboarding_navbar_title_biometrics">Use biometrics</string>
<string name="onboarding_navbar_title_creating_backup">Creating a backup</string> <string name="onboarding_navbar_title_creating_backup">Creating a backup</string>
<string name="onboarding_navbar_title_last_step">Last step</string>
<string name="onboarding_navbar_upgrade_wallet_biometrics">Biometrics</string> <string name="onboarding_navbar_upgrade_wallet_biometrics">Biometrics</string>
<string name="onboarding_seed_button_read_more">Read more about seed phrase</string> <string name="onboarding_seed_button_read_more">Read more about seed phrase</string>
<plurals name="onboarding_seed_generate_message_words_count"> <plurals name="onboarding_seed_generate_message_words_count">
@ -1516,13 +1534,21 @@
<string name="swap_story_fifth_subtitle">Feel confident with round-the-clock support to help with any issues</string> <string name="swap_story_fifth_subtitle">Feel confident with round-the-clock support to help with any issues</string>
<string name="swap_story_fifth_title">Always Here</string> <string name="swap_story_fifth_title">Always Here</string>
<string name="swap_story_first_subtitle">Multiple trusted providers in one place—swap any asset effortlessly in your wallet</string> <string name="swap_story_first_subtitle">Multiple trusted providers in one place—swap any asset effortlessly in your wallet</string>
<string name="swap_story_first_subtitle_v2">Exchange crypto directly in Tangem\nNo additional transfers\nNo moving funds to exchanges</string>
<string name="swap_story_first_title">Swap With Us</string> <string name="swap_story_first_title">Swap With Us</string>
<string name="swap_story_first_title_v2">Swap Inside Your Wallet</string>
<string name="swap_story_forth_subtitle">No fumbles, no turnovers, no blind spots—your transaction is always protected</string> <string name="swap_story_forth_subtitle">No fumbles, no turnovers, no blind spots—your transaction is always protected</string>
<string name="swap_story_forth_subtitle_v2">Swaps are executed through trusted providers. Your keys remain in your Tangem wallet at all times. Clear. Transparent. Self-custodial.</string>
<string name="swap_story_forth_title">Impenetrable Defense</string> <string name="swap_story_forth_title">Impenetrable Defense</string>
<string name="swap_story_forth_title_v2">You Stay in Control</string>
<string name="swap_story_second_subtitle">Maximize your value with rates sourced from a wide network of trusted providers, always choosing the best one</string> <string name="swap_story_second_subtitle">Maximize your value with rates sourced from a wide network of trusted providers, always choosing the best one</string>
<string name="swap_story_second_subtitle_v2">Tangem compares multiple providers, both DEX and CEX. The best rate is selected automatically. Prefer another provider? You can choose it manually.</string>
<string name="swap_story_second_title">Unbeatable Rates</string> <string name="swap_story_second_title">Unbeatable Rates</string>
<string name="swap_story_second_title_v2">Best Available Rate</string>
<string name="swap_story_third_subtitle">Hassle-free and intuitive, allowing you to swap tokens in just a few taps</string> <string name="swap_story_third_subtitle">Hassle-free and intuitive, allowing you to swap tokens in just a few taps</string>
<string name="swap_story_third_subtitle_v2">Swap across major networksand thousands of tokens 0% fee on stablecoin-to-stablecoin swaps</string>
<string name="swap_story_third_title">Simply Convenient</string> <string name="swap_story_third_title">Simply Convenient</string>
<string name="swap_story_third_title_v2">90+ Blockchains\n16,000+ Assets</string>
<string name="swap_via_provider">Swap via provider</string> <string name="swap_via_provider">Swap via provider</string>
<string name="swap_your_assets_title">Your assets</string> <string name="swap_your_assets_title">Your assets</string>
<string name="swapping_alert_cex_description">The amount includes:\n• service provider\'s fee\n• network fee for sending %s from the exchange back to the user\'s address.</string> <string name="swapping_alert_cex_description">The amount includes:\n• service provider\'s fee\n• network fee for sending %s from the exchange back to the user\'s address.</string>
@ -1557,17 +1583,6 @@
<string name="tangem_pay_explore_transaction">Explore transaction</string> <string name="tangem_pay_explore_transaction">Explore transaction</string>
<string name="tangem_pay_fee_subtitle">Service fees</string> <string name="tangem_pay_fee_subtitle">Service fees</string>
<string name="tangem_pay_fee_title">Fee</string> <string name="tangem_pay_fee_title">Fee</string>
<string name="tangempay_card_details_reissue_card">Replace card</string>
<string name="tangempay_reissue_card_title">Replace your card?</string>
<string name="tangempay_reissue_card_description">This generates a new set of card details. Your old details will stop working. You can\'t undo this.</string>
<string name="tangempay_reissue_card_fee_label">Replacement fee</string>
<string name="tangempay_reissue_card_confirm">Replace card</string>
<string name="tangempay_reissue_card_in_progress">Replacing your digital card</string>
<string name="tangempay_reissue_card_in_progress_description">Usually takes up to 5 minutes. In rare cases, up to 48 hours.</string>
<string name="tangempay_reissue_card_insufficient_funds">Insufficient funds to replace the card</string>
<string name="tangempay_reissue_card_insufficient_funds_title">Unable to cover fee</string>
<string name="tangempay_reissue_card_insufficient_funds_subtitle">Deposit USDC to payment account to cover the issuing fee</string>
<string name="tangempay_reissue_card_fee_unreachable_error_title">Replacement fee info unreachable</string>
<string name="tangem_pay_freeze_card_alert_body">Keep your money safe. You can unfreeze anytime.</string> <string name="tangem_pay_freeze_card_alert_body">Keep your money safe. You can unfreeze anytime.</string>
<string name="tangem_pay_freeze_card_alert_title">Freeze your card?</string> <string name="tangem_pay_freeze_card_alert_title">Freeze your card?</string>
<string name="tangem_pay_freeze_card_failed">Failed to freeze the card. Try again later.</string> <string name="tangem_pay_freeze_card_failed">Failed to freeze the card. Try again later.</string>
@ -1629,6 +1644,7 @@
<string name="tangempay_card_details_receive_description">Share your address or show QR-code</string> <string name="tangempay_card_details_receive_description">Share your address or show QR-code</string>
<string name="tangempay_card_details_receive_error_description">Technical issues detected. Please try again later or contact support.</string> <string name="tangempay_card_details_receive_error_description">Technical issues detected. Please try again later or contact support.</string>
<string name="tangempay_card_details_receive_error_title">Receive unavailable now</string> <string name="tangempay_card_details_receive_error_title">Receive unavailable now</string>
<string name="tangempay_card_details_reissue_card">Replace card</string>
<string name="tangempay_card_details_rename_card_invalid_description">Only letters and numbers are allowed.</string> <string name="tangempay_card_details_rename_card_invalid_description">Only letters and numbers are allowed.</string>
<string name="tangempay_card_details_rename_card_invalid_title">Invalid characters</string> <string name="tangempay_card_details_rename_card_invalid_title">Invalid characters</string>
<string name="tangempay_card_details_reveal_text">Reveal</string> <string name="tangempay_card_details_reveal_text">Reveal</string>
@ -1643,14 +1659,18 @@
<string name="tangempay_card_details_withdraw_error_title">Withdraw unavailable now</string> <string name="tangempay_card_details_withdraw_error_title">Withdraw unavailable now</string>
<string name="tangempay_card_details_withdraw_in_progress_description">You can\'t initiate swap or new withdrawal till the current one is finished</string> <string name="tangempay_card_details_withdraw_in_progress_description">You can\'t initiate swap or new withdrawal till the current one is finished</string>
<string name="tangempay_card_details_withdraw_in_progress_title">Withdrawal in progress</string> <string name="tangempay_card_details_withdraw_in_progress_title">Withdrawal in progress</string>
<string name="tangempay_card_page_settings_title">Card settings</string>
<string name="tangempay_card_page_daily_limit_title">Daily limit</string>
<string name="tangempay_card_page_daily_limit_current_limit">Current limit</string>
<string name="tangempay_card_page_daily_limit_change">Change</string> <string name="tangempay_card_page_daily_limit_change">Change</string>
<string name="tangempay_card_page_daily_limit_error_title">Daily limit unavailable</string> <string name="tangempay_card_page_daily_limit_current_limit">Current limit</string>
<string name="tangempay_card_page_daily_limit_error_description">We couldn\'t load your daily limit. Please try again.</string> <string name="tangempay_card_page_daily_limit_error_description">We couldn\'t load your daily limit. Please try again.</string>
<string name="tangempay_card_page_daily_limit_error_title">Daily limit unavailable</string>
<string name="tangempay_card_page_daily_limit_success_description">You can change it again anytime you like</string>
<string name="tangempay_card_page_daily_limit_success_title">Daily limit is set</string>
<string name="tangempay_card_page_daily_limit_title">Daily limit</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_daily_limit_hint" formatted="false">Set a limit from %s to %s</string>
<string name="tangempay_daily_limit_set_button">Set limits</string>
<string name="tangempay_digital_card">Digital card</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>
@ -1693,12 +1713,22 @@
<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>
<string name="tangempay_reissue_card_confirm">Replace card</string>
<string name="tangempay_reissue_card_description">This generates a new set of card details. Your old details will stop working. You can\'t undo this.</string>
<string name="tangempay_reissue_card_fee_label">Replacement fee</string>
<string name="tangempay_reissue_card_fee_unreachable_error_title">Replacement fee info unreachable</string>
<string name="tangempay_reissue_card_in_progress">Replacing your digital card</string>
<string name="tangempay_reissue_card_in_progress_description">Usually takes up to 5 minutes. In rare cases, up to 48 hours.</string>
<string name="tangempay_reissue_card_insufficient_funds">Insufficient funds to replace the card</string>
<string name="tangempay_reissue_card_insufficient_funds_subtitle">Deposit USDC to payment account to cover the issuing fee</string>
<string name="tangempay_reissue_card_insufficient_funds_title">Unable to cover fee</string>
<string name="tangempay_reissue_card_title">Replace your card?</string>
<string name="tangempay_service_unavailable_description">Were fixing a technical issue. Please try again later.</string> <string name="tangempay_service_unavailable_description">Were fixing a technical issue. Please try again later.</string>
<string name="tangempay_service_unavailable_title">Service temporarily unavailable</string> <string name="tangempay_service_unavailable_title">Service temporarily unavailable</string>
<string name="tangempay_service_unreachable_try_later">Unable to display details. However, card payments are still working.</string> <string name="tangempay_service_unreachable_try_later">Unable to display details. However, card payments are still working.</string>
<string name="tangempay_set_pin_code">Set \nPIN code</string> <string name="tangempay_set_pin_code">Set \nPIN code</string>
<string name="tangempay_sync_needed">Session expired</string> <string name="tangempay_sync_needed">Session expired</string>
<string name="tangempay_sync_needed_restore_access">Restore access</string> <string name="tangempay_sync_needed_restore_access">Renew session</string>
<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>
@ -1786,7 +1816,7 @@
<string name="unlock_wallet_description_full">Use %s or scan a card/ring to have access to your wallet</string> <string name="unlock_wallet_description_full">Use %s or scan a card/ring to have access to your wallet</string>
<string name="unsupported_wc_version">Connection failed: This dApp uses Wallet Connect version 1.0, which is not supported. Please ensure the dApp supports Wallet Connect version 2.0 to connect successfully.</string> <string name="unsupported_wc_version">Connection failed: This dApp uses Wallet Connect version 1.0, which is not supported. Please ensure the dApp supports Wallet Connect version 2.0 to connect successfully.</string>
<string name="update_approval_permission_fee_note">Previous permission will be revoked and a new one will be issued. Network will charge token approval fee for each of theses actions. You will see a zero-amount transaction in the history as a revoke evidence.</string> <string name="update_approval_permission_fee_note">Previous permission will be revoked and a new one will be issued. Network will charge token approval fee for each of theses actions. You will see a zero-amount transaction in the history as a revoke evidence.</string>
<string name="update_approval_permission_subtitle">Transaction exceeds previously granted permission amount.\nUpdate permission to proceed</string> <string name="update_approval_permission_subtitle">Transaction exceeds previously granted permission amount.\nUpdate permission to proceed</string>
<string name="update_approval_permission_title">Update permission</string> <string name="update_approval_permission_title">Update permission</string>
<string name="upgrade_to_hardware_wallet_button_title">Upgrade to hardware wallet</string> <string name="upgrade_to_hardware_wallet_button_title">Upgrade to hardware wallet</string>
<string name="user_push_notification_agreement_argument_one">Stay up to date with the latest features and news</string> <string name="user_push_notification_agreement_argument_one">Stay up to date with the latest features and news</string>
@ -2157,8 +2187,6 @@
<string name="wc_specify_networks_subtitle">At least one network is required for dApp connection</string> <string name="wc_specify_networks_subtitle">At least one network is required for dApp connection</string>
<string name="wc_specify_networks_title">Specify selected networks</string> <string name="wc_specify_networks_title">Specify selected networks</string>
<string name="wc_successfully_signed">Successfully signed</string> <string name="wc_successfully_signed">Successfully signed</string>
<string name="wc_get_addresses_title">Share Addresses</string>
<string name="wc_get_addresses_addresses_title">Addresses to share</string>
<string name="wc_transaction_flow_title" translatable="false">WalletConnect</string> <string name="wc_transaction_flow_title" translatable="false">WalletConnect</string>
<string name="wc_transaction_info_to_title">To</string> <string name="wc_transaction_info_to_title">To</string>
<string name="wc_transaction_request">Transaction request</string> <string name="wc_transaction_request">Transaction request</string>
@ -2268,7 +2296,7 @@
<string name="yield_module_start_earning_sheet_next_deposits_v2">All your future incoming %1$s deposits will be automatically supplied to Aave.</string> <string name="yield_module_start_earning_sheet_next_deposits_v2">All your future incoming %1$s deposits will be automatically supplied to Aave.</string>
<string name="yield_module_status_active">Active</string> <string name="yield_module_status_active">Active</string>
<string name="yield_module_status_paused">Paused</string> <string name="yield_module_status_paused">Paused</string>
<string name="yield_module_stop_earning">Disabling Yield Mode</string> <string name="yield_module_stop_earning">Disable Yield Mode</string>
<string name="yield_module_stop_earning_sheet_description">Turning this off will withdraw your assets from Aave, convert them back to %s in your wallet, and stop yield accrual.</string> <string name="yield_module_stop_earning_sheet_description">Turning this off will withdraw your assets from Aave, convert them back to %s in your wallet, and stop yield accrual.</string>
<string name="yield_module_stop_earning_sheet_fee_note">A network fee is charged by the blockchain when you exit Yield Mode.</string> <string name="yield_module_stop_earning_sheet_fee_note">A network fee is charged by the blockchain when you exit Yield Mode.</string>
<string name="yield_module_stop_earning_sheet_title">Disable Yield Mode</string> <string name="yield_module_stop_earning_sheet_title">Disable Yield Mode</string>

View file

@ -1,6 +1,5 @@
package com.tangem.features.onboarding.v2.addresssync package com.tangem.features.onboarding.v2.addresssync
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import com.arkivanov.decompose.DelicateDecomposeApi import com.arkivanov.decompose.DelicateDecomposeApi
@ -18,6 +17,7 @@ import com.tangem.features.onboarding.v2.addresssync.model.AddressSyncIntent
import com.tangem.features.onboarding.v2.addresssync.model.AddressSyncModel import com.tangem.features.onboarding.v2.addresssync.model.AddressSyncModel
import com.tangem.features.onboarding.v2.addresssync.navigation.AddressSyncStep import com.tangem.features.onboarding.v2.addresssync.navigation.AddressSyncStep
import com.tangem.features.onboarding.v2.addresssync.ui.AddressSyncContent import com.tangem.features.onboarding.v2.addresssync.ui.AddressSyncContent
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
import com.tangem.features.pushnotifications.api.PushNotificationsComponent import com.tangem.features.pushnotifications.api.PushNotificationsComponent
import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks
import com.tangem.features.pushnotifications.api.PushNotificationsParams import com.tangem.features.pushnotifications.api.PushNotificationsParams
@ -25,11 +25,12 @@ import com.tangem.features.pushnotifications.api.PushNotificationsParams
@OptIn(DelicateDecomposeApi::class) @OptIn(DelicateDecomposeApi::class)
internal class DefaultAddressSyncComponent( internal class DefaultAddressSyncComponent(
appComponentContext: AppComponentContext, appComponentContext: AppComponentContext,
params: MultiWalletChildParams,
private val askBiometryComponentFactory: AskBiometryComponent.Factory, private val askBiometryComponentFactory: AskBiometryComponent.Factory,
private val pushNotificationsComponentFactory: PushNotificationsComponent.Factory, private val pushNotificationsComponentFactory: PushNotificationsComponent.Factory,
) : AppComponentContext by appComponentContext, AddressSyncComponent { ) : AppComponentContext by appComponentContext, AddressSyncComponent {
private val model: AddressSyncModel = getOrCreateModel() private val model: AddressSyncModel = getOrCreateModel(params)
private val childStack: Value<ChildStack<AddressSyncStep, ComposableContentComponent>> = private val childStack: Value<ChildStack<AddressSyncStep, ComposableContentComponent>> =
childStack( childStack(
@ -58,10 +59,6 @@ internal class DefaultAddressSyncComponent(
} }
}, },
) )
BackHandler {
model.onIntent(AddressSyncIntent.Back)
}
} }
private fun createChild(step: AddressSyncStep, childContext: AppComponentContext): ComposableContentComponent { private fun createChild(step: AddressSyncStep, childContext: AppComponentContext): ComposableContentComponent {
@ -82,7 +79,6 @@ internal class DefaultAddressSyncComponent(
model.onIntent( model.onIntent(
AddressSyncIntent.Next( AddressSyncIntent.Next(
step = AddressSyncStep.ASK_NOTIFICATIONS, step = AddressSyncStep.ASK_NOTIFICATIONS,
shouldReplace = true,
), ),
) )
} }
@ -91,7 +87,6 @@ internal class DefaultAddressSyncComponent(
model.onIntent( model.onIntent(
AddressSyncIntent.Next( AddressSyncIntent.Next(
step = AddressSyncStep.ASK_NOTIFICATIONS, step = AddressSyncStep.ASK_NOTIFICATIONS,
shouldReplace = false,
), ),
) )
} }
@ -109,7 +104,6 @@ internal class DefaultAddressSyncComponent(
model.onIntent( model.onIntent(
AddressSyncIntent.Next( AddressSyncIntent.Next(
step = AddressSyncStep.ADDRESS_SYNC, step = AddressSyncStep.ADDRESS_SYNC,
shouldReplace = true,
), ),
) )
} }
@ -118,7 +112,6 @@ internal class DefaultAddressSyncComponent(
model.onIntent( model.onIntent(
AddressSyncIntent.Next( AddressSyncIntent.Next(
step = AddressSyncStep.ADDRESS_SYNC, step = AddressSyncStep.ADDRESS_SYNC,
shouldReplace = false,
), ),
) )
} }
@ -127,7 +120,6 @@ internal class DefaultAddressSyncComponent(
model.onIntent( model.onIntent(
AddressSyncIntent.Next( AddressSyncIntent.Next(
step = AddressSyncStep.ADDRESS_SYNC, step = AddressSyncStep.ADDRESS_SYNC,
shouldReplace = false,
), ),
) )
} }

View file

@ -3,6 +3,6 @@ package com.tangem.features.onboarding.v2.addresssync.model
import com.tangem.features.onboarding.v2.addresssync.navigation.AddressSyncStep import com.tangem.features.onboarding.v2.addresssync.navigation.AddressSyncStep
sealed interface AddressSyncIntent { sealed interface AddressSyncIntent {
data class Next(val step: AddressSyncStep, val shouldReplace: Boolean) : AddressSyncIntent data class Next(val step: AddressSyncStep) : AddressSyncIntent
data object Back : AddressSyncIntent data object Back : AddressSyncIntent
} }

View file

@ -3,16 +3,19 @@ package com.tangem.features.onboarding.v2.addresssync.model
import com.arkivanov.decompose.DelicateDecomposeApi import com.arkivanov.decompose.DelicateDecomposeApi
import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.pop import com.arkivanov.decompose.router.stack.pop
import com.arkivanov.decompose.router.stack.push
import com.arkivanov.decompose.router.stack.replaceCurrent import com.arkivanov.decompose.router.stack.replaceCurrent
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.settings.CanUseBiometryUseCase import com.tangem.domain.settings.CanUseBiometryUseCase
import com.tangem.domain.settings.ShouldAskPermissionUseCase import com.tangem.domain.settings.ShouldAskPermissionUseCase
import com.tangem.domain.settings.ShouldShowAskBiometryUseCase import com.tangem.domain.settings.ShouldShowAskBiometryUseCase
import com.tangem.features.onboarding.v2.addresssync.navigation.AddressSyncStep import com.tangem.features.onboarding.v2.addresssync.navigation.AddressSyncStep
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@ -23,10 +26,24 @@ internal class AddressSyncModel @Inject constructor(
private val shouldShowAskBiometryUseCase: ShouldShowAskBiometryUseCase, private val shouldShowAskBiometryUseCase: ShouldShowAskBiometryUseCase,
private val canUseBiometryUseCase: CanUseBiometryUseCase, private val canUseBiometryUseCase: CanUseBiometryUseCase,
private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase, private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase,
paramsContainer: ParamsContainer,
) : Model() { ) : Model() {
private val params = paramsContainer.require<MultiWalletChildParams>()
val stackNavigation = StackNavigation<AddressSyncStep>() val stackNavigation = StackNavigation<AddressSyncStep>()
init {
params.innerNavigation.update { innerNavigationState ->
innerNavigationState.copy(
stackSize = AddressSyncStep.ASK_BIOMETRY.pageNumber,
stackMaxSize = ADDRESS_SYNC_MAX_STEPS,
)
}
modelScope.launch {
trySkippingScreen(AddressSyncStep.ASK_BIOMETRY)
}
}
fun onIntent(intent: AddressSyncIntent) { fun onIntent(intent: AddressSyncIntent) {
when (intent) { when (intent) {
is AddressSyncIntent.Next -> nextScreen(intent) is AddressSyncIntent.Next -> nextScreen(intent)
@ -35,27 +52,24 @@ internal class AddressSyncModel @Inject constructor(
} }
private fun nextScreen(next: AddressSyncIntent.Next) { private fun nextScreen(next: AddressSyncIntent.Next) {
val (nextStep, replace) = next stackNavigation.replaceCurrent(configuration = next.step)
if (replace) { updateStepperPage(next)
stackNavigation.replaceCurrent(configuration = nextStep) updateTitle(next)
} else { modelScope.launch { trySkippingScreen(next.step) }
stackNavigation.push(configuration = nextStep)
}
modelScope.launch { trySkippingScreen(next) }
} }
private suspend fun trySkippingScreen(next: AddressSyncIntent.Next) { private suspend fun trySkippingScreen(step: AddressSyncStep) {
when (next.step) { when (step) {
AddressSyncStep.ASK_BIOMETRY -> { AddressSyncStep.ASK_BIOMETRY -> {
val shouldShowAskBiometry = canUseBiometryUseCase.strict() && shouldShowAskBiometryUseCase() val shouldShowAskBiometry = canUseBiometryUseCase.strict() && shouldShowAskBiometryUseCase()
if (shouldShowAskBiometry.not()) { if (shouldShowAskBiometry.not()) {
nextScreen(AddressSyncIntent.Next(step = AddressSyncStep.ASK_NOTIFICATIONS, shouldReplace = true)) nextScreen(AddressSyncIntent.Next(step = AddressSyncStep.ASK_NOTIFICATIONS))
} }
} }
AddressSyncStep.ASK_NOTIFICATIONS -> { AddressSyncStep.ASK_NOTIFICATIONS -> {
val shouldShowAskNotification = shouldAskPermissionUseCase(PUSH_PERMISSION) val shouldShowAskNotification = shouldAskPermissionUseCase(PUSH_PERMISSION)
if (shouldShowAskNotification.not()) { if (shouldShowAskNotification.not()) {
nextScreen(AddressSyncIntent.Next(step = AddressSyncStep.ADDRESS_SYNC, shouldReplace = true)) nextScreen(AddressSyncIntent.Next(step = AddressSyncStep.ADDRESS_SYNC))
} }
} }
AddressSyncStep.ADDRESS_SYNC -> Unit AddressSyncStep.ADDRESS_SYNC -> Unit
@ -65,4 +79,22 @@ internal class AddressSyncModel @Inject constructor(
private fun goBack() { private fun goBack() {
stackNavigation.pop() stackNavigation.pop()
} }
private fun updateStepperPage(next: AddressSyncIntent.Next) {
params.innerNavigation.update { innerNavigationState ->
innerNavigationState.copy(
stackSize = next.step.pageNumber,
)
}
}
private fun updateTitle(next: AddressSyncIntent.Next) {
params.parentParams.titleProvider.changeTitle(
text = resourceReference(next.step.stringId),
)
}
private companion object {
const val ADDRESS_SYNC_MAX_STEPS = 3
}
} }

View file

@ -1,7 +1,10 @@
package com.tangem.features.onboarding.v2.addresssync.navigation package com.tangem.features.onboarding.v2.addresssync.navigation
enum class AddressSyncStep { import androidx.annotation.StringRes
ASK_BIOMETRY, import com.tangem.features.onboarding.v2.impl.R
ASK_NOTIFICATIONS,
ADDRESS_SYNC, enum class AddressSyncStep(val pageNumber: Int, @StringRes val stringId: Int) {
ASK_BIOMETRY(pageNumber = 1, stringId = R.string.onboarding_navbar_title_biometrics),
ASK_NOTIFICATIONS(pageNumber = 2, stringId = R.string.onboarding_title_notifications),
ADDRESS_SYNC(pageNumber = 3, stringId = R.string.onboarding_navbar_title_last_step),
} }

View file

@ -194,6 +194,7 @@ internal class DefaultOnboardingMultiWalletComponent @AssistedInject constructor
) )
AddressSync -> DefaultAddressSyncComponent( AddressSync -> DefaultAddressSyncComponent(
appComponentContext = childContext, appComponentContext = childContext,
params = childParams,
askBiometryComponentFactory = askBiometryComponentFactory, askBiometryComponentFactory = askBiometryComponentFactory,
pushNotificationsComponentFactory = pushNotificationsComponentFactory, pushNotificationsComponentFactory = pushNotificationsComponentFactory,
) )

View file

@ -1,5 +1,6 @@
package com.tangem.features.onboarding.v2.multiwallet.impl.model package com.tangem.features.onboarding.v2.multiwallet.impl.model
import com.tangem.common.routing.AppRoute
import com.tangem.common.ui.userwallet.converter.ArtworkUMConverter import com.tangem.common.ui.userwallet.converter.ArtworkUMConverter
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
@ -70,7 +71,11 @@ internal class OnboardingMultiWalletModel @Inject constructor(
onConfirm = { onConfirm = {
modelScope.launch { modelScope.launch {
onboardingRepository.clearUnfinishedFinalizeOnboarding() onboardingRepository.clearUnfinishedFinalizeOnboarding()
router.pop() if (params.mode == OnboardingMultiWalletComponent.Mode.AddressSync) {
router.replaceAll(AppRoute.Wallet)
} else {
router.pop()
}
} }
}, },
), ),

View file

@ -15,5 +15,5 @@ fun screenTitleByStep(step: OnboardingMultiWalletState.Step): TextReference = wh
OnboardingMultiWalletState.Step.Finalize -> resourceReference(R.string.onboarding_button_finalize_backup) OnboardingMultiWalletState.Step.Finalize -> resourceReference(R.string.onboarding_button_finalize_backup)
OnboardingMultiWalletState.Step.Done -> resourceReference(R.string.common_done) OnboardingMultiWalletState.Step.Done -> resourceReference(R.string.common_done)
OnboardingMultiWalletState.Step.UpgradeWallet -> resourceReference(R.string.common_tangem) OnboardingMultiWalletState.Step.UpgradeWallet -> resourceReference(R.string.common_tangem)
OnboardingMultiWalletState.Step.AddressSync -> TODO("Will be implemented during [REDACTED_TASK_KEY]") OnboardingMultiWalletState.Step.AddressSync -> resourceReference(R.string.onboarding_navbar_title_biometrics)
} }

View file

@ -1,15 +1,23 @@
package com.tangem.features.onboarding.v2.addresssync.model package com.tangem.features.onboarding.v2.addresssync.model
import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.StackNavigation
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.settings.CanUseBiometryUseCase import com.tangem.domain.settings.CanUseBiometryUseCase
import com.tangem.domain.settings.ShouldAskPermissionUseCase import com.tangem.domain.settings.ShouldAskPermissionUseCase
import com.tangem.domain.settings.ShouldShowAskBiometryUseCase import com.tangem.domain.settings.ShouldShowAskBiometryUseCase
import com.tangem.features.onboarding.v2.TitleProvider
import com.tangem.features.onboarding.v2.addresssync.navigation.AddressSyncStep import com.tangem.features.onboarding.v2.addresssync.navigation.AddressSyncStep
import com.tangem.features.onboarding.v2.multiwallet.impl.MultiWalletInnerNavigationState
import com.tangem.features.onboarding.v2.multiwallet.impl.child.MultiWalletChildParams
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.coEvery import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.advanceUntilIdle
@ -23,12 +31,36 @@ internal class AddressSyncModelTest {
private val shouldShowAskBiometryUseCase: ShouldShowAskBiometryUseCase = mockk() private val shouldShowAskBiometryUseCase: ShouldShowAskBiometryUseCase = mockk()
private val canUseBiometryUseCase: CanUseBiometryUseCase = mockk() private val canUseBiometryUseCase: CanUseBiometryUseCase = mockk()
private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase = mockk() private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase = mockk()
private val paramsContainer: ParamsContainer = mockk()
private val testInnerNavigation = MutableStateFlow(
value = MultiWalletInnerNavigationState(
stackSize = 0,
stackMaxSize = 0,
)
)
private val titleProvider: TitleProvider = mockk(relaxUnitFun = true)
private val params: MultiWalletChildParams = mockk {
every { innerNavigation } returns testInnerNavigation
every { parentParams } returns mockk {
every { titleProvider } returns this@AddressSyncModelTest.titleProvider
}
}
@BeforeEach @BeforeEach
fun setUp() { fun setUp() {
coEvery { canUseBiometryUseCase.strict() } returns false coEvery { canUseBiometryUseCase.strict() } returns false
coEvery { shouldShowAskBiometryUseCase() } returns false coEvery { shouldShowAskBiometryUseCase() } returns false
coEvery { shouldAskPermissionUseCase(PUSH_PERMISSION) } returns false coEvery { shouldAskPermissionUseCase(PUSH_PERMISSION) } returns false
every { paramsContainer.require<MultiWalletChildParams>() } returns params
}
@Test
fun `WHEN model is created THEN inner navigation stack size and max size are set`() = runTest {
createModel(this)
val state = testInnerNavigation.value
assert(state.stackSize == AddressSyncStep.ASK_BIOMETRY.pageNumber)
assert(state.stackMaxSize == AddressSyncStep.entries.size)
} }
@Test @Test
@ -39,10 +71,11 @@ internal class AddressSyncModelTest {
val model = createModel(this) val model = createModel(this)
val stack = model.stackNavigation.trackStack() val stack = model.stackNavigation.trackStack()
model.onIntent(AddressSyncIntent.Next(step = AddressSyncStep.ASK_BIOMETRY, shouldReplace = false)) model.onIntent(AddressSyncIntent.Next(step = AddressSyncStep.ASK_BIOMETRY))
advanceUntilIdle() advanceUntilIdle()
assert(stack == listOf(AddressSyncStep.ASK_BIOMETRY)) assert(stack == listOf(AddressSyncStep.ASK_BIOMETRY))
assertStepperAndTitleFor(AddressSyncStep.ASK_BIOMETRY)
} }
@Test @Test
@ -55,10 +88,11 @@ internal class AddressSyncModelTest {
val model = createModel(this) val model = createModel(this)
val stack = model.stackNavigation.trackStack() val stack = model.stackNavigation.trackStack()
model.onIntent(AddressSyncIntent.Next(step = AddressSyncStep.ASK_BIOMETRY, shouldReplace = false)) model.onIntent(AddressSyncIntent.Next(step = AddressSyncStep.ASK_BIOMETRY))
advanceUntilIdle() advanceUntilIdle()
assert(stack == listOf(AddressSyncStep.ASK_NOTIFICATIONS)) assert(stack == listOf(AddressSyncStep.ASK_NOTIFICATIONS))
assertStepperAndTitleFor(AddressSyncStep.ASK_NOTIFICATIONS)
} }
@Test @Test
@ -70,10 +104,11 @@ internal class AddressSyncModelTest {
val model = createModel(this) val model = createModel(this)
val stack = model.stackNavigation.trackStack() val stack = model.stackNavigation.trackStack()
model.onIntent(AddressSyncIntent.Next(step = AddressSyncStep.ASK_BIOMETRY, shouldReplace = false)) model.onIntent(AddressSyncIntent.Next(step = AddressSyncStep.ASK_BIOMETRY))
advanceUntilIdle() advanceUntilIdle()
assert(stack == listOf(AddressSyncStep.ADDRESS_SYNC)) assert(stack == listOf(AddressSyncStep.ADDRESS_SYNC))
assertStepperAndTitleFor(AddressSyncStep.ADDRESS_SYNC)
} }
@Test @Test
@ -83,10 +118,11 @@ internal class AddressSyncModelTest {
val model = createModel(this) val model = createModel(this)
val stack = model.stackNavigation.trackStack() val stack = model.stackNavigation.trackStack()
model.onIntent(AddressSyncIntent.Next(step = AddressSyncStep.ASK_NOTIFICATIONS, shouldReplace = false)) model.onIntent(AddressSyncIntent.Next(step = AddressSyncStep.ASK_NOTIFICATIONS))
advanceUntilIdle() advanceUntilIdle()
assert(stack == listOf(AddressSyncStep.ASK_NOTIFICATIONS)) assert(stack == listOf(AddressSyncStep.ASK_NOTIFICATIONS))
assertStepperAndTitleFor(AddressSyncStep.ASK_NOTIFICATIONS)
} }
@Test @Test
@ -96,10 +132,16 @@ internal class AddressSyncModelTest {
val model = createModel(this) val model = createModel(this)
val stack = model.stackNavigation.trackStack() val stack = model.stackNavigation.trackStack()
model.onIntent(AddressSyncIntent.Next(step = AddressSyncStep.ASK_NOTIFICATIONS, shouldReplace = false)) model.onIntent(AddressSyncIntent.Next(step = AddressSyncStep.ASK_NOTIFICATIONS))
advanceUntilIdle() advanceUntilIdle()
assert(stack == listOf(AddressSyncStep.ADDRESS_SYNC)) assert(stack == listOf(AddressSyncStep.ADDRESS_SYNC))
assertStepperAndTitleFor(AddressSyncStep.ADDRESS_SYNC)
}
private fun assertStepperAndTitleFor(step: AddressSyncStep) {
assert(testInnerNavigation.value.stackSize == step.pageNumber)
verify { titleProvider.changeTitle(resourceReference(step.stringId)) }
} }
private fun StackNavigation<AddressSyncStep>.trackStack(): List<AddressSyncStep> { private fun StackNavigation<AddressSyncStep>.trackStack(): List<AddressSyncStep> {
@ -118,6 +160,7 @@ internal class AddressSyncModelTest {
shouldShowAskBiometryUseCase = shouldShowAskBiometryUseCase, shouldShowAskBiometryUseCase = shouldShowAskBiometryUseCase,
canUseBiometryUseCase = canUseBiometryUseCase, canUseBiometryUseCase = canUseBiometryUseCase,
shouldAskPermissionUseCase = shouldAskPermissionUseCase, shouldAskPermissionUseCase = shouldAskPermissionUseCase,
paramsContainer = paramsContainer,
) )
} }