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 8eed0785bc..b85be1b1aa 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 @@ -120,6 +120,12 @@ sealed class AnalyticsParam { override val token: String, override val feeType: FeeType, ) : TxSentFrom("NFT"), TxData + + data class SendWithSwap( + override val blockchain: String, + override val token: String, + override val feeType: FeeType, + ) : TxSentFrom("Send&Swap"), TxData } sealed interface TxData { @@ -224,5 +230,9 @@ sealed class AnalyticsParam { const val STANDARD = "Standard" const val NO_COLLECTION = "No collection" const val EMULATION_STATUS = "Emulation Status" + const val SEND_TOKEN = "Send Token" + const val RECEIVE_TOKEN = "Receive Token" + const val SEND_BLOCKCHAIN = "Send Blockchain" + const val RECEIVE_BLOCKCHAIN = "Receive Blockchain" } } \ No newline at end of file diff --git a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/entity/FeeSelectorUM.kt b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/entity/FeeSelectorUM.kt index 6386d23721..ca50585ecf 100644 --- a/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/entity/FeeSelectorUM.kt +++ b/features/send-v2/api/src/main/java/com/tangem/features/send/v2/api/entity/FeeSelectorUM.kt @@ -3,9 +3,11 @@ package com.tangem.features.send.v2.api.entity import androidx.compose.runtime.Immutable import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.transaction.error.GetFeeError +import com.tangem.features.send.v2.api.entity.FeeItem.* import kotlinx.collections.immutable.ImmutableList import java.math.BigDecimal import java.math.BigInteger @@ -31,7 +33,19 @@ sealed class FeeSelectorUM { val feeExtraInfo: FeeExtraInfo, val feeFiatRateUM: FeeFiatRateUM?, val feeNonce: FeeNonce, - ) : FeeSelectorUM() + ) : FeeSelectorUM() { + fun toAnalyticType(): AnalyticsParam.FeeType = when (fees) { + is TransactionFee.Single -> AnalyticsParam.FeeType.Fixed + is TransactionFee.Choosable -> when (selectedFeeItem) { + is Suggested, + is Custom, + -> AnalyticsParam.FeeType.Custom + is Fast -> AnalyticsParam.FeeType.Max + is Market -> AnalyticsParam.FeeType.Normal + is Slow -> AnalyticsParam.FeeType.Min + } + } + } } @Immutable diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt index 013178fc55..47c52407c5 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt @@ -90,13 +90,15 @@ internal class DefaultSendComponent @AssistedInject constructor( ) { stack -> componentScope.launch { when (val activeComponent = stack.active.instance) { - is SendConfirmComponent -> if (model.currentRoute.value.isEditMode) { + is SendConfirmComponent -> { analyticsEventHandler.send( CommonSendAnalyticEvents.ConfirmationScreenOpened( categoryName = model.analyticCategoryName, ), ) - activeComponent.updateState(model.uiState.value) + if (model.currentRoute.value.isEditMode) { + activeComponent.updateState(model.uiState.value) + } } is SendAmountComponent -> { analyticsEventHandler.send( diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticEvents.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticEvents.kt index 120a1808f8..3a705b2270 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticEvents.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticEvents.kt @@ -32,4 +32,15 @@ internal sealed class SendAnalyticEvents( NONCE to nonceNotEmpty.toString().capitalize(), ), ) + + data class ConvertTokenButtonClicked( + val token: String, + val blockchain: String, + ) : SendAnalyticEvents( + event = "Button - Convert Token", + params = mapOf( + TOKEN_PARAM to token, + BLOCKCHAIN to blockchain, + ), + ) } \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt index 7af880d59a..f0a63844c1 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt @@ -51,6 +51,7 @@ import com.tangem.features.send.v2.common.CommonSendRoute import com.tangem.features.send.v2.common.CommonSendRoute.* import com.tangem.features.send.v2.common.SendConfirmAlertFactory import com.tangem.features.send.v2.common.ui.state.ConfirmUM +import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents import com.tangem.features.send.v2.send.confirm.SendConfirmComponent import com.tangem.features.send.v2.send.success.SendConfirmSuccessComponent import com.tangem.features.send.v2.send.ui.state.SendUM @@ -227,6 +228,12 @@ internal class SendModel @Inject constructor( } override fun onConvertToAnotherToken(lastAmount: String) { + analyticsEventHandler.send( + SendAnalyticEvents.ConvertTokenButtonClicked( + token = cryptoCurrency.symbol, + blockchain = cryptoCurrency.network.name, + ), + ) params.callback?.onConvertToAnotherToken(lastAmount = lastAmount) } diff --git a/features/swap-v2/impl/build.gradle.kts b/features/swap-v2/impl/build.gradle.kts index ab1ccbbe75..fed1e814da 100644 --- a/features/swap-v2/impl/build.gradle.kts +++ b/features/swap-v2/impl/build.gradle.kts @@ -30,6 +30,7 @@ dependencies { implementation(projects.core.navigation) implementation(projects.core.configToggles) implementation(projects.core.datasource) + implementation(projects.core.analytics) /** Common */ implementation(projects.common.ui) diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt index 0d6966910d..4df2de3238 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/SwapAmountBlockComponent.kt @@ -6,7 +6,6 @@ import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.extensions.compose.subscribeAsState -import com.arkivanov.decompose.router.slot.activate import com.arkivanov.decompose.router.slot.childSlot import com.arkivanov.decompose.router.slot.dismiss import com.arkivanov.essenty.lifecycle.subscribe @@ -69,20 +68,7 @@ internal class SwapAmountBlockComponent( isClickEnabled = isClickEnabled, onClick = onClick, onFinishAnimation = model::onFinishAnimation, - onProviderSelectClick = { - val amountUM = model.uiState.value as? SwapAmountUM.Content ?: return@SwapAmountBlockContent - val selectedProvider = amountUM.selectedQuote.provider ?: return@SwapAmountBlockContent - val cryptoCurrency = params.secondaryCryptoCurrency ?: return@SwapAmountBlockContent - - model.bottomSheetNavigation.activate( - SwapChooseProviderConfig( - providers = amountUM.swapQuotes, - cryptoCurrency = cryptoCurrency, - selectedProvider = selectedProvider, - userCountry = model.userCountry, - ), - ) - }, + onProviderSelectClick = model::onProviderClick, ) bottomSheet.child?.instance?.BottomSheet() diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/analytics/SwapAmountAnalyticEvents.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/analytics/SwapAmountAnalyticEvents.kt new file mode 100644 index 0000000000..197bd8a27c --- /dev/null +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/analytics/SwapAmountAnalyticEvents.kt @@ -0,0 +1,27 @@ +package com.tangem.features.swap.v2.impl.amount.analytics + +import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.core.analytics.models.AnalyticsParam.Key.PROVIDER + +internal sealed class SwapAmountAnalyticEvents( + category: String, + event: String, + params: Map = mapOf(), +) : AnalyticsEvent(category = category, event = event, params = params) { + + data class ProviderSelectorClicked( + val categoryName: String, + ) : SwapAmountAnalyticEvents( + category = categoryName, + event = "Provider Clicked", + ) + + data class ProviderChosen( + val categoryName: String, + val providerName: String, + ) : SwapAmountAnalyticEvents( + category = categoryName, + event = "Provider Chosen", + params = mapOf(PROVIDER to providerName), + ) +} \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountClickIntents.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountClickIntents.kt index 7e5a767bb6..cf6b75586e 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountClickIntents.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountClickIntents.kt @@ -9,4 +9,5 @@ internal interface SwapAmountClickIntents : AmountScreenClickIntents { fun onInfoClick() fun onSelectTokenClick() fun onSeparatorClick() + fun onProviderClick() } \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt index 62e938cd22..8ff3dc702c 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/model/SwapAmountModel.kt @@ -2,6 +2,7 @@ package com.tangem.features.swap.v2.impl.amount.model import arrow.core.getOrElse import com.arkivanov.decompose.router.slot.SlotNavigation +import com.arkivanov.decompose.router.slot.activate import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter import com.tangem.common.ui.amountScreen.converters.MaxEnterAmountConverter @@ -10,6 +11,7 @@ import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary import com.tangem.common.ui.navigationButtons.NavigationButton import com.tangem.common.ui.navigationButtons.NavigationUM import com.tangem.common.ui.notifications.NotificationId +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer @@ -33,6 +35,7 @@ import com.tangem.domain.swap.usecase.GetSwapQuoteUseCase import com.tangem.domain.swap.usecase.SelectInitialPairUseCase import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase import com.tangem.domain.transaction.usecase.GetAllowanceUseCase +import com.tangem.features.send.v2.api.subcomponents.amount.analytics.CommonSendAmountAnalyticEvents import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorReloadTrigger import com.tangem.features.swap.v2.api.choosetoken.SwapChooseTokenNetworkListener import com.tangem.features.swap.v2.impl.R @@ -40,6 +43,7 @@ import com.tangem.features.swap.v2.impl.amount.SwapAmountBlockComponent.SwapChoo import com.tangem.features.swap.v2.impl.amount.SwapAmountComponentParams import com.tangem.features.swap.v2.impl.amount.SwapAmountReduceListener import com.tangem.features.swap.v2.impl.amount.SwapAmountUpdateListener +import com.tangem.features.swap.v2.impl.amount.analytics.SwapAmountAnalyticEvents import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM @@ -86,6 +90,7 @@ internal class SwapAmountModel @Inject constructor( private val swapAmountReduceListener: SwapAmountReduceListener, private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger, private val shouldShowNotificationUseCase: ShouldShowNotificationUseCase, + private val analyticsEventHandler: AnalyticsEventHandler, ) : Model(), SwapAmountClickIntents, SwapChooseProviderComponent.ModelCallback { private val params: SwapAmountComponentParams = paramsContainer.require() @@ -146,6 +151,12 @@ internal class SwapAmountModel @Inject constructor( } override fun onProviderResult(quoteUM: SwapQuoteUM) { + analyticsEventHandler.send( + SwapAmountAnalyticEvents.ProviderChosen( + categoryName = params.analyticsCategoryName, + providerName = quoteUM.provider?.name.orEmpty(), + ), + ) uiState.transformerUpdate( SwapAmountSelectQuoteTransformer( quoteUM = quoteUM, @@ -201,6 +212,9 @@ internal class SwapAmountModel @Inject constructor( } override fun onMaxValueClick() { + analyticsEventHandler.send( + CommonSendAmountAnalyticEvents.MaxAmountButtonClicked(categoryName = params.analyticsCategoryName), + ) uiState.transformerUpdate( SwapAmountValueMaxTransformer( primaryMaximumAmountBoundary = primaryMaximumAmountBoundary, @@ -221,6 +235,23 @@ internal class SwapAmountModel @Inject constructor( } override fun onAmountNext() { + val amountState = uiState.value.swapDirection.withSwapDirection( + onDirect = { uiState.value.primaryAmount.amountField }, + onReverse = { uiState.value.secondaryAmount.amountField }, + ) as? AmountState.Data + + amountState?.amountTextField?.isFiatValue?.let { isFiatSelected -> + analyticsEventHandler.send( + CommonSendAmountAnalyticEvents.SelectedCurrency( + categoryName = params.analyticsCategoryName, + type = if (isFiatSelected) { + CommonSendAmountAnalyticEvents.SelectedCurrencyType.AppCurrency + } else { + CommonSendAmountAnalyticEvents.SelectedCurrencyType.Token + }, + ), + ) + } saveResult() } @@ -266,10 +297,30 @@ internal class SwapAmountModel @Inject constructor( } } + override fun onProviderClick() { + val amountUM = uiState.value as? SwapAmountUM.Content ?: return + val selectedProvider = amountUM.selectedQuote.provider ?: return + val cryptoCurrency = params.secondaryCryptoCurrency ?: return + + analyticsEventHandler.send( + SwapAmountAnalyticEvents.ProviderSelectorClicked( + categoryName = params.analyticsCategoryName, + ), + ) + + bottomSheetNavigation.activate( + SwapChooseProviderConfig( + providers = amountUM.swapQuotes, + cryptoCurrency = cryptoCurrency, + selectedProvider = selectedProvider, + userCountry = userCountry, + ), + ) + } + private fun confirmSendWithSwapClose() { val amountParams = params as? SwapAmountComponentParams.AmountParams ?: return val amountFieldData = uiState.value.primaryAmount.amountField as? AmountState.Data - val callback = (params as? SwapAmountComponentParams.AmountParams)?.callback ?: return val primaryCryptoCurrencyStatus = (uiState.value as? SwapAmountUM.Content)?.primaryCryptoCurrencyStatus if (primaryCryptoCurrencyStatus != null) { @@ -625,7 +676,7 @@ internal class SwapAmountModel @Inject constructor( }, isEnabled = state.isPrimaryButtonEnabled, onClick = { - saveResult() + onAmountNext() params.callback.onNextClick() }, ), diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountClickIntentsStub.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountClickIntentsStub.kt index 1c60ad739a..4c55f4759a 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountClickIntentsStub.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/amount/ui/preview/SwapAmountClickIntentsStub.kt @@ -12,6 +12,8 @@ internal object SwapAmountClickIntentsStub : SwapAmountClickIntents { override fun onSeparatorClick() {} + override fun onProviderClick() {} + override fun onAmountValueChange(value: String) {} override fun onAmountPasteTriggerDismiss() {} diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt index e331657e56..ed82f2f734 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/DefaultSendWithSwapComponent.kt @@ -12,6 +12,7 @@ import com.arkivanov.decompose.router.stack.pop import com.arkivanov.decompose.value.ObserveLifecycleMode import com.arkivanov.decompose.value.subscribe import com.tangem.common.ui.navigationButtons.NavigationUM +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.model.getOrCreateModel @@ -21,6 +22,7 @@ import com.tangem.core.ui.decompose.getEmptyComposableContentComponent import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.swap.models.R import com.tangem.domain.swap.models.SwapDirection +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents import com.tangem.features.send.v2.api.subcomponents.destination.DestinationRoute import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponent import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams @@ -44,6 +46,7 @@ internal class DefaultSendWithSwapComponent @AssistedInject constructor( @Assisted private val params: SendWithSwapComponent.Params, private val sendDestinationComponentFactory: SendDestinationComponent.Factory, private val confirmComponentFactory: SendWithSwapConfirmComponent.Factory, + private val analyticsEventHandler: AnalyticsEventHandler, ) : SendWithSwapComponent, AppComponentContext by appComponentContext { private val stackNavigation = StackNavigation() @@ -80,15 +83,23 @@ internal class DefaultSendWithSwapComponent @AssistedInject constructor( componentScope.launch { when (val activeComponent = stack.active.instance) { is SwapAmountComponent -> { - // todo send with swap analytics + analyticsEventHandler.send( + CommonSendAnalyticEvents.AmountScreenOpened(categoryName = model.analyticCategoryName), + ) activeComponent.updateState(model.uiState.value.amountUM) } is SendDestinationComponent -> { - // todo send with swap analytics + analyticsEventHandler.send( + CommonSendAnalyticEvents.AddressScreenOpened(categoryName = model.analyticCategoryName), + ) activeComponent.updateState(model.uiState.value.destinationUM) } is SendWithSwapConfirmComponent -> if (model.currentRoute.value.isEditMode) { - // todo send with swap analytics + analyticsEventHandler.send( + CommonSendAnalyticEvents.ConfirmationScreenOpened( + categoryName = model.analyticCategoryName, + ), + ) activeComponent.updateState(model.uiState.value) } } @@ -125,7 +136,7 @@ internal class DefaultSendWithSwapComponent @AssistedInject constructor( title = resourceReference(R.string.common_send), currentRoute = model.currentRoute.filterIsInstance(), isBalanceHidingFlow = model.isBalanceHiddenFlow, - analyticsCategoryName = "", + analyticsCategoryName = model.analyticCategoryName, primaryCryptoCurrencyStatusFlow = model.primaryCryptoCurrencyStatusFlow, secondaryCryptoCurrency = null, swapDirection = SwapDirection.Direct, @@ -148,7 +159,7 @@ internal class DefaultSendWithSwapComponent @AssistedInject constructor( state = model.uiState.value.destinationUM, currentRoute = model.currentRoute.filterIsInstance(), isBalanceHidingFlow = model.isBalanceHiddenFlow, - analyticsCategoryName = "", + analyticsCategoryName = model.analyticCategoryName, title = resourceReference(R.string.send_recipient_label), userWalletId = params.userWalletId, cryptoCurrency = secondaryCryptoCurrency, @@ -167,7 +178,7 @@ internal class DefaultSendWithSwapComponent @AssistedInject constructor( appCurrency = model.appCurrency, userWallet = model.userWallet, callback = model, - analyticsCategoryName = "", + analyticsCategoryName = model.analyticCategoryName, primaryCryptoCurrencyStatusFlow = model.primaryCryptoCurrencyStatusFlow, primaryFeePaidCurrencyStatusFlow = model.primaryFeePaidCurrencyStatusFlow, swapDirection = SwapDirection.Direct, @@ -182,7 +193,7 @@ internal class DefaultSendWithSwapComponent @AssistedInject constructor( sendWithSwapUMFlow = model.uiState, currentRoute = model.currentRoute.filterIsInstance(), callback = model, - analyticsCategoryName = "", + analyticsCategoryName = model.analyticCategoryName, ), ) } diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/analytics/SendWithSwapAnalyticEvents.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/analytics/SendWithSwapAnalyticEvents.kt new file mode 100644 index 0000000000..ad1927f7cd --- /dev/null +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/analytics/SendWithSwapAnalyticEvents.kt @@ -0,0 +1,34 @@ +package com.tangem.features.swap.v2.impl.sendviaswap.analytics + +import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.AnalyticsParam.Key.PROVIDER +import com.tangem.core.analytics.models.AnalyticsParam.Key.RECEIVE_BLOCKCHAIN +import com.tangem.core.analytics.models.AnalyticsParam.Key.RECEIVE_TOKEN +import com.tangem.core.analytics.models.AnalyticsParam.Key.SEND_BLOCKCHAIN +import com.tangem.core.analytics.models.AnalyticsParam.Key.SEND_TOKEN +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents + +internal sealed class SendWithSwapAnalyticEvents( + event: String, + params: Map = mapOf(), +) : AnalyticsEvent(category = CommonSendAnalyticEvents.SEND_CATEGORY, event = event, params = params) { + + data class TransactionScreenOpened( + val providerName: String, + val feeType: AnalyticsParam.FeeType, + val fromToken: CryptoCurrency, + val toToken: CryptoCurrency, + ) : SendWithSwapAnalyticEvents( + event = "Send With Swap In Progress Screen Opened", + params = mapOf( + PROVIDER to providerName, + "Commission" to if (feeType is AnalyticsParam.FeeType.Normal) "Market" else "Fast", + SEND_TOKEN to fromToken.symbol, + RECEIVE_TOKEN to toToken.symbol, + SEND_BLOCKCHAIN to fromToken.network.name, + RECEIVE_BLOCKCHAIN to toToken.network.name, + ), + ) +} \ No newline at end of file diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt index 847f6d100b..5ebe7db905 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt @@ -9,6 +9,9 @@ import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.common.ui.navigationButtons.NavigationButton import com.tangem.common.ui.navigationButtons.NavigationUM +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.Basic import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer @@ -25,6 +28,8 @@ import com.tangem.domain.transaction.usecase.EstimateFeeUseCase import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase import com.tangem.features.send.v2.api.SendNotificationsComponent import com.tangem.features.send.v2.api.SendNotificationsComponent.Params.NotificationData +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents.SendScreenSource import com.tangem.features.send.v2.api.callbacks.FeeSelectorModelCallback import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM @@ -43,6 +48,7 @@ import com.tangem.features.swap.v2.impl.notifications.SwapNotificationsComponent import com.tangem.features.swap.v2.impl.notifications.SwapNotificationsUpdateListener import com.tangem.features.swap.v2.impl.notifications.SwapNotificationsUpdateTrigger import com.tangem.features.swap.v2.impl.sendviaswap.SendWithSwapRoute +import com.tangem.features.swap.v2.impl.sendviaswap.analytics.SendWithSwapAnalyticEvents import com.tangem.features.swap.v2.impl.sendviaswap.confirm.SendWithSwapConfirmComponent import com.tangem.features.swap.v2.impl.sendviaswap.confirm.model.transformers.SendWithSwapConfirmInitialStateTransformer import com.tangem.features.swap.v2.impl.sendviaswap.confirm.model.transformers.SendWithSwapConfirmationNotificationsTransformer @@ -73,6 +79,7 @@ internal class SendWithSwapConfirmModel @Inject constructor( private val feeSelectorReloadTrigger: FeeSelectorReloadTrigger, private val swapAlertFactory: SwapAlertFactory, private val appRouter: AppRouter, + private val analyticsEventHandler: AnalyticsEventHandler, swapTransactionSenderFactory: SwapTransactionSender.Factory, paramsContainer: ParamsContainer, ) : Model(), FeeSelectorModelCallback, SendNotificationsComponent.ModelCallback { @@ -185,10 +192,22 @@ internal class SendWithSwapConfirmModel @Inject constructor( } fun showEditAmount() { + analyticsEventHandler.send( + CommonSendAnalyticEvents.ScreenReopened( + categoryName = params.analyticsCategoryName, + source = SendScreenSource.Amount, + ), + ) router.push(SendWithSwapRoute.Amount(isEditMode = true)) } fun showEditDestination() { + analyticsEventHandler.send( + CommonSendAnalyticEvents.ScreenReopened( + categoryName = params.analyticsCategoryName, + source = SendScreenSource.Address, + ), + ) router.push(SendWithSwapRoute.Destination(isEditMode = true)) } @@ -260,7 +279,7 @@ internal class SendWithSwapConfirmModel @Inject constructor( txHash = txHash, networkId = primaryCurrencyStatus.currency.network.id, ).getOrNull().orEmpty() - + sendSuccessAnalytics() uiState.update { it.copy( confirmUM = ConfirmUM.Success( @@ -348,6 +367,33 @@ internal class SendWithSwapConfirmModel @Inject constructor( }.launchIn(modelScope) } + private fun sendSuccessAnalytics() { + val selectedProvider = confirmData.quote?.provider ?: return + val fromCurrency = confirmData.fromCryptoCurrencyStatus?.currency ?: return + val toCurrency = confirmData.toCryptoCurrencyStatus?.currency ?: return + val feeSelectorUM = uiState.value.feeSelectorUM as? FeeSelectorUM.Content ?: return + val feeType = feeSelectorUM.toAnalyticType() + + analyticsEventHandler.send( + SendWithSwapAnalyticEvents.TransactionScreenOpened( + providerName = selectedProvider.name, + feeType = feeType, + fromToken = fromCurrency, + toToken = toCurrency, + ), + ) + analyticsEventHandler.send( + Basic.TransactionSent( + sentFrom = AnalyticsParam.TxSentFrom.SendWithSwap( + blockchain = fromCurrency.network.name, + token = fromCurrency.symbol, + feeType = feeType, + ), + memoType = Basic.TransactionSent.MemoType.Null, + ), + ) + } + private fun configConfirmNavigation() { combine( flow = uiState, diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/model/SendWithSwapModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/model/SendWithSwapModel.kt index e370965fe4..ede2387f9d 100644 --- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/model/SendWithSwapModel.kt +++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/model/SendWithSwapModel.kt @@ -21,6 +21,7 @@ import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.wallets.usecase.GetUserWalletUseCase +import com.tangem.features.send.v2.api.analytics.CommonSendAnalyticEvents import com.tangem.features.send.v2.api.entity.FeeSelectorUM import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponent import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM @@ -58,6 +59,7 @@ internal class SendWithSwapModel @Inject constructor( private val params: SendWithSwapComponent.Params = paramsContainer.require() + val analyticCategoryName = CommonSendAnalyticEvents.SEND_CATEGORY val initialRoute = SendWithSwapRoute.Amount(false) val currentRoute = MutableStateFlow(initialRoute)