From 54e094abac8c48dec3f74fd61cc33b4bc227349e Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 21 Aug 2025 17:21:54 +0200 Subject: [PATCH] Updated on 2026-08-14 --- .../core/analytics/models/AnalyticsParam.kt | 6 ++ .../TokenReceiveNewAnalyticsEvent.kt | 59 +++++++++++++++++++ .../v2/send/analytics/SendAnalyticEvents.kt | 6 ++ .../v2/send/analytics/SendAnalyticHelper.kt | 11 ++++ .../component/DefaultTokenReceiveComponent.kt | 1 + .../component/TokenReceiveAssetsComponent.kt | 1 + .../model/TokenReceiveAssetsModel.kt | 24 ++++++++ .../tokenreceive/model/TokenReceiveModel.kt | 41 +++++++++++-- 8 files changed, 145 insertions(+), 4 deletions(-) create mode 100644 domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenReceiveNewAnalyticsEvent.kt 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 9257e90ce8..79c0d883c0 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 @@ -187,6 +187,10 @@ sealed class AnalyticsParam { Pending(value = "Pending"), } + enum class EnsStatus(val value: String) { + EMPTY("Empty"), FULL("Full") + } + companion object Key { const val BLOCKCHAIN = "Blockchain" const val TOKEN_PARAM = "Token" @@ -235,5 +239,7 @@ sealed class AnalyticsParam { const val SEND_BLOCKCHAIN = "Send Blockchain" const val RECEIVE_BLOCKCHAIN = "Receive Blockchain" const val CHOSEN_TOKEN = "Token Chosen" + const val ENS = "ENS" + const val ENS_ADDRESS = "ENS Address" } } \ No newline at end of file diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenReceiveNewAnalyticsEvent.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenReceiveNewAnalyticsEvent.kt new file mode 100644 index 0000000000..424ac7f642 --- /dev/null +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/TokenReceiveNewAnalyticsEvent.kt @@ -0,0 +1,59 @@ +package com.tangem.domain.tokens.model.analytics + +import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.AnalyticsParam.Key.BLOCKCHAIN +import com.tangem.core.analytics.models.AnalyticsParam.Key.ENS +import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM + +sealed class TokenReceiveNewAnalyticsEvent( + event: String, + params: Map = mapOf(), +) : AnalyticsEvent("Token / Receive", event, params) { + + class ReceiveScreenOpened( + token: String, + blockchainName: String, + ensStatus: AnalyticsParam.EnsStatus, + ) : TokenReceiveNewAnalyticsEvent( + event = "Receive Screen Opened", + params = mapOf( + TOKEN_PARAM to token, + BLOCKCHAIN to blockchainName, + ENS to ensStatus.value, + ), + ) + + class ButtonCopyAddress( + token: String, + blockchainName: String, + ) : TokenReceiveNewAnalyticsEvent( + event = "Button - Copy Address", + params = mapOf( + TOKEN_PARAM to token, + BLOCKCHAIN to blockchainName, + ), + ) + + class ButtonCopyEns( + token: String, + blockchainName: String, + ) : TokenReceiveNewAnalyticsEvent( + event = "Button - ENS", + params = mapOf( + TOKEN_PARAM to token, + BLOCKCHAIN to blockchainName, + ), + ) + + class QrScreenOpened( + token: String, + blockchainName: String, + ) : TokenReceiveNewAnalyticsEvent( + event = "QR Screen Opened", + params = mapOf( + TOKEN_PARAM to token, + BLOCKCHAIN to blockchainName, + ), + ) +} \ No newline at end of file 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 3a705b2270..c486cce7a4 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 @@ -3,6 +3,7 @@ package com.tangem.features.send.v2.send.analytics import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.AnalyticsParam.Key.BLOCKCHAIN +import com.tangem.core.analytics.models.AnalyticsParam.Key.ENS_ADDRESS import com.tangem.core.analytics.models.AnalyticsParam.Key.FEE_TYPE import com.tangem.core.analytics.models.AnalyticsParam.Key.NONCE import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM @@ -23,6 +24,7 @@ internal sealed class SendAnalyticEvents( val feeType: AnalyticsParam.FeeType, val blockchain: String, val nonceNotEmpty: Boolean, + private val ensStatus: AnalyticsParam.EnsStatus, ) : SendAnalyticEvents( event = "Transaction Sent Screen Opened", params = mapOf( @@ -30,6 +32,10 @@ internal sealed class SendAnalyticEvents( FEE_TYPE to feeType.value, BLOCKCHAIN to blockchain, NONCE to nonceNotEmpty.toString().capitalize(), + ENS_ADDRESS to when (ensStatus) { + AnalyticsParam.EnsStatus.EMPTY -> false.toString() + AnalyticsParam.EnsStatus.FULL -> true.toString() + }, ), ) diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticHelper.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticHelper.kt index ab9490ce77..ad01b7a5c9 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticHelper.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/analytics/SendAnalyticHelper.kt @@ -28,6 +28,7 @@ internal class SendAnalyticHelper @Inject constructor( feeType = feeType, blockchain = cryptoCurrency.network.name, nonceNotEmpty = feeSelectorUM.nonce != null, + ensStatus = getEnsStatus(sendUM), ), ) analyticsEventHandler.send( @@ -52,4 +53,14 @@ internal class SendAnalyticHelper @Inject constructor( else -> Basic.TransactionSent.MemoType.Null } } + + private fun getEnsStatus(sendUM: SendUM): AnalyticsParam.EnsStatus { + val blockchainAddressForEns = + (sendUM.destinationUM as? DestinationUM.Content)?.addressTextField?.blockchainAddress + return if (blockchainAddressForEns != null) { + AnalyticsParam.EnsStatus.FULL + } else { + AnalyticsParam.EnsStatus.EMPTY + } + } } \ No newline at end of file diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/DefaultTokenReceiveComponent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/DefaultTokenReceiveComponent.kt index e467be69ac..83b0cf3b6f 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/DefaultTokenReceiveComponent.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/DefaultTokenReceiveComponent.kt @@ -93,6 +93,7 @@ internal class DefaultTokenReceiveComponent @AssistedInject constructor( notificationConfigs = model.state.value.notificationConfigs, showMemoDisclaimer = model.params.config.showMemoDisclaimer, fullName = model.params.config.cryptoCurrency.network.name, + tokenName = model.getTokenName(), ), ) TokenReceiveRoutes.Warning -> TokenReceiveWarningComponent( diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/TokenReceiveAssetsComponent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/TokenReceiveAssetsComponent.kt index de8c7e2578..7ece6e356d 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/TokenReceiveAssetsComponent.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/TokenReceiveAssetsComponent.kt @@ -39,5 +39,6 @@ internal class TokenReceiveAssetsComponent( val onDismiss: () -> Unit, val showMemoDisclaimer: Boolean, val fullName: String, + val tokenName: String, ) } \ No newline at end of file diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveAssetsModel.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveAssetsModel.kt index 04b0caf856..bf37b011e3 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveAssetsModel.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveAssetsModel.kt @@ -1,10 +1,14 @@ package com.tangem.features.tokenreceive.model import androidx.compose.runtime.Stable +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.domain.tokens.model.analytics.TokenReceiveNewAnalyticsEvent import com.tangem.features.tokenreceive.component.TokenReceiveAssetsComponent +import com.tangem.features.tokenreceive.entity.ReceiveAddress import com.tangem.features.tokenreceive.ui.state.ReceiveAssetsUM import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.MutableStateFlow @@ -16,10 +20,21 @@ import javax.inject.Inject internal class TokenReceiveAssetsModel @Inject constructor( paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, + analyticsEventHandler: AnalyticsEventHandler, ) : Model() { private val params = paramsContainer.require() + init { + analyticsEventHandler.send( + TokenReceiveNewAnalyticsEvent.ReceiveScreenOpened( + token = params.tokenName, + blockchainName = params.fullName, + ensStatus = configureEnsStatus(), + ), + ) + } + internal val state: StateFlow field = MutableStateFlow( ReceiveAssetsUM( @@ -32,4 +47,13 @@ internal class TokenReceiveAssetsModel @Inject constructor( fullName = params.fullName, ), ) + + private fun configureEnsStatus(): AnalyticsParam.EnsStatus { + val hasEnsAddress = params.addresses.values.any { it.type == ReceiveAddress.Type.Ens } + return if (hasEnsAddress) { + AnalyticsParam.EnsStatus.FULL + } else { + AnalyticsParam.EnsStatus.EMPTY + } + } } \ No newline at end of file diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveModel.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveModel.kt index 3313bf1cb9..183d11d046 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveModel.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveModel.kt @@ -3,6 +3,7 @@ package com.tangem.features.tokenreceive.model import com.arkivanov.decompose.router.stack.StackNavigation import com.arkivanov.decompose.router.stack.push import com.tangem.common.ui.notifications.NotificationUM +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 @@ -19,6 +20,7 @@ import com.tangem.domain.models.ReceiveAddressModel import com.tangem.domain.models.ens.EnsAddress import com.tangem.domain.models.network.Network import com.tangem.domain.tokens.SaveViewedTokenReceiveWarningUseCase +import com.tangem.domain.tokens.model.analytics.TokenReceiveNewAnalyticsEvent import com.tangem.domain.transaction.usecase.GetReverseResolvedEnsAddressUseCase import com.tangem.features.tokenreceive.TokenReceiveComponent import com.tangem.features.tokenreceive.component.TokenReceiveModelCallback @@ -35,6 +37,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import javax.inject.Inject +@Suppress("LongParameterList") @ModelScoped internal class TokenReceiveModel @Inject constructor( private val clipboardManager: ClipboardManager, @@ -43,6 +46,7 @@ internal class TokenReceiveModel @Inject constructor( private val saveViewedTokenReceiveWarningUseCase: SaveViewedTokenReceiveWarningUseCase, paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, + private val analyticsEventHandler: AnalyticsEventHandler, ) : Model(), TokenReceiveModelCallback { private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter) @@ -61,11 +65,18 @@ internal class TokenReceiveModel @Inject constructor( } override fun onQrCodeClick(id: Int) { + analyticsEventHandler.send( + TokenReceiveNewAnalyticsEvent.QrScreenOpened( + token = getTokenName(), + blockchainName = params.config.cryptoCurrency.network.name, + ), + ) stackNavigation.push(configuration = TokenReceiveRoutes.QrCode(addressId = id)) } override fun onCopyClick(id: Int) { val addressToCopy = state.value.addresses[id] ?: return + sendCopyActionAnalytic(addressToCopy) clipboardManager.setText(text = addressToCopy.value, isSensitive = true) } @@ -85,6 +96,13 @@ internal class TokenReceiveModel @Inject constructor( } } + internal fun getTokenName(): String { + return when (val asset = params.config.asset) { + Asset.Currency -> params.config.cryptoCurrency.symbol + Asset.NFT -> asset.name + } + } + private fun mapAddresses(addresses: List): ImmutableMap { return buildMap { addresses.mapIndexed { index, model -> @@ -151,10 +169,7 @@ internal class TokenReceiveModel @Inject constructor( title = resourceReference( R.string.receive_bottom_sheet_warning_title, wrappedList( - when (val asset = params.config.asset) { - Asset.Currency -> params.config.cryptoCurrency.symbol - Asset.NFT -> asset.name - }, + getTokenName(), params.config.cryptoCurrency.network.name, ), ), @@ -183,4 +198,22 @@ internal class TokenReceiveModel @Inject constructor( notificationConfigs = getNotifications().toImmutableList(), ) } + + private fun sendCopyActionAnalytic(receiveAddress: ReceiveAddress) { + val event = when (receiveAddress.type) { + is ReceiveAddress.Type.Default -> { + TokenReceiveNewAnalyticsEvent.ButtonCopyAddress( + token = getTokenName(), + blockchainName = params.config.cryptoCurrency.network.name, + ) + } + ReceiveAddress.Type.Ens -> { + TokenReceiveNewAnalyticsEvent.ButtonCopyEns( + token = getTokenName(), + blockchainName = params.config.cryptoCurrency.network.name, + ) + } + } + analyticsEventHandler.send(event) + } } \ No newline at end of file