From 46eee92ceaf2cbd1cc1d911e4de609bd9f6fd604 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 3 Dec 2024 13:45:26 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../middlewares/TradeCryptoMiddleware.kt | 15 ++- .../tangem/tap/routing/utils/ChildFactory.kt | 6 +- common/routing/build.gradle.kts | 1 + .../com/tangem/common/routing/AppRoute.kt | 2 + domain/onramp/build.gradle.kts | 3 + .../domain/onramp/model/OnrampSource.kt | 9 ++ .../onramp/analytics/OnrampAnalyticsEvent.kt | 104 ++++++++++++++++++ domain/tokens/build.gradle.kts | 1 + .../domain/tokens/legacy/TradeCryptoAction.kt | 2 + features/markets/impl/build.gradle.kts | 1 + .../impl/model/TokenActionsHandler.kt | 2 + features/onramp/api/build.gradle.kts | 1 + .../onramp/component/OnrampComponent.kt | 3 +- .../model/ConfirmResidencyModel.kt | 11 +- .../onramp/main/DefaultOnrampMainComponent.kt | 3 +- .../onramp/main/OnrampMainComponent.kt | 2 + .../main/model/OnrampMainComponentModel.kt | 22 ++++ .../providers/SelectProviderComponent.kt | 2 + .../providers/model/SelectProviderModel.kt | 13 +++ .../onramp/root/DefaultOnrampComponent.kt | 1 + .../model/OnrampSelectCountryModel.kt | 6 + .../model/OnrampSelectCurrencyModel.kt | 4 + .../selecttoken/model/OnrampOperationModel.kt | 2 + .../settings/model/OnrampSettingsModel.kt | 4 + .../viewmodels/TokenDetailsViewModel.kt | 2 + features/wallet/impl/build.gradle.kts | 1 + .../WalletCurrencyActionsClickIntents.kt | 2 + 27 files changed, 218 insertions(+), 7 deletions(-) create mode 100644 domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampSource.kt create mode 100644 domain/onramp/src/main/java/com/tangem/domain/onramp/analytics/OnrampAnalyticsEvent.kt diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index be04d0a426..7c99d07e6e 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -5,6 +5,7 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter import com.tangem.core.analytics.Analytics +import com.tangem.domain.onramp.model.OnrampSource import com.tangem.domain.settings.usercountry.models.UserCountry import com.tangem.domain.tokens.legacy.TradeCryptoAction import com.tangem.domain.tokens.model.CryptoCurrency @@ -56,14 +57,22 @@ object TradeCryptoMiddleware { private fun proceedBuyAction(state: () -> AppState?, action: TradeCryptoAction.Buy) { val isOnrampEnabled = store.inject(DaggerGraphState::onrampFeatureToggles).isFeatureEnabled if (isOnrampEnabled) { - proceedWithOnramp(action.userWallet.walletId, action.cryptoCurrencyStatus.currency) + proceedWithOnramp(action.userWallet.walletId, action.cryptoCurrencyStatus.currency, action.source) } else { proceedWithLegacyBuyAction(state, action) } } - private fun proceedWithOnramp(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency) { - store.dispatchNavigationAction { push(AppRoute.Onramp(userWalletId = userWalletId, currency = cryptoCurrency)) } + private fun proceedWithOnramp(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, source: OnrampSource) { + store.dispatchNavigationAction { + push( + AppRoute.Onramp( + userWalletId = userWalletId, + currency = cryptoCurrency, + source = source, + ), + ) + } } private fun proceedWithLegacyBuyAction(state: () -> AppState?, action: TradeCryptoAction.Buy) { diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index 356dbcbe9e..6941455b7d 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -197,7 +197,11 @@ internal class ChildFactory @Inject constructor( is AppRoute.Onramp -> { route.asComponentChild( contextProvider = contextProvider(route, contextFactory), - params = OnrampComponent.Params(userWalletId = route.userWalletId, cryptoCurrency = route.currency), + params = OnrampComponent.Params( + userWalletId = route.userWalletId, + cryptoCurrency = route.currency, + source = route.source, + ), componentFactory = onrampComponentFactory, ) } diff --git a/common/routing/build.gradle.kts b/common/routing/build.gradle.kts index 71933a08b2..4fd88cfb46 100644 --- a/common/routing/build.gradle.kts +++ b/common/routing/build.gradle.kts @@ -20,6 +20,7 @@ dependencies { implementation(projects.domain.wallets.models) implementation(projects.domain.staking.models) implementation(projects.domain.markets.models) + implementation(projects.domain.onramp.models) implementation(projects.domain.appCurrency.models) /* Libs - Other */ diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index 94e2a9f2ae..f6f5afe476 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -8,6 +8,7 @@ import com.tangem.core.decompose.navigation.Route import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.markets.TokenMarketParams import com.tangem.domain.models.scan.ScanResponse +import com.tangem.domain.onramp.model.OnrampSource import com.tangem.domain.qrscanning.models.SourceType import com.tangem.domain.staking.model.stakekit.Yield import com.tangem.domain.tokens.model.CryptoCurrency @@ -296,6 +297,7 @@ sealed class AppRoute(val path: String) : Route { @Serializable data class Onramp( + val source: OnrampSource, val userWalletId: UserWalletId, val currency: CryptoCurrency, ) : AppRoute(path = "/onramp/${userWalletId.stringValue}/${currency.symbol}"), RouteBundleParams { diff --git a/domain/onramp/build.gradle.kts b/domain/onramp/build.gradle.kts index b676e92120..98797db862 100644 --- a/domain/onramp/build.gradle.kts +++ b/domain/onramp/build.gradle.kts @@ -5,6 +5,9 @@ plugins { } dependencies { + /** Core modules */ + implementation(projects.core.analytics.models) + api(projects.domain.onramp.models) api(projects.domain.tokens.models) api(projects.domain.wallets.models) diff --git a/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampSource.kt b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampSource.kt new file mode 100644 index 0000000000..86b72907ad --- /dev/null +++ b/domain/onramp/models/src/main/kotlin/com/tangem/domain/onramp/model/OnrampSource.kt @@ -0,0 +1,9 @@ +package com.tangem.domain.onramp.model + +// TODO: Move to features/onramp/api package after removing old buy logic. [REDACTED_JIRA] +enum class OnrampSource(val analyticsName: String) { + ACTION_BUTTONS("Main"), + TOKEN_LONG_TAP("Long Tap"), + TOKEN_DETAILS("Token"), + MARKETS("Markets"), +} \ No newline at end of file diff --git a/domain/onramp/src/main/java/com/tangem/domain/onramp/analytics/OnrampAnalyticsEvent.kt b/domain/onramp/src/main/java/com/tangem/domain/onramp/analytics/OnrampAnalyticsEvent.kt new file mode 100644 index 0000000000..fe3ac20287 --- /dev/null +++ b/domain/onramp/src/main/java/com/tangem/domain/onramp/analytics/OnrampAnalyticsEvent.kt @@ -0,0 +1,104 @@ +package com.tangem.domain.onramp.analytics + +import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.domain.onramp.model.OnrampSource + +sealed class OnrampAnalyticsEvent( + event: String, + params: Map = mapOf(), +) : AnalyticsEvent( + category = "Onramp", + event = event, + params = params, +) { + + data class ScreenOpened( + val source: OnrampSource, + val cryptoCurrency: String, + ) : OnrampAnalyticsEvent( + event = "Buy Screen Opened", + params = mapOf( + "Source" to source.analyticsName, + "Token" to cryptoCurrency, + ), + ) + + data object SelectCurrencyScreenOpened : OnrampAnalyticsEvent(event = "Currency Screen Opened") + + data class FiatCurrencyChosen( + private val currency: String, + ) : OnrampAnalyticsEvent( + event = "Currency Chosen", + params = mapOf("Currency" to currency), + ) + + data object CloseOnramp : OnrampAnalyticsEvent(event = "Button - Close") + + data object SettingsOpened : OnrampAnalyticsEvent(event = "Onramp Settings Screen Opened") + + data object SelectResidenceOpened : OnrampAnalyticsEvent(event = "Residence Screen Opened") + + data class OnResidenceChosen( + private val residence: String, + ) : OnrampAnalyticsEvent( + event = "Residence Chosen", + params = mapOf("Residence" to residence), + ) + + data class ResidenceConfirmScreenOpened( + private val residence: String, + ) : OnrampAnalyticsEvent( + event = "Residence Confirm Screen", + params = mapOf("Residence" to residence), + ) + + data object OnResidenceChange : OnrampAnalyticsEvent(event = "Button - Change") + + data class OnResidenceConfirm( + private val residence: String, + ) : OnrampAnalyticsEvent( + event = "Button - Confirm", + params = mapOf("Residence" to residence), + ) + + data object ProvidersScreenOpened : OnrampAnalyticsEvent(event = "Providers Screen Opened") + + data class ProviderCalculated( + private val providerName: String, + private val cryptoCurrency: String, + ) : OnrampAnalyticsEvent( + event = "Provider Calculated", + params = mapOf( + "Provider" to providerName, + "Token" to cryptoCurrency, + ), + ) + + data object PaymentMethodsScreenOpened : OnrampAnalyticsEvent(event = "Payment Method Screen Opened") + + data object OnPaymentMethodChosen : OnrampAnalyticsEvent(event = "Method Chosen") + + data class OnProviderChosen( + private val providerName: String, + private val cryptoCurrency: String, + ) : OnrampAnalyticsEvent( + event = "Provider Chosen", + params = mapOf( + "Provider" to providerName, + "Token" to cryptoCurrency, + ), + ) + + data class OnBuyClick( + private val providerName: String, + private val currency: String, + private val cryptoCurrency: String, + ) : OnrampAnalyticsEvent( + event = "Provider Chosen", + params = mapOf( + "Provider" to providerName, + "Currency" to currency, + "Token" to cryptoCurrency, + ), + ) +} \ No newline at end of file diff --git a/domain/tokens/build.gradle.kts b/domain/tokens/build.gradle.kts index a1c639300b..06966f21e2 100644 --- a/domain/tokens/build.gradle.kts +++ b/domain/tokens/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(projects.domain.transaction.models) implementation(projects.domain.wallets.models) implementation(projects.domain.appCurrency.models) + implementation(projects.domain.onramp.models) implementation(projects.domain.settings) implementation(projects.features.swap.domain.api) implementation(projects.features.swap.domain.models) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/legacy/TradeCryptoAction.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/legacy/TradeCryptoAction.kt index b32c7a091f..a9ffeda4c8 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/legacy/TradeCryptoAction.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/legacy/TradeCryptoAction.kt @@ -1,5 +1,6 @@ package com.tangem.domain.tokens.legacy +import com.tangem.domain.onramp.model.OnrampSource import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWallet @@ -13,6 +14,7 @@ sealed class TradeCryptoAction : Action { data class Buy( val userWallet: UserWallet, val cryptoCurrencyStatus: CryptoCurrencyStatus, + val source: OnrampSource, val appCurrencyCode: String, val checkUserLocation: Boolean = true, ) : TradeCryptoAction() diff --git a/features/markets/impl/build.gradle.kts b/features/markets/impl/build.gradle.kts index 6b12108830..4f73123e32 100644 --- a/features/markets/impl/build.gradle.kts +++ b/features/markets/impl/build.gradle.kts @@ -26,6 +26,7 @@ dependencies { implementation(projects.domain.feedback) implementation(projects.domain.manageTokens) implementation(projects.domain.markets) + implementation(projects.domain.onramp.models) implementation(projects.domain.staking.models) implementation(projects.domain.tokens) implementation(projects.domain.tokens.models) diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokenActionsHandler.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokenActionsHandler.kt index 2333f6b48f..dc0486d1cc 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokenActionsHandler.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokenActionsHandler.kt @@ -13,6 +13,7 @@ import com.tangem.core.ui.message.ContentMessage import com.tangem.core.ui.message.SnackbarMessage import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.demo.IsDemoCardUseCase +import com.tangem.domain.onramp.model.OnrampSource import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.tokens.legacy.TradeCryptoAction import com.tangem.domain.tokens.model.TokenActionsState @@ -131,6 +132,7 @@ internal class TokenActionsHandler @AssistedInject constructor( reduxStateHolder.dispatch( TradeCryptoAction.Buy( userWallet = cryptoCurrencyData.userWallet, + source = OnrampSource.MARKETS, cryptoCurrencyStatus = cryptoCurrencyData.status, appCurrencyCode = currentAppCurrency().code, ), diff --git a/features/onramp/api/build.gradle.kts b/features/onramp/api/build.gradle.kts index 632fd29071..ac5210f2a6 100644 --- a/features/onramp/api/build.gradle.kts +++ b/features/onramp/api/build.gradle.kts @@ -15,6 +15,7 @@ dependencies { implementation(projects.core.ui) /* Project - Domain */ + implementation(projects.domain.onramp.models) implementation(projects.domain.tokens.models) implementation(projects.domain.wallets.models) diff --git a/features/onramp/api/src/main/kotlin/com/tangem/features/onramp/component/OnrampComponent.kt b/features/onramp/api/src/main/kotlin/com/tangem/features/onramp/component/OnrampComponent.kt index ac7c7c7800..9f7280b153 100644 --- a/features/onramp/api/src/main/kotlin/com/tangem/features/onramp/component/OnrampComponent.kt +++ b/features/onramp/api/src/main/kotlin/com/tangem/features/onramp/component/OnrampComponent.kt @@ -2,12 +2,13 @@ package com.tangem.features.onramp.component import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.onramp.model.OnrampSource import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId interface OnrampComponent : ComposableContentComponent { - data class Params(val userWalletId: UserWalletId, val cryptoCurrency: CryptoCurrency) + data class Params(val userWalletId: UserWalletId, val cryptoCurrency: CryptoCurrency, val source: OnrampSource) interface Factory : ComponentFactory } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/model/ConfirmResidencyModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/model/ConfirmResidencyModel.kt index d9afb5e94b..460f0a03ad 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/model/ConfirmResidencyModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/confirmresidency/model/ConfirmResidencyModel.kt @@ -2,6 +2,7 @@ package com.tangem.features.onramp.confirmresidency.model import com.arkivanov.decompose.router.slot.SlotNavigation import com.arkivanov.decompose.router.slot.activate +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ComponentScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer @@ -9,6 +10,7 @@ import com.tangem.core.decompose.navigation.Router import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.onramp.GetOnrampCountryUseCase import com.tangem.domain.onramp.OnrampSaveDefaultCountryUseCase +import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent import com.tangem.features.onramp.confirmresidency.ConfirmResidencyComponent import com.tangem.features.onramp.confirmresidency.entity.ConfirmResidencyBottomSheetConfig import com.tangem.features.onramp.confirmresidency.entity.ConfirmResidencyUM @@ -23,6 +25,7 @@ import javax.inject.Inject @ComponentScoped internal class ConfirmResidencyModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, + private val analyticsEventHandler: AnalyticsEventHandler, private val router: Router, private val saveDefaultCountryUseCase: OnrampSaveDefaultCountryUseCase, getOnrampCountryUseCase: GetOnrampCountryUseCase, @@ -45,6 +48,7 @@ internal class ConfirmResidencyModel @Inject constructor( ) init { + analyticsEventHandler.send(OnrampAnalyticsEvent.ResidenceConfirmScreenOpened(params.country.name)) getOnrampCountryUseCase.invoke() .onEach { maybeCountry -> val country = maybeCountry.getOrNull() @@ -58,18 +62,23 @@ internal class ConfirmResidencyModel @Inject constructor( private fun getPrimaryButtonConfig() = if (params.country.onrampAvailable) { ConfirmResidencyUM.ActionButtonConfig( onClick = { + analyticsEventHandler.send(OnrampAnalyticsEvent.OnResidenceConfirm(params.country.name)) modelScope.launch { saveDefaultCountryUseCase.invoke(params.country) } }, text = resourceReference(R.string.common_confirm), ) } else { ConfirmResidencyUM.ActionButtonConfig( - onClick = router::pop, + onClick = { + analyticsEventHandler.send(OnrampAnalyticsEvent.CloseOnramp) + router.pop() + }, text = resourceReference(R.string.common_close), ) } private fun onChangeClick() { + analyticsEventHandler.send(OnrampAnalyticsEvent.OnResidenceChange) bottomSheetNavigation.activate(ConfirmResidencyBottomSheetConfig.SelectCountry) } } \ No newline at end of file diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/DefaultOnrampMainComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/DefaultOnrampMainComponent.kt index a5c15531e0..22f3fd3a48 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/DefaultOnrampMainComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/DefaultOnrampMainComponent.kt @@ -24,7 +24,7 @@ import dagger.assisted.AssistedInject internal class DefaultOnrampMainComponent @AssistedInject constructor( @Assisted appComponentContext: AppComponentContext, - @Assisted params: OnrampMainComponent.Params, + @Assisted private val params: OnrampMainComponent.Params, private val confirmResidencyComponentFactory: ConfirmResidencyComponent.Factory, private val selectCurrencyComponentFactory: SelectCurrencyComponent.Factory, private val selectProviderComponentFactory: SelectProviderComponent.Factory, @@ -68,6 +68,7 @@ internal class DefaultOnrampMainComponent @AssistedInject constructor( onProviderClick = model::onProviderSelected, onDismiss = model.bottomSheetNavigation::dismiss, selectedPaymentMethod = config.selectedPaymentMethod, + cryptoCurrency = params.cryptoCurrency, ), ) } diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/OnrampMainComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/OnrampMainComponent.kt index b8998352d9..8bce587855 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/OnrampMainComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/OnrampMainComponent.kt @@ -3,12 +3,14 @@ package com.tangem.features.onramp.main import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.domain.onramp.model.OnrampProviderWithQuote +import com.tangem.domain.onramp.model.OnrampSource import com.tangem.domain.tokens.model.CryptoCurrency internal interface OnrampMainComponent : ComposableContentComponent { data class Params( val cryptoCurrency: CryptoCurrency, + val source: OnrampSource, val openSettings: () -> Unit, val openRedirectPage: (quote: OnrampProviderWithQuote.Data) -> Unit, ) diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt index 14ffd88063..7b74e90295 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/main/model/OnrampMainComponentModel.kt @@ -2,10 +2,12 @@ package com.tangem.features.onramp.main.model import com.arkivanov.decompose.router.slot.SlotNavigation import com.arkivanov.decompose.router.slot.activate +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router import com.tangem.domain.onramp.* +import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent import com.tangem.domain.onramp.model.OnrampAvailability import com.tangem.domain.onramp.model.OnrampCurrency import com.tangem.domain.onramp.model.OnrampProviderWithQuote @@ -27,6 +29,7 @@ import javax.inject.Inject @Suppress("LongParameterList") internal class OnrampMainComponentModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, + private val analyticsEventHandler: AnalyticsEventHandler, private val router: Router, private val checkOnrampAvailabilityUseCase: CheckOnrampAvailabilityUseCase, private val getOnrampCurrencyUseCase: GetOnrampCurrencyUseCase, @@ -58,10 +61,20 @@ internal class OnrampMainComponentModel @Inject constructor( val bottomSheetNavigation: SlotNavigation = SlotNavigation() init { + sendScreenOpenAnalytics() checkResidenceCountry() subscribeToAmountChanges() } + private fun sendScreenOpenAnalytics() { + analyticsEventHandler.send( + OnrampAnalyticsEvent.ScreenOpened( + source = params.source, + cryptoCurrency = params.cryptoCurrency.name, + ), + ) + } + fun onProviderSelected(providerWithQuote: OnrampProviderWithQuote.Data) { _state.update { amountStateFactory.getAmountSecondaryUpdatedState(providerWithQuote) } } @@ -132,10 +145,19 @@ internal class OnrampMainComponentModel @Inject constructor( } override fun onBuyClick(quote: OnrampProviderWithQuote.Data) { + val currentContentState = state.value as? OnrampMainComponentUM.Content ?: return + analyticsEventHandler.send( + OnrampAnalyticsEvent.OnBuyClick( + providerName = quote.provider.info.name, + currency = currentContentState.amountBlockState.currencyUM.code, + cryptoCurrency = params.cryptoCurrency.name, + ), + ) params.openRedirectPage(quote) } override fun openCurrenciesList() { + analyticsEventHandler.send(OnrampAnalyticsEvent.SelectCurrencyScreenOpened) bottomSheetNavigation.activate(OnrampMainBottomSheetConfig.CurrenciesList) } diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/SelectProviderComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/SelectProviderComponent.kt index 1e12daef48..181a2ed99e 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/SelectProviderComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/SelectProviderComponent.kt @@ -4,6 +4,7 @@ import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.domain.onramp.model.OnrampPaymentMethod import com.tangem.domain.onramp.model.OnrampProviderWithQuote +import com.tangem.domain.tokens.model.CryptoCurrency internal interface SelectProviderComponent : ComposableBottomSheetComponent { @@ -11,6 +12,7 @@ internal interface SelectProviderComponent : ComposableBottomSheetComponent { val onProviderClick: (OnrampProviderWithQuote.Data) -> Unit, val onDismiss: () -> Unit, val selectedPaymentMethod: OnrampPaymentMethod, + val cryptoCurrency: CryptoCurrency, ) interface Factory : ComponentFactory diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt index cbab9bf39e..a4a558cf10 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/providers/model/SelectProviderModel.kt @@ -3,6 +3,7 @@ package com.tangem.features.onramp.providers.model import com.arkivanov.decompose.router.slot.SlotNavigation import com.arkivanov.decompose.router.slot.activate import com.arkivanov.decompose.router.slot.dismiss +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ComponentScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer @@ -15,6 +16,7 @@ import com.tangem.domain.onramp.GetOnrampPaymentMethodsUseCase import com.tangem.domain.onramp.GetOnrampProviderWithQuoteUseCase import com.tangem.domain.onramp.GetOnrampSelectedPaymentMethodUseCase import com.tangem.domain.onramp.OnrampSaveSelectedPaymentMethod +import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent import com.tangem.domain.onramp.model.OnrampPaymentMethod import com.tangem.domain.onramp.model.OnrampProviderWithQuote import com.tangem.features.onramp.impl.R @@ -31,9 +33,11 @@ import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject +@Suppress("LongParameterList") @ComponentScoped internal class SelectProviderModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, + private val analyticsEventHandler: AnalyticsEventHandler, private val getSelectedPaymentMethodsUseCase: GetOnrampPaymentMethodsUseCase, private val getOnrampSelectedPaymentMethodUseCase: GetOnrampSelectedPaymentMethodUseCase, private val getOnrampProviderWithQuoteUseCase: GetOnrampProviderWithQuoteUseCase, @@ -47,6 +51,7 @@ internal class SelectProviderModel @Inject constructor( private val _state = MutableStateFlow(getInitialState()) init { + analyticsEventHandler.send(OnrampAnalyticsEvent.ProvidersScreenOpened) getProviders(params.selectedPaymentMethod) subscribeToPaymentMethodUpdates() } @@ -80,6 +85,7 @@ internal class SelectProviderModel @Inject constructor( } private fun openPaymentMethods() { + analyticsEventHandler.send(OnrampAnalyticsEvent.PaymentMethodsScreenOpened) modelScope.launch { val methods = getSelectedPaymentMethodsUseCase.invoke().getOrNull().orEmpty() bottomSheetNavigation.activate( @@ -101,6 +107,7 @@ internal class SelectProviderModel @Inject constructor( } private fun onPaymentMethodSelected(paymentMethod: OnrampPaymentMethod) { + analyticsEventHandler.send(OnrampAnalyticsEvent.OnPaymentMethodChosen) modelScope.launch { saveSelectedPaymentMethod.invoke(paymentMethod) _state.update { state -> @@ -128,6 +135,12 @@ internal class SelectProviderModel @Inject constructor( rate = rate, isSelected = index == 0, onClick = { + analyticsEventHandler.send( + OnrampAnalyticsEvent.OnProviderChosen( + providerName = quote.provider.info.name, + cryptoCurrency = params.cryptoCurrency.name, + ), + ) params.onProviderClick(quote) params.onDismiss() }, diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/root/DefaultOnrampComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/root/DefaultOnrampComponent.kt index 0c6a0c9b77..826a86e2dc 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/root/DefaultOnrampComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/root/DefaultOnrampComponent.kt @@ -67,6 +67,7 @@ internal class DefaultOnrampComponent @AssistedInject constructor( params = OnrampMainComponent.Params( cryptoCurrency = params.cryptoCurrency, openSettings = { navigation.push(OnrampChild.Settings) }, + source = params.source, openRedirectPage = { navigation.push( OnrampChild.RedirectPage( diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcountry/model/OnrampSelectCountryModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcountry/model/OnrampSelectCountryModel.kt index 9047b28af5..6e8fdf903c 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcountry/model/OnrampSelectCountryModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcountry/model/OnrampSelectCountryModel.kt @@ -1,5 +1,6 @@ package com.tangem.features.onramp.selectcountry.model +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ComponentScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer @@ -8,6 +9,7 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.onramp.GetOnrampCountriesUseCase import com.tangem.domain.onramp.GetOnrampCountryUseCase import com.tangem.domain.onramp.OnrampSaveDefaultCountryUseCase +import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent import com.tangem.domain.onramp.model.OnrampCountry import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.selectcountry.SelectCountryComponent @@ -27,9 +29,11 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import javax.inject.Inject +@Suppress("LongParameterList") @ComponentScoped internal class OnrampSelectCountryModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, + private val analyticsEventHandler: AnalyticsEventHandler, private val searchManager: InputManager, private val getOnrampCountriesUseCase: GetOnrampCountriesUseCase, private val saveDefaultCountryUseCase: OnrampSaveDefaultCountryUseCase, @@ -46,6 +50,7 @@ internal class OnrampSelectCountryModel @Inject constructor( private val refreshTrigger = MutableSharedFlow() init { + analyticsEventHandler.send(OnrampAnalyticsEvent.SelectResidenceOpened) modelScope.launch { subscribeOnUpdateState() } } @@ -73,6 +78,7 @@ internal class OnrampSelectCountryModel @Inject constructor( } private fun saveCountry(country: OnrampCountry) { + analyticsEventHandler.send(OnrampAnalyticsEvent.OnResidenceChosen(country.name)) modelScope.launch { saveDefaultCountryUseCase.invoke(country) dismiss() diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcurrency/model/OnrampSelectCurrencyModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcurrency/model/OnrampSelectCurrencyModel.kt index 83e0cbac8a..ae4035b1e5 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcurrency/model/OnrampSelectCurrencyModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/selectcurrency/model/OnrampSelectCurrencyModel.kt @@ -1,5 +1,6 @@ package com.tangem.features.onramp.selectcurrency.model +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ComponentScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer @@ -8,6 +9,7 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.domain.onramp.GetOnrampCurrenciesUseCase import com.tangem.domain.onramp.OnrampSaveDefaultCurrencyUseCase +import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent import com.tangem.domain.onramp.model.OnrampCurrency import com.tangem.features.onramp.impl.R import com.tangem.features.onramp.selectcurrency.SelectCurrencyComponent @@ -31,6 +33,7 @@ import javax.inject.Inject @ComponentScoped internal class OnrampSelectCurrencyModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, + private val analyticsEventHandler: AnalyticsEventHandler, private val searchManager: InputManager, private val getOnrampCurrenciesUseCase: GetOnrampCurrenciesUseCase, private val saveDefaultCurrencyUseCase: OnrampSaveDefaultCurrencyUseCase, @@ -72,6 +75,7 @@ internal class OnrampSelectCurrencyModel @Inject constructor( } private fun saveDefaultCurrency(currency: OnrampCurrency) { + analyticsEventHandler.send(OnrampAnalyticsEvent.FiatCurrencyChosen(currency.code)) modelScope.launch { saveDefaultCurrencyUseCase.invoke(currency) dismiss() 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 cb2f24483a..2f64fc2fe7 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 @@ -18,6 +18,7 @@ import com.tangem.core.ui.message.ContentMessage import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.demo.IsDemoCardUseCase +import com.tangem.domain.onramp.model.OnrampSource import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.tokens.legacy.TradeCryptoAction import com.tangem.domain.tokens.model.CryptoCurrencyStatus @@ -118,6 +119,7 @@ internal class OnrampOperationModel @Inject constructor( return TradeCryptoAction.Buy( userWallet = selectedUserWallet, cryptoCurrencyStatus = status, + source = OnrampSource.ACTION_BUTTONS, appCurrencyCode = appCurrencyCode, ) } diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/settings/model/OnrampSettingsModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/settings/model/OnrampSettingsModel.kt index 0f41e6f6ca..17fb7e14db 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/settings/model/OnrampSettingsModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/settings/model/OnrampSettingsModel.kt @@ -3,10 +3,12 @@ package com.tangem.features.onramp.settings.model import arrow.core.Either import com.arkivanov.decompose.router.slot.SlotNavigation import com.arkivanov.decompose.router.slot.activate +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ComponentScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.domain.onramp.GetOnrampCountryUseCase +import com.tangem.domain.onramp.analytics.OnrampAnalyticsEvent import com.tangem.domain.onramp.model.OnrampCountry import com.tangem.features.onramp.settings.OnrampSettingsComponent import com.tangem.features.onramp.settings.entity.OnrampSettingsConfig @@ -20,6 +22,7 @@ import javax.inject.Inject @ComponentScoped internal class OnrampSettingsModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, + private val analyticsEventHandler: AnalyticsEventHandler, private val getOnrampCountryUseCase: GetOnrampCountryUseCase, paramsContainer: ParamsContainer, ) : Model() { @@ -31,6 +34,7 @@ internal class OnrampSettingsModel @Inject constructor( private val _state: MutableStateFlow = MutableStateFlow(getInitialState()) init { + analyticsEventHandler.send(OnrampAnalyticsEvent.SettingsOpened) subscribeOnUpdateState() } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index bef30a208e..2f75b23f8a 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -29,6 +29,7 @@ import com.tangem.domain.card.GetExtendedPublicKeyForCurrencyUseCase import com.tangem.domain.card.NetworkHasDerivationUseCase import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.demo.IsDemoCardUseCase +import com.tangem.domain.onramp.model.OnrampSource import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.settings.ShouldShowSwapPromoTokenUseCase import com.tangem.domain.staking.GetStakingAvailabilityUseCase @@ -441,6 +442,7 @@ internal class TokenDetailsViewModel @Inject constructor( reduxStateHolder.dispatch( TradeCryptoAction.Buy( userWallet = userWallet, + source = OnrampSource.TOKEN_DETAILS, cryptoCurrencyStatus = status, appCurrencyCode = selectedAppCurrencyFlow.value.code, ), diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index bbddfd2a4d..88d3ff2a2c 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -81,6 +81,7 @@ dependencies { implementation(projects.domain.staking.models) implementation(projects.domain.markets.models) implementation(projects.domain.feedback) + implementation(projects.domain.onramp.models) //TODO: Create api/impl modules for onboarding [REDACTED_JIRA] implementation(projects.features.onboarding) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt index 2eac6d5e37..3f7c720582 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt @@ -26,6 +26,7 @@ import com.tangem.domain.core.utils.lceError import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.markets.TokenMarketParams +import com.tangem.domain.onramp.model.OnrampSource import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.settings.usercountry.GetUserCountryUseCase import com.tangem.domain.settings.usercountry.models.UserCountry @@ -374,6 +375,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( reduxStateHolder.dispatch( TradeCryptoAction.Buy( userWallet = userWallet, + source = OnrampSource.TOKEN_LONG_TAP, cryptoCurrencyStatus = cryptoCurrencyStatus, appCurrencyCode = getSelectedAppCurrencyUseCase.unwrap().code, ),