From abf6fc7d697de469dc5edd6c91d161fdeeaa36d7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 8 Jul 2026 14:20:04 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../models/event/MainScreenAnalyticsEvent.kt | 4 ++ .../models/event/TransferAnalyticsEvent.kt | 23 ++++++++++ .../analytics/TokenScreenAnalyticsEvent.kt | 24 +++++++++++ .../analytics/ManageFundsAnalyticsEvent.kt | 5 +-- .../managefunds/model/ManageFundsModel.kt | 42 +++++++++++++++---- .../details/MarketsTokenDetailsModel.kt | 1 + .../analytics/MarketDetailsAnalyticsEvent.kt | 5 +++ .../tokendetails/model/TokenDetailsModel.kt | 19 +++++++++ .../transformer/UpdateTransferTransformer.kt | 7 ++++ .../UpdateTransferTransformerTest.kt | 9 ++++ .../model/intents/WalletClickIntents.kt | 1 + 11 files changed, 129 insertions(+), 11 deletions(-) create mode 100644 core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/TransferAnalyticsEvent.kt 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 b09ed9c19d..aeea25ea21 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 @@ -42,6 +42,10 @@ sealed class MainScreenAnalyticsEvent( event = "Button - Add Funds", ) + class ButtonTransfer : MainScreenAnalyticsEvent( + event = "Button - Transfer", + ) + class LimitsClicked : MainScreenAnalyticsEvent( event = "Limits Clicked", ) diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/TransferAnalyticsEvent.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/TransferAnalyticsEvent.kt new file mode 100644 index 0000000000..c197e44afd --- /dev/null +++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/TransferAnalyticsEvent.kt @@ -0,0 +1,23 @@ +package com.tangem.core.analytics.models.event + +import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.core.analytics.models.AnalyticsParam + +sealed class TransferAnalyticsEvent( + event: String, + params: Map = emptyMap(), +) : AnalyticsEvent(category = "Transfer", event = event, params = params) { + + class MethodScreenOpened(source: AnalyticsParam.ScreensSources) : TransferAnalyticsEvent( + event = "Method Screen Opened", + params = mapOf(AnalyticsParam.SOURCE to source.value), + ) + + class ButtonSell : TransferAnalyticsEvent(event = "Button - Sell") + + class ButtonSwap : TransferAnalyticsEvent(event = "Button - Swap") + + class ButtonSend : TransferAnalyticsEvent(event = "Button - Send") + + class ButtonSwapAndSend : TransferAnalyticsEvent(event = "Button - Swap&Send") +} \ No newline at end of file diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenScreenAnalyticsEvent.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenScreenAnalyticsEvent.kt index ba7056df00..cc59832b8d 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenScreenAnalyticsEvent.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenScreenAnalyticsEvent.kt @@ -136,6 +136,30 @@ sealed class TokenScreenAnalyticsEvent( status = status, blockchain = blockchain, ) + + class ButtonAddFunds( + token: String, + blockchain: String, + derivationIndex: Int? = null, + ) : ButtonWithParams( + event = "Button - Add Funds", + token = token, + status = null, + blockchain = blockchain, + derivationIndex = derivationIndex, + ) + + class ButtonTransfer( + token: String, + blockchain: String, + derivationIndex: Int? = null, + ) : ButtonWithParams( + event = "Button - Transfer", + token = token, + status = null, + blockchain = blockchain, + derivationIndex = derivationIndex, + ) } class ActionButtonDisabled( diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/analytics/ManageFundsAnalyticsEvent.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/analytics/ManageFundsAnalyticsEvent.kt index 19c26683b6..e59d62bbe1 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/analytics/ManageFundsAnalyticsEvent.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/analytics/ManageFundsAnalyticsEvent.kt @@ -8,9 +8,9 @@ internal sealed class ManageFundsAnalyticsEvent( params: Map = emptyMap(), ) : AnalyticsEvent(category = CATEGORY, event = event, params = params) { - class MethodScreenOpened(source: String) : ManageFundsAnalyticsEvent( + class MethodScreenOpened(source: AnalyticsParam.ScreensSources) : ManageFundsAnalyticsEvent( event = "Method Screen Opened", - params = mapOf(AnalyticsParam.SOURCE to source), + params = mapOf(AnalyticsParam.SOURCE to source.value), ) class ButtonBuy : ManageFundsAnalyticsEvent(event = "Button - Buy") @@ -21,6 +21,5 @@ internal sealed class ManageFundsAnalyticsEvent( companion object { private const val CATEGORY = "Add Funds" - const val SOURCE_MAIN_SCREEN = "Main Screen" } } \ No newline at end of file diff --git a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/model/ManageFundsModel.kt b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/model/ManageFundsModel.kt index a1178676e5..94b88e2469 100644 --- a/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/model/ManageFundsModel.kt +++ b/features/common-features/impl/src/main/java/com/tangem/features/commonfeatures/impl/managefunds/model/ManageFundsModel.kt @@ -7,6 +7,8 @@ import com.tangem.common.routing.AppRouter import com.tangem.common.ui.markets.action.CryptoCurrencyData import com.tangem.common.ui.markets.action.TokenActionsBSContentUM import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.event.TransferAnalyticsEvent import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer @@ -150,11 +152,20 @@ internal class ManageFundsModel @Inject constructor( } override fun onQuickActionClick(action: TokenActionsBSContentUM.Action, shouldDismiss: Boolean) { - val event = when (action) { - TokenActionsBSContentUM.Action.Buy -> ManageFundsAnalyticsEvent.ButtonBuy() - TokenActionsBSContentUM.Action.Exchange -> ManageFundsAnalyticsEvent.ButtonSwap() - TokenActionsBSContentUM.Action.Receive -> ManageFundsAnalyticsEvent.ButtonReceive() - else -> null + val event = when (flowType) { + ManageFundsComponent.FlowType.AddFunds -> when (action) { + TokenActionsBSContentUM.Action.Buy -> ManageFundsAnalyticsEvent.ButtonBuy() + TokenActionsBSContentUM.Action.Exchange -> ManageFundsAnalyticsEvent.ButtonSwap() + TokenActionsBSContentUM.Action.Receive -> ManageFundsAnalyticsEvent.ButtonReceive() + else -> null + } + ManageFundsComponent.FlowType.Transfer -> when (action) { + TokenActionsBSContentUM.Action.Send -> TransferAnalyticsEvent.ButtonSend() + TokenActionsBSContentUM.Action.Exchange -> TransferAnalyticsEvent.ButtonSwap() + TokenActionsBSContentUM.Action.SendWithSwap -> TransferAnalyticsEvent.ButtonSwapAndSend() + TokenActionsBSContentUM.Action.Sell -> TransferAnalyticsEvent.ButtonSell() + else -> null + } } event?.let { analyticsEventHandler.send(it) } if (shouldDismiss) { @@ -178,9 +189,7 @@ internal class ManageFundsModel @Inject constructor( private fun initChooseToken(mode: ManageFundsComponent.LaunchMode.ChooseToken) { chooseTokenBridge.selectWalletTab(mode.userWalletId) - analyticsEventHandler.send( - ManageFundsAnalyticsEvent.MethodScreenOpened(source = ManageFundsAnalyticsEvent.SOURCE_MAIN_SCREEN), - ) + sendMethodScreenOpenedEvent() replaceRoot(UiRoute.ChooseToken) modelScope.launch { chooseTokenBridge.onCurrencyChosen.receiveAsFlow().collect(::openTokenActionsFromBridge) @@ -209,6 +218,7 @@ internal class ManageFundsModel @Inject constructor( params.onDismiss() return@launch } + sendMethodScreenOpenedEvent() tokenActionsTrigger.value = TokenActionsRequest(wallet, match.first, match.second) replaceRoot(tokenActionsRoute(match.second)) } @@ -217,6 +227,9 @@ internal class ManageFundsModel @Inject constructor( private fun initFilteredByRawId(mode: ManageFundsComponent.LaunchMode.FilteredByRawId) { modelScope.launch { val entries = collectFilteredEntries(mode.rawCurrencyId) + if (entries.isNotEmpty()) { + sendMethodScreenOpenedEvent() + } when (entries.size) { 0 -> params.onDismiss() 1 -> { @@ -250,6 +263,19 @@ internal class ManageFundsModel @Inject constructor( } } + private fun sendMethodScreenOpenedEvent() { + val source = when (launchMode) { + is ManageFundsComponent.LaunchMode.ChooseToken -> AnalyticsParam.ScreensSources.Main + is ManageFundsComponent.LaunchMode.TokenActionsOnly -> AnalyticsParam.ScreensSources.Token + is ManageFundsComponent.LaunchMode.FilteredByRawId -> AnalyticsParam.ScreensSources.Market + } + val event = when (flowType) { + ManageFundsComponent.FlowType.AddFunds -> ManageFundsAnalyticsEvent.MethodScreenOpened(source = source) + ManageFundsComponent.FlowType.Transfer -> TransferAnalyticsEvent.MethodScreenOpened(source = source) + } + analyticsEventHandler.send(event) + } + private fun openTokenActionsFromBridge(result: ChooseTokenResult) { val account = result.account as? AccountStatus.CryptoPortfolio ?: return openTokenActions( diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/MarketsTokenDetailsModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/MarketsTokenDetailsModel.kt index d5976c19fa..e79b45966a 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/MarketsTokenDetailsModel.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/MarketsTokenDetailsModel.kt @@ -393,6 +393,7 @@ internal class MarketsTokenDetailsModel @Inject constructor( } fun openAddFunds(rawCurrencyId: com.tangem.domain.models.currency.CryptoCurrency.RawID) { + analyticsEventHandler.send(analyticsEventBuilder.addFundsClicked()) addFundsSheetNavigation.activate(AddFundsSlotRoute(rawCurrencyId = rawCurrencyId)) } diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/analytics/MarketDetailsAnalyticsEvent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/analytics/MarketDetailsAnalyticsEvent.kt index 1ae3f86fab..907705a88b 100644 --- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/analytics/MarketDetailsAnalyticsEvent.kt +++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/market/details/analytics/MarketDetailsAnalyticsEvent.kt @@ -70,6 +70,11 @@ internal class MarketDetailsAnalyticsEvent( event = "Button - Share", params = mapOf("Token" to token.symbol), ) + + fun addFundsClicked() = MarketDetailsAnalyticsEvent( + event = "Button - Add Funds", + params = mapOf("Token" to token.symbol), + ) } enum class IntervalType(val source: String) { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt index f4b55056ba..73fd82bed5 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt @@ -20,6 +20,7 @@ import com.tangem.common.ui.userwallet.ext.walletInterationIcon import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.event.OfframpAnalyticsEvent +import com.tangem.core.analytics.models.event.TransferAnalyticsEvent import com.tangem.core.decompose.di.GlobalUiMessageSender import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model @@ -368,6 +369,7 @@ internal class TokenDetailsModel @Inject constructor( actions = state.states, networkSource = networkSource, clickIntents = this@TokenDetailsModel, + analyticsEventHandler = analyticsEventsHandler, onActionDispatched = bottomSheetNavigation::dismiss, ), ) @@ -547,6 +549,13 @@ internal class TokenDetailsModel @Inject constructor( } override fun onAddFundsClick() { + analyticsEventsHandler.send( + TokenScreenAnalyticsEvent.ButtonWithParams.ButtonAddFunds( + token = cryptoCurrency.symbol, + blockchain = cryptoCurrency.network.name, + derivationIndex = getAccountIndexOrNull(), + ), + ) bottomSheetNavigation.activate( TokenDetailsBottomSheetConfig.AddFunds( userWalletId = userWalletId, @@ -556,6 +565,13 @@ internal class TokenDetailsModel @Inject constructor( } override fun onTransferClick() { + analyticsEventsHandler.send( + TokenScreenAnalyticsEvent.ButtonWithParams.ButtonTransfer( + token = cryptoCurrency.symbol, + blockchain = cryptoCurrency.network.name, + derivationIndex = getAccountIndexOrNull(), + ), + ) val amount = cryptoCurrencyStatus?.value?.amount if (amount == null || amount.signum() <= 0) { handleUnavailabilityReason( @@ -565,6 +581,9 @@ internal class TokenDetailsModel @Inject constructor( ) return } + analyticsEventsHandler.send( + TransferAnalyticsEvent.MethodScreenOpened(source = AnalyticsParam.ScreensSources.Token), + ) bottomSheetNavigation.activate(TokenDetailsBottomSheetConfig.Transfer) } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateTransferTransformer.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateTransferTransformer.kt index 46f2b89ec5..c33785b3a3 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateTransferTransformer.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateTransferTransformer.kt @@ -1,5 +1,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.transformer +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.event.TransferAnalyticsEvent import com.tangem.domain.models.StatusSource import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason import com.tangem.domain.tokens.model.TokenActionsState @@ -13,6 +15,7 @@ internal class UpdateTransferTransformer( private val actions: List, private val networkSource: StatusSource, private val clickIntents: TokenDetailsClickIntents, + private val analyticsEventHandler: AnalyticsEventHandler, private val onActionDispatched: () -> Unit, ) : Transformer { @@ -28,6 +31,7 @@ internal class UpdateTransferTransformer( isLoading = action.unavailabilityReason.isOutdatedLoading(), isEnabled = action.unavailabilityReason == ScenarioUnavailabilityReason.None, onClick = { + analyticsEventHandler.send(TransferAnalyticsEvent.ButtonSend()) onActionDispatched() clickIntents.onSendClick(action.unavailabilityReason) }, @@ -38,6 +42,7 @@ internal class UpdateTransferTransformer( isLoading = action.unavailabilityReason.isLoading, isEnabled = action.unavailabilityReason == ScenarioUnavailabilityReason.None, onClick = { + analyticsEventHandler.send(TransferAnalyticsEvent.ButtonSwap()) onActionDispatched() clickIntents.onSwapFromClick(action.unavailabilityReason) }, @@ -53,6 +58,7 @@ internal class UpdateTransferTransformer( isLoading = false, isEnabled = true, onClick = { + analyticsEventHandler.send(TransferAnalyticsEvent.ButtonSwapAndSend()) onActionDispatched() clickIntents.onSwapAndSendClick(it.unavailabilityReason) }, @@ -63,6 +69,7 @@ internal class UpdateTransferTransformer( isLoading = action.unavailabilityReason.isOutdatedLoading(), isEnabled = action.unavailabilityReason == ScenarioUnavailabilityReason.None, onClick = { + analyticsEventHandler.send(TransferAnalyticsEvent.ButtonSell()) onActionDispatched() clickIntents.onSellClick(action.unavailabilityReason) }, diff --git a/features/tokendetails/impl/src/test/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateTransferTransformerTest.kt b/features/tokendetails/impl/src/test/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateTransferTransformerTest.kt index e50e0b4c1a..3fcf8b3ee0 100644 --- a/features/tokendetails/impl/src/test/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateTransferTransformerTest.kt +++ b/features/tokendetails/impl/src/test/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/transformer/UpdateTransferTransformerTest.kt @@ -1,6 +1,8 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.transformer import com.google.common.truth.Truth.assertThat +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.event.TransferAnalyticsEvent import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.extensions.stringReference @@ -24,6 +26,7 @@ import org.junit.jupiter.api.Test class UpdateTransferTransformerTest { private val clickIntents: TokenDetailsClickIntents = mockk(relaxed = true) + private val analyticsEventHandler: AnalyticsEventHandler = mockk(relaxed = true) private val onActionDispatched: () -> Unit = mockk(relaxed = true) @Test @@ -112,6 +115,7 @@ class UpdateTransferTransformerTest { // THEN verifyOrder { + analyticsEventHandler.send(ofType()) onActionDispatched.invoke() clickIntents.onSendClick(ScenarioUnavailabilityReason.None) } @@ -130,6 +134,7 @@ class UpdateTransferTransformerTest { // THEN verifyOrder { + analyticsEventHandler.send(ofType()) onActionDispatched.invoke() clickIntents.onSellClick(ScenarioUnavailabilityReason.None) } @@ -225,6 +230,7 @@ class UpdateTransferTransformerTest { // THEN verifyOrder { + analyticsEventHandler.send(ofType()) onActionDispatched.invoke() clickIntents.onSwapFromClick(ScenarioUnavailabilityReason.None) } @@ -347,6 +353,7 @@ class UpdateTransferTransformerTest { // Assert verifyOrder { + analyticsEventHandler.send(ofType()) onActionDispatched.invoke() clickIntents.onSwapAndSendClick(ScenarioUnavailabilityReason.None) } @@ -405,6 +412,7 @@ class UpdateTransferTransformerTest { // THEN verify(exactly = 0) { onActionDispatched.invoke() } verify(exactly = 0) { clickIntents.onSendClick(any()) } + verify(exactly = 0) { analyticsEventHandler.send(any()) } } private fun createTransformer( @@ -414,6 +422,7 @@ class UpdateTransferTransformerTest { actions = actions, networkSource = networkSource, clickIntents = clickIntents, + analyticsEventHandler = analyticsEventHandler, onActionDispatched = onActionDispatched, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletClickIntents.kt index d983fe385c..0ed6aa49de 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletClickIntents.kt @@ -125,6 +125,7 @@ internal class WalletClickIntents @Inject constructor( } fun onTransferClick(userWalletId: UserWalletId) { + analyticsEventHandler.send(MainScreenAnalyticsEvent.ButtonTransfer()) router.openTransfer(userWalletId) }