diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/MainScreenAnalyticsEvent.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/MainScreenAnalyticsEvent.kt index 02c1a6d4c0..5d8c3a6887 100644 --- a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/MainScreenAnalyticsEvent.kt +++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/MainScreenAnalyticsEvent.kt @@ -2,6 +2,7 @@ package com.tangem.core.analytics.models.event import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.AnalyticsParam.Key.ERROR_CODE import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM /** @@ -71,5 +72,15 @@ sealed class MainScreenAnalyticsEvent( event = "Button - Close", params = mapOf(AnalyticsParam.SOURCE to source.value), ) + + data class HotTokenClicked(val currencySymbol: String) : MainScreenAnalyticsEvent( + event = "Hot Token Clicked", + params = mapOf(TOKEN_PARAM to currencySymbol), + ) + + data class HotTokenError(val errorCode: String) : MainScreenAnalyticsEvent( + event = "Hot Token Error", + params = mapOf(ERROR_CODE to errorCode), + ) // endregion } \ No newline at end of file diff --git a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json index 463e2e5f2d..dab1120565 100644 --- a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json @@ -37,7 +37,7 @@ }, { "name": "ONRAMP_HOT_TOKENS_ENABLED", - "version": "undefined" + "version": "5.21.0" }, { "name": "STAKING_TON_ENABLED", diff --git a/core/datasource/build.gradle.kts b/core/datasource/build.gradle.kts index 128cbb0ab2..8823ad1d94 100644 --- a/core/datasource/build.gradle.kts +++ b/core/datasource/build.gradle.kts @@ -20,6 +20,7 @@ android { dependencies { /** Project */ + implementation(projects.core.analytics) implementation(projects.core.utils) implementation(projects.libs.auth) implementation(projects.domain.appTheme.models) diff --git a/core/datasource/src/main/java/com/tangem/datasource/exchangeservice/hotcrypto/DefaultHotCryptoLoader.kt b/core/datasource/src/main/java/com/tangem/datasource/exchangeservice/hotcrypto/DefaultHotCryptoLoader.kt index 6ca3229ae4..1f0f77224b 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/exchangeservice/hotcrypto/DefaultHotCryptoLoader.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/exchangeservice/hotcrypto/DefaultHotCryptoLoader.kt @@ -1,5 +1,8 @@ package com.tangem.datasource.exchangeservice.hotcrypto +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent +import com.tangem.datasource.api.common.response.ApiResponseError import com.tangem.datasource.api.common.response.getOrThrow import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse @@ -32,6 +35,7 @@ internal class DefaultHotCryptoLoader @Inject constructor( private val appPreferencesStore: AppPreferencesStore, private val dispatchers: CoroutineDispatcherProvider, private val onrampFeatureToggles: OnrampFeatureToggles, + private val analyticsEventHandler: AnalyticsEventHandler, ) : HotCryptoLoader { override suspend fun fetch(tokens: List) { @@ -68,6 +72,12 @@ internal class DefaultHotCryptoLoader @Inject constructor( } .onFailure { Timber.e(it, "Unable to fetch hot crypto") + + analyticsEventHandler.send( + event = MainScreenAnalyticsEvent.HotTokenError( + errorCode = (it as? ApiResponseError.HttpException)?.code?.code?.toString().orEmpty(), + ), + ) } } diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index 7828708c51..2ae77c48bf 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -142,7 +142,7 @@ Adressen synchronisieren Zum Anbieter gehen Zum Token - Ausblenden + Ausblenden stunde Importieren In Arbeit @@ -316,8 +316,8 @@ Kauf %s Kauf %s… Diese Transaktion ausblenden - Sobald der Transaktionsstatus ausgeblendet ist, kann er nicht mehr angezeigt werden. Stattdessen kannst Du ihn durch einfaches Wischen schließen. - Diese Transaktion ausblenden + Sobald der Transaktionsstatus ausgeblendet ist, kann er nicht mehr angezeigt werden. Stattdessen kannst Du ihn durch einfaches Wischen schließen. + Diese Transaktion ausblenden Keine Token gefunden. Bitte versuche eine andere Anfrage ID: %s Transaktions-ID kopiert diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index 818393b791..2a6d80caf7 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -87,8 +87,8 @@ ADA insuffisant Accepter Accès refusé - Ajouter au portfolio - Ajouter un jeton + Ajouter au portfolio + Ajouter un jeton Tous Permettre Analytique @@ -142,7 +142,7 @@ Synchroniser les adresses Aller au fournisseur Aller au jeton - Cacher + Cacher heure Importez En cours @@ -315,9 +315,9 @@ Acheté %s Achat en cours %s Achat en cours %s... - Masquer cette transaction - Une fois masqué, le statut de la transaction ne peut plus être consulté. Vous pouvez simplement faire glisser votre doigt pour le fermer. - Masquer le statut de la transaction ? + Masquer cette transaction + Une fois masqué, le statut de la transaction ne peut plus être consulté. Vous pouvez simplement faire glisser votre doigt pour le fermer. + Masquer le statut de la transaction ? Aucun jeton trouvé. Veuillez essayer une autre demande ID : %s ID de transaction copié @@ -344,8 +344,8 @@ Illimité Commandez Scannez - à %s - Via %s + à %s + Via %s Ces informations ont été générées avec l\'IA.\nAppuyez ici si vous trouvez des erreurs. Touchez, pour modifier le code d\'accès Touchez, pour modifier le mot de passe @@ -794,8 +794,8 @@ Migrer Native staking Aucun validateur actif n\'est disponible pour le staking pour le moment. Veuillez réessayer plus tard. - Lors du staking sur le réseau Cardano, l\'intégralité du solde est utilisée. 2 ADA supplémentaires seront réservés et restitués après le dé-staking. Votre ADA n\'est pas verrouillé pendant le staking. - Détails du ADA staking + Lors du staking sur le réseau Cardano, l\'intégralité du solde est utilisée. 2 ADA supplémentaires seront réservés et restitués après le dé-staking. Votre ADA n\'est pas verrouillé pendant le staking. + Détails du ADA staking Récomp. gagnées vous seront envoyées et dispo pour utilisation immédiate Stakez et commencez à gagner vos récompenses Stakez en toute sécurité et commencez à gagner des récompenses quotidiennes @@ -809,8 +809,8 @@ Gagnez des récompenses de staking Votre solde stakée sera trop faible pour annuler votre staking. Vous devrez staker davantage pour atteindre le montant minimum pour annuler le staking. Solde de staking faible - Un minimum de %1$s %2$s est requis pour le re-staking. Veuillez recharger votre solde. - Pas assez de %s + Un minimum de %1$s %2$s est requis pour le re-staking. Veuillez recharger votre solde. + Pas assez de %s L\'option de staking n\'est actuellement pas disponible en raison des conditions du réseau. Veuillez réessayer plus tard. Le staking dans le réseau %1$s avec un nouveau validateur transférera automatiquement tous les fonds précédemment stakés vers ce validateur Réinvestissez vos récompenses gagnées dans le montant que vous avez staké, augmentant ainsi vos gains potentiels. @@ -1057,7 +1057,7 @@ Êtes-vous sûr(e) de vouloir annuler la transaction ? Vous ne pourrez plus réessayer. Votre transaction d\'un montant de %1$s %2$s n\'a pas été finalisée. Vous pouvez réessayer plus tard pour la finaliser. Vous avez une transaction inachevée - La dernière mise à jour a été %s + La dernière mise à jour a été %s Il ne reste que %s signatures sur cette carte. Vous devez retirer tous vos fonds. Faible nombre de signatures Les jetons sur différents réseaux peuvent avoir des adresses différentes. Vérifiez bien que votre adresse correspond au réseau lorsque vous transférez des fonds. @@ -1090,7 +1090,7 @@ Le réseau Solana facture un loyer de %1$s tous les 2 jours. Les comptes qui ne peuvent pas se permettre le loyer sont purgés du réseau. Déposez sur votre compte plus de %2$s pour l\'utiliser gratuitement. Certains réseaux sont actuellement inaccessibles. Veuillez réessayer plus tard. Certains réseaux sont inaccessibles - Certains soldes de jetons n\'ont pas pu être mis à jour + Certains soldes de jetons n\'ont pas pu être mis à jour Il s\'agit d\'une carte Testnet. Elle ne peut pas traiter les transactions et ne doit être utilisée qu\'à des fins de test et de développement. À des fins de test uniquement Ignorer diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml index 5f33cc9489..4c8d28caee 100644 --- a/core/res/src/main/res/values-ja/strings.xml +++ b/core/res/src/main/res/values-ja/strings.xml @@ -139,7 +139,7 @@ アドレスを同期する プロバイダーへ移動 トークンへ移動 - 非表示 + 非表示 時間 インポート 進行中 @@ -312,9 +312,9 @@ %sを買い付けました %sを買い付けています %sを買い付けています... - この取引を非表示にする - 一度非表示にすると、取引状況を再度表示することはできません。代わりに、スワイプして閉じることができます。 - 取引状況を非表示にしますか? + この取引を非表示にする + 一度非表示にすると、取引状況を再度表示することはできません。代わりに、スワイプして閉じることができます。 + 取引状況を非表示にしますか? トークンが見つかりません。別のリクエストをお試しください。 ID: %s 取引IDをコピーしました @@ -778,8 +778,8 @@ ステーキングから資金の引き出しを要求した後、トークンが利用可能になるまでの待機期間。 ウォームアップ期間 ステーキングへの参加を有効にするために割り当てられた時間。 - 現在、利用可能なバリデーターは見つかりません。しばらくしてからもう一度お試しください。 - ステーキングは利用できません + 現在、利用可能なバリデーターは見つかりません。しばらくしてからもう一度お試しください。 + ステーキングは利用できません ネットワークは、ステーキングのためにトークンの使用を承認していることを確認するために、トークン承認手数料を請求します。 ステーキング機能を使用すると、プロバイダーの%1$sと%2$sに同意したことになります ロック中 @@ -801,8 +801,8 @@ ステーキング報酬を獲得 残りのステーキング残高が少なすぎてステーキングを解除できません。最小のステーキング解除残高を満たすには、さらにステーキングする必要があります。 ステーキング残高が低いです - 再ステーキングには最低%1$s %2$sが必要です。残高を補充してください。 - %sが足りません + 再ステーキングには最低%1$s %2$sが必要です。残高を補充してください。 + %sが足りません ネットワークの状態により、現在ステーキングはご利用いただけません。しばらくしてからもう一度お試しください。 新しいバリデーターで%1$sネットワークにステーキングすると、以前にステーキングされた資金はすべてこのバリデーターに自動的に転送されます。 獲得した報酬をステーキングに再投資し、潜在的な収益を増やします。 diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 137cd069ba..aa1d5e2109 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -345,7 +345,7 @@ Order Tangem Scan Tangem to %s - In %s network + On %s network This information was generated with AI.\nTap here, if you find any errors. To change the access code tap the card or ring as shown above and do not remove until the end of the operation To change the passcode tap the card as shown above and do not remove until the end of the operation diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusSyncUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusSyncUseCase.kt index 9c87e00297..e69c36858f 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusSyncUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyStatusSyncUseCase.kt @@ -21,6 +21,7 @@ class GetCryptoCurrencyStatusSyncUseCase( internal val dispatchers: CoroutineDispatcherProvider, ) { + // multi-currency suspend operator fun invoke( userWalletId: UserWalletId, cryptoCurrencyId: CryptoCurrency.ID, @@ -38,6 +39,7 @@ class GetCryptoCurrencyStatusSyncUseCase( .mapLeft { error -> error.mapToCurrencyError() } } + // single-currency suspend operator fun invoke(userWalletId: UserWalletId): Either { val operations = CurrenciesStatusesOperations( userWalletId = userWalletId, diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/entity/OnrampAddToPortfolioUM.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/entity/OnrampAddToPortfolioUM.kt index d7e568d894..3e61a19e9a 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/entity/OnrampAddToPortfolioUM.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/entity/OnrampAddToPortfolioUM.kt @@ -13,7 +13,7 @@ import com.tangem.features.onramp.impl.R * @property currencyName currency name * @property networkName network name * @property currencyIconState currency icon state - * @property onAddClick lambda be invoked when add button is clicked + * @property addButtonUM add button UM * [REDACTED_AUTHOR] */ @@ -22,7 +22,7 @@ data class OnrampAddToPortfolioUM( val currencyName: String, val networkName: String, val currencyIconState: CurrencyIconState, - val onAddClick: () -> Unit, + val addButtonUM: AddButtonUM, ) { val bsTitle: TextReference = resourceReference(id = R.string.common_add_token) @@ -37,5 +37,8 @@ data class OnrampAddToPortfolioUM( formatArgs = wrappedList(networkName), ) - val addButtonText: TextReference = resourceReference(R.string.common_add_to_portfolio) + data class AddButtonUM(val isProgress: Boolean, val onClick: () -> Unit) { + + val text: TextReference = resourceReference(R.string.common_add_to_portfolio) + } } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/model/OnrampAddToPortfolioModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/model/OnrampAddToPortfolioModel.kt index 438b2a1b9d..9ca6e59406 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/model/OnrampAddToPortfolioModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/model/OnrampAddToPortfolioModel.kt @@ -13,6 +13,7 @@ import com.tangem.features.onramp.hottokens.portfolio.entity.OnrampAddToPortfoli import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -48,7 +49,7 @@ internal class OnrampAddToPortfolioModel @Inject constructor( currencyName = params.cryptoCurrency.name, networkName = params.cryptoCurrency.network.name, currencyIconState = params.currencyIconState, - onAddClick = ::onAddClick, + addButtonUM = OnrampAddToPortfolioUM.AddButtonUM(isProgress = false, onClick = ::onAddClick), ) } @@ -60,8 +61,12 @@ internal class OnrampAddToPortfolioModel @Inject constructor( private fun onAddClick() { modelScope.launch { + changeAddButtonProgressStatus(isProgress = true) + derivePublicKeysUseCase(params.userWalletId, listOfNotNull(params.cryptoCurrency)).getOrElse { Timber.e("Failed to derive public keys: $it") + + changeAddButtonProgressStatus(isProgress = false) } addCryptoCurrenciesUseCase( @@ -69,6 +74,13 @@ internal class OnrampAddToPortfolioModel @Inject constructor( currencies = listOfNotNull(params.cryptoCurrency), ) .onRight { params.onSuccessAdding(params.cryptoCurrency.id) } + .onLeft { changeAddButtonProgressStatus(isProgress = false) } + } + } + + private fun changeAddButtonProgressStatus(isProgress: Boolean) { + _state.update { + it.copy(addButtonUM = it.addButtonUM.copy(isProgress = isProgress)) } } } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/ui/OnrampAddToPortfolioContent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/ui/OnrampAddToPortfolioContent.kt index b3069190f9..4666c4e5b7 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/ui/OnrampAddToPortfolioContent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/ui/OnrampAddToPortfolioContent.kt @@ -66,8 +66,7 @@ fun OnrampAddToPortfolioContent(state: OnrampAddToPortfolioUM) { SpacerH24() AddToPortfolioButton( - text = state.addButtonText, - onClick = state.onAddClick, + state = state.addButtonUM, modifier = Modifier.padding(horizontal = 16.dp), ) @@ -109,13 +108,13 @@ private fun CurrencyNetworkName(text: TextReference, modifier: Modifier = Modifi } @Composable -private fun AddToPortfolioButton(text: TextReference, onClick: () -> Unit, modifier: Modifier = Modifier) { +private fun AddToPortfolioButton(state: OnrampAddToPortfolioUM.AddButtonUM, modifier: Modifier = Modifier) { TangemButton( - text = text.resolveReference(), + text = state.text.resolveReference(), icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_tangem_24), - onClick = onClick, + onClick = state.onClick, colors = TangemButtonsDefaults.primaryButtonColors, - showProgress = false, + showProgress = state.isProgress, enabled = true, modifier = modifier.fillMaxWidth(), size = TangemButtonSize.WideAction, @@ -150,7 +149,7 @@ private fun PreviewOnrampAddToPortfolioContent() { isGrayscale = false, showCustomBadge = false, ), - onAddClick = {}, + addButtonUM = OnrampAddToPortfolioUM.AddButtonUM(isProgress = false, onClick = {}), ), ) }, diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/DefaultOnrampOperationComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/DefaultOnrampOperationComponent.kt index 3b5e14e0dc..9888b7dddb 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/DefaultOnrampOperationComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/DefaultOnrampOperationComponent.kt @@ -59,7 +59,7 @@ internal class DefaultOnrampOperationComponent @AssistedInject constructor( context = child(key = "hot_crypto"), params = HotCryptoComponent.Params( userWalletId = params.userWalletId, - onTokenClick = model::onTokenClick, + onTokenClick = model::onHotTokenClick, ), ) } else { diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/model/OnrampOperationModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/model/OnrampOperationModel.kt index f9e8289476..2f8902ef66 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/model/OnrampOperationModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selecttoken/model/OnrampOperationModel.kt @@ -72,6 +72,18 @@ internal class OnrampOperationModel @Inject constructor( }, ) + selectTokenIfDemoModeOff(status) + } + + fun onHotTokenClick(status: CryptoCurrencyStatus) { + analyticsEventHandler.send( + event = MainScreenAnalyticsEvent.HotTokenClicked(currencySymbol = status.currency.symbol), + ) + + selectTokenIfDemoModeOff(status) + } + + private fun selectTokenIfDemoModeOff(status: CryptoCurrencyStatus) { if (params is Params.Sell || !onrampFeatureToggles.isFeatureEnabled) { showErrorIfDemoModeOrElse { selectToken(status) } } else {