diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/MainScreen.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/MainScreen.kt deleted file mode 100644 index f97dca3a02..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/MainScreen.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.tangem.tap.common.analytics.events - -import com.tangem.core.analytics.models.AnalyticsEvent - -/** -[REDACTED_AUTHOR] - */ -sealed class MainScreen( - event: String, - params: Map = mapOf(), -) : AnalyticsEvent("Main Screen", event, params) { - - class ScreenOpened : MainScreen("Screen opened") - class ButtonScanCard : MainScreen("Button - Scan Card") - - class EnableBiometrics(state: AnalyticsParam.OnOffState) : MainScreen( - event = "Enable Biometric", - params = mapOf("State" to state.value), - ) - - class NoticeRateAppButton(result: AnalyticsParam.RateApp) : MainScreen( - event = "Notice - Rate The App Button Tapped", - params = mapOf("Result" to result.value), - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt b/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt index edc221250e..42a7f46c75 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt @@ -9,7 +9,6 @@ import com.tangem.domain.wallets.builder.UserWalletIdBuilder import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.IntroductionProcess -import com.tangem.tap.common.analytics.events.MainScreen import com.tangem.tap.common.extensions.inject import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.proxy.redux.DaggerGraphState @@ -31,7 +30,7 @@ class CardContextInterceptor( override fun canBeAppliedTo(event: AnalyticsEvent): Boolean { return when (event) { - is IntroductionProcess.ButtonScanCard, is MainScreen.ButtonScanCard -> false + is IntroductionProcess.ButtonScanCard -> false else -> true } } diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt index c8e093611d..2c7903ad60 100644 --- a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt @@ -5,11 +5,11 @@ import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.guard import com.tangem.common.routing.AppRoute import com.tangem.core.analytics.Analytics +import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent import com.tangem.domain.wallets.builder.UserWalletBuilder import com.tangem.domain.wallets.models.UserWallet import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.MainScreen import com.tangem.tap.common.analytics.events.Onboarding import com.tangem.tap.common.extensions.dispatchNavigationAction import com.tangem.tap.common.extensions.dispatchWithMain @@ -110,7 +110,11 @@ internal class SaveWalletMiddleware { // TODO: Remove after onboarding refactoring Analytics.send(Onboarding.EnableBiometrics(AnalyticsParam.OnOffState.On)) } else { - Analytics.send(MainScreen.EnableBiometrics(AnalyticsParam.OnOffState.On)) + Analytics.send( + MainScreenAnalyticsEvent.EnableBiometrics( + state = com.tangem.core.analytics.models.AnalyticsParam.OnOffState.On, + ), + ) } /* @@ -148,7 +152,11 @@ internal class SaveWalletMiddleware { // TODO: Remove after onboarding refactoring Analytics.send(Onboarding.EnableBiometrics(AnalyticsParam.OnOffState.Off)) } else { - Analytics.send(MainScreen.EnableBiometrics(AnalyticsParam.OnOffState.Off)) + Analytics.send( + MainScreenAnalyticsEvent.EnableBiometrics( + com.tangem.core.analytics.models.AnalyticsParam.OnOffState.Off, + ), + ) } } diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt index b646142403..ce733b8c51 100644 --- a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt +++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt @@ -76,6 +76,9 @@ sealed class AnalyticsParam { data object MyWallets : ScreensSources("My Wallets") data object Token : ScreensSources("Token") data object Stories : ScreensSources("Stories") + data object Buy : ScreensSources("Buy") + data object Swap : ScreensSources("Swap") + data object Sell : ScreensSources("Sell") } sealed class TxSentFrom(val value: String) { @@ -161,6 +164,12 @@ sealed class AnalyticsParam { } } + enum class Status(val value: String) { + Success(value = "Success"), + Error(value = "Error"), + Pending(value = "Pending"), + } + companion object Key { const val BLOCKCHAIN = "blockchain" const val TOKEN_PARAM = "Token" 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 new file mode 100644 index 0000000000..02c1a6d4c0 --- /dev/null +++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/MainScreenAnalyticsEvent.kt @@ -0,0 +1,75 @@ +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.TOKEN_PARAM + +/** + * Main screen analytics event + * + * @param event event name + * @param params params + */ +sealed class MainScreenAnalyticsEvent( + event: String, + params: Map = mapOf(), +) : AnalyticsEvent(category = "Main Screen", event = event, params = params) { + + class EnableBiometrics(state: AnalyticsParam.OnOffState) : MainScreenAnalyticsEvent( + event = "Enable Biometric", + params = mapOf("State" to state.value), + ) + + // region Action Buttons feature + data class ButtonBuy(val status: AnalyticsParam.Status) : MainScreenAnalyticsEvent( + event = "Button - Buy", + params = mapOf(AnalyticsParam.STATUS to status.value), + ) + + data class ButtonSwap(val status: AnalyticsParam.Status) : MainScreenAnalyticsEvent( + event = "Button - Swap", + params = mapOf(AnalyticsParam.STATUS to status.value), + ) + + data class ButtonSell(val status: AnalyticsParam.Status) : MainScreenAnalyticsEvent( + event = "Button - Sell", + params = mapOf(AnalyticsParam.STATUS to status.value), + ) + + data object BuyScreenOpened : MainScreenAnalyticsEvent(event = "Buy Screen Opened") + + data object SwapScreenOpened : MainScreenAnalyticsEvent(event = "Swap Screen Opened") + + data object SellScreenOpened : MainScreenAnalyticsEvent(event = "Sell Screen Opened") + + data class BuyTokenClicked(val currencySymbol: String) : MainScreenAnalyticsEvent( + event = "Buy Token Clicked", + params = mapOf(TOKEN_PARAM to currencySymbol), + ) + + data class SellTokenClicked(val currencySymbol: String) : MainScreenAnalyticsEvent( + event = "Sell Token Clicked", + params = mapOf(TOKEN_PARAM to currencySymbol), + ) + + data class SwapTokenClicked(val currencySymbol: String) : MainScreenAnalyticsEvent( + event = "Swap Token Clicked", + params = mapOf(TOKEN_PARAM to currencySymbol), + ) + + data class ReceiveTokenClicked(val currencySymbol: String) : MainScreenAnalyticsEvent( + event = "Receive Token Clicked", + params = mapOf(TOKEN_PARAM to currencySymbol), + ) + + data class RemoveTokenClicked(val currencySymbol: String) : MainScreenAnalyticsEvent( + event = "Remove Button Clicked", + params = mapOf(TOKEN_PARAM to currencySymbol), + ) + + data class ButtonClose(val source: AnalyticsParam.ScreensSources) : MainScreenAnalyticsEvent( + event = "Button - Close", + params = mapOf(AnalyticsParam.SOURCE to source.value), + ) + // endregion +} \ No newline at end of file diff --git a/features/onramp/impl/build.gradle.kts b/features/onramp/impl/build.gradle.kts index d2aa39d798..25efafca87 100644 --- a/features/onramp/impl/build.gradle.kts +++ b/features/onramp/impl/build.gradle.kts @@ -19,10 +19,11 @@ dependencies { implementation(projects.features.swap.domain.models) /** Project - Core */ - implementation(projects.core.decompose) - implementation(projects.core.ui) + implementation(projects.core.analytics) implementation(projects.core.configToggles) + implementation(projects.core.decompose) implementation(projects.core.navigation) + implementation(projects.core.ui) /** Project - Common */ implementation(projects.common.routing) 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 6947d107cd..5829d1f651 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 @@ -3,6 +3,9 @@ package com.tangem.features.onramp.selecttoken import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import arrow.core.getOrElse +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.child import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase @@ -20,6 +23,8 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.launch +// TODO: Add OnrampOperationModel +@Suppress("LongParameterList") internal class DefaultOnrampOperationComponent @AssistedInject constructor( @Assisted appComponentContext: AppComponentContext, onrampTokenListComponentFactory: OnrampTokenListComponent.Factory, @@ -27,6 +32,7 @@ internal class DefaultOnrampOperationComponent @AssistedInject constructor( private val reduxStateHolder: ReduxStateHolder, private val getWalletsUseCase: GetWalletsUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, + private val analyticsEventHandler: AnalyticsEventHandler, ) : AppComponentContext by appComponentContext, OnrampOperationComponent { private val onrampTokenListComponent: OnrampTokenListComponent = onrampTokenListComponentFactory.create( @@ -42,6 +48,15 @@ internal class DefaultOnrampOperationComponent @AssistedInject constructor( ), ) + init { + analyticsEventHandler.send( + event = when (params) { + is OnrampOperationComponent.Params.Buy -> MainScreenAnalyticsEvent.BuyScreenOpened + is OnrampOperationComponent.Params.Sell -> MainScreenAnalyticsEvent.SellScreenOpened + }, + ) + } + @Composable override fun Content(modifier: Modifier) { OnrampSelectToken( @@ -49,13 +64,25 @@ internal class DefaultOnrampOperationComponent @AssistedInject constructor( is OnrampOperationComponent.Params.Buy -> R.string.common_buy is OnrampOperationComponent.Params.Sell -> R.string.common_sell }, - onBackClick = router::pop, + onBackClick = ::onBackClick, onrampTokenListComponent = onrampTokenListComponent, modifier = modifier, ) } private fun onTokenClick(status: CryptoCurrencyStatus) { + val currencySymbol = status.currency.symbol + analyticsEventHandler.send( + event = when (params) { + is OnrampOperationComponent.Params.Buy -> { + MainScreenAnalyticsEvent.BuyTokenClicked(currencySymbol = currencySymbol) + } + is OnrampOperationComponent.Params.Sell -> { + MainScreenAnalyticsEvent.SellTokenClicked(currencySymbol = currencySymbol) + } + }, + ) + componentScope.launch { val appCurrencyCode = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default }.code @@ -76,6 +103,19 @@ internal class DefaultOnrampOperationComponent @AssistedInject constructor( ) } + private fun onBackClick() { + analyticsEventHandler.send( + event = MainScreenAnalyticsEvent.ButtonClose( + source = when (params) { + is OnrampOperationComponent.Params.Buy -> AnalyticsParam.ScreensSources.Buy + is OnrampOperationComponent.Params.Sell -> AnalyticsParam.ScreensSources.Sell + }, + ), + ) + + router.pop() + } + @AssistedFactory interface Factory : OnrampOperationComponent.Factory { diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/DefaultSwapSelectTokensComponent.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/DefaultSwapSelectTokensComponent.kt index d49405a047..7c48a4eb47 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/DefaultSwapSelectTokensComponent.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/DefaultSwapSelectTokensComponent.kt @@ -4,6 +4,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.child import com.tangem.core.decompose.model.getOrCreateModel @@ -21,6 +23,7 @@ import dagger.assisted.AssistedInject internal class DefaultSwapSelectTokensComponent @AssistedInject constructor( tokenListComponentFactory: OnrampTokenListComponent.Factory, availableSwapPairsComponentFactory: AvailableSwapPairsComponent.Factory, + analyticsEventHandler: AnalyticsEventHandler, @Assisted private val appComponentContext: AppComponentContext, @Assisted private val params: SwapSelectTokensComponent.Params, ) : AppComponentContext by appComponentContext, SwapSelectTokensComponent { @@ -46,6 +49,10 @@ internal class DefaultSwapSelectTokensComponent @AssistedInject constructor( ), ) + init { + analyticsEventHandler.send(event = MainScreenAnalyticsEvent.SwapScreenOpened) + } + @Composable override fun Content(modifier: Modifier) { val state = model.state.collectAsStateWithLifecycle() diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/SwapSelectTokensController.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/SwapSelectTokensController.kt index f6cd655930..4e76010bdb 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/SwapSelectTokensController.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/SwapSelectTokensController.kt @@ -1,6 +1,5 @@ package com.tangem.features.onramp.swap.entity -import com.tangem.core.decompose.navigation.Router import com.tangem.features.onramp.swap.entity.utils.createEmptyExchangeFrom import com.tangem.features.onramp.swap.entity.utils.createEmptyExchangeTo import kotlinx.coroutines.flow.MutableStateFlow @@ -12,19 +11,15 @@ import javax.inject.Inject /** * [SwapSelectTokensUM] controller * - * @property router router - * [REDACTED_AUTHOR] */ -internal class SwapSelectTokensController @Inject constructor( - private val router: Router, -) { +internal class SwapSelectTokensController @Inject constructor() { val state: StateFlow get() = _state private val _state: MutableStateFlow = MutableStateFlow( value = SwapSelectTokensUM( - onBackClick = router::pop, + onBackClick = {}, exchangeFrom = createEmptyExchangeFrom(), exchangeTo = createEmptyExchangeTo(), ), diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/model/SwapSelectTokensModel.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/model/SwapSelectTokensModel.kt index e86d202e54..6944ed517f 100644 --- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/model/SwapSelectTokensModel.kt +++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/model/SwapSelectTokensModel.kt @@ -1,6 +1,9 @@ package com.tangem.features.onramp.swap.model import com.tangem.common.routing.AppRoute +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router @@ -27,6 +30,7 @@ internal class SwapSelectTokensModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, private val controller: SwapSelectTokensController, private val router: Router, + private val analyticsEventHandler: AnalyticsEventHandler, ) : Model() { val state: StateFlow = controller.state @@ -38,6 +42,12 @@ internal class SwapSelectTokensModel @Inject constructor( private val params = paramsContainer.require() + init { + controller.update { + it.copy(onBackClick = ::onBackClick) + } + } + /** * Select "from" token * @@ -45,12 +55,16 @@ internal class SwapSelectTokensModel @Inject constructor( * @param status crypto currency status */ fun selectFromToken(selectedTokenItemState: TokenItemState, status: CryptoCurrencyStatus) { + analyticsEventHandler.send( + event = MainScreenAnalyticsEvent.SwapTokenClicked(currencySymbol = status.currency.symbol), + ) + _fromCurrencyStatus.value = status controller.update( transformer = SelectFromTokenTransformer( selectedTokenItemState = selectedTokenItemState, - onRemoveClick = ::removeSelectedFromToken, + onRemoveClick = ::onRemoveFromTokenClick, ), ) } @@ -62,6 +76,10 @@ internal class SwapSelectTokensModel @Inject constructor( * @param status crypto currency status */ fun selectToToken(selectedTokenItemState: TokenItemState, status: CryptoCurrencyStatus) { + analyticsEventHandler.send( + event = MainScreenAnalyticsEvent.ReceiveTokenClicked(currencySymbol = status.currency.symbol), + ) + modelScope.launch { _toCurrencyStatus.value = status @@ -88,6 +106,26 @@ internal class SwapSelectTokensModel @Inject constructor( } } + private fun onBackClick() { + analyticsEventHandler.send( + event = MainScreenAnalyticsEvent.ButtonClose(source = AnalyticsParam.ScreensSources.Swap), + ) + + router.pop() + } + + private fun onRemoveFromTokenClick() { + val currencySymbol = requireNotNull(_fromCurrencyStatus.value?.currency?.symbol) { + "Token was not selected" + } + + analyticsEventHandler.send( + event = MainScreenAnalyticsEvent.RemoveTokenClicked(currencySymbol = currencySymbol), + ) + + removeSelectedFromToken() + } + private fun removeSelectedFromToken() { _fromCurrencyStatus.value = null 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 23d45eab8b..3e0fac69ce 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 @@ -5,6 +5,8 @@ import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter import com.tangem.common.ui.tokens.getUnavailabilityReasonText import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent import com.tangem.core.ui.clipboard.ClipboardManager import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig @@ -439,16 +441,34 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor( } override fun onMultiWalletSellClick(userWalletId: UserWalletId) { + analyticsEventHandler.send( + event = MainScreenAnalyticsEvent.ButtonSell( + status = AnalyticsParam.Status.Success, // TODO [REDACTED_JIRA] + ), + ) + // TODO [REDACTED_JIRA] appRouter.push(route = AppRoute.SellCrypto(userWalletId = userWalletId)) } override fun onMultiWalletSwapClick(userWalletId: UserWalletId) { + analyticsEventHandler.send( + event = MainScreenAnalyticsEvent.ButtonSwap( + status = AnalyticsParam.Status.Success, // TODO [REDACTED_JIRA] + ), + ) + // TODO [REDACTED_JIRA] appRouter.push(route = AppRoute.SwapCrypto(userWalletId = userWalletId)) } override fun onMultiWalletBuyClick(userWalletId: UserWalletId) { + analyticsEventHandler.send( + event = MainScreenAnalyticsEvent.ButtonBuy( + status = AnalyticsParam.Status.Success, // TODO [REDACTED_JIRA] + ), + ) + // TODO [REDACTED_JIRA] appRouter.push(route = AppRoute.BuyCrypto(userWalletId = userWalletId)) }