Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-13 11:33:35 +03:00
parent bc1e6acd0f
commit b9b41e9d26
24 changed files with 570 additions and 815 deletions

View file

@ -1362,7 +1362,7 @@
<string name="user_push_notification_agreement_argument_two_subtitle">Frühzeitiger Zugriff auf neue Funktionen und exklusive Angebote.</string> <string name="user_push_notification_agreement_argument_two_subtitle">Frühzeitiger Zugriff auf neue Funktionen und exklusive Angebote.</string>
<string name="user_push_notification_agreement_argument_two_title">Updates zu Funktionen und Neuigkeiten</string> <string name="user_push_notification_agreement_argument_two_title">Updates zu Funktionen und Neuigkeiten</string>
<string name="user_push_notification_agreement_header">Möchtest du Push-Benachrichtigungen verwenden?</string> <string name="user_push_notification_agreement_header">Möchtest du Push-Benachrichtigungen verwenden?</string>
<string name="user_push_notification_banner_subtitle">Aktiviere Push-Benachrichtigungen und wir benachrichtigen Dich sofort, wenn Gelder eintreffen \n</string> <string name="user_push_notification_banner_subtitle">Aktiviere Push-Benachrichtigungen und wir benachrichtigen Dich sofort, wenn Gelder eintreffen</string>
<string name="user_push_notification_banner_title">Verpasse keine Transaktion</string> <string name="user_push_notification_banner_title">Verpasse keine Transaktion</string>
<string name="user_wallet_list_add_button">Neues Wallet hinzufügen</string> <string name="user_wallet_list_add_button">Neues Wallet hinzufügen</string>
<string name="user_wallet_list_delete_prompt">Möchtest Du diese Wallet wirklich entfernen?</string> <string name="user_wallet_list_delete_prompt">Möchtest Du diese Wallet wirklich entfernen?</string>

View file

@ -1212,6 +1212,8 @@
<string name="user_push_notification_agreement_argument_two_subtitle">Acceso anticipado a nuevas funciones y ofertas exclusivas.</string> <string name="user_push_notification_agreement_argument_two_subtitle">Acceso anticipado a nuevas funciones y ofertas exclusivas.</string>
<string name="user_push_notification_agreement_argument_two_title">Actualizaciones de características y noticias</string> <string name="user_push_notification_agreement_argument_two_title">Actualizaciones de características y noticias</string>
<string name="user_push_notification_agreement_header">¿Quiere utilizar\nnotificaciones push?</string> <string name="user_push_notification_agreement_header">¿Quiere utilizar\nnotificaciones push?</string>
<string name="user_push_notification_banner_subtitle">Active las notificaciones push y le avisaremos al instante cuando le lleguen fondos.</string>
<string name="user_push_notification_banner_title">No se pierda ninguna transacción</string>
<string name="user_wallet_list_add_button">Agregar una nueva billetera</string> <string name="user_wallet_list_add_button">Agregar una nueva billetera</string>
<string name="user_wallet_list_delete_prompt">¿Estás seguro de que deseas olvidar esta billetera?</string> <string name="user_wallet_list_delete_prompt">¿Estás seguro de que deseas olvidar esta billetera?</string>
<string name="user_wallet_list_error_unable_to_unlock">Ha ocurrido un error, por favor escanee su tarjeta o anillo para iniciar sesión</string> <string name="user_wallet_list_error_unable_to_unlock">Ha ocurrido un error, por favor escanee su tarjeta o anillo para iniciar sesión</string>

View file

@ -1185,6 +1185,8 @@
<string name="user_push_notification_agreement_argument_two_subtitle">Accès anticipé à de nouvelles fonctionnalités et à des offres exclusives.</string> <string name="user_push_notification_agreement_argument_two_subtitle">Accès anticipé à de nouvelles fonctionnalités et à des offres exclusives.</string>
<string name="user_push_notification_agreement_argument_two_title">Actualités et mises à jour</string> <string name="user_push_notification_agreement_argument_two_title">Actualités et mises à jour</string>
<string name="user_push_notification_agreement_header">Souhaitez-vous utiliser les\nnotifications push?</string> <string name="user_push_notification_agreement_header">Souhaitez-vous utiliser les\nnotifications push?</string>
<string name="user_push_notification_banner_subtitle">Activez les notifications pour recevoir des alertes lorsque des fonds arrivent dans votre portefeuille.</string>
<string name="user_push_notification_banner_title">Ne manquez aucune transaction</string>
<string name="user_wallet_list_add_button">Ajouter un nouveau portefeuille</string> <string name="user_wallet_list_add_button">Ajouter un nouveau portefeuille</string>
<string name="user_wallet_list_delete_prompt">Êtes-vous sûr de vouloir supprimer ce portefeuille ?</string> <string name="user_wallet_list_delete_prompt">Êtes-vous sûr de vouloir supprimer ce portefeuille ?</string>
<string name="user_wallet_list_error_unable_to_unlock">Une erreur s\'est produite, veuillez scanner votre carte ou bague pour vous connecter</string> <string name="user_wallet_list_error_unable_to_unlock">Une erreur s\'est produite, veuillez scanner votre carte ou bague pour vous connecter</string>

View file

@ -134,7 +134,7 @@
<string name="biometric_unavailable_warning">お使いの携帯電話で生体認証が無効になっているため、アプリにウォレットを保存できません。ウォレットを保存するには、携帯電話の設定で生体認証機能を有効にしてください。</string> <string name="biometric_unavailable_warning">お使いの携帯電話で生体認証が無効になっているため、アプリにウォレットを保存できません。ウォレットを保存するには、携帯電話の設定で生体認証機能を有効にしてください。</string>
<string name="bitcoin_promo_activation_error">プロモーションコードの処理中にエラーが発生しました。しばらくしてからもう一度お試しください。</string> <string name="bitcoin_promo_activation_error">プロモーションコードの処理中にエラーが発生しました。しばらくしてからもう一度お試しください。</string>
<string name="bitcoin_promo_activation_error_title">アクティベーションエラー</string> <string name="bitcoin_promo_activation_error_title">アクティベーションエラー</string>
<string name="bitcoin_promo_activation_success">プロモコードが正常に有効化されました。リワードは14日以内にあなたのアカウントに付与されます。</string> <string name="bitcoin_promo_activation_success">プロモーションコードが正常に有効化されました。報酬は14日以内にビットコインアカウントに入金されます。</string>
<string name="bitcoin_promo_activation_success_title">プロモーションコードが有効になりました</string> <string name="bitcoin_promo_activation_success_title">プロモーションコードが有効になりました</string>
<string name="bitcoin_promo_already_activated">このプロモーションコードは既に使用されているため、再度使うことはできません。</string> <string name="bitcoin_promo_already_activated">このプロモーションコードは既に使用されているため、再度使うことはできません。</string>
<string name="bitcoin_promo_already_activated_title">コードが利用できません</string> <string name="bitcoin_promo_already_activated_title">コードが利用できません</string>
@ -180,6 +180,7 @@
<string name="common_add">追加</string> <string name="common_add">追加</string>
<string name="common_add_to_portfolio">ポートフォリオに追加</string> <string name="common_add_to_portfolio">ポートフォリオに追加</string>
<string name="common_add_token">トークンを追加</string> <string name="common_add_token">トークンを追加</string>
<string name="common_added">追加済み</string>
<string name="common_address">アドレス</string> <string name="common_address">アドレス</string>
<string name="common_all">すべて</string> <string name="common_all">すべて</string>
<string name="common_allow">許可する</string> <string name="common_allow">許可する</string>
@ -291,6 +292,7 @@
<string name="common_range_with_space">%1$s — %2$s</string> <string name="common_range_with_space">%1$s — %2$s</string>
<string name="common_read_more">続きを読む</string> <string name="common_read_more">続きを読む</string>
<string name="common_receive">受け取る</string> <string name="common_receive">受け取る</string>
<string name="common_recommended">おすすめ</string>
<string name="common_reject">拒否</string> <string name="common_reject">拒否</string>
<string name="common_reload">リロード</string> <string name="common_reload">リロード</string>
<string name="common_rename">名前を変更</string> <string name="common_rename">名前を変更</string>
@ -347,6 +349,8 @@
<string name="common_yes">はい</string> <string name="common_yes">はい</string>
<string name="contract_address_copied_message">コントラクトアドレスをコピーしました!</string> <string name="contract_address_copied_message">コントラクトアドレスをコピーしました!</string>
<string name="currency_subtitle_expanded">利用可能なネットワーク</string> <string name="currency_subtitle_expanded">利用可能なネットワーク</string>
<string name="custom_token_another_account_dialog_description">このトークンの導出パスが%1$sの導出パスと一致しています。トークンはこのアカウントに追加されます。</string>
<string name="custom_token_another_account_dialog_title">導出パスは別のアカウントに属します。</string>
<string name="custom_token_contract_address_input_title">コントラクトアドレス</string> <string name="custom_token_contract_address_input_title">コントラクトアドレス</string>
<string name="custom_token_creation_error_invalid_contract_address">コントラクトアドレスが無効です</string> <string name="custom_token_creation_error_invalid_contract_address">コントラクトアドレスが無効です</string>
<string name="custom_token_creation_error_network_not_selected">ネットワークを選択してください</string> <string name="custom_token_creation_error_network_not_selected">ネットワークを選択してください</string>
@ -464,6 +468,7 @@
<string name="express_provider">プロバイダー</string> <string name="express_provider">プロバイダー</string>
<string name="express_provider_best_rate">ベストレート</string> <string name="express_provider_best_rate">ベストレート</string>
<string name="express_provider_fca_warning_list">FCA警告リスト</string> <string name="express_provider_fca_warning_list">FCA警告リスト</string>
<string name="express_provider_great_rate">お得なレート</string>
<string name="express_provider_in_fca_warning_list">FCA警告リストに掲載されたプロバイダー</string> <string name="express_provider_in_fca_warning_list">FCA警告リストに掲載されたプロバイダー</string>
<string name="express_provider_max_amount">最大 %s まで使用可能</string> <string name="express_provider_max_amount">最大 %s まで使用可能</string>
<string name="express_provider_min_amount">%s 以上で利用可能</string> <string name="express_provider_min_amount">%s 以上で利用可能</string>
@ -1361,9 +1366,10 @@
<string name="user_push_notification_agreement_argument_two_subtitle">新しい機能や限定オファーへの早期アクセス。</string> <string name="user_push_notification_agreement_argument_two_subtitle">新しい機能や限定オファーへの早期アクセス。</string>
<string name="user_push_notification_agreement_argument_two_title">機能およびニュースのアップデート</string> <string name="user_push_notification_agreement_argument_two_title">機能およびニュースのアップデート</string>
<string name="user_push_notification_agreement_header">プッシュ通知を使用しますか?</string> <string name="user_push_notification_agreement_header">プッシュ通知を使用しますか?</string>
<string name="user_push_notification_banner_subtitle">プッシュ通知を有効にすると、資金が到着するとすぐに通知されます</string> <string name="user_push_notification_banner_subtitle">プッシュ通知を有効にすると、ウォレットに着金したときにアラートを受信できます。</string>
<string name="user_push_notification_banner_title">取引を見逃さない</string> <string name="user_push_notification_banner_title">取引を見逃さない</string>
<string name="user_wallet_list_add_button">新しいウォレットを追加</string> <string name="user_wallet_list_add_button">新しいウォレットを追加</string>
<string name="user_wallet_list_delete_hw_prompt">バックアップせずにこのウォレットを削除すると、資金に永久にアクセスできなくなります。</string>
<string name="user_wallet_list_delete_prompt">このウォレットを忘れてもよろしいですか?</string> <string name="user_wallet_list_delete_prompt">このウォレットを忘れてもよろしいですか?</string>
<string name="user_wallet_list_error_unable_to_unlock">エラーが発生しました。カードまたはリングをスキャンしてログインしてください。</string> <string name="user_wallet_list_error_unable_to_unlock">エラーが発生しました。カードまたはリングをスキャンしてログインしてください。</string>
<string name="user_wallet_list_error_wallet_already_saved">このウォレットはすでに保存されています。別のウォレットを追加できます。</string> <string name="user_wallet_list_error_wallet_already_saved">このウォレットはすでに保存されています。別のウォレットを追加できます。</string>
@ -1393,6 +1399,13 @@
<string name="visa_unlock_notification_button">ロック解除</string> <string name="visa_unlock_notification_button">ロック解除</string>
<string name="visa_unlock_notification_subtitle">カードをスキャンしてアクセスロックを解除する</string> <string name="visa_unlock_notification_subtitle">カードをスキャンしてアクセスロックを解除する</string>
<string name="visa_unlock_notification_title">ロック解除が必要</string> <string name="visa_unlock_notification_title">ロック解除が必要</string>
<string name="wallet_add_common_title">ウォレットの追加方法を選択してください</string>
<string name="wallet_add_hardware_description">Tangemカードまたはリングをスキャンして復元するか、別のウォレットからインポートしてください。</string>
<string name="wallet_add_hardware_info_create">ハードウェアウォレットを作成</string>
<string name="wallet_add_hardware_purchase">Tangemウォレットを購入しますか</string>
<string name="wallet_add_import_seed_phrase">シードフレーズをインポート</string>
<string name="wallet_add_mobile_description">スマートフォン上でウォレットを復元するか、他のアプリからインポートできますが、Tangemカードと比べるとセキュリティは劣ります。</string>
<string name="wallet_add_support_title">お選びください</string>
<string name="wallet_balance_blockchain_unreachable_try_later">ブロックチェーンにアクセスできません。後でもう一度お試しください。</string> <string name="wallet_balance_blockchain_unreachable_try_later">ブロックチェーンにアクセスできません。後でもう一度お試しください。</string>
<string name="wallet_balance_missing_derivation">カードまたはリングをスキャン</string> <string name="wallet_balance_missing_derivation">カードまたはリングをスキャン</string>
<string name="wallet_been_activated_message">このウォレットはすでに有効化されています。\nあなたが行ったのでない場合は、サポートに連絡してください。\nTangemは、事前に生成されたアクセスコードと一緒にウォレットを販売することはありません。</string> <string name="wallet_been_activated_message">このウォレットはすでに有効化されています。\nあなたが行ったのでない場合は、サポートに連絡してください。\nTangemは、事前に生成されたアクセスコードと一緒にウォレットを販売することはありません。</string>
@ -1432,6 +1445,7 @@
<string name="wallet_connect_subtitle">dAppsに接続する</string> <string name="wallet_connect_subtitle">dAppsに接続する</string>
<string name="wallet_connect_title">WalletConnect</string> <string name="wallet_connect_title">WalletConnect</string>
<string name="wallet_connect_toast_awaiting_session_proposal">接続には数秒かかる場合があります</string> <string name="wallet_connect_toast_awaiting_session_proposal">接続には数秒かかる場合があります</string>
<string name="wallet_create_common_title">Tangemウォレットを作成する</string>
<string name="wallet_create_hardware_badge">%sから</string> <string name="wallet_create_hardware_badge">%sから</string>
<string name="wallet_create_hardware_description">Tangemウォレットを購入ーこれは、あなたの秘密鍵をオフラインで安全に保管する物理デバイスです。</string> <string name="wallet_create_hardware_description">Tangemウォレットを購入ーこれは、あなたの秘密鍵をオフラインで安全に保管する物理デバイスです。</string>
<string name="wallet_create_hardware_title">ハードウェアウォレット</string> <string name="wallet_create_hardware_title">ハードウェアウォレット</string>
@ -1657,13 +1671,20 @@
<string name="wc_uri_already_used_title">URIはすでに使用されています</string> <string name="wc_uri_already_used_title">URIはすでに使用されています</string>
<string name="wc_wallet_connect">WalletConnect</string> <string name="wc_wallet_connect">WalletConnect</string>
<string name="wc_warning_transaction">不審な取引</string> <string name="wc_warning_transaction">不審な取引</string>
<string name="welcome_create_wallet_already_have">すでにTangemをお持ちですか</string>
<string name="welcome_create_wallet_feature_assets">数千種類の資産</string>
<string name="welcome_create_wallet_feature_class">業界最高水準のハードウェアウォレット</string> <string name="welcome_create_wallet_feature_class">業界最高水準のハードウェアウォレット</string>
<string name="welcome_create_wallet_feature_delivery">迅速な配送</string> <string name="welcome_create_wallet_feature_delivery">迅速な配送</string>
<string name="welcome_create_wallet_feature_one_tap">ワンタップで開始</string>
<string name="welcome_create_wallet_feature_seamless">シームレスで安全</string>
<string name="welcome_create_wallet_feature_use">シンプルな操作</string> <string name="welcome_create_wallet_feature_use">シンプルな操作</string>
<string name="welcome_create_wallet_hardware_description">Tangemでハードウェアウォレットを作成しましょう。銀行のカードのようにスリムで、金庫のように安全です。</string> <string name="welcome_create_wallet_hardware_description">Tangemでハードウェアウォレットを作成しましょう。銀行のカードのようにスリムで、金庫のように安全です。</string>
<string name="welcome_create_wallet_mobile_description">ソフトウェアウォレットを作成またはインポート</string> <string name="welcome_create_wallet_mobile_description">ソフトウェアウォレットを作成またはインポート</string>
<string name="welcome_create_wallet_mobile_description_full">スマートフォン上にソフトウェアウォレットを作成するか、インポートしてください。</string>
<string name="welcome_create_wallet_mobile_title">モバイルウォレットから始める</string> <string name="welcome_create_wallet_mobile_title">モバイルウォレットから始める</string>
<string name="welcome_create_wallet_other_method">その他の方法</string> <string name="welcome_create_wallet_other_method">その他の方法</string>
<string name="welcome_create_wallet_use_hardware_description">Tangemハードウェアウォレットを使用する</string>
<string name="welcome_create_wallet_use_hardware_title">詳細を見る・購入する</string>
<string name="welcome_interrupted_backup_alert_discard">破棄</string> <string name="welcome_interrupted_backup_alert_discard">破棄</string>
<string name="welcome_interrupted_backup_alert_message">バックアップが中断されました。再開しますか?</string> <string name="welcome_interrupted_backup_alert_message">バックアップが中断されました。再開しますか?</string>
<string name="welcome_interrupted_backup_alert_resume">はい、再開します</string> <string name="welcome_interrupted_backup_alert_resume">はい、再開します</string>
@ -1688,6 +1709,7 @@
<string name="yield_module_approve_sheet_title">承認を確定する</string> <string name="yield_module_approve_sheet_title">承認を確定する</string>
<string name="yield_module_balance_info_sheet_subtitle">資産残高に関するテキスト [プレースホルダー]</string> <string name="yield_module_balance_info_sheet_subtitle">資産残高に関するテキスト [プレースホルダー]</string>
<string name="yield_module_balance_info_sheet_title">あなたの%sはAaveに預けられています</string> <string name="yield_module_balance_info_sheet_title">あなたの%sはAaveに預けられています</string>
<string name="yield_module_chart_loading_error">チャートを読み込めません・・</string>
<string name="yield_module_deposit_error_notification_title">受け取った金額%1$s %2$sはAaveに入金されませんでした。</string> <string name="yield_module_deposit_error_notification_title">受け取った金額%1$s %2$sはAaveに入金されませんでした。</string>
<string name="yield_module_earn_badge">%1$s%% を獲得</string> <string name="yield_module_earn_badge">%1$s%% を獲得</string>
<string name="yield_module_earn_sheet_available_title">利用可能</string> <string name="yield_module_earn_sheet_available_title">利用可能</string>
@ -1705,6 +1727,7 @@
<string name="yield_module_fee_policy_sheet_max_fee_title">最大手数料</string> <string name="yield_module_fee_policy_sheet_max_fee_title">最大手数料</string>
<string name="yield_module_fee_policy_sheet_title">手数料ポリシー</string> <string name="yield_module_fee_policy_sheet_title">手数料ポリシー</string>
<string name="yield_module_high_fee_error">ネットワーク手数料が現在高すぎます。設定した上限を下回るまで待機しています。</string> <string name="yield_module_high_fee_error">ネットワーク手数料が現在高すぎます。設定した上限を下回るまで待機しています。</string>
<string name="yield_module_historical_returns">過去のリターン</string>
<string name="yield_module_main_view_approve_notification_description">ここに説明を入力してください。1〜3行が理想的です。[プレースホルダー]</string> <string name="yield_module_main_view_approve_notification_description">ここに説明を入力してください。1〜3行が理想的です。[プレースホルダー]</string>
<string name="yield_module_main_view_approve_notification_title">トークン承認が必要</string> <string name="yield_module_main_view_approve_notification_title">トークン承認が必要</string>
<string name="yield_module_network_fee_unreachable_notification_description">ネットワーク接続を確認してください</string> <string name="yield_module_network_fee_unreachable_notification_description">ネットワーク接続を確認してください</string>
@ -1738,7 +1761,8 @@
<string name="yield_module_supply_apr">供給APR</string> <string name="yield_module_supply_apr">供給APR</string>
<string name="yield_module_token_details_earn_notification_apy">APY</string> <string name="yield_module_token_details_earn_notification_apy">APY</string>
<string name="yield_module_token_details_earn_notification_description">あなたの資産を眠らせない — 残高を運用して利息を得ましょう。</string> <string name="yield_module_token_details_earn_notification_description">あなたの資産を眠らせない — 残高を運用して利息を得ましょう。</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_title">保有資産から報酬を得る</string> <string name="yield_module_token_details_earn_notification_earning_on_your_balance_subtitle">利息は自動的に発生します</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_title">Aaveレンディング</string>
<string name="yield_module_token_details_earn_notification_processing">入金の処理中</string> <string name="yield_module_token_details_earn_notification_processing">入金の処理中</string>
<string name="yield_module_token_details_earn_notification_title">年間%1$s%%の収益</string> <string name="yield_module_token_details_earn_notification_title">年間%1$s%%の収益</string>
<string name="yield_module_transfer_mode_automatic">自動</string> <string name="yield_module_transfer_mode_automatic">自動</string>

View file

@ -80,7 +80,7 @@
<string name="biometric_unavailable_warning">Вы отключили биометрическую аутентификацию на вашем телефоне и не сможете сохранять кошельки в приложении. Для сохранения кошельков, пожалуйста, включите функцию биометрической аутентификации в настройках телефона.</string> <string name="biometric_unavailable_warning">Вы отключили биометрическую аутентификацию на вашем телефоне и не сможете сохранять кошельки в приложении. Для сохранения кошельков, пожалуйста, включите функцию биометрической аутентификации в настройках телефона.</string>
<string name="bitcoin_promo_activation_error">При обработке промокода произошла ошибка. Пожалуйста, попробуйте позже.</string> <string name="bitcoin_promo_activation_error">При обработке промокода произошла ошибка. Пожалуйста, попробуйте позже.</string>
<string name="bitcoin_promo_activation_error_title">Ошибка активации</string> <string name="bitcoin_promo_activation_error_title">Ошибка активации</string>
<string name="bitcoin_promo_activation_success">Ваш промокод был успешно активирован. Награда будет зачислена на ваш счёт в течение 14 дней.</string> <string name="bitcoin_promo_activation_success">Ваш промокод был успешно активирован. Награда будет зачислена на ваш Bitcoin адрес в течение 14 дней.</string>
<string name="bitcoin_promo_activation_success_title">Промокод активирован</string> <string name="bitcoin_promo_activation_success_title">Промокод активирован</string>
<string name="bitcoin_promo_already_activated">Этот промокод уже был использован и не может быть активирован повторно.</string> <string name="bitcoin_promo_already_activated">Этот промокод уже был использован и не может быть активирован повторно.</string>
<string name="bitcoin_promo_already_activated_title">Код недоступен</string> <string name="bitcoin_promo_already_activated_title">Код недоступен</string>
@ -1288,6 +1288,8 @@
<string name="user_push_notification_agreement_argument_two_subtitle">Ранний доступ к новым функциям и эксклюзивным предложениям.</string> <string name="user_push_notification_agreement_argument_two_subtitle">Ранний доступ к новым функциям и эксклюзивным предложениям.</string>
<string name="user_push_notification_agreement_argument_two_title">Новые функции и важные новости</string> <string name="user_push_notification_agreement_argument_two_title">Новые функции и важные новости</string>
<string name="user_push_notification_agreement_header">Хотите использовать Push-уведомления?</string> <string name="user_push_notification_agreement_header">Хотите использовать Push-уведомления?</string>
<string name="user_push_notification_banner_subtitle">Включите push-уведомления, и мы мгновенно сообщим вам, когда поступят средства.</string>
<string name="user_push_notification_banner_title">Не пропустите транзакцию</string>
<string name="user_wallet_list_add_button">Добавить новый кошелек</string> <string name="user_wallet_list_add_button">Добавить новый кошелек</string>
<string name="user_wallet_list_delete_prompt">Вы уверены, что хотите забыть этот кошелек?</string> <string name="user_wallet_list_delete_prompt">Вы уверены, что хотите забыть этот кошелек?</string>
<string name="user_wallet_list_error_unable_to_unlock">Произошла ошибка, пожалуйста, отсканируйте свою карту или кольцо для входа</string> <string name="user_wallet_list_error_unable_to_unlock">Произошла ошибка, пожалуйста, отсканируйте свою карту или кольцо для входа</string>

View file

@ -135,7 +135,7 @@
<string name="biometric_unavailable_warning">You have disabled biometric authentication on your phone and will not be able to save wallets in the app. To save wallets, please enable the biometric authentication function in your phone settings.</string> <string name="biometric_unavailable_warning">You have disabled biometric authentication on your phone and will not be able to save wallets in the app. To save wallets, please enable the biometric authentication function in your phone settings.</string>
<string name="bitcoin_promo_activation_error">An error occurred while processing your promo code. Please try again later.</string> <string name="bitcoin_promo_activation_error">An error occurred while processing your promo code. Please try again later.</string>
<string name="bitcoin_promo_activation_error_title">Activation error</string> <string name="bitcoin_promo_activation_error_title">Activation error</string>
<string name="bitcoin_promo_activation_success">Your promo code was successfully activated. A reward will be credited to your account within 14 days.</string> <string name="bitcoin_promo_activation_success">Your promo code was successfully activated. The reward will be credited to your Bitcoin account within 14 days.</string>
<string name="bitcoin_promo_activation_success_title">Promo Code Activated</string> <string name="bitcoin_promo_activation_success_title">Promo Code Activated</string>
<string name="bitcoin_promo_already_activated">This promo code has already been used and cannot be activated again.</string> <string name="bitcoin_promo_already_activated">This promo code has already been used and cannot be activated again.</string>
<string name="bitcoin_promo_already_activated_title">Code unavailable</string> <string name="bitcoin_promo_already_activated_title">Code unavailable</string>
@ -183,6 +183,7 @@
<string name="common_add">Add</string> <string name="common_add">Add</string>
<string name="common_add_to_portfolio">Add to portfolio</string> <string name="common_add_to_portfolio">Add to portfolio</string>
<string name="common_add_token">Add token</string> <string name="common_add_token">Add token</string>
<string name="common_added">Added</string>
<string name="common_address">Address</string> <string name="common_address">Address</string>
<string name="common_all">All</string> <string name="common_all">All</string>
<string name="common_allow">Allow</string> <string name="common_allow">Allow</string>
@ -297,6 +298,7 @@
<string name="common_range_with_space">%1$s — %2$s</string> <string name="common_range_with_space">%1$s — %2$s</string>
<string name="common_read_more">Read more</string> <string name="common_read_more">Read more</string>
<string name="common_receive">Receive</string> <string name="common_receive">Receive</string>
<string name="common_recommended">Recommended</string>
<string name="common_reject">Reject</string> <string name="common_reject">Reject</string>
<string name="common_reload">Reload</string> <string name="common_reload">Reload</string>
<string name="common_rename">Rename</string> <string name="common_rename">Rename</string>
@ -474,6 +476,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_great_rate">Great 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>
<string name="express_provider_min_amount">Available from %s</string> <string name="express_provider_min_amount">Available from %s</string>
@ -1386,9 +1389,10 @@
<string name="user_push_notification_agreement_argument_two_subtitle">Early access to fresh features and exclusive offers.</string> <string name="user_push_notification_agreement_argument_two_subtitle">Early access to fresh features and exclusive offers.</string>
<string name="user_push_notification_agreement_argument_two_title">Feature and News Updates</string> <string name="user_push_notification_agreement_argument_two_title">Feature and News Updates</string>
<string name="user_push_notification_agreement_header">Would you like to use\nPush-notifications?</string> <string name="user_push_notification_agreement_header">Would you like to use\nPush-notifications?</string>
<string name="user_push_notification_banner_subtitle">Enable push notifications and well notify you instantly when funds arrive\n</string> <string name="user_push_notification_banner_subtitle">Enable push notifications to receive alerts when funds arrive in your wallet.</string>
<string name="user_push_notification_banner_title">Dont Miss a Transaction</string> <string name="user_push_notification_banner_title">Don\'t Miss a Transaction</string>
<string name="user_wallet_list_add_button">Add new wallet</string> <string name="user_wallet_list_add_button">Add new wallet</string>
<string name="user_wallet_list_delete_hw_prompt">If you delete this wallet without a backup, you will permanently lose access to your funds</string>
<string name="user_wallet_list_delete_prompt">Are you sure you want to forget this wallet?</string> <string name="user_wallet_list_delete_prompt">Are you sure you want to forget this wallet?</string>
<string name="user_wallet_list_error_unable_to_unlock">An error has occurred, please scan your card or ring to log in</string> <string name="user_wallet_list_error_unable_to_unlock">An error has occurred, please scan your card or ring to log in</string>
<string name="user_wallet_list_error_wallet_already_saved">This wallet has already been saved, you can add another one</string> <string name="user_wallet_list_error_wallet_already_saved">This wallet has already been saved, you can add another one</string>
@ -1465,6 +1469,13 @@
<string name="visa_unlock_notification_button">Unlock</string> <string name="visa_unlock_notification_button">Unlock</string>
<string name="visa_unlock_notification_subtitle">Scan your card to unlock access</string> <string name="visa_unlock_notification_subtitle">Scan your card to unlock access</string>
<string name="visa_unlock_notification_title">Needed unlock</string> <string name="visa_unlock_notification_title">Needed unlock</string>
<string name="wallet_add_common_title">Choose how to add your wallet</string>
<string name="wallet_add_hardware_description">Scan your Tangem card or ring to restore it or import from another wallet.</string>
<string name="wallet_add_hardware_info_create">Create Hardware Wallet</string>
<string name="wallet_add_hardware_purchase">Want to purchase a Tangem Wallet?</string>
<string name="wallet_add_import_seed_phrase">Import seed phrase</string>
<string name="wallet_add_mobile_description">Restore your wallet on your phone or import from another app — convenient, but less secure than a Tangem card.</string>
<string name="wallet_add_support_title">What to choose?</string>
<string name="wallet_balance_blockchain_unreachable_try_later">Blockchain is unreachable. Try later</string> <string name="wallet_balance_blockchain_unreachable_try_later">Blockchain is unreachable. Try later</string>
<string name="wallet_balance_missing_derivation">Scan card or ring</string> <string name="wallet_balance_missing_derivation">Scan card or ring</string>
<string name="wallet_been_activated_message">This wallet has already been activated earlier.\nIf it was not done by you, please contact support.\nTangem never sells wallets along with pre-generated access codes.</string> <string name="wallet_been_activated_message">This wallet has already been activated earlier.\nIf it was not done by you, please contact support.\nTangem never sells wallets along with pre-generated access codes.</string>
@ -1504,6 +1515,7 @@
<string name="wallet_connect_subtitle">Connect to dApps</string> <string name="wallet_connect_subtitle">Connect to dApps</string>
<string name="wallet_connect_title">WalletConnect</string> <string name="wallet_connect_title">WalletConnect</string>
<string name="wallet_connect_toast_awaiting_session_proposal">Connecting may take a few seconds</string> <string name="wallet_connect_toast_awaiting_session_proposal">Connecting may take a few seconds</string>
<string name="wallet_create_common_title">Create Tangem Wallet</string>
<string name="wallet_create_hardware_badge">From %s</string> <string name="wallet_create_hardware_badge">From %s</string>
<string name="wallet_create_hardware_description">Buy Tangem Wallet—a physical device that securely stores your private key offline.</string> <string name="wallet_create_hardware_description">Buy Tangem Wallet—a physical device that securely stores your private key offline.</string>
<string name="wallet_create_hardware_title">Hardware Wallet</string> <string name="wallet_create_hardware_title">Hardware Wallet</string>
@ -1785,7 +1797,10 @@
<string name="yield_module_fee_policy_sheet_description">All future %s top-ups will be supplied to Aave automatically, with the transaction fee deducted.</string> <string name="yield_module_fee_policy_sheet_description">All future %s top-ups will be supplied to Aave automatically, with the transaction fee deducted.</string>
<string name="yield_module_fee_policy_sheet_max_fee_note">If network fees rise above maximum fee, the transaction wont go through until they decrease. You can change this limit later.</string> <string name="yield_module_fee_policy_sheet_max_fee_note">If network fees rise above maximum fee, the transaction wont go through until they decrease. You can change this limit later.</string>
<string name="yield_module_fee_policy_sheet_max_fee_title">Maximum fee</string> <string name="yield_module_fee_policy_sheet_max_fee_title">Maximum fee</string>
<string name="yield_module_fee_policy_sheet_min_amount_note">The transaction fee must stay below 4% of your deposit. Tangem will transfer funds to Aave only once your balance is large enough to meet this condition. </string>
<string name="yield_module_fee_policy_sheet_min_amount_title">Minimal amount</string>
<string name="yield_module_fee_policy_sheet_title">Fee policy</string> <string name="yield_module_fee_policy_sheet_title">Fee policy</string>
<string name="yield_module_fee_policy_tangem_service_fee_title">Tangem also takes a 3% service fee on the yield earned.</string>
<string name="yield_module_high_fee_error">Network fee is too high right now. Waiting until it falls below your limit.</string> <string name="yield_module_high_fee_error">Network fee is too high right now. Waiting until it falls below your limit.</string>
<string name="yield_module_historical_returns">Historical returns</string> <string name="yield_module_historical_returns">Historical returns</string>
<string name="yield_module_main_view_approve_notification_description">Write description here. In one, two or three lines will be awesome. [PLACEHOLDER]</string> <string name="yield_module_main_view_approve_notification_description">Write description here. In one, two or three lines will be awesome. [PLACEHOLDER]</string>
@ -1821,7 +1836,8 @@
<string name="yield_module_supply_apr">Supply APR</string> <string name="yield_module_supply_apr">Supply APR</string>
<string name="yield_module_token_details_earn_notification_apy">APY</string> <string name="yield_module_token_details_earn_notification_apy">APY</string>
<string name="yield_module_token_details_earn_notification_description">Make your money work — earn interest on your balance.</string> <string name="yield_module_token_details_earn_notification_description">Make your money work — earn interest on your balance.</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_title">Earning on your balance</string> <string name="yield_module_token_details_earn_notification_earning_on_your_balance_subtitle">Interest accrues automatically</string>
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_title">Aave lending</string>
<string name="yield_module_token_details_earn_notification_processing">Processing your deposit</string> <string name="yield_module_token_details_earn_notification_processing">Processing your deposit</string>
<string name="yield_module_token_details_earn_notification_title">Earn %1$s%% per year</string> <string name="yield_module_token_details_earn_notification_title">Earn %1$s%% per year</string>
<string name="yield_module_transfer_mode_automatic">Automatic</string> <string name="yield_module_transfer_mode_automatic">Automatic</string>

View file

@ -1,198 +0,0 @@
package com.tangem.domain.account.status.producer
import com.google.common.truth.Truth
import com.tangem.common.test.utils.getEmittedValues
import com.tangem.domain.account.models.AccountStatusList
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
@Suppress("UnusedFlow")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class DefaultMultiAccountStatusListProducerTest {
private val userWalletsListRepository: UserWalletsListRepository = mockk()
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier = mockk()
private val dispatchers = TestingCoroutineDispatcherProvider()
private val userWalletId1 = UserWalletId("001")
private val userWallet1 = mockk<UserWallet> {
every { walletId } returns userWalletId1
}
private val userWalletId2 = UserWalletId("002")
private val userWallet2 = mockk<UserWallet> {
every { walletId } returns userWalletId2
}
private val producer = DefaultMultiAccountStatusListProducer(
params = Unit,
userWalletsListRepository = userWalletsListRepository,
singleAccountStatusListSupplier = singleAccountStatusListSupplier,
dispatchers = dispatchers,
)
@AfterEach
fun tearDown() {
clearMocks(userWalletsListRepository, singleAccountStatusListSupplier)
}
@Test
fun `produce returns status lists for all user wallets`() = runTest {
// Arrange
val wallets = listOf(userWallet1, userWallet2)
val walletsFlow = MutableStateFlow(wallets)
every { userWalletsListRepository.userWallets } returns walletsFlow
val accountStatusList1 = mockk<AccountStatusList>()
val accountStatusList2 = mockk<AccountStatusList>()
every {
singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWalletId1),
)
} returns flowOf(accountStatusList1)
every {
singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWalletId2),
)
} returns flowOf(accountStatusList2)
// Act
val actual = producer.produce().let(::getEmittedValues)
// Assert
val expected = listOf(accountStatusList1, accountStatusList2)
Truth.assertThat(actual).containsExactly(expected)
coVerify(ordering = Ordering.SEQUENCE) {
userWalletsListRepository.userWallets
singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWalletId1),
)
singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWalletId2),
)
}
}
@Test
fun `produce returns empty flow if userWallets is empty list`() = runTest {
// Arrange
val walletsFlow = MutableStateFlow<List<UserWallet>>(emptyList())
every { userWalletsListRepository.userWallets } returns walletsFlow
// Act
val actual = producer.produce().let(::getEmittedValues)
// Assert
Truth.assertThat(actual).isEmpty()
coVerify(ordering = Ordering.SEQUENCE) {
userWalletsListRepository.userWallets
}
}
@Test
fun `produce returns empty flow if userWallets is null`() = runTest {
// Arrange
val walletsFlow = MutableStateFlow<List<UserWallet>?>(null)
every { userWalletsListRepository.userWallets } returns walletsFlow
// Act
val actual = producer.produce().let(::getEmittedValues)
// Assert
Truth.assertThat(actual).isEmpty()
coVerify(ordering = Ordering.SEQUENCE) {
userWalletsListRepository.userWallets
}
}
@Test
fun `flow will updated if userWallets are updated`() = runTest {
// Arrange
val userWalletId3 = UserWalletId("003")
val userWallet3 = mockk<UserWallet> { every { walletId } returns userWalletId3 }
val walletsFlow = MutableStateFlow(listOf(userWallet1, userWallet2))
every { userWalletsListRepository.userWallets } returns walletsFlow
val accountStatusList1 = mockk<AccountStatusList>()
val accountStatusList2 = mockk<AccountStatusList>()
val accountStatusList3 = mockk<AccountStatusList>()
every {
singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWalletId1),
)
} returns flowOf(accountStatusList1)
every {
singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWalletId2),
)
} returns flowOf(accountStatusList2)
every {
singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWalletId3),
)
} returns flowOf(accountStatusList3)
// Act (first emission)
val actual1 = producer.produce().let(::getEmittedValues)
// Assert (first emission)
val expected1 = listOf(accountStatusList1, accountStatusList2)
Truth.assertThat(actual1).containsExactly(expected1)
// Act (second emission)
walletsFlow.value = listOf(userWallet1, userWallet2, userWallet3)
val actual2 = producer.produce().let(::getEmittedValues)
// Assert (second emission)
val expected2 = listOf(accountStatusList1, accountStatusList2, accountStatusList3)
Truth.assertThat(actual2).containsExactly(expected2)
coVerify(ordering = Ordering.SEQUENCE) {
userWalletsListRepository.userWallets
singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWalletId1),
)
singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWalletId2),
)
singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWalletId1),
)
singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWalletId2),
)
singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWalletId3),
)
userWalletsListRepository.userWallets
singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWalletId1),
)
singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWalletId2),
)
singleAccountStatusListSupplier(
params = SingleAccountStatusListProducer.Params(userWalletId3),
)
}
}
}

View file

@ -1,244 +0,0 @@
package com.tangem.domain.account.status.producer
import arrow.core.nonEmptyListOf
import com.google.common.truth.Truth
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
import com.tangem.common.test.utils.getEmittedValues
import com.tangem.domain.account.models.AccountList
import com.tangem.domain.account.models.AccountStatusList
import com.tangem.domain.account.producer.SingleAccountListProducer
import com.tangem.domain.account.status.utils.CryptoCurrencyStatusesFlowFactory
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.core.utils.lceContent
import com.tangem.domain.core.utils.lceLoading
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.TokensSortType
import com.tangem.domain.models.TotalFiatBalance
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.quote.PriceChange
import com.tangem.domain.models.tokenlist.TokenList
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
import io.mockk.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import java.math.BigDecimal
/**
[REDACTED_AUTHOR]
*/
@Suppress("UnusedFlow")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class DefaultSingleAccountStatusListProducerTest {
private val singleAccountListSupplier: SingleAccountListSupplier = mockk()
private val cryptoCurrencyStatusesFlowFactory: CryptoCurrencyStatusesFlowFactory = mockk()
private val userWalletId = UserWalletId("011")
private val userWallet = mockk<UserWallet> {
every { this@mockk.walletId } returns userWalletId
}
private val producer = DefaultSingleAccountStatusListProducer(
params = SingleAccountStatusListProducer.Params(userWalletId),
singleAccountListSupplier = singleAccountListSupplier,
cryptoCurrencyStatusesFlowFactory = cryptoCurrencyStatusesFlowFactory,
dispatchers = TestingCoroutineDispatcherProvider(),
)
@AfterEach
fun tearDown() {
clearMocks(singleAccountListSupplier, cryptoCurrencyStatusesFlowFactory)
}
@Test
fun `flow is mapped for user wallet id from params`() = runTest {
// Arrange
val accountList = AccountList.empty(userWallet)
every {
singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
} returns flowOf(accountList)
// Act
val actual = producer.produce().let(::getEmittedValues)
// Assert
val expected = AccountStatusList(
userWallet = userWallet,
accountStatuses = setOf(
AccountStatus.CryptoPortfolio(
account = accountList.mainAccount,
tokenList = TokenList.Empty,
priceChangeLce = PriceChange(value = BigDecimal("0.00"), source = StatusSource.ACTUAL).lceContent(),
),
),
totalAccounts = 1,
totalFiatBalance = TotalFiatBalance.Loaded(amount = BigDecimal.ZERO, source = StatusSource.ACTUAL),
)
Truth.assertThat(actual).containsExactly(expected)
coVerify(ordering = Ordering.SEQUENCE) {
singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
}
}
@Test
fun `flow will updated if balances are updated`() = runTest {
// Arrange
val accountList = AccountList.empty(userWallet)
val updatedAccountList = AccountList.empty(userWallet = userWallet, sortType = TokensSortType.BALANCE)
val accountListFlow = MutableStateFlow(value = accountList)
every {
singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
} returns accountListFlow
// Act (first emission)
val actual1 = producer.produce().let(::getEmittedValues)
// Assert (first emission)
val expected = AccountStatusList(
userWallet = userWallet,
accountStatuses = setOf(
AccountStatus.CryptoPortfolio(
account = accountList.mainAccount,
tokenList = TokenList.Empty,
priceChangeLce = PriceChange(value = BigDecimal("0.00"), source = StatusSource.ACTUAL).lceContent(),
),
),
totalAccounts = 1,
totalFiatBalance = TotalFiatBalance.Loaded(amount = BigDecimal.ZERO, source = StatusSource.ACTUAL),
)
Truth.assertThat(actual1).containsExactly(expected)
// Act (second emission)
accountListFlow.value = updatedAccountList
val actual2 = producer.produce().let(::getEmittedValues)
// Assert (second emission)
val expected2 = AccountStatusList(
userWallet = userWallet,
accountStatuses = setOf(
AccountStatus.CryptoPortfolio(
account = updatedAccountList.mainAccount,
tokenList = TokenList.Empty,
priceChangeLce = PriceChange(value = BigDecimal("0.00"), source = StatusSource.ACTUAL).lceContent(),
),
),
totalAccounts = 1,
totalFiatBalance = TotalFiatBalance.Loaded(amount = BigDecimal.ZERO, source = StatusSource.ACTUAL),
)
Truth.assertThat(actual2).containsExactly(expected2)
coVerify(ordering = Ordering.SEQUENCE) {
singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
}
}
@Test
fun `flow is filtered the same balance`() = runTest {
// Arrange
val accountList = AccountList.empty(userWallet)
val accountListFlow = MutableStateFlow(value = accountList)
every {
singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
} returns accountListFlow
val expected = AccountStatusList(
userWallet = userWallet,
accountStatuses = setOf(
AccountStatus.CryptoPortfolio(
account = accountList.mainAccount,
tokenList = TokenList.Empty,
priceChangeLce = PriceChange(value = BigDecimal("0.00"), source = StatusSource.ACTUAL).lceContent(),
),
),
totalAccounts = 1,
totalFiatBalance = TotalFiatBalance.Loaded(amount = BigDecimal.ZERO, source = StatusSource.ACTUAL),
)
// Act (first emission)
val actual1 = producer.produce().let(::getEmittedValues)
// Assert (first emission)
Truth.assertThat(actual1).containsExactly(expected)
// Act (second emission)
accountListFlow.value = accountList
val actual2 = producer.produce().let(::getEmittedValues)
// Assert (second emission)
Truth.assertThat(actual2).containsExactly(expected)
coVerify(ordering = Ordering.SEQUENCE) {
singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
}
}
@Test
fun `flow is produced for account with non empty crypto currencies`() = runTest {
// Arrange
val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
val accountList = AccountList.empty(
userWallet = userWallet,
cryptoCurrencies = cryptoCurrencyFactory.ethereumAndStellar.toSet(),
)
every {
singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
} returns flowOf(accountList)
val ethereumStatus = CryptoCurrencyStatus(
currency = cryptoCurrencyFactory.ethereum,
value = CryptoCurrencyStatus.Loading,
)
every {
cryptoCurrencyStatusesFlowFactory.create(userWallet = userWallet, currency = cryptoCurrencyFactory.ethereum)
} returns flowOf(ethereumStatus)
val stellarStatus = CryptoCurrencyStatus(
currency = cryptoCurrencyFactory.stellar,
value = CryptoCurrencyStatus.MissedDerivation(priceChange = null, fiatRate = null),
)
every {
cryptoCurrencyStatusesFlowFactory.create(userWallet = userWallet, currency = cryptoCurrencyFactory.stellar)
} returns flowOf(stellarStatus)
// Act
val actual = producer.produce().let(::getEmittedValues)
// Assert
val expected = AccountStatusList(
userWallet = userWallet,
accountStatuses = setOf(
AccountStatus.CryptoPortfolio(
account = accountList.mainAccount,
tokenList = TokenList.Ungrouped(
totalFiatBalance = TotalFiatBalance.Loading,
sortedBy = TokensSortType.NONE,
currencies = nonEmptyListOf(ethereumStatus, stellarStatus),
),
priceChangeLce = lceLoading(),
),
),
totalAccounts = 1,
totalFiatBalance = TotalFiatBalance.Loading,
)
Truth.assertThat(actual).containsExactly(expected)
coVerify(ordering = Ordering.SEQUENCE) {
singleAccountListSupplier(params = SingleAccountListProducer.Params(userWalletId))
}
}
}

View file

@ -1,308 +0,0 @@
package com.tangem.domain.account.status.utils
import arrow.core.left
import arrow.core.right
import com.google.common.truth.Truth
import com.tangem.common.test.domain.token.MockCryptoCurrencyFactory
import com.tangem.common.test.utils.getEmittedValues
import com.tangem.domain.models.StatusSource
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.models.network.NetworkStatus
import com.tangem.domain.models.quote.QuoteStatus
import com.tangem.domain.models.staking.StakingID
import com.tangem.domain.models.staking.YieldBalance
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.networks.single.SingleNetworkStatusProducer
import com.tangem.domain.networks.single.SingleNetworkStatusSupplier
import com.tangem.domain.quotes.single.SingleQuoteStatusProducer
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
import com.tangem.domain.staking.StakingIdFactory
import com.tangem.domain.staking.single.SingleYieldBalanceProducer
import com.tangem.domain.staking.single.SingleYieldBalanceSupplier
import io.mockk.*
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import java.math.BigDecimal
/**
[REDACTED_AUTHOR]
*/
@Suppress("UnusedFlow")
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class CryptoCurrencyStatusesFlowFactoryTest {
private val singleNetworkStatusSupplier: SingleNetworkStatusSupplier = mockk()
private val singleQuoteStatusSupplier: SingleQuoteStatusSupplier = mockk()
private val singleYieldBalanceSupplier: SingleYieldBalanceSupplier = mockk()
private val stakingIdFactory: StakingIdFactory = mockk()
private val factory = CryptoCurrencyStatusesFlowFactory(
singleNetworkStatusSupplier = singleNetworkStatusSupplier,
singleQuoteStatusSupplier = singleQuoteStatusSupplier,
singleYieldBalanceSupplier = singleYieldBalanceSupplier,
stakingIdFactory = stakingIdFactory,
)
private val userWalletId = UserWalletId("011")
private val cryptoCurrencyFactory = MockCryptoCurrencyFactory()
private val networkAddress = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "0x1", type = NetworkAddress.Address.Type.Primary),
)
@AfterEach
fun tearDown() {
clearMocks(
singleNetworkStatusSupplier,
singleQuoteStatusSupplier,
singleYieldBalanceSupplier,
stakingIdFactory,
)
}
@Test
fun `if rawCurrencyId is null, there will be no subscription to the quote status`() = runTest {
// Arrange
val userWallet = mockk<UserWallet> {
every { this@mockk.walletId } returns userWalletId
every { this@mockk.isMultiCurrency } returns true
}
val currency = cryptoCurrencyFactory.ethereum.copy(
id = cryptoCurrencyFactory.ethereum.id.copy(
suffix = CryptoCurrency.ID.Suffix.ContractAddress(contractAddress = "0x12345"),
),
)
val networkStatus = NetworkStatus(
network = currency.network,
value = NetworkStatus.Unreachable(address = networkAddress),
)
val networkStatusFlow = flowOf(networkStatus)
every {
singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
} returns networkStatusFlow
val stakingId = StakingID(integrationId = "id", address = networkAddress.defaultAddress.value)
coEvery {
stakingIdFactory.create(currencyId = currency.id, defaultAddress = networkAddress.defaultAddress.value)
} returns stakingId.right()
val yieldBalance = YieldBalance.Empty(stakingId = stakingId, source = StatusSource.ACTUAL)
val yieldBalanceFlow = flowOf(yieldBalance)
every {
singleYieldBalanceSupplier(
params = SingleYieldBalanceProducer.Params(userWalletId = userWalletId, stakingId = stakingId),
)
} returns yieldBalanceFlow
// Act
val actual = factory.create(userWallet = userWallet, currency = currency).let(::getEmittedValues)
// Assert
val expected = CryptoCurrencyStatus(
currency = currency,
value = CryptoCurrencyStatus.Unreachable(
priceChange = null,
fiatRate = null,
networkAddress = networkAddress,
),
)
Truth.assertThat(actual).containsExactly(expected)
coVerify(ordering = Ordering.SEQUENCE) {
singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
stakingIdFactory.create(currencyId = currency.id, defaultAddress = networkAddress.defaultAddress.value)
singleYieldBalanceSupplier(params = SingleYieldBalanceProducer.Params(userWalletId, stakingId))
}
}
@Test
fun `if userWallet is not multi-currency, there will be no subscription to the yield balance`() = runTest {
// Arrange
val userWallet = mockk<UserWallet> {
every { this@mockk.walletId } returns userWalletId
every { this@mockk.isMultiCurrency } returns false
}
val currency = cryptoCurrencyFactory.ethereum
val networkStatus = NetworkStatus(
network = currency.network,
value = NetworkStatus.Unreachable(address = networkAddress),
)
val networkStatusFlow = flowOf(networkStatus)
every {
singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
} returns networkStatusFlow
val quoteStatus = QuoteStatus(
rawCurrencyId = currency.id.rawCurrencyId!!,
value = QuoteStatus.Data(
source = StatusSource.ACTUAL,
fiatRate = BigDecimal.ONE,
priceChange = BigDecimal.ONE,
),
)
val quoteStatusFlow = flowOf(quoteStatus)
every {
singleQuoteStatusSupplier(params = SingleQuoteStatusProducer.Params(currency.id.rawCurrencyId!!))
} returns quoteStatusFlow
// Act
val actual = factory.create(userWallet = userWallet, currency = currency).let(::getEmittedValues)
// Assert
val expected = CryptoCurrencyStatus(
currency = currency,
value = CryptoCurrencyStatus.Unreachable(
priceChange = BigDecimal.ONE,
fiatRate = BigDecimal.ONE,
networkAddress = networkAddress,
),
)
Truth.assertThat(actual).containsExactly(expected)
coVerify(ordering = Ordering.SEQUENCE) {
singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
singleQuoteStatusSupplier(params = SingleQuoteStatusProducer.Params(currency.id.rawCurrencyId!!))
}
}
@Test
fun `no subscription to the quote status and yield balance`() = runTest {
// Arrange
val userWallet = mockk<UserWallet> {
every { this@mockk.walletId } returns userWalletId
every { this@mockk.isMultiCurrency } returns false
}
val currency = cryptoCurrencyFactory.ethereum.copy(
id = cryptoCurrencyFactory.ethereum.id.copy(
suffix = CryptoCurrency.ID.Suffix.ContractAddress(contractAddress = "0x12345"),
),
)
val networkStatus = NetworkStatus(
network = currency.network,
value = NetworkStatus.Unreachable(address = networkAddress),
)
val networkStatusFlow = flowOf(networkStatus)
every {
singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
} returns networkStatusFlow
// Act
val actual = factory.create(userWallet = userWallet, currency = currency).let(::getEmittedValues)
// Assert
val expected = CryptoCurrencyStatus(
currency = currency,
value = CryptoCurrencyStatus.Unreachable(
priceChange = null,
fiatRate = null,
networkAddress = networkAddress,
),
)
Truth.assertThat(actual).containsExactly(expected)
coVerify(ordering = Ordering.SEQUENCE) {
singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
}
}
@Test
fun `if stakingId is not supported, yield balance will be null`() = runTest {
// Arrange
val userWallet = mockk<UserWallet> {
every { this@mockk.walletId } returns userWalletId
every { this@mockk.isMultiCurrency } returns true
}
val currency = cryptoCurrencyFactory.ethereum
val networkStatus = NetworkStatus(
network = currency.network,
value = NetworkStatus.Unreachable(address = networkAddress),
)
val networkStatusFlow = flowOf(networkStatus)
every {
singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
} returns networkStatusFlow
val quoteStatus = QuoteStatus(
rawCurrencyId = currency.id.rawCurrencyId!!,
value = QuoteStatus.Data(
source = StatusSource.ACTUAL,
fiatRate = BigDecimal.ONE,
priceChange = BigDecimal.ONE,
),
)
val quoteStatusFlow = flowOf(quoteStatus)
every {
singleQuoteStatusSupplier(params = SingleQuoteStatusProducer.Params(currency.id.rawCurrencyId!!))
} returns quoteStatusFlow
coEvery {
stakingIdFactory.create(currencyId = currency.id, defaultAddress = networkAddress.defaultAddress.value)
} returns StakingIdFactory.Error.UnsupportedCurrency.left()
// Act
val actual = factory.create(userWallet = userWallet, currency = currency).let(::getEmittedValues)
// Assert
val expected = CryptoCurrencyStatus(
currency = currency,
value = CryptoCurrencyStatus.Unreachable(
priceChange = BigDecimal.ONE,
fiatRate = BigDecimal.ONE,
networkAddress = networkAddress,
),
)
Truth.assertThat(actual).containsExactly(expected)
coVerify(ordering = Ordering.SEQUENCE) {
singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
singleQuoteStatusSupplier(params = SingleQuoteStatusProducer.Params(currency.id.rawCurrencyId!!))
}
}
@Test
fun `all sources are empty`() = runTest {
// Arrange
val userWallet = mockk<UserWallet> {
every { this@mockk.walletId } returns userWalletId
every { this@mockk.isMultiCurrency } returns true
}
val currency = cryptoCurrencyFactory.ethereum
every {
singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
} returns emptyFlow()
every {
singleQuoteStatusSupplier(params = SingleQuoteStatusProducer.Params(currency.id.rawCurrencyId!!))
} returns emptyFlow()
// Act
val actual = factory.create(userWallet = userWallet, currency = currency).let(::getEmittedValues)
// Assert
val expected = CryptoCurrencyStatus(currency = currency, value = CryptoCurrencyStatus.Loading)
Truth.assertThat(actual).containsExactly(expected)
coVerify(ordering = Ordering.SEQUENCE) {
singleNetworkStatusSupplier(params = SingleNetworkStatusProducer.Params(userWalletId, currency.network))
singleQuoteStatusSupplier(params = SingleQuoteStatusProducer.Params(currency.id.rawCurrencyId!!))
}
}
}

View file

@ -69,4 +69,8 @@ internal interface StakingClickIntents : AmountScreenClickIntents {
fun showPrimaryClickAlert() fun showPrimaryClickAlert()
fun onOpenLearnMoreAboutApproveClick() fun onOpenLearnMoreAboutApproveClick()
fun onActivateTonAccountNotificationClick()
fun onActivateTonAccountClick()
} }

View file

@ -2,6 +2,7 @@ package com.tangem.features.staking.impl.presentation.model
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import arrow.core.getOrElse import arrow.core.getOrElse
import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.routing.AppRouter import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
@ -48,8 +49,11 @@ import com.tangem.domain.staking.utils.getValidatorsCount
import com.tangem.domain.tokens.* import com.tangem.domain.tokens.*
import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.usecase.CreateApprovalTransactionUseCase import com.tangem.domain.transaction.usecase.CreateApprovalTransactionUseCase
import com.tangem.domain.transaction.usecase.CreateTransferTransactionUseCase
import com.tangem.domain.transaction.usecase.GetAllowanceUseCase import com.tangem.domain.transaction.usecase.GetAllowanceUseCase
import com.tangem.domain.transaction.usecase.GetFeeUseCase
import com.tangem.domain.transaction.usecase.SendTransactionUseCase import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.staking.api.StakingComponent import com.tangem.features.staking.api.StakingComponent
import com.tangem.features.staking.impl.analytics.StakingParamsInterceptor import com.tangem.features.staking.impl.analytics.StakingParamsInterceptor
@ -69,10 +73,15 @@ import com.tangem.features.staking.impl.presentation.state.transformers.approval
import com.tangem.features.staking.impl.presentation.state.transformers.confirmation.SetUpdatedAllowanceTransformer import com.tangem.features.staking.impl.presentation.state.transformers.confirmation.SetUpdatedAllowanceTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.notifications.AddStakingNotificationsTransformer import com.tangem.features.staking.impl.presentation.state.transformers.notifications.AddStakingNotificationsTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.notifications.DismissStakingNotificationsStateTransformer import com.tangem.features.staking.impl.presentation.state.transformers.notifications.DismissStakingNotificationsStateTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.ton.CompleteInitializeBottomSheetTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.ton.SetFeeToTonInitializeBottomSheetTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.ton.ShowTonInitializeBottomSheetTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.ton.SetFeeErrorToTonInitializeBottomSheetTransformer
import com.tangem.features.staking.impl.presentation.state.transformers.validator.ValidatorSelectChangeTransformer import com.tangem.features.staking.impl.presentation.state.transformers.validator.ValidatorSelectChangeTransformer
import com.tangem.features.staking.impl.presentation.state.utils.checkAndCalculateSubtractedAmount import com.tangem.features.staking.impl.presentation.state.utils.checkAndCalculateSubtractedAmount
import com.tangem.features.staking.impl.presentation.state.utils.isSingleAction import com.tangem.features.staking.impl.presentation.state.utils.isSingleAction
import com.tangem.features.staking.impl.presentation.state.utils.withStubUnstakeAction import com.tangem.features.staking.impl.presentation.state.utils.withStubUnstakeAction
import com.tangem.lib.crypto.BlockchainUtils.isTon
import com.tangem.utils.Provider import com.tangem.utils.Provider
import com.tangem.utils.TangemBlogUrlBuilder.RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP import com.tangem.utils.TangemBlogUrlBuilder.RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP
import com.tangem.utils.coroutines.* import com.tangem.utils.coroutines.*
@ -122,6 +131,9 @@ internal class StakingModel @Inject constructor(
private val getActionsUseCase: GetActionsUseCase, private val getActionsUseCase: GetActionsUseCase,
private val getYieldUseCase: GetYieldUseCase, private val getYieldUseCase: GetYieldUseCase,
private val checkAccountInitializedUseCase: CheckAccountInitializedUseCase, private val checkAccountInitializedUseCase: CheckAccountInitializedUseCase,
private val createTransferTransactionUseCase: CreateTransferTransactionUseCase,
private val getFeeUseCase: GetFeeUseCase,
private val getNetworkAddressesUseCase: GetNetworkAddressesUseCase,
private val getActionRequirementAmountUseCase: GetActionRequirementAmountUseCase, private val getActionRequirementAmountUseCase: GetActionRequirementAmountUseCase,
private val paramsInterceptorHolder: ParamsInterceptorHolder, private val paramsInterceptorHolder: ParamsInterceptorHolder,
private val shareManager: ShareManager, private val shareManager: ShareManager,
@ -150,11 +162,14 @@ internal class StakingModel @Inject constructor(
} }
} }
private var isAccountInitialized: Boolean = true
private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull() private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
private var stakingActions: List<StakingAction> = emptyList() private var stakingActions: List<StakingAction> = emptyList()
private var feeCryptoCurrencyStatus: CryptoCurrencyStatus? = null private var feeCryptoCurrencyStatus: CryptoCurrencyStatus? = null
private var minimumTransactionAmount: EnterAmountBoundary? = null private var minimumTransactionAmount: EnterAmountBoundary? = null
private var tonAccountInitializeTransaction: TransactionData.Uncompiled? = null
private val userWallet by lazy { private val userWallet by lazy {
requireNotNull( requireNotNull(
getUserWalletUseCase(userWalletId).getOrNull(), getUserWalletUseCase(userWalletId).getOrNull(),
@ -250,26 +265,12 @@ internal class StakingModel @Inject constructor(
val isInitialInfoStep = value.currentStep == StakingStep.InitialInfo val isInitialInfoStep = value.currentStep == StakingStep.InitialInfo
val noBalanceState = balanceState == null val noBalanceState = balanceState == null
val noYieldBalanceData = cryptoCurrencyStatus.value.yieldBalance !is YieldBalance.Data val noYieldBalanceData = cryptoCurrencyStatus.value.yieldBalance !is YieldBalance.Data
val isAccountInitialized = checkAccountInitializedUseCase.invoke(
userWalletId = userWalletId,
network = cryptoCurrencyStatus.currency.network,
).getOrElse {
Timber.e(it)
false
}
when { when {
isInitialInfoStep && noBalanceState && yield.allValidatorsFull && noYieldBalanceData -> { isInitialInfoStep && noBalanceState && yield.allValidatorsFull && noYieldBalanceData -> {
stakingEventFactory.createStakingValidatorsUnavailableAlert() stakingEventFactory.createStakingValidatorsUnavailableAlert()
return@launch return@launch
} }
isInitialInfoStep && noBalanceState && !isAccountInitialized -> {
analyticsEventHandler.send(StakingAnalyticsEvent.UnitializedAddress(
token = cryptoCurrencyStatus.currency.symbol,
))
stakingEventFactory.createInitializeAccountAlert()
return@launch
}
isInitialInfoStep && noBalanceState -> { isInitialInfoStep && noBalanceState -> {
val list = buildList { val list = buildList {
SetConfirmationStateInitTransformer( SetConfirmationStateInitTransformer(
@ -763,6 +764,7 @@ internal class StakingModel @Inject constructor(
AddStakingNotificationsTransformer( AddStakingNotificationsTransformer(
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
appCurrencyProvider = Provider { appCurrency }, appCurrencyProvider = Provider { appCurrency },
isAccountInitializedProvider = Provider { isAccountInitialized },
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus, feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
currencyWarning = currencyWarning, currencyWarning = currencyWarning,
currencyCheck = currencyStatus, currencyCheck = currencyStatus,
@ -934,6 +936,85 @@ internal class StakingModel @Inject constructor(
urlOpener.openUrl(RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP) urlOpener.openUrl(RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP)
} }
override fun onActivateTonAccountNotificationClick() {
modelScope.launch {
stateController.update(
ShowTonInitializeBottomSheetTransformer(
onDismiss = { stateController.update(DismissBottomSheetStateTransformer) },
),
)
val tonAmount = BigDecimal(1)
val destination = getNetworkAddressesUseCase.invokeSync(
userWalletId = userWalletId,
network = cryptoCurrencyStatus.currency.network,
).getOrNull(0)?.address ?: return@launch
val transaction = createTransferTransactionUseCase(
amount = tonAmount.convertToSdkAmount(cryptoCurrencyStatus),
destination = destination,
userWalletId = userWalletId,
network = cryptoCurrencyStatus.currency.network,
memo = null,
)
tonAccountInitializeTransaction = transaction.getOrElse {
stateController.update(
SetFeeErrorToTonInitializeBottomSheetTransformer(),
)
return@launch
}
val transactionFee = getFeeUseCase(
userWallet = userWallet,
network = cryptoCurrencyStatus.currency.network,
transactionData = tonAccountInitializeTransaction!!,
)
transactionFee.fold(
ifLeft = {
stateController.update(
SetFeeErrorToTonInitializeBottomSheetTransformer(),
)
},
ifRight = {
stateController.update(
SetFeeToTonInitializeBottomSheetTransformer(
appCurrencyProvider = Provider { appCurrency },
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
fee = it.normal,
isFeeApproximate = false,
),
)
},
)
}
}
override fun onActivateTonAccountClick() {
modelScope.launch {
tonAccountInitializeTransaction?.let { transaction ->
sendTransactionUseCase.invoke(
txData = transaction,
userWallet = userWallet,
network = cryptoCurrencyStatus.currency.network,
).fold(
ifLeft = {
stateController.update(DismissBottomSheetStateTransformer)
},
ifRight = {
stateController.update(CompleteInitializeBottomSheetTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatus,
minimumTransactionAmount = minimumTransactionAmount,
))
balanceUpdater.partialUpdateWithDelay()
stateController.update(DismissBottomSheetStateTransformer)
},
)
}
}
}
private suspend fun setupApprovalNeeded() { private suspend fun setupApprovalNeeded() {
val approval = StakingIntegrationID.create(currencyId = cryptoCurrencyStatus.currency.id)?.approval val approval = StakingIntegrationID.create(currencyId = cryptoCurrencyStatus.currency.id)?.approval
?: StakingApproval.Empty ?: StakingApproval.Empty
@ -961,7 +1042,9 @@ internal class StakingModel @Inject constructor(
) )
.conflate() .conflate()
.distinctUntilChanged() .distinctUntilChanged()
.filter { value.currentStep == StakingStep.InitialInfo } .filter {
value.currentStep == StakingStep.InitialInfo || isCaseWithUnitializedTonAccount()
}
.onEach { maybeStatus -> .onEach { maybeStatus ->
maybeStatus.fold( maybeStatus.fold(
ifRight = { status -> ifRight = { status ->
@ -989,6 +1072,19 @@ internal class StakingModel @Inject constructor(
} }
cryptoCurrencyStatus = status cryptoCurrencyStatus = status
val isAccountInitializedNew = checkAccountInitializedUseCase.invoke(
userWalletId = userWalletId,
network = cryptoCurrencyStatus.currency.network,
).getOrElse {
Timber.e(it)
false
}
if (isAccountInitializedNew && !isAccountInitialized) {
isAccountInitialized = true
updateNotifications()
}
isAccountInitialized = isAccountInitializedNew
setupApprovalNeeded() setupApprovalNeeded()
setupIsAnyTokenStaked() setupIsAnyTokenStaked()
checkIfSubtractAvailable() checkIfSubtractAvailable()
@ -1150,6 +1246,12 @@ internal class StakingModel @Inject constructor(
.getOrElse { false } .getOrElse { false }
} }
private fun isCaseWithUnitializedTonAccount(): Boolean {
return value.currentStep == StakingStep.Confirmation &&
isTon(cryptoCurrencyStatus.currency.network.rawId) &&
!isAccountInitialized
}
private companion object { private companion object {
const val WHAT_IS_STAKING_ARTICLE_URL = "https://tangem.com/en/blog/post/how-to-stake-cryptocurrency/" const val WHAT_IS_STAKING_ARTICLE_URL = "https://tangem.com/en/blog/post/how-to-stake-cryptocurrency/"
const val ALLOWANCE_UPDATE_DELAY = 10_000L const val ALLOWANCE_UPDATE_DELAY = 10_000L

View file

@ -60,6 +60,15 @@ internal object StakingNotification {
title = resourceReference(R.string.staking_notification_low_staked_balance_title), title = resourceReference(R.string.staking_notification_low_staked_balance_title),
subtitle = resourceReference(R.string.staking_notification_low_staked_balance_text), subtitle = resourceReference(R.string.staking_notification_low_staked_balance_text),
) )
data class InitializeTonAccount(private val onInitializeClick: () -> Unit) : Info(
title = resourceReference(R.string.staking_notification_ton_account_initialization_title),
subtitle = resourceReference(R.string.staking_notification_ton_account_initialization_message),
buttonsState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
text = resourceReference(R.string.common_activate),
onClick = onInitializeClick,
),
)
} }
sealed class Info( sealed class Info(

View file

@ -0,0 +1,15 @@
package com.tangem.features.staking.impl.presentation.state.bottomsheet
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.extensions.TextReference
import com.tangem.features.staking.impl.presentation.state.FeeState
internal data class TonInitializeAccountBottomSheetConfig(
val title: TextReference,
val message: TextReference,
val footer: TextReference,
val onButtonClick: () -> Unit,
val isButtonEnabled: Boolean,
val isButtonLoading: Boolean,
val feeState: FeeState,
) : TangemBottomSheetConfigContent

View file

@ -62,13 +62,6 @@ internal sealed class StakingAlertUM : AlertUM {
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok) override val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
} }
data object InitializeAccount : StakingAlertUM() {
override val onConfirmClick: (() -> Unit)? = null
override val title: TextReference = resourceReference(id = R.string.staking_error_no_validators_title)
override val message: TextReference = resourceReference(id = R.string.staking_notification_ton_activate_account)
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
}
data class RewardsMinimumRequirementsError( data class RewardsMinimumRequirementsError(
val cryptoCurrencyName: String, val cryptoCurrencyName: String,
val cryptoAmountValue: String, val cryptoAmountValue: String,

View file

@ -47,11 +47,6 @@ internal class StakingEventFactory(
stateController.updateEvent(alert) stateController.updateEvent(alert)
} }
fun createInitializeAccountAlert() {
val alert = StakingEvent.ShowAlert(alert = StakingAlertUM.InitializeAccount)
stateController.updateEvent(alert)
}
fun createStakingRewardsMinimumRequirementsErrorAlert(cryptoCurrencyName: String, cryptoAmountValue: String) { fun createStakingRewardsMinimumRequirementsErrorAlert(cryptoCurrencyName: String, cryptoAmountValue: String) {
stateController.updateEvent( stateController.updateEvent(
StakingEvent.ShowAlert( StakingEvent.ShowAlert(

View file

@ -76,6 +76,11 @@ internal class StakingBalanceUpdater @AssistedInject constructor(
} }
} }
suspend fun partialUpdateWithDelay() {
delay(BALANCE_UPDATE_DELAY)
partialUpdate()
}
private suspend fun fetchCurrencyStatus(delayMillis: Long = 0L) { private suspend fun fetchCurrencyStatus(delayMillis: Long = 0L) {
delay(delayMillis) delay(delayMillis)
fetchCurrencyStatusUseCase( fetchCurrencyStatusUseCase(

View file

@ -78,4 +78,8 @@ internal object StakingClickIntentsStub : StakingClickIntents {
override fun showPrimaryClickAlert() {} override fun showPrimaryClickAlert() {}
override fun onOpenLearnMoreAboutApproveClick() {} override fun onOpenLearnMoreAboutApproveClick() {}
override fun onActivateTonAccountNotificationClick() {}
override fun onActivateTonAccountClick() {}
} }

View file

@ -40,6 +40,7 @@ import java.math.BigDecimal
internal class AddStakingNotificationsTransformer( internal class AddStakingNotificationsTransformer(
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>, private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
private val appCurrencyProvider: Provider<AppCurrency>, private val appCurrencyProvider: Provider<AppCurrency>,
private val isAccountInitializedProvider: Provider<Boolean>,
private val feeCryptoCurrencyStatus: CryptoCurrencyStatus?, private val feeCryptoCurrencyStatus: CryptoCurrencyStatus?,
private val currencyWarning: CryptoCurrencyWarning?, private val currencyWarning: CryptoCurrencyWarning?,
private val feeError: GetFeeError?, private val feeError: GetFeeError?,
@ -90,34 +91,40 @@ internal class AddStakingNotificationsTransformer(
BigDecimal.ZERO BigDecimal.ZERO
} }
val notifications = buildList { val notifications = if (isAccountInitializedProvider.invoke()) {
// errors buildList {
addErrorNotifications( // errors
prevState = prevState, addErrorNotifications(
feeError = feeError, prevState = prevState,
sendingAmount = sendingAmount, feeError = feeError,
onReload = prevState.clickIntents::getFee, sendingAmount = sendingAmount,
feeValue = feeValue, onReload = prevState.clickIntents::getFee,
) feeValue = feeValue,
addStakingErrorNotifications(stakingError = stakingError, onReload = prevState.clickIntents::getFee) )
// warnings addStakingErrorNotifications(stakingError = stakingError, onReload = prevState.clickIntents::getFee)
addWarningNotifications( // warnings
prevState = prevState, addWarningNotifications(
amountState = amountState, prevState = prevState,
feeState = feeState, amountState = amountState,
sendingAmount = sendingAmount, feeState = feeState,
isFeeCoverage = isFeeCoverage && isEnterAction && !sendingAmount.equals(minimumRequirement), sendingAmount = sendingAmount,
) isFeeCoverage = isFeeCoverage && isEnterAction && !sendingAmount.equals(minimumRequirement),
)
stakingInfoNotificationsFactory.addInfoNotifications( stakingInfoNotificationsFactory.addInfoNotifications(
notifications = this, notifications = this,
prevState = prevState, prevState = prevState,
sendingAmount = sendingAmount, sendingAmount = sendingAmount,
actionAmount = amountValue, actionAmount = amountValue,
feeValue = feeValue, feeValue = feeValue,
tonBalanceExtraFeeThreshold = TON_BALANCE_EXTRA_FEE_THRESHOLD, tonBalanceExtraFeeThreshold = TON_BALANCE_EXTRA_FEE_THRESHOLD,
) )
}.toImmutableList() }.toImmutableList()
} else {
buildList {
addTonInitializeAccountNotification(prevState)
}.toImmutableList()
}
val isActualSources = with(cryptoCurrencyStatus.value) { val isActualSources = with(cryptoCurrencyStatus.value) {
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual() sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
@ -130,7 +137,8 @@ internal class AddStakingNotificationsTransformer(
it is StakingNotification.Error || it is StakingNotification.Error ||
it is NotificationUM.Error || it is NotificationUM.Error ||
it is NotificationUM.Warning.NetworkFeeUnreachable || it is NotificationUM.Warning.NetworkFeeUnreachable ||
it is StakingNotification.Warning.TransactionInProgress it is StakingNotification.Warning.TransactionInProgress ||
it is StakingNotification.Warning.InitializeTonAccount
} && isActualSources, } && isActualSources,
), ),
) )
@ -219,7 +227,6 @@ internal class AddStakingNotificationsTransformer(
) { ) {
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
val appCurrency = appCurrencyProvider() val appCurrency = appCurrencyProvider()
cryptoCurrencyStatus.currency
addRentExemptionNotification( addRentExemptionNotification(
rentWarning = currencyCheck.rentWarning, rentWarning = currencyCheck.rentWarning,
@ -282,6 +289,19 @@ internal class AddStakingNotificationsTransformer(
} }
} }
private fun MutableList<NotificationUM>.addTonInitializeAccountNotification(prevState: StakingUiState) {
val isAccountInitialized = isAccountInitializedProvider.invoke()
val cryptoCurrencyNetworkIdValue = cryptoCurrencyStatusProvider().currency.network.rawId
if (isTon(cryptoCurrencyNetworkIdValue) && !isAccountInitialized) {
add(
StakingNotification.Warning.InitializeTonAccount(
onInitializeClick = prevState.clickIntents::onActivateTonAccountNotificationClick,
),
)
}
}
private companion object { private companion object {
val TON_BALANCE_EXTRA_FEE_THRESHOLD = BigDecimal(0.2) val TON_BALANCE_EXTRA_FEE_THRESHOLD = BigDecimal(0.2)
} }

View file

@ -0,0 +1,53 @@
package com.tangem.features.staking.impl.presentation.state.transformers.ton
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer
import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer.ReduceByData
import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.features.staking.impl.presentation.state.FeeState
import com.tangem.features.staking.impl.presentation.state.StakingStates
import com.tangem.features.staking.impl.presentation.state.StakingUiState
import com.tangem.features.staking.impl.presentation.state.bottomsheet.TonInitializeAccountBottomSheetConfig
import com.tangem.utils.transformer.Transformer
import java.math.BigDecimal
internal class CompleteInitializeBottomSheetTransformer(
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
private val minimumTransactionAmount: EnterAmountBoundary?,
) : Transformer<StakingUiState> {
override fun transform(prevState: StakingUiState): StakingUiState {
val prevBottomSheetConfig = prevState.bottomSheetConfig ?: return prevState
val prevBottomSheetConfigContent =
prevBottomSheetConfig.content as? TonInitializeAccountBottomSheetConfig ?: return prevState
val feeAmount = (prevBottomSheetConfigContent.feeState as? FeeState.Content)
?.fee?.amount?.value?.multiply(BigDecimal(FEE_MULTIPLIER)) ?: return prevState
val confirmationState = prevState.confirmationState as? StakingStates.ConfirmationState.Data ?: return prevState
return prevState.copy(
amountState = AmountReduceByTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatus,
minimumTransactionAmount = minimumTransactionAmount,
value = ReduceByData(
feeAmount,
feeAmount,
),
).transform(prevState.amountState),
confirmationState = confirmationState.copy(
reduceAmountBy = feeAmount,
),
bottomSheetConfig = prevBottomSheetConfig.copy(
content = prevBottomSheetConfigContent.copy(
isButtonLoading = true,
isButtonEnabled = false,
),
),
)
}
private companion object {
const val FEE_MULTIPLIER = 1.1
}
}

View file

@ -0,0 +1,25 @@
package com.tangem.features.staking.impl.presentation.state.transformers.ton
import com.tangem.features.staking.impl.presentation.state.FeeState
import com.tangem.features.staking.impl.presentation.state.StakingUiState
import com.tangem.features.staking.impl.presentation.state.bottomsheet.TonInitializeAccountBottomSheetConfig
import com.tangem.utils.transformer.Transformer
internal class SetFeeErrorToTonInitializeBottomSheetTransformer : Transformer<StakingUiState> {
override fun transform(prevState: StakingUiState): StakingUiState {
val prevBottomSheetConfig = prevState.bottomSheetConfig ?: return prevState
val prevBottomSheetConfigContent =
prevBottomSheetConfig.content as? TonInitializeAccountBottomSheetConfig ?: return prevState
return prevState.copy(
bottomSheetConfig = prevBottomSheetConfig.copy(
content = prevBottomSheetConfigContent.copy(
isButtonEnabled = false,
feeState = FeeState.Error,
isButtonLoading = false,
),
),
)
}
}

View file

@ -0,0 +1,41 @@
package com.tangem.features.staking.impl.presentation.state.transformers.ton
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.features.staking.impl.presentation.state.FeeState
import com.tangem.features.staking.impl.presentation.state.StakingUiState
import com.tangem.features.staking.impl.presentation.state.bottomsheet.TonInitializeAccountBottomSheetConfig
import com.tangem.utils.Provider
import com.tangem.utils.transformer.Transformer
internal class SetFeeToTonInitializeBottomSheetTransformer(
private val appCurrencyProvider: Provider<AppCurrency>,
private val feeCryptoCurrencyStatus: CryptoCurrencyStatus?,
private val fee: Fee,
private val isFeeApproximate: Boolean,
) : Transformer<StakingUiState> {
override fun transform(prevState: StakingUiState): StakingUiState {
val prevBottomSheetConfig = prevState.bottomSheetConfig ?: return prevState
val prevBottomSheetConfigContent =
prevBottomSheetConfig.content as? TonInitializeAccountBottomSheetConfig ?: return prevState
val isFeeConvertibleToFiat = feeCryptoCurrencyStatus?.currency?.network?.hasFiatFeeRate == true
return prevState.copy(
bottomSheetConfig = prevBottomSheetConfig.copy(
content = prevBottomSheetConfigContent.copy(
isButtonEnabled = true,
isButtonLoading = false,
feeState = FeeState.Content(
fee = fee,
rate = feeCryptoCurrencyStatus?.value?.fiatRate,
isFeeConvertibleToFiat = isFeeConvertibleToFiat,
appCurrency = appCurrencyProvider(),
isFeeApproximate = isFeeApproximate,
),
),
),
)
}
}

View file

@ -0,0 +1,32 @@
package com.tangem.features.staking.impl.presentation.state.transformers.ton
import com.tangem.common.ui.R
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.features.staking.impl.presentation.state.FeeState
import com.tangem.features.staking.impl.presentation.state.StakingUiState
import com.tangem.features.staking.impl.presentation.state.bottomsheet.TonInitializeAccountBottomSheetConfig
import com.tangem.utils.transformer.Transformer
internal class ShowTonInitializeBottomSheetTransformer(
private val onDismiss: () -> Unit,
) : Transformer<StakingUiState> {
override fun transform(prevState: StakingUiState): StakingUiState {
return prevState.copy(
bottomSheetConfig = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = onDismiss,
content = TonInitializeAccountBottomSheetConfig(
title = resourceReference(R.string.staking_account_initialization_title),
message = resourceReference(R.string.staking_account_initialization_message),
footer = resourceReference(R.string.staking_account_initialization_footer),
onButtonClick = prevState.clickIntents::onActivateTonAccountClick,
isButtonEnabled = false,
feeState = FeeState.Loading,
isButtonLoading = false,
),
),
)
}
}

View file

@ -28,8 +28,10 @@ import com.tangem.features.staking.impl.presentation.state.StakingStep
import com.tangem.features.staking.impl.presentation.state.StakingUiState import com.tangem.features.staking.impl.presentation.state.StakingUiState
import com.tangem.features.staking.impl.presentation.state.bottomsheet.StakingActionSelectionBottomSheetConfig import com.tangem.features.staking.impl.presentation.state.bottomsheet.StakingActionSelectionBottomSheetConfig
import com.tangem.features.staking.impl.presentation.state.bottomsheet.StakingInfoBottomSheetConfig import com.tangem.features.staking.impl.presentation.state.bottomsheet.StakingInfoBottomSheetConfig
import com.tangem.features.staking.impl.presentation.state.bottomsheet.TonInitializeAccountBottomSheetConfig
import com.tangem.features.staking.impl.presentation.ui.bottomsheet.StakingActionSelectorBottomSheet import com.tangem.features.staking.impl.presentation.ui.bottomsheet.StakingActionSelectorBottomSheet
import com.tangem.features.staking.impl.presentation.ui.bottomsheet.StakingInfoBottomSheet import com.tangem.features.staking.impl.presentation.ui.bottomsheet.StakingInfoBottomSheet
import com.tangem.features.staking.impl.presentation.ui.bottomsheet.TonInitializeAccountBottomSheet
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
@ -83,6 +85,7 @@ fun StakingBottomSheet(bottomSheetConfig: TangemBottomSheetConfig?) {
is StakingInfoBottomSheetConfig -> StakingInfoBottomSheet(bottomSheetConfig) is StakingInfoBottomSheetConfig -> StakingInfoBottomSheet(bottomSheetConfig)
is GiveTxPermissionBottomSheetConfig -> GiveTxPermissionBottomSheet(bottomSheetConfig) is GiveTxPermissionBottomSheetConfig -> GiveTxPermissionBottomSheet(bottomSheetConfig)
is StakingActionSelectionBottomSheetConfig -> StakingActionSelectorBottomSheet(bottomSheetConfig) is StakingActionSelectionBottomSheetConfig -> StakingActionSelectorBottomSheet(bottomSheetConfig)
is TonInitializeAccountBottomSheetConfig -> TonInitializeAccountBottomSheet(bottomSheetConfig)
} }
} }

View file

@ -0,0 +1,158 @@
package com.tangem.features.staking.impl.presentation.ui.bottomsheet
import android.content.res.Configuration
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.common.ui.R
import com.tangem.core.ui.components.SpacerH
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
import com.tangem.core.ui.components.buttons.common.TangemButton
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
import com.tangem.core.ui.components.buttons.common.TangemButtonSize
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.features.staking.impl.presentation.state.FeeState
import com.tangem.features.staking.impl.presentation.state.bottomsheet.TonInitializeAccountBottomSheetConfig
import com.tangem.features.staking.impl.presentation.ui.block.StakingFeeBlock
import java.math.BigDecimal
@Composable
internal fun TonInitializeAccountBottomSheet(config: TangemBottomSheetConfig) {
TangemBottomSheet<TonInitializeAccountBottomSheetConfig>(
config = config,
containerColor = TangemTheme.colors.background.tertiary,
) { content ->
Column(
modifier = Modifier
.padding(
start = TangemTheme.dimens.spacing16,
end = TangemTheme.dimens.spacing16,
bottom = TangemTheme.dimens.spacing16,
)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
SpacerH(height = 16.dp)
Image(
painter = painterResource(id = R.drawable.img_ton_22),
modifier = Modifier.size(56.dp),
contentDescription = null,
)
SpacerH(height = 24.dp)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
text = content.title.resolveReference(),
style = TangemTheme.typography.h3,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
)
SpacerH(height = 16.dp)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
text = content.message.resolveReference(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.secondary,
textAlign = TextAlign.Center,
)
SpacerH(height = 24.dp)
StakingFeeBlock(content.feeState)
SpacerH(height = 12.dp)
Text(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
text = content.footer.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
textAlign = TextAlign.Center,
)
SpacerH(height = 20.dp)
TangemButton(
text = stringResourceSafe(R.string.common_activate),
icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_tangem_24),
onClick = content.onButtonClick,
colors = TangemButtonsDefaults.primaryButtonColors,
showProgress = content.feeState is FeeState.Loading || content.isButtonLoading,
enabled = content.isButtonEnabled,
size = TangemButtonSize.WideAction,
textStyle = TangemTheme.typography.subtitle1,
modifier = Modifier.fillMaxWidth(),
)
SpacerH(height = 8.dp)
}
}
}
// region Preview
@Composable
@Preview(showBackground = true, widthDp = 360)
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun Preview_TonInitializeAccountBottomSheetContent() {
TangemThemePreview {
TonInitializeAccountBottomSheet(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = {},
content = TonInitializeAccountBottomSheetConfig(
title = resourceReference(R.string.staking_account_initialization_title),
message = resourceReference(R.string.staking_account_initialization_message),
footer = resourceReference(R.string.staking_account_initialization_footer),
onButtonClick = {},
isButtonEnabled = true,
isButtonLoading = false,
feeState = FeeState.Content(
fee = Fee.Common(
amount = Amount(
currencySymbol = "MATIC",
value = BigDecimal(0.159806),
decimals = 18,
type = AmountType.Coin,
),
),
rate = BigDecimal.ONE,
appCurrency = AppCurrency.Default,
isFeeApproximate = false,
isFeeConvertibleToFiat = true,
),
),
),
)
}
}
// endregion Preview