From cfd8ae295761e58eb59b5be77d729b681bcaf63a Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 10 Oct 2025 12:28:38 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../ui/tokens/TokenItemStateConverter.kt | 33 ++++++- core/res/src/main/res/values-de/strings.xml | 2 +- core/res/src/main/res/values-es/strings.xml | 2 + core/res/src/main/res/values-fr/strings.xml | 2 + core/res/src/main/res/values-ja/strings.xml | 21 ++++- core/res/src/main/res/values-ru/strings.xml | 9 +- core/res/src/main/res/values/strings.xml | 31 +++++-- .../converter/TokenListStateConverter.kt | 47 +--------- .../impl/main/DefaultYieldSupplyComponent.kt | 3 +- .../supply/impl/main/entity/YieldSupplyUM.kt | 3 +- .../impl/main/model/YieldSupplyModel.kt | 85 ++++++++++++------ .../impl/main/ui/YieldSupplyBlockContent.kt | 86 +++++++++++-------- .../active/ui/YieldSupplyActiveContent.kt | 25 ++---- 13 files changed, 209 insertions(+), 140 deletions(-) diff --git a/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt index 0d23288ee3..de0d5e70fe 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt @@ -7,7 +7,9 @@ import com.tangem.core.ui.components.icons.IconTint import com.tangem.core.ui.components.marketprice.PriceChangeType import com.tangem.core.ui.components.marketprice.utils.PriceChangeConverter import com.tangem.core.ui.components.token.state.TokenItemState +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.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format @@ -15,6 +17,8 @@ import com.tangem.core.ui.format.bigdecimal.percent import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.currency.yieldSupplyKey import com.tangem.domain.models.staking.YieldBalance import com.tangem.domain.staking.utils.getTotalWithRewardsStakingBalance import com.tangem.utils.StringsSigns.DASH_SIGN @@ -35,10 +39,13 @@ import java.math.BigDecimal */ class TokenItemStateConverter( private val appCurrency: AppCurrency, + private val apyMap: Map = emptyMap(), private val iconStateProvider: (CryptoCurrencyStatus) -> CurrencyIconState = { CryptoCurrencyToIconStateConverter().convert(it) }, - private val titleStateProvider: (CryptoCurrencyStatus) -> TokenItemState.TitleState = Companion::createTitleState, + private val titleStateProvider: (CryptoCurrencyStatus) -> TokenItemState.TitleState = { + createTitleState(it, apyMap) + }, private val subtitleStateProvider: (CryptoCurrencyStatus) -> TokenItemState.SubtitleState? = { createSubtitleState(it, appCurrency) }, @@ -144,7 +151,10 @@ class TokenItemStateConverter( private fun CryptoCurrencyStatus.getStakedBalance() = (value.yieldBalance as? YieldBalance.Data) ?.getTotalWithRewardsStakingBalance(blockchainId = currency.network.rawId).orZero() - private fun createTitleState(currencyStatus: CryptoCurrencyStatus): TokenItemState.TitleState { + private fun createTitleState( + currencyStatus: CryptoCurrencyStatus, + apyMap: Map, + ): TokenItemState.TitleState { return when (val value = currencyStatus.value) { is CryptoCurrencyStatus.Loading, is CryptoCurrencyStatus.MissedDerivation, @@ -158,14 +168,33 @@ class TokenItemStateConverter( is CryptoCurrencyStatus.NoQuote, is CryptoCurrencyStatus.NoAccount, -> { + val earnApyText = resolveEarnApy(currencyStatus, apyMap)?.let { apy -> + resourceReference( + R.string.yield_module_earn_badge, + wrappedList(apy), + ) + } TokenItemState.TitleState.Content( text = stringReference(currencyStatus.currency.name), hasPending = value.hasCurrentNetworkTransactions, + earnApy = earnApyText, ) } } } + private fun resolveEarnApy(cryptoCurrencyStatus: CryptoCurrencyStatus, apyMap: Map): String? { + if (apyMap.isEmpty()) return null + + val isYieldSupplyActive = (cryptoCurrencyStatus.value as? CryptoCurrencyStatus.Loaded) + ?.yieldSupplyStatus?.isActive == true + if (isYieldSupplyActive) return null + + val token = cryptoCurrencyStatus.currency as? CryptoCurrency.Token ?: return null + + return apyMap[token.yieldSupplyKey()] + } + private fun createSubtitleState( currencyStatus: CryptoCurrencyStatus, appCurrency: AppCurrency, diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index 522132b11b..56f11d7388 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -1362,7 +1362,7 @@ Frühzeitiger Zugriff auf neue Funktionen und exklusive Angebote. Updates zu Funktionen und Neuigkeiten Möchtest du Push-Benachrichtigungen verwenden? - Aktiviere Push-Benachrichtigungen und wir benachrichtigen Dich sofort, wenn Gelder eintreffen \n + Aktiviere Push-Benachrichtigungen und wir benachrichtigen Dich sofort, wenn Gelder eintreffen Verpasse keine Transaktion Neues Wallet hinzufügen Möchtest Du diese Wallet wirklich entfernen? diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml index dcce73dd3a..6a2f5f1caf 100644 --- a/core/res/src/main/res/values-es/strings.xml +++ b/core/res/src/main/res/values-es/strings.xml @@ -1212,6 +1212,8 @@ Acceso anticipado a nuevas funciones y ofertas exclusivas. Actualizaciones de características y noticias ¿Quiere utilizar\nnotificaciones push? + Active las notificaciones push y le avisaremos al instante cuando le lleguen fondos. + No se pierda ninguna transacción Agregar una nueva billetera ¿Estás seguro de que deseas olvidar esta billetera? Ha ocurrido un error, por favor escanee su tarjeta o anillo para iniciar sesión diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index 004c7dd622..868ad2f695 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -1185,6 +1185,8 @@ Accès anticipé à de nouvelles fonctionnalités et à des offres exclusives. Actualités et mises à jour Souhaitez-vous utiliser les\nnotifications push? + Activez les notifications pour recevoir des alertes lorsque des fonds arrivent dans votre portefeuille. + Ne manquez aucune transaction Ajouter un nouveau portefeuille Êtes-vous sûr de vouloir supprimer ce portefeuille ? Une erreur s\'est produite, veuillez scanner votre carte ou bague pour vous connecter diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index 97d1117305..c357e3c31f 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -291,6 +291,7 @@ %1$s — %2$s 続きを読む 受け取る + おすすめ 拒否 リロード 名前を変更 @@ -347,6 +348,8 @@ はい コントラクトアドレスをコピーしました! 利用可能なネットワーク + このトークンの導出パスが%1$sの導出パスと一致しています。トークンはこのアカウントに追加されます。 + 導出パスは別のアカウントに属します。 コントラクトアドレス コントラクトアドレスが無効です ネットワークを選択してください @@ -464,6 +467,7 @@ プロバイダー ベストレート FCA警告リスト + お得なレート FCA警告リストに掲載されたプロバイダー 最大 %s まで使用可能 %s 以上で利用可能 @@ -1361,9 +1365,10 @@ 新しい機能や限定オファーへの早期アクセス。 機能およびニュースのアップデート プッシュ通知を使用しますか? - プッシュ通知を有効にすると、資金が到着するとすぐに通知されます + プッシュ通知を有効にすると、資金が到着するとすぐに通知されます。 取引を見逃さない 新しいウォレットを追加 + バックアップせずにこのウォレットを削除すると、資金に永久にアクセスできなくなります。 このウォレットを忘れてもよろしいですか? エラーが発生しました。カードまたはリングをスキャンしてログインしてください。 このウォレットはすでに保存されています。別のウォレットを追加できます。 @@ -1393,6 +1398,13 @@ ロック解除 カードをスキャンしてアクセスロックを解除する ロック解除が必要 + ウォレットの追加方法を選択してください + Tangemカードまたはリングをスキャンして復元するか、別のウォレットからインポートしてください。 + ハードウェアウォレットを作成 + Tangemウォレットを購入しますか? + シードフレーズをインポート + スマートフォン上でウォレットを復元するか、他のアプリからインポートできますが、Tangemカードと比べるとセキュリティは劣ります。 + お選びください ブロックチェーンにアクセスできません。後でもう一度お試しください。 カードまたはリングをスキャン このウォレットはすでに有効化されています。\nあなたが行ったのでない場合は、サポートに連絡してください。\nTangemは、事前に生成されたアクセスコードと一緒にウォレットを販売することはありません。 @@ -1657,13 +1669,20 @@ URIはすでに使用されています WalletConnect 不審な取引 + すでにTangemをお持ちですか? + 数千種類の資産 業界最高水準のハードウェアウォレット 迅速な配送 + ワンタップで開始 + シームレスで安全 シンプルな操作 Tangemでハードウェアウォレットを作成しましょう。銀行のカードのようにスリムで、金庫のように安全です。 ソフトウェアウォレットを作成またはインポート + スマートフォン上にソフトウェアウォレットを作成するか、インポートしてください。 モバイルウォレットから始める その他の方法 + Tangemハードウェアウォレットを使用する + 詳細を見る・購入する 破棄 バックアップが中断されました。再開しますか? はい、再開します diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index cf16e56da7..acc3f54800 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -80,7 +80,7 @@ Вы отключили биометрическую аутентификацию на вашем телефоне и не сможете сохранять кошельки в приложении. Для сохранения кошельков, пожалуйста, включите функцию биометрической аутентификации в настройках телефона. При обработке промокода произошла ошибка. Пожалуйста, попробуйте позже. Ошибка активации - Ваш промокод успешно активирован. Бонус 10 USDT в Bitcoin будет зачислен через 14 дней. + Ваш промокод был успешно активирован. Награда будет зачислена на ваш счёт в течение 14 дней. Промокод активирован Этот промокод уже был использован и не может быть активирован повторно. Код недоступен @@ -132,7 +132,7 @@ Аналитика Применить Одобрение - Подтвердить + Разрешить Внимание Доступные сети Баланс: %s @@ -1202,7 +1202,7 @@ В сумму включена комиссия провайдера сервиса. \n\nПроскальзывание провайдера составляет до %s Информация Подтверждения считаются отраслевым стандартом для всех децентрализованных бирж и защищают ваш кошелек от доступа со стороны смарт-контракта без вашего разрешения. По замыслу смарт-контракты не могут получить доступ к вашим токенам, если вы не одобрите доступ со своей стороны. «Разблокируя» свои токены, вы даете смарт-контракту 1inch разрешение тратить ваши активы. Майнеры сети получают компенсацию за газ (оплачиваемый вами) за запись этого действия в блокчейне. Как только разрешение будет предоставлено, вы сможете обменять свой токен. - Подтвердить + Разрешение Ошибка расчета комиссии. Пожалуйста, отправьте информацию в поддержку. Вы отправляете Обмен этой суммы выбранных токенов может вызвать значительные колебания цены и уменьшить получаемую сумму. @@ -1288,6 +1288,8 @@ Ранний доступ к новым функциям и эксклюзивным предложениям. Новые функции и важные новости Хотите использовать Push-уведомления? + Включите push-уведомления, и мы мгновенно сообщим вам, когда поступят средства. + Не пропустите транзакцию Добавить новый кошелек Вы уверены, что хотите забыть этот кошелек? Произошла ошибка, пожалуйста, отсканируйте свою карту или кольцо для входа @@ -1559,6 +1561,7 @@ Нет, отправить все Уменьшить на %s XTZ Чтобы не платить повышенную комиссию при следующем пополнении кошелька, уменьшите сумму на %s XTZ + Выдать разрешение Невозможно загрузить график Полученная сумма, %1$s %2$s, не была зачислена на Aave. Сетевая комиссия сейчас слишком высокая. Ожидаем, пока она упадёт ниже вашего лимита. diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 6479a28a3f..368cbca8bf 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -135,7 +135,7 @@ You have disabled biometric authentication on your phone and will not be able to save wallets in the app. To save wallets, please enable the biometric authentication function in your phone settings. An error occurred while processing your promo code. Please try again later. Activation error - Your promo code was successfully activated. A bonus of 10 USDT in Bitcoin will be credited to your account within 14 days. + Your promo code was successfully activated. A reward will be credited to your account within 14 days. Promo Code Activated This promo code has already been used and cannot be activated again. Code unavailable @@ -297,6 +297,7 @@ %1$s — %2$s Read more Receive + Recommended Reject Reload Rename @@ -474,6 +475,7 @@ Provider Best rate FCA Warning List + Great rate Provider in FCA warning list Available up to %s Available from %s @@ -1117,8 +1119,8 @@ This will remove the wallet from the application. The wallet itself can be added again. Name Put your token to work - A network fee is a small payment to process and confirm your transaction on the blockchain. - To start staking, your TON account must be activated with a self-transaction of 1 TON. The funds stay in your wallet — this step only enables your account for staking. + A network fee is a small payment required to process and confirm your transaction on the blockchain. + To start staking, your TON account must be activated with a self-transaction of 1 TON. The funds stay in your wallet — this step only enables your account for staking. Account activation The amount to stake must be at least %s Staking amount will be rounded to %1$s TRX due to network rules. @@ -1386,9 +1388,10 @@ Early access to fresh features and exclusive offers. Feature and News Updates Would you like to use\nPush-notifications? - Enable push notifications and we’ll notify you instantly when funds arrive\n - Don’t Miss a Transaction + Enable push notifications to receive alerts when funds arrive in your wallet. + Don\'t Miss a Transaction Add new wallet + If you delete this wallet without a backup, you will permanently lose access to your funds Are you sure you want to forget this wallet? An error has occurred, please scan your card or ring to log in This wallet has already been saved, you can add another one @@ -1465,6 +1468,13 @@ Unlock Scan your card to unlock access Needed unlock + Choose how to add your wallet + Scan your Tangem card or ring to restore it or import from another wallet. + Create Hardware Wallet + Want to purchase a Tangem Wallet? + Import seed phrase + Restore your wallet on your phone or import from another app — convenient, but less secure than a Tangem card. + What to choose? Blockchain is unreachable. Try later Scan card or ring This wallet has already been activated earlier.\nIf it was not done by you, please contact support.\nTangem never sells wallets along with pre-generated access codes. @@ -1504,6 +1514,7 @@ Connect to dApps WalletConnect Connecting may take a few seconds + Create Tangem Wallet From %s Buy Tangem Wallet—a physical device that securely stores your private key offline. Hardware Wallet @@ -1731,13 +1742,20 @@ URI already used WalletConnect Suspicious transaction + Already have Tangem? + Thousands of assets Best in class hardware wallet Fast delivery + Start in one tap + Seamless and secure Simple to use Create a hardware wallet with Tangem. Slim as a bank card, secure as a bank vault. Create or import a software wallet + Create or import a software wallet on your phone Start with Mobile Wallet Other method + Use Tangem Hardware Wallet + Learn more & buy Discard You have an interrupted backup. Do you want to resume? Yes, resume @@ -1814,7 +1832,8 @@ Supply APR APY Make your money work — earn interest on your balance. - Earning on your balance + Interest accrues automatically + Aave lending Processing your deposit Earn %1$s%% per year Automatic diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt index eb2c314a09..eee1c4776a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt @@ -2,7 +2,6 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers.convert import com.tangem.common.ui.account.AccountCryptoPortfolioItemStateConverter import com.tangem.common.ui.tokens.TokenItemStateConverter -import com.tangem.core.ui.components.token.state.TokenItemState import com.tangem.core.ui.components.tokenlist.state.PortfolioTokensListItemUM import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM import com.tangem.core.ui.extensions.resourceReference @@ -14,9 +13,7 @@ import com.tangem.domain.models.TotalFiatBalance import com.tangem.domain.models.account.Account import com.tangem.domain.models.account.AccountId import com.tangem.domain.models.account.AccountStatus -import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus -import com.tangem.domain.models.currency.yieldSupplyKey import com.tangem.domain.models.tokenlist.TokenList import com.tangem.domain.models.tokenlist.TokenList.GroupedByNetwork.NetworkGroup import com.tangem.domain.models.wallet.UserWallet @@ -37,7 +34,7 @@ internal class TokenListStateConverter( private val params: TokenConverterParams, private val selectedWallet: UserWallet, private val clickIntents: WalletClickIntents, - private val apyMap: Map = emptyMap(), + private val apyMap: Map, ) : Converter { private val onTokenClick: (accountId: AccountId?, currencyStatus: CryptoCurrencyStatus) -> Unit = @@ -52,6 +49,7 @@ internal class TokenListStateConverter( private fun tokenStatusConverter(accountId: AccountId? = null) = TokenItemStateConverter( appCurrency = appCurrency, + apyMap = apyMap, onItemClick = { _, status -> onTokenClick(accountId, status) }, onItemLongClick = { _, status -> onTokenLongClick(accountId, status) }, ) @@ -168,52 +166,13 @@ internal class TokenListStateConverter( tokenConverter: TokenItemStateConverter, token: CryptoCurrencyStatus, ): List { - val tokenItemState = tokenConverter.convert(token).withEarnApyBadge(token) + val tokenItemState = tokenConverter.convert(token) add(TokensListItemUM.Token(tokenItemState)) return this } - private fun TokenItemState.withEarnApyBadge(cryptoCurrencyStatus: CryptoCurrencyStatus): TokenItemState { - val apy: String? = resolveEarnApy(cryptoCurrencyStatus) - - val shouldApply = apy != null && this.titleState is TokenItemState.TitleState.Content - - return if (!shouldApply) { - this - } else { - val contentTitle = this.titleState as TokenItemState.TitleState.Content - val newTitle = contentTitle.copy( - earnApy = resourceReference( - R.string.yield_module_earn_badge, - wrappedList(apy), - ), - ) - - when (this) { - is TokenItemState.Content -> this.copy(titleState = newTitle) - is TokenItemState.Unreachable -> this.copy(titleState = newTitle) - is TokenItemState.NoAddress -> this.copy(titleState = newTitle) - is TokenItemState.Loading -> this.copy(titleState = newTitle) - is TokenItemState.Draggable -> this.copy(titleState = newTitle) - is TokenItemState.Locked -> this - } - } - } - - private fun resolveEarnApy(cryptoCurrencyStatus: CryptoCurrencyStatus): String? { - if (apyMap.isEmpty()) return null - - val isYieldSupplyActive = (cryptoCurrencyStatus.value as? CryptoCurrencyStatus.Loaded) - ?.yieldSupplyStatus?.isActive == true - if (isYieldSupplyActive) return null - - val token = cryptoCurrencyStatus.currency as? CryptoCurrency.Token ?: return null - - return apyMap[token.yieldSupplyKey()] - } - private fun getOrganizeTokensButtonState(tokenList: TokenList): WalletOrganizeTokensButtonConfig? { val currenciesSize = when (tokenList) { TokenList.Empty -> return null diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/DefaultYieldSupplyComponent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/DefaultYieldSupplyComponent.kt index bfc0e7857d..abb21ee496 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/DefaultYieldSupplyComponent.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/DefaultYieldSupplyComponent.kt @@ -38,9 +38,8 @@ internal class DefaultYieldSupplyComponent @AssistedInject constructor( override fun Content(modifier: Modifier) { val yieldSupplyUM by model.uiState.collectAsStateWithLifecycle() val bottomSheet by bottomSheetSlot.subscribeAsState() - val isBalanceHidden by model.isBalanceHiddenFlow.collectAsStateWithLifecycle() - YieldSupplyBlockContent(yieldSupplyUM = yieldSupplyUM, isBalanceHidden = isBalanceHidden, modifier = modifier) + YieldSupplyBlockContent(yieldSupplyUM = yieldSupplyUM, modifier = modifier) bottomSheet.child?.instance?.BottomSheet() } diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/entity/YieldSupplyUM.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/entity/YieldSupplyUM.kt index 192ea9551a..30008334c4 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/entity/YieldSupplyUM.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/entity/YieldSupplyUM.kt @@ -18,7 +18,8 @@ internal sealed class YieldSupplyUM { data object Unavailable : YieldSupplyUM() data class Content( - val rewardsBalance: TextReference, + val title: TextReference, + val subtitle: TextReference, val rewardsApy: TextReference, val onClick: () -> Unit, val isAllowedToSpend: Boolean, diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/model/YieldSupplyModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/model/YieldSupplyModel.kt index 9b267af4e3..5b5f8676da 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/model/YieldSupplyModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/model/YieldSupplyModel.kt @@ -7,12 +7,15 @@ import com.tangem.common.routing.AppRouter import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer -import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.combinedReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringReference import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.network.TxInfo import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.models.yield.supply.YieldSupplyStatus import com.tangem.domain.tokens.FetchCurrencyStatusUseCase import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase @@ -20,6 +23,7 @@ import com.tangem.domain.yield.supply.usecase.YieldSupplyActivateUseCase import com.tangem.domain.yield.supply.usecase.YieldSupplyDeactivateUseCase import com.tangem.domain.yield.supply.usecase.YieldSupplyGetTokenStatusUseCase import com.tangem.domain.yield.supply.usecase.YieldSupplyIsAvailableUseCase +import com.tangem.features.yield.supply.impl.R import com.tangem.features.yield.supply.api.YieldSupplyComponent import com.tangem.features.yield.supply.impl.main.entity.YieldSupplyUM import com.tangem.features.yield.supply.impl.main.model.transformers.YieldSupplyTokenStatusSuccessTransformer @@ -72,6 +76,8 @@ internal class YieldSupplyModel @Inject constructor( val isBalanceHiddenFlow: StateFlow field = MutableStateFlow(false) + private var lastYieldSupplyStatus: YieldSupplyStatus? = null + init { checkIfYieldSupplyIsAvailable() } @@ -100,7 +106,7 @@ internal class YieldSupplyModel @Inject constructor( maybeCryptoCurrency.fold( ifRight = { cryptoCurrencyStatus -> cryptoCurrencyStatusFlow.update { cryptoCurrencyStatus } - onDataLoaded(cryptoCurrencyStatus) + onCryptoCurrencyStatusUpdated(cryptoCurrencyStatus) }, ifLeft = { Timber.w(it.toString()) @@ -128,6 +134,7 @@ internal class YieldSupplyModel @Inject constructor( ), ) }.onLeft { + Timber.e(it) uiState.update { YieldSupplyUM.Unavailable } } } @@ -157,50 +164,76 @@ internal class YieldSupplyModel @Inject constructor( .launchIn(modelScope) } - private fun onDataLoaded(cryptoCurrencyStatus: CryptoCurrencyStatus) { + @Suppress("MaximumLineLength") + private fun onCryptoCurrencyStatusUpdated(cryptoCurrencyStatus: CryptoCurrencyStatus) { val yieldSupplyStatus = cryptoCurrencyStatus.value.yieldSupplyStatus val hasActiveTransaction = cryptoCurrencyStatus.value.hasCurrentNetworkTransactions val yieldTransaction = cryptoCurrencyStatus.value.pendingTransactions.firstOrNull { it.type is TxInfo.TransactionType.YieldSupply }?.type as? TxInfo.TransactionType.YieldSupply - sendInfoAboutProtocolStatus(yieldSupplyStatus?.isActive == true) + sendInfoAboutProtocolStatus(cryptoCurrencyStatus) - val yieldSupplyUM = when { + when { hasActiveTransaction && yieldTransaction != null -> { coroutineScope.launch(dispatchers.io) { delay(PROCESSING_UPDATE_DELAY) fetchCurrencyStatusUseCase(userWalletId = userWallet.walletId, cryptoCurrency.id) } - when (yieldTransaction) { - TxInfo.TransactionType.YieldSupply.Enter -> YieldSupplyUM.Processing.Enter - TxInfo.TransactionType.YieldSupply.Exit -> YieldSupplyUM.Processing.Exit + uiState.update { + when (yieldTransaction) { + TxInfo.TransactionType.YieldSupply.Enter -> YieldSupplyUM.Processing.Enter + TxInfo.TransactionType.YieldSupply.Exit -> YieldSupplyUM.Processing.Exit + } + } + } + yieldSupplyStatus?.isActive == true -> { + val cryptoCurrencyToken = cryptoCurrency as? CryptoCurrency.Token ?: return + modelScope.launch(dispatchers.default) { + yieldSupplyGetTokenStatusUseCase(cryptoCurrencyToken) + .onRight { tokenStatus -> + uiState.update { + YieldSupplyUM.Content( + title = resourceReference( + R.string.yield_module_token_details_earn_notification_earning_on_your_balance_title, + ), + subtitle = resourceReference( + R.string.yield_module_token_details_earn_notification_earning_on_your_balance_subtitle, + ), + rewardsApy = combinedReference( + resourceReference( + R.string.yield_module_token_details_earn_notification_apy, + ), + stringReference(tokenStatus.apy.toString() + "%"), + ), + onClick = ::onActiveClick, + isAllowedToSpend = yieldSupplyStatus.isAllowedToSpend, + ) + } + }.onLeft { + Timber.e(it) + uiState.update { YieldSupplyUM.Loading } + } } } - yieldSupplyStatus?.isActive == true -> - YieldSupplyUM.Content( - rewardsBalance = TextReference.EMPTY, - rewardsApy = TextReference.EMPTY, - onClick = ::onActiveClick, - isAllowedToSpend = yieldSupplyStatus.isAllowedToSpend, - ) - else -> YieldSupplyUM.Initial - } - uiState.update { yieldSupplyUM } - - when (yieldSupplyUM) { - is YieldSupplyUM.Initial -> loadTokenStatus() - else -> Unit + else -> { + loadTokenStatus() + } } } - private fun sendInfoAboutProtocolStatus(isActivated: Boolean) { + private fun sendInfoAboutProtocolStatus(cryptoCurrencyStatus: CryptoCurrencyStatus) { + if (lastYieldSupplyStatus == cryptoCurrencyStatus.value.yieldSupplyStatus) return val token = cryptoCurrency as? CryptoCurrency.Token ?: return modelScope.launch(dispatchers.default) { - if (isActivated) { - yieldSupplyActivateUseCase(token) + if (cryptoCurrencyStatus.value.yieldSupplyStatus?.isActive == true) { + yieldSupplyActivateUseCase(token).onRight { + lastYieldSupplyStatus = cryptoCurrencyStatus.value.yieldSupplyStatus + } } else { - yieldSupplyDeactivateUseCase(token) + yieldSupplyDeactivateUseCase(token).onRight { + lastYieldSupplyStatus = cryptoCurrencyStatus.value.yieldSupplyStatus + } } } } diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/ui/YieldSupplyBlockContent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/ui/YieldSupplyBlockContent.kt index e044b1c598..b596d93729 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/ui/YieldSupplyBlockContent.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/main/ui/YieldSupplyBlockContent.kt @@ -3,6 +3,7 @@ package com.tangem.features.yield.supply.impl.main.ui import android.content.res.Configuration import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -17,12 +18,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.dp import com.tangem.core.ui.components.SecondaryButton +import com.tangem.core.ui.components.SpacerW12 import com.tangem.core.ui.components.SpacerW8 import com.tangem.core.ui.components.TextShimmer import com.tangem.core.ui.components.buttons.common.TangemButtonSize @@ -34,11 +37,7 @@ import com.tangem.features.yield.supply.impl.main.entity.YieldSupplyUM import com.tangem.utils.StringsSigns @Composable -internal fun YieldSupplyBlockContent( - yieldSupplyUM: YieldSupplyUM, - isBalanceHidden: Boolean, - modifier: Modifier = Modifier, -) { +internal fun YieldSupplyBlockContent(yieldSupplyUM: YieldSupplyUM, modifier: Modifier = Modifier) { AnimatedContent( targetState = yieldSupplyUM, modifier = modifier, @@ -46,7 +45,7 @@ internal fun YieldSupplyBlockContent( when (supplyUM) { is YieldSupplyUM.Available -> SupplyAvailable(supplyUM) YieldSupplyUM.Loading -> SupplyLoading() - is YieldSupplyUM.Content -> SupplyContent(supplyUM, isBalanceHidden) + is YieldSupplyUM.Content -> SupplyContent(supplyUM) YieldSupplyUM.Processing.Enter -> SupplyProcessing( resourceReference(R.string.yield_module_token_details_earn_notification_processing), ) @@ -87,7 +86,7 @@ private fun SupplyUnavailable() { } @Composable -private fun SupplyContent(supplyUM: YieldSupplyUM.Content, isBalanceHidden: Boolean) { +private fun SupplyContent(supplyUM: YieldSupplyUM.Content) { Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier @@ -96,22 +95,21 @@ private fun SupplyContent(supplyUM: YieldSupplyUM.Content, isBalanceHidden: Bool .clickable(onClick = supplyUM.onClick) .padding(12.dp), ) { + Image( + painter = painterResource(R.drawable.img_aave_22), + modifier = Modifier.size(36.dp), + contentDescription = null, + ) + SpacerW12() Column( verticalArrangement = Arrangement.spacedBy(4.dp), modifier = Modifier.weight(1f), ) { - Text( - text = stringResourceSafe( - R.string.yield_module_token_details_earn_notification_earning_on_your_balance_title, - ), - style = TangemTheme.typography.subtitle2, - color = TangemTheme.colors.text.tertiary, - ) Row( horizontalArrangement = Arrangement.spacedBy(4.dp), ) { Text( - text = supplyUM.rewardsBalance.orMaskWithStars(isBalanceHidden).resolveReference(), + text = supplyUM.title.resolveReference(), style = TangemTheme.typography.subtitle1, color = TangemTheme.colors.text.primary1, ) @@ -123,9 +121,15 @@ private fun SupplyContent(supplyUM: YieldSupplyUM.Content, isBalanceHidden: Bool Text( text = supplyUM.rewardsApy.resolveReference(), style = TangemTheme.typography.subtitle1, - color = TangemTheme.colors.text.tertiary, + maxLines = 1, + color = TangemTheme.colors.text.accent, ) } + Text( + text = supplyUM.subtitle.resolveReference(), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.primary1, + ) } SpacerW8() AnimatedVisibility(supplyUM.isAllowedToSpend.not()) { @@ -146,36 +150,42 @@ private fun SupplyContent(supplyUM: YieldSupplyUM.Content, isBalanceHidden: Bool @Composable private fun SupplyProcessing(text: TextReference) { - Column( - verticalArrangement = Arrangement.spacedBy(4.dp), + Row( + verticalAlignment = Alignment.CenterVertically, modifier = Modifier - .fillMaxWidth() .clip(RoundedCornerShape(16.dp)) .background(TangemTheme.colors.background.primary) .padding(12.dp), ) { - Text( - text = stringResourceSafe( - R.string.yield_module_token_details_earn_notification_earning_on_your_balance_title, - ), - style = TangemTheme.typography.subtitle2, - color = TangemTheme.colors.text.tertiary, + Image( + painter = painterResource(R.drawable.img_aave_22), + modifier = Modifier.size(36.dp), + contentDescription = null, ) - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(4.dp), + SpacerW12() + Column( + verticalArrangement = Arrangement.spacedBy(4.dp), + modifier = Modifier.weight(1f), ) { Text( - text = text.resolveReference(), - style = TangemTheme.typography.body1, + text = stringResourceSafe( + R.string.yield_module_token_details_earn_notification_earning_on_your_balance_title, + ), + style = TangemTheme.typography.subtitle2, color = TangemTheme.colors.text.tertiary, ) - CircularProgressIndicator( - modifier = Modifier.size(16.dp), - color = TangemTheme.colors.icon.accent, - strokeWidth = 2.dp, + Text( + text = text.resolveReference(), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.primary1, ) } + SpacerW8() + CircularProgressIndicator( + modifier = Modifier.size(20.dp), + color = TangemTheme.colors.icon.accent, + strokeWidth = 2.dp, + ) } } @@ -286,7 +296,7 @@ private fun SupplyInfo( @Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) private fun YieldSupplyBlockContent_Preview(@PreviewParameter(PreviewProvider::class) params: YieldSupplyUM) { TangemThemePreview { - YieldSupplyBlockContent(params, true) + YieldSupplyBlockContent(yieldSupplyUM = params, modifier = Modifier) } } @@ -301,13 +311,15 @@ private class PreviewProvider : PreviewParameterProvider { onClick = {}, ), YieldSupplyUM.Content( - rewardsBalance = stringReference("1 USDT"), + title = stringReference("Aave lending is active"), + subtitle = stringReference("Interest accrues automatically"), rewardsApy = stringReference("5.1 % APY"), onClick = {}, isAllowedToSpend = false, ), YieldSupplyUM.Content( - rewardsBalance = stringReference("1 USDT"), + title = stringReference("Aave lending is active"), + subtitle = stringReference("Interest accrues automatically"), rewardsApy = stringReference("5.1 % APY"), onClick = {}, isAllowedToSpend = true, diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/ui/YieldSupplyActiveContent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/ui/YieldSupplyActiveContent.kt index 383a4b2231..eab1197a0e 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/ui/YieldSupplyActiveContent.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/active/ui/YieldSupplyActiveContent.kt @@ -56,17 +56,6 @@ internal fun YieldSupplyActiveContent( .fillMaxWidth() .padding(12.dp), ) { - Text( - text = stringResourceSafe(R.string.yield_module_earn_sheet_total_earnings_title), - style = TangemTheme.typography.subtitle2, - color = TangemTheme.colors.text.tertiary, - ) - ResizableText( - text = state.totalEarnings.orMaskWithStars(isBalanceHidden).resolveReference(), - style = TangemTheme.typography.h2, - color = TangemTheme.colors.text.primary1, - ) - CurrentApy(state.apy) chartComponent.Content(Modifier.padding(bottom = 12.dp)) } @@ -85,22 +74,23 @@ internal fun YieldSupplyActiveContent( @Composable private fun CurrentApy(apy: TextReference?, modifier: Modifier = Modifier) { - Row(modifier = modifier.padding(vertical = 12.dp), verticalAlignment = Alignment.CenterVertically) { + Column(modifier = modifier.padding(vertical = 12.dp)) { Text( - modifier = modifier.weight(1.0f), + modifier = Modifier, text = stringResourceSafe(R.string.yield_module_earn_sheet_current_apy_title), - style = TangemTheme.typography.body1, + style = TangemTheme.typography.subtitle2, color = TangemTheme.colors.text.tertiary, ) AnimatedContent( + modifier = Modifier.height(32.dp), targetState = apy?.resolveReference(), label = "CurrentApy", ) { apyText -> if (apyText == null) { TextShimmer( - modifier = modifier.width(56.dp), + modifier = modifier.width(94.dp), text = "", - style = TangemTheme.typography.body1, + style = TangemTheme.typography.head, ) } else { Row(verticalAlignment = Alignment.CenterVertically) { @@ -108,7 +98,7 @@ private fun CurrentApy(apy: TextReference?, modifier: Modifier = Modifier) { painterResource(R.drawable.ic_arrow_up_8), tint = TangemTheme.colors.text.accent, contentDescription = null, - modifier = Modifier.padding(end = 8.dp), + modifier = Modifier.padding(end = 6.dp).size(12.dp), ) Text( modifier = modifier, @@ -260,6 +250,7 @@ private class YieldSupplyActiveBottomSheetPreviewProvider : PreviewParameterProv ), subtitleLink = resourceReference(R.string.common_read_more), notificationUM = NotificationUM.Error.InvalidAmount, + apy = stringReference("5,14%"), ), ) }