Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-12 14:15:13 +05:00
parent bc76eaa64a
commit 5f938d6139
11 changed files with 70 additions and 17 deletions

View file

@ -47,7 +47,7 @@ sealed class OnboardingAnalyticsEvent(
params: Map<String, String> = mapOf(),
) : OnboardingAnalyticsEvent(category = "Onboarding / Create Wallet", event = event, params = params) {
data object ButtonCreateWallet : CreateWallet("Button - Create Wallet")
class ButtonCreateWallet : CreateWallet("Button - Create Wallet")
class WalletCreatedSuccessfully(
source: String,
@ -77,10 +77,17 @@ sealed class OnboardingAnalyticsEvent(
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")
class CreateMobileScreenOpened(
source: String,
) : SeedPhrase(
event = "Create Mobile Screen Opened",
params = mapOf(
AnalyticsParam.SOURCE to source,
),
)
class ButtonImportWallet : SeedPhrase("Button - Import Wallet")
class ImportSeedPhraseScreenOpened : SeedPhrase("Import Seed Phrase Screen Opened")
class ButtonImport : SeedPhrase("Button - Import")
}
sealed class Error(

View file

@ -5,6 +5,7 @@ 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.analytics.models.event.OnboardingAnalyticsEvent
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.navigation.Router
@ -32,6 +33,7 @@ import javax.inject.Inject
internal class CreateWalletSelectionModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val trackingContextProxy: TrackingContextProxy,
private val analyticsEventHandler: AnalyticsEventHandler,
private val generateBuyTangemCardLinkUseCase: GenerateBuyTangemCardLinkUseCase,
private val urlOpener: UrlOpener,
@ -99,6 +101,7 @@ internal class CreateWalletSelectionModel @Inject constructor(
}
private fun onMobileWalletClick() {
trackingContextProxy.addHotWalletContext()
analyticsEventHandler.send(
event = OnboardingAnalyticsEvent.Onboarding.ButtonMobileWallet(
source = AnalyticsParam.ScreensSources.AddNewWallet.value,

View file

@ -8,6 +8,7 @@ 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.analytics.models.event.OnboardingAnalyticsEvent
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
@ -60,6 +61,7 @@ internal class CreateWalletStartModel @Inject constructor(
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
private val generateBuyTangemCardLinkUseCase: GenerateBuyTangemCardLinkUseCase,
private val urlOpener: UrlOpener,
private val trackingContextProxy: TrackingContextProxy,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model() {
@ -141,6 +143,7 @@ internal class CreateWalletStartModel @Inject constructor(
}
private fun onStartWithMobileWalletClick() {
trackingContextProxy.addHotWalletContext()
analyticsEventHandler.send(
event = OnboardingAnalyticsEvent.Onboarding.ButtonMobileWallet(
source = AnalyticsParam.ScreensSources.CreateWalletIntro.value,

View file

@ -64,7 +64,7 @@ internal class AddExistingWalletImportModel @Inject constructor(
}
init {
analyticsEventHandler.send(OnboardingAnalyticsEvent.SeedPhrase.ImportSeedPhraseScreenOpened)
analyticsEventHandler.send(OnboardingAnalyticsEvent.SeedPhrase.ImportSeedPhraseScreenOpened())
importSeedPhraseUiStateBuilder = ImportSeedPhraseUiStateBuilder(
modelScope = modelScope,
mnemonicRepository = mnemonicRepository,
@ -77,7 +77,7 @@ internal class AddExistingWalletImportModel @Inject constructor(
)
},
onPassphraseInfoClick = ::onPassphraseInfoClick,
onImportClick = { analyticsEventHandler.send(OnboardingAnalyticsEvent.SeedPhrase.ButtonImport) },
onImportClick = { analyticsEventHandler.send(OnboardingAnalyticsEvent.SeedPhrase.ButtonImport()) },
)
}

View file

@ -60,8 +60,12 @@ internal class CreateMobileWalletModel @Inject constructor(
init {
trackingContextProxy.addHotWalletContext()
analyticsEventHandler.send(OnboardingAnalyticsEvent.Onboarding.Started(source = params.source))
analyticsEventHandler.send(OnboardingAnalyticsEvent.SeedPhrase.CreateMobileScreenOpened)
analyticsEventHandler.send(
event = OnboardingAnalyticsEvent.Onboarding.Started(source = params.source),
)
analyticsEventHandler.send(
event = OnboardingAnalyticsEvent.SeedPhrase.CreateMobileScreenOpened(source = params.source),
)
}
override fun onDestroy() {
@ -70,13 +74,13 @@ internal class CreateMobileWalletModel @Inject constructor(
}
private fun onImportClick() {
analyticsEventHandler.send(OnboardingAnalyticsEvent.SeedPhrase.ButtonImportWallet)
analyticsEventHandler.send(OnboardingAnalyticsEvent.SeedPhrase.ButtonImportWallet())
checkHotWalletCreationSupported(notSupported = { return })
router.push(AppRoute.AddExistingWallet)
}
private fun onCreateClick() {
analyticsEventHandler.send(OnboardingAnalyticsEvent.CreateWallet.ButtonCreateWallet)
analyticsEventHandler.send(OnboardingAnalyticsEvent.CreateWallet.ButtonCreateWallet())
checkHotWalletCreationSupported(notSupported = { return })
modelScope.launch {

View file

@ -19,13 +19,16 @@ sealed class OnboardingEvent(
class ScreenOpened : CreateWallet("Create Wallet Screen Opened")
class ButtonCreateWallet : CreateWallet("Button - Create Wallet")
class ButtonOtherOptions : CreateWallet("Button - Other Options")
class WalletCreatedSuccessfully(
creationType: WalletCreationType = WalletCreationType.PrivateKey,
seedPhraseLength: Int? = null,
passPhraseState: AnalyticsParam.EmptyFull,
) : CreateWallet(
event = "Wallet Created Successfully",
params = buildMap {
put("Creation Type", creationType.value)
put("Passphrase", passPhraseState.value)
if (seedPhraseLength != null) {
put("Seed Phrase Length", seedPhraseLength.toString())
}

View file

@ -4,6 +4,7 @@ import androidx.compose.runtime.Stable
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
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
@ -68,6 +69,7 @@ internal class MultiWalletCreateWalletModel @Inject constructor(
},
showOtherOptionsButton = params.parentParams.withSeedPhraseFlow,
onOtherOptionsClick = {
analyticsHandler.send(OnboardingEvent.CreateWallet.ButtonOtherOptions())
modelScope.launch {
onDone.emit(Step.SeedPhrase)
}
@ -104,7 +106,11 @@ internal class MultiWalletCreateWalletModel @Inject constructor(
cardRepository.startCardActivation(cardId = result.data.card.cardId)
analyticsHandler.send(OnboardingEvent.CreateWallet.WalletCreatedSuccessfully())
analyticsHandler.send(
event = OnboardingEvent.CreateWallet.WalletCreatedSuccessfully(
passPhraseState = AnalyticsParam.EmptyFull.Empty,
),
)
val cardDoesNotSupportBackup = result.data.card.settings.isBackupAllowed.not()
when {

View file

@ -5,6 +5,8 @@ import arrow.core.getOrElse
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
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.ModelScoped
import com.tangem.core.decompose.model.Model
@ -60,6 +62,7 @@ internal class MultiWalletSeedPhraseModel @Inject constructor(
private val isWalletAlreadySavedUseCase: IsWalletAlreadySavedUseCase,
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model() {
private val params = paramsContainer.require<MultiWalletChildParams>()
@ -108,6 +111,7 @@ internal class MultiWalletSeedPhraseModel @Inject constructor(
updateUiState = { block -> updateUiStateSpecific(block) },
readyToImport = { ready -> state.update { it.copy(readyToImport = ready) } },
importWallet = { mnemonic, passphrase ->
analyticsEventHandler.send(OnboardingAnalyticsEvent.SeedPhrase.ButtonImport())
importWallet(
mnemonic = mnemonic,
passphrase = passphrase,
@ -149,6 +153,7 @@ internal class MultiWalletSeedPhraseModel @Inject constructor(
private fun getInitialUIState(): MultiWalletSeedPhraseUM {
return MultiWalletSeedPhraseUM.Start(
onImportSeedPhraseClicked = {
analyticsEventHandler.send(OnboardingAnalyticsEvent.SeedPhrase.ButtonImportWallet())
openImportSeedPhrase()
},
onGenerateSeedPhraseClicked = {
@ -177,6 +182,7 @@ internal class MultiWalletSeedPhraseModel @Inject constructor(
}
private fun openImportSeedPhrase() {
analyticsEventHandler.send(OnboardingAnalyticsEvent.SeedPhrase.ImportSeedPhraseScreenOpened())
_uiState.value = importSeedPhraseUiStateBuilder.getState()
}
@ -232,6 +238,11 @@ internal class MultiWalletSeedPhraseModel @Inject constructor(
OnboardingEvent.CreateWallet.WalletCreationType.SeedImport
},
seedPhraseLength = mnemonic.mnemonicComponents.size,
passPhraseState = if (passphrase.isNullOrBlank()) {
AnalyticsParam.EmptyFull.Empty
} else {
AnalyticsParam.EmptyFull.Full
},
),
)

View file

@ -1,7 +1,8 @@
package com.tangem.features.onboarding.v2.note.impl.child.create.model
import com.tangem.common.CompletionResult
import com.tangem.core.analytics.Analytics
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
@ -22,6 +23,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject
@Suppress("LongParameterList")
@ModelScoped
internal class OnboardingNoteCreateWalletModel @Inject constructor(
paramsContainer: ParamsContainer,
@ -30,6 +32,7 @@ internal class OnboardingNoteCreateWalletModel @Inject constructor(
private val cardRepository: CardRepository,
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
private val saveWalletUseCase: SaveWalletUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model() {
private val params = paramsContainer.require<OnboardingNoteCreateWalletComponent.Params>()
@ -41,11 +44,11 @@ internal class OnboardingNoteCreateWalletModel @Inject constructor(
)
init {
Analytics.send(OnboardingEvent.CreateWallet.ScreenOpened())
analyticsEventHandler.send(OnboardingEvent.CreateWallet.ScreenOpened())
modelScope.launch {
val scanResponse = params.childParams.commonState.value.scanResponse ?: return@launch
if (!cardRepository.isActivationStarted(scanResponse.card.cardId)) {
Analytics.send(OnboardingEvent.Started())
analyticsEventHandler.send(OnboardingEvent.Started())
}
}
observeArtwork()
@ -62,7 +65,11 @@ internal class OnboardingNoteCreateWalletModel @Inject constructor(
val result = tangemSdkManager.createProductWallet(scanResponse)
when (result) {
is CompletionResult.Success -> {
Analytics.send(OnboardingEvent.CreateWallet.WalletCreatedSuccessfully())
analyticsEventHandler.send(
event = OnboardingEvent.CreateWallet.WalletCreatedSuccessfully(
passPhraseState = AnalyticsParam.EmptyFull.Empty,
),
)
createWalletAndNavigateBackWithDone(scanResponse.copy(card = result.data.card))
}
is CompletionResult.Failure -> _uiState.update {

View file

@ -2,6 +2,7 @@ package com.tangem.features.onboarding.v2.note.impl.model
import com.arkivanov.decompose.router.stack.StackNavigation
import com.tangem.common.ui.userwallet.converter.ArtworkUMConverter
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
@ -9,6 +10,7 @@ import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.usecase.GetCardImageUseCase
import com.tangem.features.onboarding.v2.common.analytics.OnboardingEvent
import com.tangem.features.onboarding.v2.common.ui.exitOnboardingDialog
import com.tangem.features.onboarding.v2.note.api.OnboardingNoteComponent
import com.tangem.features.onboarding.v2.note.impl.OnboardingNoteInnerNavigationState
@ -22,6 +24,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject
@Suppress("LongParameterList")
@ModelScoped
internal class OnboardingNoteModel @Inject constructor(
paramsContainer: ParamsContainer,
@ -30,6 +33,7 @@ internal class OnboardingNoteModel @Inject constructor(
private val messageSender: UiMessageSender,
private val getCardImageUseCase: GetCardImageUseCase,
private val artworkUMConverter: ArtworkUMConverter,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model() {
@Suppress("UnusedPrivateMember")
@ -72,6 +76,7 @@ internal class OnboardingNoteModel @Inject constructor(
}
fun onWalletCreated(userWallet: UserWallet) {
analyticsEventHandler.send(OnboardingEvent.Finished())
commonUiState.update {
it.copy(userWallet = userWallet)
}

View file

@ -171,7 +171,11 @@ internal class OnboardingTwinModel @Inject constructor(
}
}
analyticsEventHandler.send(OnboardingEvent.CreateWallet.WalletCreatedSuccessfully())
analyticsEventHandler.send(
event = OnboardingEvent.CreateWallet.WalletCreatedSuccessfully(
passPhraseState = AnalyticsParam.EmptyFull.Empty,
),
)
update<OnboardingTwinUM.ScanCard> {
it.copy(