Updated on 2026-08-14

This commit is contained in:
Tangem 2026-02-05 17:35:20 +03:00
parent fcc17181ad
commit 5b3b19f3ee
11 changed files with 123 additions and 37 deletions

View file

@ -1,6 +1,7 @@
package com.tangem.features.account.analytics
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.domain.models.account.AccountName
import com.tangem.domain.models.account.CryptoPortfolioIcon
import com.tangem.features.account.AccountCreateEditComponent
@ -15,20 +16,48 @@ sealed class AccountSettingsAnalyticEvents(
event = "Account Settings Screen Opened",
)
class ButtonManageTokens : AccountSettingsAnalyticEvents(
class ButtonManageTokens(
accountDerivation: Int?,
) : AccountSettingsAnalyticEvents(
event = "Button - Manage Tokens",
params = buildMap {
accountDerivation?.let {
put(AnalyticsParam.ACCOUNT_DERIVATION, it.toString())
}
},
)
class ButtonArchiveAccount : AccountSettingsAnalyticEvents(
class ButtonArchiveAccount(
accountDerivation: Int?,
) : AccountSettingsAnalyticEvents(
event = "Button - Archive Account",
params = buildMap {
accountDerivation?.let {
put(AnalyticsParam.ACCOUNT_DERIVATION, it.toString())
}
},
)
class ButtonArchiveAccountConfirmation : AccountSettingsAnalyticEvents(
class ButtonArchiveAccountConfirmation(
accountDerivation: Int?,
) : AccountSettingsAnalyticEvents(
event = "Button - Archive Account Confirmation",
params = buildMap {
accountDerivation?.let {
put(AnalyticsParam.ACCOUNT_DERIVATION, it.toString())
}
},
)
class ButtonCancelAccountArchivation : AccountSettingsAnalyticEvents(
class ButtonCancelAccountArchivation(
accountDerivation: Int?,
) : AccountSettingsAnalyticEvents(
event = "Button - Cancel Account Archivation",
params = buildMap {
accountDerivation?.let {
put(AnalyticsParam.ACCOUNT_DERIVATION, it.toString())
}
},
)
class AccountArchived : AccountSettingsAnalyticEvents(
@ -39,13 +68,21 @@ sealed class AccountSettingsAnalyticEvents(
event = "Button - Edit",
)
class AccountEditScreenOpened : AccountSettingsAnalyticEvents(
class AccountEditScreenOpened(
accountDerivation: Int?,
) : AccountSettingsAnalyticEvents(
event = "Account Edit Screen Opened",
params = buildMap {
accountDerivation?.let {
put(AnalyticsParam.ACCOUNT_DERIVATION, it.toString())
}
},
)
class ButtonSave(
val name: AccountName,
val icon: CryptoPortfolioIcon,
accountDerivation: Int?,
) : AccountSettingsAnalyticEvents(
event = "Button - Save",
params = buildMap {
@ -56,13 +93,16 @@ sealed class AccountSettingsAnalyticEvents(
put("Name", accountName)
put("Color", icon.color.name)
put("Icon", icon.value.name)
accountDerivation?.let {
put(AnalyticsParam.ACCOUNT_DERIVATION, it.toString())
}
},
)
class ButtonAddNewAccount(
val name: AccountName,
val icon: CryptoPortfolioIcon,
val derivationIndex: Int,
accountDerivation: Int?,
) : AccountSettingsAnalyticEvents(
event = "Button - Add New Account",
params = buildMap {
@ -73,18 +113,24 @@ sealed class AccountSettingsAnalyticEvents(
put("Name", accountName)
put("Color", icon.color.name)
put("Icon", icon.value.name)
put("Derivation", derivationIndex.toString())
accountDerivation?.let {
put(AnalyticsParam.ACCOUNT_DERIVATION, it.toString())
}
},
)
class AccountError(
val source: Source,
val error: String,
accountDerivation: Int?,
) : AccountSettingsAnalyticEvents(
event = "Account Error",
params = buildMap {
put("Error", error)
put("Source", source.value)
accountDerivation?.let {
put(AnalyticsParam.ACCOUNT_DERIVATION, it.toString())
}
},
)

View file

@ -1,6 +1,7 @@
package com.tangem.features.account.analytics
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
sealed class WalletSettingsAccountAnalyticEvents(
category: String = "Settings / Wallet Settings",
@ -16,11 +17,23 @@ sealed class WalletSettingsAccountAnalyticEvents(
event = "Account Recovered",
)
class ArchivedAccountsScreenOpened : WalletSettingsAccountAnalyticEvents(
class ArchivedAccountsScreenOpened(
private val accountsCount: Int,
) : WalletSettingsAccountAnalyticEvents(
event = "Archived Accounts Screen Opened",
params = buildMap {
put("Accounts Count", accountsCount.toString())
},
)
class ButtonRecoverAccount : WalletSettingsAccountAnalyticEvents(
class ButtonRecoverAccount(
accountDerivation: Int?,
) : WalletSettingsAccountAnalyticEvents(
event = "Button - Recover Account",
params = buildMap {
accountDerivation?.let {
put(AnalyticsParam.ACCOUNT_DERIVATION, it.toString())
}
},
)
}

View file

@ -13,9 +13,9 @@ import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.EventMessageAction
import com.tangem.core.ui.message.ToastMessage
import com.tangem.domain.account.models.AccountList
import com.tangem.domain.account.models.ArchivedAccount
import com.tangem.domain.account.status.usecase.RecoverCryptoPortfolioUseCase
import com.tangem.domain.account.usecase.GetArchivedAccountsUseCase
import com.tangem.domain.models.account.AccountId
import com.tangem.features.account.ArchivedAccountListComponent
import com.tangem.features.account.analytics.WalletSettingsAccountAnalyticEvents
import com.tangem.features.account.archived.entity.AccountArchivedUM
@ -53,7 +53,6 @@ internal class ArchivedAccountListModel @Inject constructor(
private val getArchivedAccountsJob = JobHolder()
init {
analyticsEventHandler.send(WalletSettingsAccountAnalyticEvents.ArchivedAccountsScreenOpened())
getArchivedAccounts()
}
@ -69,14 +68,16 @@ internal class ArchivedAccountListModel @Inject constructor(
umBuilder.mapContent(
accounts = content,
onCloseClick = onCloseClick,
onRecoverClick = { recoverCryptoPortfolio(accountId = it.accountId) },
onRecoverClick = { recoverCryptoPortfolio(account = it) },
)
},
ifContent = { content ->
val event = WalletSettingsAccountAnalyticEvents.ArchivedAccountsScreenOpened(content.size)
analyticsEventHandler.send(event)
umBuilder.mapContent(
accounts = content,
onCloseClick = onCloseClick,
onRecoverClick = { recoverCryptoPortfolio(accountId = it.accountId) },
onRecoverClick = { recoverCryptoPortfolio(account = it) },
)
},
ifError = { error ->
@ -97,13 +98,15 @@ internal class ArchivedAccountListModel @Inject constructor(
.saveIn(getArchivedAccountsJob)
}
private fun recoverCryptoPortfolio(accountId: AccountId) = modelScope.launch {
analyticsEventHandler.send(WalletSettingsAccountAnalyticEvents.ButtonRecoverAccount())
uiState.update { it.toggleProgress(accountId, isLoading = true) }
private fun recoverCryptoPortfolio(account: ArchivedAccount) = modelScope.launch {
analyticsEventHandler.send(
WalletSettingsAccountAnalyticEvents.ButtonRecoverAccount(account.derivationIndex.value),
)
uiState.update { it.toggleProgress(account.accountId, isLoading = true) }
val result = withContext(dispatchers.default) {
recoverCryptoPortfolioUseCase(accountId)
recoverCryptoPortfolioUseCase(account.accountId)
}
uiState.update { it.toggleProgress(accountId, isLoading = false) }
uiState.update { it.toggleProgress(account.accountId, isLoading = false) }
result
.onLeft(::handleRecoverError)
.onRight {

View file

@ -25,6 +25,7 @@ import com.tangem.domain.account.usecase.GetUnoccupiedAccountIndexUseCase
import com.tangem.domain.account.usecase.UpdateCryptoPortfolioUseCase
import com.tangem.domain.models.account.CryptoPortfolioIcon
import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.account.derivationIndex
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.account.AccountCreateEditComponent
import com.tangem.features.account.analytics.AccountSettingsAnalyticEvents
@ -69,10 +70,13 @@ internal class AccountCreateEditModel @Inject constructor(
field = MutableStateFlow(value = getInitialState())
init {
if (params is AccountCreateEditComponent.Params.Create) {
updateDerivationInfo(userWalletId = params.userWalletId)
} else {
analyticsEventHandler.send(AccountSettingsAnalyticEvents.AccountEditScreenOpened())
when (params) {
is AccountCreateEditComponent.Params.Create -> updateDerivationInfo(userWalletId = params.userWalletId)
is AccountCreateEditComponent.Params.Edit -> {
val derivationIndex = params.account.derivationIndex?.value
val event = AccountSettingsAnalyticEvents.AccountEditScreenOpened(derivationIndex)
analyticsEventHandler.send(event)
}
}
}
@ -116,7 +120,7 @@ internal class AccountCreateEditModel @Inject constructor(
val event = AccountSettingsAnalyticEvents.ButtonAddNewAccount(
name = name,
icon = icon,
derivationIndex = derivationIndex.value,
accountDerivation = derivationIndex.value,
)
analyticsEventHandler.send(event)
@ -130,7 +134,7 @@ internal class AccountCreateEditModel @Inject constructor(
uiState.value = uiState.value.toggleProgress(showProgress = false)
result
.onLeft(::handleAddAccountError)
.onLeft { error -> handleAddAccountError(error, derivationIndex.value) }
.onRight {
analyticsEventHandler.send(WalletSettingsAccountAnalyticEvents.AccountCreated())
showMessage(R.string.account_create_success_message)
@ -138,9 +142,10 @@ internal class AccountCreateEditModel @Inject constructor(
}
}
private fun handleAddAccountError(error: AddCryptoPortfolioUseCase.Error) {
private fun handleAddAccountError(error: AddCryptoPortfolioUseCase.Error, derivationIndex: Int) {
val event = AccountSettingsAnalyticEvents.AccountError(
source = params.toAnalyticSource(),
accountDerivation = derivationIndex,
error = when (error) {
is AddCryptoPortfolioUseCase.Error.AccountListRequirementsNotMet -> error.cause.tag
is AddCryptoPortfolioUseCase.Error.DataOperationFailed -> error.cause.message.orEmpty()
@ -165,7 +170,8 @@ internal class AccountCreateEditModel @Inject constructor(
val icon = CryptoPortfolioIconConverter.convertBack(state.account.portfolioIcon)
val isNewName = name != params.account.accountName
val isNewIcon = icon != params.account.portfolioIcon
analyticsEventHandler.send(AccountSettingsAnalyticEvents.ButtonSave(name, icon))
val derivationIndex = params.account.derivationIndex?.value
analyticsEventHandler.send(AccountSettingsAnalyticEvents.ButtonSave(name, icon, derivationIndex))
uiState.value = uiState.value.toggleProgress(showProgress = true)
val result = updateCryptoPortfolioUseCase(
@ -176,16 +182,17 @@ internal class AccountCreateEditModel @Inject constructor(
uiState.value = uiState.value.toggleProgress(showProgress = false)
result
.onLeft(::handleEditAccountError)
.onLeft { error -> handleEditAccountError(error, params.account.derivationIndex?.value) }
.onRight {
showMessage(R.string.account_edit_success_message)
router.pop()
}
}
private fun handleEditAccountError(error: UpdateCryptoPortfolioUseCase.Error) {
private fun handleEditAccountError(error: UpdateCryptoPortfolioUseCase.Error, derivationIndex: Int?) {
val event = AccountSettingsAnalyticEvents.AccountError(
source = params.toAnalyticSource(),
accountDerivation = derivationIndex,
error = when (error) {
is UpdateCryptoPortfolioUseCase.Error.AccountListRequirementsNotMet -> error.cause.tag
is UpdateCryptoPortfolioUseCase.Error.DataOperationFailed -> error.cause.message.orEmpty()

View file

@ -19,6 +19,7 @@ import com.tangem.domain.account.status.usecase.ArchiveCryptoPortfolioUseCase
import com.tangem.domain.account.supplier.SingleAccountSupplier
import com.tangem.domain.models.PortfolioId
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.derivationIndex
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.account.AccountDetailsComponent
@ -65,15 +66,19 @@ internal class AccountDetailsModel @Inject constructor(
private fun onManageTokensClick(account: Account) {
val route = AppRoute.ManageTokens(
source = AppRoute.ManageTokens.Source.SETTINGS,
source = AppRoute.ManageTokens.Source.ACCOUNT,
portfolioId = PortfolioId(account.accountId),
)
analyticsEventHandler.send(AccountSettingsAnalyticEvents.ButtonManageTokens())
analyticsEventHandler.send(
AccountSettingsAnalyticEvents.ButtonManageTokens(account.derivationIndex?.value),
)
router.push(route)
}
private fun onArchiveAccountClick() {
analyticsEventHandler.send(AccountSettingsAnalyticEvents.ButtonArchiveAccount())
val accountDerivation = params.account.derivationIndex?.value
val event = AccountSettingsAnalyticEvents.ButtonArchiveAccount(accountDerivation)
analyticsEventHandler.send(event)
confirmArchiveDialog()
}
@ -81,7 +86,9 @@ internal class AccountDetailsModel @Inject constructor(
val secondAction = EventMessageAction(
title = resourceReference(R.string.common_cancel),
onClick = {
analyticsEventHandler.send(AccountSettingsAnalyticEvents.ButtonCancelAccountArchivation())
val accountDerivation = params.account.derivationIndex?.value
val event = AccountSettingsAnalyticEvents.ButtonCancelAccountArchivation(accountDerivation)
analyticsEventHandler.send(event)
},
)
val firstAction = EventMessageAction(
@ -100,7 +107,9 @@ internal class AccountDetailsModel @Inject constructor(
}
private fun archiveCryptoPortfolio() = modelScope.launch {
analyticsEventHandler.send(AccountSettingsAnalyticEvents.ButtonArchiveAccountConfirmation())
val accountDerivation = params.account.derivationIndex?.value
val event = AccountSettingsAnalyticEvents.ButtonArchiveAccountConfirmation(accountDerivation)
analyticsEventHandler.send(event)
uiState.update { it.toggleProgress(true) }
archiveCryptoPortfolioUseCase(accountId)
.onLeft { error ->
@ -119,6 +128,7 @@ internal class AccountDetailsModel @Inject constructor(
val event = AccountSettingsAnalyticEvents.AccountError(
source = AccountSettingsAnalyticEvents.Source.ARCHIVE,
error = error.tag,
accountDerivation = params.account.derivationIndex?.value,
)
analyticsEventHandler.send(event)
val titleRes: Int

View file

@ -6,7 +6,8 @@ import com.tangem.domain.models.wallet.UserWalletId
enum class ManageTokensSource(val analyticsName: String) {
STORIES(analyticsName = "Stories"),
ONBOARDING(analyticsName = "Onboarding"),
SETTINGS(analyticsName = "Settings"),
SETTINGS(analyticsName = "Wallet Settings"),
ACCOUNT(analyticsName = "Account"),
SEND_VIA_SWAP(analyticsName = "SendViaSwap"),
}

View file

@ -2,6 +2,7 @@ package com.tangem.features.swap.v2.impl.sendviaswap.analytics
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.AnalyticsParam.Key.ACCOUNT_DERIVATION_FROM
import com.tangem.core.analytics.models.AnalyticsParam.Key.FEE_TYPE
import com.tangem.core.analytics.models.AnalyticsParam.Key.PROVIDER
import com.tangem.core.analytics.models.AnalyticsParam.Key.RECEIVE_BLOCKCHAIN
@ -32,7 +33,7 @@ internal sealed class SendWithSwapAnalyticEvents(
put(RECEIVE_TOKEN, toToken.symbol)
put(SEND_BLOCKCHAIN, fromToken.network.name)
put(RECEIVE_BLOCKCHAIN, toToken.network.name)
if (fromDerivationIndex != null) put("Account Derivation (from)", fromDerivationIndex.toString())
if (fromDerivationIndex != null) put(ACCOUNT_DERIVATION_FROM, fromDerivationIndex.toString())
},
), AppsFlyerIncludedEvent

View file

@ -2,6 +2,8 @@ package com.tangem.feature.swap.analytics
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam.Key.ACCOUNT_DERIVATION_FROM
import com.tangem.core.analytics.models.AnalyticsParam.Key.ACCOUNT_DERIVATION_TO
import com.tangem.core.analytics.models.AnalyticsParam.Key.BLOCKCHAIN
import com.tangem.core.analytics.models.AnalyticsParam.Key.ERROR_CODE
import com.tangem.core.analytics.models.AnalyticsParam.Key.ERROR_MESSAGE
@ -105,8 +107,8 @@ sealed class SwapEvents(
put("Receive Token", receiveToken)
put("Send Blockchain", sendBlockchain)
put("Receive Blockchain", receiveBlockchain)
if (fromDerivationIndex != null) put("Account Derivation (from)", fromDerivationIndex.toString())
if (toDerivationIndex != null) put("Account Derivation (to)", toDerivationIndex.toString())
if (fromDerivationIndex != null) put(ACCOUNT_DERIVATION_FROM, fromDerivationIndex.toString())
if (toDerivationIndex != null) put(ACCOUNT_DERIVATION_TO, toDerivationIndex.toString())
put(FEE_TOKEN, feeToken)
},
), AppsFlyerIncludedEvent