Updated on 2026-08-14
This commit is contained in:
parent
5f1a711c8c
commit
bd13529d69
17 changed files with 550 additions and 77 deletions
|
|
@ -93,7 +93,7 @@
|
||||||
<string name="addfunds_buy_row_description">Credit card or bank account</string>
|
<string name="addfunds_buy_row_description">Credit card or bank account</string>
|
||||||
<string name="addfunds_fund_token">Fund token</string>
|
<string name="addfunds_fund_token">Fund token</string>
|
||||||
<string name="addfunds_receive_row_description">Share your address or QR-code</string>
|
<string name="addfunds_receive_row_description">Share your address or QR-code</string>
|
||||||
<string name="addfunds_swap_row_description">Exchange one crypto for another</string>
|
<string name="addfunds_swap_row_description">Between your portfolios</string>
|
||||||
<string name="addfunds_you_receive_title">You receive</string>
|
<string name="addfunds_you_receive_title">You receive</string>
|
||||||
<string name="address_book_add_address">Add address</string>
|
<string name="address_book_add_address">Add address</string>
|
||||||
<string name="address_book_add_address_description">Add address and select network</string>
|
<string name="address_book_add_address_description">Add address and select network</string>
|
||||||
|
|
@ -105,7 +105,6 @@
|
||||||
<item quantity="other">%d addresses</item>
|
<item quantity="other">%d addresses</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="address_book_choose_address">Choose address</string>
|
<string name="address_book_choose_address">Choose address</string>
|
||||||
<string name="address_book_clear_all_networks">Clear All</string>
|
|
||||||
<string name="address_book_contact">Contact</string>
|
<string name="address_book_contact">Contact</string>
|
||||||
<string name="address_book_contact_name">Contact name</string>
|
<string name="address_book_contact_name">Contact name</string>
|
||||||
<string name="address_book_copy_address">Copy address</string>
|
<string name="address_book_copy_address">Copy address</string>
|
||||||
|
|
@ -131,12 +130,10 @@
|
||||||
<string name="address_book_no_contacts">No contacts yet</string>
|
<string name="address_book_no_contacts">No contacts yet</string>
|
||||||
<string name="address_book_no_contacts_description">Contacts added will appear here</string>
|
<string name="address_book_no_contacts_description">Contacts added will appear here</string>
|
||||||
<string name="address_book_remove_address">Remove address</string>
|
<string name="address_book_remove_address">Remove address</string>
|
||||||
<string name="address_book_save_address">Save address</string>
|
|
||||||
<string name="address_book_save_contact">Save contact</string>
|
<string name="address_book_save_contact">Save contact</string>
|
||||||
<string name="address_book_save_to_wallet_title">Save to Wallet</string>
|
<string name="address_book_save_to_wallet_title">Save to Wallet</string>
|
||||||
<string name="address_book_save_wallet_to_description">This contact will be linked to this wallet’s address book.</string>
|
<string name="address_book_save_wallet_to_description">This contact will be linked to this wallet’s address book.</string>
|
||||||
<string name="address_book_search_no_results">No results found.\nTry another name</string>
|
<string name="address_book_search_no_results">No results found.\nTry another name</string>
|
||||||
<string name="address_book_select_all_networks">Select All</string>
|
|
||||||
<string name="address_book_select_network">Select network</string>
|
<string name="address_book_select_network">Select network</string>
|
||||||
<string name="address_book_title">Address book</string>
|
<string name="address_book_title">Address book</string>
|
||||||
<string name="address_book_unsaved_changes">Unsaved changes</string>
|
<string name="address_book_unsaved_changes">Unsaved changes</string>
|
||||||
|
|
@ -639,8 +636,6 @@
|
||||||
<string name="express_exchange_notification_long_transaction_time_title">Long transaction time</string>
|
<string name="express_exchange_notification_long_transaction_time_title">Long transaction time</string>
|
||||||
<string name="express_exchange_notification_refund_text">The transaction amount was refunded in %1$s to your wallet due to OKX or bridge rules. %2$s</string>
|
<string name="express_exchange_notification_refund_text">The transaction amount was refunded in %1$s to your wallet due to OKX or bridge rules. %2$s</string>
|
||||||
<string name="express_exchange_notification_refund_title">The amount was refunded in %1$s (%2$s network)</string>
|
<string name="express_exchange_notification_refund_title">The amount was refunded in %1$s (%2$s network)</string>
|
||||||
<string name="express_exchange_notification_refunded_in_text">Your funds have been refunded in %1$s to your wallet on the %2$s network, in accordance with OKX exchange rules.</string>
|
|
||||||
<string name="express_exchange_notification_refunded_in_title">Refunded in %s</string>
|
|
||||||
<string name="express_exchange_notification_verification_text">Visit provider’s website for verification</string>
|
<string name="express_exchange_notification_verification_text">Visit provider’s website for verification</string>
|
||||||
<string name="express_exchange_notification_verification_title">KYC verification required by provider</string>
|
<string name="express_exchange_notification_verification_title">KYC verification required by provider</string>
|
||||||
<string name="express_exchange_status_bought">Purchase completed</string>
|
<string name="express_exchange_status_bought">Purchase completed</string>
|
||||||
|
|
@ -726,11 +721,6 @@
|
||||||
<string name="feedback_subject_tx_failed">Can\'t send a transaction</string>
|
<string name="feedback_subject_tx_failed">Can\'t send a transaction</string>
|
||||||
<string name="feedback_token_description_error">Coin description error</string>
|
<string name="feedback_token_description_error">Coin description error</string>
|
||||||
<string name="for_you_description">Review portfolio and explore earn opportunities</string>
|
<string name="for_you_description">Review portfolio and explore earn opportunities</string>
|
||||||
<string name="for_you_earn_opportunities">Earn opportunities</string>
|
|
||||||
<string name="for_you_earn_opportunities_all_tokens_active">All your assets are at work, explore new opportunities</string>
|
|
||||||
<string name="for_you_earn_opportunities_no_available_tokens">Get up to %1$s annually</string>
|
|
||||||
<string name="for_you_earn_opportunities_tokens_rewards">Max potential rewards %1$s</string>
|
|
||||||
<string name="for_you_earn_per_year">%1$s/year</string>
|
|
||||||
<string name="for_you_portfolio_review_title">Portfolio review</string>
|
<string name="for_you_portfolio_review_title">Portfolio review</string>
|
||||||
<string name="for_you_title">For You</string>
|
<string name="for_you_title">For You</string>
|
||||||
<string name="force_update_action">Update now</string>
|
<string name="force_update_action">Update now</string>
|
||||||
|
|
@ -933,14 +923,12 @@
|
||||||
<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_portfolio_block_title">Your portfolio</string>
|
||||||
<string name="markets_portfolio_block_token_unsupported">**Token not supported**. This token is currently not supported in the wallet</string>
|
<string name="markets_portfolio_block_token_unsupported">**Token not supported**. This token is currently not supported in the wallet</string>
|
||||||
<string name="markets_portfolio_eligible_block_title">Other eligible tokens</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>
|
||||||
<string name="markets_search_header_title">Search tokens</string>
|
<string name="markets_search_header_title">Search tokens</string>
|
||||||
<string name="markets_search_hint_header">Recent searches</string>
|
<string name="markets_search_hint_header">Recent\'s</string>
|
||||||
<string name="markets_search_portfolio_header">In your portfolio</string>
|
<string name="markets_search_portfolio_header">In your portfolio</string>
|
||||||
<string name="markets_search_recent_tokens_header">Recent tokens</string>
|
|
||||||
<string name="markets_search_result_title">Result</string>
|
<string name="markets_search_result_title">Result</string>
|
||||||
<string name="markets_search_see_tokens_under_100k">See tokens under 100k USD market cap</string>
|
<string name="markets_search_see_tokens_under_100k">See tokens under 100k USD market cap</string>
|
||||||
<string name="markets_search_show_tokens">Show tokens</string>
|
<string name="markets_search_show_tokens">Show tokens</string>
|
||||||
|
|
@ -1311,21 +1299,6 @@
|
||||||
<string name="organize_tokens_sort_by_balance">By balance</string>
|
<string name="organize_tokens_sort_by_balance">By balance</string>
|
||||||
<string name="organize_tokens_title">Organize tokens</string>
|
<string name="organize_tokens_title">Organize tokens</string>
|
||||||
<string name="organize_tokens_ungroup">Ungroup</string>
|
<string name="organize_tokens_ungroup">Ungroup</string>
|
||||||
<string name="promo_campaign_already_activated_subtitle">Eligible cashback will be distributed to:</string>
|
|
||||||
<string name="promo_campaign_already_activated_title">You\'re already enrolled in %1$s</string>
|
|
||||||
<string name="promo_campaign_eligible_tokens">Eligible tokens</string>
|
|
||||||
<string name="promo_campaign_enroll">Enroll</string>
|
|
||||||
<string name="promo_campaign_enroll_success_title">You\'re successfully enrolled in %1$s</string>
|
|
||||||
<string name="promo_campaign_not_active_subtitle">This campaign no longer exists or has expired</string>
|
|
||||||
<string name="promo_campaign_not_active_title">Campaign not active</string>
|
|
||||||
<string name="promo_campaign_reactivation_summary_description">Earn 0.5% cashback on every swap over $500, on any pair except stable to stable. Max payout $50 per swap.\n\nComplete five qualifying swaps and unlock an extra $10 bonus.\n\nRewards are paid weekly in USDT or USDC on the address selected.</string>
|
|
||||||
<string name="promo_campaign_select_cashback_account">Select cashback account</string>
|
|
||||||
<string name="promo_campaign_select_token">Select token</string>
|
|
||||||
<string name="promo_campaign_summary_title">Enroll in %1$s</string>
|
|
||||||
<string name="promo_campaign_terms_agreement">I agree with %1$s</string>
|
|
||||||
<string name="promo_campaign_terms_agreement_android">I agree with</string>
|
|
||||||
<string name="promo_campaign_terms_link">%1$s Terms</string>
|
|
||||||
<string name="promo_campaign_whale_swap_summary_description">Earn cashback on every swap from $10K until the end of July.\n\nRates step up with size: 0.10% from $10K, 0.20% from $20K, 0.50% from $100K.\n\nMax payout: $500 per swap, and $10,000 per wallet per swap direction until campaign lasts. Stable coin into stablecoin swaps are excluded.\n\nPayout arrives weekly in USDT or USDC address of your choice.</string>
|
|
||||||
<string name="provider_name_support">%s support</string>
|
<string name="provider_name_support">%s support</string>
|
||||||
<string name="push_notification_settings_banner_description">Push Notifications are enabled but won\'t work until you allow them</string>
|
<string name="push_notification_settings_banner_description">Push Notifications are enabled but won\'t work until you allow them</string>
|
||||||
<string name="push_notification_settings_banner_title">Allow notifications</string>
|
<string name="push_notification_settings_banner_title">Allow notifications</string>
|
||||||
|
|
@ -1358,12 +1331,12 @@
|
||||||
<string name="qr_scanner_error_unsupported_network_title">No supported tokens found</string>
|
<string name="qr_scanner_error_unsupported_network_title">No supported tokens found</string>
|
||||||
<string name="qr_scanner_warning_unknown_parameters_message">This QR code contains parameters that are not recognized: %s. Some payment details may be lost if you continue.</string>
|
<string name="qr_scanner_warning_unknown_parameters_message">This QR code contains parameters that are not recognized: %s. Some payment details may be lost if you continue.</string>
|
||||||
<string name="qr_scanner_warning_unknown_parameters_title">Unknown Parameters</string>
|
<string name="qr_scanner_warning_unknown_parameters_title">Unknown Parameters</string>
|
||||||
<string name="quick_action_buy_description">Get crypto by card, bank transfer & more</string>
|
<string name="quick_action_buy_description">Credit card or bank account</string>
|
||||||
<string name="quick_action_receive_description">Share your address or QR-code</string>
|
<string name="quick_action_receive_description">Share your address or QR-code</string>
|
||||||
<string name="quick_action_sell_description">Convert crypto to fiat currency</string>
|
<string name="quick_action_sell_description">Sell crypto securely</string>
|
||||||
<string name="quick_action_send_and_swap_description">Exchange and send in one step</string>
|
<string name="quick_action_send_and_swap_description">Send with swap to another token</string>
|
||||||
<string name="quick_action_send_description">Transfer crypto to another wallet</string>
|
<string name="quick_action_send_description">Send to another wallet</string>
|
||||||
<string name="quick_action_swap_description">Exchange one crypto for another</string>
|
<string name="quick_action_swap_description">Between your portfolios</string>
|
||||||
<string name="quick_top_up_chip_other">Other</string>
|
<string name="quick_top_up_chip_other">Other</string>
|
||||||
<string name="quick_top_up_title">Quick top up</string>
|
<string name="quick_top_up_title">Quick top up</string>
|
||||||
<string name="receive_bottom_sheet_no_memo_required_message">No memo required</string>
|
<string name="receive_bottom_sheet_no_memo_required_message">No memo required</string>
|
||||||
|
|
@ -1549,7 +1522,7 @@
|
||||||
<string name="send_validation_invalid_total">Total amount exceeds balance</string>
|
<string name="send_validation_invalid_total">Total amount exceeds balance</string>
|
||||||
<string name="send_with_swap_change_token_alert_message">Are you sure you want to change the receiving token? This will reset your previously entered data.</string>
|
<string name="send_with_swap_change_token_alert_message">Are you sure you want to change the receiving token? This will reset your previously entered data.</string>
|
||||||
<string name="send_with_swap_change_token_alert_title">Changing token</string>
|
<string name="send_with_swap_change_token_alert_title">Changing token</string>
|
||||||
<string name="send_with_swap_confirm_title">Swap & Send</string>
|
<string name="send_with_swap_confirm_title">Swap and send</string>
|
||||||
<string name="send_with_swap_convert_token_alert_message">Proceed with swap? This will clear your previous data.</string>
|
<string name="send_with_swap_convert_token_alert_message">Proceed with swap? This will clear your previous data.</string>
|
||||||
<string name="send_with_swap_convert_token_alert_title">Confirm Conversion</string>
|
<string name="send_with_swap_convert_token_alert_title">Confirm Conversion</string>
|
||||||
<string name="send_with_swap_correct_recipient_network_notification_message">Sending any other currency will result in its irreversible loss.</string>
|
<string name="send_with_swap_correct_recipient_network_notification_message">Sending any other currency will result in its irreversible loss.</string>
|
||||||
|
|
@ -1657,9 +1630,9 @@
|
||||||
<string name="staking_notification_ton_account_initialization_message">To begin staking, you need to activate your TON account first.</string>
|
<string name="staking_notification_ton_account_initialization_message">To begin staking, you need to activate your TON account first.</string>
|
||||||
<string name="staking_notification_ton_account_initialization_title">Account activation</string>
|
<string name="staking_notification_ton_account_initialization_title">Account activation</string>
|
||||||
<string name="staking_notification_ton_activate_account">To start staking in TON, first send a small transaction to your own address — this will activate your wallet.</string>
|
<string name="staking_notification_ton_activate_account">To start staking in TON, first send a small transaction to your own address — this will activate your wallet.</string>
|
||||||
<string name="staking_notification_ton_extra_reserve_info">Up to 0.2 GRAM may be required in addition to the network fee to complete the transaction. Any unused amount will be refunded.</string>
|
<string name="staking_notification_ton_extra_reserve_info">Up to 0.2 TON may be required in addition to the network fee to complete the transaction. Any unused amount will be refunded.</string>
|
||||||
<string name="staking_notification_ton_extra_reserve_is_required">0.2 GRAM is required to proceed with this operation, in addition to the network fee. Please top up your balance.</string>
|
<string name="staking_notification_ton_extra_reserve_is_required">0.2 TON is required to proceed with this operation, in addition to the network fee. Please top up your balance.</string>
|
||||||
<string name="staking_notification_ton_extra_reserve_title">GRAM reserve required</string>
|
<string name="staking_notification_ton_extra_reserve_title">TON reserve required</string>
|
||||||
<string name="staking_notification_ton_have_to_unstake_all_text">This action will close other positions or switch them to withdrawal status, according to network rules.</string>
|
<string name="staking_notification_ton_have_to_unstake_all_text">This action will close other positions or switch them to withdrawal status, according to network rules.</string>
|
||||||
<string name="staking_notification_ton_have_to_unstake_all_title">Positions status</string>
|
<string name="staking_notification_ton_have_to_unstake_all_title">Positions status</string>
|
||||||
<string name="staking_notification_unlock_text">Unlock your money to withdraw it from staking process. Unlocking takes %s.</string>
|
<string name="staking_notification_unlock_text">Unlock your money to withdraw it from staking process. Unlocking takes %s.</string>
|
||||||
|
|
@ -1864,9 +1837,6 @@
|
||||||
<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>
|
||||||
<string name="tangempay_card_details_awaiting_deposit_cancel_button">Cancel %1$s, move to %2$s</string>
|
|
||||||
<string name="tangempay_card_details_awaiting_deposit_subtitle">To pay monthly fee for plan and start use card</string>
|
|
||||||
<string name="tangempay_card_details_awaiting_deposit_title">Top-up your account on %1$s</string>
|
|
||||||
<string name="tangempay_card_details_card_number">Card Number</string>
|
<string name="tangempay_card_details_card_number">Card Number</string>
|
||||||
<string name="tangempay_card_details_change_pin">PIN code</string>
|
<string name="tangempay_card_details_change_pin">PIN code</string>
|
||||||
<string name="tangempay_card_details_change_pin_success_description">The card is fully ready for payments.</string>
|
<string name="tangempay_card_details_change_pin_success_description">The card is fully ready for payments.</string>
|
||||||
|
|
@ -1901,8 +1871,6 @@
|
||||||
<string name="tangempay_card_details_rename_card_placeholder">Card name</string>
|
<string name="tangempay_card_details_rename_card_placeholder">Card name</string>
|
||||||
<string name="tangempay_card_details_reveal_text">Reveal</string>
|
<string name="tangempay_card_details_reveal_text">Reveal</string>
|
||||||
<string name="tangempay_card_details_show_details">Details</string>
|
<string name="tangempay_card_details_show_details">Details</string>
|
||||||
<string name="tangempay_card_details_system_downgrade_subtitle">If it will remain below zero your %1$s cards will be closed on %2$s</string>
|
|
||||||
<string name="tangempay_card_details_system_downgrade_title">Top up your account shortly</string>
|
|
||||||
<string name="tangempay_card_details_title">Details</string>
|
<string name="tangempay_card_details_title">Details</string>
|
||||||
<string name="tangempay_card_details_unable_to_rename_card_description">Please try again later</string>
|
<string name="tangempay_card_details_unable_to_rename_card_description">Please try again later</string>
|
||||||
<string name="tangempay_card_details_unfreeze_card">Unfreeze Card</string>
|
<string name="tangempay_card_details_unfreeze_card">Unfreeze Card</string>
|
||||||
|
|
@ -1930,22 +1898,12 @@
|
||||||
<item quantity="one">%d card</item>
|
<item quantity="one">%d card</item>
|
||||||
<item quantity="other">%d cards</item>
|
<item quantity="other">%d cards</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="tangempay_cashback_deactivated_description">It was made due to your suspicious behavior. Contact support to learn more</string>
|
|
||||||
<string name="tangempay_cashback_deactivated_title">Cashback deactivated</string>
|
|
||||||
<string name="tangempay_cashback_deposited_on">Will be deposited on %1$s</string>
|
|
||||||
<string name="tangempay_cashback_widget_title">%1$s cashback in %2$s</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_common_card">Card</string>
|
<string name="tangempay_common_card">Card</string>
|
||||||
<string name="tangempay_common_error_loading">Error loading</string>
|
|
||||||
<string name="tangempay_current_plan_active_till_notification">Your %1$s plan is active till %2$s, then we will move you to %3$s. %4$s won\'t be charged.</string>
|
|
||||||
<string name="tangempay_current_plan_change">Change plan</string>
|
<string name="tangempay_current_plan_change">Change plan</string>
|
||||||
<string name="tangempay_current_plan_fee_charged_notification">%1$s monthly fee will be charged on %2$s</string>
|
|
||||||
<string name="tangempay_current_plan_section_card">Card related</string>
|
<string name="tangempay_current_plan_section_card">Card related</string>
|
||||||
<string name="tangempay_current_plan_section_plan">Plan related</string>
|
<string name="tangempay_current_plan_section_plan">Plan related</string>
|
||||||
<string name="tangempay_current_plan_stay_button">Stay on %1$s</string>
|
|
||||||
<string name="tangempay_current_plan_stay_sheet_body">Your transition on %1$s will be canceled</string>
|
|
||||||
<string name="tangempay_current_plan_stay_sheet_title">Do you want to stay on %1$s?</string>
|
|
||||||
<string name="tangempay_current_plan_title">Current plan</string>
|
<string name="tangempay_current_plan_title">Current plan</string>
|
||||||
<string name="tangempay_daily_limit_hint" formatted="false">Set a limit from %s to %s</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_daily_limit_set_button">Set limits</string>
|
||||||
|
|
@ -2002,7 +1960,6 @@
|
||||||
<string name="tangempay_kyc_rejected_button_text">Hide KYC block</string>
|
<string name="tangempay_kyc_rejected_button_text">Hide KYC block</string>
|
||||||
<string name="tangempay_kyc_rejected_description">Sorry, we couldn\'t verify</string>
|
<string name="tangempay_kyc_rejected_description">Sorry, we couldn\'t verify</string>
|
||||||
<string name="tangempay_kyc_rejected_description_span">your profile.</string>
|
<string name="tangempay_kyc_rejected_description_span">your profile.</string>
|
||||||
<string name="tangempay_main_select_plan">Select plan</string>
|
|
||||||
<string name="tangempay_maximum_cards_issued_description">You can have up to 3 cards. Delete one to add a new card.</string>
|
<string name="tangempay_maximum_cards_issued_description">You can have up to 3 cards. Delete one to add a new card.</string>
|
||||||
<string name="tangempay_maximum_cards_issued_title">Maximum Cards Issued</string>
|
<string name="tangempay_maximum_cards_issued_title">Maximum Cards Issued</string>
|
||||||
<string name="tangempay_newonboard_Q1_body">Yes – to operate a regulated Visa card, identity verification is mandatory. KYC is handled by Sumsub (compliance partner).</string>
|
<string name="tangempay_newonboard_Q1_body">Yes – to operate a regulated Visa card, identity verification is mandatory. KYC is handled by Sumsub (compliance partner).</string>
|
||||||
|
|
@ -2057,16 +2014,7 @@
|
||||||
<string name="tangempay_select_plan_btn_select">Select</string>
|
<string name="tangempay_select_plan_btn_select">Select</string>
|
||||||
<string name="tangempay_select_plan_btn_upgrade">Upgrade plan</string>
|
<string name="tangempay_select_plan_btn_upgrade">Upgrade plan</string>
|
||||||
<string name="tangempay_select_plan_compare">Compare plans</string>
|
<string name="tangempay_select_plan_compare">Compare plans</string>
|
||||||
<string name="tangempay_select_plan_confirm_downgrade_title">Your %1$s plan and %2$s cards will be active till %3$s</string>
|
|
||||||
<string name="tangempay_select_plan_confirm_point_cancel_till">You can cancel this transition till %1$s</string>
|
|
||||||
<string name="tangempay_select_plan_confirm_point_cards_closed">Your %1$s cards will be closed</string>
|
|
||||||
<string name="tangempay_select_plan_confirm_point_monthly_fee">%1$s monthly fee will be taken from your account</string>
|
|
||||||
<string name="tangempay_select_plan_confirm_point_move_on_date">On %1$s we will move you to %2$s plan</string>
|
|
||||||
<string name="tangempay_select_plan_confirm_point_no_fee">No fee applied</string>
|
|
||||||
<string name="tangempay_select_plan_confirm_point_virtual_card">You will get your virtual %1$s in minutes</string>
|
|
||||||
<string name="tangempay_select_plan_confirm_switch_title">You are switching to %1$s</string>
|
|
||||||
<string name="tangempay_select_plan_confirm_title">Confirm selection</string>
|
<string name="tangempay_select_plan_confirm_title">Confirm selection</string>
|
||||||
<string name="tangempay_select_plan_confirm_upgrade_title">We will issue %1$s for you</string>
|
|
||||||
<string name="tangempay_select_plan_title">Select plan</string>
|
<string name="tangempay_select_plan_title">Select plan</string>
|
||||||
<string name="tangempay_service_unavailable_description">We’re fixing a technical issue. Please try again later.</string>
|
<string name="tangempay_service_unavailable_description">We’re 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>
|
||||||
|
|
@ -2075,7 +2023,6 @@
|
||||||
<string name="tangempay_set_pin_header">Set up new PIN</string>
|
<string name="tangempay_set_pin_header">Set up new PIN</string>
|
||||||
<string name="tangempay_set_pin_title">Set PIN</string>
|
<string name="tangempay_set_pin_title">Set PIN</string>
|
||||||
<string name="tangempay_status_deactivated">Account closed</string>
|
<string name="tangempay_status_deactivated">Account closed</string>
|
||||||
<string name="tangempay_status_inactive">Inactive</string>
|
|
||||||
<string name="tangempay_status_replacing">Replacing your card</string>
|
<string name="tangempay_status_replacing">Replacing your card</string>
|
||||||
<string name="tangempay_sync_needed">Use your card or ring to renew session</string>
|
<string name="tangempay_sync_needed">Use your card or ring to renew session</string>
|
||||||
<string name="tangempay_sync_needed_body">Use your card or ring to renew session</string>
|
<string name="tangempay_sync_needed_body">Use your card or ring to renew session</string>
|
||||||
|
|
@ -2093,7 +2040,6 @@
|
||||||
<string name="tangempay_usdc_on_polygon_network">USDC on Polygon network</string>
|
<string name="tangempay_usdc_on_polygon_network">USDC on Polygon network</string>
|
||||||
<string name="tangempay_va_banking_details_error_description">Please try again or contact support if the issue persists</string>
|
<string name="tangempay_va_banking_details_error_description">Please try again or contact support if the issue persists</string>
|
||||||
<string name="tangempay_va_banking_details_error_title">Couldn\'t load banking details</string>
|
<string name="tangempay_va_banking_details_error_title">Couldn\'t load banking details</string>
|
||||||
<string name="tangempay_visa_benefits">Visa Benefits</string>
|
|
||||||
<string name="tangempay_withdrawal_note_description">Funds from refunded purchases won’t be returned your on-chain Polygon balance or be available for withdrawal, but will stay on your card balance for purchases</string>
|
<string name="tangempay_withdrawal_note_description">Funds from refunded purchases won’t be returned your on-chain Polygon 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>
|
||||||
<string name="tangempay_your_pin_code">Your PIN code</string>
|
<string name="tangempay_your_pin_code">Your PIN code</string>
|
||||||
|
|
@ -2137,10 +2083,6 @@
|
||||||
<string name="token_receive_show_qr_code_title">Show QR code</string>
|
<string name="token_receive_show_qr_code_title">Show QR code</string>
|
||||||
<string name="token_summary_can_not_load_token">Can’t load data of the token</string>
|
<string name="token_summary_can_not_load_token">Can’t load data of the token</string>
|
||||||
<string name="token_summary_go_to_swap_button">Go to swap</string>
|
<string name="token_summary_go_to_swap_button">Go to swap</string>
|
||||||
<string name="token_summary_last_update_subtitle">Last update: %1$s</string>
|
|
||||||
<string name="token_summary_negative_outlook_title">Negative outlook</string>
|
|
||||||
<string name="token_summary_neutral_outlook_title">Neutral outlook</string>
|
|
||||||
<string name="token_summary_positive_outlook_title">Positive outlook</string>
|
|
||||||
<string name="token_summary_title">Token summary</string>
|
<string name="token_summary_title">Token summary</string>
|
||||||
<string name="token_swap_changelly_promotion_message">Exchange this token for another at %1$s service fees from February %2$s-%3$s.</string>
|
<string name="token_swap_changelly_promotion_message">Exchange this token for another at %1$s service fees from February %2$s-%3$s.</string>
|
||||||
<string name="token_swap_changelly_promotion_title">Swap with Changelly, %s fees</string>
|
<string name="token_swap_changelly_promotion_title">Swap with Changelly, %s fees</string>
|
||||||
|
|
@ -2658,7 +2600,7 @@
|
||||||
<string name="yield_module_approve_sheet_fee_note">The fee will be deducted, and your assets will be resupplied.</string>
|
<string name="yield_module_approve_sheet_fee_note">The fee will be deducted, and your assets will be resupplied.</string>
|
||||||
<string name="yield_module_approve_sheet_subtitle">To continue generating yield, approval is required.</string>
|
<string name="yield_module_approve_sheet_subtitle">To continue generating yield, approval is required.</string>
|
||||||
<string name="yield_module_approve_sheet_title">Confirm approval</string>
|
<string name="yield_module_approve_sheet_title">Confirm approval</string>
|
||||||
<string name="yield_module_average_apy">Current APY %1$s%%</string>
|
<string name="yield_module_average_apy">Average APY %1$s%%</string>
|
||||||
<string name="yield_module_balance_info_sheet_subtitle">Your funds are currently supplied to the Aave protocol, but you can manage them at any time.</string>
|
<string name="yield_module_balance_info_sheet_subtitle">Your funds are currently supplied to the Aave protocol, but you can manage them at any time.</string>
|
||||||
<string name="yield_module_balance_info_sheet_title">Your %s is supplied to Aave</string>
|
<string name="yield_module_balance_info_sheet_title">Your %s is supplied to Aave</string>
|
||||||
<string name="yield_module_chart_loading_error">Unable to load chart...</string>
|
<string name="yield_module_chart_loading_error">Unable to load chart...</string>
|
||||||
|
|
|
||||||
|
|
@ -408,7 +408,7 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
|
||||||
return onboardingRepository.getBankCredentials(userWalletId, accountInstance.id).fold(
|
return onboardingRepository.getBankCredentials(userWalletId, accountInstance.id).fold(
|
||||||
ifLeft = { error ->
|
ifLeft = { error ->
|
||||||
logger.e("getBankCredentials failed for ${accountInstance.id}: $error")
|
logger.e("getBankCredentials failed for ${accountInstance.id}: $error")
|
||||||
null
|
VirtualAccountOnramp.BankCredentialsError
|
||||||
},
|
},
|
||||||
ifRight = { credentials ->
|
ifRight = { credentials ->
|
||||||
VirtualAccountOnramp.Available(
|
VirtualAccountOnramp.Available(
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,7 @@ internal class DefaultPaymentAccountStatusFetcherTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `GIVEN toggle on and ACCOUNT instance but bank credentials fetch fails WHEN invoke THEN virtualAccount is null`() =
|
fun `GIVEN toggle on and ACCOUNT instance but bank credentials fetch fails WHEN invoke THEN virtualAccount is Error`() =
|
||||||
runTest {
|
runTest {
|
||||||
// Arrange
|
// Arrange
|
||||||
val customerInfo = buildCustomerInfo(
|
val customerInfo = buildCustomerInfo(
|
||||||
|
|
@ -256,7 +256,7 @@ internal class DefaultPaymentAccountStatusFetcherTest {
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
val loaded = storedStatuses.lastLoaded()
|
val loaded = storedStatuses.lastLoaded()
|
||||||
assertThat(loaded.virtualAccount).isNull()
|
assertThat(loaded.virtualAccount).isEqualTo(VirtualAccountOnramp.BankCredentialsError)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -21,4 +21,12 @@ sealed interface VirtualAccountOnramp {
|
||||||
val productInstanceId: String,
|
val productInstanceId: String,
|
||||||
val bankCredentials: BankCredentials,
|
val bankCredentials: BankCredentials,
|
||||||
) : VirtualAccountOnramp
|
) : VirtualAccountOnramp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VA product instance exists, but its bank credentials failed to load. The bank-transfer entry point
|
||||||
|
* stays visible; tapping it surfaces a retryable "couldn't load banking details" error instead of the
|
||||||
|
* requisites. Transient — never persisted, re-resolved on the next status fetch.
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
data object BankCredentialsError : VirtualAccountOnramp
|
||||||
}
|
}
|
||||||
|
|
@ -130,6 +130,15 @@ internal class TangemPayCardPageScreenComponent(
|
||||||
onFieldCopied = model::onVaFieldCopied,
|
onFieldCopied = model::onVaFieldCopied,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
is TangemPayCardNavigation.VaBankingDetailsError -> TangemPayVaBankingDetailsErrorComponent(
|
||||||
|
appComponentContext = context,
|
||||||
|
params = TangemPayVaBankingDetailsErrorComponent.Params(
|
||||||
|
userWalletId = navigation.userWalletId,
|
||||||
|
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||||
|
onContactSupport = model::onContactSupportClicked,
|
||||||
|
onResolved = model::onVaBankingDetailsResolved,
|
||||||
|
),
|
||||||
|
)
|
||||||
is TangemPayCardNavigation.Receive -> tokenReceiveComponentFactory.create(
|
is TangemPayCardNavigation.Receive -> tokenReceiveComponentFactory.create(
|
||||||
context = context,
|
context = context,
|
||||||
params = TokenReceiveComponent.Params(
|
params = TokenReceiveComponent.Params(
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,15 @@ internal class TangemPayDetailsComponent(
|
||||||
onFieldCopied = model::onVaFieldCopied,
|
onFieldCopied = model::onVaFieldCopied,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
is TangemPayDetailsNavigation.VaBankingDetailsError -> TangemPayVaBankingDetailsErrorComponent(
|
||||||
|
appComponentContext = context,
|
||||||
|
params = TangemPayVaBankingDetailsErrorComponent.Params(
|
||||||
|
userWalletId = navigation.userWalletId,
|
||||||
|
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||||
|
onContactSupport = model::onContactSupportClicked,
|
||||||
|
onResolved = model::onVaBankingDetailsResolved,
|
||||||
|
),
|
||||||
|
)
|
||||||
is TangemPayDetailsNavigation.IssueAdditionalCard -> TangemPayIssueAdditionalCardComponent(
|
is TangemPayDetailsNavigation.IssueAdditionalCard -> TangemPayIssueAdditionalCardComponent(
|
||||||
appComponentContext = context,
|
appComponentContext = context,
|
||||||
params = TangemPayIssueAdditionalCardComponent.Params(
|
params = TangemPayIssueAdditionalCardComponent.Params(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.tangem.features.tangempay.components
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import com.tangem.core.decompose.context.AppComponentContext
|
||||||
|
import com.tangem.core.decompose.model.getOrCreateModel
|
||||||
|
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||||
|
import com.tangem.domain.models.account.VirtualAccountOnramp
|
||||||
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import com.tangem.features.tangempay.model.TangemPayVaBankingDetailsErrorModel
|
||||||
|
import com.tangem.features.tangempay.ui.TangemPayVaBankingDetailsErrorBottomSheet
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error bottom sheet shown when VA bank credentials fail to load ([VirtualAccountOnramp.BankCredentialsError]).
|
||||||
|
*
|
||||||
|
* "Try again" re-fetches the payment account status while showing a loader on the button; on success the
|
||||||
|
* resolved on-ramp is handed back via [Params.onResolved] (the parent opens the bank-transfer sheet), otherwise
|
||||||
|
* the error stays visible with the loader cleared.
|
||||||
|
*/
|
||||||
|
internal class TangemPayVaBankingDetailsErrorComponent(
|
||||||
|
appComponentContext: AppComponentContext,
|
||||||
|
params: Params,
|
||||||
|
) : ComposableBottomSheetComponent, AppComponentContext by appComponentContext {
|
||||||
|
|
||||||
|
private val model: TangemPayVaBankingDetailsErrorModel = getOrCreateModel(params = params)
|
||||||
|
|
||||||
|
override fun dismiss() {
|
||||||
|
model.onDismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun BottomSheet() {
|
||||||
|
val state by model.uiState.collectAsStateWithLifecycle()
|
||||||
|
TangemPayVaBankingDetailsErrorBottomSheet(state = state)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Params(
|
||||||
|
val userWalletId: UserWalletId,
|
||||||
|
val onDismiss: () -> Unit,
|
||||||
|
val onContactSupport: () -> Unit,
|
||||||
|
val onResolved: (VirtualAccountOnramp) -> Unit,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -50,6 +50,11 @@ internal interface TangemPayModelModule {
|
||||||
@ClassKey(TangemPayVirtualAccountDepositModel::class)
|
@ClassKey(TangemPayVirtualAccountDepositModel::class)
|
||||||
fun bindTangemPayVirtualAccountDepositModel(model: TangemPayVirtualAccountDepositModel): Model
|
fun bindTangemPayVirtualAccountDepositModel(model: TangemPayVirtualAccountDepositModel): Model
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@ClassKey(TangemPayVaBankingDetailsErrorModel::class)
|
||||||
|
fun bindTangemPayVaBankingDetailsErrorModel(model: TangemPayVaBankingDetailsErrorModel): Model
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@ClassKey(TangemPayViewPinModel::class)
|
@ClassKey(TangemPayViewPinModel::class)
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,11 @@ internal sealed class TangemPayCardNavigation {
|
||||||
val bankCredentials: BankCredentials,
|
val bankCredentials: BankCredentials,
|
||||||
) : TangemPayCardNavigation()
|
) : TangemPayCardNavigation()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class VaBankingDetailsError(
|
||||||
|
val userWalletId: UserWalletId,
|
||||||
|
) : TangemPayCardNavigation()
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Receive(val config: TokenReceiveConfig) : TangemPayCardNavigation()
|
data class Receive(val config: TokenReceiveConfig) : TangemPayCardNavigation()
|
||||||
}
|
}
|
||||||
|
|
@ -39,6 +39,11 @@ internal sealed class TangemPayDetailsNavigation {
|
||||||
val bankCredentials: BankCredentials,
|
val bankCredentials: BankCredentials,
|
||||||
) : TangemPayDetailsNavigation()
|
) : TangemPayDetailsNavigation()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class VaBankingDetailsError(
|
||||||
|
val userWalletId: UserWalletId,
|
||||||
|
) : TangemPayDetailsNavigation()
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class TransactionDetails(
|
data class TransactionDetails(
|
||||||
val transaction: TangemPayTxHistoryItem,
|
val transaction: TangemPayTxHistoryItem,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.tangem.features.tangempay.entity
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI state for the "couldn't load banking details" bottom sheet (VA MVP0, TWI-1638).
|
||||||
|
*
|
||||||
|
* @property isRetryLoading whether the "Try again" button shows a loader while the payment account status
|
||||||
|
* is being re-fetched. While `true` both actions are disabled.
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
internal data class TangemPayVaBankingDetailsErrorUM(
|
||||||
|
val isRetryLoading: Boolean,
|
||||||
|
val onRetryClick: () -> Unit,
|
||||||
|
val onContactSupportClick: () -> Unit,
|
||||||
|
val onDismiss: () -> Unit,
|
||||||
|
)
|
||||||
|
|
@ -7,6 +7,7 @@ import com.arkivanov.decompose.router.slot.dismiss
|
||||||
import com.tangem.common.routing.AppRoute
|
import com.tangem.common.routing.AppRoute
|
||||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||||
import com.tangem.core.analytics.models.AnalyticsParam
|
import com.tangem.core.analytics.models.AnalyticsParam
|
||||||
|
import com.tangem.core.analytics.models.Basic
|
||||||
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.decompose.model.ParamsContainer
|
||||||
|
|
@ -24,6 +25,9 @@ import com.tangem.core.ui.res.TangemTheme
|
||||||
import com.tangem.core.ui.res.generated.icons.Icons
|
import com.tangem.core.ui.res.generated.icons.Icons
|
||||||
import com.tangem.core.ui.res.generated.icons.ic_arrow_refresh_20
|
import com.tangem.core.ui.res.generated.icons.ic_arrow_refresh_20
|
||||||
import com.tangem.core.ui.test.TangemPayTestTags
|
import com.tangem.core.ui.test.TangemPayTestTags
|
||||||
|
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||||
|
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||||
|
import com.tangem.domain.feedback.models.WalletMetaInfo
|
||||||
import com.tangem.domain.models.StatusSource
|
import com.tangem.domain.models.StatusSource
|
||||||
import com.tangem.domain.models.TokenReceiveConfig
|
import com.tangem.domain.models.TokenReceiveConfig
|
||||||
import com.tangem.domain.models.account.AccountStatus
|
import com.tangem.domain.models.account.AccountStatus
|
||||||
|
|
@ -72,11 +76,12 @@ import com.tangem.core.ui.R as CoreUiR
|
||||||
@ModelScoped
|
@ModelScoped
|
||||||
internal class TangemPayCardPageModel @Inject constructor(
|
internal class TangemPayCardPageModel @Inject constructor(
|
||||||
paramsContainer: ParamsContainer,
|
paramsContainer: ParamsContainer,
|
||||||
paymentAccountStatusSupplier: PaymentAccountStatusSupplier,
|
private val paymentAccountStatusSupplier: PaymentAccountStatusSupplier,
|
||||||
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher,
|
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher,
|
||||||
override val dispatchers: CoroutineDispatcherProvider,
|
override val dispatchers: CoroutineDispatcherProvider,
|
||||||
private val router: Router,
|
private val router: Router,
|
||||||
private val analytics: AnalyticsEventHandler,
|
private val analytics: AnalyticsEventHandler,
|
||||||
|
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||||
private val cardDetailsRepository: TangemPayCardDetailsRepository,
|
private val cardDetailsRepository: TangemPayCardDetailsRepository,
|
||||||
private val uiMessageSender: UiMessageSender,
|
private val uiMessageSender: UiMessageSender,
|
||||||
private val changeCardFrozenStateUseCase: ChangeCardFrozenStateUseCase,
|
private val changeCardFrozenStateUseCase: ChangeCardFrozenStateUseCase,
|
||||||
|
|
@ -488,7 +493,16 @@ internal class TangemPayCardPageModel @Inject constructor(
|
||||||
|
|
||||||
override fun onClickBankTransfer() {
|
override fun onClickBankTransfer() {
|
||||||
val loaded = currentStatus.value.ifLoadedOrNull { it } ?: return
|
val loaded = currentStatus.value.ifLoadedOrNull { it } ?: return
|
||||||
val onramp = loaded.virtualAccount ?: return
|
when (val onramp = loaded.virtualAccount) {
|
||||||
|
null -> return
|
||||||
|
is VirtualAccountOnramp.BankCredentialsError -> showVaBankingDetailsError()
|
||||||
|
is VirtualAccountOnramp.Available,
|
||||||
|
VirtualAccountOnramp.Eligible,
|
||||||
|
-> openVirtualAccountDeposit(onramp, loaded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openVirtualAccountDeposit(onramp: VirtualAccountOnramp, loaded: PaymentAccountStatusValue.Loaded) {
|
||||||
analytics.send(TangemPayAnalyticsEvents.VaTopupButtonClicked())
|
analytics.send(TangemPayAnalyticsEvents.VaTopupButtonClicked())
|
||||||
bottomSheetNavigation.dismiss()
|
bottomSheetNavigation.dismiss()
|
||||||
bottomSheetNavigation.activate(
|
bottomSheetNavigation.activate(
|
||||||
|
|
@ -500,6 +514,31 @@ internal class TangemPayCardPageModel @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showVaBankingDetailsError() {
|
||||||
|
bottomSheetNavigation.dismiss()
|
||||||
|
bottomSheetNavigation.activate(
|
||||||
|
TangemPayCardNavigation.VaBankingDetailsError(userWalletId = userWalletId),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onVaBankingDetailsResolved(onramp: VirtualAccountOnramp) {
|
||||||
|
val loaded = currentStatus.value.ifLoadedOrNull { it } ?: return
|
||||||
|
openVirtualAccountDeposit(onramp, loaded)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onContactSupportClicked() {
|
||||||
|
analytics.send(Basic.ButtonSupport(source = AnalyticsParam.ScreensSources.TangemPay))
|
||||||
|
val customerId = currentStatus.value.ifLoadedOrNull { it.customerId } ?: return
|
||||||
|
modelScope.launch {
|
||||||
|
sendFeedbackEmailUseCase.invoke(
|
||||||
|
type = FeedbackEmailType.Visa.FeatureIsBeta(
|
||||||
|
walletMetaInfo = WalletMetaInfo(userWalletId = userWalletId),
|
||||||
|
customerId = customerId,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onVirtualAccountOrderCreated() {
|
fun onVirtualAccountOrderCreated() {
|
||||||
analytics.send(TangemPayAnalyticsEvents.VaSuccessScreenActivation())
|
analytics.send(TangemPayAnalyticsEvents.VaSuccessScreenActivation())
|
||||||
bottomSheetNavigation.dismiss()
|
bottomSheetNavigation.dismiss()
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ import javax.inject.Inject
|
||||||
@ModelScoped
|
@ModelScoped
|
||||||
internal class TangemPayDetailsModel @Inject constructor(
|
internal class TangemPayDetailsModel @Inject constructor(
|
||||||
paramsContainer: ParamsContainer,
|
paramsContainer: ParamsContainer,
|
||||||
paymentAccountStatusSupplier: PaymentAccountStatusSupplier,
|
private val paymentAccountStatusSupplier: PaymentAccountStatusSupplier,
|
||||||
override val dispatchers: CoroutineDispatcherProvider,
|
override val dispatchers: CoroutineDispatcherProvider,
|
||||||
private val analytics: AnalyticsEventHandler,
|
private val analytics: AnalyticsEventHandler,
|
||||||
private val router: Router,
|
private val router: Router,
|
||||||
|
|
@ -313,7 +313,16 @@ internal class TangemPayDetailsModel @Inject constructor(
|
||||||
|
|
||||||
override fun onClickBankTransfer() {
|
override fun onClickBankTransfer() {
|
||||||
val loaded = currentStatus.value.ifLoadedOrNull { it } ?: return
|
val loaded = currentStatus.value.ifLoadedOrNull { it } ?: return
|
||||||
val onramp = loaded.virtualAccount ?: return
|
when (val onramp = loaded.virtualAccount) {
|
||||||
|
null -> return
|
||||||
|
is VirtualAccountOnramp.BankCredentialsError -> showVaBankingDetailsError()
|
||||||
|
is VirtualAccountOnramp.Available,
|
||||||
|
VirtualAccountOnramp.Eligible,
|
||||||
|
-> openVirtualAccountDeposit(onramp, loaded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openVirtualAccountDeposit(onramp: VirtualAccountOnramp, loaded: PaymentAccountStatusValue.Loaded) {
|
||||||
analytics.send(TangemPayAnalyticsEvents.VaTopupButtonClicked())
|
analytics.send(TangemPayAnalyticsEvents.VaTopupButtonClicked())
|
||||||
bottomSheetNavigation.dismiss()
|
bottomSheetNavigation.dismiss()
|
||||||
bottomSheetNavigation.activate(
|
bottomSheetNavigation.activate(
|
||||||
|
|
@ -325,6 +334,18 @@ internal class TangemPayDetailsModel @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showVaBankingDetailsError() {
|
||||||
|
bottomSheetNavigation.dismiss()
|
||||||
|
bottomSheetNavigation.activate(
|
||||||
|
TangemPayDetailsNavigation.VaBankingDetailsError(userWalletId = userWalletId),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onVaBankingDetailsResolved(onramp: VirtualAccountOnramp) {
|
||||||
|
val loaded = currentStatus.value.ifLoadedOrNull { it } ?: return
|
||||||
|
openVirtualAccountDeposit(onramp, loaded)
|
||||||
|
}
|
||||||
|
|
||||||
fun onVirtualAccountOrderCreated() {
|
fun onVirtualAccountOrderCreated() {
|
||||||
analytics.send(TangemPayAnalyticsEvents.VaSuccessScreenActivation())
|
analytics.send(TangemPayAnalyticsEvents.VaSuccessScreenActivation())
|
||||||
bottomSheetNavigation.dismiss()
|
bottomSheetNavigation.dismiss()
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
package com.tangem.features.tangempay.model
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import com.tangem.core.decompose.di.ModelScoped
|
||||||
|
import com.tangem.core.decompose.model.Model
|
||||||
|
import com.tangem.core.decompose.model.ParamsContainer
|
||||||
|
import com.tangem.domain.models.account.VirtualAccountOnramp
|
||||||
|
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
|
||||||
|
import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier
|
||||||
|
import com.tangem.features.tangempay.components.TangemPayVaBankingDetailsErrorComponent
|
||||||
|
import com.tangem.features.tangempay.entity.TangemPayVaBankingDetailsErrorUM
|
||||||
|
import com.tangem.features.tangempay.utils.ifLoadedOrNull
|
||||||
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
@ModelScoped
|
||||||
|
internal class TangemPayVaBankingDetailsErrorModel @Inject constructor(
|
||||||
|
paramsContainer: ParamsContainer,
|
||||||
|
override val dispatchers: CoroutineDispatcherProvider,
|
||||||
|
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher,
|
||||||
|
private val paymentAccountStatusSupplier: PaymentAccountStatusSupplier,
|
||||||
|
) : Model() {
|
||||||
|
|
||||||
|
private val params = paramsContainer.require<TangemPayVaBankingDetailsErrorComponent.Params>()
|
||||||
|
|
||||||
|
val uiState: StateFlow<TangemPayVaBankingDetailsErrorUM>
|
||||||
|
field = MutableStateFlow(
|
||||||
|
TangemPayVaBankingDetailsErrorUM(
|
||||||
|
isRetryLoading = false,
|
||||||
|
onRetryClick = ::onRetryClick,
|
||||||
|
onContactSupportClick = params.onContactSupport,
|
||||||
|
onDismiss = ::onDismiss,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
fun onDismiss() {
|
||||||
|
params.onDismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onRetryClick() {
|
||||||
|
if (uiState.value.isRetryLoading) return
|
||||||
|
uiState.update { it.copy(isRetryLoading = true) }
|
||||||
|
modelScope.launch {
|
||||||
|
paymentAccountStatusFetcher.invoke(params.userWalletId)
|
||||||
|
val onramp = paymentAccountStatusSupplier.invoke(params.userWalletId)
|
||||||
|
.first()
|
||||||
|
.ifLoadedOrNull { it.virtualAccount }
|
||||||
|
when (onramp) {
|
||||||
|
is VirtualAccountOnramp.Available,
|
||||||
|
VirtualAccountOnramp.Eligible,
|
||||||
|
-> params.onResolved(onramp)
|
||||||
|
// Still failing (BankCredentialsError) or unavailable — keep the sheet, clear the loader.
|
||||||
|
else -> uiState.update { it.copy(isRetryLoading = false) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -81,6 +81,9 @@ internal class TangemPayVirtualAccountDepositModel @Inject constructor(
|
||||||
analytics.send(TangemPayAnalyticsEvents.VaShowDetailsFirstTimeClicked())
|
analytics.send(TangemPayAnalyticsEvents.VaShowDetailsFirstTimeClicked())
|
||||||
createVirtualAccountOrder()
|
createVirtualAccountOrder()
|
||||||
}
|
}
|
||||||
|
// Error onramp is intercepted before this sheet opens (a dedicated error sheet is shown instead);
|
||||||
|
// the branch only keeps the `when` exhaustive.
|
||||||
|
VirtualAccountOnramp.BankCredentialsError -> onDismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
package com.tangem.features.tangempay.ui
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
|
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||||
|
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
|
||||||
|
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||||
|
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||||
|
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetType
|
||||||
|
import com.tangem.core.ui.ds.topbar.TangemTopBar
|
||||||
|
import com.tangem.core.ui.ds.topbar.TangemTopBarType
|
||||||
|
import com.tangem.core.ui.ds2.button.Close
|
||||||
|
import com.tangem.core.ui.ds2.button.TangemButton
|
||||||
|
import com.tangem.core.ui.extensions.TextReference
|
||||||
|
import com.tangem.core.ui.extensions.resolveReference
|
||||||
|
import com.tangem.core.ui.extensions.resourceReference
|
||||||
|
import com.tangem.core.ui.res.TangemTheme
|
||||||
|
import com.tangem.core.ui.res.TangemThemePreviewRedesign
|
||||||
|
import com.tangem.core.ui.res.generated.icons.Icons
|
||||||
|
import com.tangem.core.ui.res.generated.icons.ic_error_28
|
||||||
|
import com.tangem.features.tangempay.details.impl.R
|
||||||
|
import com.tangem.features.tangempay.entity.TangemPayVaBankingDetailsErrorUM
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun TangemPayVaBankingDetailsErrorBottomSheet(state: TangemPayVaBankingDetailsErrorUM) {
|
||||||
|
TangemBottomSheet<TangemBottomSheetConfigContent.Empty>(
|
||||||
|
config = TangemBottomSheetConfig(
|
||||||
|
isShown = true,
|
||||||
|
onDismissRequest = state.onDismiss,
|
||||||
|
content = TangemBottomSheetConfigContent.Empty,
|
||||||
|
),
|
||||||
|
type = TangemBottomSheetType.Modal,
|
||||||
|
containerColor = TangemTheme.colors3.bg.secondary,
|
||||||
|
title = {
|
||||||
|
TangemTopBar(
|
||||||
|
type = TangemTopBarType.BottomSheet,
|
||||||
|
title = null,
|
||||||
|
endContent = { TangemButton.Close(onClick = state.onDismiss) },
|
||||||
|
)
|
||||||
|
},
|
||||||
|
content = { _ -> Content(state) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Content(state: TangemPayVaBankingDetailsErrorUM, modifier: Modifier = Modifier) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
.padding(horizontal = TangemTheme.dimens2.x4)
|
||||||
|
.padding(bottom = TangemTheme.dimens2.x4),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
) {
|
||||||
|
WarningIcon(modifier = Modifier.padding(top = TangemTheme.dimens2.x4))
|
||||||
|
TitleText(
|
||||||
|
text = resourceReference(R.string.tangempay_va_banking_details_error_title),
|
||||||
|
modifier = Modifier.padding(top = TangemTheme.dimens2.x8),
|
||||||
|
)
|
||||||
|
SubtitleText(
|
||||||
|
text = resourceReference(R.string.tangempay_va_banking_details_error_description),
|
||||||
|
modifier = Modifier.padding(top = TangemTheme.dimens2.x2),
|
||||||
|
)
|
||||||
|
TangemButton(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = TangemTheme.dimens2.x8),
|
||||||
|
text = resourceReference(R.string.common_contact_support),
|
||||||
|
variant = TangemButton.Variant.Secondary,
|
||||||
|
size = TangemButton.Size.X12,
|
||||||
|
isEnabled = !state.isRetryLoading,
|
||||||
|
onClick = state.onContactSupportClick,
|
||||||
|
)
|
||||||
|
TangemButton(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = TangemTheme.dimens2.x2),
|
||||||
|
text = resourceReference(R.string.alert_button_try_again),
|
||||||
|
variant = TangemButton.Variant.Primary,
|
||||||
|
size = TangemButton.Size.X12,
|
||||||
|
isLoading = state.isRetryLoading,
|
||||||
|
isEnabled = !state.isRetryLoading,
|
||||||
|
onClick = state.onRetryClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun WarningIcon(modifier: Modifier = Modifier) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.size(TangemTheme.dimens2.x20)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(TangemTheme.colors3.bg.status.warningSubtle),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
modifier = Modifier.size(TangemTheme.dimens2.x7),
|
||||||
|
imageVector = Icons.ic_error_28,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = TangemTheme.colors3.icon.status.warning,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TitleText(text: TextReference, modifier: Modifier = Modifier) {
|
||||||
|
Text(
|
||||||
|
modifier = modifier.fillMaxWidth(),
|
||||||
|
text = text.resolveReference(),
|
||||||
|
style = TangemTheme.typography3.heading.small,
|
||||||
|
color = TangemTheme.colors3.text.primary,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SubtitleText(text: TextReference, modifier: Modifier = Modifier) {
|
||||||
|
Text(
|
||||||
|
modifier = modifier.fillMaxWidth(),
|
||||||
|
text = text.resolveReference(),
|
||||||
|
style = TangemTheme.typography3.subheading.medium,
|
||||||
|
color = TangemTheme.colors3.text.secondary,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(showBackground = true)
|
||||||
|
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
private fun TangemPayVaBankingDetailsErrorPreview(
|
||||||
|
@PreviewParameter(VaBankingDetailsErrorPreviewProvider::class) state: TangemPayVaBankingDetailsErrorUM,
|
||||||
|
) {
|
||||||
|
TangemThemePreviewRedesign {
|
||||||
|
Content(
|
||||||
|
state = state,
|
||||||
|
modifier = Modifier.background(TangemTheme.colors3.bg.secondary),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class VaBankingDetailsErrorPreviewProvider :
|
||||||
|
CollectionPreviewParameterProvider<TangemPayVaBankingDetailsErrorUM>(
|
||||||
|
collection = listOf(
|
||||||
|
TangemPayVaBankingDetailsErrorUM(
|
||||||
|
isRetryLoading = false,
|
||||||
|
onRetryClick = {},
|
||||||
|
onContactSupportClick = {},
|
||||||
|
onDismiss = {},
|
||||||
|
),
|
||||||
|
TangemPayVaBankingDetailsErrorUM(
|
||||||
|
isRetryLoading = true,
|
||||||
|
onRetryClick = {},
|
||||||
|
onContactSupportClick = {},
|
||||||
|
onDismiss = {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
package com.tangem.features.tangempay.model
|
||||||
|
|
||||||
|
import arrow.core.Either
|
||||||
|
import arrow.core.right
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import com.tangem.core.decompose.model.MutableParamsContainer
|
||||||
|
import com.tangem.domain.models.account.AccountStatus
|
||||||
|
import com.tangem.domain.models.account.PaymentAccountStatusValue
|
||||||
|
import com.tangem.domain.models.account.VirtualAccountOnramp
|
||||||
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
|
import com.tangem.domain.pay.flow.PaymentAccountStatusFetcher
|
||||||
|
import com.tangem.domain.pay.flow.PaymentAccountStatusSupplier
|
||||||
|
import com.tangem.features.tangempay.components.TangemPayVaBankingDetailsErrorComponent
|
||||||
|
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||||
|
import io.mockk.Called
|
||||||
|
import io.mockk.clearMocks
|
||||||
|
import io.mockk.coEvery
|
||||||
|
import io.mockk.coVerify
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||||
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
internal class TangemPayVaBankingDetailsErrorModelTest {
|
||||||
|
|
||||||
|
private val userWalletId = UserWalletId("1234567890ABCDEF")
|
||||||
|
|
||||||
|
private val paymentAccountStatusFetcher: PaymentAccountStatusFetcher = mockk()
|
||||||
|
private val paymentAccountStatusSupplier: PaymentAccountStatusSupplier = mockk()
|
||||||
|
private val onDismiss: () -> Unit = mockk(relaxed = true)
|
||||||
|
private val onContactSupport: () -> Unit = mockk(relaxed = true)
|
||||||
|
private val onResolved: (VirtualAccountOnramp) -> Unit = mockk(relaxed = true)
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun resetMocks() {
|
||||||
|
clearMocks(paymentAccountStatusFetcher, paymentAccountStatusSupplier, onDismiss, onContactSupport, onResolved)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN refetch resolves to available WHEN retry THEN onResolved called`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
val onramp = VirtualAccountOnramp.Available(productInstanceId = "pi_1", bankCredentials = mockk())
|
||||||
|
coEvery { paymentAccountStatusFetcher.invoke(userWalletId) } returns Unit.right()
|
||||||
|
stubSupplier(onramp)
|
||||||
|
val model = createModel()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
model.uiState.value.onRetryClick()
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
verify(exactly = 1) { onResolved(onramp) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN refetch still fails WHEN retry THEN onResolved not called and loading reset`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
coEvery { paymentAccountStatusFetcher.invoke(userWalletId) } returns Unit.right()
|
||||||
|
stubSupplier(VirtualAccountOnramp.BankCredentialsError)
|
||||||
|
val model = createModel()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
model.uiState.value.onRetryClick()
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
verify { onResolved wasNot Called }
|
||||||
|
assertThat(model.uiState.value.isRetryLoading).isFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN refetch in progress WHEN retry twice THEN fetch invoked once and loading shown`() = runTest {
|
||||||
|
// Arrange
|
||||||
|
val pending = CompletableDeferred<Either<Throwable, Unit>>()
|
||||||
|
coEvery { paymentAccountStatusFetcher.invoke(userWalletId) } coAnswers { pending.await() }
|
||||||
|
stubSupplier(VirtualAccountOnramp.BankCredentialsError)
|
||||||
|
val model = createModel()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
model.uiState.value.onRetryClick() // starts loading, fetch suspends
|
||||||
|
advanceUntilIdle()
|
||||||
|
model.uiState.value.onRetryClick() // gated by isRetryLoading — must be ignored
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assertThat(model.uiState.value.isRetryLoading).isTrue()
|
||||||
|
coVerify(exactly = 1) { paymentAccountStatusFetcher.invoke(userWalletId) }
|
||||||
|
|
||||||
|
pending.complete(Unit.right()) // let the in-flight call finish cleanly
|
||||||
|
advanceUntilIdle()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stubSupplier(onramp: VirtualAccountOnramp) {
|
||||||
|
val loaded = mockk<PaymentAccountStatusValue.Loaded>()
|
||||||
|
every { loaded.virtualAccount } returns onramp
|
||||||
|
val status = mockk<AccountStatus.Payment>()
|
||||||
|
every { status.value } returns loaded
|
||||||
|
every { paymentAccountStatusSupplier.invoke(userWalletId) } returns flowOf(status)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TestScope.createModel() = TangemPayVaBankingDetailsErrorModel(
|
||||||
|
paramsContainer = MutableParamsContainer(
|
||||||
|
TangemPayVaBankingDetailsErrorComponent.Params(
|
||||||
|
userWalletId = userWalletId,
|
||||||
|
onDismiss = onDismiss,
|
||||||
|
onContactSupport = onContactSupport,
|
||||||
|
onResolved = onResolved,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
dispatchers = createTestingCoroutineDispatcherProvider(),
|
||||||
|
paymentAccountStatusFetcher = paymentAccountStatusFetcher,
|
||||||
|
paymentAccountStatusSupplier = paymentAccountStatusSupplier,
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun TestScope.createTestingCoroutineDispatcherProvider(): TestingCoroutineDispatcherProvider {
|
||||||
|
val testDispatcher = StandardTestDispatcher(testScheduler)
|
||||||
|
return TestingCoroutineDispatcherProvider(
|
||||||
|
main = testDispatcher,
|
||||||
|
mainImmediate = testDispatcher,
|
||||||
|
io = testDispatcher,
|
||||||
|
default = testDispatcher,
|
||||||
|
single = testDispatcher,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue