Updated on 2026-08-14
This commit is contained in:
parent
54800d32cc
commit
faa58cf142
39 changed files with 722 additions and 245 deletions
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.di.domain
|
|||
import com.tangem.domain.markets.GetMarketsTokenListFlowUseCase
|
||||
import com.tangem.domain.markets.GetTokenMarketInfoUseCase
|
||||
import com.tangem.domain.markets.GetTokenPriceChartUseCase
|
||||
import com.tangem.domain.markets.GetTokenQuotesUseCase
|
||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -33,4 +34,10 @@ object MarketsDomainModule {
|
|||
fun provideGetTokenMarketInfoUseCase(marketsTokenRepository: MarketsTokenRepository): GetTokenMarketInfoUseCase {
|
||||
return GetTokenMarketInfoUseCase(marketsTokenRepository = marketsTokenRepository)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGetTokenQuotesUseCase(marketsTokenRepository: MarketsTokenRepository): GetTokenQuotesUseCase {
|
||||
return GetTokenQuotesUseCase(marketsTokenRepository = marketsTokenRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -295,7 +295,7 @@ private fun MarketChartPreview(
|
|||
updateLook {
|
||||
it.copy(
|
||||
xAxisFormatter = { value ->
|
||||
value.toLong().toTimeFormat(DateTimeFormatters.dateMMMMd)
|
||||
value.toLong().toTimeFormat(DateTimeFormatters.dateMMMdd)
|
||||
},
|
||||
yAxisFormatter = { value ->
|
||||
value.setScale(3, RoundingMode.HALF_UP).toPlainString()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<resources>
|
||||
<string name="add_custom_token_choose_network">Netzwerk wählen</string>
|
||||
<string name="add_custom_token_title">Benutzerdef. Token hinzuf.</string>
|
||||
<string name="add_custom_token_title">Token anlegen</string>
|
||||
<string name="add_tokens_title">Token verwalten</string>
|
||||
<string name="address_qr_code_message_format">Sende nur %1$s ( %2$s ) vom %3$s -Netzwerk an diese Adresse. Die Verwendung anderer Token und Netzwerke kann zum Verlust von Geldern führen.</string>
|
||||
<string name="alert_button_how_to_scan">So scannt man</string>
|
||||
|
|
@ -88,7 +88,7 @@
|
|||
<string name="common_copy">Kopieren</string>
|
||||
<string name="common_copy_address">Adresse kopieren</string>
|
||||
<string name="common_create">Erstellen</string>
|
||||
<string name="common_custom">Benutzerdef.</string>
|
||||
<string name="common_custom">Eigen</string>
|
||||
<plurals name="common_days">
|
||||
<item quantity="one">%d tag</item>
|
||||
<item quantity="other">%d tage</item>
|
||||
|
|
@ -126,6 +126,7 @@
|
|||
<string name="common_passphrase">Passphrase</string>
|
||||
<string name="common_paste">Einfügen</string>
|
||||
<string name="common_range">%1$s-%2$s</string>
|
||||
<string name="common_range_with_space">%1$s — %2$s</string>
|
||||
<string name="common_read_more">Weiterlesen</string>
|
||||
<string name="common_receive">Empfangen</string>
|
||||
<string name="common_reject">Ablehnen</string>
|
||||
|
|
@ -278,7 +279,7 @@
|
|||
<string name="feedback_subject_support">Rückmeldung</string>
|
||||
<string name="feedback_subject_support_tangem">Feedback zu Tangem</string>
|
||||
<string name="feedback_subject_tx_failed">Eine Transaktion kann nicht gesendet werden</string>
|
||||
<string name="give_permission_current_transaction">Aktuelle Transaktion</string>
|
||||
<string name="give_permission_current_transaction">Nur diese</string>
|
||||
<string name="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="give_permission_policy_type_footer">Gib das Genehmigungslimit für das ausgewählte Token an</string>
|
||||
<string name="give_permission_rows_amount">Betrag %s</string>
|
||||
|
|
@ -341,7 +342,7 @@
|
|||
<string name="markets_available_networks">Verfügbare Netzwerke</string>
|
||||
<string name="markets_common_my_portfolio">Mein Portfolio</string>
|
||||
<string name="markets_common_title">Markt</string>
|
||||
<string name="markets_generate_addresses_notification">Um Adressen für ausgewählte Netzwerke zu generieren, musst du eine Tangem-Karte scannen/ einsetzen</string>
|
||||
<string name="markets_generate_addresses_notification">Um Adressen für ausgewählte Netzwerke zu generieren, musst du eine Tangem-Karte scannen</string>
|
||||
<string name="markets_loading_error_title">Die Daten konnten nicht geladen werden...</string>
|
||||
<string name="markets_quick_actions">Schnelle Aktionen</string>
|
||||
<string name="markets_search_result_title">Ergebnis</string>
|
||||
|
|
@ -446,7 +447,7 @@
|
|||
<string name="onboarding_exit_alert_title">Möchtest du den Aktivierungsprozess abbrechen?</string>
|
||||
<string name="onboarding_getting_started">Erste Schritte</string>
|
||||
<string name="onboarding_linking_error_card_with_wallets">Für die Karte, die du hinzufügen möchtest, wurde bereits eine andere Wallets erstellt. Wenn du Guthaben auf dieser Wallets hast, hebe es bitte ab, setze diese Karte zurück und füge sie als Backup hinzu.</string>
|
||||
<string name="onboarding_navbar_title_creating_backup">Erstellen eines Backups</string>
|
||||
<string name="onboarding_navbar_title_creating_backup">Backups anlegen</string>
|
||||
<string name="onboarding_seed_button_read_more">Lese mehr über die Seed-Phrase</string>
|
||||
<plurals name="onboarding_seed_generate_message_words_count">
|
||||
<item quantity="one">leer</item>
|
||||
|
|
@ -499,7 +500,7 @@
|
|||
<string name="organize_tokens_group">Gruppe erstellen</string>
|
||||
<string name="organize_tokens_sort_by_balance">Nach Guthaben</string>
|
||||
<string name="organize_tokens_title">Token organisieren</string>
|
||||
<string name="organize_tokens_ungroup">Gruppierung aufh.</string>
|
||||
<string name="organize_tokens_ungroup">Gruppe löschen</string>
|
||||
<string name="qr_scanner_camera_denied_gallery_button">Wählen aus der Galerie aus</string>
|
||||
<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>
|
||||
|
|
@ -647,7 +648,7 @@
|
|||
<string name="staking_active">Aktiv</string>
|
||||
<string name="staking_active_footer">Um deine Kryptos zu unstaken, klick hier.</string>
|
||||
<string name="staking_amount_requirement_error">Die Anzahl der zu stakenden Krypros muss mindesten %s betragen</string>
|
||||
<string name="staking_claim_unstaked">nicht gestakte beanspruche</string>
|
||||
<string name="staking_claim_unstaked">Nicht gestakte beanspruche</string>
|
||||
<string name="staking_details_annual_percentage_rate">Jährliche prozentuale Rendite</string>
|
||||
<string name="staking_details_annual_percentage_rate_info">Die jährliche prozentuale Rendite, die du durch die Teilnahme am Staking erzielen kannst.</string>
|
||||
<string name="staking_details_apr">Effektiver Jahreszins</string>
|
||||
|
|
@ -671,7 +672,10 @@
|
|||
<string name="staking_details_warmup_period_info">Die zugewiesene Zeit für die Aktivierung der Teilnahme am Staking.</string>
|
||||
<string name="staking_migrate">Migrieren</string>
|
||||
<string name="staking_native">Natives Staking</string>
|
||||
<string name="staking_notification_earn_rewards_text">Mit Staking kannst du %1$s verdienen. Deine Staking-Belohnungen kommen alle ~%2$s Tage.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_day">Mit dem Staking kannst Du %1$s verdienen. Deine Staking-Belohnungen erhältst Du jeden tag.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_hour">Mit dem Staking kannst Du %1$s verdienen. Deine Staking-Belohnungen erhältst Du jede Stunde.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_month">Mit dem Staking kannst Du %1$s verdienen. Deine Staking-Belohnungen erhältst Du jeden Monat.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_week">Mit dem Staking kannst Du %1$s verdienen. Deine Staking-Belohnungen erhältst Du jede Woche.</string>
|
||||
<string name="staking_notification_earn_rewards_title">Verdiene Staking-Belohnungen</string>
|
||||
<string name="staking_notification_unstake_text">Die Belohnungen werden sofort nach dem unstaken gestoppt. Der unstakingprozess dauert %s.</string>
|
||||
<string name="staking_rebond">Erneut binden</string>
|
||||
|
|
@ -679,10 +683,11 @@
|
|||
<string name="staking_restake_rewards">Belohnungen erneut staken</string>
|
||||
<string name="staking_revoke">Widerrufen</string>
|
||||
<string name="staking_revote">Neuwahl</string>
|
||||
<string name="staking_reward_claiming_auto">automatisch</string>
|
||||
<string name="staking_reward_claiming_manual">händisch</string>
|
||||
<string name="staking_reward_claiming_auto">Automatisch</string>
|
||||
<string name="staking_reward_claiming_manual">Händisch</string>
|
||||
<string name="staking_reward_schedule_block">Block</string>
|
||||
<string name="staking_reward_schedule_day">Tag</string>
|
||||
<string name="staking_reward_schedule_each_day">Täglich</string>
|
||||
<string name="staking_reward_schedule_epoch">Epoche</string>
|
||||
<string name="staking_reward_schedule_era">Ära</string>
|
||||
<string name="staking_reward_schedule_hour">Stunde</string>
|
||||
|
|
@ -693,7 +698,7 @@
|
|||
<string name="staking_stake_more">Mehr staken</string>
|
||||
<string name="staking_title_stake">Stake %s</string>
|
||||
<string name="staking_title_unstake">Staking beenden%s</string>
|
||||
<string name="staking_unlocked_locked">gelocktes unlocken</string>
|
||||
<string name="staking_unlocked_locked">Gelocktes unlocken</string>
|
||||
<string name="staking_unstaked">Unstaken</string>
|
||||
<string name="staking_unstaked_footer">Prüfe, was nicht eingesetzt wurde, um dein Vermögen zu beanspruchen</string>
|
||||
<string name="staking_unstaking">Staking beenden</string>
|
||||
|
|
@ -834,7 +839,7 @@
|
|||
<string name="warning_backup_errors_title">Aktivierungsfehler</string>
|
||||
<string name="warning_beacon_chain_retirement_content">Laut den Entwicklern des BNB-Netzes wird die Unterstützung für den BEP-2-Standard im Juni 2024 enden. Um den Verlust von Vermögenswerten mit diesem Standard zu vermeiden, konvertiere bitte in den BEP-20 Standard. Nutze gerne unseren Swap-Service, um sie auf das BNB Smart Chain Netzwerk zu übertragen.</string>
|
||||
<string name="warning_beacon_chain_retirement_title">BNB Beacon Chain wird abgeschaltet</string>
|
||||
<string name="warning_button_could_be_better">ausbaufähig</string>
|
||||
<string name="warning_button_could_be_better">Ausbaufähig</string>
|
||||
<string name="warning_button_like_it">Gefällt mir</string>
|
||||
<string name="warning_button_ok">OK, habe ich verstanden!</string>
|
||||
<string name="warning_button_really_cool">Echt toll!</string>
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@
|
|||
<string name="common_passphrase">Passphrase</string>
|
||||
<string name="common_paste">Coller</string>
|
||||
<string name="common_range">%1$s-%2$s</string>
|
||||
<string name="common_range_with_space">%1$s — %2$s</string>
|
||||
<string name="common_read_more">En savoir plus</string>
|
||||
<string name="common_receive">Recevoir</string>
|
||||
<string name="common_reject">Rejeter</string>
|
||||
|
|
@ -269,7 +270,7 @@
|
|||
<string name="feedback_subject_support">Commentaires</string>
|
||||
<string name="feedback_subject_support_tangem">Commentaires sur Tangem</string>
|
||||
<string name="feedback_subject_tx_failed">Impossible d\'envoyer une transaction</string>
|
||||
<string name="give_permission_current_transaction">Transaction en cours</string>
|
||||
<string name="give_permission_current_transaction">Transaction</string>
|
||||
<string name="give_permission_fee_footer">Le réseau facturera des frais d\'approbation de jeton pour vérifier que vous autorisez l\'utilisation de votre jeton pour l\'échange.</string>
|
||||
<string name="give_permission_policy_type_footer">Spécifiez la limite approuvée pour le jeton sélectionné</string>
|
||||
<string name="give_permission_rows_amount">Montant %s</string>
|
||||
|
|
@ -328,7 +329,7 @@
|
|||
<string name="markets_add_to_portfolio_button">Ajouter au portfolio</string>
|
||||
<string name="markets_common_my_portfolio">Mon portfolio</string>
|
||||
<string name="markets_common_title">Marché</string>
|
||||
<string name="markets_generate_addresses_notification">Pour générer des adresses pour les réseaux sélectionnés, vous devez scanner votre carte Tangem.</string>
|
||||
<string name="markets_generate_addresses_notification">Pour générer des adresses pour les réseaux sélectionnés, vous devez scanner votre carte Tangem</string>
|
||||
<string name="markets_select_wallet">Sélectionnez un portefeuille</string>
|
||||
<string name="markets_sort_by_title">Trier par</string>
|
||||
<string name="markets_token_details_insights">Idées</string>
|
||||
|
|
@ -594,7 +595,6 @@
|
|||
<string name="staking_details_warmup_period">Période d\'échauffement</string>
|
||||
<string name="staking_details_warmup_period_info">Le temps imparti pour activer la participation au staking.</string>
|
||||
<string name="staking_native">Native staking</string>
|
||||
<string name="staking_notification_earn_rewards_text">Le staking vous permet d\'en gagner %1$s. Vos récompenses de staking arrivent tous les ~%2$s jours.</string>
|
||||
<string name="staking_notification_earn_rewards_title">Gagnez des récompenses de staking</string>
|
||||
<string name="staking_rewards">Récompenses</string>
|
||||
<string name="staking_stake_more">Staker plus</string>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
<string name="common_save_changes">Mantieni le modifiche</string>
|
||||
<string name="common_send">Invia</string>
|
||||
<string name="common_success">Con successo</string>
|
||||
<string name="common_warning">Avviso</string>
|
||||
<string name="details_manage_security_access_code">Codice di accesso</string>
|
||||
<string name="details_manage_security_access_code_description">Prima di scansionare la carta sarà necessario inserire il codice di accesso corretto</string>
|
||||
<string name="details_manage_security_long_tap">Mantenimento della carta</string>
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@
|
|||
<string name="common_passphrase">パスフレーズ</string>
|
||||
<string name="common_paste">ペースト</string>
|
||||
<string name="common_range">%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_receive">受け取る</string>
|
||||
<string name="common_reject">拒否</string>
|
||||
|
|
@ -276,7 +277,7 @@
|
|||
<string name="feedback_subject_support">フィードバック</string>
|
||||
<string name="feedback_subject_support_tangem">Tangemへのフィードバック</string>
|
||||
<string name="feedback_subject_tx_failed">取引を送信できません</string>
|
||||
<string name="give_permission_current_transaction">現在の取引</string>
|
||||
<string name="give_permission_current_transaction">取引</string>
|
||||
<string name="give_permission_fee_footer">ネットワークは、あなたがトークンのスワップを承認していることを確認するために、トークン承認手数料を請求します。</string>
|
||||
<string name="give_permission_policy_type_footer">選択したトークンの承認制限を指定します</string>
|
||||
<string name="give_permission_rows_amount">金額%s</string>
|
||||
|
|
@ -636,7 +637,7 @@
|
|||
<string name="staking_active_footer">資産のステーキングを解除するには、ここをクリックしてください。</string>
|
||||
<string name="staking_amount_requirement_error">ステーキング金額は %s 以上である必要があります</string>
|
||||
<string name="staking_claim_unstaked">ステーキング解除分を請求する</string>
|
||||
<string name="staking_details_annual_percentage_rate">APY</string>
|
||||
<string name="staking_details_annual_percentage_rate">年率</string>
|
||||
<string name="staking_details_annual_percentage_rate_info">ステーキングに参加することで得られる年間収益率。</string>
|
||||
<string name="staking_details_apr">APR</string>
|
||||
<string name="staking_details_available">利用可能</string>
|
||||
|
|
@ -659,7 +660,10 @@
|
|||
<string name="staking_details_warmup_period_info">ステーキングへの参加を有効にするために割り当てられた時間。</string>
|
||||
<string name="staking_migrate">移行</string>
|
||||
<string name="staking_native">ネイティブステーキング</string>
|
||||
<string name="staking_notification_earn_rewards_text">ステーキングにより%1$sを獲得できます。ステーキング報酬は ~ %2$s日ごとに届きます。</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_day">ステーキングにより%1$sを獲得できます。ステーキング報酬は毎日受け取れます。</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_hour">ステーキングにより%1$sを獲得できます。ステーキング報酬は1時間ごとに受け取れます。</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_month">ステーキングにより%1$sを獲得できます。ステーキング報酬は毎月受け取れます。</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_week">ステーキングにより%1$sを獲得できます。ステーキング報酬は毎週受け取れます。</string>
|
||||
<string name="staking_notification_earn_rewards_title">ステーキング報酬を獲得</string>
|
||||
<string name="staking_notification_unstake_text">ステーキング解除後、報酬の獲得はすぐに停止します。ステーキング解除プロセスには%sかかります。</string>
|
||||
<string name="staking_rebond">再結束</string>
|
||||
|
|
@ -671,6 +675,7 @@
|
|||
<string name="staking_reward_claiming_manual">手動</string>
|
||||
<string name="staking_reward_schedule_block">ブロック</string>
|
||||
<string name="staking_reward_schedule_day">日</string>
|
||||
<string name="staking_reward_schedule_each_day">毎日</string>
|
||||
<string name="staking_reward_schedule_epoch">エポック</string>
|
||||
<string name="staking_reward_schedule_era">時代</string>
|
||||
<string name="staking_reward_schedule_hour">時</string>
|
||||
|
|
@ -686,6 +691,7 @@
|
|||
<string name="staking_unstaked_footer">資産を請求するために、unstakedを確認してください</string>
|
||||
<string name="staking_unstaking">ステーキング解除</string>
|
||||
<string name="staking_validator">バリデーター</string>
|
||||
<string name="staking_validators">バリデーター</string>
|
||||
<string name="staking_vote">投票する</string>
|
||||
<string name="staking_vote_locked">投票はロックされています</string>
|
||||
<string name="staking_withdraw">引き出す</string>
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@
|
|||
</plurals>
|
||||
<string name="card_settings_access_code_recovery_disabled_description">Отключите эту опцию, если не хотите, чтобы эта карта использовалась для сброса кодов доступа на другие карты этого кошелька. Обратите внимание, сброс кода также не будет доступен на этой карте.</string>
|
||||
<string name="card_settings_access_code_recovery_enabled_description">Использовать эту карту для сброса кода доступа на других картах в этом кошельке</string>
|
||||
<string name="card_settings_access_code_recovery_title">Восстановление кода доступа</string>
|
||||
<string name="card_settings_access_code_recovery_title">Восстановление доступа</string>
|
||||
<string name="card_settings_action_sheet_reset">Сбросить</string>
|
||||
<string name="card_settings_action_sheet_title">Вы уверены, что хотите это сделать?</string>
|
||||
<string name="card_settings_change_access_code">Смена кода доступа</string>
|
||||
|
|
@ -128,6 +128,7 @@
|
|||
<string name="common_passphrase">Парольная фраза</string>
|
||||
<string name="common_paste">Вставить</string>
|
||||
<string name="common_range">%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_receive">Получить</string>
|
||||
<string name="common_reject">Отклонить</string>
|
||||
|
|
@ -628,7 +629,6 @@
|
|||
<string name="staking_active_footer">Для завершения стейкинга нажмите сюда</string>
|
||||
<string name="staking_amount_requirement_error">Сумма для стейкинга должна быть не менее %s</string>
|
||||
<string name="staking_claim_unstaked">Забрать средства</string>
|
||||
<string name="staking_details_annual_percentage_rate">APY</string>
|
||||
<string name="staking_details_annual_percentage_rate_info">Годовой процентный доход, который вы можете получить от участия в стейкинге.</string>
|
||||
<string name="staking_details_apr">APR</string>
|
||||
<string name="staking_details_available">Доступно</string>
|
||||
|
|
@ -650,13 +650,26 @@
|
|||
<string name="staking_details_warmup_period_info">Время, необходимое для начала процесса стейкинга и активации процесса начисления наград</string>
|
||||
<string name="staking_migrate">Переместить</string>
|
||||
<string name="staking_native">Нативный стейкинг</string>
|
||||
<string name="staking_notification_earn_rewards_text">Стейкинг дает возможность вам получать %1$s. Награда будет зачисляться каждый %2$s</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_day">Стейкинг дает возможность вам получать %1$s. Награда будет зачисляться каждый день.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_hour">Стейкинг дает возможность вам получать %1$s. Награда будет зачисляться каждый час.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_month">Стейкинг дает возможность вам получать %1$s. Награда будет зачисляться каждый месяц.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_week">Стейкинг дает возможность вам получать %1$s. Награда будет зачисляться каждую неделю.</string>
|
||||
<string name="staking_notification_earn_rewards_title">Получите награду за стейкинг</string>
|
||||
<string name="staking_notification_unstake_text">Награда перестанет начисляться сразу после завершения стейкинга. Процесс завершения длится %s.</string>
|
||||
<string name="staking_restake">Повторный стейкинг</string>
|
||||
<string name="staking_restake_rewards">Застейкать вознаграждения</string>
|
||||
<string name="staking_revoke">Отозвать</string>
|
||||
<string name="staking_revote">Переголосовать</string>
|
||||
<string name="staking_reward_claiming_auto">Автоматически</string>
|
||||
<string name="staking_reward_claiming_manual">Вручную</string>
|
||||
<string name="staking_reward_schedule_block">Блок</string>
|
||||
<string name="staking_reward_schedule_day">День</string>
|
||||
<string name="staking_reward_schedule_each_day">Каждый день</string>
|
||||
<string name="staking_reward_schedule_epoch">Эпоха</string>
|
||||
<string name="staking_reward_schedule_era">Эра</string>
|
||||
<string name="staking_reward_schedule_hour">Час</string>
|
||||
<string name="staking_reward_schedule_month">Месяц</string>
|
||||
<string name="staking_reward_schedule_week">Неделя</string>
|
||||
<string name="staking_rewards">Вознаграждения</string>
|
||||
<string name="staking_stake_more">Застейкать еще</string>
|
||||
<string name="staking_title_stake">Застейкать %s</string>
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@
|
|||
<string name="common_passphrase">Парольна фраза</string>
|
||||
<string name="common_paste">Вставити</string>
|
||||
<string name="common_range">%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_receive">Отримати</string>
|
||||
<string name="common_reject">Відхилити</string>
|
||||
|
|
@ -282,7 +283,7 @@
|
|||
<string name="feedback_subject_support">Звернення в підтримку</string>
|
||||
<string name="feedback_subject_support_tangem">Звернення в підтримку Tangem</string>
|
||||
<string name="feedback_subject_tx_failed">Не вдається відправити транзакцію</string>
|
||||
<string name="give_permission_current_transaction">Поточна транзакція</string>
|
||||
<string name="give_permission_current_transaction">Транзакція</string>
|
||||
<string name="give_permission_fee_footer">Мережа стягує комісію за схвалення токену за підтвердження, що саме ви дозволяєте використовувати ваш токен для обміну.</string>
|
||||
<string name="give_permission_policy_type_footer">Вкажіть ліміт доступу для обраного токена</string>
|
||||
<string name="give_permission_rows_amount">Кількість %s</string>
|
||||
|
|
@ -347,7 +348,7 @@
|
|||
<string name="markets_available_networks">Доступні мережі</string>
|
||||
<string name="markets_common_my_portfolio">Моє портфоліо</string>
|
||||
<string name="markets_common_title">Маркет</string>
|
||||
<string name="markets_generate_addresses_notification">Щоб згенерувати адреси для обраних мереж, потрібно відсканувати свою картку Tangem.</string>
|
||||
<string name="markets_generate_addresses_notification">Щоб згенерувати адреси для обраних мереж, потрібно відсканувати свою картку Tangem</string>
|
||||
<string name="markets_loading_error_title">Не вдалося завантажити дані...</string>
|
||||
<string name="markets_quick_actions">Швидкі дії</string>
|
||||
<string name="markets_search_result_title">Результат</string>
|
||||
|
|
@ -682,7 +683,10 @@
|
|||
<string name="staking_details_warmup_period_info">Відведений час для активації участі в стейкінгу.</string>
|
||||
<string name="staking_migrate">Перемістити</string>
|
||||
<string name="staking_native">Нативний стейкінг</string>
|
||||
<string name="staking_notification_earn_rewards_text">Стейкінг дозволяє заробляти %1$s. Ваші винагороди за стейкінг надходять кожні ~%2$s днів.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_day">Стейкінг дозволяє заробляти %1$s. Ваші винагороди за стейкінг зараховуються щодня.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_hour">Стейкінг дозволяє заробляти %1$s. Ваші винагороди за стейкінг зараховуються кожну годину.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_month">Стейкінг дозволяє заробляти %1$s. Ваші винагороди за стейкінг зараховуються щомісяця.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_week">Стейкінг дозволяє заробляти %1$s. Ваші винагороди за стейкінг зараховуються щотиждня.</string>
|
||||
<string name="staking_notification_earn_rewards_title">Отримуйте винагороду за стейкінг</string>
|
||||
<string name="staking_notification_unstake_text">Винагороди припиняють нараховуватися одразу після того, як ви знімаєте ставку. Процес зняття займає %s.</string>
|
||||
<string name="staking_rebond">Зʼєднати ще раз</string>
|
||||
|
|
|
|||
|
|
@ -9,9 +9,6 @@
|
|||
<string name="alert_failed_to_send_transaction_message">原因:%s</string>
|
||||
<string name="alert_failed_to_send_transaction_title">無法發送交易</string>
|
||||
<string name="alert_manage_tokens_unsupported_message">此卡不支持%1$s網路上的代幣因為韌體限制</string>
|
||||
<string name="alert_negative_app_rate_sent_message">感謝您的反饋。我們會盡快回复</string>
|
||||
<string name="alert_negative_app_rate_sent_title">你的建議已送出</string>
|
||||
<string name="alert_troubleshooting_scan_card_message">請嘗試完全按照動畫中顯示的方式點擊卡片或請求支持</string>
|
||||
<string name="alert_troubleshooting_scan_card_title">有困難在掃描卡上嗎?</string>
|
||||
<string name="alert_unsupported_card">此卡不適用於此app</string>
|
||||
<string name="app_settings_enable_biometrics_description">轉到設置以在 Tangem App 中啟用生物識別身份驗證</string>
|
||||
|
|
@ -56,7 +53,6 @@
|
|||
<string name="common_create">創造</string>
|
||||
<string name="common_delete">刪除</string>
|
||||
<string name="common_disabled">禁用</string>
|
||||
<string name="common_disconnect">斷開連接</string>
|
||||
<string name="common_done">完成</string>
|
||||
<string name="common_enable">允許</string>
|
||||
<string name="common_enabled">啟用</string>
|
||||
|
|
@ -69,7 +65,6 @@
|
|||
<string name="common_origin_card">主卡片</string>
|
||||
<string name="common_reject">拒絕</string>
|
||||
<string name="common_rename">重新命名</string>
|
||||
<string name="common_retry">重試</string>
|
||||
<string name="common_save_changes">保存設置</string>
|
||||
<string name="common_search">搜索</string>
|
||||
<string name="common_search_tokens">搜尋代幣</string>
|
||||
|
|
@ -88,7 +83,6 @@
|
|||
<string name="common_transactions">交易</string>
|
||||
<string name="common_understand">我了解</string>
|
||||
<string name="common_unreachable">無法觸達</string>
|
||||
<string name="common_warning">警告</string>
|
||||
<string name="common_yes">是</string>
|
||||
<string name="contract_address_copied_message">已複製代幣地址</string>
|
||||
<string name="currency_subtitle_expanded">支持的網路</string>
|
||||
|
|
@ -121,7 +115,6 @@
|
|||
<string name="details_row_title_currency">App Currency</string>
|
||||
<string name="details_row_title_issuer">發行人</string>
|
||||
<string name="details_row_title_signed_hashes">簽署</string>
|
||||
<string name="details_security_management_warning">如果您忘記密碼,您將無法使用您的資金。無法恢復代碼</string>
|
||||
<string name="details_title">更多</string>
|
||||
<string name="disclaimer_error_loading">檢查您的網路連接或切換到其他網絡</string>
|
||||
<string name="disclaimer_title">服務條款</string>
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@
|
|||
<string name="common_passphrase">Passphrase</string>
|
||||
<string name="common_paste">Paste</string>
|
||||
<string name="common_range">%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_receive">Receive</string>
|
||||
<string name="common_reject">Reject</string>
|
||||
|
|
@ -667,7 +668,10 @@
|
|||
<string name="staking_details_warmup_period_info">The allocated time for activating participation in staking.</string>
|
||||
<string name="staking_migrate">Migrate</string>
|
||||
<string name="staking_native">Native staking</string>
|
||||
<string name="staking_notification_earn_rewards_text">Staking allow you to earn %1$s. Your staking rewards arrive every ~%2$s days.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_day">Staking allow you to earn %1$s. Your staking rewards arrive every day.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_hour">Staking allow you to earn %1$s. Your staking rewards arrive every hour.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_month">Staking allow you to earn %1$s. Your staking rewards arrive every month.</string>
|
||||
<string name="staking_notification_earn_rewards_text_period_week">Staking allow you to earn %1$s. Your staking rewards arrive every week.</string>
|
||||
<string name="staking_notification_earn_rewards_title">Earn staking rewards</string>
|
||||
<string name="staking_notification_unstake_text">Rewards stop accruing immediately after you unstake. The unstaking process takes %s.</string>
|
||||
<string name="staking_rebond">Rebond</string>
|
||||
|
|
@ -679,6 +683,7 @@
|
|||
<string name="staking_reward_claiming_manual">Manual</string>
|
||||
<string name="staking_reward_schedule_block">Block</string>
|
||||
<string name="staking_reward_schedule_day">Day</string>
|
||||
<string name="staking_reward_schedule_each_day">Each day</string>
|
||||
<string name="staking_reward_schedule_epoch">Epoch</string>
|
||||
<string name="staking_reward_schedule_era">Era</string>
|
||||
<string name="staking_reward_schedule_hour">Hour</string>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
package com.tangem.core.ui.components.marketprice
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -26,6 +23,11 @@ fun PriceChangeInPercent(
|
|||
modifier: Modifier = Modifier,
|
||||
textStyle: TextStyle = TangemTheme.typography.body2,
|
||||
) {
|
||||
if (valueInPercent.isBlank()) {
|
||||
Box(modifier)
|
||||
return
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,13 @@ object BigDecimalFormatter {
|
|||
private const val FIAT_MARKET_DEFAULT_DIGITS = 2
|
||||
private const val FIAT_MARKET_EXTENDED_DIGITS = 6
|
||||
|
||||
private val bigDecimal01 = BigDecimal("0.1")
|
||||
private val bigDecimal001 = BigDecimal("0.01")
|
||||
private val bigDecimal0001 = BigDecimal("0.001")
|
||||
private val bigDecimal00001 = BigDecimal("0.0001")
|
||||
private val bigDecimal000001 = BigDecimal("0.00001")
|
||||
private val bigDecimal0000001 = BigDecimal("0.000001")
|
||||
|
||||
fun formatCryptoAmount(
|
||||
cryptoAmount: BigDecimal?,
|
||||
cryptoCurrency: String,
|
||||
|
|
@ -169,6 +176,28 @@ object BigDecimalFormatter {
|
|||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol)
|
||||
}
|
||||
|
||||
fun formatFiatPriceUncapped(
|
||||
fiatAmount: BigDecimal?,
|
||||
fiatCurrencyCode: String,
|
||||
fiatCurrencySymbol: String,
|
||||
locale: Locale = Locale.getDefault(),
|
||||
): String {
|
||||
if (fiatAmount == null) return EMPTY_BALANCE_SIGN
|
||||
val formatterCurrency = getCurrency(fiatCurrencyCode)
|
||||
|
||||
val decimals = getProperFiatPriceDecimals(fiatAmount)
|
||||
|
||||
val formatter = NumberFormat.getCurrencyInstance(locale).apply {
|
||||
currency = formatterCurrency
|
||||
maximumFractionDigits = decimals
|
||||
minimumFractionDigits = FIAT_MARKET_DEFAULT_DIGITS
|
||||
roundingMode = RoundingMode.HALF_UP
|
||||
}
|
||||
|
||||
return formatter.format(fiatAmount)
|
||||
.replace(formatterCurrency.getSymbol(locale), fiatCurrencySymbol)
|
||||
}
|
||||
|
||||
fun formatFiatEditableAmount(
|
||||
fiatAmount: String?,
|
||||
fiatCurrencyCode: String,
|
||||
|
|
@ -330,4 +359,18 @@ object BigDecimalFormatter {
|
|||
}
|
||||
|
||||
private fun BigDecimal.isLessThanThreshold() = this > BigDecimal.ZERO && this < FIAT_FORMAT_THRESHOLD
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun getProperFiatPriceDecimals(price: BigDecimal): Int {
|
||||
return when {
|
||||
price >= BigDecimal.ONE -> 2
|
||||
price >= bigDecimal01 -> 3
|
||||
price >= bigDecimal001 -> 4
|
||||
price >= bigDecimal0001 -> 6
|
||||
price >= bigDecimal00001 -> 8
|
||||
price >= bigDecimal000001 -> 10
|
||||
price >= bigDecimal0000001 -> 12
|
||||
else -> price.stripTrailingZeros().scale()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,8 +10,6 @@ import java.util.Locale
|
|||
@Suppress("MagicNumber")
|
||||
object DateTimeFormatters {
|
||||
|
||||
private const val DDMMYYYY = "dd.MM.yyyy"
|
||||
|
||||
/**
|
||||
* Two SS means, SHORT style for date and time.
|
||||
* If pattern contains "a", it means time is in 12 hour format.
|
||||
|
|
@ -51,7 +49,7 @@ object DateTimeFormatters {
|
|||
|
||||
val dateDDMMYYYY: DateTimeFormatter by lazy {
|
||||
DateTimeFormatterBuilder()
|
||||
.appendPattern(DDMMYYYY)
|
||||
.appendPattern("dd.MM.yyyy")
|
||||
.toFormatter()
|
||||
.withLocale(Locale.getDefault())
|
||||
}
|
||||
|
|
@ -59,18 +57,12 @@ object DateTimeFormatters {
|
|||
/**
|
||||
* In API version < 24, there may be some problems with getting the best date and time format pattern.
|
||||
*/
|
||||
val dateMMMMd: DateTimeFormatter by lazy {
|
||||
DateTimeFormatterBuilder()
|
||||
.appendPattern(DateFormat.getBestDateTimePattern(Locale.getDefault(), "dd MMM"))
|
||||
.toFormatter()
|
||||
.withLocale(Locale.getDefault())
|
||||
val dateMMMdd: DateTimeFormatter by lazy {
|
||||
getBestFormatterBySkeleton("dd MMM")
|
||||
}
|
||||
|
||||
val dateYYYY: DateTimeFormatter by lazy {
|
||||
DateTimeFormatterBuilder()
|
||||
.appendPattern(DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyy"))
|
||||
.toFormatter()
|
||||
.withLocale(Locale.getDefault())
|
||||
getBestFormatterBySkeleton("yyyy")
|
||||
}
|
||||
|
||||
val dateTimeFormatter: DateTimeFormatter by lazy {
|
||||
|
|
@ -80,4 +72,11 @@ object DateTimeFormatters {
|
|||
fun formatDate(date: DateTime, formatter: DateTimeFormatter = dateFormatter): String {
|
||||
return formatter.print(date)
|
||||
}
|
||||
|
||||
fun getBestFormatterBySkeleton(skeleton: String): DateTimeFormatter {
|
||||
return DateTimeFormatterBuilder()
|
||||
.appendPattern(DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton))
|
||||
.toFormatter()
|
||||
.withLocale(Locale.getDefault())
|
||||
}
|
||||
}
|
||||
|
|
@ -29,5 +29,12 @@ fun Long.toDateFormatWithTodayYesterday(formatter: DateTimeFormatter = DateTimeF
|
|||
* Returns formatted time according to [formatter].
|
||||
*/
|
||||
fun Long.toTimeFormat(formatter: DateTimeFormatter = DateTimeFormatters.timeFormatter): String {
|
||||
return formatAsDateTime(formatter)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns formatted date-time according to [formatter].
|
||||
*/
|
||||
fun Long.formatAsDateTime(formatter: DateTimeFormatter): String {
|
||||
return DateTimeFormatters.formatDate(date = DateTime(this, DateTimeZone.getDefault()), formatter = formatter)
|
||||
}
|
||||
|
|
@ -19,9 +19,6 @@ internal class DefaultMarketsTokenRepository(
|
|||
private val dispatcherProvider: CoroutineDispatcherProvider,
|
||||
) : MarketsTokenRepository {
|
||||
|
||||
private val tokenListConverter = TokenMarketListConverter()
|
||||
private val tokenChartConverter = TokenChartConverter()
|
||||
|
||||
private fun createTokenMarketsFetcher(firstBatchSize: Int, nextBatchSize: Int) = LimitOffsetBatchFetcher(
|
||||
prefetchDistance = firstBatchSize,
|
||||
batchSize = nextBatchSize,
|
||||
|
|
@ -65,7 +62,7 @@ internal class DefaultMarketsTokenRepository(
|
|||
val last = res.tokens.size < request.limit
|
||||
|
||||
return BatchFetchResult.Success(
|
||||
data = tokenListConverter.convert(res),
|
||||
data = TokenMarketListConverter.convert(res),
|
||||
last = last,
|
||||
empty = res.tokens.isEmpty(),
|
||||
)
|
||||
|
|
@ -105,7 +102,7 @@ internal class DefaultMarketsTokenRepository(
|
|||
interval = interval.toRequestParam(),
|
||||
)
|
||||
|
||||
return tokenChartConverter.convert(interval, response.getOrThrow())
|
||||
return TokenChartConverter.convert(interval, response.getOrThrow())
|
||||
}
|
||||
|
||||
override suspend fun getTokenInfo(
|
||||
|
|
@ -119,6 +116,17 @@ internal class DefaultMarketsTokenRepository(
|
|||
language = languageCode,
|
||||
)
|
||||
|
||||
return TokenMarketInfoConverter().convert(response.getOrThrow())
|
||||
return TokenMarketInfoConverter.convert(response.getOrThrow())
|
||||
}
|
||||
|
||||
override suspend fun getTokenQuotes(fiatCurrencyCode: String, tokenId: String): TokenQuotes {
|
||||
// TODO change method when backend is ready
|
||||
val response = marketsApi.getCoinMarketData(
|
||||
currency = fiatCurrencyCode,
|
||||
coinId = tokenId,
|
||||
language = "en",
|
||||
)
|
||||
|
||||
return TokenMarketInfoConverter.convert(response.getOrThrow()).quotes
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
package com.tangem.data.markets
|
||||
|
||||
import com.tangem.data.markets.converters.TokenChartConverter
|
||||
import com.tangem.data.markets.converters.TokenMarketChartsConverter
|
||||
import com.tangem.data.markets.converters.TokenQuotesConverter
|
||||
import com.tangem.data.markets.converters.TokenQuotesShortConverter
|
||||
import com.tangem.data.markets.converters.toRequestParam
|
||||
import com.tangem.data.markets.utils.retryOnError
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
|
|
@ -21,9 +20,6 @@ internal class MarketsBatchUpdateFetcher(
|
|||
private val tangemTechApi: TangemTechApi,
|
||||
) : BatchUpdateFetcher<Int, List<TokenMarket>, TokenMarketUpdateRequest> {
|
||||
|
||||
private val tokenListChartsConverter = TokenMarketChartsConverter(TokenChartConverter())
|
||||
private val tokenQuotesConverter = TokenQuotesConverter()
|
||||
|
||||
override suspend fun BatchUpdateFetcher.UpdateContext<Int, List<TokenMarket>>.fetchUpdateAsync(
|
||||
toUpdate: List<Batch<Int, List<TokenMarket>>>,
|
||||
updateRequest: TokenMarketUpdateRequest,
|
||||
|
|
@ -75,7 +71,7 @@ internal class MarketsBatchUpdateFetcher(
|
|||
val res = toUpdate.map { batch ->
|
||||
batch.copy(
|
||||
data = batch.data.map {
|
||||
it.copy(tokenQuotes = tokenQuotesConverter.convert(it.id, quotesRes))
|
||||
it.copy(tokenQuotesShort = TokenQuotesShortConverter.convert(it.id, quotesRes))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -97,7 +93,7 @@ internal class MarketsBatchUpdateFetcher(
|
|||
key = batchToUpdate.key,
|
||||
data = batchToUpdate.data.map {
|
||||
it.copy(
|
||||
tokenCharts = tokenListChartsConverter.convert(
|
||||
tokenCharts = TokenMarketChartsConverter.convert(
|
||||
chartsToCopy = it.tokenCharts,
|
||||
tokenId = it.id,
|
||||
interval = updateRequest.interval,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import com.tangem.datasource.api.markets.models.response.TokenMarketChartRespons
|
|||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import com.tangem.domain.markets.TokenChart
|
||||
|
||||
class TokenChartConverter {
|
||||
internal object TokenChartConverter {
|
||||
|
||||
fun convert(interval: PriceChangeInterval, value: TokenMarketChartResponse): TokenChart {
|
||||
return TokenChart(
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ package com.tangem.data.markets.converters
|
|||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import com.tangem.domain.markets.TokenMarketListConfig
|
||||
|
||||
fun TokenMarketListConfig.Interval.toRequestParam(): String = when (this) {
|
||||
internal fun TokenMarketListConfig.Interval.toRequestParam(): String = when (this) {
|
||||
TokenMarketListConfig.Interval.H24 -> "24h"
|
||||
TokenMarketListConfig.Interval.WEEK -> "1w"
|
||||
TokenMarketListConfig.Interval.MONTH -> "30d"
|
||||
}
|
||||
|
||||
fun TokenMarketListConfig.Order.toRequestParam(): String = when (this) {
|
||||
internal fun TokenMarketListConfig.Order.toRequestParam(): String = when (this) {
|
||||
TokenMarketListConfig.Order.ByRating -> "rating"
|
||||
TokenMarketListConfig.Order.Trending -> "trending"
|
||||
TokenMarketListConfig.Order.Buyers -> "buyers"
|
||||
|
|
@ -17,7 +17,7 @@ fun TokenMarketListConfig.Order.toRequestParam(): String = when (this) {
|
|||
TokenMarketListConfig.Order.TopLosers -> "losers"
|
||||
}
|
||||
|
||||
fun PriceChangeInterval.toRequestParam(): String = when (this) {
|
||||
internal fun PriceChangeInterval.toRequestParam(): String = when (this) {
|
||||
PriceChangeInterval.H24 -> "24h"
|
||||
PriceChangeInterval.WEEK -> "1w"
|
||||
PriceChangeInterval.MONTH -> "1m"
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@ import com.tangem.domain.markets.PriceChangeInterval
|
|||
import com.tangem.domain.markets.TokenMarket
|
||||
import com.tangem.domain.markets.TokenMarketListConfig
|
||||
|
||||
class TokenMarketChartsConverter(
|
||||
private val tokenChartConverter: TokenChartConverter,
|
||||
) {
|
||||
internal object TokenMarketChartsConverter {
|
||||
|
||||
fun convert(
|
||||
chartsToCopy: TokenMarket.Charts,
|
||||
|
|
@ -20,13 +18,13 @@ class TokenMarketChartsConverter(
|
|||
}
|
||||
return when (interval) {
|
||||
TokenMarketListConfig.Interval.H24 -> chartsToCopy.copy(
|
||||
h24 = tokenChartConverter.convert(interval.toPriceChangeInterval(), prices),
|
||||
h24 = TokenChartConverter.convert(interval.toPriceChangeInterval(), prices),
|
||||
)
|
||||
TokenMarketListConfig.Interval.WEEK -> chartsToCopy.copy(
|
||||
week = tokenChartConverter.convert(interval.toPriceChangeInterval(), prices),
|
||||
week = TokenChartConverter.convert(interval.toPriceChangeInterval(), prices),
|
||||
)
|
||||
TokenMarketListConfig.Interval.MONTH -> chartsToCopy.copy(
|
||||
month = tokenChartConverter.convert(interval.toPriceChangeInterval(), prices),
|
||||
month = TokenChartConverter.convert(interval.toPriceChangeInterval(), prices),
|
||||
)
|
||||
else -> error("unsupported interval=$interval. This shouldn't have happened.")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ package com.tangem.data.markets.converters
|
|||
|
||||
import com.tangem.datasource.api.markets.models.response.TokenMarketInfoResponse
|
||||
import com.tangem.domain.markets.TokenMarketInfo
|
||||
import com.tangem.domain.markets.TokenQuotes
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class TokenMarketInfoConverter : Converter<TokenMarketInfoResponse, TokenMarketInfo> {
|
||||
internal object TokenMarketInfoConverter : Converter<TokenMarketInfoResponse, TokenMarketInfo> {
|
||||
|
||||
override fun convert(value: TokenMarketInfoResponse): TokenMarketInfo {
|
||||
return with(value) {
|
||||
|
|
@ -12,8 +13,7 @@ internal class TokenMarketInfoConverter : Converter<TokenMarketInfoResponse, Tok
|
|||
id = id,
|
||||
name = name,
|
||||
symbol = symbol,
|
||||
currentPrice = currentPrice,
|
||||
priceChangePercentage = priceChangePercentage?.convert(),
|
||||
quotes = getQuotes(),
|
||||
networks = networks?.convert(),
|
||||
shortDescription = shortDescription,
|
||||
fullDescription = fullDescription,
|
||||
|
|
@ -25,15 +25,16 @@ internal class TokenMarketInfoConverter : Converter<TokenMarketInfoResponse, Tok
|
|||
}
|
||||
}
|
||||
|
||||
private fun TokenMarketInfoResponse.PriceChangePercentage.convert(): TokenMarketInfo.PriceChangePercentage {
|
||||
return TokenMarketInfo.PriceChangePercentage(
|
||||
day = day,
|
||||
week = week,
|
||||
month = month,
|
||||
threeMonths = threeMonths,
|
||||
sixMonths = sixMonths,
|
||||
year = year,
|
||||
allTime = allTime,
|
||||
private fun TokenMarketInfoResponse.getQuotes(): TokenQuotes {
|
||||
return TokenQuotes(
|
||||
currentPrice = currentPrice,
|
||||
h24ChangePercent = priceChangePercentage?.day?.movePointLeft(2),
|
||||
weekChangePercent = priceChangePercentage?.week?.movePointLeft(2),
|
||||
monthChangePercent = priceChangePercentage?.month?.movePointLeft(2),
|
||||
m3ChangePercent = priceChangePercentage?.threeMonths?.movePointLeft(2),
|
||||
m6ChangePercent = priceChangePercentage?.sixMonths?.movePointLeft(2),
|
||||
yearChangePercent = priceChangePercentage?.year?.movePointLeft(2),
|
||||
allTimeChangePercent = priceChangePercentage?.allTime?.movePointLeft(2),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
package com.tangem.data.markets.converters
|
||||
|
||||
import com.tangem.datasource.api.markets.models.response.TokenMarketListResponse
|
||||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import com.tangem.domain.markets.TokenMarket
|
||||
import com.tangem.domain.markets.TokenQuotes
|
||||
import com.tangem.domain.markets.TokenQuotesShort
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
class TokenMarketListConverter : Converter<TokenMarketListResponse, List<TokenMarket>> {
|
||||
internal object TokenMarketListConverter : Converter<TokenMarketListResponse, List<TokenMarket>> {
|
||||
|
||||
override fun convert(value: TokenMarketListResponse): List<TokenMarket> {
|
||||
val imageHost = value.imageHost ?: run {
|
||||
|
|
@ -25,13 +24,11 @@ class TokenMarketListConverter : Converter<TokenMarketListResponse, List<TokenMa
|
|||
marketRating = token.marketRating,
|
||||
marketCap = token.marketCap,
|
||||
imageHost = imageHost,
|
||||
tokenQuotes = TokenQuotes(
|
||||
tokenQuotesShort = TokenQuotesShort(
|
||||
currentPrice = token.currentPrice,
|
||||
priceChanges = mapOf(
|
||||
PriceChangeInterval.H24 to token.priceChangePercentage.h24.movePointLeft(2),
|
||||
PriceChangeInterval.WEEK to token.priceChangePercentage.week1.movePointLeft(2),
|
||||
PriceChangeInterval.MONTH to token.priceChangePercentage.day30.movePointLeft(2),
|
||||
),
|
||||
h24ChangePercent = token.priceChangePercentage.h24.movePointLeft(2),
|
||||
weekChangePercent = token.priceChangePercentage.week1.movePointLeft(2),
|
||||
monthChangePercent = token.priceChangePercentage.day30.movePointLeft(2),
|
||||
),
|
||||
tokenCharts = TokenMarket.Charts(null, null, null),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,25 +1,22 @@
|
|||
package com.tangem.data.markets.converters
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.models.QuotesResponse
|
||||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import com.tangem.domain.markets.TokenQuotes
|
||||
import com.tangem.domain.markets.TokenQuotesShort
|
||||
import java.math.BigDecimal
|
||||
|
||||
class TokenQuotesConverter {
|
||||
internal object TokenQuotesShortConverter {
|
||||
|
||||
fun convert(tokenId: String, value: QuotesResponse): TokenQuotes {
|
||||
fun convert(tokenId: String, value: QuotesResponse): TokenQuotesShort {
|
||||
val quote = requireNotNull(value.quotes[tokenId]) {
|
||||
"$tokenId is not found in the response. This shouldn't have happened."
|
||||
}
|
||||
return TokenQuotes(
|
||||
return TokenQuotesShort(
|
||||
currentPrice = requireNotNull(quote.price) {
|
||||
"Price is not found in the QuotesResponse. This shouldn't have happened."
|
||||
},
|
||||
priceChanges = mapOf(
|
||||
PriceChangeInterval.H24 to (quote.priceChange24h ?: BigDecimal.ZERO).movePointLeft(2),
|
||||
PriceChangeInterval.WEEK to (quote.priceChange1w ?: BigDecimal.ZERO).movePointLeft(2),
|
||||
PriceChangeInterval.MONTH to (quote.priceChange30d ?: BigDecimal.ZERO).movePointLeft(2),
|
||||
),
|
||||
h24ChangePercent = (quote.priceChange24h ?: BigDecimal.ZERO).movePointLeft(2),
|
||||
weekChangePercent = (quote.priceChange1w ?: BigDecimal.ZERO).movePointLeft(2),
|
||||
monthChangePercent = (quote.priceChange30d ?: BigDecimal.ZERO).movePointLeft(2),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ data class TokenMarket(
|
|||
val symbol: String,
|
||||
val marketRating: Int?,
|
||||
val marketCap: BigDecimal?,
|
||||
val tokenQuotes: TokenQuotes,
|
||||
val tokenQuotesShort: TokenQuotesShort,
|
||||
val tokenCharts: Charts,
|
||||
private val imageHost: String,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ data class TokenMarketInfo(
|
|||
val id: String,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val currentPrice: BigDecimal,
|
||||
val priceChangePercentage: PriceChangePercentage?,
|
||||
val quotes: TokenQuotes,
|
||||
val networks: List<Network>?,
|
||||
val shortDescription: String?,
|
||||
val fullDescription: String?,
|
||||
|
|
@ -16,16 +15,6 @@ data class TokenMarketInfo(
|
|||
val links: Links?,
|
||||
val pricePerformance: PricePerformance?,
|
||||
) {
|
||||
data class PriceChangePercentage(
|
||||
val day: BigDecimal?,
|
||||
val week: BigDecimal?,
|
||||
val month: BigDecimal?,
|
||||
val threeMonths: BigDecimal?,
|
||||
val sixMonths: BigDecimal?,
|
||||
val year: BigDecimal?,
|
||||
val allTime: BigDecimal?,
|
||||
)
|
||||
|
||||
data class Network(
|
||||
val networkId: String,
|
||||
val exchangeable: Boolean,
|
||||
|
|
|
|||
|
|
@ -4,15 +4,11 @@ import java.math.BigDecimal
|
|||
|
||||
data class TokenQuotes(
|
||||
val currentPrice: BigDecimal,
|
||||
private val priceChanges: Map<PriceChangeInterval, BigDecimal>,
|
||||
) {
|
||||
init {
|
||||
require(priceChanges.containsKey(PriceChangeInterval.H24))
|
||||
require(priceChanges.containsKey(PriceChangeInterval.WEEK))
|
||||
require(priceChanges.containsKey(PriceChangeInterval.MONTH))
|
||||
}
|
||||
|
||||
fun h24Percent() = priceChanges[PriceChangeInterval.H24]!!
|
||||
fun weekPercent() = priceChanges[PriceChangeInterval.WEEK]!!
|
||||
fun monthPercent() = priceChanges[PriceChangeInterval.MONTH]!!
|
||||
}
|
||||
val h24ChangePercent: BigDecimal?,
|
||||
val weekChangePercent: BigDecimal?,
|
||||
val monthChangePercent: BigDecimal?,
|
||||
val m3ChangePercent: BigDecimal?,
|
||||
val m6ChangePercent: BigDecimal?,
|
||||
val yearChangePercent: BigDecimal?,
|
||||
val allTimeChangePercent: BigDecimal?,
|
||||
)
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.domain.markets
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class TokenQuotesShort(
|
||||
val currentPrice: BigDecimal,
|
||||
val h24ChangePercent: BigDecimal,
|
||||
val weekChangePercent: BigDecimal,
|
||||
val monthChangePercent: BigDecimal,
|
||||
)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.domain.markets
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.repositories.MarketsTokenRepository
|
||||
|
||||
class GetTokenQuotesUseCase(
|
||||
private val marketsTokenRepository: MarketsTokenRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(appCurrency: AppCurrency, tokenId: String): Either<Unit, TokenQuotes> {
|
||||
return Either.catch {
|
||||
marketsTokenRepository.getTokenQuotes(
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
tokenId = tokenId,
|
||||
)
|
||||
}.mapLeft {}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,4 +13,6 @@ interface MarketsTokenRepository {
|
|||
suspend fun getChart(fiatCurrencyCode: String, interval: PriceChangeInterval, tokenId: String): TokenChart
|
||||
|
||||
suspend fun getTokenInfo(fiatCurrencyCode: String, tokenId: String, languageCode: String): TokenMarketInfo
|
||||
|
||||
suspend fun getTokenQuotes(fiatCurrencyCode: String, tokenId: String): TokenQuotes
|
||||
}
|
||||
|
|
@ -30,10 +30,10 @@ fun TokenMarket.toSerializable(): TokenMarketSerializable {
|
|||
symbol = symbol,
|
||||
marketCap = marketCap,
|
||||
tokenQuotes = TokenMarketSerializable.Quotes(
|
||||
currentPrice = tokenQuotes.currentPrice,
|
||||
h24Percent = tokenQuotes.h24Percent(),
|
||||
weekPercent = tokenQuotes.weekPercent(),
|
||||
monthPercent = tokenQuotes.monthPercent(),
|
||||
currentPrice = tokenQuotesShort.currentPrice,
|
||||
h24Percent = tokenQuotesShort.h24ChangePercent,
|
||||
weekPercent = tokenQuotesShort.weekChangePercent,
|
||||
monthPercent = tokenQuotesShort.monthChangePercent,
|
||||
),
|
||||
imageUrl = imageUrlLarge,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
package com.tangem.features.markets.details.impl
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.lifecycle.compose.LifecycleStartEffect
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
|
|
@ -32,11 +30,23 @@ internal class DefaultMarketsTokenDetailsComponent @AssistedInject constructor(
|
|||
onHeaderSizeChange: (Dp) -> Unit,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
LifecycleStartEffect(Unit) {
|
||||
model.isVisibleOnScreen.value = true
|
||||
onStopOrDispose {
|
||||
model.isVisibleOnScreen.value = false
|
||||
}
|
||||
}
|
||||
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
val bsState by bottomSheetState
|
||||
|
||||
LaunchedEffect(bsState) {
|
||||
model.containerBottomSheetState.value = bsState
|
||||
}
|
||||
|
||||
MarketsTokenDetailsContent(
|
||||
state = state,
|
||||
onBackClick = { onBack() },
|
||||
onBackClick = onBack,
|
||||
onHeaderSizeChange = onHeaderSizeChange,
|
||||
modifier = modifier,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,19 +9,22 @@ import com.tangem.core.navigation.url.UrlOpener
|
|||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.event.triggeredEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.core.ui.utils.DateTimeFormatters
|
||||
import com.tangem.core.ui.utils.toTimeFormat
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.GetTokenMarketInfoUseCase
|
||||
import com.tangem.domain.markets.GetTokenPriceChartUseCase
|
||||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import com.tangem.domain.markets.*
|
||||
import com.tangem.features.markets.component.BottomSheetState
|
||||
import com.tangem.features.markets.details.api.MarketsTokenDetailsComponent
|
||||
import com.tangem.features.markets.details.impl.model.converters.DescriptionConverter
|
||||
import com.tangem.features.markets.details.impl.model.converters.TokenMarketInfoConverter
|
||||
import com.tangem.features.markets.details.impl.model.formatter.*
|
||||
import com.tangem.features.markets.details.impl.model.formatter.formatAsPrice
|
||||
import com.tangem.features.markets.details.impl.model.formatter.getChangePercentBetween
|
||||
import com.tangem.features.markets.details.impl.model.formatter.getPercentByInterval
|
||||
import com.tangem.features.markets.details.impl.ui.state.InfoBottomSheetContent
|
||||
import com.tangem.features.markets.details.impl.ui.state.MarketsTokenDetailsUM
|
||||
import com.tangem.features.markets.impl.R
|
||||
|
|
@ -30,13 +33,15 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
|||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import org.joda.time.DateTime
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LargeClass")
|
||||
@Suppress("LargeClass", "LongParameterList")
|
||||
@Stable
|
||||
internal class MarketsTokenDetailsModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
|
|
@ -44,10 +49,12 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getTokenPriceChartUseCase: GetTokenPriceChartUseCase,
|
||||
private val getTokenMarketInfoUseCase: GetTokenMarketInfoUseCase,
|
||||
private val getTokenQuotesUseCase: GetTokenQuotesUseCase,
|
||||
private val urlOpener: UrlOpener,
|
||||
) : Model() {
|
||||
|
||||
val params = paramsContainer.require<MarketsTokenDetailsComponent.Params>()
|
||||
private var quotesJob = JobHolder()
|
||||
private val params = paramsContainer.require<MarketsTokenDetailsComponent.Params>()
|
||||
|
||||
private val currentAppCurrency = getSelectedAppCurrencyUseCase()
|
||||
.map { maybeAppCurrency ->
|
||||
|
|
@ -77,13 +84,13 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
chartData = MarketChartData.NoData.Loading
|
||||
|
||||
updateLook {
|
||||
val percentChangeType = params.token.tokenQuotes.h24Percent.percentChangeType()
|
||||
|
||||
it.copy(
|
||||
type = getChartTypeByPercent(params.token.tokenQuotes.h24Percent),
|
||||
xAxisFormatter = { value ->
|
||||
value.toLong().toTimeFormat(DateTimeFormatters.timeFormatter)
|
||||
},
|
||||
type = percentChangeType.toChartType(),
|
||||
xAxisFormatter = MarketsDateTimeFormatters.getChartXFormatterByInterval(PriceChangeInterval.H24),
|
||||
yAxisFormatter = { value ->
|
||||
BigDecimalFormatter.formatFiatAmountUncapped(
|
||||
BigDecimalFormatter.formatFiatPriceUncapped(
|
||||
fiatAmount = value,
|
||||
fiatCurrencyCode = currentAppCurrency.value.code,
|
||||
fiatCurrencySymbol = "",
|
||||
|
|
@ -93,10 +100,28 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private val currentQuotes = MutableStateFlow(
|
||||
TokenQuotes(
|
||||
currentPrice = params.token.tokenQuotes.currentPrice,
|
||||
h24ChangePercent = params.token.tokenQuotes.h24Percent,
|
||||
weekChangePercent = params.token.tokenQuotes.weekPercent,
|
||||
monthChangePercent = params.token.tokenQuotes.monthPercent,
|
||||
m3ChangePercent = null,
|
||||
m6ChangePercent = null,
|
||||
yearChangePercent = null,
|
||||
allTimeChangePercent = null,
|
||||
),
|
||||
)
|
||||
|
||||
private var lastUpdatedTimestamp: Long = DateTime.now().millis
|
||||
|
||||
val containerBottomSheetState = MutableStateFlow(BottomSheetState.COLLAPSED)
|
||||
val isVisibleOnScreen = MutableStateFlow(false)
|
||||
|
||||
val state = MutableStateFlow(
|
||||
MarketsTokenDetailsUM(
|
||||
tokenName = params.token.name,
|
||||
priceText = BigDecimalFormatter.formatFiatAmountUncapped(
|
||||
priceText = BigDecimalFormatter.formatFiatPriceUncapped(
|
||||
fiatAmount = params.token.tokenQuotes.currentPrice,
|
||||
fiatCurrencyCode = currentAppCurrency.value.code,
|
||||
fiatCurrencySymbol = currentAppCurrency.value.symbol,
|
||||
|
|
@ -121,7 +146,9 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
),
|
||||
selectedInterval = PriceChangeInterval.H24,
|
||||
onSelectedIntervalChange = ::onSelectedIntervalChange,
|
||||
markerSet = false,
|
||||
body = MarketsTokenDetailsUM.Body.Loading,
|
||||
triggerPriceChange = consumedEvent(),
|
||||
infoBottomSheet = TangemBottomSheetConfig(
|
||||
isShow = false,
|
||||
onDismissRequest = {},
|
||||
|
|
@ -146,21 +173,22 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun initialLoad() {
|
||||
loadChart(state.value.selectedInterval)
|
||||
loadInfo()
|
||||
loadChart(state.value.selectedInterval)
|
||||
modelScope.loadQuotesWithTimer(QUOTES_UPDATE_INTERVAL_MILLIS)
|
||||
}
|
||||
|
||||
private fun onSelectedIntervalChange(interval: PriceChangeInterval) {
|
||||
if (state.value.selectedInterval == interval) return
|
||||
|
||||
state.update {
|
||||
it.copy(
|
||||
selectedInterval = interval,
|
||||
priceChangeType = PriceChangeType.UP,
|
||||
private fun loadQuotes() {
|
||||
modelScope.launch {
|
||||
val result = getTokenQuotesUseCase(
|
||||
tokenId = params.token.id,
|
||||
appCurrency = currentAppCurrency.value,
|
||||
)
|
||||
}
|
||||
|
||||
loadChart(interval)
|
||||
result.onRight { res ->
|
||||
updateQuotes(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadChart(interval: PriceChangeInterval) {
|
||||
|
|
@ -192,7 +220,7 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
val xAxisFormatter = getFormatterByInterval(state.value.selectedInterval)
|
||||
val xAxisFormatter = MarketsDateTimeFormatters.getChartXFormatterByInterval(state.value.selectedInterval)
|
||||
|
||||
chart.onRight {
|
||||
chartDataProducer.runTransactionSuspend {
|
||||
|
|
@ -204,6 +232,7 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
updateLook {
|
||||
it.copy(
|
||||
xAxisFormatter = xAxisFormatter,
|
||||
type = state.value.priceChangeType.toChartType(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -247,8 +276,15 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
|
||||
tokenMarketInfo.fold(
|
||||
ifRight = { result ->
|
||||
currentQuotes.value = result.quotes
|
||||
val percent = result.quotes.getPercentByInterval(interval = state.value.selectedInterval)
|
||||
state.update {
|
||||
it.copy(
|
||||
priceText = result.quotes.currentPrice.formatAsPrice(currentAppCurrency.value),
|
||||
priceChangePercentText = result.quotes.getFormattedPercentByInterval(
|
||||
interval = it.selectedInterval,
|
||||
),
|
||||
priceChangeType = percent.percentChangeType(),
|
||||
body = MarketsTokenDetailsUM.Body.Content(
|
||||
description = descriptionConverter.convert(result),
|
||||
infoBlocks = infoConverter.convert(result),
|
||||
|
|
@ -275,57 +311,101 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getFormatterByInterval(interval: PriceChangeInterval): (BigDecimal) -> String {
|
||||
return when (interval) {
|
||||
PriceChangeInterval.H24 -> { value: BigDecimal ->
|
||||
value.toLong().toTimeFormat(DateTimeFormatters.timeFormatter)
|
||||
}
|
||||
PriceChangeInterval.WEEK,
|
||||
PriceChangeInterval.MONTH,
|
||||
PriceChangeInterval.MONTH3,
|
||||
PriceChangeInterval.MONTH6,
|
||||
-> { value ->
|
||||
value.toLong().toTimeFormat(DateTimeFormatters.dateMMMMd)
|
||||
}
|
||||
PriceChangeInterval.YEAR -> { value ->
|
||||
value.toLong().toTimeFormat(DateTimeFormatters.dateMMMMd)
|
||||
}
|
||||
PriceChangeInterval.ALL_TIME -> { value ->
|
||||
value.toLong().toTimeFormat(DateTimeFormatters.dateYYYY)
|
||||
}
|
||||
private suspend fun updateQuotes(newQuotes: TokenQuotes) {
|
||||
val triggerPriceChangeType = getFormattedPriceChange(
|
||||
currentPrice = currentQuotes.value.currentPrice,
|
||||
updatedPrice = newQuotes.currentPrice,
|
||||
)
|
||||
val trigger = if (triggerPriceChangeType != PriceChangeType.NEUTRAL) {
|
||||
triggeredEvent(
|
||||
data = triggerPriceChangeType,
|
||||
onConsume = {
|
||||
state.update { it.copy(triggerPriceChange = consumedEvent()) }
|
||||
},
|
||||
)
|
||||
} else {
|
||||
consumedEvent()
|
||||
}
|
||||
|
||||
val percent = newQuotes.getPercentByInterval(interval = state.value.selectedInterval)
|
||||
val priceChangeType = percent.percentChangeType()
|
||||
|
||||
// wait until marker is removed
|
||||
state.first { it.markerSet.not() }
|
||||
|
||||
currentQuotes.value = newQuotes
|
||||
lastUpdatedTimestamp = DateTime.now().millis
|
||||
|
||||
state.update { stateToUpdate ->
|
||||
stateToUpdate.copy(
|
||||
priceText = newQuotes.currentPrice.formatAsPrice(currentAppCurrency.value),
|
||||
priceChangePercentText = newQuotes.getFormattedPercentByInterval(
|
||||
interval = stateToUpdate.selectedInterval,
|
||||
),
|
||||
priceChangeType = priceChangeType,
|
||||
triggerPriceChange = trigger,
|
||||
dateTimeText = getDefaultDateTimeString(stateToUpdate.selectedInterval),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSelectedIntervalChange(interval: PriceChangeInterval) {
|
||||
if (state.value.selectedInterval == interval) return
|
||||
|
||||
val quotes = currentQuotes.value
|
||||
val priceChangePercent = quotes.getFormattedPercentByInterval(interval)
|
||||
|
||||
state.update {
|
||||
it.copy(
|
||||
priceChangePercentText = priceChangePercent,
|
||||
selectedInterval = interval,
|
||||
priceChangeType = quotes.getPercentByInterval(interval)?.percentChangeType()
|
||||
?: PriceChangeType.NEUTRAL,
|
||||
dateTimeText = getDefaultDateTimeString(interval),
|
||||
)
|
||||
}
|
||||
|
||||
loadChart(interval)
|
||||
|
||||
if (priceChangePercent.isEmpty()) {
|
||||
loadQuotes()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun onMarkerPointSelected(time: BigDecimal?, price: BigDecimal?) {
|
||||
val timeText = time?.toLong()?.toTimeFormat(DateTimeFormatters.dateTimeFormatter)?.let {
|
||||
resourceReference(R.string.common_range, wrappedList(it, resourceReference(R.string.common_now)))
|
||||
} ?: resourceReference(R.string.common_today)
|
||||
private fun onMarkerPointSelected(markerTimestamp: BigDecimal?, price: BigDecimal?) {
|
||||
val currentState = state.value
|
||||
|
||||
val percent = price?.subtract(params.token.tokenQuotes.currentPrice)
|
||||
?.divide(params.token.tokenQuotes.currentPrice, 4, RoundingMode.HALF_UP)
|
||||
?.multiply(BigDecimal(-100))
|
||||
?: params.token.tokenQuotes.h24Percent
|
||||
val dateTimeText = markerTimestamp?.let {
|
||||
MarketsDateTimeFormatters.formatDateByIntervalWithMarker(
|
||||
interval = currentState.selectedInterval,
|
||||
markerTimestamp = it,
|
||||
)
|
||||
} ?: getDefaultDateTimeString(currentState.selectedInterval)
|
||||
|
||||
val percentText = BigDecimalFormatter.formatPercent(
|
||||
percent = percent,
|
||||
useAbsoluteValue = true,
|
||||
)
|
||||
val priceText = (price ?: currentQuotes.value.currentPrice).formatAsPrice(currentAppCurrency.value)
|
||||
|
||||
state.update {
|
||||
it.copy(
|
||||
dateTimeText = timeText,
|
||||
priceText = BigDecimalFormatter.formatFiatAmountUncapped(
|
||||
fiatAmount = price ?: params.token.tokenQuotes.currentPrice,
|
||||
fiatCurrencyCode = currentAppCurrency.value.code,
|
||||
fiatCurrencySymbol = currentAppCurrency.value.symbol,
|
||||
),
|
||||
val percent = price?.let {
|
||||
getChangePercentBetween(
|
||||
previousPrice = it,
|
||||
currentPrice = currentQuotes.value.currentPrice,
|
||||
)
|
||||
} ?: currentQuotes.value.getPercentByInterval(currentState.selectedInterval)
|
||||
|
||||
val percentText = percent?.let {
|
||||
BigDecimalFormatter.formatPercent(
|
||||
percent = it,
|
||||
useAbsoluteValue = true,
|
||||
)
|
||||
} ?: ""
|
||||
|
||||
state.update { stateToUpdate ->
|
||||
stateToUpdate.copy(
|
||||
markerSet = markerTimestamp != null,
|
||||
dateTimeText = dateTimeText,
|
||||
priceText = priceText,
|
||||
priceChangePercentText = percentText,
|
||||
priceChangeType = when {
|
||||
percent < BigDecimal.ZERO -> PriceChangeType.DOWN
|
||||
percent > BigDecimal.ZERO -> PriceChangeType.UP
|
||||
else -> PriceChangeType.NEUTRAL
|
||||
},
|
||||
priceChangeType = percent.percentChangeType(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -338,14 +418,6 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getChartTypeByPercent(percent: BigDecimal): MarketChartLook.Type {
|
||||
return if (percent >= BigDecimal.ZERO) {
|
||||
MarketChartLook.Type.Growing
|
||||
} else {
|
||||
MarketChartLook.Type.Falling
|
||||
}
|
||||
}
|
||||
|
||||
private fun showInfoBottomSheet(content: InfoBottomSheetContent) {
|
||||
state.update { stateToUpdate ->
|
||||
stateToUpdate.copy(
|
||||
|
|
@ -379,6 +451,35 @@ internal class MarketsTokenDetailsModel @Inject constructor(
|
|||
currentState.body is MarketsTokenDetailsUM.Body.Nothing
|
||||
) {
|
||||
loadInfo()
|
||||
modelScope.loadQuotesWithTimer(QUOTES_UPDATE_INTERVAL_MILLIS)
|
||||
}
|
||||
}
|
||||
|
||||
private fun CoroutineScope.loadQuotesWithTimer(timeMillis: Long) {
|
||||
launch {
|
||||
while (true) {
|
||||
delay(timeMillis)
|
||||
// Update quotes only when the container bottom sheet is in the expanded state
|
||||
containerBottomSheetState.first { it == BottomSheetState.EXPANDED }
|
||||
// and is visible on the screen
|
||||
isVisibleOnScreen.first { it }
|
||||
|
||||
loadQuotes()
|
||||
}
|
||||
}.saveIn(quotesJob)
|
||||
}
|
||||
|
||||
private fun getDefaultDateTimeString(interval: PriceChangeInterval): TextReference {
|
||||
return MarketsDateTimeFormatters.formatDateByInterval(
|
||||
interval = interval,
|
||||
startTimestamp = MarketsDateTimeFormatters.getStartTimestampByInterval(
|
||||
interval = interval,
|
||||
currentTimestamp = lastUpdatedTimestamp,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val QUOTES_UPDATE_INTERVAL_MILLIS = 60000L
|
||||
}
|
||||
}
|
||||
|
|
@ -42,8 +42,9 @@ internal class PricePerformanceConverter(
|
|||
|
||||
private fun BigDecimal?.convert(): String {
|
||||
val currency = appCurrency()
|
||||
return BigDecimalFormatter.formatCompactFiatAmount(
|
||||
amount = this,
|
||||
|
||||
return BigDecimalFormatter.formatFiatPriceUncapped(
|
||||
fiatAmount = this,
|
||||
fiatCurrencyCode = currency.code,
|
||||
fiatCurrencySymbol = currency.symbol,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,98 @@
|
|||
package com.tangem.features.markets.details.impl.model.formatter
|
||||
|
||||
import com.tangem.common.ui.charts.state.MarketChartLook
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import com.tangem.domain.markets.TokenQuotes
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
|
||||
internal fun BigDecimal.formatAsPrice(currency: AppCurrency): String {
|
||||
return BigDecimalFormatter.formatFiatPriceUncapped(
|
||||
fiatAmount = this,
|
||||
fiatCurrencyCode = currency.code,
|
||||
fiatCurrencySymbol = currency.symbol,
|
||||
)
|
||||
}
|
||||
|
||||
internal fun TokenQuotes.getFormattedPercentByInterval(interval: PriceChangeInterval): String {
|
||||
val percent = when (interval) {
|
||||
PriceChangeInterval.H24 -> h24ChangePercent
|
||||
PriceChangeInterval.WEEK -> weekChangePercent
|
||||
PriceChangeInterval.MONTH -> monthChangePercent
|
||||
PriceChangeInterval.MONTH3 -> m3ChangePercent
|
||||
PriceChangeInterval.MONTH6 -> m6ChangePercent
|
||||
PriceChangeInterval.YEAR -> yearChangePercent
|
||||
PriceChangeInterval.ALL_TIME -> allTimeChangePercent
|
||||
}
|
||||
|
||||
return percent?.let {
|
||||
BigDecimalFormatter.formatPercent(
|
||||
percent = it,
|
||||
useAbsoluteValue = true,
|
||||
)
|
||||
} ?: ""
|
||||
}
|
||||
|
||||
internal fun TokenQuotes.getPercentByInterval(interval: PriceChangeInterval): BigDecimal? {
|
||||
return when (interval) {
|
||||
PriceChangeInterval.H24 -> h24ChangePercent
|
||||
PriceChangeInterval.WEEK -> weekChangePercent
|
||||
PriceChangeInterval.MONTH -> monthChangePercent
|
||||
PriceChangeInterval.MONTH3 -> m3ChangePercent
|
||||
PriceChangeInterval.MONTH6 -> m6ChangePercent
|
||||
PriceChangeInterval.YEAR -> yearChangePercent
|
||||
PriceChangeInterval.ALL_TIME -> allTimeChangePercent
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BigDecimal?.percentChangeType(): PriceChangeType {
|
||||
return when {
|
||||
this == null -> PriceChangeType.NEUTRAL
|
||||
this > BigDecimal.ZERO -> PriceChangeType.UP
|
||||
this < BigDecimal.ZERO -> PriceChangeType.DOWN
|
||||
else -> PriceChangeType.NEUTRAL
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
internal fun getChangePercentBetween(currentPrice: BigDecimal, previousPrice: BigDecimal): BigDecimal {
|
||||
return if (previousPrice == BigDecimal.ZERO) {
|
||||
BigDecimal.ZERO
|
||||
} else {
|
||||
currentPrice.subtract(previousPrice).divide(previousPrice, 4, RoundingMode.HALF_UP)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getFormattedPriceChange(currentPrice: BigDecimal, updatedPrice: BigDecimal): PriceChangeType {
|
||||
val updatedPriceDecimals = BigDecimalFormatter.getProperFiatPriceDecimals(updatedPrice)
|
||||
|
||||
val current = currentPrice.setScale(updatedPriceDecimals, RoundingMode.HALF_UP)
|
||||
val updated = updatedPrice.setScale(updatedPriceDecimals, RoundingMode.HALF_UP)
|
||||
|
||||
return when {
|
||||
updated > current -> PriceChangeType.UP
|
||||
updated < current -> PriceChangeType.DOWN
|
||||
else -> PriceChangeType.NEUTRAL
|
||||
}
|
||||
}
|
||||
|
||||
internal fun PriceChangeType.toChartType(): MarketChartLook.Type {
|
||||
return when (this) {
|
||||
PriceChangeType.UP -> MarketChartLook.Type.Growing
|
||||
PriceChangeType.DOWN -> MarketChartLook.Type.Falling
|
||||
PriceChangeType.NEUTRAL -> MarketChartLook.Type.Growing
|
||||
}
|
||||
}
|
||||
|
||||
internal fun getChartTypeByPercent(percent: BigDecimal?): MarketChartLook.Type {
|
||||
return when {
|
||||
percent == null -> return MarketChartLook.Type.Growing
|
||||
percent == BigDecimal.ZERO -> MarketChartLook.Type.Growing
|
||||
percent > BigDecimal.ZERO -> MarketChartLook.Type.Growing
|
||||
percent < BigDecimal.ZERO -> MarketChartLook.Type.Falling
|
||||
else -> MarketChartLook.Type.Growing
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
package com.tangem.features.markets.details.impl.model.formatter
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.utils.DateTimeFormatters
|
||||
import com.tangem.core.ui.utils.formatAsDateTime
|
||||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import com.tangem.features.markets.impl.R
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.DateTimeZone
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal object MarketsDateTimeFormatters {
|
||||
|
||||
private const val H24_MILLIS = 24L * 60 * 60 * 1000
|
||||
private const val WEEK_MILLIS = 7L * H24_MILLIS
|
||||
|
||||
private val dateTimeMMMFormatter by lazy {
|
||||
DateTimeFormatters.getBestFormatterBySkeleton("dd MMM hh:mm")
|
||||
}
|
||||
|
||||
private val dateFormatter = DateTimeFormatters.dateDDMMYYYY
|
||||
|
||||
internal fun getChartXFormatterByInterval(interval: PriceChangeInterval): (BigDecimal) -> String {
|
||||
return when (interval) {
|
||||
PriceChangeInterval.H24 -> { value: BigDecimal ->
|
||||
value.toLong().formatAsDateTime(DateTimeFormatters.timeFormatter)
|
||||
}
|
||||
PriceChangeInterval.WEEK,
|
||||
PriceChangeInterval.MONTH,
|
||||
PriceChangeInterval.MONTH3,
|
||||
PriceChangeInterval.MONTH6,
|
||||
-> { value ->
|
||||
value.toLong().formatAsDateTime(DateTimeFormatters.dateMMMdd)
|
||||
}
|
||||
PriceChangeInterval.YEAR -> { value ->
|
||||
value.toLong().formatAsDateTime(DateTimeFormatters.dateMMMdd)
|
||||
}
|
||||
PriceChangeInterval.ALL_TIME -> { value ->
|
||||
value.toLong().formatAsDateTime(DateTimeFormatters.dateYYYY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun formatDateByInterval(interval: PriceChangeInterval, startTimestamp: Long): TextReference {
|
||||
return when (interval) {
|
||||
PriceChangeInterval.H24 -> resourceReference(R.string.common_today)
|
||||
PriceChangeInterval.WEEK,
|
||||
PriceChangeInterval.MONTH,
|
||||
PriceChangeInterval.MONTH3,
|
||||
-> {
|
||||
resourceReference(
|
||||
R.string.common_range_with_space,
|
||||
wrappedList(
|
||||
stringReference(
|
||||
startTimestamp.formatAsDateTime(MarketsDateTimeFormatters.dateTimeMMMFormatter),
|
||||
),
|
||||
resourceReference(R.string.common_now),
|
||||
),
|
||||
)
|
||||
}
|
||||
PriceChangeInterval.MONTH6,
|
||||
PriceChangeInterval.YEAR,
|
||||
-> {
|
||||
resourceReference(
|
||||
R.string.common_range_with_space,
|
||||
wrappedList(
|
||||
stringReference(
|
||||
startTimestamp.formatAsDateTime(MarketsDateTimeFormatters.dateFormatter),
|
||||
),
|
||||
resourceReference(R.string.common_now),
|
||||
),
|
||||
)
|
||||
}
|
||||
PriceChangeInterval.ALL_TIME -> resourceReference(R.string.common_all)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun formatDateByIntervalWithMarker(
|
||||
interval: PriceChangeInterval,
|
||||
markerTimestamp: BigDecimal,
|
||||
): TextReference {
|
||||
return when (interval) {
|
||||
PriceChangeInterval.H24,
|
||||
PriceChangeInterval.WEEK,
|
||||
PriceChangeInterval.MONTH,
|
||||
PriceChangeInterval.MONTH3,
|
||||
-> {
|
||||
resourceReference(
|
||||
R.string.common_range_with_space,
|
||||
wrappedList(
|
||||
stringReference(
|
||||
markerTimestamp.toLong().formatAsDateTime(MarketsDateTimeFormatters.dateTimeMMMFormatter),
|
||||
),
|
||||
resourceReference(R.string.common_now),
|
||||
),
|
||||
)
|
||||
}
|
||||
PriceChangeInterval.MONTH6,
|
||||
PriceChangeInterval.YEAR,
|
||||
PriceChangeInterval.ALL_TIME,
|
||||
-> {
|
||||
resourceReference(
|
||||
R.string.common_range_with_space,
|
||||
wrappedList(
|
||||
stringReference(
|
||||
markerTimestamp.toLong().formatAsDateTime(MarketsDateTimeFormatters.dateFormatter),
|
||||
),
|
||||
resourceReference(R.string.common_now),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun getStartTimestampByInterval(interval: PriceChangeInterval, currentTimestamp: Long): Long {
|
||||
return when (interval) {
|
||||
PriceChangeInterval.H24 -> currentTimestamp - H24_MILLIS
|
||||
PriceChangeInterval.WEEK -> currentTimestamp - WEEK_MILLIS
|
||||
PriceChangeInterval.MONTH -> DateTime(currentTimestamp, DateTimeZone.UTC).minusMonths(1).millis
|
||||
PriceChangeInterval.MONTH3 -> DateTime(currentTimestamp, DateTimeZone.UTC).minusMonths(3).millis
|
||||
PriceChangeInterval.MONTH6 -> DateTime(currentTimestamp, DateTimeZone.UTC).minusMonths(6).millis
|
||||
PriceChangeInterval.YEAR -> DateTime(currentTimestamp, DateTimeZone.UTC).minusYears(1).millis
|
||||
PriceChangeInterval.ALL_TIME -> 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,13 @@
|
|||
package com.tangem.features.markets.details.impl.ui
|
||||
|
||||
import androidx.compose.animation.Animatable
|
||||
import androidx.compose.animation.core.snap
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
|
|
@ -23,6 +26,9 @@ import com.tangem.core.ui.components.buttons.segmentedbutton.SegmentedButtons
|
|||
import com.tangem.core.ui.components.currency.icon.CoinIcon
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeInPercent
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.event.EventEffect
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
|
@ -134,11 +140,12 @@ private fun Header(state: MarketsTokenDetailsUM, modifier: Modifier = Modifier)
|
|||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
text = state.priceText,
|
||||
style = TangemTheme.typography.head,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
Column(
|
||||
Modifier.weight(1f),
|
||||
) {
|
||||
TokenPriceText(
|
||||
price = state.priceText,
|
||||
triggerPriceChange = state.triggerPriceChange,
|
||||
)
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4)) {
|
||||
Text(
|
||||
|
|
@ -155,7 +162,7 @@ private fun Header(state: MarketsTokenDetailsUM, modifier: Modifier = Modifier)
|
|||
}
|
||||
SpacerW4()
|
||||
CoinIcon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size48),
|
||||
modifier = Modifier.requiredSize(TangemTheme.dimens.size48),
|
||||
url = state.iconUrl,
|
||||
alpha = 1f,
|
||||
colorFilter = null,
|
||||
|
|
@ -164,6 +171,38 @@ private fun Header(state: MarketsTokenDetailsUM, modifier: Modifier = Modifier)
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenPriceText(
|
||||
price: String,
|
||||
triggerPriceChange: StateEvent<PriceChangeType>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val growColor = TangemTheme.colors.text.accent
|
||||
val fallColor = TangemTheme.colors.text.warning
|
||||
val generalColor = TangemTheme.colors.text.primary1
|
||||
|
||||
val color = remember { Animatable(generalColor) }
|
||||
|
||||
EventEffect(triggerPriceChange) {
|
||||
val nextColor = when (it) {
|
||||
PriceChangeType.UP,
|
||||
-> growColor
|
||||
PriceChangeType.DOWN -> fallColor
|
||||
PriceChangeType.NEUTRAL -> return@EventEffect
|
||||
}
|
||||
|
||||
color.animateTo(nextColor, snap())
|
||||
color.animateTo(generalColor, tween(durationMillis = 500))
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = modifier,
|
||||
text = price,
|
||||
color = color.value,
|
||||
style = TangemTheme.typography.head,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun IntervalSelector(
|
||||
trendInterval: PriceChangeInterval,
|
||||
|
|
@ -224,9 +263,9 @@ private fun Preview() {
|
|||
modifier = Modifier.background(TangemTheme.colors.background.tertiary),
|
||||
state = MarketsTokenDetailsUM(
|
||||
tokenName = "Token Name",
|
||||
priceText = "Price",
|
||||
dateTimeText = stringReference("Date Time"),
|
||||
priceChangePercentText = "Price Change",
|
||||
priceText = "$0.00000000324",
|
||||
dateTimeText = stringReference("Today"),
|
||||
priceChangePercentText = "52.00%",
|
||||
iconUrl = "",
|
||||
priceChangeType = PriceChangeType.UP,
|
||||
chartState = MarketsTokenDetailsUM.ChartState(
|
||||
|
|
@ -244,6 +283,8 @@ private fun Preview() {
|
|||
onDismissRequest = {},
|
||||
content = TangemBottomSheetConfigContent.Empty,
|
||||
),
|
||||
markerSet = false,
|
||||
triggerPriceChange = consumedEvent(),
|
||||
),
|
||||
onHeaderSizeChange = {},
|
||||
onBackClick = {},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.features.markets.details.impl.ui.components
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.Text
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.common.ui.charts.state.MarketChartDataProducer
|
|||
import com.tangem.common.ui.charts.state.MarketChartLook
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeType
|
||||
import com.tangem.core.ui.event.StateEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.markets.PriceChangeInterval
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -17,9 +18,11 @@ internal data class MarketsTokenDetailsUM(
|
|||
val priceChangePercentText: String,
|
||||
val priceChangeType: PriceChangeType,
|
||||
val selectedInterval: PriceChangeInterval,
|
||||
val markerSet: Boolean,
|
||||
val chartState: ChartState,
|
||||
val onSelectedIntervalChange: (PriceChangeInterval) -> Unit,
|
||||
val infoBottomSheet: TangemBottomSheetConfig,
|
||||
val triggerPriceChange: StateEvent<PriceChangeType>,
|
||||
val body: Body,
|
||||
) {
|
||||
|
||||
|
|
|
|||
|
|
@ -48,17 +48,17 @@ internal class MarketsTokenItemConverter(
|
|||
ratingPosition = new.marketRating?.toString(),
|
||||
marketCap = ifChanged(prev.marketCap, new.marketCap, prevUI.marketCap) { new.getMarketCap() },
|
||||
iconUrl = new.imageUrlLarge,
|
||||
price = ifChanged(prev = prev.tokenQuotes, new = new.tokenQuotes, prevR = prevUI.price) {
|
||||
price = ifChanged(prev = prev.tokenQuotesShort, new = new.tokenQuotesShort, prevR = prevUI.price) {
|
||||
new.getCurrentPrice(
|
||||
prev = prev,
|
||||
)
|
||||
},
|
||||
trendPercentText = ifChanged(
|
||||
prev.tokenQuotes,
|
||||
new.tokenQuotes,
|
||||
prev.tokenQuotesShort,
|
||||
new.tokenQuotesShort,
|
||||
prevUI.trendPercentText,
|
||||
) { new.getTrendPercent() },
|
||||
trendType = ifChanged(prev.tokenQuotes, new.tokenQuotes, prevUI.trendType) { new.getTrendType() },
|
||||
trendType = ifChanged(prev.tokenQuotesShort, new.tokenQuotesShort, prevUI.trendType) { new.getTrendType() },
|
||||
chardData = ifChanged(prev.tokenCharts, new.tokenCharts, prevUI.chardData) { new.getChartData() },
|
||||
)
|
||||
}
|
||||
|
|
@ -79,16 +79,16 @@ internal class MarketsTokenItemConverter(
|
|||
}
|
||||
|
||||
private fun TokenMarket.getCurrentPrice(prev: TokenMarket? = null): MarketsListItemUM.Price {
|
||||
val prevPrice = prev?.tokenQuotes?.currentPrice
|
||||
val prevPrice = prev?.tokenQuotesShort?.currentPrice
|
||||
|
||||
val priceText = BigDecimalFormatter.formatFiatAmountUncapped(
|
||||
fiatAmount = tokenQuotes.currentPrice,
|
||||
val priceText = BigDecimalFormatter.formatFiatPriceUncapped(
|
||||
fiatAmount = tokenQuotesShort.currentPrice,
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
|
||||
val changeType = if (prevPrice != null) {
|
||||
if (tokenQuotes.currentPrice > prevPrice) {
|
||||
if (tokenQuotesShort.currentPrice > prevPrice) {
|
||||
PriceChangeType.UP
|
||||
} else {
|
||||
PriceChangeType.DOWN
|
||||
|
|
@ -122,9 +122,9 @@ internal class MarketsTokenItemConverter(
|
|||
|
||||
private fun TokenMarket.getTrendType(): PriceChangeType {
|
||||
val percent = when (currentTrendInterval) {
|
||||
TrendInterval.H24 -> tokenQuotes.h24Percent()
|
||||
TrendInterval.D7 -> tokenQuotes.weekPercent()
|
||||
TrendInterval.M1 -> tokenQuotes.monthPercent()
|
||||
TrendInterval.H24 -> tokenQuotesShort.h24ChangePercent
|
||||
TrendInterval.D7 -> tokenQuotesShort.weekChangePercent
|
||||
TrendInterval.M1 -> tokenQuotesShort.monthChangePercent
|
||||
}.setScale(2, RoundingMode.UP)
|
||||
|
||||
return when (percent.compareTo(BigDecimal.ZERO)) {
|
||||
|
|
@ -136,9 +136,9 @@ internal class MarketsTokenItemConverter(
|
|||
|
||||
private fun TokenMarket.getTrendPercent(): String {
|
||||
val percent = when (currentTrendInterval) {
|
||||
TrendInterval.H24 -> tokenQuotes.h24Percent()
|
||||
TrendInterval.D7 -> tokenQuotes.weekPercent()
|
||||
TrendInterval.M1 -> tokenQuotes.monthPercent()
|
||||
TrendInterval.H24 -> tokenQuotesShort.h24ChangePercent
|
||||
TrendInterval.D7 -> tokenQuotesShort.weekChangePercent
|
||||
TrendInterval.M1 -> tokenQuotesShort.monthChangePercent
|
||||
}
|
||||
|
||||
return BigDecimalFormatter.formatPercent(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ 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.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.features.staking.impl.R
|
||||
|
||||
internal sealed class StakingNotification(val config: NotificationConfig) {
|
||||
|
|
@ -55,8 +54,7 @@ internal sealed class StakingNotification(val config: NotificationConfig) {
|
|||
) : Warning(
|
||||
title = resourceReference(R.string.staking_notification_earn_rewards_title),
|
||||
subtitle = resourceReference(
|
||||
R.string.staking_notification_earn_rewards_text,
|
||||
wrappedList(currencyName, days),
|
||||
R.string.common_error, // FIXME
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue