Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-09 17:22:28 +05:00
parent aeb60e0cec
commit f2f10fe950
23 changed files with 182 additions and 125 deletions

View file

@ -12,12 +12,6 @@ sealed class AnalyticsParam {
class Amount(amount: com.tangem.blockchain.common.Amount) : CurrencyType(amount.currencySymbol) class Amount(amount: com.tangem.blockchain.common.Amount) : CurrencyType(amount.currencySymbol)
} }
sealed class CardBalanceState(val value: String) {
data object Empty : CardBalanceState("Empty")
data object Full : CardBalanceState("Full")
companion object
}
sealed class RateApp(val value: String) { sealed class RateApp(val value: String) {
data object Liked : RateApp("Liked") data object Liked : RateApp("Liked")
data object Closed : RateApp("Close") data object Closed : RateApp("Close")

View file

@ -11,85 +11,5 @@ sealed class Onboarding(
params: Map<String, String> = emptyMap(), params: Map<String, String> = emptyMap(),
) : AnalyticsEvent(category, event, params) { ) : AnalyticsEvent(category, event, params) {
class Started : Onboarding("Onboarding", "Onboarding Started")
class Finished : Onboarding("Onboarding", "Onboarding Finished") class Finished : Onboarding("Onboarding", "Onboarding Finished")
sealed class CreateWallet(
event: String,
params: Map<String, String> = emptyMap(),
) : Onboarding("Onboarding / Create Wallet", event, params) {
class ScreenOpened : CreateWallet("Create Wallet Screen Opened")
class ButtonCreateWallet : CreateWallet("Button - Create Wallet")
class WalletCreatedSuccessfully(
creationType: AnalyticsParam.WalletCreationType = AnalyticsParam.WalletCreationType.PrivateKey,
seedPhraseLength: Int? = null,
) : CreateWallet(
event = "Wallet Created Successfully",
params = buildMap {
put(AnalyticsParam.CREATION_TYPE, creationType.value)
if (seedPhraseLength != null) put(AnalyticsParam.SEED_PHRASE_LENGTH, seedPhraseLength.toString())
},
)
}
sealed class Backup(
event: String,
params: Map<String, String> = emptyMap(),
) : Onboarding("Onboarding / Backup", event, params) {
class ScreenOpened : Backup("Backup Screen Opened")
class Started : Backup("Backup Started")
class Skipped : Backup("Backup Skipped")
class SettingAccessCodeStarted : Backup("Setting Access Code Started")
class AccessCodeEntered : Backup("Access Code Entered")
class AccessCodeReEntered : Backup("Access Code Re-entered")
class Finished(cardsCount: Int) : Backup(
event = "Backup Finished",
params = mapOf("Cards count" to "$cardsCount"),
)
object ResetCancelEvent : Backup(
event = "Reset Card Notification",
params = mapOf("Option" to "Cancel"),
)
object ResetPerformEvent : Backup(
event = "Reset Card Notification",
params = mapOf("Option" to "Reset"),
)
}
sealed class Topup(
event: String,
params: Map<String, String> = emptyMap(),
) : Onboarding("Onboarding / Top Up", event, params) {
class ScreenOpened : Topup("Activation Screen Opened")
class ButtonBuyCrypto(currency: AnalyticsParam.CurrencyType) : Topup(
event = "Button - Buy Crypto",
params = mapOf(AnalyticsParam.CURRENCY to currency.value),
)
class ButtonShowWalletAddress : Topup("Button - Show the Wallet Address")
}
sealed class Twins(
event: String,
params: Map<String, String> = emptyMap(),
) : Onboarding("Onboarding / Twins", event, params) {
class ScreenOpened : Twins("Twinning Screen Opened")
class SetupStarted : Twins("Twin Setup Started")
class SetupFinished : Twins("Twin Setup Finished")
}
class EnableBiometrics(state: AnalyticsParam.OnOffState) : Onboarding(
category = "Onboarding / Biometric",
event = "Enable Biometric",
params = mapOf("State" to state.value),
)
} }

View file

@ -164,7 +164,7 @@ internal class LegacyScanProcessor @Inject constructor(
) { ) {
if (error is TangemSdkError.CardVerificationFailed) { if (error is TangemSdkError.CardVerificationFailed) {
analyticsEventHandler.send( analyticsEventHandler.send(
event = OnboardingAnalyticsEvent.Onboarding.OfflineAttestationFailed( event = OnboardingAnalyticsEvent.Error.OfflineAttestationFailed(
analyticsSource, analyticsSource,
), ),
) )

View file

@ -529,7 +529,9 @@ internal class ChildFactory @Inject constructor(
is AppRoute.CreateMobileWallet -> { is AppRoute.CreateMobileWallet -> {
createComponentChild( createComponentChild(
context = context, context = context,
params = Unit, params = CreateMobileWalletComponent.Params(
source = route.source,
),
componentFactory = createMobileWalletComponentFactory, componentFactory = createMobileWalletComponentFactory,
) )
} }

View file

@ -346,7 +346,9 @@ sealed class AppRoute(val path: String) : Route {
object CreateHardwareWallet : AppRoute(path = "/create_hardware_wallet") object CreateHardwareWallet : AppRoute(path = "/create_hardware_wallet")
@Serializable @Serializable
object CreateMobileWallet : AppRoute(path = "/create_mobile_wallet") data class CreateMobileWallet(
val source: String,
) : AppRoute(path = "/create_mobile_wallet")
@Serializable @Serializable
data class UpgradeWallet( data class UpgradeWallet(

View file

@ -11,11 +11,6 @@ sealed class AnalyticsParam {
companion object companion object
} }
sealed class TokenBalanceState(val value: String) {
data object Empty : TokenBalanceState("Empty")
data object Full : TokenBalanceState("Full")
}
sealed class RateApp(val value: String) { sealed class RateApp(val value: String) {
data object Liked : RateApp("Liked") data object Liked : RateApp("Liked")
data object Disliked : RateApp("Disliked") data object Disliked : RateApp("Disliked")
@ -205,8 +200,9 @@ sealed class AnalyticsParam {
Pending(value = "Pending"), Pending(value = "Pending"),
} }
enum class EnsStatus(val value: String) { enum class EmptyFull(val value: String) {
EMPTY("Empty"), FULL("Full") Empty("Empty"),
Full("Full"),
} }
enum class ProductType(val value: String) { enum class ProductType(val value: String) {

View file

@ -12,11 +12,85 @@ sealed class OnboardingAnalyticsEvent(
sealed class Onboarding( sealed class Onboarding(
event: String, event: String,
params: Map<String, String> = mapOf(), params: Map<String, String> = mapOf(),
) : OnboardingAnalyticsEvent(category = "Onboarding", event = event, params = params) {
class Started(
source: String,
) : Onboarding(
event = "Onboarding Started",
params = mapOf(
AnalyticsParam.SOURCE to source,
),
)
class Finished(
source: String,
) : Onboarding(
event = "Onboarding Finished",
params = mapOf(
AnalyticsParam.SOURCE to source,
),
)
class ButtonMobileWallet(
source: String,
) : Onboarding(
event = "Button - Mobile Wallet",
params = mapOf(
AnalyticsParam.SOURCE to source,
),
)
}
sealed class CreateWallet(
event: String,
params: Map<String, String> = mapOf(),
) : OnboardingAnalyticsEvent(category = "Onboarding / Create Wallet", event = event, params = params) {
data object ButtonCreateWallet : CreateWallet("Button - Create Wallet")
class WalletCreatedSuccessfully(
source: String,
creationType: WalletCreationType = WalletCreationType.NewSeed,
seedPhraseLength: Int? = null,
passPhraseState: AnalyticsParam.EmptyFull,
) : CreateWallet(
event = "Wallet Created Successfully",
params = buildMap {
put(AnalyticsParam.SOURCE, source)
put("Creation Type", creationType.value)
put("Passphrase", passPhraseState.value)
if (seedPhraseLength != null) {
put("Seed Phrase Length", seedPhraseLength.toString())
}
},
)
sealed class WalletCreationType(val value: String) {
data object NewSeed : WalletCreationType(value = "New Seed")
data object SeedImport : WalletCreationType(value = "Seed Import")
}
}
sealed class SeedPhrase(
event: String,
params: Map<String, String> = mapOf(),
) : OnboardingAnalyticsEvent(category = "Onboarding / Seed Phrase", event = event, params = params) {
data object CreateMobileScreenOpened : SeedPhrase("Create Mobile Screen Opened")
data object ButtonImportWallet : SeedPhrase("Button - Import Wallet")
data object ImportSeedPhraseScreenOpened : SeedPhrase("Import Seed Phrase Screen Opened")
data object ButtonImport : SeedPhrase("Button - Import")
}
sealed class Error(
event: String,
params: Map<String, String> = mapOf(),
) : OnboardingAnalyticsEvent(category = "Error", event = event, params = params) { ) : OnboardingAnalyticsEvent(category = "Error", event = event, params = params) {
data class OfflineAttestationFailed( data class OfflineAttestationFailed(
val source: AnalyticsParam.ScreensSources, val source: AnalyticsParam.ScreensSources,
) : Onboarding( ) : Error(
event = "Offline Attestation Failed", event = "Offline Attestation Failed",
params = mapOf(AnalyticsParam.SOURCE to source.value), params = mapOf(AnalyticsParam.SOURCE to source.value),
) )

View file

@ -1,6 +1,7 @@
package com.tangem.domain.nft.analytics package com.tangem.domain.nft.analytics
import com.tangem.core.analytics.models.AnalyticsEvent 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.BLOCKCHAIN
import com.tangem.core.analytics.models.AnalyticsParam.Key.COLLECTIONS import com.tangem.core.analytics.models.AnalyticsParam.Key.COLLECTIONS
import com.tangem.core.analytics.models.AnalyticsParam.Key.NFT import com.tangem.core.analytics.models.AnalyticsParam.Key.NFT
@ -18,7 +19,7 @@ sealed class NFTAnalyticsEvent(
) { ) {
data class NFTListScreenOpened( data class NFTListScreenOpened(
val state: State, val state: AnalyticsParam.EmptyFull,
val collectionsCount: Int, val collectionsCount: Int,
val allAssetsCount: Int, val allAssetsCount: Int,
val noCollectionAssetsCount: Int, val noCollectionAssetsCount: Int,
@ -30,12 +31,7 @@ sealed class NFTAnalyticsEvent(
put(NFT, allAssetsCount.toString()) put(NFT, allAssetsCount.toString())
put(NO_COLLECTION, noCollectionAssetsCount.toString()) put(NO_COLLECTION, noCollectionAssetsCount.toString())
}, },
) { )
enum class State(val value: String) {
Empty("Empty"),
Full("Full"),
}
}
object Receive { object Receive {
data object ScreenOpened : NFTAnalyticsEvent(event = "Receive NFT Screen Opened") data object ScreenOpened : NFTAnalyticsEvent(event = "Receive NFT Screen Opened")

View file

@ -15,7 +15,7 @@ sealed class TokenReceiveNewAnalyticsEvent(
class ReceiveScreenOpened( class ReceiveScreenOpened(
token: String, token: String,
blockchainName: String, blockchainName: String,
ensStatus: AnalyticsParam.EnsStatus, ensStatus: AnalyticsParam.EmptyFull,
) : TokenReceiveNewAnalyticsEvent( ) : TokenReceiveNewAnalyticsEvent(
event = "Receive Screen Opened", event = "Receive Screen Opened",
params = mapOf( params = mapOf(

View file

@ -4,6 +4,7 @@ import com.tangem.common.routing.AppRoute
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic import com.tangem.core.analytics.models.Basic
import com.tangem.core.analytics.models.event.OnboardingAnalyticsEvent
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.navigation.Router import com.tangem.core.decompose.navigation.Router
@ -98,6 +99,11 @@ internal class CreateWalletSelectionModel @Inject constructor(
} }
private fun onMobileWalletClick() { private fun onMobileWalletClick() {
analyticsEventHandler.send(
event = OnboardingAnalyticsEvent.Onboarding.ButtonMobileWallet(
source = AnalyticsParam.ScreensSources.AddNewWallet.value,
),
)
if (!isHotWalletCreationSupported()) { if (!isHotWalletCreationSupported()) {
uiMessageSender.send( uiMessageSender.send(
hotWalletCreationNotSupportedDialog(isHotWalletCreationSupported.getLeastVersionName()), hotWalletCreationNotSupportedDialog(isHotWalletCreationSupported.getLeastVersionName()),
@ -105,7 +111,7 @@ internal class CreateWalletSelectionModel @Inject constructor(
return return
} }
router.push(AppRoute.CreateMobileWallet) router.push(AppRoute.CreateMobileWallet(AnalyticsParam.ScreensSources.AddNewWallet.value))
} }
private fun onHardwareWalletClick() { private fun onHardwareWalletClick() {

View file

@ -7,6 +7,7 @@ import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic import com.tangem.core.analytics.models.Basic
import com.tangem.core.analytics.models.event.OnboardingAnalyticsEvent
import com.tangem.core.decompose.di.GlobalUiMessageSender import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.Model
@ -134,6 +135,11 @@ internal class CreateWalletStartModel @Inject constructor(
} }
private fun onStartWithMobileWalletClick() { private fun onStartWithMobileWalletClick() {
analyticsEventHandler.send(
event = OnboardingAnalyticsEvent.Onboarding.ButtonMobileWallet(
source = AnalyticsParam.ScreensSources.CreateNewWallet.value,
),
)
if (!isHotWalletCreationSupported()) { if (!isHotWalletCreationSupported()) {
uiMessageSender.send( uiMessageSender.send(
hotWalletCreationNotSupportedDialog(isHotWalletCreationSupported.getLeastVersionName()), hotWalletCreationNotSupportedDialog(isHotWalletCreationSupported.getLeastVersionName()),
@ -141,7 +147,7 @@ internal class CreateWalletStartModel @Inject constructor(
return return
} }
router.push(AppRoute.CreateMobileWallet) router.push(AppRoute.CreateMobileWallet(AnalyticsParam.ScreensSources.CreateNewWallet.value))
} }
private fun onBuyClick() { private fun onBuyClick() {

View file

@ -4,5 +4,9 @@ import com.tangem.core.decompose.factory.ComponentFactory
import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.decompose.ComposableContentComponent
interface CreateMobileWalletComponent : ComposableContentComponent { interface CreateMobileWalletComponent : ComposableContentComponent {
interface Factory : ComponentFactory<Unit, CreateMobileWalletComponent> data class Params(
val source: String,
)
interface Factory : ComponentFactory<Params, CreateMobileWalletComponent>
} }

View file

@ -1,5 +1,8 @@
package com.tangem.features.hotwallet.addexistingwallet.im.port.model package com.tangem.features.hotwallet.addexistingwallet.im.port.model
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.event.OnboardingAnalyticsEvent
import com.tangem.core.decompose.di.GlobalUiMessageSender import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.Model
@ -37,6 +40,7 @@ internal class AddExistingWalletImportModel @Inject constructor(
private val hotUserWalletBuilderFactory: HotUserWalletBuilder.Factory, private val hotUserWalletBuilderFactory: HotUserWalletBuilder.Factory,
private val saveUserWalletUseCase: SaveWalletUseCase, private val saveUserWalletUseCase: SaveWalletUseCase,
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender, @GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model() { ) : Model() {
private val params: AddExistingWalletImportComponent.Params = paramsContainer.require() private val params: AddExistingWalletImportComponent.Params = paramsContainer.require()
@ -60,6 +64,7 @@ internal class AddExistingWalletImportModel @Inject constructor(
} }
init { init {
analyticsEventHandler.send(OnboardingAnalyticsEvent.SeedPhrase.ImportSeedPhraseScreenOpened)
importSeedPhraseUiStateBuilder = ImportSeedPhraseUiStateBuilder( importSeedPhraseUiStateBuilder = ImportSeedPhraseUiStateBuilder(
modelScope = modelScope, modelScope = modelScope,
mnemonicRepository = mnemonicRepository, mnemonicRepository = mnemonicRepository,
@ -72,6 +77,7 @@ internal class AddExistingWalletImportModel @Inject constructor(
) )
}, },
onPassphraseInfoClick = ::onPassphraseInfoClick, onPassphraseInfoClick = ::onPassphraseInfoClick,
onImportClick = { analyticsEventHandler.send(OnboardingAnalyticsEvent.SeedPhrase.ButtonImport) },
) )
} }
@ -101,6 +107,23 @@ internal class AddExistingWalletImportModel @Inject constructor(
} }
.onRight { .onRight {
setImportProgress(false) setImportProgress(false)
analyticsEventHandler.send(
event = OnboardingAnalyticsEvent.Onboarding.Finished(
source = AnalyticsParam.ScreensSources.ImportWallet.value,
),
)
analyticsEventHandler.send(
event = OnboardingAnalyticsEvent.CreateWallet.WalletCreatedSuccessfully(
source = AnalyticsParam.ScreensSources.ImportWallet.value,
creationType = OnboardingAnalyticsEvent.CreateWallet.WalletCreationType.SeedImport,
seedPhraseLength = mnemonic.mnemonicComponents.size,
passPhraseState = if (passphrase.isNullOrBlank()) {
AnalyticsParam.EmptyFull.Empty
} else {
AnalyticsParam.EmptyFull.Full
},
),
)
params.callbacks.onWalletImported(userWallet.walletId) params.callbacks.onWalletImported(userWallet.walletId)
} }
}.onFailure { }.onFailure {

View file

@ -17,6 +17,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Suppress("LongParameterList")
internal class ImportSeedPhraseUiStateBuilder( internal class ImportSeedPhraseUiStateBuilder(
private val modelScope: CoroutineScope, private val modelScope: CoroutineScope,
private val mnemonicRepository: MnemonicRepository, private val mnemonicRepository: MnemonicRepository,
@ -24,6 +25,7 @@ internal class ImportSeedPhraseUiStateBuilder(
private val updateUiState: ((AddExistingWalletImportUM) -> AddExistingWalletImportUM) -> Unit, private val updateUiState: ((AddExistingWalletImportUM) -> AddExistingWalletImportUM) -> Unit,
private val importWallet: (mnemonic: Mnemonic, passphrase: String?) -> Unit, private val importWallet: (mnemonic: Mnemonic, passphrase: String?) -> Unit,
private val onPassphraseInfoClick: () -> Unit, private val onPassphraseInfoClick: () -> Unit,
private val onImportClick: () -> Unit,
) { ) {
private val wordsCheckJobHolder = JobHolder() private val wordsCheckJobHolder = JobHolder()
private var importedMnemonic: Mnemonic? = null private var importedMnemonic: Mnemonic? = null
@ -57,6 +59,7 @@ internal class ImportSeedPhraseUiStateBuilder(
} }
private fun onCreateWallet() { private fun onCreateWallet() {
onImportClick()
val mnemonic = importedMnemonic ?: return val mnemonic = importedMnemonic ?: return
val passphrase = passphrase?.takeIf { it.isNotEmpty() } val passphrase = passphrase?.takeIf { it.isNotEmpty() }
importWallet(mnemonic, passphrase) importWallet(mnemonic, passphrase)

View file

@ -1,9 +1,13 @@
package com.tangem.features.hotwallet.createmobilewallet package com.tangem.features.hotwallet.createmobilewallet
import com.tangem.common.routing.AppRoute 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.OnboardingAnalyticsEvent
import com.tangem.core.analytics.utils.TrackingContextProxy import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.message.dialog.Dialogs.hotWalletCreationNotSupportedDialog import com.tangem.core.ui.message.dialog.Dialogs.hotWalletCreationNotSupportedDialog
@ -11,6 +15,7 @@ import com.tangem.domain.hotwallet.IsHotWalletCreationSupported
import com.tangem.domain.wallets.builder.HotUserWalletBuilder import com.tangem.domain.wallets.builder.HotUserWalletBuilder
import com.tangem.domain.wallets.usecase.SaveWalletUseCase import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import com.tangem.domain.wallets.usecase.SyncWalletWithRemoteUseCase import com.tangem.domain.wallets.usecase.SyncWalletWithRemoteUseCase
import com.tangem.features.hotwallet.CreateMobileWalletComponent
import com.tangem.features.hotwallet.createmobilewallet.entity.CreateMobileWalletUM import com.tangem.features.hotwallet.createmobilewallet.entity.CreateMobileWalletUM
import com.tangem.hot.sdk.TangemHotSdk import com.tangem.hot.sdk.TangemHotSdk
import com.tangem.hot.sdk.model.HotAuth import com.tangem.hot.sdk.model.HotAuth
@ -28,6 +33,7 @@ import javax.inject.Inject
@Suppress("LongParameterList") @Suppress("LongParameterList")
@ModelScoped @ModelScoped
internal class CreateMobileWalletModel @Inject constructor( internal class CreateMobileWalletModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider, override val dispatchers: CoroutineDispatcherProvider,
private val hotUserWalletBuilderFactory: HotUserWalletBuilder.Factory, private val hotUserWalletBuilderFactory: HotUserWalletBuilder.Factory,
private val saveUserWalletUseCase: SaveWalletUseCase, private val saveUserWalletUseCase: SaveWalletUseCase,
@ -37,8 +43,11 @@ internal class CreateMobileWalletModel @Inject constructor(
private val trackingContextProxy: TrackingContextProxy, private val trackingContextProxy: TrackingContextProxy,
private val isHotWalletCreationSupported: IsHotWalletCreationSupported, private val isHotWalletCreationSupported: IsHotWalletCreationSupported,
private val uiMessageSender: UiMessageSender, private val uiMessageSender: UiMessageSender,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model() { ) : Model() {
private val params: CreateMobileWalletComponent.Params = paramsContainer.require()
internal val uiState: StateFlow<CreateMobileWalletUM> internal val uiState: StateFlow<CreateMobileWalletUM>
field = MutableStateFlow( field = MutableStateFlow(
CreateMobileWalletUM( CreateMobileWalletUM(
@ -51,6 +60,8 @@ internal class CreateMobileWalletModel @Inject constructor(
init { init {
trackingContextProxy.addHotWalletContext() trackingContextProxy.addHotWalletContext()
analyticsEventHandler.send(OnboardingAnalyticsEvent.Onboarding.Started(source = params.source))
analyticsEventHandler.send(OnboardingAnalyticsEvent.SeedPhrase.CreateMobileScreenOpened)
} }
override fun onDestroy() { override fun onDestroy() {
@ -59,11 +70,13 @@ internal class CreateMobileWalletModel @Inject constructor(
} }
private fun onImportClick() { private fun onImportClick() {
analyticsEventHandler.send(OnboardingAnalyticsEvent.SeedPhrase.ButtonImportWallet)
checkHotWalletCreationSupported(notSupported = { return }) checkHotWalletCreationSupported(notSupported = { return })
router.push(AppRoute.AddExistingWallet) router.push(AppRoute.AddExistingWallet)
} }
private fun onCreateClick() { private fun onCreateClick() {
analyticsEventHandler.send(OnboardingAnalyticsEvent.CreateWallet.ButtonCreateWallet)
checkHotWalletCreationSupported(notSupported = { return }) checkHotWalletCreationSupported(notSupported = { return })
modelScope.launch { modelScope.launch {
@ -78,6 +91,16 @@ internal class CreateMobileWalletModel @Inject constructor(
saveUserWalletUseCase(userWallet) saveUserWalletUseCase(userWallet)
analyticsEventHandler.send(OnboardingAnalyticsEvent.Onboarding.Finished(source = params.source))
analyticsEventHandler.send(
event = OnboardingAnalyticsEvent.CreateWallet.WalletCreatedSuccessfully(
source = params.source,
creationType = OnboardingAnalyticsEvent.CreateWallet.WalletCreationType.NewSeed,
seedPhraseLength = SEED_PHRASE_LENGTH,
passPhraseState = AnalyticsParam.EmptyFull.Empty,
),
)
launch(dispatchers.main + NonCancellable) { launch(dispatchers.main + NonCancellable) {
syncWalletWithRemoteUseCase(userWalletId = userWallet.walletId) syncWalletWithRemoteUseCase(userWalletId = userWallet.walletId)
} }
@ -99,4 +122,8 @@ internal class CreateMobileWalletModel @Inject constructor(
notSupported() notSupported()
} }
} }
companion object {
private const val SEED_PHRASE_LENGTH = 12
}
} }

View file

@ -15,7 +15,7 @@ import dagger.assisted.AssistedInject
@Suppress("UnusedPrivateMember") @Suppress("UnusedPrivateMember")
internal class DefaultCreateMobileWalletComponent @AssistedInject constructor( internal class DefaultCreateMobileWalletComponent @AssistedInject constructor(
@Assisted private val context: AppComponentContext, @Assisted private val context: AppComponentContext,
@Assisted private val params: Unit, @Assisted private val params: CreateMobileWalletComponent.Params,
) : CreateMobileWalletComponent, AppComponentContext by context { ) : CreateMobileWalletComponent, AppComponentContext by context {
private val model: CreateMobileWalletModel = getOrCreateModel(params) private val model: CreateMobileWalletModel = getOrCreateModel(params)
@ -31,6 +31,9 @@ internal class DefaultCreateMobileWalletComponent @AssistedInject constructor(
@AssistedFactory @AssistedFactory
interface Factory : CreateMobileWalletComponent.Factory { interface Factory : CreateMobileWalletComponent.Factory {
override fun create(context: AppComponentContext, params: Unit): DefaultCreateMobileWalletComponent override fun create(
context: AppComponentContext,
params: CreateMobileWalletComponent.Params,
): DefaultCreateMobileWalletComponent
} }
} }

View file

@ -250,7 +250,7 @@ class MultiWalletBackupModel @Inject constructor(
private fun showCardVerificationFailedDialog(error: TangemSdkError.CardVerificationFailed) { private fun showCardVerificationFailedDialog(error: TangemSdkError.CardVerificationFailed) {
analyticsEventHandler.send( analyticsEventHandler.send(
event = OnboardingAnalyticsEvent.Onboarding.OfflineAttestationFailed(AnalyticsParam.ScreensSources.Backup), event = OnboardingAnalyticsEvent.Error.OfflineAttestationFailed(AnalyticsParam.ScreensSources.Backup),
) )
val resource = error.localizedDescriptionRes() val resource = error.localizedDescriptionRes()

View file

@ -24,7 +24,7 @@ internal sealed class SendAnalyticEvents(
val feeType: AnalyticsParam.FeeType, val feeType: AnalyticsParam.FeeType,
val blockchain: String, val blockchain: String,
val nonceNotEmpty: Boolean, val nonceNotEmpty: Boolean,
private val ensStatus: AnalyticsParam.EnsStatus, private val ensStatus: AnalyticsParam.EmptyFull,
) : SendAnalyticEvents( ) : SendAnalyticEvents(
event = "Transaction Sent Screen Opened", event = "Transaction Sent Screen Opened",
params = mapOf( params = mapOf(
@ -33,8 +33,8 @@ internal sealed class SendAnalyticEvents(
BLOCKCHAIN to blockchain, BLOCKCHAIN to blockchain,
NONCE to nonceNotEmpty.toString().capitalize(), NONCE to nonceNotEmpty.toString().capitalize(),
ENS_ADDRESS to when (ensStatus) { ENS_ADDRESS to when (ensStatus) {
AnalyticsParam.EnsStatus.EMPTY -> false.toString().capitalize() AnalyticsParam.EmptyFull.Empty -> false.toString().capitalize()
AnalyticsParam.EnsStatus.FULL -> true.toString().capitalize() AnalyticsParam.EmptyFull.Full -> true.toString().capitalize()
}, },
), ),
) )

View file

@ -53,13 +53,13 @@ internal class SendAnalyticHelper @Inject constructor(
} }
} }
private fun getEnsStatus(sendUM: SendUM): AnalyticsParam.EnsStatus { private fun getEnsStatus(sendUM: SendUM): AnalyticsParam.EmptyFull {
val blockchainAddressForEns = val blockchainAddressForEns =
(sendUM.destinationUM as? DestinationUM.Content)?.addressTextField?.isAddressEns (sendUM.destinationUM as? DestinationUM.Content)?.addressTextField?.isAddressEns
return if (blockchainAddressForEns == true) { return if (blockchainAddressForEns == true) {
AnalyticsParam.EnsStatus.FULL AnalyticsParam.EmptyFull.Full
} else { } else {
AnalyticsParam.EnsStatus.EMPTY AnalyticsParam.EmptyFull.Empty
} }
} }
} }

View file

@ -56,12 +56,12 @@ internal class TokenReceiveAssetsModel @Inject constructor(
), ),
) )
private fun configureEnsStatus(): AnalyticsParam.EnsStatus { private fun configureEnsStatus(): AnalyticsParam.EmptyFull {
val hasEnsAddress = params.addresses.any { it.type == ReceiveAddress.Type.Ens } val hasEnsAddress = params.addresses.any { it.type == ReceiveAddress.Type.Ens }
return if (hasEnsAddress) { return if (hasEnsAddress) {
AnalyticsParam.EnsStatus.FULL AnalyticsParam.EmptyFull.Full
} else { } else {
AnalyticsParam.EnsStatus.EMPTY AnalyticsParam.EmptyFull.Empty
} }
} }
} }

View file

@ -4,6 +4,7 @@ import arrow.core.getOrElse
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
import com.tangem.common.ui.tokens.TokenItemStateConverter.ApySource import com.tangem.common.ui.tokens.TokenItemStateConverter.ApySource
import com.tangem.core.analytics.api.AnalyticsEventHandler 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.analytics.models.event.MainScreenAnalyticsEvent
import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.models.account.Account import com.tangem.domain.models.account.Account
@ -354,7 +355,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
is WalletNFTItemUM.Content -> { is WalletNFTItemUM.Content -> {
analyticsEventHandler.send( analyticsEventHandler.send(
NFTAnalyticsEvent.NFTListScreenOpened( NFTAnalyticsEvent.NFTListScreenOpened(
state = NFTAnalyticsEvent.NFTListScreenOpened.State.Full, state = AnalyticsParam.EmptyFull.Full,
allAssetsCount = state.allAssetsCount, allAssetsCount = state.allAssetsCount,
collectionsCount = state.collectionsCount, collectionsCount = state.collectionsCount,
noCollectionAssetsCount = state.noCollectionAssetsCount, noCollectionAssetsCount = state.noCollectionAssetsCount,
@ -364,7 +365,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
is WalletNFTItemUM.Empty -> { is WalletNFTItemUM.Empty -> {
analyticsEventHandler.send( analyticsEventHandler.send(
NFTAnalyticsEvent.NFTListScreenOpened( NFTAnalyticsEvent.NFTListScreenOpened(
state = NFTAnalyticsEvent.NFTListScreenOpened.State.Empty, state = AnalyticsParam.EmptyFull.Empty,
allAssetsCount = 0, allAssetsCount = 0,
collectionsCount = 0, collectionsCount = 0,
noCollectionAssetsCount = 0, noCollectionAssetsCount = 0,

View file

@ -37,7 +37,7 @@ sealed class WalletScreenAnalyticsEvent {
}, },
) )
class TokenBalance(balance: AnalyticsParam.TokenBalanceState, token: String) : Basic( class TokenBalance(balance: AnalyticsParam.EmptyFull, token: String) : Basic(
event = "Token Balance", event = "Token Balance",
params = mapOf( params = mapOf(
AnalyticsParam.STATE to balance.value, AnalyticsParam.STATE to balance.value,

View file

@ -156,9 +156,9 @@ internal class TokenListAnalyticsSender @Inject constructor(
-> { -> {
if (balanceWasSentMap[blockchain.currency] != true) { if (balanceWasSentMap[blockchain.currency] != true) {
val tokenBalance = if (balanceStatus.amount.isZero()) { val tokenBalance = if (balanceStatus.amount.isZero()) {
AnalyticsParam.TokenBalanceState.Empty AnalyticsParam.EmptyFull.Empty
} else { } else {
AnalyticsParam.TokenBalanceState.Full AnalyticsParam.EmptyFull.Full
} }
analyticsEventHandler.send( analyticsEventHandler.send(
Basic.TokenBalance( Basic.TokenBalance(