Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-20 20:42:55 +08:00
commit 4d856572d8
37 changed files with 2379 additions and 301 deletions

View file

@ -33,7 +33,11 @@ internal class TangemPushNotificationService : FirebaseMessagingService() {
super.onMessageReceived(message)
if (customerIoFeatureToggles.isFeatureEnabled) {
CustomerIOFirebaseMessagingService.onMessageReceived(applicationContext, message)
CustomerIOFirebaseMessagingService.onMessageReceived(
context = applicationContext,
remoteMessage = message,
handleNotificationTrigger = false,
)
}
val notification = message.notification ?: return

View file

@ -4,7 +4,6 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.markets.*
import com.tangem.domain.markets.repositories.MarketsTokenRepository
import com.tangem.domain.promo.PromoRepository
import com.tangem.domain.quotes.single.SingleQuoteStatusSupplier
import dagger.Module
import dagger.Provides
@ -83,16 +82,4 @@ object MarketsDomainModule {
fun provideGetTokenExchangesUseCase(marketsTokenRepository: MarketsTokenRepository): GetTokenExchangesUseCase {
return GetTokenExchangesUseCase(marketsTokenRepository = marketsTokenRepository)
}
@Provides
@Singleton
fun provideShouldShowYieldModeMarketPromoUseCase(
promoRepository: PromoRepository,
marketsTokenRepository: MarketsTokenRepository,
): ShouldShowYieldModeMarketPromoUseCase {
return ShouldShowYieldModeMarketPromoUseCase(
promoRepository = promoRepository,
marketsTokenRepository = marketsTokenRepository,
)
}
}

View file

@ -4,6 +4,11 @@ import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class OrderRequest(
@Json(name = "wallet_address") val walletAddress: String,
)
data class OrderRequest(@Json(name = "data") val data: Data) {
@JsonClass(generateAdapter = true)
data class Data(
@Json(name = "customer_wallet_address") val customerWalletAddress: String,
@Json(name = "specification_name") val specificationName: String = "SP_000004",
@Json(name = "type") val type: String = "CARD_ISSUE_VIRTUAL_RAIN_KYC",
)
}

View file

@ -73,7 +73,7 @@ data class CustomerMeResponse(
@JsonClass(generateAdapter = true)
data class PaymentAccount(
@Json(name = "id") val id: String,
@Json(name = "address") val address: String,
@Json(name = "address") val address: String?,
@Json(name = "customer_wallet_address") val customerWalletAddress: String,
)

View file

@ -79,10 +79,6 @@ object PreferencesKeys {
val SHOULD_SHOW_MARKETS_TOOLTIP_KEY by lazy { booleanPreferencesKey(name = "shouldShowMarketsTooltip") }
val MARKETS_YIELD_SUPPLY_NOTIFICATION_HIDE_CLICKED_KEY by lazy {
booleanPreferencesKey(name = "marketsYieldSupplyNotificationHideClicked")
}
val WALLET_FIRST_USAGE_DATE_KEY by lazy { longPreferencesKey(name = "walletFirstUsageDate") }
val IS_WALLET_NAMES_MIGRATION_DONE_KEY by lazy { booleanPreferencesKey(name = "isWalletNamesMigrationDone") }

View file

@ -326,6 +326,7 @@
<string name="common_nft">NFT</string>
<string name="common_no">Nein</string>
<string name="common_no_address">Keine Adresse</string>
<string name="common_no_results">Keine Ergebnisse gefunden</string>
<string name="common_not_added">Nicht hinzugefügt</string>
<string name="common_not_available">Nicht verfügbar</string>
<string name="common_not_now">Nicht jetzt</string>
@ -384,6 +385,7 @@
<string name="common_to">An</string>
<string name="common_to_wallet_name">Zu %s</string>
<string name="common_today">Heute</string>
<string name="common_token_send">Zu sendendes Token</string>
<plurals name="common_tokens_count">
<item quantity="one">%d Token</item>
<item quantity="other">%d Token</item>
@ -396,6 +398,7 @@
<string name="common_understand">Ich verstehe</string>
<string name="common_understand_continue">Ich verstehe, fahre bitte fort.</string>
<string name="common_unknown_error">Es ist ein Fehler aufgetreten. Bitte versuche es erneut.</string>
<string name="common_unlock">Freischalten</string>
<string name="common_unreachable">Nicht erreichbar</string>
<string name="common_unstake">Staking beenden</string>
<string name="common_utxo_validate_withdrawal_message_warning">Aufgrund der Beschränkungen von %1$s können nur %2$d UTXOs in eine einzige Transaktion passen. Das bedeutet, dass du nur %3$s oder weniger senden kannst. Du musst den Betrag reduzieren.</string>
@ -688,6 +691,9 @@
<string name="initial_message_scan_header">Legen Sie die Karte oder Ring zum Scannen an</string>
<string name="initial_message_sign_header">Tippen um zu signieren</string>
<string name="initial_message_tap_header">Lege die Karte oder Ring an</string>
<string name="initial_wallet_sync_banner_description">Deine Wallet ist eingerichtet und einsatzbereit!</string>
<string name="initial_wallet_sync_banner_title">Wallet erfolgreich importiert</string>
<string name="initial_wallet_sync_restore_progress">Wiederherstellung %d%</string>
<string name="key_invalidated_warning_description">Du hast deine biometrischen Daten aktualisiert, scanne deine Karte oder Ring, um einzutreten</string>
<string name="koinos_insufficient_balance_to_send_koin_description">Dein Guthaben sollte höher sein als der Gebührenwert, um eine Überweisung zu tätigen</string>
<string name="koinos_insufficient_balance_to_send_koin_title">Unzureichende Mittel</string>
@ -699,6 +705,7 @@
<string name="koinos_mana_level_title">Mana-Level</string>
<string name="main_empty_tokens_list_message">Um mit der Verfolgung deiner Krypto-Assets und -Transaktionen zu beginnen, füge einen Token hinzu</string>
<string name="main_manage_tokens">Token verwalten</string>
<string name="main_qr_scan_hint">QR-Code scannen, um Geld zu senden oder eine Verbindung zu einer App herzustellen</string>
<string name="main_scan_card_warning_view_subtitle">Um auf alle Netzwerke zugreifen zu können, musst du die Karte oder Ring scannen</string>
<string name="main_scan_card_warning_view_title">Scanne deine Karte oder Ring</string>
<string name="main_swap_changelly_promotion_message">Genießen %1$s Servicegebühren auf Swaps über Changelly von Februar %2$s-%3$s</string>
@ -736,6 +743,8 @@
<string name="markets_earn_common_title">Verdiene Geld mit Tangem</string>
<string name="markets_generate_addresses_notification">Um Adressen für ausgewählte Netzwerke zu generieren, musst du eine Tangem-Karte oder Ring scannen</string>
<string name="markets_hint">Um Token hinzuzufügen, rufe dies auf oder tippe auf die Suchleiste</string>
<string name="markets_hint_part_one">Wischen Sie nach oben, um den Markt zu erkunden</string>
<string name="markets_hint_part_two">Entdecke neue verborgene Schätze</string>
<string name="markets_insights_info_description_message">Die Daten dieses Abschnitts stammen aus den folgenden Netzwerken: %s</string>
<string name="markets_loading_error_title">Die Daten konnten nicht geladen werden...</string>
<string name="markets_loading_no_data_title">Keine Daten</string>
@ -798,6 +807,10 @@
<string name="markets_token_details_holders">Inhaber/ Halter</string>
<string name="markets_token_details_holders_description">Die Änderung der Anzahl der Token-Inhaber innerhalb eines bestimmten Zeitraums</string>
<string name="markets_token_details_holders_full">Inhaber/ Halter</string>
<string name="markets_token_details_insight_day_timeline">D</string>
<string name="markets_token_details_insight_month_timeline">M</string>
<string name="markets_token_details_insight_week_timeline">W</string>
<string name="markets_token_details_insight_year_timeline">Y</string>
<string name="markets_token_details_insights">Einblicke</string>
<string name="markets_token_details_links">Links</string>
<string name="markets_token_details_liquidity">Liquidität</string>
@ -825,9 +838,11 @@
<string name="markets_token_details_total_supply">Gesamtangebot</string>
<string name="markets_token_details_total_supply_description">Die maximale Anzahl von Coins oder Tokens, die jemals für eine bestimmte Kryptowährung existieren können</string>
<string name="markets_token_details_total_supply_full">Gesamtangebot</string>
<string name="markets_token_details_trading_interval">24 Stunden</string>
<string name="markets_token_details_trading_volume">Handelsvolumen (24h)</string>
<string name="markets_token_details_trading_volume_24h_description">Der Gesamtbetrag einer Kryptowährung, der innerhalb der letzten 24 Stunden gehandelt wurde, wobei das Aktivitäts- und Liquiditätsniveau auf dem Markt angegeben wird</string>
<string name="markets_token_details_trading_volume_full">Handelsvolumen (24h)</string>
<string name="markets_token_details_valuation_value_in_total">%s insgesamt</string>
<string name="markets_token_details_volume">Volumen</string>
<string name="markets_tooltip_message">Rufe dies auf oder tippe auf die Suchleiste, um Token direkt vom Markt hinzuzufügen</string>
<string name="markets_tooltip_title">Token hinzufügen</string>
@ -1088,6 +1103,10 @@
<string name="qr_scanner_camera_denied_settings_button">Einstellungen</string>
<string name="qr_scanner_camera_denied_text">Du hast keinen Zugriff auf deine Kamera gewährt</string>
<string name="qr_scanner_camera_denied_title">Kamerazugriff verweigert</string>
<string name="qr_scanner_error_unrecognized_message">Dieser QR-Code konnte leider nicht erkannt werden.</string>
<string name="qr_scanner_error_unrecognized_title">Unerkannter QR-Code</string>
<string name="qr_scanner_error_unsupported_network_message">Dieses Netzwerk wird von keinem der von Ihnen hinzugefügten Token unterstützt. Fügen Sie einen unterstützten Token hinzu, um Krypto zu senden.</string>
<string name="qr_scanner_error_unsupported_network_title">Keine unterstützten Token gefunden</string>
<string name="receive_bottom_sheet_no_memo_required_message">Kein Memo erforderlich</string>
<string name="receive_bottom_sheet_warning_message">%1$s ( %2$s ) im %3$s Netzwerk</string>
<string name="receive_bottom_sheet_warning_message_compact">%1$s im %2$s Netzwerk</string>
@ -1229,6 +1248,11 @@
<string name="send_notification_transaction_limit_title">Begrenzung der Transaktionen</string>
<string name="send_optional_field">Optional</string>
<string name="send_qrcode_scan_info">Bitte richte den QR-Code auf das Quadrat aus, um ihn zu scannen. Stell sicher, dass du die Netzwerkadresse %s scannen.</string>
<string name="send_rate_fixed_info_description">Wenn Sie einen festen Zinssatz verwenden, wird der Betrag, den Sie erhalten, zum Zeitpunkt des Swapgeschäfts festgelegt. Dies schützt Sie vor Preisänderungen während der Transaktion.</string>
<string name="send_rate_fixed_info_title">Fester Zinssatz</string>
<string name="send_rate_floating_info_description">Ein variabler Zinssatz bedeutet, dass sich der endgültige Betrag, den Sie erhalten, je nach Marktbedingungen zwischen dem Beginn und dem Abschluss des Swaps geringfügig ändern kann.</string>
<string name="send_rate_floating_info_title">Variabler Zinssatz</string>
<string name="send_rate_is_fixed">Der Zinssatz ist fest.</string>
<string name="send_recent_transactions">Jüngste</string>
<string name="send_recipient">Empfänger</string>
<string name="send_recipient_address_error">Keine gültige Adresse</string>
@ -1272,6 +1296,7 @@
<string name="send_with_swap_recipient_get_amount">Empfänger erhält %s</string>
<string name="send_with_swap_remove_convert_alert_message">Möchtest Du die Konvertierung wirklich abbrechen? Deine bisherigen Daten werden gelöscht.</string>
<string name="send_with_swap_remove_convert_alert_title">Konvertierung entfernen</string>
<string name="send_with_swap_something_went_wrong">Etwas ist schiefgelaufen. Versuchen Sie es erneut.</string>
<string name="send_with_swap_title">Senden mit Swap</string>
<string name="sent_transaction_sent_title">Transaktion gesendet</string>
<string name="settings_card_settings_footer">Bereite das Scannen der Karte oder Ring vor, die du einrichten möchtest.</string>
@ -1438,7 +1463,7 @@
<string name="swap_give_permission_fee_footer">Das Netzwerk erhebt eine Token-Genehmigungsgebühr, um zu überprüfen, ob Sie die Verwendung Ihres Tokens für den Swap genehmigen.</string>
<string name="swap_promo_text">Tausche mehr Token zu besseren Kursen direkt in deiner Brieftasche.</string>
<string name="swap_promo_title">Neuer Swap-Anbieter verfügbar!</string>
<string name="swap_search_suggestion_hint">Suchen Sie etwas anderes? Versuchen Sie es mit der Suche oder erkunden Sie eine andere Kryptowährung!</string>
<string name="swap_search_suggestion_hint">Suchen Sie etwas anderes?\n Versuchen Sie es mit der Suche oder erkunden Sie eine andere Kryptowährung!</string>
<string name="swap_search_tooltip_description">Suchen Sie nach einem beliebigen Token, auch wenn es noch nicht in Ihrer Liste ist.</string>
<string name="swap_search_tooltip_title">Nutzen Sie die Suche, um zu finden, was Sie benötigen.</string>
<string name="swap_story_fifth_subtitle">Vertraue auf den rund um die Uhr verfügbaren Support bei allen Problemen</string>
@ -1509,6 +1534,7 @@
<string name="tangempay_cancel_kyc">KYC vom Hauptbildschirm ausblenden</string>
<string name="tangempay_card_details_add_funds">Guthaben hinzufügen</string>
<string name="tangempay_card_details_add_funds_subtitle">Aufladeoptionen</string>
<string name="tangempay_card_details_add_to_wallet_button_text">Zu Google Wallet hinzufügen</string>
<string name="tangempay_card_details_card_number">Kartennummer</string>
<string name="tangempay_card_details_change_pin">PIN ändern</string>
<string name="tangempay_card_details_change_pin_success_description">Die Karte ist vollständig für Zahlungen bereit.</string>

View file

@ -320,6 +320,7 @@
<string name="common_nft">NFT</string>
<string name="common_no">いいえ</string>
<string name="common_no_address">アドレスがありません</string>
<string name="common_no_results">該当する結果はありません</string>
<string name="common_not_added">未追加</string>
<string name="common_not_available">利用できません</string>
<string name="common_not_now">今はしない</string>
@ -378,6 +379,7 @@
<string name="common_to">宛先</string>
<string name="common_to_wallet_name">%sへ</string>
<string name="common_today">今日</string>
<string name="common_token_send">送信するトークン</string>
<plurals name="common_tokens_count">
<item quantity="other">%d トークン</item>
</plurals>
@ -389,6 +391,7 @@
<string name="common_understand">わかりました</string>
<string name="common_understand_continue">理解して続行</string>
<string name="common_unknown_error">エラーが発生しました。もう一度お試しください。</string>
<string name="common_unlock">ロック解除</string>
<string name="common_unreachable">アクセスできません</string>
<string name="common_unstake">ステーキング解除</string>
<string name="common_utxo_validate_withdrawal_message_warning">%1$sの制限により、1つのトランザクションに収まるUTXOは%2$d個のみです。つまり、 %3$s以下しか送信できません。量を減らす必要があります。</string>
@ -472,7 +475,7 @@
<string name="earn_filter_networks">ネットワーク</string>
<string name="earn_mostly_used">よく使われています</string>
<string name="earn_no_results">該当する結果はありません</string>
<string name="earn_title">運用</string>
<string name="earn_title">ステーキング・利息モード</string>
<string name="email_preface_wc_error">こんにちは、サポートチームの皆さん、コード %s のエラーが発生しました。</string>
<string name="email_subject_wc_error">WalletConnectエラー</string>
<string name="error_wrong_wallet_tapped">別のウォレットのカードまたはリングを使用しました。このウォレットにリンクしているカードまたはリングをタップしてください。</string>
@ -682,6 +685,9 @@
<string name="initial_message_scan_header">タップしてスキャン</string>
<string name="initial_message_sign_header">タップして署名</string>
<string name="initial_message_tap_header">カードまたはリングをタップ</string>
<string name="initial_wallet_sync_banner_description">ウォレットは同期され、準備ができています。\n一部のトークンが見つかりませんか</string>
<string name="initial_wallet_sync_banner_title">ウォレットのインポートが完了しました</string>
<string name="initial_wallet_sync_restore_progress">%d%を復元中</string>
<string name="key_invalidated_warning_description">生体認証を更新しました。カードまたはリングをスキャンして入ってください。</string>
<string name="koinos_insufficient_balance_to_send_koin_description">送金するには、残高が手数料額より高くなければなりません。</string>
<string name="koinos_insufficient_balance_to_send_koin_title">残高不足</string>
@ -727,9 +733,11 @@
<string name="markets_apy_placeholder">APY %s</string>
<string name="markets_common_my_portfolio">私のポートフォリオ</string>
<string name="markets_common_title">マーケット</string>
<string name="markets_earn_common_title">Tangemで稼ぐ</string>
<string name="markets_earn_common_title">ステーキング・利息モード</string>
<string name="markets_generate_addresses_notification">選択したネットワークのアドレスを生成するには、Tangemウォレットカードまたはリングをスキャンする必要があります。</string>
<string name="markets_hint">トークンを追加するには、これをスワイプするか、検索バーをタップしてください。</string>
<string name="markets_hint_part_one">上にスワイプしてマーケットをチェック</string>
<string name="markets_hint_part_two">新たな有望銘柄を見つける</string>
<string name="markets_insights_info_description_message">このセクションのデータは、次のネットワークから取得されています: %s</string>
<string name="markets_loading_error_title">データを読み込めません…</string>
<string name="markets_loading_no_data_title">データなし</string>
@ -739,6 +747,7 @@
<string name="markets_search_result_title">結果</string>
<string name="markets_search_see_tokens_under_100k">時価総額10万ドル以下のトークンを見る</string>
<string name="markets_search_show_tokens">トークンを表示</string>
<string name="markets_search_title_placeholder">暗号資産、ニュースなど</string>
<string name="markets_search_token_no_result_title">検索結果はありません</string>
<string name="markets_select_network">ネットワークを選択</string>
<string name="markets_select_wallet">ウォレットを選択</string>
@ -790,6 +799,10 @@
<string name="markets_token_details_holders">ホルダー</string>
<string name="markets_token_details_holders_description">特定の期間内のトークンホルダー数の変化</string>
<string name="markets_token_details_holders_full">ホルダー</string>
<string name="markets_token_details_insight_day_timeline"></string>
<string name="markets_token_details_insight_month_timeline"></string>
<string name="markets_token_details_insight_week_timeline"></string>
<string name="markets_token_details_insight_year_timeline"></string>
<string name="markets_token_details_insights">インサイト</string>
<string name="markets_token_details_links">リンク</string>
<string name="markets_token_details_liquidity">流動性</string>
@ -817,9 +830,11 @@
<string name="markets_token_details_total_supply">総供給量</string>
<string name="markets_token_details_total_supply_description">特定の暗号資産に存在しうるコインまたはトークンの最大数</string>
<string name="markets_token_details_total_supply_full">総供給量</string>
<string name="markets_token_details_trading_interval">24h</string>
<string name="markets_token_details_trading_volume">取引量24時間</string>
<string name="markets_token_details_trading_volume_24h_description">過去24時間以内に取引された暗号資産の合計額。市場の活発さと流動性を示します。</string>
<string name="markets_token_details_trading_volume_full">取引量24時間</string>
<string name="markets_token_details_valuation_value_in_total">合計 %s</string>
<string name="markets_token_details_volume">取引量</string>
<string name="markets_tooltip_message">これをドラッグするか、検索窓をタップして、マーケットから直接トークンを追加します</string>
<string name="markets_tooltip_title">トークンを追加</string>
@ -1024,7 +1039,7 @@
<string name="onramp_max_amount_restriction">買付金額は%s以下にしてください</string>
<string name="onramp_min_amount_restriction">買付金額は少なくとも%sである必要があります</string>
<string name="onramp_no_available_providers">この通貨で利用可能なプロバイダーはありません</string>
<string name="onramp_offer_type_fastet">最短処理</string>
<string name="onramp_offer_type_fastet">最短処理</string>
<string name="onramp_pay_with">支払う</string>
<string name="onramp_payment_method_subtitle">支払方法</string>
<string name="onramp_provider_max_amount">最大 %s まで使用可能</string>
@ -1072,6 +1087,12 @@
<string name="qr_scanner_camera_denied_settings_button">設定</string>
<string name="qr_scanner_camera_denied_text">カメラへのアクセスを許可していません。</string>
<string name="qr_scanner_camera_denied_title">カメラへのアクセスが拒否されました</string>
<string name="qr_scanner_error_token_not_added_message">指定したトークンがウォレットに追加されていません。追加してからもう一度お試しください。</string>
<string name="qr_scanner_error_token_not_added_title">トークンが追加されていません</string>
<string name="qr_scanner_error_unrecognized_message">申し訳ありません。このQRコードを認識できませんでした。</string>
<string name="qr_scanner_error_unrecognized_title">認識できないQRコード</string>
<string name="qr_scanner_error_unsupported_network_message">このネットワークは、追加されているトークンのいずれにも対応していません。対応しているトークンを追加してから暗号資産を送金してください。</string>
<string name="qr_scanner_error_unsupported_network_title">対応しているトークンが見つかりません</string>
<string name="receive_bottom_sheet_no_memo_required_message">メモ不要</string>
<string name="receive_bottom_sheet_warning_message">%3$sネットワーク上の%1$s ( %2$s )</string>
<string name="receive_bottom_sheet_warning_message_compact">%2$sネットワーク上の%1$s</string>
@ -1184,6 +1205,9 @@
<string name="send_memo">メモ: %s</string>
<string name="send_memo_destination_tag_error">無効なメモ</string>
<string name="send_network_fee_warning_title">ネットワーク手数料のカバー</string>
<plurals name="send_network_selection_hidden_tokens">
<item quantity="other">%dトークンは、このアドレスには対応していません。</item>
</plurals>
<string name="send_nonce">ナンス</string>
<string name="send_nonce_footer">各取引に固有の番号です。保留中の取引を再送信またはキャンセルするために使用します。</string>
<string name="send_nonce_hint">ナンスを入力してください...</string>
@ -1211,6 +1235,11 @@
<string name="send_notification_transaction_limit_title">取引制限</string>
<string name="send_optional_field">オプション</string>
<string name="send_qrcode_scan_info">QRコードを正方形に合わせてスキャンしてください。%s ネットワークアドレスをスキャンしてください。</string>
<string name="send_rate_fixed_info_description">固定レートを使用すると、受け取る金額はスワップ時点で確定します。これにより、取引中の価格変動から保護されます。</string>
<string name="send_rate_fixed_info_title">固定レート</string>
<string name="send_rate_floating_info_description">変動レートとは、スワップ開始から完了までの間の市場状況に応じて、最終的に受け取る金額がわずかに変動する可能性があることを意味します。</string>
<string name="send_rate_floating_info_title">変動レート</string>
<string name="send_rate_is_fixed">レートは固定されています</string>
<string name="send_recent_transactions">最近の取引</string>
<string name="send_recipient">受取人</string>
<string name="send_recipient_address_error">有効なアドレスではありません</string>
@ -1254,6 +1283,7 @@
<string name="send_with_swap_recipient_get_amount">受信者は%sを取得します</string>
<string name="send_with_swap_remove_convert_alert_message">変換をキャンセルしてもよろしいですか?以前のデータは消去されます。</string>
<string name="send_with_swap_remove_convert_alert_title">変換を削除</string>
<string name="send_with_swap_something_went_wrong">問題が発生しました。もう一度お試しください。</string>
<string name="send_with_swap_title">スワップして送信</string>
<string name="sent_transaction_sent_title">取引が送信されました</string>
<string name="settings_card_settings_footer">設定したいカードまたはリングをスキャンするために準備してください。</string>
@ -1447,6 +1477,7 @@
<string name="swapping_high_price_impact_description">選択したトークンをこの量を交換すると、価格に大きな影響が生じ、結果が減少します。</string>
<string name="swapping_high_price_impact_title">価格への影響が甚大です</string>
<string name="swapping_insufficient_funds">残高不足</string>
<string name="swapping_insufficient_funds_description">この取引を完了するには残高が不足しています。受け取り額を減らすか、資金を追加してください。</string>
<string name="swapping_permission_header">許可を与える</string>
<string name="swapping_swap_action">スワップ</string>
<string name="swapping_swap_action_in_progress">スワップ中…</string>

File diff suppressed because it is too large Load diff

View file

@ -491,8 +491,9 @@
<string name="earn_filter_by">Фильтровать по</string>
<string name="earn_filter_my_networks">Мои сети</string>
<string name="earn_filter_networks">Сети</string>
<string name="earn_mostly_used">Часто используемые</string>
<string name="earn_mostly_used">Популярные</string>
<string name="earn_no_results">Нет результата</string>
<string name="earn_title">Доступные активы</string>
<string name="email_preface_wc_error">Привет, команда поддержки, у меня возникла ошибка с кодом: %s</string>
<string name="email_subject_wc_error">Ошибка WalletConnect</string>
<string name="error_wrong_wallet_tapped">Вы использовали карту или кольцо от другого кошелька. Приложите карту или кольцо, связанную с этим кошельком.</string>
@ -746,7 +747,7 @@
<string name="markets_apy_placeholder">APY %s</string>
<string name="markets_common_my_portfolio">Мой портфель</string>
<string name="markets_common_title">Рынок</string>
<string name="markets_earn_common_title">Зарабатывай с Tangem</string>
<string name="markets_earn_common_title">Доход с Tangem</string>
<string name="markets_generate_addresses_notification">Чтобы создать адреса для выбранных сетей, отсканируйте вашу карту или кольцо Tangem кошелька</string>
<string name="markets_hint">Потяните вверх или коснитесь поисковой строки, чтобы добавить токен</string>
<string name="markets_insights_info_description_message">Данные раздела получены из следующих сетей: %s</string>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="common_add_tokens">添加代幣</string>
<string name="common_no_results">沒有結果</string>
<string name="common_token_send">要發送的代幣</string>
<string name="common_unlock">解鎖</string>
<string name="initial_wallet_sync_banner_description">您的錢包已同步並準備就緒。\n缺少某些代幣</string>
<string name="initial_wallet_sync_banner_title">錢包導入成功</string>
<string name="initial_wallet_sync_restore_progress">恢復%d%</string>
<string name="markets_token_details_insight_day_timeline"></string>
<string name="markets_token_details_insight_month_timeline"></string>
<string name="markets_token_details_insight_week_timeline"></string>
<string name="markets_token_details_insight_year_timeline"></string>
<string name="send_rate_fixed_info_description">使用固定利率時,您在置換交易時收到的金額將被鎖定,這可以保護您免受交易過程中價格波動的影響。</string>
<string name="send_rate_fixed_info_title">固定利率</string>
<string name="send_rate_floating_info_description">浮動利率意味著您最終收到的金額可能會根據您開始和完成置換之間的市場情況略有變化。</string>
<string name="send_rate_floating_info_title">浮動利率</string>
<string name="send_rate_is_fixed">利率固定</string>
</resources>

View file

@ -306,6 +306,7 @@
<string name="common_hour">hour</string>
<string name="common_import">Import</string>
<string name="common_in_progress">In progress</string>
<string name="common_insufficient_balance">Insufficient balance</string>
<string name="common_later">Later</string>
<string name="common_learn_more">Learn more</string>
<string name="common_left">%1$s left</string>
@ -755,6 +756,7 @@
<string name="markets_search_result_title">Result</string>
<string name="markets_search_see_tokens_under_100k">See tokens under 100k USD market cap</string>
<string name="markets_search_show_tokens">Show tokens</string>
<string name="markets_search_title_placeholder">Crypto, news and more</string>
<string name="markets_search_token_no_result_title">No result</string>
<string name="markets_select_network">Select network</string>
<string name="markets_select_wallet">Select wallet</string>
@ -1497,6 +1499,7 @@
<string name="swapping_high_price_impact_description">Swapping this amount of selected tokens will cause a significant price impact and reduce your outcome.</string>
<string name="swapping_high_price_impact_title">High price impact</string>
<string name="swapping_insufficient_funds">Insufficient funds</string>
<string name="swapping_insufficient_funds_description">Not enough funds to complete this transaction. Reduce the amount to receive or add more funds.</string>
<string name="swapping_permission_header">Give Permission</string>
<string name="swapping_swap_action">Swap</string>
<string name="swapping_swap_action_in_progress">Swapping...</string>

View file

@ -19,7 +19,6 @@ import com.tangem.datasource.api.common.response.getOrThrow
import com.tangem.datasource.api.markets.TangemTechMarketsApi
import com.tangem.datasource.api.markets.models.response.TokenMarketExchangesResponse
import com.tangem.datasource.local.datastore.RuntimeStateStore
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.common.wallets.getSyncStrict
import com.tangem.domain.markets.*
@ -31,7 +30,6 @@ import com.tangem.pagination.*
import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicLong
@ -294,26 +292,6 @@ internal class DefaultMarketsTokenRepository(
}
}
override suspend fun showYieldModePromo(
appCurrency: AppCurrency,
interval: TokenMarketListConfig.Interval,
): Boolean = try {
val hasYieldSupplyTokens = marketsApi.getCoinsList(
currency = appCurrency.code,
interval = interval.toRequestParam(),
order = TokenMarketListConfig.Order.YieldSupply.toRequestParam(),
offset = 0,
limit = 40,
timestamp = null,
search = null,
).getOrThrow().tokens.isNotEmpty()
hasYieldSupplyTokens
} catch (error: Exception) {
Timber.e(error)
false
}
inline fun <T> catchListErrorAndSendEvent(block: () -> T): T {
return catchErrorAndSendEvent(block, ::createListErrorEvent)
}

View file

@ -15,8 +15,6 @@ internal fun TokenMarketListConfig.Order.toRequestParam(): String = when (this)
TokenMarketListConfig.Order.Buyers -> "buyers"
TokenMarketListConfig.Order.TopGainers -> "gainers"
TokenMarketListConfig.Order.TopLosers -> "losers"
TokenMarketListConfig.Order.Staking -> "staking"
TokenMarketListConfig.Order.YieldSupply -> "yield"
}
internal fun PriceChangeInterval.toRequestParam(): String = when (this) {

View file

@ -96,20 +96,6 @@ internal class DefaultPromoRepository(
appPreferencesStore.store(PreferencesKeys.getShouldShowPromoKey(promoId = promoId.name), false)
}
override fun isMarketsYieldSupplyNotificationHideClicked(): Flow<Boolean> {
return appPreferencesStore.get(
key = PreferencesKeys.MARKETS_YIELD_SUPPLY_NOTIFICATION_HIDE_CLICKED_KEY,
default = false,
)
}
override suspend fun setMarketsYieldSupplyNotificationHideClicked() {
appPreferencesStore.store(
key = PreferencesKeys.MARKETS_YIELD_SUPPLY_NOTIFICATION_HIDE_CLICKED_KEY,
value = true,
)
}
override suspend fun isMoonpayPromoActive(): Boolean {
val banner = runCatching(dispatchers.io) {
val response = promoBannerStore.getSyncOrNull(MOONPAY_NAME) ?: run {

View file

@ -96,8 +96,7 @@ internal class DefaultTangemPayEligibilityManager @Inject constructor(
}
private fun UserWallet.isCompatible(): Boolean = when (this) {
is UserWallet.Cold ->
scanResponse.card.firmwareVersion >= FirmwareVersion.HDWalletAvailable
is UserWallet.Cold -> scanResponse.card.firmwareVersion >= FirmwareVersion.HDWalletAvailable
is UserWallet.Hot -> hotWalletId.authType != HotWalletId.AuthType.NoPassword
}

View file

@ -104,9 +104,9 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
ifRight = { customerInfo ->
Timber.tag(TAG).i("proceedWithoutOrder data customerInfo $userWalletId")
val status = customerInfo.mapToPaymentAccountStatus()
if (status is PaymentAccountStatus.IssuingCard && customerInfo.kycStatus == KycStatus.APPROVED) {
// If order id wasn't saved -> start order creation and get customer info
if (customerInfo.productInstance == null) {
onboardingRepository.createOrder(userWalletId)
.onLeft { Timber.tag(TAG).e("createOrder failed: $it") }
}
status
},
@ -128,9 +128,6 @@ internal class DefaultPaymentAccountStatusFetcher @Inject constructor(
-> PaymentAccountStatus.IssuingCard(source = StatusSource.ACTUAL)
OrderStatus.CANCELED -> {
// If order was cancelled -> clear previous order from local storage and start order creation
onboardingRepository.clearOrderId(userWalletId)
onboardingRepository.createOrder(userWalletId)
PaymentAccountStatus.Error.CardIssueFailed
}
OrderStatus.COMPLETED -> {

View file

@ -27,6 +27,7 @@ internal class DefaultCustomerOrderRepository @Inject constructor(
OrderResponse.Result.Status.CANCELED -> OrderStatus.CANCELED
}
OrderData(
customerId = response.result?.customerId.orEmpty(),
status = status,
withdrawTxHash = response.result?.data?.transactionHash?.ifEmpty { null },
)

View file

@ -1,13 +1,14 @@
package com.tangem.data.pay.repository
import arrow.core.Either
import arrow.core.raise.catch
import arrow.core.right
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.datasource.api.pay.TangemPayApi
import com.tangem.datasource.api.pay.models.request.DeeplinkValidityRequest
import com.tangem.datasource.api.pay.models.request.OrderRequest
import com.tangem.datasource.api.pay.models.request.SetTangemPayEnabledRequest
import com.tangem.datasource.api.pay.models.response.CustomerMeResponse
import com.tangem.datasource.api.pay.models.response.OrderResponse
import com.tangem.datasource.local.visa.TangemPayCardFrozenStateStore
import com.tangem.datasource.local.visa.TangemPayStorage
import com.tangem.domain.common.wallets.UserWalletsListRepository
@ -24,14 +25,11 @@ import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.domain.visa.model.TangemPayCardFrozenState
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
private const val VALID_STATUS = "valid"
private const val TAG = "TangemPay: OnboardingRepository"
@Suppress("LongParameterList")
internal class DefaultOnboardingRepository @Inject constructor(
@ -110,25 +108,31 @@ internal class DefaultOnboardingRepository @Inject constructor(
return lastFetchedCustomerInfoMap[userWalletId]
}
override suspend fun createOrder(userWalletId: UserWalletId) = withContext(dispatcherProvider.io) {
launch {
catch(
block = {
val walletAddress = requestHelper.getCustomerWalletAddress(userWalletId)
val response = requestHelper.performRequest(userWalletId) { authHeader ->
tangemPayApi.createOrder(authHeader, body = OrderRequest(walletAddress))
}.getOrNull()
override suspend fun createOrder(userWalletId: UserWalletId): Either<VisaApiError, String> =
withContext(dispatcherProvider.io) {
val existingOrderId = getOrderId(userWalletId)
if (existingOrderId != null) {
val orderStatus = requestHelper.performRequest(userWalletId) { authHeader ->
tangemPayApi.getOrder(authHeader, existingOrderId)
}.map { it.result?.status }.getOrNull()
val result = requireNotNull(response?.result)
val customerWalletAddress = requireNotNull(result.data.customerWalletAddress)
tangemPayStorage.storeOrderId(customerWalletAddress, result.id)
},
catch = {
Timber.tag(TAG).e("createOrder: $it")
},
)
if (orderStatus == OrderResponse.Result.Status.NEW ||
orderStatus == OrderResponse.Result.Status.PROCESSING
) {
return@withContext existingOrderId.right()
}
}
val walletAddress = requestHelper.getCustomerWalletAddress(userWalletId)
requestHelper.performRequest(userWalletId) { authHeader ->
val data = OrderRequest.Data(customerWalletAddress = walletAddress)
tangemPayApi.createOrder(authHeader, body = OrderRequest(data = data))
}.map { response ->
val result = requireNotNull(response.result)
tangemPayStorage.storeOrderId(walletAddress, result.id)
result.id
}
}
}
private fun getUserWallet(userWalletId: UserWalletId): UserWallet {
return userWalletsListRepository.userWallets.value?.firstOrNull { it.walletId == userWalletId }

View file

@ -9,7 +9,7 @@ data class TokenMarketListConfig(
) {
enum class Order {
ByRating, Trending, Buyers, TopGainers, TopLosers, Staking, YieldSupply,
ByRating, Trending, Buyers, TopGainers, TopLosers
}
enum class Interval {

View file

@ -1,24 +0,0 @@
package com.tangem.domain.markets
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.repositories.MarketsTokenRepository
import com.tangem.domain.promo.PromoRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
class ShouldShowYieldModeMarketPromoUseCase(
private val promoRepository: PromoRepository,
private val marketsTokenRepository: MarketsTokenRepository,
) {
operator fun invoke(appCurrency: AppCurrency, interval: TokenMarketListConfig.Interval): Flow<Boolean> {
val hideClickedFlow = promoRepository.isMarketsYieldSupplyNotificationHideClicked()
return hideClickedFlow.map { hideClicked ->
!hideClicked && marketsTokenRepository.showYieldModePromo(
appCurrency = appCurrency,
interval = interval,
)
}
}
}

View file

@ -1,6 +1,5 @@
package com.tangem.domain.markets.repositories
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.*
import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency
@ -54,6 +53,4 @@ interface MarketsTokenRepository {
* @param tokenId token id
*/
suspend fun getTokenExchanges(tokenId: CryptoCurrency.RawID): List<TokenMarketExchange>
suspend fun showYieldModePromo(appCurrency: AppCurrency, interval: TokenMarketListConfig.Interval): Boolean
}

View file

@ -16,10 +16,6 @@ interface PromoRepository {
suspend fun setNeverToShowTokenPromo(promoId: PromoId)
fun isMarketsYieldSupplyNotificationHideClicked(): Flow<Boolean>
suspend fun setMarketsYieldSupplyNotificationHideClicked()
suspend fun isMoonpayPromoActive(): Boolean
// endregion

View file

@ -1,6 +1,7 @@
package com.tangem.domain.pay.model
data class OrderData(
val customerId: String,
val status: OrderStatus,
val withdrawTxHash: String?,
)

View file

@ -6,7 +6,6 @@ import com.tangem.domain.models.TangemPayEligibilityType
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.model.CustomerInfo
import com.tangem.domain.visa.error.VisaApiError
import kotlinx.coroutines.Job
interface OnboardingRepository {
@ -18,7 +17,7 @@ interface OnboardingRepository {
suspend fun getCustomerInfo(userWalletId: UserWalletId): Either<VisaApiError, CustomerInfo>
suspend fun createOrder(userWalletId: UserWalletId): Job
suspend fun createOrder(userWalletId: UserWalletId): Either<VisaApiError, String>
suspend fun clearOrderId(userWalletId: UserWalletId)

View file

@ -130,11 +130,13 @@ class TangemPayMainScreenCustomerInfoUseCase(
}
.map { customerInfo ->
Timber.tag(TAG).i("customerInfo")
if (customerInfo.cardInfo == null && customerInfo.kycStatus == KycStatus.APPROVED) {
// If order id wasn't saved -> start order creation and get customer info
if (customerInfo.productInstance == null) {
onboardingRepository.createOrder(userWalletId)
Timber.tag("ddk9499").d("TangemPayMainScreenCustomerInfoUseCase.proceedWithoutOrder: ")
MainScreenCustomerInfo(info = customerInfo, orderStatus = OrderStatus.NEW)
} else {
MainScreenCustomerInfo(info = customerInfo, orderStatus = OrderStatus.COMPLETED)
}
MainScreenCustomerInfo(info = customerInfo, orderStatus = OrderStatus.UNKNOWN)
}
}
@ -149,27 +151,33 @@ class TangemPayMainScreenCustomerInfoUseCase(
},
ifRight = { orderData ->
when (orderData.status) {
// Kyc is passed and user waits for order creation -> no need to get customer info
OrderStatus.NEW,
OrderStatus.PROCESSING,
-> MainScreenCustomerInfo(
info = CustomerInfo(
customerId = null,
productInstance = null,
kycStatus = KycStatus.APPROVED,
cardInfo = null,
),
orderStatus = orderData.status,
).right()
-> {
onboardingRepository.getCustomerInfo(userWalletId = userWalletId)
.mapLeft { it.mapErrorForCustomer() }
.map { customerInfo ->
MainScreenCustomerInfo(info = customerInfo, orderStatus = orderData.status)
}
}
// Order cancelled. No need to get customer info
OrderStatus.CANCELED -> {
MainScreenCustomerInfo(
info = CustomerInfo(
customerId = null,
productInstance = null,
kycStatus = KycStatus.INIT,
cardInfo = null,
),
orderStatus = OrderStatus.CANCELED,
).right()
}
// Order was created/cancelled -> clear order id and get customer info
OrderStatus.COMPLETED,
OrderStatus.CANCELED,
OrderStatus.UNKNOWN,
-> {
onboardingRepository.clearOrderId(userWalletId)
// If order was cancelled -> start order creation
if (orderData.status == OrderStatus.CANCELED) onboardingRepository.createOrder(userWalletId)
onboardingRepository.getCustomerInfo(userWalletId = userWalletId)
.mapLeft { it.mapErrorForCustomer() }
.map { customerInfo ->

View file

@ -353,8 +353,6 @@ internal class FeedComponentModel @Inject constructor(
SortByTypeUM.ExperiencedBuyers,
SortByTypeUM.TopGainers,
SortByTypeUM.TopLosers,
SortByTypeUM.Staking,
SortByTypeUM.YieldSupply,
-> {
analyticsEventHandler.send(
FeedAnalyticsEvent.TokenListOpened(
@ -439,8 +437,6 @@ internal class FeedComponentModel @Inject constructor(
SortByTypeUM.ExperiencedBuyers -> TokenMarketListConfig.Order.Buyers
SortByTypeUM.TopGainers -> TokenMarketListConfig.Order.TopGainers
SortByTypeUM.TopLosers -> TokenMarketListConfig.Order.TopLosers
SortByTypeUM.Staking -> TokenMarketListConfig.Order.Staking
SortByTypeUM.YieldSupply -> TokenMarketListConfig.Order.YieldSupply
}
}

View file

@ -341,8 +341,6 @@ internal class FeedMarketsBatchFlowManager(
TokenMarketListConfig.Order.Buyers -> SortByTypeUM.ExperiencedBuyers
TokenMarketListConfig.Order.TopGainers -> SortByTypeUM.TopGainers
TokenMarketListConfig.Order.TopLosers -> SortByTypeUM.TopLosers
TokenMarketListConfig.Order.Staking -> SortByTypeUM.Staking
TokenMarketListConfig.Order.YieldSupply -> SortByTypeUM.YieldSupply
}
}
}

View file

@ -1,7 +1,6 @@
package com.tangem.features.feed.model.market.list
import androidx.compose.runtime.Stable
import arrow.core.Either
import arrow.core.getOrElse
import com.tangem.common.ui.markets.models.MarketsListItemUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
@ -12,20 +11,11 @@ import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.markets.GetMarketsTokenListFlowUseCase
import com.tangem.domain.markets.ShouldShowYieldModeMarketPromoUseCase
import com.tangem.domain.markets.TokenMarketListConfig
import com.tangem.domain.markets.toSerializableParam
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.promo.PromoRepository
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
import com.tangem.domain.settings.usercountry.models.UserCountry
import com.tangem.domain.settings.usercountry.models.UserCountryError
import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
import com.tangem.features.feed.components.market.list.DefaultMarketsTokenListComponent
import com.tangem.features.feed.model.market.list.analytics.MarketsListAnalyticsEvent
import com.tangem.features.feed.model.market.list.state.ListUM
import com.tangem.features.feed.model.market.list.state.MarketsListUM
import com.tangem.features.feed.model.market.list.state.MarketsNotificationUM
import com.tangem.features.feed.model.market.list.state.SortByTypeUM
import com.tangem.features.feed.model.market.list.statemanager.MarketsListBatchFlowManager
import com.tangem.features.feed.model.market.list.statemanager.MarketsListUMStateManager
@ -49,11 +39,8 @@ internal class MarketsListModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
getMarketsTokenListFlowUseCase: GetMarketsTokenListFlowUseCase,
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
shouldShowYieldModeMarketPromoUseCase: ShouldShowYieldModeMarketPromoUseCase,
paramsContainer: ParamsContainer,
private val promoRepository: PromoRepository,
private val analyticsEventHandler: AnalyticsEventHandler,
private val getUserCountryUseCase: GetUserCountryUseCase,
) : Model() {
private val updateQuotesJob = JobHolder()
@ -126,61 +113,30 @@ internal class MarketsListModel @Inject constructor(
flow = searchMarketsListManager.uiItems,
flow2 = searchMarketsListManager.isInInitialLoadingErrorState,
flow3 = searchMarketsListManager.isSearchNotFoundState,
flow4 = shouldShowYieldModeMarketPromoUseCase(
appCurrency = currentAppCurrency.value,
interval = marketsListUMStateManager.selectedInterval.toBatchRequestInterval(),
).conflate(),
flow5 = getUserCountryUseCase.invoke(),
) { uiItems, isInInitialLoadingErrorState, isSearchNotFoundState, isYieldModePromo, userCountry ->
) { uiItems, isInInitialLoadingErrorState, isSearchNotFoundState ->
MarketsItemsData(
items = uiItems,
isInErrorState = isInInitialLoadingErrorState,
isSearchNotFound = isSearchNotFoundState,
shouldShowYieldModePromo = isYieldModePromo,
userCountry = userCountry,
)
}
} else {
combine(
flow = mainMarketsListManager.uiItems,
flow2 = mainMarketsListManager.isInInitialLoadingErrorState,
flow3 = shouldShowYieldModeMarketPromoUseCase(
appCurrency = currentAppCurrency.value,
interval = marketsListUMStateManager.selectedInterval.toBatchRequestInterval(),
).conflate(),
flow4 = getUserCountryUseCase.invoke(),
) { uiItems, isInInitialLoadingErrorState, shouldShowYieldModePromo, userCountry ->
) { uiItems, isInInitialLoadingErrorState ->
MarketsItemsData(
items = uiItems,
isInErrorState = isInInitialLoadingErrorState,
isSearchNotFound = false,
shouldShowYieldModePromo = shouldShowYieldModePromo,
userCountry = userCountry,
)
}
}
}.collect { marketsItemsData ->
val isApplyFCARestrictions = marketsItemsData.userCountry.getOrNull().needApplyFCARestrictions()
val shouldShowYieldModePromo = marketsItemsData.shouldShowYieldModePromo && !isApplyFCARestrictions
if (marketsListUMStateManager.state.value.marketsNotificationUM == null && shouldShowYieldModePromo) {
analyticsEventHandler.send(MarketsListAnalyticsEvent.YieldModePromoShown())
}
marketsListUMStateManager.onUiItemsChanged(
uiItems = marketsItemsData.items,
isInErrorState = marketsItemsData.isInErrorState,
isSearchNotFound = marketsItemsData.isSearchNotFound,
marketsNotificationUM = if (shouldShowYieldModePromo) {
MarketsNotificationUM.YieldSupplyPromo(
onClick = {
analyticsEventHandler.send(MarketsListAnalyticsEvent.YieldModeMoreInfoClicked())
marketsListUMStateManager.selectedSortByType = SortByTypeUM.YieldSupply
},
onCloseClick = { onYieldModeNotificationCloseClick() },
)
} else {
null
},
)
}
}
@ -282,14 +238,6 @@ internal class MarketsListModel @Inject constructor(
mainMarketsListManager.reload()
}
private fun MarketsListUM.TrendInterval.toBatchRequestInterval(): TokenMarketListConfig.Interval {
return when (this) {
MarketsListUM.TrendInterval.H24 -> TokenMarketListConfig.Interval.H24
MarketsListUM.TrendInterval.D7 -> TokenMarketListConfig.Interval.WEEK
MarketsListUM.TrendInterval.M1 -> TokenMarketListConfig.Interval.MONTH
}
}
private fun initAnalytics() {
searchMarketsListManager
.isSearchNotFoundState
@ -331,18 +279,9 @@ internal class MarketsListModel @Inject constructor(
}.saveIn(updateQuotesJob)
}
private fun onYieldModeNotificationCloseClick() {
analyticsEventHandler.send(MarketsListAnalyticsEvent.YieldModePromoClosed())
modelScope.launch {
promoRepository.setMarketsYieldSupplyNotificationHideClicked()
}
}
private class MarketsItemsData(
val items: ImmutableList<MarketsListItemUM>,
val isInErrorState: Boolean,
val isSearchNotFound: Boolean,
val shouldShowYieldModePromo: Boolean,
val userCountry: Either<UserCountryError, UserCountry>,
)
}

View file

@ -23,8 +23,6 @@ internal sealed class MarketsListAnalyticsEvent(
SortByTypeUM.ExperiencedBuyers -> "Buyers"
SortByTypeUM.TopGainers -> "Gainers"
SortByTypeUM.TopLosers -> "Losers"
SortByTypeUM.Staking -> "Staking"
SortByTypeUM.YieldSupply -> "Yield Supply"
},
"Period" to when (interval) {
MarketsListUM.TrendInterval.H24 -> "24h"

View file

@ -19,7 +19,6 @@ internal data class MarketsListUM(
val selectedInterval: TrendInterval,
val onIntervalClick: (TrendInterval) -> Unit,
val onSortByButtonClick: () -> Unit,
val marketsNotificationUM: MarketsNotificationUM?,
val onSearchClicked: () -> Unit,
) {
val isInSearchMode
@ -43,8 +42,6 @@ internal enum class SortByTypeUM(val text: TextReference) {
ExperiencedBuyers(resourceReference(R.string.markets_sort_by_experienced_buyers_title)),
TopGainers(resourceReference(R.string.markets_sort_by_top_gainers_title)),
TopLosers(resourceReference(R.string.markets_sort_by_top_losers_title)),
Staking(resourceReference(R.string.common_staking)),
YieldSupply(resourceReference(R.string.markets_sort_by_yield_mode_title)),
}
@Immutable

View file

@ -1,22 +0,0 @@
package com.tangem.features.feed.model.market.list.state
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.features.feed.impl.R
internal sealed class MarketsNotificationUM(val config: NotificationConfig) {
data class YieldSupplyPromo(
val onClick: () -> Unit,
val onCloseClick: () -> Unit,
) : MarketsNotificationUM(
config = NotificationConfig(
iconResId = R.drawable.img_yield_supply_in_market_notification,
title = resourceReference(R.string.markets_yield_supply_banner_title),
subtitle = TextReference.EMPTY,
onClick = onClick,
onCloseClick = onCloseClick,
),
)
}

View file

@ -24,8 +24,10 @@ import com.tangem.utils.coroutines.saveIn
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
private const val LOG_EVENTS = true
@ -305,8 +307,6 @@ internal class MarketsListBatchFlowManager(
SortByTypeUM.ExperiencedBuyers -> TokenMarketListConfig.Order.Buyers
SortByTypeUM.TopGainers -> TokenMarketListConfig.Order.TopGainers
SortByTypeUM.TopLosers -> TokenMarketListConfig.Order.TopLosers
SortByTypeUM.Staking -> TokenMarketListConfig.Order.Staking
SortByTypeUM.YieldSupply -> TokenMarketListConfig.Order.YieldSupply
}
}

View file

@ -99,7 +99,6 @@ internal class MarketsListUMStateManager(
isInErrorState: Boolean,
isSearchNotFound: Boolean,
uiItems: ImmutableList<MarketsListItemUM>,
marketsNotificationUM: MarketsNotificationUM?,
) {
state.update { marketsListUM ->
when {
@ -115,19 +114,13 @@ internal class MarketsListUMStateManager(
marketsListUM.copy(list = ListUM.Loading)
}
else -> {
marketsListUM.updateItems(
newItems = uiItems,
marketsNotificationUM = marketsNotificationUM,
)
marketsListUM.updateItems(newItems = uiItems)
}
}
}
}
private fun MarketsListUM.updateItems(
newItems: ImmutableList<MarketsListItemUM>,
marketsNotificationUM: MarketsNotificationUM?,
): MarketsListUM {
private fun MarketsListUM.updateItems(newItems: ImmutableList<MarketsListItemUM>): MarketsListUM {
val currentState = this
if (isInSearchMode.not() || currentState.showUnder100kButtonAlreadyPressed()) {
@ -139,7 +132,6 @@ internal class MarketsListUMStateManager(
shouldShowUnder100kTokensNotificationWasHidden = currentState
.showUnder100kButtonAlreadyPressed(),
),
marketsNotificationUM = marketsNotificationUM,
)
}
@ -243,7 +235,6 @@ internal class MarketsListUMStateManager(
onOptionClicked = ::onBottomSheetOptionClicked,
),
),
marketsNotificationUM = null,
onSearchClicked = {
analyticsEventHandler.send(FeedAnalyticsEvent.TokenSearchedClicked())
changeSearchBarIsActive(true)

View file

@ -2,7 +2,6 @@ package com.tangem.features.feed.ui.feed.preview
import com.tangem.common.ui.charts.state.MarketChartRawData
import com.tangem.common.ui.markets.models.MarketsListItemUM
import com.tangem.features.feed.ui.feed.components.articles.ArticleConfigUM
import com.tangem.core.ui.R
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.components.label.entity.LabelLeadingContentUM
@ -15,6 +14,7 @@ import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.feed.model.market.list.state.SortByTypeUM
import com.tangem.features.feed.ui.earn.state.EarnListItemUM
import com.tangem.features.feed.ui.earn.state.EarnListUM
import com.tangem.features.feed.ui.feed.components.articles.ArticleConfigUM
import com.tangem.features.feed.ui.feed.state.*
import kotlinx.collections.immutable.*
@ -77,8 +77,6 @@ internal object FeedListPreviewDataProvider {
items = items,
isSelected = false,
),
SortByTypeUM.Staking to MarketChartUM.Loading,
SortByTypeUM.TopLosers to MarketChartUM.Loading,
)
val ratingChart = if (includeErrorState) {

View file

@ -33,7 +33,9 @@ import com.tangem.core.ui.components.fields.TangemSearchBarDefaults
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.components.keyboardAsState
import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.extensions.*
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.LocalMainBottomSheetColor
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
@ -42,12 +44,10 @@ import com.tangem.features.feed.impl.R
import com.tangem.features.feed.model.market.list.state.*
import com.tangem.features.feed.ui.market.list.components.MarketsListLazyColumn
import com.tangem.features.feed.ui.market.list.components.MarketsListSortByBottomSheet
import com.tangem.features.feed.ui.market.list.components.YieldSupplyInMarketsPromoNotification
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.delay
private const val SHOW_MORE_KEY = "privacyPolicy"
private const val DELAY_FOR_FOCUS_REQUEST = 500L
@Composable
@ -152,40 +152,6 @@ private fun ColumnScope.Content(state: MarketsListUM, modifier: Modifier = Modif
onSortByClick = state.onSortByButtonClick,
)
}
AnimatedVisibility(
state.list !is ListUM.LoadingError && state.isInSearchMode.not() &&
state.selectedSortBy != SortByTypeUM.YieldSupply,
) {
val wrappedNotification = remember(state.list) { state.marketsNotificationUM }
val showMore = stringResourceSafe(R.string.common_show_more)
when (wrappedNotification) {
is MarketsNotificationUM.YieldSupplyPromo -> {
val description = stringResourceSafe(
R.string.markets_yield_supply_banner_description,
showMore,
)
val clickableDescription = annotatedReference {
append(description.substringBefore(showMore))
pushStringAnnotation(SHOW_MORE_KEY, "")
appendColored(showMore, TangemTheme.colors.text.accent)
pop()
}
YieldSupplyInMarketsPromoNotification(
config = wrappedNotification.config.copy(
subtitle = clickableDescription,
),
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing12),
)
}
else -> { /* no-op */
}
}
}
}
}
val strokeWidth = TangemTheme.dimens.size0_5
@ -376,10 +342,6 @@ private fun Preview() {
onDismissRequest = {},
content = SortByBottomSheetContentUM(selectedOption = SortByTypeUM.Rating) {},
),
marketsNotificationUM = MarketsNotificationUM.YieldSupplyPromo(
onClick = {},
onCloseClick = {},
),
onSearchClicked = {},
),
)

View file

@ -95,9 +95,14 @@ internal class TangemPayOnboardingModel @Inject constructor(
uiState.transformerUpdate(TangemPayOnboardingButtonLoadingTransformer(isLoading = true))
repository.getCustomerInfo(userWalletId = userWalletId)
.onRight { customerInfo ->
uiState.transformerUpdate(TangemPayOnboardingButtonLoadingTransformer(isLoading = false))
when {
customerInfo.kycStatus != KycStatus.APPROVED -> {
if (customerInfo.productInstance == null) {
repository.createOrder(userWalletId)
.onLeft { error ->
Timber.e("Error creating order before KYC: $error")
}
}
when (params) {
is TangemPayOnboardingComponent.Params.ContinueOnboarding -> openKyc(userWalletId)
else -> startOnboarding(userWalletId)
@ -105,6 +110,7 @@ internal class TangemPayOnboardingModel @Inject constructor(
}
else -> back()
}
uiState.transformerUpdate(TangemPayOnboardingButtonLoadingTransformer(isLoading = false))
}
.onLeft { startOnboarding(userWalletId) }
}
@ -178,6 +184,12 @@ internal class TangemPayOnboardingModel @Inject constructor(
if (customerInfo.kycStatus == KycStatus.APPROVED) {
back()
} else {
if (customerInfo.productInstance == null) {
repository.createOrder(userWalletId)
.onLeft { error ->
Timber.e("Error creating order before KYC: $error")
}
}
openKyc(userWalletId)
}
},

View file

@ -56,7 +56,7 @@ internal class TangemPayUpdateInfoStateTransformer(
value.orderStatus == OrderStatus.CANCELED -> createCancelledState(customerId)
value.info.kycStatus != KycStatus.APPROVED && !value.info.customerId.isNullOrEmpty() ->
createKycInProgressState(kycStatus = value.info.kycStatus, customerId = customerId)
cardInfo != null && productInstance != null ->
cardInfo != null && productInstance != null && value.orderStatus == OrderStatus.COMPLETED ->
getCardInfoState(customerId, cardInfo, productInstance)
else -> createIssueProgressState()
}