Updated on 2026-08-14

This commit is contained in:
Tangem 2025-11-27 17:44:05 +05:00
parent b4e9c23f6d
commit 4fb12ce1ff
23 changed files with 415 additions and 84 deletions

View file

@ -10,6 +10,8 @@ interface CreateWalletBackupComponent : ComposableContentComponent {
val userWalletId: UserWalletId,
val isUpgradeFlow: Boolean,
val shouldSetAccessCode: Boolean,
val analyticsSource: String,
val analyticsAction: String,
)
interface Factory : ComponentFactory<Params, CreateWalletBackupComponent>

View file

@ -7,6 +7,7 @@ import com.tangem.domain.models.wallet.UserWalletId
interface UpdateAccessCodeComponent : ComposableContentComponent {
data class Params(
val userWalletId: UserWalletId,
val source: String,
)
interface Factory : ComponentFactory<Params, UpdateAccessCodeComponent>
}

View file

@ -2,6 +2,8 @@ package com.tangem.features.hotwallet.addexistingwallet.entry
import com.arkivanov.decompose.router.stack.*
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.utils.TrackingContextProxy
import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.core.decompose.di.ModelScoped
@ -15,6 +17,7 @@ import com.tangem.core.ui.message.EventMessageAction
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.settings.ShouldAskPermissionUseCase
import com.tangem.domain.hotwallet.SetAccessCodeSkippedUseCase
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
import com.tangem.features.hotwallet.addexistingwallet.entry.routing.AddExistingWalletRoute
import com.tangem.features.hotwallet.addexistingwallet.im.port.AddExistingWalletImportComponent
import com.tangem.features.hotwallet.manualbackup.completed.ManualBackupCompletedComponent
@ -28,6 +31,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
@Suppress("LongParameterList")
@ModelScoped
internal class AddExistingWalletModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
@ -36,6 +40,7 @@ internal class AddExistingWalletModel @Inject constructor(
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
private val trackingContextProxy: TrackingContextProxy,
private val setAccessCodeSkippedUseCase: SetAccessCodeSkippedUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model() {
val hotWalletStepperComponentModelCallback = HotWalletStepperComponentModelCallback()
@ -49,6 +54,8 @@ internal class AddExistingWalletModel @Inject constructor(
val startRoute = AddExistingWalletRoute.Import
val currentRoute: MutableStateFlow<AddExistingWalletRoute> = MutableStateFlow(startRoute)
private val analyticsSource = AnalyticsParam.ScreensSources.ImportWallet
init {
trackingContextProxy.addHotWalletContext()
}
@ -133,6 +140,9 @@ internal class AddExistingWalletModel @Inject constructor(
inner class ManualBackupCompletedModelCallbacks : ManualBackupCompletedComponent.ModelCallbacks {
override fun onContinueClick(userWalletId: UserWalletId) {
analyticsEventHandler.send(
event = WalletSettingsAnalyticEvents.AccessCodeScreenOpened(source = analyticsSource.value),
)
stackNavigation.replaceAll(AddExistingWalletRoute.SetAccessCode(userWalletId))
}
@ -141,6 +151,9 @@ internal class AddExistingWalletModel @Inject constructor(
inner class AccessCodeModelCallbacks : AccessCodeComponent.ModelCallbacks {
override fun onNewAccessCodeInput(userWalletId: UserWalletId, accessCode: String) {
analyticsEventHandler.send(
event = WalletSettingsAnalyticEvents.ReEnterAccessCodeScreen(source = analyticsSource.value),
)
stackNavigation.push(AddExistingWalletRoute.ConfirmAccessCode(userWalletId, accessCode))
}

View file

@ -23,6 +23,7 @@ import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.common.wallets.error.SaveWalletError
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
import com.tangem.domain.wallets.usecase.GenerateBuyTangemCardLinkUseCase
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
@ -64,6 +65,10 @@ internal class CreateHardwareWalletModel @Inject constructor(
),
)
init {
analyticsEventHandler.send(WalletSettingsAnalyticEvents.CreateWalletScreenOpened)
}
override fun onDestroy() {
super.onDestroy()
}

View file

@ -4,6 +4,7 @@ import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.pop
import com.arkivanov.decompose.router.stack.push
import com.tangem.common.routing.AppRoute
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
@ -11,6 +12,7 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.navigation.popTo
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
import com.tangem.features.hotwallet.CreateWalletBackupComponent
import com.tangem.features.hotwallet.createwalletbackup.routing.CreateWalletBackupRoute
import com.tangem.features.hotwallet.manualbackup.check.ManualBackupCheckComponent
@ -27,6 +29,7 @@ internal class CreateWalletBackupModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val trackingContextProxy: TrackingContextProxy,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model() {
val params = paramsContainer.require<CreateWalletBackupComponent.Params>()
@ -42,6 +45,12 @@ internal class CreateWalletBackupModel @Inject constructor(
init {
trackingContextProxy.addHotWalletContext()
analyticsEventHandler.send(
event = WalletSettingsAnalyticEvents.RecoveryPhraseScreenInfo(
source = params.analyticsSource,
action = params.analyticsAction,
),
)
}
override fun onDestroy() {
@ -59,14 +68,32 @@ internal class CreateWalletBackupModel @Inject constructor(
}
fun onManualBackupStarted() {
analyticsEventHandler.send(
event = WalletSettingsAnalyticEvents.RecoveryPhraseScreen(
source = params.analyticsSource,
action = params.analyticsAction,
),
)
stackNavigation.push(CreateWalletBackupRoute.RecoveryPhrase)
}
fun onManualBackupPhraseShown() {
analyticsEventHandler.send(
event = WalletSettingsAnalyticEvents.RecoveryPhraseCheck(
source = params.analyticsSource,
action = params.analyticsAction,
),
)
stackNavigation.push(CreateWalletBackupRoute.ConfirmBackup)
}
fun onManualBackupChecked() {
analyticsEventHandler.send(
event = WalletSettingsAnalyticEvents.BackupCompleteScreen(
source = params.analyticsSource,
action = params.analyticsAction,
),
)
stackNavigation.push(
configuration = CreateWalletBackupRoute.BackupCompleted(
isUpgradeFlow = params.isUpgradeFlow,
@ -80,6 +107,7 @@ internal class CreateWalletBackupModel @Inject constructor(
router.replaceCurrent(
route = AppRoute.UpdateAccessCode(
userWalletId = params.userWalletId,
source = params.analyticsSource,
),
)
} else {

View file

@ -11,6 +11,8 @@ import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.hotwallet.UpdateAccessCodeComponent
import com.tangem.features.hotwallet.updateaccesscode.routing.UpdateAccessCodeRoute
import com.tangem.features.hotwallet.accesscode.AccessCodeComponent
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
import com.tangem.features.hotwallet.setupfinished.MobileWalletSetupFinishedComponent
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.MutableStateFlow
@ -21,6 +23,7 @@ internal class UpdateAccessCodeModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
paramsContainer: ParamsContainer,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model(), AccessCodeComponent.ModelCallbacks {
private val params = paramsContainer.require<UpdateAccessCodeComponent.Params>()
@ -30,6 +33,10 @@ internal class UpdateAccessCodeModel @Inject constructor(
val currentRoute: MutableStateFlow<UpdateAccessCodeRoute> = MutableStateFlow(startRoute)
val mobileWalletSetupFinishedComponentModelCallbacks = MobileWalletSetupFinishedComponentModelCallbacks()
init {
analyticsEventHandler.send(WalletSettingsAnalyticEvents.AccessCodeScreenOpened(source = params.source))
}
fun onChildBack() {
when (currentRoute.value) {
is UpdateAccessCodeRoute.SetAccessCode -> router.pop()
@ -39,6 +46,7 @@ internal class UpdateAccessCodeModel @Inject constructor(
}
override fun onNewAccessCodeInput(userWalletId: UserWalletId, accessCode: String) {
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ReEnterAccessCodeScreen(source = params.source))
stackNavigation.push(UpdateAccessCodeRoute.ConfirmAccessCode(userWalletId, accessCode))
}

View file

@ -6,6 +6,7 @@ import com.tangem.common.doOnFailure
import com.tangem.common.doOnResult
import com.tangem.common.doOnSuccess
import com.tangem.common.routing.AppRoute
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
@ -25,6 +26,7 @@ import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
import com.tangem.domain.wallets.usecase.ClearHotWalletContextualUnlockUseCase
import com.tangem.domain.wallets.usecase.GenerateBuyTangemCardLinkUseCase
@ -58,6 +60,7 @@ internal class UpgradeWalletModel @Inject constructor(
private val tangemSdkManager: TangemSdkManager,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model() {
private val params = paramsContainer.require<UpgradeWalletComponent.Params>()
@ -73,6 +76,10 @@ internal class UpgradeWalletModel @Inject constructor(
),
)
init {
analyticsEventHandler.send(WalletSettingsAnalyticEvents.HardwareUpgradeScreenOpened)
}
override fun onDestroy() {
clearHotWalletContextualUnlockUseCase.invoke(params.userWalletId)
super.onDestroy()
@ -85,6 +92,7 @@ internal class UpgradeWalletModel @Inject constructor(
}
private fun onContinueClick() {
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonStartUpgrade)
scanCard()
}
@ -151,8 +159,6 @@ internal class UpgradeWalletModel @Inject constructor(
private fun showCardVerificationFailedDialog(error: TangemError) {
if (error !is TangemSdkError.CardVerificationFailed) return
// TODO [REDACTED_TASK_KEY] track error
val resource = error.localizedDescriptionRes()
val resId = resource.resId ?: R.string.common_unknown_error
val resArgs = resource.args.map { it.value }

View file

@ -4,6 +4,8 @@ import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.pop
import com.arkivanov.decompose.router.stack.push
import com.arkivanov.decompose.router.stack.replaceAll
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.core.decompose.di.ModelScoped
@ -18,6 +20,7 @@ import com.tangem.core.ui.message.EventMessageAction
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.settings.ShouldAskPermissionUseCase
import com.tangem.domain.hotwallet.SetAccessCodeSkippedUseCase
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
import com.tangem.features.hotwallet.manualbackup.check.ManualBackupCheckComponent
import com.tangem.features.hotwallet.manualbackup.completed.ManualBackupCompletedComponent
import com.tangem.features.hotwallet.manualbackup.phrase.ManualBackupPhraseComponent
@ -41,9 +44,10 @@ internal class WalletActivationModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val shouldAskPermissionUseCase: ShouldAskPermissionUseCase,
private val setAccessCodeSkippedUseCase: SetAccessCodeSkippedUseCase,
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
private val trackingContextProxy: TrackingContextProxy,
private val setAccessCodeSkippedUseCase: SetAccessCodeSkippedUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model() {
val params = paramsContainer.require<WalletActivationComponent.Params>()
@ -66,8 +70,19 @@ internal class WalletActivationModel @Inject constructor(
}
val currentRoute: MutableStateFlow<WalletActivationRoute> = MutableStateFlow(startRoute)
private val source = AnalyticsParam.ScreensSources.Main
private val action = WalletSettingsAnalyticEvents.RecoveryPhraseScreenAction.Backup
init {
trackingContextProxy.addHotWalletContext()
if (startRoute is WalletActivationRoute.ManualBackupStart) {
analyticsEventHandler.send(
event = WalletSettingsAnalyticEvents.RecoveryPhraseScreenInfo(
source = source.value,
action = action.value,
),
)
}
}
override fun onDestroy() {
@ -143,13 +158,23 @@ internal class WalletActivationModel @Inject constructor(
inner class ManualBackupStartModelCallbacks : ManualBackupStartComponent.ModelCallbacks {
override fun onContinueClick() {
stackNavigation.push(WalletActivationRoute.ManualBackupPhrase)
analyticsEventHandler.send(
event = WalletSettingsAnalyticEvents.RecoveryPhraseScreen(
source = source.value,
action = action.value,
),
)
}
}
inner class ManualBackupPhraseModelCallbacks : ManualBackupPhraseComponent.ModelCallbacks {
override fun onContinueClick() {
stackNavigation.push(
WalletActivationRoute.ManualBackupCheck,
stackNavigation.push(WalletActivationRoute.ManualBackupCheck)
analyticsEventHandler.send(
event = WalletSettingsAnalyticEvents.RecoveryPhraseCheck(
source = source.value,
action = action.value,
),
)
}
}
@ -157,6 +182,12 @@ internal class WalletActivationModel @Inject constructor(
inner class ManualBackupCheckModelCallbacks : ManualBackupCheckComponent.ModelCallbacks {
override fun onCompleteClick() {
stackNavigation.push(WalletActivationRoute.ManualBackupCompleted)
analyticsEventHandler.send(
event = WalletSettingsAnalyticEvents.BackupCompleteScreen(
source = source.value,
action = action.value,
),
)
}
}

View file

@ -1,6 +1,8 @@
package com.tangem.features.hotwallet.walletbackup.model
import com.tangem.common.routing.AppRoute
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
@ -11,6 +13,7 @@ import com.tangem.core.ui.components.label.entity.LabelStyle
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
import com.tangem.domain.wallets.usecase.GenerateBuyTangemCardLinkUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.UnlockHotWalletContextualUseCase
@ -27,12 +30,13 @@ import javax.inject.Inject
@ModelScoped
internal class WalletBackupModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val unlockHotWalletContextualUseCase: UnlockHotWalletContextualUseCase,
override val dispatchers: CoroutineDispatcherProvider,
private val generateBuyTangemCardLinkUseCase: GenerateBuyTangemCardLinkUseCase,
private val urlOpener: UrlOpener,
private val router: Router,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model() {
private val params: WalletBackupComponent.Params = paramsContainer.require()
@ -63,6 +67,7 @@ internal class WalletBackupModel @Inject constructor(
)
init {
analyticsEventHandler.send(WalletSettingsAnalyticEvents.BackupScreenOpened(isManualBackupEnabled = true))
getUserWalletUseCase.invokeFlow(params.userWalletId)
.onEach { either ->
either.fold(
@ -112,6 +117,7 @@ internal class WalletBackupModel @Inject constructor(
}
private fun onRecoveryPhraseClick() {
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonRecoveryPhrase)
if (uiState.value.backedUp) {
getUserWalletUseCase.invoke(params.userWalletId)
.fold(
@ -132,6 +138,8 @@ internal class WalletBackupModel @Inject constructor(
router.push(
AppRoute.CreateWalletBackup(
userWalletId = params.userWalletId,
analyticsSource = AnalyticsParam.ScreensSources.Backup.value,
analyticsAction = WalletSettingsAnalyticEvents.RecoveryPhraseScreenAction.Backup.value,
),
)
}
@ -152,6 +160,7 @@ internal class WalletBackupModel @Inject constructor(
}
private fun onHardwareWalletClick() {
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonHardwareUpdate)
router.push(AppRoute.WalletHardwareBackup(params.userWalletId))
}
}

View file

@ -2,6 +2,8 @@ package com.tangem.features.hotwallet.wallethardwarebackup.model
import arrow.core.getOrElse
import com.tangem.common.routing.AppRoute
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
@ -18,6 +20,7 @@ import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.bottomSheetMessage
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
import com.tangem.domain.wallets.usecase.GenerateBuyTangemCardLinkUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.wallets.usecase.UnlockHotWalletContextualUseCase
@ -46,30 +49,41 @@ internal class WalletHardwareBackupModel @Inject constructor(
private val urlOpener: UrlOpener,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val messageSender: UiMessageSender,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model() {
private val params = paramsContainer.require<WalletHardwareBackupComponent.Params>()
private val makeBackupAtFirstAlertBS
get() = bottomSheetMessage {
infoBlock {
icon(R.drawable.ic_passcode_lock_32) {
type = MessageBottomSheetUMV2.Icon.Type.Accent
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
get() = run {
analyticsEventHandler.send(
WalletSettingsAnalyticEvents.NoticeBackupFirst(
source = AnalyticsParam.ScreensSources.HardwareWallet.value,
action = WalletSettingsAnalyticEvents.NoticeBackupFirst.Action.Upgrade,
),
)
bottomSheetMessage {
infoBlock {
icon(R.drawable.ic_passcode_lock_32) {
type = MessageBottomSheetUMV2.Icon.Type.Accent
backgroundType = MessageBottomSheetUMV2.Icon.BackgroundType.SameAsTint
}
title = resourceReference(R.string.hw_backup_need_finish_first)
body = resourceReference(R.string.hw_backup_to_upgrade_description)
}
title = resourceReference(R.string.hw_backup_need_finish_first)
body = resourceReference(R.string.hw_backup_to_upgrade_description)
}
primaryButton {
text = resourceReference(R.string.hw_backup_need_action)
onClick {
router.push(
AppRoute.CreateWalletBackup(
userWalletId = params.userWalletId,
isUpgradeFlow = true,
),
)
closeBs()
primaryButton {
text = resourceReference(R.string.hw_backup_need_action)
onClick {
router.push(
AppRoute.CreateWalletBackup(
userWalletId = params.userWalletId,
isUpgradeFlow = true,
analyticsSource = AnalyticsParam.ScreensSources.HardwareWallet.value,
analyticsAction = WalletSettingsAnalyticEvents.RecoveryPhraseScreenAction.Upgrade.value,
),
)
closeBs()
}
}
}
}
@ -100,6 +114,7 @@ internal class WalletHardwareBackupModel @Inject constructor(
)
init {
analyticsEventHandler.send(WalletSettingsAnalyticEvents.HardwareBackupScreenOpened)
showPurchaseBlockWithDelay()
}
@ -111,6 +126,7 @@ internal class WalletHardwareBackupModel @Inject constructor(
}
private fun onCreateNewWalletClick() {
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonCreateNewWallet)
router.push(AppRoute.CreateHardwareWallet)
}
@ -118,6 +134,7 @@ internal class WalletHardwareBackupModel @Inject constructor(
val userWallet = getUserWalletUseCase.invoke(params.userWalletId)
.getOrElse { error("Cannot find user wallet with id: ${params.userWalletId.stringValue}") }
if (userWallet is UserWallet.Hot) {
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonUpgradeCurrent)
if (!userWallet.backedUp) {
messageSender.send(makeBackupAtFirstAlertBS)
} else {

View file

@ -1,14 +0,0 @@
package com.tangem.feature.walletsettings.analytics
import com.tangem.core.analytics.models.AnalyticsEvent
internal sealed class Settings(
category: String = "Settings",
event: String,
params: Map<String, String> = emptyMap(),
) : AnalyticsEvent(category, event, params) {
data object ButtonCreateBackup : Settings(event = "Button - Create Backup")
data object ButtonManageTokens : Settings(event = "Button - Manage Tokens")
}

View file

@ -1,17 +0,0 @@
package com.tangem.feature.walletsettings.analytics
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.AnalyticsParam.Key.STATUS
internal sealed class WalletSettingsAnalyticEvents(
category: String = "Settings / Wallet",
event: String,
params: Map<String, String> = emptyMap(),
) : AnalyticsEvent(category, event, params) {
data class NftToggleSwitch(val enabled: AnalyticsParam.OnOffState) : WalletSettingsAnalyticEvents(
event = "NFT toggle switch",
params = mapOf(STATUS to enabled.value),
)
}

View file

@ -5,7 +5,6 @@ import androidx.compose.ui.Modifier
import com.tangem.common.ui.account.AccountIconPreviewData
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.common.ui.userwallet.state.UserWalletItemUM.ImageState
import com.tangem.core.decompose.navigation.DummyRouter
import com.tangem.core.ui.components.block.model.BlockUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
@ -43,9 +42,7 @@ internal class PreviewWalletSettingsComponent : WalletSettingsComponent {
private val previewState = WalletSettingsUM(
popBack = {},
items = ItemsBuilder(
router = DummyRouter(),
).buildItems(
items = ItemsBuilder().buildItems(
userWallet = UserWallet.Hot(
walletId = UserWalletId("011"),
name = "My Wallet",
@ -71,6 +68,8 @@ internal class PreviewWalletSettingsComponent : WalletSettingsComponent {
walletUpgradeDismissed = false,
onUpgradeWalletClick = {},
onDismissUpgradeWalletClick = {},
onBackupClick = {},
onCardSettingsClick = {},
accountsUM = previewAccounts(),
cardItem = previewCardBlock(),
),

View file

@ -8,6 +8,7 @@ import com.arkivanov.decompose.router.slot.activate
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRoute.ManageTokens.Source
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.AnalyticsParam.OnOffState.Off
import com.tangem.core.analytics.models.AnalyticsParam.OnOffState.On
import com.tangem.core.analytics.utils.TrackingContextProxy
@ -36,9 +37,10 @@ import com.tangem.domain.nft.EnableWalletNFTUseCase
import com.tangem.domain.nft.GetWalletNFTEnabledUseCase
import com.tangem.domain.notifications.repository.NotificationsRepository
import com.tangem.domain.settings.repositories.PermissionRepository
import com.tangem.domain.wallets.analytics.Settings
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents.RecoveryPhraseScreenAction
import com.tangem.domain.wallets.usecase.*
import com.tangem.feature.walletsettings.analytics.Settings
import com.tangem.feature.walletsettings.analytics.WalletSettingsAnalyticEvents
import com.tangem.feature.walletsettings.component.WalletSettingsComponent
import com.tangem.feature.walletsettings.entity.*
import com.tangem.feature.walletsettings.impl.R
@ -111,8 +113,9 @@ internal class WalletSettingsModel @Inject constructor(
)
init {
getUserWalletUseCase.invoke(params.userWalletId).onRight {
trackingContextProxy.addContext(it)
getUserWalletUseCase.invoke(params.userWalletId).onRight { wallet ->
trackingContextProxy.addContext(wallet)
analyticsEventHandler.send(WalletSettingsAnalyticEvents.WalletSettingsScreenOpened)
}
fun combineUI(wallet: UserWallet) = combine(
@ -235,10 +238,12 @@ internal class WalletSettingsModel @Inject constructor(
isNotificationsPermissionGranted = isNotificationsPermissionGranted,
onCheckedNotificationsChanged = ::onCheckedNotificationsChange,
onNotificationsDescriptionClick = ::onNotificationsDescriptionClick,
onAccessCodeClick = ::onAccessCodeClick,
onAccessCodeClick = { onAccessCodeClick(userWallet) },
walletUpgradeDismissed = isUpgradeNotificationEnabled,
onUpgradeWalletClick = ::onUpgradeWalletClick,
onUpgradeWalletClick = { onUpgradeWalletClick() },
onDismissUpgradeWalletClick = ::onDismissUpgradeWalletClick,
onBackupClick = ::onBackupClick,
onCardSettingsClick = ::onCardSettingsClick,
accountsUM = accountList,
)
}
@ -262,7 +267,6 @@ internal class WalletSettingsModel @Inject constructor(
}
private fun onLinkMoreCardsClick(scanResponse: ScanResponse) {
analyticsEventHandler.send(Settings.ButtonCreateBackup)
trackingContextProxy.addContext(scanResponse)
router.push(
@ -357,23 +361,35 @@ internal class WalletSettingsModel @Inject constructor(
}
}
private fun onAccessCodeClick() {
if (!state.value.isWalletBackedUp) {
showMakeBackupAtFirstAlertBS(isUpgradeFlow = false)
} else {
unlockWalletIfNeedAndProceed { authorizationRequired ->
router.push(
route = AppRoute.UpdateAccessCode(
userWalletId = params.userWalletId,
),
private fun onAccessCodeClick(userWallet: UserWallet) {
if (userWallet is UserWallet.Hot) {
val isCodeSet = userWallet.hotWalletId.authType != HotWalletId.AuthType.NoPassword
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonAccessCode(isCodeSet))
if (!state.value.isWalletBackedUp) {
showMakeBackupAtFirstAlertBS(
isUpgradeFlow = false,
action = WalletSettingsAnalyticEvents.NoticeBackupFirst.Action.AccessCode,
)
} else {
unlockWalletIfNeedAndProceed { authorizationRequired ->
router.push(
route = AppRoute.UpdateAccessCode(
userWalletId = params.userWalletId,
source = AnalyticsParam.ScreensSources.WalletSettings.value,
),
)
}
}
}
}
private fun onUpgradeWalletClick() {
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonHardwareUpdate)
if (!state.value.isWalletBackedUp) {
showMakeBackupAtFirstAlertBS(isUpgradeFlow = true)
showMakeBackupAtFirstAlertBS(
isUpgradeFlow = true,
action = WalletSettingsAnalyticEvents.NoticeBackupFirst.Action.Upgrade,
)
} else {
unlockWalletIfNeedAndProceed {
router.push(AppRoute.UpgradeWallet(userWalletId = params.userWalletId))
@ -387,7 +403,25 @@ internal class WalletSettingsModel @Inject constructor(
}
}
private fun showMakeBackupAtFirstAlertBS(isUpgradeFlow: Boolean) {
private fun onBackupClick() {
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonBackup)
router.push(AppRoute.WalletBackup(params.userWalletId))
}
private fun onCardSettingsClick() {
router.push(AppRoute.CardSettings(params.userWalletId))
}
private fun showMakeBackupAtFirstAlertBS(
action: WalletSettingsAnalyticEvents.NoticeBackupFirst.Action,
isUpgradeFlow: Boolean,
) {
analyticsEventHandler.send(
event = WalletSettingsAnalyticEvents.NoticeBackupFirst(
source = AnalyticsParam.ScreensSources.WalletSettings.value,
action = action,
),
)
val message = bottomSheetMessage {
infoBlock {
icon(R.drawable.ic_passcode_lock_32) {
@ -405,6 +439,12 @@ internal class WalletSettingsModel @Inject constructor(
userWalletId = params.userWalletId,
isUpgradeFlow = isUpgradeFlow,
setAccessCode = true,
analyticsSource = AnalyticsParam.ScreensSources.WalletSettings.value,
analyticsAction = if (isUpgradeFlow) {
RecoveryPhraseScreenAction.Backup.value
} else {
RecoveryPhraseScreenAction.AccessCode.value
},
),
)
closeBs()
@ -478,6 +518,15 @@ internal class WalletSettingsModel @Inject constructor(
)
}
is UserWallet.Hot -> {
if (!userWallet.backedUp) {
analyticsEventHandler.send(
event = WalletSettingsAnalyticEvents.NoticeBackupFirst(
source = AnalyticsParam.ScreensSources.WalletSettings.value,
action = WalletSettingsAnalyticEvents.NoticeBackupFirst.Action.Remove,
),
)
}
bottomSheetMessage {
infoBlock {
icon(R.drawable.ic_alert_circle_24) {
@ -525,6 +574,8 @@ internal class WalletSettingsModel @Inject constructor(
router.push(
AppRoute.CreateWalletBackup(
userWalletId = userWallet.walletId,
analyticsSource = AnalyticsParam.ScreensSources.WalletSettings.value,
analyticsAction = RecoveryPhraseScreenAction.Remove.value,
),
)
closeBs()

View file

@ -1,8 +1,6 @@
package com.tangem.feature.walletsettings.utils
import com.tangem.common.routing.AppRoute
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.ui.components.block.model.BlockUM
import com.tangem.core.ui.components.label.entity.LabelStyle
import com.tangem.core.ui.components.label.entity.LabelUM
@ -18,9 +16,7 @@ import kotlinx.collections.immutable.toImmutableList
import javax.inject.Inject
@ModelScoped
internal class ItemsBuilder @Inject constructor(
private val router: Router,
) {
internal class ItemsBuilder @Inject constructor() {
@Suppress("LongParameterList")
fun buildItems(
@ -45,6 +41,8 @@ internal class ItemsBuilder @Inject constructor(
walletUpgradeDismissed: Boolean,
onUpgradeWalletClick: () -> Unit,
onDismissUpgradeWalletClick: () -> Unit,
onBackupClick: () -> Unit,
onCardSettingsClick: () -> Unit,
): PersistentList<WalletSettingsItemUM> = persistentListOf<WalletSettingsItemUM>()
.add(cardItem)
.addAll(
@ -66,6 +64,8 @@ internal class ItemsBuilder @Inject constructor(
onLinkMoreCardsClick = onLinkMoreCardsClick,
onReferralClick = onReferralClick,
onManageTokensClick = onManageTokensClick,
onBackupClick = onBackupClick,
onCardSettingsClick = onCardSettingsClick,
),
)
.addAll(
@ -173,18 +173,19 @@ internal class ItemsBuilder @Inject constructor(
onLinkMoreCardsClick: () -> Unit,
onReferralClick: () -> Unit,
onManageTokensClick: () -> Unit,
onBackupClick: () -> Unit,
onCardSettingsClick: () -> Unit,
) = WalletSettingsItemUM.WithItems(
id = "card",
description = resourceReference(R.string.settings_card_settings_footer),
blocks = buildList {
val userWalletId = userWallet.walletId
val isHotWallet = userWallet is UserWallet.Hot
if (isHotWallet) {
val hasBackup = userWallet.backedUp
val backupBlock = BlockUM(
text = resourceReference(R.string.common_backup),
iconRes = R.drawable.ic_more_cards_24,
onClick = { router.push(AppRoute.WalletBackup(userWalletId)) },
onClick = { onBackupClick() },
endContent = if (hasBackup) {
BlockUM.EndContent.None
} else {
@ -224,7 +225,7 @@ internal class ItemsBuilder @Inject constructor(
val deviceSettingsBlock = BlockUM(
text = resourceReference(R.string.card_settings_title),
iconRes = R.drawable.ic_card_settings_24,
onClick = { router.push(AppRoute.CardSettings(userWalletId)) },
onClick = { onCardSettingsClick() },
)
add(deviceSettingsBlock)