Updated on 2026-08-14
This commit is contained in:
parent
b316ef4d87
commit
370690a359
42 changed files with 486 additions and 35 deletions
|
|
@ -266,5 +266,6 @@ sealed class AnalyticsParam {
|
|||
const val CHOSEN_TOKEN = "Token Chosen"
|
||||
const val ENS = "ENS"
|
||||
const val ENS_ADDRESS = "ENS Address"
|
||||
const val ACCOUNT_DERIVATION_FROM = "Account Derivation (from)"
|
||||
}
|
||||
}
|
||||
|
|
@ -56,6 +56,14 @@ sealed class MainScreenAnalyticsEvent(
|
|||
event = "Button - Explore",
|
||||
)
|
||||
|
||||
class AccountShowTokens : MainScreenAnalyticsEvent(
|
||||
event = "Button - Account Show Tokens",
|
||||
)
|
||||
|
||||
class AccountHideTokens : MainScreenAnalyticsEvent(
|
||||
event = "Button - Account Hide Tokens",
|
||||
)
|
||||
|
||||
data class ButtonSwap(val status: AnalyticsParam.Status) : MainScreenAnalyticsEvent(
|
||||
event = "Button - Swap",
|
||||
params = mapOf(AnalyticsParam.STATUS to status.value),
|
||||
|
|
|
|||
|
|
@ -166,7 +166,6 @@ internal class DefaultWcPairUseCase @AssistedInject constructor(
|
|||
}
|
||||
|
||||
override fun approve(sessionForApprove: WcSessionApprove) {
|
||||
analytics.send(WcAnalyticEvents.PairButtonConnect())
|
||||
onCallTerminalAction.trySend(TerminalAction.Approve(sessionForApprove))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.tokens.model.analytics
|
||||
|
||||
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.ACTION
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.BALANCE
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.BLOCKCHAIN
|
||||
|
|
@ -58,12 +59,14 @@ sealed class TokenScreenAnalyticsEvent(
|
|||
token: String,
|
||||
blockchain: String,
|
||||
status: String?,
|
||||
derivationIndex: Int? = null,
|
||||
) : TokenScreenAnalyticsEvent(
|
||||
event = event,
|
||||
params = buildMap {
|
||||
put(TOKEN_PARAM, token)
|
||||
put(BLOCKCHAIN, blockchain)
|
||||
status?.let { put(STATUS, it) }
|
||||
derivationIndex?.let { put(ACCOUNT_DERIVATION_FROM, it.toString()) }
|
||||
},
|
||||
) {
|
||||
|
||||
|
|
@ -71,11 +74,13 @@ sealed class TokenScreenAnalyticsEvent(
|
|||
token: String,
|
||||
blockchain: String,
|
||||
status: String?,
|
||||
derivationIndex: Int? = null,
|
||||
) : ButtonWithParams(
|
||||
event = "Button - Buy",
|
||||
token = token,
|
||||
status = status,
|
||||
blockchain = blockchain,
|
||||
derivationIndex = derivationIndex,
|
||||
)
|
||||
|
||||
class ButtonSell(
|
||||
|
|
@ -93,22 +98,26 @@ sealed class TokenScreenAnalyticsEvent(
|
|||
token: String,
|
||||
status: String,
|
||||
blockchain: String,
|
||||
derivationIndex: Int? = null,
|
||||
) : ButtonWithParams(
|
||||
event = "Button - Exchange",
|
||||
token = token,
|
||||
status = status,
|
||||
blockchain = blockchain,
|
||||
derivationIndex = derivationIndex,
|
||||
)
|
||||
|
||||
class ButtonSend(
|
||||
token: String,
|
||||
status: String,
|
||||
blockchain: String,
|
||||
derivationIndex: Int? = null,
|
||||
) : ButtonWithParams(
|
||||
event = "Button - Send",
|
||||
token = token,
|
||||
status = status,
|
||||
blockchain = blockchain,
|
||||
derivationIndex = derivationIndex,
|
||||
)
|
||||
|
||||
class ButtonReceive(
|
||||
|
|
|
|||
|
|
@ -277,4 +277,23 @@ fun CheckDAppResult.toAnalyticVerificationStatus(): String = when (this) {
|
|||
SAFE -> DAppVerificationStatus.Verified
|
||||
UNSAFE -> DAppVerificationStatus.Risky
|
||||
FAILED_TO_VERIFY -> DAppVerificationStatus.Unknown
|
||||
}.status
|
||||
}.status
|
||||
|
||||
sealed class WcAnalyticAccountEvents(
|
||||
event: String,
|
||||
params: Map<String, String> = emptyMap(),
|
||||
) : AnalyticsEvent(category = WC_CATEGORY_ACCOUNT_NAME, event = event, params = params) {
|
||||
|
||||
data class PairButtonConnect(
|
||||
private val accountDerivation: Int,
|
||||
) : WcAnalyticAccountEvents(
|
||||
event = "Button - Connect",
|
||||
params = mapOf(
|
||||
"Account Derivation" to accountDerivation.toString(),
|
||||
),
|
||||
)
|
||||
|
||||
companion object {
|
||||
const val WC_CATEGORY_ACCOUNT_NAME = "WalletConnect - Account"
|
||||
}
|
||||
}
|
||||
|
|
@ -16,14 +16,35 @@ sealed class WalletSettingsAnalyticEvents(
|
|||
params = mapOf(STATUS to enabled.value),
|
||||
)
|
||||
|
||||
class WalletSettingsScreenOpened : WalletSettingsAnalyticEvents(
|
||||
class WalletSettingsScreenOpened(
|
||||
private val accountsCount: Int?,
|
||||
) : WalletSettingsAnalyticEvents(
|
||||
event = "Wallet Settings Screen Opened",
|
||||
params = buildMap {
|
||||
if (accountsCount != null) put("Accounts Count", accountsCount.toString())
|
||||
},
|
||||
)
|
||||
|
||||
class ButtonBackup : WalletSettingsAnalyticEvents(
|
||||
event = "Button - Backup",
|
||||
)
|
||||
|
||||
class ButtonAddAccount : WalletSettingsAnalyticEvents(
|
||||
event = "Button - Add Account",
|
||||
)
|
||||
|
||||
class ButtonOpenExistingAccount : WalletSettingsAnalyticEvents(
|
||||
event = "Button - Open Existing Account",
|
||||
)
|
||||
|
||||
class ButtonArchivedAccounts : WalletSettingsAnalyticEvents(
|
||||
event = "Button - Archived Accounts",
|
||||
)
|
||||
|
||||
class LongtapAccountsOrder : WalletSettingsAnalyticEvents(
|
||||
event = "Longtap - Accounts Order",
|
||||
)
|
||||
|
||||
data class ButtonAccessCode(
|
||||
private val isCodeSet: Boolean,
|
||||
) : WalletSettingsAnalyticEvents(
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ interface PortfolioSelectorController {
|
|||
* combine and update with your Feature data and [PortfolioFetcher.data]
|
||||
*/
|
||||
val isEnabled: MutableStateFlow<(UserWallet, AccountStatus) -> Boolean>
|
||||
suspend fun isAccountModeSync(): Boolean
|
||||
|
||||
fun selectAccount(accountId: AccountId?)
|
||||
fun selectedAccountWithData(portfolioFetcher: PortfolioFetcher): Flow<Pair<UserWallet, AccountStatus>?>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
package com.tangem.features.account.analytics
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.domain.models.account.AccountName
|
||||
import com.tangem.domain.models.account.CryptoPortfolioIcon
|
||||
import com.tangem.features.account.AccountCreateEditComponent
|
||||
|
||||
sealed class AccountSettingsAnalyticEvents(
|
||||
category: String = "Settings / Account",
|
||||
event: String,
|
||||
params: Map<String, String> = emptyMap(),
|
||||
) : AnalyticsEvent(category, event, params) {
|
||||
|
||||
class AccountSettingsScreenOpened : AccountSettingsAnalyticEvents(
|
||||
event = "Account Settings Screen Opened",
|
||||
)
|
||||
|
||||
class ButtonManageTokens : AccountSettingsAnalyticEvents(
|
||||
event = "Button - Manage Tokens",
|
||||
)
|
||||
|
||||
class ButtonArchiveAccount : AccountSettingsAnalyticEvents(
|
||||
event = "Button - Archive Account",
|
||||
)
|
||||
|
||||
class ButtonArchiveAccountConfirmation : AccountSettingsAnalyticEvents(
|
||||
event = "Button - Archive Account Confirmation",
|
||||
)
|
||||
|
||||
class ButtonCancelAccountArchivation : AccountSettingsAnalyticEvents(
|
||||
event = "Button - Cancel Account Archivation",
|
||||
)
|
||||
|
||||
class AccountArchived : AccountSettingsAnalyticEvents(
|
||||
event = "Account Archived",
|
||||
)
|
||||
|
||||
class ButtonEdit : AccountSettingsAnalyticEvents(
|
||||
event = "Button - Edit",
|
||||
)
|
||||
|
||||
class AccountEditScreenOpened : AccountSettingsAnalyticEvents(
|
||||
event = "Account Edit Screen Opened",
|
||||
)
|
||||
|
||||
class ButtonSave(
|
||||
val name: AccountName,
|
||||
val icon: CryptoPortfolioIcon,
|
||||
) : AccountSettingsAnalyticEvents(
|
||||
event = "Button - Save",
|
||||
params = buildMap {
|
||||
val accountName = when (name) {
|
||||
is AccountName.Custom -> name.value
|
||||
AccountName.DefaultMain -> "DefaultMain"
|
||||
}
|
||||
put("Name", accountName)
|
||||
put("Color", icon.color.name)
|
||||
put("Icon", icon.value.name)
|
||||
},
|
||||
)
|
||||
|
||||
class ButtonAddNewAccount(
|
||||
val name: AccountName,
|
||||
val icon: CryptoPortfolioIcon,
|
||||
val derivationIndex: Int,
|
||||
) : AccountSettingsAnalyticEvents(
|
||||
event = "Button - Add New Account",
|
||||
params = buildMap {
|
||||
val accountName = when (name) {
|
||||
is AccountName.Custom -> name.value
|
||||
AccountName.DefaultMain -> "DefaultMain"
|
||||
}
|
||||
put("Name", accountName)
|
||||
put("Color", icon.color.name)
|
||||
put("Icon", icon.value.name)
|
||||
put("Derivation", derivationIndex.toString())
|
||||
},
|
||||
)
|
||||
|
||||
class AccountError(
|
||||
val source: Source,
|
||||
val error: String,
|
||||
) : AccountSettingsAnalyticEvents(
|
||||
event = "Account Error",
|
||||
params = buildMap {
|
||||
put("Error", error)
|
||||
},
|
||||
)
|
||||
|
||||
enum class Source(val value: String) {
|
||||
NEW_ACCOUNT("New Account"), EDIT("Edit"), ARCHIVE("Archive")
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun AccountCreateEditComponent.Params.toAnalyticSource() = when (this) {
|
||||
is AccountCreateEditComponent.Params.Create -> Source.NEW_ACCOUNT
|
||||
is AccountCreateEditComponent.Params.Edit -> Source.EDIT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.features.account.analytics
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
|
||||
sealed class WalletSettingsAccountAnalyticEvents(
|
||||
category: String = "Settings / Wallet Settings",
|
||||
event: String,
|
||||
params: Map<String, String> = emptyMap(),
|
||||
) : AnalyticsEvent(category, event, params) {
|
||||
|
||||
class AccountCreated : WalletSettingsAccountAnalyticEvents(
|
||||
event = "Account Created",
|
||||
)
|
||||
|
||||
class AccountRecovered : WalletSettingsAccountAnalyticEvents(
|
||||
event = "Account Recovered",
|
||||
)
|
||||
|
||||
class ArchivedAccountsScreenOpened : WalletSettingsAccountAnalyticEvents(
|
||||
event = "Archived Accounts Screen Opened",
|
||||
)
|
||||
|
||||
class ButtonRecoverAccount : WalletSettingsAccountAnalyticEvents(
|
||||
event = "Button - Recover Account",
|
||||
)
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.features.account.archived
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
|
|
@ -18,6 +19,7 @@ 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
|
||||
import com.tangem.features.account.archived.entity.AccountArchivedUMBuilder
|
||||
import com.tangem.features.account.archived.entity.AccountArchivedUMBuilder.Companion.toggleProgress
|
||||
|
|
@ -40,6 +42,7 @@ internal class ArchivedAccountListModel @Inject constructor(
|
|||
private val recoverCryptoPortfolioUseCase: RecoverCryptoPortfolioUseCase,
|
||||
private val getArchivedAccountsUseCase: GetArchivedAccountsUseCase,
|
||||
private val umBuilder: AccountArchivedUMBuilder,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
) : Model() {
|
||||
|
||||
|
|
@ -52,6 +55,7 @@ internal class ArchivedAccountListModel @Inject constructor(
|
|||
private val getArchivedAccountsJob = JobHolder()
|
||||
|
||||
init {
|
||||
analyticsEventHandler.send(WalletSettingsAccountAnalyticEvents.ArchivedAccountsScreenOpened())
|
||||
getArchivedAccounts()
|
||||
}
|
||||
|
||||
|
|
@ -96,6 +100,7 @@ internal class ArchivedAccountListModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun recoverCryptoPortfolio(accountId: AccountId) = modelScope.launch {
|
||||
analyticsEventHandler.send(WalletSettingsAccountAnalyticEvents.ButtonRecoverAccount())
|
||||
uiState.update { it.toggleProgress(accountId, isLoading = true) }
|
||||
val result = withContext(dispatchers.default) {
|
||||
recoverCryptoPortfolioUseCase(accountId)
|
||||
|
|
@ -148,6 +153,7 @@ internal class ArchivedAccountListModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun showSuccessRecoverMessage() {
|
||||
analyticsEventHandler.send(WalletSettingsAccountAnalyticEvents.AccountRecovered())
|
||||
val message = resourceReference(R.string.account_recover_success_message)
|
||||
messageSender.send(ToastMessage(message = message))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import androidx.annotation.StringRes
|
|||
import com.tangem.common.ui.account.AccountNameUM
|
||||
import com.tangem.common.ui.account.toDomain
|
||||
import com.tangem.common.ui.account.toUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
|
||||
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
|
|
@ -25,6 +26,9 @@ import com.tangem.domain.models.account.CryptoPortfolioIcon
|
|||
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
|
||||
import com.tangem.features.account.analytics.AccountSettingsAnalyticEvents.Companion.toAnalyticSource
|
||||
import com.tangem.features.account.analytics.WalletSettingsAccountAnalyticEvents
|
||||
import com.tangem.features.account.createedit.entity.AccountCreateEditUM
|
||||
import com.tangem.features.account.createedit.entity.AccountCreateEditUMBuilder
|
||||
import com.tangem.features.account.createedit.entity.AccountCreateEditUMBuilder.Companion.portfolioIcon
|
||||
|
|
@ -54,6 +58,7 @@ internal class AccountCreateEditModel @Inject constructor(
|
|||
private val addCryptoPortfolioUseCase: AddCryptoPortfolioUseCase,
|
||||
private val getUnoccupiedAccountIndexUseCase: GetUnoccupiedAccountIndexUseCase,
|
||||
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<AccountCreateEditComponent.Params>()
|
||||
|
|
@ -65,6 +70,8 @@ internal class AccountCreateEditModel @Inject constructor(
|
|||
init {
|
||||
if (params is AccountCreateEditComponent.Params.Create) {
|
||||
updateDerivationInfo(userWalletId = params.userWalletId)
|
||||
} else {
|
||||
analyticsEventHandler.send(AccountSettingsAnalyticEvents.AccountEditScreenOpened())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -105,6 +112,12 @@ internal class AccountCreateEditModel @Inject constructor(
|
|||
val icon = state.account.portfolioIcon.toDomain()
|
||||
val index = state.account.derivationInfo.index ?: return
|
||||
val derivationIndex = DerivationIndex(value = index).getOrNull() ?: return
|
||||
val event = AccountSettingsAnalyticEvents.ButtonAddNewAccount(
|
||||
name = name,
|
||||
icon = icon,
|
||||
derivationIndex = derivationIndex.value,
|
||||
)
|
||||
analyticsEventHandler.send(event)
|
||||
|
||||
uiState.value = uiState.value.toggleProgress(showProgress = true)
|
||||
val result = addCryptoPortfolioUseCase(
|
||||
|
|
@ -118,12 +131,22 @@ internal class AccountCreateEditModel @Inject constructor(
|
|||
result
|
||||
.onLeft(::handleAddAccountError)
|
||||
.onRight {
|
||||
analyticsEventHandler.send(WalletSettingsAccountAnalyticEvents.AccountCreated())
|
||||
showMessage(R.string.account_create_success_message)
|
||||
router.pop()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAddAccountError(error: AddCryptoPortfolioUseCase.Error) {
|
||||
val event = AccountSettingsAnalyticEvents.AccountError(
|
||||
source = params.toAnalyticSource(),
|
||||
error = when (error) {
|
||||
is AddCryptoPortfolioUseCase.Error.AccountListRequirementsNotMet -> error.cause.tag
|
||||
is AddCryptoPortfolioUseCase.Error.DataOperationFailed -> error.cause.message.orEmpty()
|
||||
},
|
||||
)
|
||||
analyticsEventHandler.send(event)
|
||||
|
||||
val isDuplicateAccountNamesError = (error as? AddCryptoPortfolioUseCase.Error.AccountListRequirementsNotMet)
|
||||
?.cause is AccountList.Error.DuplicateAccountNames
|
||||
when {
|
||||
|
|
@ -141,6 +164,7 @@ internal class AccountCreateEditModel @Inject constructor(
|
|||
val icon = state.account.portfolioIcon.toDomain()
|
||||
val isNewName = name != params.account.accountName
|
||||
val isNewIcon = icon != params.account.portfolioIcon
|
||||
analyticsEventHandler.send(AccountSettingsAnalyticEvents.ButtonSave(name, icon))
|
||||
|
||||
uiState.value = uiState.value.toggleProgress(showProgress = true)
|
||||
val result = updateCryptoPortfolioUseCase(
|
||||
|
|
@ -159,6 +183,15 @@ internal class AccountCreateEditModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleEditAccountError(error: UpdateCryptoPortfolioUseCase.Error) {
|
||||
val event = AccountSettingsAnalyticEvents.AccountError(
|
||||
source = params.toAnalyticSource(),
|
||||
error = when (error) {
|
||||
is UpdateCryptoPortfolioUseCase.Error.AccountListRequirementsNotMet -> error.cause.tag
|
||||
is UpdateCryptoPortfolioUseCase.Error.DataOperationFailed -> error.cause.message.orEmpty()
|
||||
UpdateCryptoPortfolioUseCase.Error.NothingToUpdate -> error::class.simpleName.orEmpty()
|
||||
},
|
||||
)
|
||||
analyticsEventHandler.send(event)
|
||||
val isDuplicateAccountNamesError = (error as? UpdateCryptoPortfolioUseCase.Error.AccountListRequirementsNotMet)
|
||||
?.cause is AccountList.Error.DuplicateAccountNames
|
||||
when {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.features.account.details
|
|||
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.ui.account.toUM
|
||||
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
|
||||
|
|
@ -20,6 +21,7 @@ import com.tangem.domain.models.account.Account
|
|||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.features.account.AccountDetailsComponent
|
||||
import com.tangem.features.account.analytics.AccountSettingsAnalyticEvents
|
||||
import com.tangem.features.account.createedit.entity.AccountCreateEditUMBuilder.Companion.portfolioIcon
|
||||
import com.tangem.features.account.details.entity.AccountDetailsUM
|
||||
import com.tangem.features.account.details.entity.AccountDetailsUM.ArchiveMode
|
||||
|
|
@ -37,6 +39,7 @@ internal class AccountDetailsModel @Inject constructor(
|
|||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val archiveCryptoPortfolioUseCase: ArchiveCryptoPortfolioUseCase,
|
||||
singleAccountSupplier: SingleAccountSupplier,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
) : Model() {
|
||||
|
||||
|
|
@ -48,12 +51,14 @@ internal class AccountDetailsModel @Inject constructor(
|
|||
private val accountId = params.account.accountId
|
||||
|
||||
init {
|
||||
analyticsEventHandler.send(AccountSettingsAnalyticEvents.AccountSettingsScreenOpened())
|
||||
singleAccountSupplier(SingleAccountProducer.Params(accountId))
|
||||
.onEach { account -> uiState.update { buildUI(account) } }
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun onEditAccountClick(account: Account) {
|
||||
analyticsEventHandler.send(AccountSettingsAnalyticEvents.ButtonEdit())
|
||||
router.push(AppRoute.EditAccount(account))
|
||||
}
|
||||
|
||||
|
|
@ -62,17 +67,21 @@ internal class AccountDetailsModel @Inject constructor(
|
|||
source = AppRoute.ManageTokens.Source.SETTINGS,
|
||||
portfolioId = PortfolioId(account.accountId),
|
||||
)
|
||||
analyticsEventHandler.send(AccountSettingsAnalyticEvents.ButtonManageTokens())
|
||||
router.push(route)
|
||||
}
|
||||
|
||||
private fun onArchiveAccountClick() {
|
||||
analyticsEventHandler.send(AccountSettingsAnalyticEvents.ButtonArchiveAccount())
|
||||
confirmArchiveDialog()
|
||||
}
|
||||
|
||||
private fun confirmArchiveDialog() {
|
||||
val secondAction = EventMessageAction(
|
||||
title = resourceReference(R.string.common_cancel),
|
||||
onClick = {},
|
||||
onClick = {
|
||||
analyticsEventHandler.send(AccountSettingsAnalyticEvents.ButtonCancelAccountArchivation())
|
||||
},
|
||||
)
|
||||
val firstAction = EventMessageAction(
|
||||
title = resourceReference(R.string.account_details_archive_action),
|
||||
|
|
@ -90,6 +99,7 @@ internal class AccountDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun archiveCryptoPortfolio() = modelScope.launch {
|
||||
analyticsEventHandler.send(AccountSettingsAnalyticEvents.ButtonArchiveAccountConfirmation())
|
||||
uiState.update { it.toggleProgress(true) }
|
||||
archiveCryptoPortfolioUseCase(accountId)
|
||||
.onLeft { error ->
|
||||
|
|
@ -97,6 +107,7 @@ internal class AccountDetailsModel @Inject constructor(
|
|||
uiState.update { it.toggleProgress(false) }
|
||||
}
|
||||
.onRight {
|
||||
analyticsEventHandler.send(AccountSettingsAnalyticEvents.AccountArchived())
|
||||
val message = resourceReference(R.string.account_archive_success_message)
|
||||
messageSender.send(ToastMessage(message = message))
|
||||
router.pop()
|
||||
|
|
@ -104,6 +115,11 @@ internal class AccountDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun failedArchiveDialog(error: ArchiveCryptoPortfolioUseCase.Error) {
|
||||
val event = AccountSettingsAnalyticEvents.AccountError(
|
||||
source = AccountSettingsAnalyticEvents.Source.ARCHIVE,
|
||||
error = error.tag,
|
||||
)
|
||||
analyticsEventHandler.send(event)
|
||||
val titleRes = R.string.common_something_went_wrong
|
||||
val messageRes = when (error) {
|
||||
is ArchiveCryptoPortfolioUseCase.Error.CriticalTechError.AccountListRequirementsNotMet,
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ internal class DefaultPortfolioSelectorController @Inject constructor(
|
|||
|
||||
override val isEnabled: MutableStateFlow<(UserWallet, AccountStatus) -> Boolean> = MutableStateFlow { _, _ -> true }
|
||||
|
||||
override suspend fun isAccountModeSync(): Boolean = isAccountsModeEnabledUseCase.invokeSync()
|
||||
|
||||
override fun selectAccount(accountId: AccountId?) {
|
||||
_selectedAccount.tryEmit(accountId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,19 @@ internal sealed class CustomTokenAnalyticsEvent(
|
|||
),
|
||||
)
|
||||
|
||||
class AddTokenToAnotherAccount(
|
||||
currencySymbol: String,
|
||||
derivationPath: String,
|
||||
source: ManageTokensSource,
|
||||
) : CustomTokenAnalyticsEvent(
|
||||
event = "Button - Add Token To Another Account",
|
||||
params = mapOf(
|
||||
AnalyticsParam.Key.TOKEN_PARAM to currencySymbol,
|
||||
AnalyticsParam.Key.DERIVATION to derivationPath,
|
||||
AnalyticsParam.Key.SOURCE to source.analyticsName,
|
||||
),
|
||||
)
|
||||
|
||||
class NetworkSelected(networkName: String, source: ManageTokensSource) : CustomTokenAnalyticsEvent(
|
||||
event = "Custom Token Network Selected",
|
||||
params = mapOf(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.features.managetokens.component
|
|||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.managetokens.entity.customtoken.CustomTokenFormValues
|
||||
import com.tangem.features.managetokens.entity.customtoken.SelectedDerivationPath
|
||||
import com.tangem.features.managetokens.entity.customtoken.SelectedNetwork
|
||||
|
|
@ -16,7 +17,7 @@ internal interface CustomTokenFormComponent : ComposableContentComponent {
|
|||
val source: ManageTokensSource,
|
||||
val onSelectNetworkClick: (CustomTokenFormValues) -> Unit,
|
||||
val onSelectDerivationPathClick: (CustomTokenFormValues) -> Unit,
|
||||
val onCurrencyAdded: () -> Unit,
|
||||
val onCurrencyAdded: (currency: CryptoCurrency) -> Unit,
|
||||
)
|
||||
|
||||
interface Factory : ComponentFactory<Params, CustomTokenFormComponent>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import com.tangem.core.decompose.context.childByContext
|
|||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.features.managetokens.analytics.CustomTokenAnalyticsEvent
|
||||
import com.tangem.features.managetokens.component.AddCustomTokenComponent
|
||||
|
|
@ -232,7 +233,16 @@ internal class DefaultAddCustomTokenComponent @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun dismissAndNotify() {
|
||||
private fun dismissAndNotify(currency: CryptoCurrency) {
|
||||
val account = addedToAccount
|
||||
if (account is Account.CryptoPortfolio && !account.isMainAccount) {
|
||||
val event = CustomTokenAnalyticsEvent.AddTokenToAnotherAccount(
|
||||
currencySymbol = currency.symbol,
|
||||
derivationPath = currency.network.derivationPath.value.orEmpty(),
|
||||
source = params.source,
|
||||
)
|
||||
analyticsEventHandler.send(event)
|
||||
}
|
||||
params.onCurrencyAdded(addedToAccount)
|
||||
dismiss()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ internal class CustomTokenFormModel @Inject constructor(
|
|||
return@resource
|
||||
}
|
||||
|
||||
params.onCurrencyAdded()
|
||||
params.onCurrencyAdded(currency)
|
||||
}
|
||||
|
||||
private fun selectNetwork() {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.arkivanov.decompose.router.stack.StackNavigation
|
|||
import com.arkivanov.decompose.router.stack.popToFirst
|
||||
import com.arkivanov.decompose.router.stack.pushNew
|
||||
import com.arkivanov.decompose.router.stack.replaceAll
|
||||
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
|
||||
|
|
@ -47,6 +48,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCaseV2,
|
||||
private val getTokenMarketCryptoCurrency: GetTokenMarketCryptoCurrency,
|
||||
private val messageSender: UiMessageSender,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
val portfolioSelectorController: PortfolioSelectorController,
|
||||
) : Model(),
|
||||
ChooseNetworkComponent.Callbacks by callbackDelegate,
|
||||
|
|
@ -93,6 +95,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
.map { it.availableToAddData }
|
||||
.distinctUntilChanged()
|
||||
.stateIn(this)
|
||||
val isAccountMode = portfolioSelectorController.isAccountModeSync()
|
||||
|
||||
// use snapshot data, looks like we don’t need to remap at runtime
|
||||
val data = featureDataFlow.value
|
||||
|
|
@ -117,6 +120,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
// force select a portfolio, triggers [selectedPortfolio]
|
||||
portfolioSelectorController.selectAccount(accountId)
|
||||
} else {
|
||||
logAccountSelector(isAccountMode)
|
||||
navigation.replaceAll(AddToPortfolioRoutes.PortfolioSelector)
|
||||
}
|
||||
|
||||
|
|
@ -165,6 +169,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
.onEach {
|
||||
middleNavigationJob?.cancel()
|
||||
middleNavigationJob = changePortfolioNavigationFlow(data).launchIn(this)
|
||||
logAccountSelector(isAccountMode)
|
||||
navigation.pushNew(AddToPortfolioRoutes.PortfolioSelector)
|
||||
}
|
||||
.launchIn(this)
|
||||
|
|
@ -194,6 +199,12 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun logAccountSelector(isAccountMode: Boolean) {
|
||||
if (isAccountMode) {
|
||||
analyticsEventHandler.send(eventBuilder.popupToChooseAccount())
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeNetworkNavigationFlow(): Flow<SelectedNetwork> {
|
||||
return setupNetworkFlow(selectedPortfolio)
|
||||
.onEach { newNetwork ->
|
||||
|
|
@ -260,6 +271,7 @@ internal class AddToPortfolioModel @Inject constructor(
|
|||
data.availableToAddWallets[selectedAccountId.userWalletId] ?: return@combine null
|
||||
val availableToAddAccount =
|
||||
availableToAddWallets.availableToAddAccounts[selectedAccountId] ?: return@combine null
|
||||
if (!isAccountMode) analyticsEventHandler.send(eventBuilder.addToPortfolioWalletChanged())
|
||||
SelectedPortfolio(
|
||||
isAccountMode = isAccountMode,
|
||||
userWallet = availableToAddWallets.userWallet,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.features.markets.portfolio.add.impl.model
|
||||
|
||||
import com.tangem.common.ui.addtoken.AddTokenUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
|
|
@ -10,13 +11,13 @@ import com.tangem.core.ui.extensions.stringReference
|
|||
import com.tangem.core.ui.message.ToastMessage
|
||||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
||||
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.wallets.usecase.ColdWalletAndHasMissedDerivationsUseCase
|
||||
import com.tangem.features.markets.impl.R
|
||||
import com.tangem.features.markets.portfolio.add.api.SelectedNetwork
|
||||
import com.tangem.features.markets.portfolio.add.api.SelectedPortfolio
|
||||
import com.tangem.features.markets.portfolio.add.impl.AddTokenComponent
|
||||
import com.tangem.features.markets.portfolio.add.impl.model.AddTokenUiBuilder.Companion.toggleProgress
|
||||
import com.tangem.common.ui.addtoken.AddTokenUM
|
||||
import com.tangem.lib.crypto.BlockchainUtils
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
|
|
@ -74,7 +75,8 @@ internal class AddTokenModel @Inject constructor(
|
|||
analyticsEventHandler.send(analyticsEventBuilder.addToPortfolioContinue(blockchainNames))
|
||||
|
||||
val cryptoCurrency = selectedNetwork.cryptoCurrency
|
||||
val accountId = selectedPortfolio.account.account.account.accountId
|
||||
val account = selectedPortfolio.account.account.account
|
||||
val accountId = account.accountId
|
||||
manageCryptoCurrenciesUseCase(accountId = accountId, add = cryptoCurrency)
|
||||
.onLeft {
|
||||
processError(error = it)
|
||||
|
|
@ -90,6 +92,11 @@ internal class AddTokenModel @Inject constructor(
|
|||
if (status == null) {
|
||||
processError(error = null)
|
||||
} else {
|
||||
when (account) {
|
||||
is Account.CryptoPortfolio -> if (!account.isMainAccount) {
|
||||
analyticsEventHandler.send(analyticsEventBuilder.addToNotMainAccount())
|
||||
}
|
||||
}
|
||||
params.callbacks.onTokenAdded(status.status)
|
||||
}
|
||||
uiState.value = um.toggleProgress(false)
|
||||
|
|
|
|||
|
|
@ -62,10 +62,7 @@ internal class TokenActionsModel @Inject constructor(
|
|||
)
|
||||
|
||||
private fun handledQuickAction(handledAction: HandledQuickAction) {
|
||||
val event = analyticsEventBuilder.quickActionClick(
|
||||
actionUM = handledAction.action,
|
||||
blockchainName = handledAction.cryptoCurrencyData.status.currency.network.name,
|
||||
)
|
||||
val event = analyticsEventBuilder.getTokenActionClick(actionUM = handledAction.action)
|
||||
analyticsEventHandler.send(event)
|
||||
val isReceive = handledAction.action == TokenActionsBSContentUM.Action.Receive
|
||||
if (!isReceive) return
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.features.markets.portfolio.add.impl.model
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
|
|
@ -15,6 +16,7 @@ import javax.inject.Inject
|
|||
@ModelScoped
|
||||
internal class TokenActionsUiBuilder @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) {
|
||||
private val params = paramsContainer.require<TokenActionsComponent.Params>()
|
||||
|
||||
|
|
@ -32,7 +34,10 @@ internal class TokenActionsUiBuilder @Inject constructor(
|
|||
)
|
||||
return TokenActionsUM(
|
||||
token = tokenUM,
|
||||
onLaterClick = { params.callbacks.onLaterClick() },
|
||||
onLaterClick = {
|
||||
analyticsEventHandler.send(params.eventBuilder.getTokenLater())
|
||||
params.callbacks.onLaterClick()
|
||||
},
|
||||
quickActions = PortfolioTokenUMConverter.quickActions(data, tokenActionsHandler),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,14 @@ internal class PortfolioAnalyticsEvent(
|
|||
),
|
||||
)
|
||||
|
||||
fun popupToChooseAccount() = PortfolioAnalyticsEvent(
|
||||
event = "Popup to choose account",
|
||||
)
|
||||
|
||||
fun addToNotMainAccount() = PortfolioAnalyticsEvent(
|
||||
event = "Button - Add (token not to main Account)",
|
||||
)
|
||||
|
||||
fun addToPortfolioWalletChanged() = PortfolioAnalyticsEvent(event = "Wallet Selected")
|
||||
|
||||
fun addToPortfolioContinue(blockchainNames: List<String>) = PortfolioAnalyticsEvent(
|
||||
|
|
@ -47,5 +55,19 @@ internal class PortfolioAnalyticsEvent(
|
|||
put("blockchain", blockchainName)
|
||||
},
|
||||
)
|
||||
|
||||
fun getTokenActionClick(actionUM: TokenActionsBSContentUM.Action) = PortfolioAnalyticsEvent(
|
||||
event = when (actionUM) {
|
||||
TokenActionsBSContentUM.Action.Buy -> "Popup Get token - Button Buy"
|
||||
TokenActionsBSContentUM.Action.Receive -> "Popup Get token - Button Receive"
|
||||
TokenActionsBSContentUM.Action.Exchange -> "Popup Get token - Button Exchange"
|
||||
TokenActionsBSContentUM.Action.Stake -> "Popup Get token - Button Stake"
|
||||
else -> "error"
|
||||
},
|
||||
)
|
||||
|
||||
fun getTokenLater() = PortfolioAnalyticsEvent(
|
||||
event = "Popup Get token - Button Later",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -193,7 +193,10 @@ internal class MarketsPortfolioModel @Inject constructor(
|
|||
NewAddToPortfolioManager.State.NothingToAdd -> AddButtonState.Unavailable
|
||||
}
|
||||
},
|
||||
onAddClick = { bottomSheetNavigation.activate(MarketsPortfolioRoute.AddToPortfolio) },
|
||||
onAddClick = {
|
||||
analyticsEventHandler.send(analyticsEventBuilder.addToPortfolioClicked())
|
||||
bottomSheetNavigation.activate(MarketsPortfolioRoute.AddToPortfolio)
|
||||
},
|
||||
)
|
||||
newMarketsPortfolioDelegate.combineData()
|
||||
.onEach { _state.value = it }
|
||||
|
|
|
|||
|
|
@ -12,4 +12,10 @@ sealed class ReferralEvents(event: String) : AnalyticsEvent(REFERRAL_PROGRAM_CAT
|
|||
class ParticipateSuccessful : ReferralEvents(event = "Participate Successful")
|
||||
}
|
||||
|
||||
private const val REFERRAL_PROGRAM_CATEGORY = "Referral Program"
|
||||
sealed class ReferralEventsAccounts(event: String) : AnalyticsEvent(REFERRAL_PROGRAM_ACCOUNT_CATEGORY, event) {
|
||||
|
||||
class ListChooseAccount : ReferralEventsAccounts(event = "List - choose account")
|
||||
}
|
||||
|
||||
private const val REFERRAL_PROGRAM_CATEGORY = "Referral Program"
|
||||
private const val REFERRAL_PROGRAM_ACCOUNT_CATEGORY = "Referral program - Account"
|
||||
|
|
@ -30,6 +30,7 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.feature.referral.analytics.ReferralEvents
|
||||
import com.tangem.feature.referral.analytics.ReferralEventsAccounts
|
||||
import com.tangem.feature.referral.api.ReferralComponent
|
||||
import com.tangem.feature.referral.domain.ReferralInteractor
|
||||
import com.tangem.feature.referral.domain.errors.ReferralError
|
||||
|
|
@ -116,6 +117,7 @@ internal class ReferralModel @Inject constructor(
|
|||
|
||||
private fun combineAccountUI(referralData: ReferralData): Flow<AccountAward?> = combine(
|
||||
flow = portfolioSelectorController.selectedAccountWithData(portfolioFetcher)
|
||||
.onEach { analyticsEventHandler.send(ReferralEventsAccounts.ListChooseAccount()) }
|
||||
.onEach { bottomSheetNavigation.dismiss() },
|
||||
flow2 = getBalanceHidingSettingsUseCase.isBalanceHidden(),
|
||||
flow3 = getSelectedAppCurrencyUseCase.invokeOrDefault(),
|
||||
|
|
|
|||
|
|
@ -51,16 +51,29 @@ sealed class CommonSendAnalyticEvents(
|
|||
),
|
||||
)
|
||||
|
||||
@Suppress("NullableToStringCall")
|
||||
/** Confirmation screen opened */
|
||||
data class ConfirmationScreenOpened(
|
||||
val categoryName: String,
|
||||
val source: CommonSendSource,
|
||||
val fromDerivationIndex: Int?,
|
||||
val toDerivationIndex: Int?,
|
||||
val sendBlockchain: String,
|
||||
val sendToken: String,
|
||||
) : CommonSendAnalyticEvents(
|
||||
category = categoryName,
|
||||
event = "Confirm Screen Opened",
|
||||
params = mapOf(
|
||||
SOURCE to source.analyticsName,
|
||||
),
|
||||
params = buildMap {
|
||||
put(SOURCE, source.analyticsName)
|
||||
put("Token", sendToken)
|
||||
put("Blockchain", sendBlockchain)
|
||||
if (fromDerivationIndex != null || toDerivationIndex != null) {
|
||||
put(
|
||||
"Account Derivation From or To (optional)",
|
||||
"$fromDerivationIndex, $toDerivationIndex",
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
/** If transaction delays notification is present */
|
||||
|
|
|
|||
|
|
@ -88,10 +88,17 @@ internal class DefaultSendComponent @AssistedInject constructor(
|
|||
componentScope.launch {
|
||||
when (val activeComponent = stack.active.instance) {
|
||||
is SendConfirmComponent -> {
|
||||
val fromCurrency = params.currency
|
||||
val fromDerivationIndex = model.accountFlow.value?.derivationIndex?.value
|
||||
.takeIf { model.isAccountModeFlow.value }
|
||||
analyticsEventHandler.send(
|
||||
CommonSendAnalyticEvents.ConfirmationScreenOpened(
|
||||
categoryName = model.analyticCategoryName,
|
||||
source = model.analyticsSendSource,
|
||||
sendBlockchain = fromCurrency.network.name,
|
||||
sendToken = fromCurrency.symbol,
|
||||
fromDerivationIndex = fromDerivationIndex,
|
||||
toDerivationIndex = null,
|
||||
),
|
||||
)
|
||||
if (model.currentRoute.value.isEditMode) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.features.send.v2.send.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.BLOCKCHAIN
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.ENS_ADDRESS
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.FEE_TYPE
|
||||
|
|
@ -25,18 +26,21 @@ internal sealed class SendAnalyticEvents(
|
|||
val blockchain: String,
|
||||
val isNonceNotEmpty: Boolean,
|
||||
private val ensStatus: AnalyticsParam.EmptyFull,
|
||||
val derivationIndex: Int?,
|
||||
) : SendAnalyticEvents(
|
||||
event = "Transaction Sent Screen Opened",
|
||||
params = mapOf(
|
||||
TOKEN_PARAM to token,
|
||||
FEE_TYPE to feeType.value,
|
||||
BLOCKCHAIN to blockchain,
|
||||
NONCE to isNonceNotEmpty.toString().capitalize(),
|
||||
ENS_ADDRESS to when (ensStatus) {
|
||||
params = buildMap {
|
||||
put(TOKEN_PARAM, token)
|
||||
put(FEE_TYPE, feeType.value)
|
||||
put(BLOCKCHAIN, blockchain)
|
||||
if (derivationIndex != null) put(ACCOUNT_DERIVATION_FROM, derivationIndex.toString())
|
||||
put(NONCE, isNonceNotEmpty.toString().capitalize())
|
||||
val ensAddress = when (ensStatus) {
|
||||
AnalyticsParam.EmptyFull.Empty -> false.toString().capitalize()
|
||||
AnalyticsParam.EmptyFull.Full -> true.toString().capitalize()
|
||||
},
|
||||
),
|
||||
}
|
||||
put(ENS_ADDRESS, ensAddress)
|
||||
},
|
||||
)
|
||||
|
||||
data class ConvertTokenButtonClicked(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
|
|||
import com.tangem.core.analytics.models.AnalyticsParam
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.features.send.v2.api.entity.FeeNonce
|
||||
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
|
||||
|
|
@ -17,10 +18,12 @@ internal class SendAnalyticHelper @Inject constructor(
|
|||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) {
|
||||
|
||||
fun sendSuccessAnalytics(cryptoCurrency: CryptoCurrency, sendUM: SendUM) {
|
||||
fun sendSuccessAnalytics(cryptoCurrency: CryptoCurrency, sendUM: SendUM, account: Account.CryptoPortfolio?) {
|
||||
val destinationUM = sendUM.destinationUM as? DestinationUM.Content
|
||||
val feeSelectorUM = sendUM.feeSelectorUM as? FeeSelectorUM.Content ?: return
|
||||
val feeType = feeSelectorUM.toAnalyticType()
|
||||
val isNotMainAccount = account != null && !account.isMainAccount
|
||||
val derivationIndex = if (isNotMainAccount) account.derivationIndex.value else null
|
||||
analyticsEventHandler.send(
|
||||
SendAnalyticEvents.TransactionScreenOpened(
|
||||
token = cryptoCurrency.symbol,
|
||||
|
|
@ -28,6 +31,7 @@ internal class SendAnalyticHelper @Inject constructor(
|
|||
blockchain = cryptoCurrency.network.name,
|
||||
isNonceNotEmpty = feeSelectorUM.feeNonce is FeeNonce.Nonce,
|
||||
ensStatus = getEnsStatus(sendUM),
|
||||
derivationIndex = derivationIndex,
|
||||
),
|
||||
)
|
||||
analyticsEventHandler.send(
|
||||
|
|
|
|||
|
|
@ -416,7 +416,11 @@ internal class SendConfirmModel @Inject constructor(
|
|||
updateTransactionStatus(txData)
|
||||
addTokenToWalletIfNeeded()
|
||||
sendBalanceUpdater.scheduleUpdates()
|
||||
sendAnalyticHelper.sendSuccessAnalytics(cryptoCurrency, uiState.value)
|
||||
sendAnalyticHelper.sendSuccessAnalytics(
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
sendUM = uiState.value,
|
||||
account = params.accountFlow.value,
|
||||
)
|
||||
params.callback.onResult(uiState.value)
|
||||
params.onSendTransaction()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -79,10 +79,17 @@ internal class DefaultNFTSendComponent @AssistedInject constructor(
|
|||
componentScope.launch {
|
||||
when (val activeComponent = stack.active.instance) {
|
||||
is NFTSendConfirmComponent -> {
|
||||
val fromCurrency = model.cryptoCurrency
|
||||
val fromDerivationIndex = model.account?.derivationIndex?.value
|
||||
.takeIf { model.isAccountsMode }
|
||||
analyticsEventHandler.send(
|
||||
CommonSendAnalyticEvents.ConfirmationScreenOpened(
|
||||
categoryName = analyticsCategoryName,
|
||||
source = analyticsSendSource,
|
||||
sendBlockchain = fromCurrency.network.name,
|
||||
sendToken = fromCurrency.symbol,
|
||||
fromDerivationIndex = fromDerivationIndex,
|
||||
toDerivationIndex = null,
|
||||
),
|
||||
)
|
||||
if (model.currentRouteFlow.value.isEditMode) {
|
||||
|
|
|
|||
|
|
@ -109,10 +109,17 @@ internal class DefaultSendWithSwapComponent @AssistedInject constructor(
|
|||
activeComponent.updateState(model.uiState.value.destinationUM)
|
||||
}
|
||||
is SendWithSwapConfirmComponent -> {
|
||||
val fromCurrency = params.currency
|
||||
val fromDerivationIndex = model.accountFlow.value?.derivationIndex?.value
|
||||
.takeIf { model.isAccountModeFlow.value }
|
||||
analyticsEventHandler.send(
|
||||
CommonSendAnalyticEvents.ConfirmationScreenOpened(
|
||||
categoryName = model.analyticCategoryName,
|
||||
source = model.analyticsSendSource,
|
||||
sendBlockchain = fromCurrency.network.name,
|
||||
sendToken = fromCurrency.symbol,
|
||||
fromDerivationIndex = fromDerivationIndex,
|
||||
toDerivationIndex = null,
|
||||
),
|
||||
)
|
||||
if (model.currentRoute.value.isEditMode) {
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ sealed class SwapEvents(
|
|||
|
||||
class ButtonSwipeClicked : SwapEvents(event = "Button - Swipe")
|
||||
|
||||
@Suppress("NullableToStringCall")
|
||||
data class SwapInProgressScreen(
|
||||
val provider: SwapProvider,
|
||||
val commission: FeeType, // Market / Fast
|
||||
|
|
@ -69,6 +70,8 @@ sealed class SwapEvents(
|
|||
val receiveBlockchain: String,
|
||||
val sendToken: String,
|
||||
val receiveToken: String,
|
||||
val fromDerivationIndex: Int?,
|
||||
val toDerivationIndex: Int?,
|
||||
) : SwapEvents(
|
||||
event = "Swap in Progress Screen Opened",
|
||||
params = mapOf(
|
||||
|
|
@ -78,6 +81,7 @@ sealed class SwapEvents(
|
|||
"Receive Token" to receiveToken,
|
||||
"Send Blockchain" to sendBlockchain,
|
||||
"Receive Blockchain" to receiveBlockchain,
|
||||
"Account Derivation From or To (optional)" to "$fromDerivationIndex, $toDerivationIndex",
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -847,6 +847,8 @@ internal class SwapModel @Inject constructor(
|
|||
val fee = dataState.selectedFee?.feeType ?: return
|
||||
val fromCurrency = dataState.fromCryptoCurrency?.currency ?: return
|
||||
val toCurrency = dataState.toCryptoCurrency?.currency ?: return
|
||||
val fromDerivationIndex = dataState.fromAccount?.derivationIndex?.value
|
||||
val toDerivationIndex = dataState.toAccount?.derivationIndex?.value
|
||||
|
||||
analyticsEventHandler.send(
|
||||
SwapEvents.SwapInProgressScreen(
|
||||
|
|
@ -856,6 +858,8 @@ internal class SwapModel @Inject constructor(
|
|||
receiveBlockchain = toCurrency.network.name,
|
||||
sendToken = fromCurrency.symbol,
|
||||
receiveToken = toCurrency.symbol,
|
||||
fromDerivationIndex = fromDerivationIndex,
|
||||
toDerivationIndex = toDerivationIndex,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -532,6 +532,7 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
status = unavailabilityReason.toReasonAnalyticsText(),
|
||||
derivationIndex = getAccountIndexOrNull(),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -557,6 +558,7 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
status = null,
|
||||
derivationIndex = getAccountIndexOrNull(),
|
||||
),
|
||||
)
|
||||
router.openTokenDetails(userWalletId = userWalletId, currency = cryptoCurrency)
|
||||
|
|
@ -578,6 +580,7 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
status = unavailabilityReason.toReasonAnalyticsText(),
|
||||
derivationIndex = getAccountIndexOrNull(),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -698,6 +701,7 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
status = unavailabilityReason.toReasonAnalyticsText(),
|
||||
derivationIndex = getAccountIndexOrNull(),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -766,6 +770,12 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
showErrorIfDemoModeOrElse(action = ::openExplorer)
|
||||
}
|
||||
|
||||
private fun getAccountIndexOrNull(): Int? {
|
||||
val account = account
|
||||
val isNotMainAccount = account != null && !account.isMainAccount
|
||||
return if (isNotMainAccount) account.derivationIndex.value else null
|
||||
}
|
||||
|
||||
private fun openExplorer() {
|
||||
val currencyStatus = cryptoCurrencyStatus ?: return
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ import com.tangem.core.ui.message.EventMessageAction
|
|||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.core.ui.message.bottomSheetMessage
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
||||
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
|
||||
import com.tangem.domain.card.common.util.cardTypesResolver
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.models.PortfolioId
|
||||
|
|
@ -88,6 +90,8 @@ internal class WalletSettingsModel @Inject constructor(
|
|||
private val dismissUpgradeWalletNotificationUseCase: DismissUpgradeWalletNotificationUseCase,
|
||||
private val unlockHotWalletContextualUseCase: UnlockHotWalletContextualUseCase,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
private val singleAccountListSupplier: SingleAccountListSupplier,
|
||||
private val accountListSortingSaver: AccountListSortingSaver,
|
||||
) : Model() {
|
||||
|
||||
|
|
@ -115,7 +119,18 @@ internal class WalletSettingsModel @Inject constructor(
|
|||
init {
|
||||
getUserWalletUseCase.invoke(params.userWalletId).onRight { wallet ->
|
||||
trackingContextProxy.addContext(wallet)
|
||||
analyticsEventHandler.send(WalletSettingsAnalyticEvents.WalletSettingsScreenOpened())
|
||||
modelScope.launch {
|
||||
val accountsCount = if (isAccountsModeEnabledUseCase.invokeSync()) {
|
||||
singleAccountListSupplier(wallet.walletId)
|
||||
.first()
|
||||
.accounts
|
||||
.size
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val event = WalletSettingsAnalyticEvents.WalletSettingsScreenOpened(accountsCount)
|
||||
analyticsEventHandler.send(event)
|
||||
}
|
||||
}
|
||||
|
||||
fun combineUI(wallet: UserWallet) = combine(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.walletsettings.utils
|
|||
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.ui.account.AccountPortfolioItemUMConverter
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
|
|
@ -24,6 +25,7 @@ import com.tangem.domain.models.account.AccountId
|
|||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
|
||||
import com.tangem.domain.wallets.extension.isAccountsSupported
|
||||
import com.tangem.feature.walletsettings.component.WalletSettingsComponent
|
||||
import com.tangem.feature.walletsettings.entity.WalletSettingsAccountsUM
|
||||
|
|
@ -46,6 +48,7 @@ internal class AccountItemsDelegate @Inject constructor(
|
|||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
private val accountListSortingSaver: AccountListSortingSaver,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) {
|
||||
|
||||
private val userWalletId = paramsContainer.require<WalletSettingsComponent.Params>().userWalletId
|
||||
|
|
@ -72,7 +75,10 @@ internal class AccountItemsDelegate @Inject constructor(
|
|||
): List<WalletSettingsAccountsUM> = buildList {
|
||||
fun AccountStatus.CryptoPortfolio.mapCryptoPortfolio(): WalletSettingsAccountsUM {
|
||||
val accountItemUM = AccountPortfolioItemUMConverter(
|
||||
onClick = { openAccountDetails(this.account) },
|
||||
onClick = {
|
||||
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonOpenExistingAccount())
|
||||
openAccountDetails(this.account)
|
||||
},
|
||||
appCurrency = appCurrency,
|
||||
accountBalance = this.tokenList.totalFiatBalance,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
|
|
@ -107,14 +113,22 @@ internal class AccountItemsDelegate @Inject constructor(
|
|||
title = resourceReference(R.string.account_form_title_create),
|
||||
isAddAccountEnabled = isAddAccountEnabled,
|
||||
onAddAccountClick = {
|
||||
if (isAddAccountEnabled) openAddAccount(userWalletId) else canNotAddAccountDialog()
|
||||
if (isAddAccountEnabled) {
|
||||
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonAddAccount())
|
||||
openAddAccount(userWalletId)
|
||||
} else {
|
||||
canNotAddAccountDialog()
|
||||
}
|
||||
},
|
||||
),
|
||||
archivedAccounts = if (isArchivedAccountsEnabled) {
|
||||
BlockUM(
|
||||
text = resourceReference(R.string.account_archived_accounts),
|
||||
iconRes = R.drawable.ic_archive_24,
|
||||
onClick = { openArchivedAccounts(userWalletId) },
|
||||
onClick = {
|
||||
analyticsEventHandler.send(WalletSettingsAnalyticEvents.ButtonArchivedAccounts())
|
||||
openArchivedAccounts(userWalletId)
|
||||
},
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package com.tangem.feature.walletsettings.utils
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.domain.account.usecase.ApplyAccountListSortingUseCase
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
|
|
@ -24,6 +26,7 @@ import kotlin.time.Duration.Companion.seconds
|
|||
@OptIn(FlowPreview::class)
|
||||
internal class AccountListSortingSaver @Inject constructor(
|
||||
private val applyAccountListSortingUseCase: ApplyAccountListSortingUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
|
|
@ -41,6 +44,9 @@ internal class AccountListSortingSaver @Inject constructor(
|
|||
applyAccountListSortingUseCase.invoke(accountIds).onLeft {
|
||||
Timber.e("Error while saving account list sorting: $it")
|
||||
}
|
||||
.onRight {
|
||||
analyticsEventHandler.send(WalletSettingsAnalyticEvents.LongtapAccountsOrder())
|
||||
}
|
||||
}
|
||||
.launchIn(coroutineScope)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import com.tangem.core.analytics.utils.TrackingContextProxy
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.supplier.SingleAccountListSupplier
|
||||
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
|
@ -99,6 +101,8 @@ internal class WalletModel @Inject constructor(
|
|||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
|
||||
private val trackingContextProxy: TrackingContextProxy,
|
||||
private val singleAccountListSupplier: SingleAccountListSupplier,
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
private val feedFeatureToggle: FeedFeatureToggle,
|
||||
val screenLifecycleProvider: ScreenLifecycleProvider,
|
||||
val innerWalletRouter: InnerWalletRouter,
|
||||
|
|
@ -293,8 +297,19 @@ internal class WalletModel @Inject constructor(
|
|||
modelScope.launch {
|
||||
val hasMobileWallet = userWalletsListRepository.userWalletsSync()
|
||||
.any { it is UserWallet.Hot }
|
||||
val accountsCount = if (isAccountsModeEnabledUseCase.invokeSync()) {
|
||||
singleAccountListSupplier(selectedWallet.walletId)
|
||||
.first()
|
||||
.accounts
|
||||
.size
|
||||
} else {
|
||||
null
|
||||
}
|
||||
analyticsEventsHandler.send(
|
||||
WalletScreenAnalyticsEvent.MainScreen.ScreenOpened(hasMobileWallet),
|
||||
WalletScreenAnalyticsEvent.MainScreen.ScreenOpened(
|
||||
hasMobileWallet = hasMobileWallet,
|
||||
accountsCount = accountsCount,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -196,11 +196,13 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
|
||||
override fun onAccountExpandClick(account: Account) {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.AccountShowTokens())
|
||||
accountDependencies.expandedAccountsHolder.expandAccount(userWalletId, account.accountId)
|
||||
}
|
||||
|
||||
override fun onAccountCollapseClick(account: Account) {
|
||||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
analyticsEventHandler.send(MainScreenAnalyticsEvent.AccountHideTokens())
|
||||
accountDependencies.expandedAccountsHolder.collapseAccount(userWalletId, account.accountId)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,9 +57,13 @@ sealed class WalletScreenAnalyticsEvent {
|
|||
|
||||
data class ScreenOpened(
|
||||
private val hasMobileWallet: Boolean,
|
||||
private val accountsCount: Int?,
|
||||
) : MainScreen(
|
||||
event = "Screen opened",
|
||||
params = mapOf("Mobile Wallet" to if (hasMobileWallet) "Yes" else "No"),
|
||||
params = buildMap {
|
||||
put("Mobile Wallet", if (hasMobileWallet) "Yes" else "No")
|
||||
if (accountsCount != null) put("Accounts Count", accountsCount.toString())
|
||||
},
|
||||
)
|
||||
|
||||
data class NoticeFinishActivation(
|
||||
|
|
|
|||
|
|
@ -31,6 +31,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.models.wallet.isMultiCurrency
|
||||
import com.tangem.domain.walletconnect.WcAnalyticAccountEvents
|
||||
import com.tangem.domain.walletconnect.WcAnalyticEvents
|
||||
import com.tangem.domain.walletconnect.model.WcPairError
|
||||
import com.tangem.domain.walletconnect.model.WcPairError.Unknown
|
||||
|
|
@ -319,6 +320,17 @@ internal class WcPairModel @Inject constructor(
|
|||
val selectedPortfolio = selectedPortfolio.replayCache.firstOrNull()
|
||||
val wallet = selectedPortfolio?.first ?: selectedUserWalletFlow.value
|
||||
val account = selectedPortfolio?.second?.account
|
||||
|
||||
modelScope.launch {
|
||||
if (selectorController.isAccountModeSync() && account != null) {
|
||||
val derivationIndex = when (account) {
|
||||
is Account.CryptoPortfolio -> account.derivationIndex.value
|
||||
}
|
||||
analytics.send(WcAnalyticAccountEvents.PairButtonConnect(derivationIndex))
|
||||
} else {
|
||||
analytics.send(WcAnalyticEvents.PairButtonConnect())
|
||||
}
|
||||
}
|
||||
wcPairUseCase.approve(
|
||||
WcSessionApprove(
|
||||
wallet = wallet,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue