diff --git a/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt index e96df425a1..28b30cb9e3 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/MarketsDomainModule.kt @@ -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) + } } \ No newline at end of file diff --git a/common/ui-charts/src/main/kotlin/com/tangem/common/ui/charts/MarketChart.kt b/common/ui-charts/src/main/kotlin/com/tangem/common/ui/charts/MarketChart.kt index 917b2eab45..2e850fd87d 100644 --- a/common/ui-charts/src/main/kotlin/com/tangem/common/ui/charts/MarketChart.kt +++ b/common/ui-charts/src/main/kotlin/com/tangem/common/ui/charts/MarketChart.kt @@ -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() diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index 6d99e6bd52..51b2bd8358 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -1,7 +1,7 @@ Netzwerk wählen - Benutzerdef. Token hinzuf. + Token anlegen Token verwalten 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. So scannt man @@ -88,7 +88,7 @@ Kopieren Adresse kopieren Erstellen - Benutzerdef. + Eigen %d tag %d tage @@ -126,6 +126,7 @@ Passphrase Einfügen %1$s-%2$s + %1$s — %2$s Weiterlesen Empfangen Ablehnen @@ -278,7 +279,7 @@ Rückmeldung Feedback zu Tangem Eine Transaktion kann nicht gesendet werden - Aktuelle Transaktion + Nur diese Das Netzwerk erhebt eine Token-Genehmigungsgebühr, um zu überprüfen, ob Sie die Verwendung Ihres Tokens für den Swap genehmigen. Gib das Genehmigungslimit für das ausgewählte Token an Betrag %s @@ -341,7 +342,7 @@ Verfügbare Netzwerke Mein Portfolio Markt - Um Adressen für ausgewählte Netzwerke zu generieren, musst du eine Tangem-Karte scannen/ einsetzen + Um Adressen für ausgewählte Netzwerke zu generieren, musst du eine Tangem-Karte scannen Die Daten konnten nicht geladen werden... Schnelle Aktionen Ergebnis @@ -446,7 +447,7 @@ Möchtest du den Aktivierungsprozess abbrechen? Erste Schritte 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. - Erstellen eines Backups + Backups anlegen Lese mehr über die Seed-Phrase leer @@ -499,7 +500,7 @@ Gruppe erstellen Nach Guthaben Token organisieren - Gruppierung aufh. + Gruppe löschen Wählen aus der Galerie aus Einstellungen Du hast keinen Zugriff auf deine Kamera gewährt @@ -647,7 +648,7 @@ Aktiv Um deine Kryptos zu unstaken, klick hier. Die Anzahl der zu stakenden Krypros muss mindesten %s betragen - nicht gestakte beanspruche + Nicht gestakte beanspruche Jährliche prozentuale Rendite Die jährliche prozentuale Rendite, die du durch die Teilnahme am Staking erzielen kannst. Effektiver Jahreszins @@ -671,7 +672,10 @@ Die zugewiesene Zeit für die Aktivierung der Teilnahme am Staking. Migrieren Natives Staking - Mit Staking kannst du %1$s verdienen. Deine Staking-Belohnungen kommen alle ~%2$s Tage. + Mit dem Staking kannst Du %1$s verdienen. Deine Staking-Belohnungen erhältst Du jeden tag. + Mit dem Staking kannst Du %1$s verdienen. Deine Staking-Belohnungen erhältst Du jede Stunde. + Mit dem Staking kannst Du %1$s verdienen. Deine Staking-Belohnungen erhältst Du jeden Monat. + Mit dem Staking kannst Du %1$s verdienen. Deine Staking-Belohnungen erhältst Du jede Woche. Verdiene Staking-Belohnungen Die Belohnungen werden sofort nach dem unstaken gestoppt. Der unstakingprozess dauert %s. Erneut binden @@ -679,10 +683,11 @@ Belohnungen erneut staken Widerrufen Neuwahl - automatisch - händisch + Automatisch + Händisch Block Tag + Täglich Epoche Ära Stunde @@ -693,7 +698,7 @@ Mehr staken Stake %s Staking beenden%s - gelocktes unlocken + Gelocktes unlocken Unstaken Prüfe, was nicht eingesetzt wurde, um dein Vermögen zu beanspruchen Staking beenden @@ -834,7 +839,7 @@ Aktivierungsfehler 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. BNB Beacon Chain wird abgeschaltet - ausbaufähig + Ausbaufähig Gefällt mir OK, habe ich verstanden! Echt toll! diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index 3c55b9ec8c..9155dc35a7 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -123,6 +123,7 @@ Passphrase Coller %1$s-%2$s + %1$s — %2$s En savoir plus Recevoir Rejeter @@ -269,7 +270,7 @@ Commentaires Commentaires sur Tangem Impossible d\'envoyer une transaction - Transaction en cours + Transaction Le réseau facturera des frais d\'approbation de jeton pour vérifier que vous autorisez l\'utilisation de votre jeton pour l\'échange. Spécifiez la limite approuvée pour le jeton sélectionné Montant %s @@ -328,7 +329,7 @@ Ajouter au portfolio Mon portfolio Marché - Pour générer des adresses pour les réseaux sélectionnés, vous devez scanner votre carte Tangem. + Pour générer des adresses pour les réseaux sélectionnés, vous devez scanner votre carte Tangem Sélectionnez un portefeuille Trier par Idées @@ -594,7 +595,6 @@ Période d\'échauffement Le temps imparti pour activer la participation au staking. Native staking - Le staking vous permet d\'en gagner %1$s. Vos récompenses de staking arrivent tous les ~%2$s jours. Gagnez des récompenses de staking Récompenses Staker plus diff --git a/core/res/src/main/res/values-it/strings.xml b/core/res/src/main/res/values-it/strings.xml index b8860bd7d8..e7caa7829e 100644 --- a/core/res/src/main/res/values-it/strings.xml +++ b/core/res/src/main/res/values-it/strings.xml @@ -14,7 +14,6 @@ Mantieni le modifiche Invia Con successo - Avviso Codice di accesso Prima di scansionare la carta sarà necessario inserire il codice di accesso corretto Mantenimento della carta diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index d5d7502641..6d695bc564 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -124,6 +124,7 @@ パスフレーズ ペースト %1$s-%2$s + %1$s — %2$s 続きを読む 受け取る 拒否 @@ -276,7 +277,7 @@ フィードバック Tangemへのフィードバック 取引を送信できません - 現在の取引 + 取引 ネットワークは、あなたがトークンのスワップを承認していることを確認するために、トークン承認手数料を請求します。 選択したトークンの承認制限を指定します 金額%s @@ -636,7 +637,7 @@ 資産のステーキングを解除するには、ここをクリックしてください。 ステーキング金額は %s 以上である必要があります ステーキング解除分を請求する - APY + 年率 ステーキングに参加することで得られる年間収益率。 APR 利用可能 @@ -659,7 +660,10 @@ ステーキングへの参加を有効にするために割り当てられた時間。 移行 ネイティブステーキング - ステーキングにより%1$sを獲得できます。ステーキング報酬は ~ %2$s日ごとに届きます。 + ステーキングにより%1$sを獲得できます。ステーキング報酬は毎日受け取れます。 + ステーキングにより%1$sを獲得できます。ステーキング報酬は1時間ごとに受け取れます。 + ステーキングにより%1$sを獲得できます。ステーキング報酬は毎月受け取れます。 + ステーキングにより%1$sを獲得できます。ステーキング報酬は毎週受け取れます。 ステーキング報酬を獲得 ステーキング解除後、報酬の獲得はすぐに停止します。ステーキング解除プロセスには%sかかります。 再結束 @@ -671,6 +675,7 @@ 手動 ブロック + 毎日 エポック 時代 @@ -686,6 +691,7 @@ 資産を請求するために、unstakedを確認してください ステーキング解除 バリデーター + バリデーター 投票する 投票はロックされています 引き出す diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index f7c6ecf61c..9df99375b9 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -47,7 +47,7 @@ Отключите эту опцию, если не хотите, чтобы эта карта использовалась для сброса кодов доступа на другие карты этого кошелька. Обратите внимание, сброс кода также не будет доступен на этой карте. Использовать эту карту для сброса кода доступа на других картах в этом кошельке - Восстановление кода доступа + Восстановление доступа Сбросить Вы уверены, что хотите это сделать? Смена кода доступа @@ -128,6 +128,7 @@ Парольная фраза Вставить %1$s-%2$s + %1$s — %2$s Подробнее Получить Отклонить @@ -628,7 +629,6 @@ Для завершения стейкинга нажмите сюда Сумма для стейкинга должна быть не менее %s Забрать средства - APY Годовой процентный доход, который вы можете получить от участия в стейкинге. APR Доступно @@ -650,13 +650,26 @@ Время, необходимое для начала процесса стейкинга и активации процесса начисления наград Переместить Нативный стейкинг - Стейкинг дает возможность вам получать %1$s. Награда будет зачисляться каждый %2$s + Стейкинг дает возможность вам получать %1$s. Награда будет зачисляться каждый день. + Стейкинг дает возможность вам получать %1$s. Награда будет зачисляться каждый час. + Стейкинг дает возможность вам получать %1$s. Награда будет зачисляться каждый месяц. + Стейкинг дает возможность вам получать %1$s. Награда будет зачисляться каждую неделю. Получите награду за стейкинг Награда перестанет начисляться сразу после завершения стейкинга. Процесс завершения длится %s. Повторный стейкинг Застейкать вознаграждения Отозвать Переголосовать + Автоматически + Вручную + Блок + День + Каждый день + Эпоха + Эра + Час + Месяц + Неделя Вознаграждения Застейкать еще Застейкать %s diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml index bdb802eb7e..d3c560257c 100644 --- a/core/res/src/main/res/values-uk-rUA/strings.xml +++ b/core/res/src/main/res/values-uk-rUA/strings.xml @@ -130,6 +130,7 @@ Парольна фраза Вставити %1$s-%2$s + %1$s — %2$s Детальніше Отримати Відхилити @@ -282,7 +283,7 @@ Звернення в підтримку Звернення в підтримку Tangem Не вдається відправити транзакцію - Поточна транзакція + Транзакція Мережа стягує комісію за схвалення токену за підтвердження, що саме ви дозволяєте використовувати ваш токен для обміну. Вкажіть ліміт доступу для обраного токена Кількість %s @@ -347,7 +348,7 @@ Доступні мережі Моє портфоліо Маркет - Щоб згенерувати адреси для обраних мереж, потрібно відсканувати свою картку Tangem. + Щоб згенерувати адреси для обраних мереж, потрібно відсканувати свою картку Tangem Не вдалося завантажити дані... Швидкі дії Результат @@ -682,7 +683,10 @@ Відведений час для активації участі в стейкінгу. Перемістити Нативний стейкінг - Стейкінг дозволяє заробляти %1$s. Ваші винагороди за стейкінг надходять кожні ~%2$s днів. + Стейкінг дозволяє заробляти %1$s. Ваші винагороди за стейкінг зараховуються щодня. + Стейкінг дозволяє заробляти %1$s. Ваші винагороди за стейкінг зараховуються кожну годину. + Стейкінг дозволяє заробляти %1$s. Ваші винагороди за стейкінг зараховуються щомісяця. + Стейкінг дозволяє заробляти %1$s. Ваші винагороди за стейкінг зараховуються щотиждня. Отримуйте винагороду за стейкінг Винагороди припиняють нараховуватися одразу після того, як ви знімаєте ставку. Процес зняття займає %s. Зʼєднати ще раз diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml index acd0b259b8..eb5bc2de81 100644 --- a/core/res/src/main/res/values-zh-rTW/strings.xml +++ b/core/res/src/main/res/values-zh-rTW/strings.xml @@ -9,9 +9,6 @@ 原因:%s 無法發送交易 此卡不支持%1$s網路上的代幣因為韌體限制 - 感謝您的反饋。我們會盡快回复 - 你的建議已送出 - 請嘗試完全按照動畫中顯示的方式點擊卡片或請求支持 有困難在掃描卡上嗎? 此卡不適用於此app 轉到設置以在 Tangem App 中啟用生物識別身份驗證 @@ -56,7 +53,6 @@ 創造 刪除 禁用 - 斷開連接 完成 允許 啟用 @@ -69,7 +65,6 @@ 主卡片 拒絕 重新命名 - 重試 保存設置 搜索 搜尋代幣 @@ -88,7 +83,6 @@ 交易 我了解 無法觸達 - 警告 已複製代幣地址 支持的網路 @@ -121,7 +115,6 @@ App Currency 發行人 簽署 - 如果您忘記密碼,您將無法使用您的資金。無法恢復代碼 更多 檢查您的網路連接或切換到其他網絡 服務條款 diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 5a4b8c08bb..23d562fd61 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -126,6 +126,7 @@ Passphrase Paste %1$s-%2$s + %1$s — %2$s Read more Receive Reject @@ -667,7 +668,10 @@ The allocated time for activating participation in staking. Migrate Native staking - Staking allow you to earn %1$s. Your staking rewards arrive every ~%2$s days. + Staking allow you to earn %1$s. Your staking rewards arrive every day. + Staking allow you to earn %1$s. Your staking rewards arrive every hour. + Staking allow you to earn %1$s. Your staking rewards arrive every month. + Staking allow you to earn %1$s. Your staking rewards arrive every week. Earn staking rewards Rewards stop accruing immediately after you unstake. The unstaking process takes %s. Rebond @@ -679,6 +683,7 @@ Manual Block Day + Each day Epoch Era Hour diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/PriceChangeInPercent.kt b/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/PriceChangeInPercent.kt index d2c55ed576..a202d8bf21 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/PriceChangeInPercent.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/PriceChangeInPercent.kt @@ -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, diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/BigDecimalFormatter.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/BigDecimalFormatter.kt index 40e51a085c..d1d4cf9c8c 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/utils/BigDecimalFormatter.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/BigDecimalFormatter.kt @@ -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() + } + } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/DateTimeFormatters.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/DateTimeFormatters.kt index 7c6fa15b74..20296aec51 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/utils/DateTimeFormatters.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/DateTimeFormatters.kt @@ -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()) + } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/DateUtils.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/DateUtils.kt index bc2560f6e9..e01b50dfb3 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/utils/DateUtils.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/DateUtils.kt @@ -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) } \ No newline at end of file diff --git a/data/markets/src/main/java/com/tangem/data/markets/DefaultMarketsTokenRepository.kt b/data/markets/src/main/java/com/tangem/data/markets/DefaultMarketsTokenRepository.kt index e670fce633..e850d1ed35 100644 --- a/data/markets/src/main/java/com/tangem/data/markets/DefaultMarketsTokenRepository.kt +++ b/data/markets/src/main/java/com/tangem/data/markets/DefaultMarketsTokenRepository.kt @@ -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 } } \ No newline at end of file diff --git a/data/markets/src/main/java/com/tangem/data/markets/MarketsBatchUpdateFetcher.kt b/data/markets/src/main/java/com/tangem/data/markets/MarketsBatchUpdateFetcher.kt index 0da0fcd457..d1db77b24e 100644 --- a/data/markets/src/main/java/com/tangem/data/markets/MarketsBatchUpdateFetcher.kt +++ b/data/markets/src/main/java/com/tangem/data/markets/MarketsBatchUpdateFetcher.kt @@ -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, TokenMarketUpdateRequest> { - private val tokenListChartsConverter = TokenMarketChartsConverter(TokenChartConverter()) - private val tokenQuotesConverter = TokenQuotesConverter() - override suspend fun BatchUpdateFetcher.UpdateContext>.fetchUpdateAsync( toUpdate: List>>, 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, diff --git a/data/markets/src/main/java/com/tangem/data/markets/converters/TokenChartConverter.kt b/data/markets/src/main/java/com/tangem/data/markets/converters/TokenChartConverter.kt index bdd90f2733..744e6fef48 100644 --- a/data/markets/src/main/java/com/tangem/data/markets/converters/TokenChartConverter.kt +++ b/data/markets/src/main/java/com/tangem/data/markets/converters/TokenChartConverter.kt @@ -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( diff --git a/data/markets/src/main/java/com/tangem/data/markets/converters/TokenListConfigConverters.kt b/data/markets/src/main/java/com/tangem/data/markets/converters/TokenListConfigConverters.kt index eee63ef4e9..1875a5b609 100644 --- a/data/markets/src/main/java/com/tangem/data/markets/converters/TokenListConfigConverters.kt +++ b/data/markets/src/main/java/com/tangem/data/markets/converters/TokenListConfigConverters.kt @@ -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" diff --git a/data/markets/src/main/java/com/tangem/data/markets/converters/TokenMarketChartsConverter.kt b/data/markets/src/main/java/com/tangem/data/markets/converters/TokenMarketChartsConverter.kt index 88eccb1163..fdd44ffba9 100644 --- a/data/markets/src/main/java/com/tangem/data/markets/converters/TokenMarketChartsConverter.kt +++ b/data/markets/src/main/java/com/tangem/data/markets/converters/TokenMarketChartsConverter.kt @@ -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.") } diff --git a/data/markets/src/main/java/com/tangem/data/markets/converters/TokenMarketInfoConverter.kt b/data/markets/src/main/java/com/tangem/data/markets/converters/TokenMarketInfoConverter.kt index 8693393e68..e9314b0d7e 100644 --- a/data/markets/src/main/java/com/tangem/data/markets/converters/TokenMarketInfoConverter.kt +++ b/data/markets/src/main/java/com/tangem/data/markets/converters/TokenMarketInfoConverter.kt @@ -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 { +internal object TokenMarketInfoConverter : Converter { override fun convert(value: TokenMarketInfoResponse): TokenMarketInfo { return with(value) { @@ -12,8 +13,7 @@ internal class TokenMarketInfoConverter : Converter> { +internal object TokenMarketListConverter : Converter> { override fun convert(value: TokenMarketListResponse): List { val imageHost = value.imageHost ?: run { @@ -25,13 +24,11 @@ class TokenMarketListConverter : Converter?, 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, diff --git a/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/TokenQuotes.kt b/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/TokenQuotes.kt index 7a162c9aac..18826801e3 100644 --- a/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/TokenQuotes.kt +++ b/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/TokenQuotes.kt @@ -4,15 +4,11 @@ import java.math.BigDecimal data class TokenQuotes( val currentPrice: BigDecimal, - private val priceChanges: Map, -) { - 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]!! -} \ No newline at end of file + val h24ChangePercent: BigDecimal?, + val weekChangePercent: BigDecimal?, + val monthChangePercent: BigDecimal?, + val m3ChangePercent: BigDecimal?, + val m6ChangePercent: BigDecimal?, + val yearChangePercent: BigDecimal?, + val allTimeChangePercent: BigDecimal?, +) \ No newline at end of file diff --git a/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/TokenQuotesShort.kt b/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/TokenQuotesShort.kt new file mode 100644 index 0000000000..3b6bb3bdf1 --- /dev/null +++ b/domain/markets/models/src/main/kotlin/com/tangem/domain/markets/TokenQuotesShort.kt @@ -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, +) \ No newline at end of file diff --git a/domain/markets/src/main/java/com/tangem/domain/markets/GetTokenQuotesUseCase.kt b/domain/markets/src/main/java/com/tangem/domain/markets/GetTokenQuotesUseCase.kt new file mode 100644 index 0000000000..6477010d57 --- /dev/null +++ b/domain/markets/src/main/java/com/tangem/domain/markets/GetTokenQuotesUseCase.kt @@ -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 { + return Either.catch { + marketsTokenRepository.getTokenQuotes( + fiatCurrencyCode = appCurrency.code, + tokenId = tokenId, + ) + }.mapLeft {} + } +} \ No newline at end of file diff --git a/domain/markets/src/main/java/com/tangem/domain/markets/repositories/MarketsTokenRepository.kt b/domain/markets/src/main/java/com/tangem/domain/markets/repositories/MarketsTokenRepository.kt index e6e3a23e03..5c80144aec 100644 --- a/domain/markets/src/main/java/com/tangem/domain/markets/repositories/MarketsTokenRepository.kt +++ b/domain/markets/src/main/java/com/tangem/domain/markets/repositories/MarketsTokenRepository.kt @@ -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 } \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/api/TokenMarketSerializable.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/api/TokenMarketSerializable.kt index 4e978ed5ee..a1aa6b178c 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/api/TokenMarketSerializable.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/api/TokenMarketSerializable.kt @@ -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, ) diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/DefaultMarketsTokenDetailsComponent.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/DefaultMarketsTokenDetailsComponent.kt index 5bc189be06..402356487a 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/DefaultMarketsTokenDetailsComponent.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/DefaultMarketsTokenDetailsComponent.kt @@ -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, ) diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt index 2359614915..9f9d524a84 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/MarketsTokenDetailsModel.kt @@ -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() + private var quotesJob = JobHolder() + private val params = paramsContainer.require() 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 + } } \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/converters/PricePerformanceConverter.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/converters/PricePerformanceConverter.kt index b08b518ba0..0ccf01cde7 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/converters/PricePerformanceConverter.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/converters/PricePerformanceConverter.kt @@ -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, ) diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/formatter/Formatters.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/formatter/Formatters.kt new file mode 100644 index 0000000000..fd560e41d7 --- /dev/null +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/formatter/Formatters.kt @@ -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 + } +} \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/formatter/MarketsDateTimeFormatters.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/formatter/MarketsDateTimeFormatters.kt new file mode 100644 index 0000000000..e93456eb34 --- /dev/null +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/model/formatter/MarketsDateTimeFormatters.kt @@ -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 + } + } +} \ No newline at end of file diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/MarketsTokenDetailsContent.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/MarketsTokenDetailsContent.kt index 7f8dfd5def..ecfc715abd 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/MarketsTokenDetailsContent.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/MarketsTokenDetailsContent.kt @@ -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, + 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 = {}, diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/PricePerformanceBlock.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/PricePerformanceBlock.kt index cb3098ca60..66a09fb533 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/PricePerformanceBlock.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/components/PricePerformanceBlock.kt @@ -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 diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/state/MarketsTokenDetailsUM.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/state/MarketsTokenDetailsUM.kt index 42f7c3e9e0..473bc3a518 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/state/MarketsTokenDetailsUM.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/ui/state/MarketsTokenDetailsUM.kt @@ -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, val body: Body, ) { diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/tokenlist/impl/model/converters/MarketsTokenItemConverter.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/tokenlist/impl/model/converters/MarketsTokenItemConverter.kt index 590aae7f3f..c43e29f406 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/tokenlist/impl/model/converters/MarketsTokenItemConverter.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/tokenlist/impl/model/converters/MarketsTokenItemConverter.kt @@ -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( diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt index 525eed8a61..327f1e521c 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt @@ -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 ), ) }