Updated on 2026-08-14
This commit is contained in:
parent
85545863e3
commit
926e89e975
16 changed files with 158 additions and 223 deletions
|
|
@ -1,15 +0,0 @@
|
|||
package com.tangem.tap.common.analytics.events
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
sealed class SignIn(
|
||||
event: String,
|
||||
params: Map<String, String> = emptyMap(),
|
||||
) : AnalyticsEvent("Sign In", event, params) {
|
||||
|
||||
class ButtonBiometricSignIn : SignIn(event = "Button - Biometric Sign In")
|
||||
class ButtonCardSignIn : SignIn(event = "Button - Card Sign In")
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import arrow.core.right
|
|||
import com.tangem.common.*
|
||||
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.Basic
|
||||
import com.tangem.core.analytics.utils.TrackingContextProxy
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
|
|
@ -19,10 +20,7 @@ import com.tangem.domain.common.wallets.UserWalletsListRepository
|
|||
import com.tangem.domain.common.wallets.UserWalletsListRepository.LockMethod
|
||||
import com.tangem.domain.common.wallets.error.*
|
||||
import com.tangem.domain.hotwallet.repository.HotWalletRepository
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isImported
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
import com.tangem.domain.models.wallet.*
|
||||
import com.tangem.domain.wallets.R
|
||||
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
|
||||
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
|
||||
|
|
@ -261,7 +259,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
when (unlockMethod) {
|
||||
UserWalletsListRepository.UnlockMethod.Biometric -> {
|
||||
unlockAllWallets().bind()
|
||||
trackSignInEvent(userWallet, Basic.SignedIn.SignInType.Biometric)
|
||||
trackSignInEvent(userWallet, AnalyticsParam.SignInType.Biometric)
|
||||
select(userWalletId)
|
||||
}
|
||||
UserWalletsListRepository.UnlockMethod.AccessCode -> {
|
||||
|
|
@ -292,7 +290,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
sensitiveInformationRepository.getAll(listOf(encryptionKey))
|
||||
.doOnSuccess { sensitiveInfo ->
|
||||
updateWallets { it?.updateWith(sensitiveInfo) }
|
||||
trackSignInEvent(userWallet, Basic.SignedIn.SignInType.AccessCode)
|
||||
trackSignInEvent(userWallet, AnalyticsParam.SignInType.AccessCode)
|
||||
}
|
||||
.doOnFailure { error -> raise(UnlockWalletError.UnableToUnlock.RawException(error)) }
|
||||
}
|
||||
|
|
@ -330,7 +328,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
walletIdToDerivedKeys = mapOf(userWallet.walletId to scanResponse.derivedKeys),
|
||||
)
|
||||
}
|
||||
trackSignInEvent(userWallet, Basic.SignedIn.SignInType.Card)
|
||||
trackSignInEvent(userWallet, AnalyticsParam.SignInType.Card)
|
||||
}
|
||||
.doOnFailure { error -> raise(UnlockWalletError.UnableToUnlock.RawException(error)) }
|
||||
}
|
||||
|
|
@ -373,7 +371,7 @@ internal class DefaultUserWalletsListRepository(
|
|||
.doOnSuccess { sensitiveInfo ->
|
||||
updateWallets { wallets -> wallets?.updateWith(sensitiveInfo) }
|
||||
selectedUserWallet.value?.let {
|
||||
trackSignInEvent(it, Basic.SignedIn.SignInType.Biometric)
|
||||
trackSignInEvent(it, AnalyticsParam.SignInType.Biometric)
|
||||
}
|
||||
}
|
||||
.doOnFailure { error -> raise(UnlockWalletError.UnableToUnlock.RawException(error)) }
|
||||
|
|
@ -602,18 +600,14 @@ internal class DefaultUserWalletsListRepository(
|
|||
return lastOrNull()
|
||||
}
|
||||
|
||||
private fun trackSignInEvent(userWallet: UserWallet, type: Basic.SignedIn.SignInType) {
|
||||
private fun trackSignInEvent(userWallet: UserWallet, type: AnalyticsParam.SignInType) {
|
||||
trackingContextProxy.addContext(userWallet)
|
||||
val isBackedUp = when (userWallet) {
|
||||
is UserWallet.Cold -> userWallet.scanResponse.card.backupStatus?.isActive == true
|
||||
is UserWallet.Hot -> userWallet.backedUp
|
||||
}
|
||||
analyticsEventHandler.send(
|
||||
event = Basic.SignedIn(
|
||||
signInType = type,
|
||||
walletsCount = userWallets.value?.size ?: 0,
|
||||
isImported = userWallet.isImported(),
|
||||
hasBackup = isBackedUp,
|
||||
isBackedUp = userWallet.isBackedUpForAnalytics(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.tangem.common.routing.entity.InitScreenLaunchMode
|
|||
import com.tangem.common.ui.notifications.NotificationId
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.core.analytics.utils.TrackingContextProxy
|
||||
|
|
@ -30,7 +31,7 @@ import com.tangem.core.ui.message.SnackbarMessage
|
|||
import com.tangem.domain.card.repository.CardRepository
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.isBackedUpForAnalytics
|
||||
import com.tangem.domain.models.wallet.isImported
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
|
|
@ -360,16 +361,12 @@ internal class DefaultRoutingComponent @AssistedInject constructor(
|
|||
val userWallets = userWalletsListRepository.userWalletsSync()
|
||||
val selectedWallet = userWalletsListRepository.selectedUserWalletSync() ?: return
|
||||
trackingContextProxy.addContext(selectedWallet)
|
||||
val isBackedUp = when (selectedWallet) {
|
||||
is UserWallet.Cold -> selectedWallet.scanResponse.card.backupStatus?.isActive == true
|
||||
is UserWallet.Hot -> selectedWallet.backedUp
|
||||
}
|
||||
analyticsEventHandler.send(
|
||||
event = Basic.SignedIn(
|
||||
signInType = Basic.SignedIn.SignInType.NoSecurity,
|
||||
signInType = AnalyticsParam.SignInType.NoSecurity,
|
||||
walletsCount = userWallets.size,
|
||||
isImported = selectedWallet.isImported(),
|
||||
hasBackup = isBackedUp,
|
||||
isBackedUp = selectedWallet.isBackedUpForAnalytics(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -239,6 +239,13 @@ sealed class AnalyticsParam {
|
|||
MobileWallet("Mobile Wallet"),
|
||||
}
|
||||
|
||||
enum class SignInType(val value: String) {
|
||||
Card("Card"),
|
||||
Biometric("Biometric"),
|
||||
NoSecurity("No Security"),
|
||||
AccessCode("Access Code"),
|
||||
}
|
||||
|
||||
companion object Key {
|
||||
const val BLOCKCHAIN = "Blockchain"
|
||||
const val TOKEN_PARAM = "Token"
|
||||
|
|
@ -297,6 +304,11 @@ sealed class AnalyticsParam {
|
|||
const val REFERRAL_ID = "Referral_ID"
|
||||
const val SEARCHED = "Searched"
|
||||
const val RATE_TYPE = "Rate Type"
|
||||
const val SIGN_IN_TYPE = "Sign in type"
|
||||
const val WALLETS_COUNT = "Wallets Count"
|
||||
const val WALLET_TYPE = "Wallet Type"
|
||||
const val BACKUPED = "Backuped"
|
||||
const val MEMO = "Memo"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,65 +2,41 @@ package com.tangem.core.analytics.models
|
|||
|
||||
sealed class Basic(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent("Basic", event, params) {
|
||||
params: Map<String, String> = emptyMap(),
|
||||
) : AnalyticsEvent(category = "Basic", event = event, params = params) {
|
||||
|
||||
/**
|
||||
* Tracks card scanning from specific entry points (Introduction, Main, My Wallets, Sign In).
|
||||
* The originating screen is reported via the [AnalyticsParam.SOURCE] parameter.
|
||||
*/
|
||||
class CardWasScanned(
|
||||
source: AnalyticsParam.ScreensSources,
|
||||
) : Basic(
|
||||
event = "Card Was Scanned",
|
||||
params = mapOf(
|
||||
AnalyticsParam.Key.SOURCE to source.value,
|
||||
AnalyticsParam.SOURCE to source.value,
|
||||
),
|
||||
)
|
||||
|
||||
class SignedInLegacy(
|
||||
currency: AnalyticsParam.WalletType,
|
||||
batch: String,
|
||||
signInType: SignInType,
|
||||
walletsCount: String,
|
||||
isImported: Boolean,
|
||||
hasBackup: Boolean?,
|
||||
) : Basic(
|
||||
event = "Signed in",
|
||||
params = buildMap {
|
||||
put(AnalyticsParam.Key.CURRENCY, currency.value)
|
||||
put(AnalyticsParam.Key.BATCH, batch)
|
||||
put("Wallet Type", if (isImported) "Seed Phrase" else "Seedless")
|
||||
put("Sign in type", signInType.name)
|
||||
put("Wallets Count", walletsCount)
|
||||
if (hasBackup != null) {
|
||||
put("Backuped", if (hasBackup) "Yes" else "No")
|
||||
}
|
||||
},
|
||||
) {
|
||||
enum class SignInType {
|
||||
Card, Biometric
|
||||
}
|
||||
}
|
||||
), CriticalEvent
|
||||
|
||||
/**
|
||||
* Tracks any sign-in into a wallet (card scan, FaceID, or wallet switch).
|
||||
* Counted as a single sign-in per session — subsequent card scans within the same session are ignored.
|
||||
*/
|
||||
class SignedIn(
|
||||
signInType: SignInType,
|
||||
signInType: AnalyticsParam.SignInType,
|
||||
walletsCount: Int,
|
||||
isImported: Boolean,
|
||||
hasBackup: Boolean?,
|
||||
isBackedUp: Boolean,
|
||||
) : Basic(
|
||||
event = "Signed in",
|
||||
params = buildMap {
|
||||
put("Sign in type", signInType.value)
|
||||
put("Wallets Count", walletsCount.toString())
|
||||
put("Wallet Type", if (isImported) "Seed Phrase" else "Seedless")
|
||||
if (hasBackup != null) {
|
||||
put("Backuped", if (hasBackup) "Yes" else "No")
|
||||
}
|
||||
put(AnalyticsParam.SIGN_IN_TYPE, signInType.value)
|
||||
put(AnalyticsParam.WALLETS_COUNT, walletsCount.toString())
|
||||
put(AnalyticsParam.WALLET_TYPE, if (isImported) "Seed Phrase" else "Seedless")
|
||||
put(AnalyticsParam.BACKUPED, if (isBackedUp) "Yes" else "No")
|
||||
},
|
||||
) {
|
||||
enum class SignInType(val value: String) {
|
||||
Card("Card"),
|
||||
Biometric("Biometric"),
|
||||
NoSecurity("No Security"),
|
||||
AccessCode("Access Code"),
|
||||
}
|
||||
), CriticalEvent, OneTimePerSessionEvent {
|
||||
override val oneTimeEventId: String = id
|
||||
}
|
||||
|
||||
class ButtonBuy(
|
||||
|
|
@ -68,39 +44,46 @@ sealed class Basic(
|
|||
) : Basic(
|
||||
event = "Button - Buy",
|
||||
params = buildMap {
|
||||
put(AnalyticsParam.Key.SOURCE, source.value)
|
||||
put(AnalyticsParam.SOURCE, source.value)
|
||||
},
|
||||
)
|
||||
|
||||
class ToppedUp(userWalletId: String, currency: AnalyticsParam.WalletType) :
|
||||
/**
|
||||
* Tracks the first time a user wallet is topped up. Sent once per wallet, when the balance
|
||||
* transitions from zero to positive (Total Balance for multi-currency wallets, or Balance for Note).
|
||||
* A wallet scanned with a non-zero balance does not count as a top-up — the event must be sent
|
||||
* only after all tokens have finished loading.
|
||||
*/
|
||||
class ToppedUp(userWalletId: String, walletType: AnalyticsParam.WalletType) :
|
||||
Basic(
|
||||
event = "Topped up",
|
||||
params = mapOf(AnalyticsParam.Key.CURRENCY to currency.value),
|
||||
params = mapOf(AnalyticsParam.CURRENCY to walletType.value),
|
||||
),
|
||||
OneTimeAnalyticsEvent {
|
||||
OneTimeAnalyticsEvent, AppsFlyerIncludedEvent, CriticalEvent {
|
||||
|
||||
override val oneTimeEventId: String = id + userWalletId
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks transaction submission from various screens (Send, Swap, WalletConnect, Sell, Approve, Staking).
|
||||
*/
|
||||
class TransactionSent(sentFrom: AnalyticsParam.TxSentFrom, memoType: MemoType) :
|
||||
Basic(
|
||||
event = "Transaction sent",
|
||||
params = buildMap {
|
||||
this[AnalyticsParam.Key.SOURCE] = sentFrom.value
|
||||
put(AnalyticsParam.SOURCE, sentFrom.value)
|
||||
if (sentFrom is AnalyticsParam.TxData) {
|
||||
this[AnalyticsParam.Key.BLOCKCHAIN] = sentFrom.blockchain
|
||||
this[AnalyticsParam.Key.TOKEN_PARAM] = sentFrom.token
|
||||
sentFrom.feeType?.value?.let {
|
||||
this[AnalyticsParam.Key.FEE_TYPE] = it
|
||||
}
|
||||
this[AnalyticsParam.Key.FEE_TOKEN] = sentFrom.feeToken
|
||||
put(AnalyticsParam.BLOCKCHAIN, sentFrom.blockchain)
|
||||
put(AnalyticsParam.TOKEN_PARAM, sentFrom.token)
|
||||
sentFrom.feeType?.value?.let { put(AnalyticsParam.FEE_TYPE, it) }
|
||||
put(AnalyticsParam.FEE_TOKEN, sentFrom.feeToken)
|
||||
}
|
||||
if (sentFrom is AnalyticsParam.TxSentFrom.Approve) {
|
||||
this[AnalyticsParam.Key.PERMISSION_TYPE] = sentFrom.permissionType
|
||||
put(AnalyticsParam.PERMISSION_TYPE, sentFrom.permissionType)
|
||||
}
|
||||
this["Memo"] = memoType.name
|
||||
put(AnalyticsParam.MEMO, memoType.name)
|
||||
},
|
||||
), AppsFlyerIncludedEvent {
|
||||
), AppsFlyerIncludedEvent, CriticalEvent {
|
||||
enum class MemoType {
|
||||
Empty, Full, Null
|
||||
}
|
||||
|
|
@ -110,31 +93,33 @@ sealed class Basic(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks the user invoking the "Request Support" email flow from various screens of the app.
|
||||
*/
|
||||
class ButtonSupport(source: AnalyticsParam.ScreensSources) : Basic(
|
||||
event = "Request Support",
|
||||
params = mapOf(
|
||||
AnalyticsParam.Key.SOURCE to source.value,
|
||||
AnalyticsParam.SOURCE to source.value,
|
||||
),
|
||||
), CriticalEvent
|
||||
|
||||
/**
|
||||
* Tracks loading of the user's total balance after sign-in. Reports whether the balance is
|
||||
* empty, has funds, failed to load, or could not be returned because of a custom token.
|
||||
*/
|
||||
class BalanceLoaded(balance: AnalyticsParam.CardBalanceState, tokensCount: Int?) : Basic(
|
||||
event = "Balance Loaded",
|
||||
params = buildMap {
|
||||
put(AnalyticsParam.BALANCE, balance.value)
|
||||
tokensCount?.let { put(AnalyticsParam.TOKENS_COUNT, it.toString()) }
|
||||
},
|
||||
), AppsFlyerIncludedEvent, CriticalEvent
|
||||
|
||||
class TokenBalance(balance: AnalyticsParam.EmptyFull, token: String) : Basic(
|
||||
event = "Token Balance",
|
||||
params = mapOf(
|
||||
AnalyticsParam.STATE to balance.value,
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
),
|
||||
)
|
||||
|
||||
class BiometryFailed(
|
||||
source: AnalyticsParam.ScreensSources,
|
||||
reason: BiometricFailReason,
|
||||
) : Basic(
|
||||
event = "Biometry Failed",
|
||||
params = mapOf(
|
||||
AnalyticsParam.Key.SOURCE to source.value,
|
||||
"Reason" to reason.value,
|
||||
),
|
||||
) {
|
||||
sealed class BiometricFailReason(val value: String) {
|
||||
data object AuthenticationLockout : BiometricFailReason("BiometricsAuthenticationLockout")
|
||||
data object AuthenticationLockoutPermanent : BiometricFailReason("BiometricsAuthenticationLockoutPermanent")
|
||||
data object BiometricsAuthenticationDisabled : BiometricFailReason("BiometricsAuthenticationDisabled")
|
||||
data object AllKeysInvalidated : BiometricFailReason("AllKeysInvalidated")
|
||||
data object AuthenticationCancelled : BiometricFailReason("AuthenticationCancelled")
|
||||
data object AuthenticationAlreadyInProgress : BiometricFailReason("AuthenticationAlreadyInProgress")
|
||||
data class Other(val reason: String) : BiometricFailReason(reason)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.core.analytics.models
|
||||
|
||||
/**
|
||||
* Marker interface for analytics events that require special attention.
|
||||
*
|
||||
* Implemented by events listed in the analytics specification (events.csv).
|
||||
*/
|
||||
interface CriticalEvent
|
||||
|
|
@ -2,20 +2,22 @@ package com.tangem.core.analytics.models.event
|
|||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.CriticalEvent
|
||||
|
||||
sealed class SignIn(
|
||||
event: String,
|
||||
params: Map<String, String> = emptyMap(),
|
||||
) : AnalyticsEvent("Sign In", event, params) {
|
||||
) : AnalyticsEvent(category = "Sign In", event = event, params = params) {
|
||||
|
||||
data class ScreenOpened(
|
||||
val walletsCount: Int,
|
||||
) : SignIn(
|
||||
/**
|
||||
* Tracks the user landing on the app's sign-in screen when a saved card exists.
|
||||
*/
|
||||
class ScreenOpened(walletsCount: Int) : SignIn(
|
||||
event = "Sign In Screen Opened",
|
||||
params = mapOf(
|
||||
"Wallets Count" to walletsCount.toString(),
|
||||
AnalyticsParam.WALLETS_COUNT to walletsCount.toString(),
|
||||
),
|
||||
)
|
||||
), CriticalEvent
|
||||
|
||||
class ButtonBiometricSignIn : SignIn(event = "Button - Biometric Sign In")
|
||||
|
||||
|
|
@ -26,24 +28,17 @@ sealed class SignIn(
|
|||
) : SignIn(event = "Error - Biometric Updated")
|
||||
|
||||
class ButtonWallet(
|
||||
signInType: SignInType,
|
||||
signInType: AnalyticsParam.SignInType,
|
||||
walletsCount: Int,
|
||||
) : SignIn(
|
||||
event = "Button - Wallet",
|
||||
params = buildMap {
|
||||
put("Wallets Count", walletsCount.toString())
|
||||
put("Sign in type", signInType.value)
|
||||
put(AnalyticsParam.WALLETS_COUNT, walletsCount.toString())
|
||||
put(AnalyticsParam.SIGN_IN_TYPE, signInType.value)
|
||||
},
|
||||
) {
|
||||
enum class SignInType(val value: String) {
|
||||
Card("Card"),
|
||||
Biometric("Biometric"),
|
||||
NoSecurity("No Security"),
|
||||
AccessCode("Access Code"),
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
data class ButtonAddWallet(
|
||||
class ButtonAddWallet(
|
||||
val sources: AnalyticsParam.ScreensSources,
|
||||
) : SignIn(
|
||||
event = "Button - Add Wallet",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.domain.card.analytics
|
|||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.CriticalEvent
|
||||
import com.tangem.core.analytics.models.getReferralParams
|
||||
|
||||
sealed class IntroductionProcess(
|
||||
|
|
@ -9,11 +10,17 @@ sealed class IntroductionProcess(
|
|||
params: Map<String, String> = emptyMap(),
|
||||
) : AnalyticsEvent("Introduction Process", event, params) {
|
||||
|
||||
class ScreenOpened : IntroductionProcess("Introduction Process Screen Opened")
|
||||
/**
|
||||
* Tracks the user opening the Introduction Process screen.
|
||||
*/
|
||||
class ScreenOpened : IntroductionProcess("Introduction Process Screen Opened"), CriticalEvent
|
||||
class ButtonTokensList : IntroductionProcess("Button - Tokens List")
|
||||
class ButtonBuyCards : IntroductionProcess("Button - Buy Cards")
|
||||
class ButtonScanCardLegacy : IntroductionProcess("Button - Scan Card")
|
||||
|
||||
/**
|
||||
* Tracks opening the Create Wallet introduction screen.
|
||||
*/
|
||||
class CreateWalletIntroScreenOpened(
|
||||
referralId: String?,
|
||||
) : IntroductionProcess(
|
||||
|
|
@ -21,7 +28,7 @@ sealed class IntroductionProcess(
|
|||
params = buildMap {
|
||||
putAll(getReferralParams(referralId))
|
||||
},
|
||||
)
|
||||
), CriticalEvent
|
||||
|
||||
class ButtonScanCard(
|
||||
val source: AnalyticsParam.ScreensSources,
|
||||
|
|
|
|||
|
|
@ -93,6 +93,13 @@ fun UserWallet.isImported(): Boolean {
|
|||
}
|
||||
}
|
||||
|
||||
fun UserWallet.isBackedUpForAnalytics(): Boolean {
|
||||
return when (this) {
|
||||
is UserWallet.Cold -> scanResponse.card.backupStatus?.isActive == true
|
||||
is UserWallet.Hot -> backedUp
|
||||
}
|
||||
}
|
||||
|
||||
fun UserWallet.copy(name: String = this.name, walletId: UserWalletId = this.walletId): UserWallet = when (this) {
|
||||
is UserWallet.Cold -> this.copy(name = name, walletId = walletId)
|
||||
is UserWallet.Hot -> this.copy(name = name, walletId = walletId)
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import com.tangem.common.routing.AppRouter
|
|||
import com.tangem.common.routing.entity.InitScreenLaunchMode
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic.SignedInLegacy
|
||||
import com.tangem.core.analytics.models.Basic.SignedInLegacy.SignInType
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.SignInType
|
||||
import com.tangem.core.analytics.models.Basic.SignedIn
|
||||
import com.tangem.core.decompose.di.GlobalUiMessageSender
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
|
|
@ -20,9 +20,7 @@ import com.tangem.core.navigation.url.UrlOpener
|
|||
import com.tangem.core.ui.message.dialog.Dialogs
|
||||
import com.tangem.domain.card.ScanCardProcessor
|
||||
import com.tangem.domain.card.analytics.IntroductionProcess
|
||||
import com.tangem.domain.card.analytics.ParamCardCurrencyConverter
|
||||
import com.tangem.domain.card.analytics.Shop
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.common.wallets.error.SaveWalletError
|
||||
|
|
@ -34,18 +32,18 @@ import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
|
|||
import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
|
||||
import com.tangem.domain.wallets.usecase.GenerateBuyTangemCardLinkUseCase
|
||||
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
|
||||
import com.tangem.feature.referral.domain.ShouldShowMobileWalletPromoUseCase
|
||||
import com.tangem.features.home.api.HomeComponent
|
||||
import com.tangem.features.home.impl.ui.state.HomeUM
|
||||
import com.tangem.features.home.impl.ui.state.Stories
|
||||
import com.tangem.features.home.impl.ui.state.getRestrictedStories
|
||||
import com.tangem.feature.referral.domain.ShouldShowMobileWalletPromoUseCase
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.Debouncer
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -218,19 +216,14 @@ internal class HomeModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun sendSignedInCardAnalyticsEvent(scanResponse: ScanResponse, isImported: Boolean) {
|
||||
val currency = ParamCardCurrencyConverter().convert(value = scanResponse.cardTypesResolver)
|
||||
if (currency != null) {
|
||||
analyticsEventHandler.send(
|
||||
SignedInLegacy(
|
||||
currency = currency,
|
||||
batch = scanResponse.card.batchId,
|
||||
signInType = SignInType.Card,
|
||||
walletsCount = userWalletsListRepository.userWalletsSync().size.toString(),
|
||||
isImported = isImported,
|
||||
hasBackup = scanResponse.card.backupStatus?.isActive,
|
||||
),
|
||||
)
|
||||
}
|
||||
analyticsEventHandler.send(
|
||||
SignedIn(
|
||||
signInType = SignInType.Card,
|
||||
walletsCount = userWalletsListRepository.userWalletsSync().size,
|
||||
isImported = isImported,
|
||||
isBackedUp = scanResponse.card.backupStatus?.isActive == true,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun setLoading(isLoading: Boolean) {
|
||||
|
|
|
|||
|
|
@ -243,10 +243,6 @@ internal class WalletModel @Inject constructor(
|
|||
} else {
|
||||
null
|
||||
}
|
||||
val isBackedUp = when (selectedWallet) {
|
||||
is UserWallet.Cold -> selectedWallet.scanResponse.card.backupStatus?.isActive == true
|
||||
is UserWallet.Hot -> selectedWallet.backedUp
|
||||
}
|
||||
val result = getAppThemeModeUseCase().firstOrNull()
|
||||
val theme = result?.getOrElse { AppThemeMode.FOLLOW_SYSTEM } ?: AppThemeMode.FOLLOW_SYSTEM
|
||||
val appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default }.code
|
||||
|
|
@ -254,7 +250,7 @@ internal class WalletModel @Inject constructor(
|
|||
WalletScreenAnalyticsEvent.MainScreen.ScreenOpened(
|
||||
hasMobileWallet = hasMobileWallet,
|
||||
accountsCount = accountsCount,
|
||||
isBackedUp = isBackedUp,
|
||||
isBackedUp = selectedWallet.isBackedUpForAnalytics(),
|
||||
theme = theme.value,
|
||||
isImported = selectedWallet.isImported(),
|
||||
referralId = appsFlyerStore.get()?.refcode,
|
||||
|
|
|
|||
|
|
@ -1,19 +1,20 @@
|
|||
package com.tangem.feature.wallet.child.wallet.model.intents
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import com.tangem.common.routing.AppRoute.*
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.ui.notifications.NotificationId
|
||||
import com.tangem.common.ui.userwallet.handle
|
||||
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.Basic.ButtonSupport
|
||||
import com.tangem.core.analytics.models.event.AssetsDiscoveryAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.navigation.review.ReviewManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.domain.assetsdiscovery.usecase.AcknowledgeAssetsDiscoveryCompletionUseCase
|
||||
import com.tangem.domain.card.SetCardWasScannedUseCase
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
|
||||
|
|
@ -41,16 +42,15 @@ import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent
|
|||
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.Program
|
||||
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.PromotionBannerClicked
|
||||
import com.tangem.domain.tokens.model.details.NavigationAction
|
||||
import com.tangem.domain.assetsdiscovery.usecase.AcknowledgeAssetsDiscoveryCompletionUseCase
|
||||
import com.tangem.domain.wallets.usecase.*
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
|
||||
import com.tangem.features.pushnotifications.api.analytics.PushNotificationAnalyticEvents
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.logging.TangemLogger
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
|
|
|
|||
|
|
@ -1,56 +1,14 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.analytics
|
||||
|
||||
import com.tangem.core.analytics.models.*
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
sealed class WalletScreenAnalyticsEvent {
|
||||
|
||||
sealed class Basic(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent(category = "Basic", event = event, params = params) {
|
||||
|
||||
class WalletToppedUp(userWalletId: UserWalletId, walletType: AnalyticsParam.WalletType) :
|
||||
Basic(
|
||||
event = "Topped up",
|
||||
params = mapOf(AnalyticsParam.CURRENCY to walletType.value),
|
||||
),
|
||||
OneTimeAnalyticsEvent, AppsFlyerIncludedEvent {
|
||||
|
||||
override val oneTimeEventId: String = id + userWalletId.stringValue
|
||||
}
|
||||
|
||||
class CardWasScanned(source: AnalyticsParam.ScreensSources) : Basic(
|
||||
event = "Card Was Scanned",
|
||||
params = mapOf(
|
||||
AnalyticsParam.SOURCE to source.value,
|
||||
),
|
||||
)
|
||||
|
||||
class BalanceLoaded(balance: AnalyticsParam.CardBalanceState, tokensCount: Int?) : Basic(
|
||||
event = "Balance Loaded",
|
||||
params = buildMap {
|
||||
put(AnalyticsParam.BALANCE, balance.value)
|
||||
tokensCount?.let { put(AnalyticsParam.TOKENS_COUNT, it.toString()) }
|
||||
},
|
||||
), AppsFlyerIncludedEvent
|
||||
|
||||
class TokenBalance(balance: AnalyticsParam.EmptyFull, token: String) : Basic(
|
||||
event = "Token Balance",
|
||||
params = mapOf(
|
||||
AnalyticsParam.STATE to balance.value,
|
||||
AnalyticsParam.TOKEN_PARAM to token,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
sealed class MainScreen(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
) : AnalyticsEvent(category = "Main Screen", event = event, params = params) {
|
||||
|
||||
class ScreenOpenedLegacy : MainScreen(event = "Screen opened")
|
||||
|
||||
data class ScreenOpened(
|
||||
private val hasMobileWallet: Boolean,
|
||||
private val accountsCount: Int?,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.blockchainsdk.utils.fromNetworkId
|
|||
import com.tangem.common.extensions.isZero
|
||||
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.MainScreenAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.analytics.CheckIsWalletToppedUpUseCase
|
||||
|
|
@ -18,7 +19,6 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
|||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider
|
||||
|
|
@ -206,7 +206,12 @@ internal class TokenListAnalyticsSender @Inject constructor(
|
|||
AnalyticsParam.WalletType.SingleCurrency(currency.currency.name)
|
||||
}
|
||||
|
||||
analyticsEventHandler.send(Basic.WalletToppedUp(userWallet.walletId, walletType))
|
||||
analyticsEventHandler.send(
|
||||
Basic.ToppedUp(
|
||||
userWalletId = userWallet.walletId.stringValue,
|
||||
walletType = walletType,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ package com.tangem.feature.wallet.presentation.wallet.subscribers
|
|||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetPrimaryCurrencyTransformer
|
||||
import dagger.assisted.Assisted
|
||||
|
|
@ -70,7 +70,7 @@ internal class PrimaryCurrencySubscriber @AssistedInject constructor(
|
|||
cardBalanceState?.let { balanceState ->
|
||||
// do not send tokens count for single currency wallet
|
||||
analyticsEventHandler.send(
|
||||
event = WalletScreenAnalyticsEvent.Basic.BalanceLoaded(
|
||||
event = Basic.BalanceLoaded(
|
||||
balance = balanceState,
|
||||
tokensCount = null,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -19,10 +19,7 @@ import com.tangem.domain.card.ScanCardProcessor
|
|||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.common.wallets.error.SaveWalletError
|
||||
import com.tangem.domain.common.wallets.error.UnlockWalletError
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isImported
|
||||
import com.tangem.domain.models.wallet.isLocked
|
||||
import com.tangem.domain.models.wallet.*
|
||||
import com.tangem.domain.settings.CanUseBiometryUseCase
|
||||
import com.tangem.domain.settings.HotWalletRestrictionManager
|
||||
import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
|
||||
|
|
@ -76,9 +73,9 @@ internal class WelcomeModel @Inject constructor(
|
|||
val userWallet = userWallets.first { it.walletId == walletId }
|
||||
trackingContextProxy.addContext(userWallet)
|
||||
val signInType = when {
|
||||
!userWallet.isLocked -> SignIn.ButtonWallet.SignInType.NoSecurity
|
||||
userWallet is UserWallet.Cold -> SignIn.ButtonWallet.SignInType.Card
|
||||
else -> SignIn.ButtonWallet.SignInType.AccessCode
|
||||
!userWallet.isLocked -> AnalyticsParam.SignInType.NoSecurity
|
||||
userWallet is UserWallet.Cold -> AnalyticsParam.SignInType.Card
|
||||
else -> AnalyticsParam.SignInType.AccessCode
|
||||
}
|
||||
analyticsEventHandler.send(
|
||||
event = SignIn.ButtonWallet(
|
||||
|
|
@ -256,7 +253,7 @@ internal class WelcomeModel @Inject constructor(
|
|||
if (userWallet.isLocked.not()) {
|
||||
// If the wallet is not locked, we can proceed to the wallet screen directly
|
||||
userWalletsListRepository.select(userWallet.walletId)
|
||||
trackSignInEvent(userWallet, Basic.SignedIn.SignInType.NoSecurity)
|
||||
trackSignInEvent(userWallet, AnalyticsParam.SignInType.NoSecurity)
|
||||
router.replaceAll(AppRoute.Wallet)
|
||||
return@launch
|
||||
}
|
||||
|
|
@ -317,19 +314,15 @@ internal class WelcomeModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun trackSignInEvent(userWallet: UserWallet, type: Basic.SignedIn.SignInType) {
|
||||
private suspend fun trackSignInEvent(userWallet: UserWallet, type: AnalyticsParam.SignInType) {
|
||||
val walletsCount = userWalletsListRepository.userWalletsSync().size
|
||||
trackingContextProxy.addContext(userWallet)
|
||||
val isBackedUp = when (userWallet) {
|
||||
is UserWallet.Cold -> userWallet.scanResponse.card.backupStatus?.isActive == true
|
||||
is UserWallet.Hot -> userWallet.backedUp
|
||||
}
|
||||
analyticsEventHandler.send(
|
||||
event = Basic.SignedIn(
|
||||
signInType = type,
|
||||
walletsCount = walletsCount,
|
||||
isImported = userWallet.isImported(),
|
||||
hasBackup = isBackedUp,
|
||||
isBackedUp = userWallet.isBackedUpForAnalytics(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue