Updated on 2026-08-14

This commit is contained in:
Tangem 2023-01-25 01:33:28 +03:00
parent 35dbee8691
commit 3a57ac60c2
13 changed files with 177 additions and 61 deletions

View file

@ -2,77 +2,156 @@ package com.tangem.tap.common.analytics.converters
import com.tangem.common.Converter
import com.tangem.common.extensions.isZero
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.ScanResponse
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.common.analytics.filters.BasicTopUpFilter
import com.tangem.tap.domain.extensions.isMultiwalletAllowed
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.reducers.calculateTotalCryptoAmount
import timber.log.Timber
import java.math.BigDecimal
/**
[REDACTED_AUTHOR]
*/
class BasicSignInEventConverter(
private val scanResponse: ScanResponse,
) : Converter<WalletState, Basic.SignedIn?> {
class BasicEventsPreChecker {
override fun convert(value: WalletState): Basic.SignedIn? {
if (!statesIsReadyToCreateEvent(scanResponse, value)) return null
val cardCurrency = ParamCardCurrencyConverter().convert(scanResponse) ?: return null
fun tryToSend(converterData: BasicEventsSourceData) {
if (!isReadyToSend(converterData)) return
BasicSignInEventConverter().convert(converterData)?.let { Analytics.send(it) }
BasicTopUpEventConverter().convert(converterData)?.let { Analytics.send(it) }
}
private fun isReadyToSend(data: BasicEventsSourceData): Boolean {
val (scanResponse, walletState, biometricsWalletDataModels) = data
if (walletState.derivationsCheckIsScheduled) {
Timber.d("FAILED: derivationsCheckIsScheduled")
return false
}
if (scanResponse.card.isMultiwalletAllowed && walletState.missingDerivations.isNotEmpty()) {
Timber.d("FAILED: isMultiwalletAllowed || missingDerivations.isNotEmpty")
return false
}
if (biometricsWalletDataModels == null) {
Timber.d("SWITCH: OLD")
val walletsDataFromStores = data.walletState.walletsDataFromStores
if (walletsDataFromStores.isEmpty()) {
Timber.d("FAILED: walletsDataFromStores.isEmpty")
return false
}
val totalBalanceState = data.walletState.totalBalance?.state
if (totalBalanceState == null || totalBalanceState == ProgressState.Loading ||
totalBalanceState == ProgressState.Refreshing
) {
Timber.d("FAILED: totalBalanceState: ${totalBalanceState?.name}")
return false
}
val balancesCount = walletsDataFromStores
.map { if (it.currencyData.amount == null) 0 else 1 }
.reduce { acc, i -> acc + i }
if (balancesCount != walletsDataFromStores.size) {
Timber.d("FAILED: balancesCount != walletsDataFromStores.size")
return false
}
} else {
Timber.d("SWITCH: BIOMETRICS")
if (biometricsWalletDataModels.isEmpty()) {
Timber.d("FAILED: biometricsWalletDataModels.isEmpty")
return false
}
if (biometricsWalletDataModels.any {
it.status is WalletDataModel.Loading ||
it.status is WalletDataModel.NoAccount ||
it.status is WalletDataModel.Unreachable ||
it.status is WalletDataModel.MissedDerivation ||
it.status.isErrorStatus
}) {
Timber.d("FAILED: by status")
return false
}
}
Timber.d("SUCCESS")
return true
}
}
/**
* With biometrics enabled, we should check its storage instead of "WalletState.walletsDataFromStores"
* because the latter is updated after some time.
* @property biometricsWalletDataModels - wallet data models from the 'WalletStoresManager'. If null, then
* the "WalletState.walletsDataFromStores" will be used to determine appropriate state
*/
data class BasicEventsSourceData(
val scanResponse: ScanResponse,
val walletState: WalletState,
val biometricsWalletDataModels: List<WalletDataModel>?,
) {
val batchId: String by lazy { scanResponse.card.batchId }
val userWalletIdStringValue: String? by lazy {
UserWalletIdBuilder.scanResponse(scanResponse)
.build()
?.stringValue
}
val paramCardCurrency: AnalyticsParam.CardCurrency? by lazy { ParamCardCurrencyConverter().convert(scanResponse) }
val paramCardBalanceState: AnalyticsParam.CardBalanceState by lazy { calculateAmount().toCardBalanceState() }
private fun calculateAmount(): BigDecimal {
return biometricsWalletDataModels?.calculateTotalCryptoAmount()
?: walletState.walletsDataFromStores.calculateTotalCryptoAmount()
}
private fun BigDecimal.toCardBalanceState(): AnalyticsParam.CardBalanceState = when {
isZero() -> AnalyticsParam.CardBalanceState.Empty
else -> AnalyticsParam.CardBalanceState.Full
}
private fun List<WalletDataModel>.calculateTotalCryptoAmount(): BigDecimal {
return this
.map { it.status.amount }
.reduce(BigDecimal::plus)
}
}
class BasicSignInEventConverter : Converter<BasicEventsSourceData, Basic.SignedIn?> {
override fun convert(value: BasicEventsSourceData): Basic.SignedIn? {
if (value.paramCardCurrency == null || value.userWalletIdStringValue == null) return null
return Basic.SignedIn(
state = AnalyticsParam.CardBalanceState.from(value.walletsDataFromStores),
currency = cardCurrency,
batch = scanResponse.card.batchId,
state = value.paramCardBalanceState,
currency = value.paramCardCurrency!!,
batch = value.batchId,
).apply {
filterData = UserWalletIdBuilder.scanResponse(scanResponse)
.build()
?.stringValue
filterData = value.userWalletIdStringValue
}
}
}
class BasicTopUpEventConverter(
private val scanResponse: ScanResponse,
) : Converter<WalletState, Basic.ToppedUp?> {
class BasicTopUpEventConverter : Converter<BasicEventsSourceData, Basic.ToppedUp?> {
override fun convert(value: WalletState): Basic.ToppedUp? {
if (!statesIsReadyToCreateEvent(scanResponse, value)) return null
val cardCurrency = ParamCardCurrencyConverter().convert(scanResponse) ?: return null
override fun convert(value: BasicEventsSourceData): Basic.ToppedUp? {
if (value.paramCardCurrency == null || value.userWalletIdStringValue == null) return null
val data = BasicTopUpFilter.Data(
walletId = UserWalletIdBuilder.scanResponse(scanResponse).build()?.stringValue ?: "",
cardBalanceState = AnalyticsParam.CardBalanceState.from(value.walletsDataFromStores),
walletId = value.userWalletIdStringValue!!,
cardBalanceState = value.paramCardBalanceState,
)
return Basic.ToppedUp(cardCurrency).apply { filterData = data }
return Basic.ToppedUp(value.paramCardCurrency!!).apply { filterData = data }
}
}
private fun AnalyticsParam.CardBalanceState.Companion.from(walletsData: List<WalletData>): AnalyticsParam.CardBalanceState {
val totalCryptoAmount = walletsData.calculateTotalCryptoAmount()
return when {
totalCryptoAmount.isZero() -> AnalyticsParam.CardBalanceState.Empty
else -> AnalyticsParam.CardBalanceState.Full
}
}
private fun statesIsReadyToCreateEvent(scanResponse: ScanResponse, state: WalletState): Boolean {
if (scanResponse.card.isMultiwalletAllowed && state.missingDerivations.isNotEmpty()) return false
if (state.walletsDataFromStores.isEmpty()) return false
val totalBalanceState = state.totalBalance?.state ?: return false
if (totalBalanceState == ProgressState.Loading || totalBalanceState == ProgressState.Refreshing) return false
val balancesCount = state.walletsDataFromStores.map {
if (it.currencyData.amount == null) 0 else 1
}.reduce { acc, i -> acc + i }
if (balancesCount != state.walletsDataFromStores.size) return false
return true
}

View file

@ -22,6 +22,7 @@ class ParamCardCurrencyConverter : Converter<ScanResponse, AnalyticsParam.CardCu
}
value.isTangemTwins() -> AnalyticsParam.CurrencyType.Blockchain(Blockchain.Bitcoin)
value.isSaltPay() -> AnalyticsParam.CurrencyType.Token(SaltPayWorkaround.tokenFrom(Blockchain.SaltPay))
value.getBlockchain() != Blockchain.Unknown -> AnalyticsParam.CurrencyType.Blockchain(value.getBlockchain())
value.getPrimaryToken() != null -> AnalyticsParam.CurrencyType.Token(value.getPrimaryToken()!!)
else -> null
} ?: return null