From 047a48eaf0dcaf082bc7b1e189d8251eb60ef934 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 30 Jan 2026 20:48:10 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/WalletsDomainModule.kt | 3 +++ .../DefaultUserWalletsListRepository.kt | 6 +++++ .../core/analytics/models/AnalyticsParam.kt | 1 + .../domain/wallets/analytics/Settings.kt | 12 ++++++++++ .../analytics/WalletSettingsAnalyticEvents.kt | 5 ++++ .../wallets/usecase/SaveWalletUseCase.kt | 23 +++++++++++++++---- .../features/details/utils/UserWalletSaver.kt | 7 +++++- .../CreateHardwareWalletModel.kt | 5 +++- .../model/MultiWalletFinalizeModel.kt | 2 ++ .../model/OnboardingVisaInProgressModel.kt | 6 ++++- .../analytics/WalletScreenAnalyticsEvent.kt | 2 +- .../welcome/impl/model/WelcomeModel.kt | 22 ++++++++++-------- 12 files changed, 76 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt index 374af96dd0..ba29372fdb 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt @@ -1,5 +1,6 @@ package com.tangem.tap.di.domain +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.domain.common.wallets.UserWalletsListRepository import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.tokens.repository.CurrenciesRepository @@ -129,12 +130,14 @@ internal object WalletsDomainModule { userWalletsListRepository: UserWalletsListRepository, hotWalletFeatureToggles: HotWalletFeatureToggles, walletsRepository: WalletsRepository, + analyticsEventHandler: AnalyticsEventHandler, ): SaveWalletUseCase { return SaveWalletUseCase( userWalletsListManager = userWalletsListManager, userWalletsListRepository = userWalletsListRepository, walletsRepository = walletsRepository, useNewRepository = hotWalletFeatureToggles.isHotWalletEnabled, + analyticsEventHandler = analyticsEventHandler, ) } diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt index 80dce033a7..414ea8a0b3 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt @@ -21,6 +21,7 @@ import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.isLocked import com.tangem.domain.wallets.R +import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents import com.tangem.domain.wallets.builder.UserWalletIdBuilder import com.tangem.domain.wallets.hot.HotWalletAccessCodeAttemptsRepository import com.tangem.domain.wallets.hot.HotWalletPasswordRequester @@ -390,6 +391,7 @@ internal class DefaultUserWalletsListRepository( if (newUserWallet.walletId == oldUserWallet.walletId && oldUserWallet is UserWallet.Hot && newUserWallet is UserWallet.Cold ) { + trackWalletUpgradeEvent() removeHotWalletsFromSDKAndRepos(walletIds = listOf(oldUserWallet.walletId)) // When upgrading from Hot to Cold, if biometric lock is available, set it if (hasBiometry()) { @@ -536,4 +538,8 @@ internal class DefaultUserWalletsListRepository( ), ) } + + private fun trackWalletUpgradeEvent() { + analyticsEventHandler.send(event = WalletSettingsAnalyticEvents.WalletUpgraded()) + } } \ No newline at end of file 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 9488b9c840..cff2fe1375 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 @@ -85,6 +85,7 @@ sealed class AnalyticsParam { data object ImportWallet : ScreensSources("Import Wallet") data object CreateWalletIntro : ScreensSources("Create Wallet Intro") data object AddNewWallet : ScreensSources("Add New Wallet") + data object AddNew : ScreensSources("Add New") data object CreateWallet : ScreensSources("Create Wallet") } diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/analytics/Settings.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/analytics/Settings.kt index 703dcb8cbb..7f8f5b9916 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/analytics/Settings.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/analytics/Settings.kt @@ -1,6 +1,9 @@ package com.tangem.domain.wallets.analytics import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.AppsFlyerIncludedEvent +import com.tangem.core.analytics.models.OneTimeAnalyticsEvent sealed class Settings( category: String = "Settings", @@ -9,4 +12,13 @@ sealed class Settings( ) : AnalyticsEvent(category, event, params) { class ButtonManageTokens : Settings(event = "Button - Manage Tokens") + + class ColdWalletAdded( + source: AnalyticsParam.ScreensSources?, + ) : Settings( + event = "Cold Wallet Added", + params = mapOf(AnalyticsParam.SOURCE to (source?.value ?: "Unknown")), + ), OneTimeAnalyticsEvent, AppsFlyerIncludedEvent { + override val oneTimeEventId: String = id + } } \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/analytics/WalletSettingsAnalyticEvents.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/analytics/WalletSettingsAnalyticEvents.kt index 1c23c85475..be4e913124 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/analytics/WalletSettingsAnalyticEvents.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/analytics/WalletSettingsAnalyticEvents.kt @@ -4,6 +4,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.AnalyticsParam.Key.ACTION import com.tangem.core.analytics.models.AnalyticsParam.Key.STATUS +import com.tangem.core.analytics.models.AppsFlyerIncludedEvent sealed class WalletSettingsAnalyticEvents( category: String = "Settings / Wallet Settings", @@ -166,6 +167,10 @@ sealed class WalletSettingsAnalyticEvents( event = "Button - Start Upgrade", ) + class WalletUpgraded : WalletSettingsAnalyticEvents( + event = "Wallet Upgraded", + ), AppsFlyerIncludedEvent + enum class RecoveryPhraseScreenAction(val value: String) { Upgrade("Upgrade"), Backup("Backup"), diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/SaveWalletUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/SaveWalletUseCase.kt index 354d0b6bc8..75f322f635 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/SaveWalletUseCase.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/SaveWalletUseCase.kt @@ -6,9 +6,12 @@ import arrow.core.raise.either import arrow.core.right import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.domain.common.wallets.UserWalletsListRepository import com.tangem.domain.common.wallets.error.SaveWalletError import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.wallets.analytics.Settings import com.tangem.domain.wallets.legacy.UserWalletsListError import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.repository.WalletsRepository @@ -16,8 +19,6 @@ import com.tangem.domain.wallets.repository.WalletsRepository /** * Use case for saving user wallet * - * @property userWalletsListManager user wallets list manager - * [REDACTED_AUTHOR] */ class SaveWalletUseCase( @@ -25,14 +26,21 @@ class SaveWalletUseCase( private val userWalletsListRepository: UserWalletsListRepository, private val walletsRepository: WalletsRepository, private val useNewRepository: Boolean, + private val analyticsEventHandler: AnalyticsEventHandler, ) { - suspend operator fun invoke(userWallet: UserWallet, canOverride: Boolean = false): Either { + suspend operator fun invoke( + userWallet: UserWallet, + canOverride: Boolean = false, + analyticsSource: AnalyticsParam.ScreensSources? = null, + ): Either { return if (useNewRepository) { either { val newUserWallet = userWalletsListRepository.userWalletsSync().none { it.walletId == userWallet.walletId } - val userWallet = userWalletsListRepository.saveWithoutLock(userWallet, canOverride).bind() + val userWallet = userWalletsListRepository.saveWithoutLock(userWallet, canOverride) + .onRight { trackColdWalletAddedIfNeeded(analyticsSource, it) } + .bind() if (newUserWallet) { when (userWallet) { @@ -76,4 +84,11 @@ class SaveWalletUseCase( } } } + + private suspend fun trackColdWalletAddedIfNeeded(source: AnalyticsParam.ScreensSources?, userWallet: UserWallet) { + val hasHotWallet = userWalletsListRepository.userWalletsSync().any { it is UserWallet.Hot } + if (hasHotWallet && userWallet is UserWallet.Cold) { + analyticsEventHandler.send(event = Settings.ColdWalletAdded(source)) + } + } } \ No newline at end of file diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt index 04d7e91736..59be5a0529 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt @@ -75,7 +75,12 @@ internal class UserWalletSaver @Inject constructor( private suspend fun Raise.saveWallet(userWallet: UserWallet) { fold( - block = { saveWalletUseCase(userWallet).bind() }, + block = { + saveWalletUseCase( + userWallet = userWallet, + analyticsSource = AnalyticsParam.ScreensSources.Settings, + ).bind() + }, recover = { error -> when (error) { is SaveWalletError.WalletAlreadySaved -> { diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createhardwarewallet/CreateHardwareWalletModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createhardwarewallet/CreateHardwareWalletModel.kt index 39a2c8b242..5d32c8952b 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createhardwarewallet/CreateHardwareWalletModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createhardwarewallet/CreateHardwareWalletModel.kt @@ -133,7 +133,10 @@ internal class CreateHardwareWalletModel @Inject constructor( return } - saveWalletUseCase(userWallet = userWallet).fold( + saveWalletUseCase( + userWallet = userWallet, + analyticsSource = AnalyticsParam.ScreensSources.AddNew, + ).fold( ifLeft = { saveWalletError -> delay(HIDE_PROGRESS_DELAY) setLoading(false) diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt index 4c9424cf98..fab52080ae 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt @@ -5,6 +5,7 @@ import arrow.core.getOrElse import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.ByteArrayKey +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 @@ -267,6 +268,7 @@ internal class MultiWalletFinalizeModel @Inject constructor( scanResponse = scanResponse.updateScanResponseAfterBackup(), ), canOverride = true, + analyticsSource = AnalyticsParam.ScreensSources.Onboarding, ) userWalletCreated } diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/inprogress/model/OnboardingVisaInProgressModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/inprogress/model/OnboardingVisaInProgressModel.kt index 235c201961..abc373283f 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/inprogress/model/OnboardingVisaInProgressModel.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/inprogress/model/OnboardingVisaInProgressModel.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.Stable import arrow.core.getOrElse import com.tangem.common.extensions.toHexString 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 @@ -173,7 +174,10 @@ internal class OnboardingVisaInProgressModel @Inject constructor( } val userWallet = createUserWallet(params.scanResponse, newTokens) - saveWalletUseCase(userWallet) + saveWalletUseCase( + userWallet = userWallet, + analyticsSource = AnalyticsParam.ScreensSources.Onboarding, + ) visaAuthTokenStorage.remove(params.scanResponse.card.cardId) otpStorage.removeOTP(params.scanResponse.card.cardId) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt index 1eb6944175..e766bf4333 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt @@ -72,7 +72,7 @@ sealed class WalletScreenAnalyticsEvent { } put("Wallet Type", seedPhrase) }, - ) + ), AppsFlyerIncludedEvent data class NoticeFinishActivation( private val activationState: ActivationState, diff --git a/features/welcome/impl/src/main/kotlin/com/tangem/features/welcome/impl/model/WelcomeModel.kt b/features/welcome/impl/src/main/kotlin/com/tangem/features/welcome/impl/model/WelcomeModel.kt index 4179ffefe8..83ab025490 100644 --- a/features/welcome/impl/src/main/kotlin/com/tangem/features/welcome/impl/model/WelcomeModel.kt +++ b/features/welcome/impl/src/main/kotlin/com/tangem/features/welcome/impl/model/WelcomeModel.kt @@ -197,18 +197,20 @@ internal class WelcomeModel @Inject constructor( onSuccess = { scanResponse -> val userWallet = coldUserWalletBuilderFactory.create(scanResponse = scanResponse).build() ?: return@scan - saveWalletUseCase.invoke(userWallet) - .onLeft { error -> - if (error is SaveWalletError.WalletAlreadySaved) { - userWalletsListRepository.unlock( - userWallet.walletId, - unlockMethod = UserWalletsListRepository.UnlockMethod.Scan(scanResponse), - ).onRight { - userWalletsListRepository.select(userWallet.walletId) - router.replaceAll(AppRoute.Wallet) - } + saveWalletUseCase.invoke( + userWallet = userWallet, + analyticsSource = AnalyticsParam.ScreensSources.SignIn, + ).onLeft { error -> + if (error is SaveWalletError.WalletAlreadySaved) { + userWalletsListRepository.unlock( + userWallet.walletId, + unlockMethod = UserWalletsListRepository.UnlockMethod.Scan(scanResponse), + ).onRight { + userWalletsListRepository.select(userWallet.walletId) + router.replaceAll(AppRoute.Wallet) } } + } .onRight { router.replaceAll(AppRoute.Wallet) }