Updated on 2026-08-14
This commit is contained in:
commit
85336b220e
64 changed files with 589 additions and 449 deletions
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="tangem_app_name" translatable="false">DebugTangem</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="tangem_app_name" translatable="false">DebugTangem</string>
|
||||
<string name="tangem_app_name" translatable="false">Debug Tangem</string>
|
||||
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
|||
6
app/src/external/res/values-ru/strings.xml
vendored
6
app/src/external/res/values-ru/strings.xml
vendored
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="tangem_app_name" translatable="false">ExternalTangem</string>
|
||||
|
||||
</resources>
|
||||
2
app/src/external/res/values/strings.xml
vendored
2
app/src/external/res/values/strings.xml
vendored
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="tangem_app_name" translatable="false">ExternalTangem</string>
|
||||
<string name="tangem_app_name" translatable="false">External Tangem</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="tangem_app_name" translatable="false">InternalTangem</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="tangem_app_name" translatable="false">InternalTangem</string>
|
||||
<string name="tangem_app_name" translatable="false">Internal Tangem</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -23,11 +23,10 @@ import com.tangem.domain.common.LogConfig
|
|||
import com.tangem.tap.common.IntentHandler
|
||||
import com.tangem.tap.common.analytics.AnalyticsFactory
|
||||
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
|
||||
import com.tangem.tap.common.analytics.filters.BasicSignInFilter
|
||||
import com.tangem.tap.common.analytics.filters.BasicTopUpFilter
|
||||
import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.handlers.appsFlyer.AppsFlyerAnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler
|
||||
import com.tangem.tap.common.analytics.topup.TopUpController
|
||||
import com.tangem.tap.common.chat.ChatManager
|
||||
import com.tangem.tap.common.feedback.AdditionalFeedbackInfo
|
||||
import com.tangem.tap.common.feedback.FeedbackManager
|
||||
|
|
@ -181,6 +180,20 @@ class TapApplication : Application(), ImageLoaderFactory {
|
|||
scope.launch {
|
||||
featureTogglesManager.init()
|
||||
}
|
||||
|
||||
initTopUpController()
|
||||
}
|
||||
|
||||
private fun initTopUpController() {
|
||||
val topUpController = TopUpController(
|
||||
scanResponseProvider = {
|
||||
store.state.globalState.scanResponse
|
||||
?: store.state.globalState.onboardingState.onboardingManager?.scanResponse
|
||||
},
|
||||
walletStoresManagerProvider = { walletStoresManager },
|
||||
topupWalletStorage = preferencesStorage.toppedUpWalletStorage,
|
||||
)
|
||||
store.dispatch(GlobalAction.SetTopUpController(topUpController))
|
||||
}
|
||||
|
||||
override fun newImageLoader(): ImageLoader {
|
||||
|
|
@ -213,9 +226,6 @@ class TapApplication : Application(), ImageLoaderFactory {
|
|||
factory.addHandlerBuilder(AppsFlyerAnalyticsHandler.Builder())
|
||||
factory.addHandlerBuilder(FirebaseAnalyticsHandler.Builder())
|
||||
|
||||
factory.addFilter(BasicSignInFilter())
|
||||
factory.addFilter(BasicTopUpFilter(preferencesStorage.toppedUpWalletStorage))
|
||||
|
||||
val buildData = AnalyticsHandlerBuilder.Data(
|
||||
application = application,
|
||||
config = config,
|
||||
|
|
|
|||
|
|
@ -1,194 +0,0 @@
|
|||
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.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.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.reducers.calculateTotalCryptoAmount
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class BasicEventsPreChecker {
|
||||
|
||||
fun tryToSend(converterData: BasicEventsSourceData) {
|
||||
if (!isReadyToSend(converterData)) return
|
||||
|
||||
BasicSignInEventConverter().convert(converterData)?.let { Analytics.send(it) }
|
||||
BasicTopUpEventConverter().convert(converterData)?.let { Analytics.send(it) }
|
||||
}
|
||||
|
||||
@Suppress("ComplexMethod")
|
||||
private fun isReadyToSend(data: BasicEventsSourceData): Boolean {
|
||||
val (scanResponse, walletState, biometricsWalletDataModels) = data
|
||||
if (walletState.derivationsCheckIsScheduled) {
|
||||
Timber.d("FAILED: derivationsCheckIsScheduled")
|
||||
return false
|
||||
}
|
||||
if (scanResponse.cardTypesResolver.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
|
||||
}
|
||||
|
||||
val isCorrectStatus = 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
|
||||
}
|
||||
if (isCorrectStatus) {
|
||||
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.cardTypesResolver)
|
||||
}
|
||||
|
||||
val paramCardBalanceState: AnalyticsParam.CardBalanceState by lazy { calculateAmount().toCardBalanceState() }
|
||||
|
||||
private fun calculateAmount(): BigDecimal {
|
||||
val calculator: IBalanceCalculator = biometricsWalletDataModels
|
||||
?.let { BiometricsBalanceCalculator(it) }
|
||||
?: BalanceCalculator(walletState)
|
||||
|
||||
return calculator.calculate()
|
||||
}
|
||||
|
||||
private fun BigDecimal.toCardBalanceState(): AnalyticsParam.CardBalanceState = when {
|
||||
isZero() -> AnalyticsParam.CardBalanceState.Empty
|
||||
else -> AnalyticsParam.CardBalanceState.Full
|
||||
}
|
||||
}
|
||||
|
||||
private interface IBalanceCalculator {
|
||||
fun calculate(): BigDecimal
|
||||
}
|
||||
|
||||
private class BiometricsBalanceCalculator(
|
||||
private val walletDataModel: List<WalletDataModel>,
|
||||
) : IBalanceCalculator {
|
||||
|
||||
override fun calculate(): BigDecimal {
|
||||
val singleToken = walletDataModel
|
||||
.filter { it.currency.isToken() }
|
||||
.firstOrNull { it.isCardSingleToken }
|
||||
|
||||
val totalAmount = singleToken?.status?.amount
|
||||
?: walletDataModel.calculateTotalCryptoAmount()
|
||||
|
||||
return totalAmount
|
||||
}
|
||||
|
||||
private fun List<WalletDataModel>.calculateTotalCryptoAmount(): BigDecimal = this
|
||||
.map { it.status.amount }
|
||||
.reduce(BigDecimal::plus)
|
||||
}
|
||||
|
||||
private class BalanceCalculator(
|
||||
private val walletState: WalletState,
|
||||
) : IBalanceCalculator {
|
||||
|
||||
override fun calculate(): BigDecimal {
|
||||
val singleTokenData = walletState.primaryTokenData
|
||||
val totalAmount = singleTokenData?.currencyData?.amount
|
||||
?: walletState.walletsDataFromStores.calculateTotalCryptoAmount()
|
||||
|
||||
return totalAmount
|
||||
}
|
||||
}
|
||||
|
||||
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 = value.paramCardBalanceState,
|
||||
currency = value.paramCardCurrency!!,
|
||||
batch = value.batchId,
|
||||
).apply {
|
||||
filterData = value.userWalletIdStringValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BasicTopUpEventConverter : Converter<BasicEventsSourceData, Basic.ToppedUp?> {
|
||||
|
||||
override fun convert(value: BasicEventsSourceData): Basic.ToppedUp? {
|
||||
if (value.paramCardCurrency == null || value.userWalletIdStringValue == null) return null
|
||||
|
||||
val data = BasicTopUpFilter.Data(
|
||||
walletId = value.userWalletIdStringValue!!,
|
||||
cardBalanceState = value.paramCardBalanceState,
|
||||
)
|
||||
|
||||
return Basic.ToppedUp(value.paramCardCurrency!!).apply { filterData = data }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.tap.common.analytics.converters
|
||||
|
||||
import com.tangem.common.Converter
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class TopUpEventConverter : Converter<CardTypesResolver, Basic.ToppedUp?> {
|
||||
|
||||
override fun convert(value: CardTypesResolver): Basic.ToppedUp? {
|
||||
val paramCardCurrency = ParamCardCurrencyConverter().convert(value) ?: return null
|
||||
|
||||
return Basic.ToppedUp(paramCardCurrency)
|
||||
}
|
||||
}
|
||||
|
|
@ -28,17 +28,21 @@ sealed class Basic(
|
|||
)
|
||||
|
||||
class SignedIn(
|
||||
state: AnalyticsParam.CardBalanceState,
|
||||
currency: AnalyticsParam.CardCurrency,
|
||||
batch: String,
|
||||
signInType: SignInType,
|
||||
) : Basic(
|
||||
event = "Signed in",
|
||||
params = mapOf(
|
||||
"State" to state.value,
|
||||
AnalyticsParam.Currency to currency.value,
|
||||
AnalyticsParam.Batch to batch,
|
||||
"Sign in type" to signInType.name,
|
||||
),
|
||||
)
|
||||
) {
|
||||
enum class SignInType {
|
||||
Card, Biometric
|
||||
}
|
||||
}
|
||||
|
||||
class ToppedUp(currency: AnalyticsParam.CardCurrency) : Basic(
|
||||
event = "Topped up",
|
||||
|
|
|
|||
|
|
@ -52,6 +52,11 @@ sealed class Token(
|
|||
params = mapOf("Token" to type.value),
|
||||
)
|
||||
|
||||
object ShowWalletAddress : Token(
|
||||
category = "Token",
|
||||
event = "Button - Show the Wallet Address"
|
||||
)
|
||||
|
||||
sealed class Receive(
|
||||
event: String,
|
||||
params: Map<String, String> = mapOf(),
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
package com.tangem.tap.common.analytics.filters
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventFilter
|
||||
import com.tangem.core.analytics.api.AnalyticsHandler
|
||||
import com.tangem.core.analytics.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class BasicSignInFilter : AnalyticsEventFilter {
|
||||
|
||||
private val alreadySignedIn = mutableSetOf<String>()
|
||||
|
||||
override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = event is Basic.SignedIn
|
||||
|
||||
override fun canBeSent(event: AnalyticsEvent): Boolean {
|
||||
val userWalletId = event.filterData as? String ?: return false
|
||||
|
||||
val canBeSent = !alreadySignedIn.contains(userWalletId)
|
||||
alreadySignedIn.add(userWalletId)
|
||||
|
||||
return canBeSent
|
||||
}
|
||||
|
||||
override fun canBeConsumedByHandler(handler: AnalyticsHandler, event: AnalyticsEvent): Boolean = true
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
package com.tangem.tap.common.analytics.filters
|
||||
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.core.analytics.api.AnalyticsEventFilter
|
||||
import com.tangem.core.analytics.api.AnalyticsHandler
|
||||
import com.tangem.core.analytics.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
import com.tangem.tap.persistence.ToppedUpWalletStorage
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class BasicTopUpFilter(
|
||||
private val topupWalletStorage: ToppedUpWalletStorage,
|
||||
) : AnalyticsEventFilter {
|
||||
|
||||
override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = event is Basic.ToppedUp
|
||||
|
||||
override fun canBeSent(event: AnalyticsEvent): Boolean {
|
||||
val data = event.filterData as? Data ?: return false
|
||||
|
||||
val walletInfo = topupWalletStorage.restore(data.walletId).guard {
|
||||
val newWalletInfo = Data(
|
||||
walletId = data.walletId,
|
||||
cardBalanceState = data.cardBalanceState,
|
||||
)
|
||||
topupWalletStorage.save(newWalletInfo)
|
||||
return false
|
||||
}
|
||||
|
||||
if (walletInfo.isToppedUp) return false
|
||||
|
||||
return if (!walletInfo.isToppedUp && data.isToppedUp) {
|
||||
topupWalletStorage.save(walletInfo.copy(cardBalanceState = AnalyticsParam.CardBalanceState.Full))
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun canBeConsumedByHandler(handler: AnalyticsHandler, event: AnalyticsEvent): Boolean = true
|
||||
|
||||
data class Data(
|
||||
val walletId: String,
|
||||
val cardBalanceState: AnalyticsParam.CardBalanceState,
|
||||
) {
|
||||
val isToppedUp: Boolean = cardBalanceState == AnalyticsParam.CardBalanceState.Full
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
package com.tangem.tap.common.analytics.topup
|
||||
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.util.UserWalletId
|
||||
import com.tangem.tap.common.analytics.converters.TopUpEventConverter
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.extensions.copy
|
||||
import com.tangem.tap.domain.model.UserWallet
|
||||
import com.tangem.tap.domain.model.WalletDataModel
|
||||
import com.tangem.tap.domain.model.WalletStoreModel
|
||||
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.domain.walletCurrencies.WalletCurrenciesManager
|
||||
import com.tangem.tap.domain.walletStores.WalletStoresManager
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.persistence.ToppedUpWalletStorage
|
||||
import com.tangem.tap.scope
|
||||
import kotlinx.coroutines.launch
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class TopUpController(
|
||||
var scanResponseProvider: (() -> ScanResponse?)? = null,
|
||||
var walletStoresManagerProvider: (() -> WalletStoresManager)? = null,
|
||||
private val topupWalletStorage: ToppedUpWalletStorage,
|
||||
) : WalletCurrenciesManager.Listener {
|
||||
|
||||
private var hadMissedDerivations: Boolean = false
|
||||
private val addedCurrencies = mutableListOf<Currency>()
|
||||
|
||||
override fun didUpdate(userWallet: UserWallet, currency: Currency) {
|
||||
tryToSend()
|
||||
}
|
||||
|
||||
override fun willCurrenciesAdd(userWallet: UserWallet, currenciesToAdd: List<Currency>) {
|
||||
addedCurrencies.addAll(currenciesToAdd.distinct())
|
||||
}
|
||||
|
||||
fun walletStoresChanged(walletStores: List<WalletStoreModel>) {
|
||||
val missedDerivations = walletStores
|
||||
.flatMap { it.walletsData }
|
||||
.map { it.status }
|
||||
.filterIsInstance<WalletDataModel.MissedDerivation>()
|
||||
|
||||
hadMissedDerivations = missedDerivations.isNotEmpty()
|
||||
}
|
||||
|
||||
fun scanToGetDerivations() {
|
||||
hadMissedDerivations = true
|
||||
}
|
||||
|
||||
fun addMissingDerivations(blockchains: List<BlockchainNetwork>) {
|
||||
hadMissedDerivations = blockchains.isNotEmpty()
|
||||
}
|
||||
|
||||
fun totalBalanceStateChanged(state: ProgressState) {
|
||||
if (state == ProgressState.Done) tryToSend()
|
||||
}
|
||||
|
||||
fun loadDataSuccess() {
|
||||
tryToSend()
|
||||
}
|
||||
|
||||
private fun tryToSend() {
|
||||
if (hadMissedDerivations) return
|
||||
val scanResponse = scanResponseProvider?.invoke() ?: return
|
||||
val userWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build() ?: return
|
||||
val walletStoresManager = walletStoresManagerProvider?.invoke() ?: return
|
||||
|
||||
scope.launch {
|
||||
val walletDataModels = walletStoresManager.getSync(userWalletId).flatMap { it.walletsData }
|
||||
if (walletDataModels.isEmpty()) return@launch
|
||||
|
||||
val isCorrectStatus = walletDataModels.any {
|
||||
it.status is WalletDataModel.Loading ||
|
||||
it.status is WalletDataModel.NoAccount ||
|
||||
it.status is WalletDataModel.Unreachable ||
|
||||
it.status is WalletDataModel.MissedDerivation ||
|
||||
it.status.isErrorStatus
|
||||
}
|
||||
if (isCorrectStatus) return@launch
|
||||
|
||||
val isToppedUpInPast = findToppedUpCurrenciesInPast(walletDataModels).isNotEmpty()
|
||||
if (isToppedUpInPast) {
|
||||
val newWalletInfo = ToppedUpWalletStorage.TopupInfo(
|
||||
walletId = userWalletId.stringValue,
|
||||
cardBalanceState = AnalyticsParam.CardBalanceState.Full,
|
||||
)
|
||||
topupWalletStorage.save(newWalletInfo)
|
||||
return@launch
|
||||
}
|
||||
|
||||
val cardBalanceState = BalanceCalculator(walletDataModels).calculate().toCardBalanceState()
|
||||
send(userWalletId, cardBalanceState, scanResponse.cardTypesResolver)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A UserWalletId registration should be after creating wallets
|
||||
*/
|
||||
fun registerEmptyWallet(scanResponse: ScanResponse) {
|
||||
UserWalletIdBuilder.scanResponse(scanResponse).build()?.let {
|
||||
topupWalletStorage.save(
|
||||
ToppedUpWalletStorage.TopupInfo(
|
||||
walletId = it.stringValue,
|
||||
cardBalanceState = AnalyticsParam.CardBalanceState.Empty,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun send(
|
||||
scanResponse: ScanResponse,
|
||||
cardBalanceState: AnalyticsParam.CardBalanceState,
|
||||
) {
|
||||
UserWalletIdBuilder.scanResponse(scanResponse).build()?.let {
|
||||
send(it, cardBalanceState, scanResponse.cardTypesResolver)
|
||||
}
|
||||
}
|
||||
|
||||
fun send(
|
||||
userWalletId: UserWalletId,
|
||||
cardBalanceState: AnalyticsParam.CardBalanceState,
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
) {
|
||||
val topupInfo = topupWalletStorage.restore(userWalletId.stringValue).guard {
|
||||
val topupInfo = ToppedUpWalletStorage.TopupInfo(
|
||||
walletId = userWalletId.stringValue,
|
||||
cardBalanceState = cardBalanceState,
|
||||
)
|
||||
topupWalletStorage.save(topupInfo)
|
||||
return
|
||||
}
|
||||
if (topupInfo.isToppedUp) return
|
||||
|
||||
if (!topupInfo.isToppedUp && cardBalanceState.isToppedUp()) {
|
||||
topupWalletStorage.save(topupInfo.copy(cardBalanceState = AnalyticsParam.CardBalanceState.Full))
|
||||
TopUpEventConverter().convert(cardTypesResolver)?.let {
|
||||
Analytics.send(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findToppedUpCurrenciesInPast(walletDataModels: List<WalletDataModel>): List<WalletDataModel> {
|
||||
val currenciesToppedUpInPast = addedCurrencies.copy()
|
||||
.mapNotNull { currency ->
|
||||
val foundCurrencyModel = walletDataModels
|
||||
.find { it.currency == currency }
|
||||
?: return@mapNotNull null
|
||||
|
||||
if (foundCurrencyModel.status.amount.isZero()) null else foundCurrencyModel
|
||||
}
|
||||
addedCurrencies.clear()
|
||||
|
||||
return currenciesToppedUpInPast
|
||||
}
|
||||
|
||||
private fun BigDecimal.toCardBalanceState(): AnalyticsParam.CardBalanceState = when {
|
||||
isZero() -> AnalyticsParam.CardBalanceState.Empty
|
||||
else -> AnalyticsParam.CardBalanceState.Full
|
||||
}
|
||||
|
||||
private fun AnalyticsParam.CardBalanceState.isToppedUp(): Boolean = this == AnalyticsParam.CardBalanceState.Full
|
||||
}
|
||||
|
||||
private interface IBalanceCalculator {
|
||||
fun calculate(): BigDecimal
|
||||
}
|
||||
|
||||
private class BalanceCalculator(
|
||||
private val walletDataModels: List<WalletDataModel>,
|
||||
) : IBalanceCalculator {
|
||||
|
||||
override fun calculate(): BigDecimal {
|
||||
val singleToken = walletDataModels
|
||||
.filter { it.currency.isToken() }
|
||||
.firstOrNull { it.isCardSingleToken }
|
||||
|
||||
val totalAmount = singleToken?.status?.amount
|
||||
?: walletDataModels.calculateTotalCryptoAmount()
|
||||
|
||||
return totalAmount
|
||||
}
|
||||
|
||||
private fun List<WalletDataModel>.calculateTotalCryptoAmount(): BigDecimal = this
|
||||
.map { it.status.amount }
|
||||
.reduce(BigDecimal::plus)
|
||||
}
|
||||
|
|
@ -28,4 +28,8 @@ fun <T> MutableList<T>.replaceBy(item: T, predicate: (T) -> Boolean): Boolean {
|
|||
|
||||
fun <T> MutableList<T>.replaceByOrAdd(item: T, predicate: (T) -> Boolean) {
|
||||
if (!replaceBy(item, predicate)) add(item)
|
||||
}
|
||||
|
||||
fun <T> MutableList<T>.copy(): MutableList<T> {
|
||||
return this.map { it }.toMutableList()
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWallet
|
|||
import com.tangem.tap.features.saveWallet.redux.SaveWalletReducer
|
||||
import com.tangem.tap.features.send.redux.reducers.SendScreenReducer
|
||||
import com.tangem.tap.features.shop.redux.ShopReducer
|
||||
import com.tangem.tap.features.signin.redux.SignInReducer
|
||||
import com.tangem.tap.features.sprinklr.redux.SprinklrReducer
|
||||
import com.tangem.tap.features.tokens.redux.TokensReducer
|
||||
import com.tangem.tap.features.wallet.redux.reducers.WalletReducer
|
||||
|
|
@ -45,6 +46,7 @@ fun appReducer(action: Action, state: AppState?, appStateHolder: AppStateHolder)
|
|||
saveWalletState = SaveWalletReducer.reduce(action, state),
|
||||
walletSelectorState = WalletSelectorReducer.reduce(action, state),
|
||||
sprinklrState = SprinklrReducer.reduce(action, state),
|
||||
signInState = SignInReducer.reduce(action, state),
|
||||
daggerGraphState = DaggerGraphReducer.reduce(action, state),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ import com.tangem.tap.features.send.redux.middlewares.SendMiddleware
|
|||
import com.tangem.tap.features.send.redux.states.SendState
|
||||
import com.tangem.tap.features.shop.redux.ShopMiddleware
|
||||
import com.tangem.tap.features.shop.redux.ShopState
|
||||
import com.tangem.tap.features.signin.redux.SignInMiddleware
|
||||
import com.tangem.tap.features.signin.redux.SignInState
|
||||
import com.tangem.tap.features.sprinklr.redux.SprinklrMiddleware
|
||||
import com.tangem.tap.features.sprinklr.redux.SprinklrState
|
||||
import com.tangem.tap.features.tokens.redux.TokensMiddleware
|
||||
|
|
@ -66,6 +68,7 @@ data class AppState(
|
|||
val saveWalletState: SaveWalletState = SaveWalletState(),
|
||||
val walletSelectorState: WalletSelectorState = WalletSelectorState(),
|
||||
val sprinklrState: SprinklrState = SprinklrState(),
|
||||
val signInState: SignInState = SignInState(),
|
||||
val daggerGraphState: DaggerGraphState = DaggerGraphState(),
|
||||
) : StateType {
|
||||
|
||||
|
|
@ -108,6 +111,7 @@ data class AppState(
|
|||
LockUserWalletsTimerMiddleware().middleware,
|
||||
AccessCodeRequestPolicyMiddleware().middleware,
|
||||
SprinklrMiddleware().middleware,
|
||||
SignInMiddleware.middleware,
|
||||
DaggerGraphMiddleware.daggerGraphMiddleware,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.tangem.common.core.TangemError
|
|||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.datasource.config.models.ChatConfig
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.analytics.topup.TopUpController
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.feedback.FeedbackData
|
||||
import com.tangem.tap.common.feedback.FeedbackManager
|
||||
|
|
@ -88,6 +89,7 @@ sealed class GlobalAction : Action {
|
|||
data class SetConfigManager(val configManager: ConfigManager) : GlobalAction()
|
||||
data class SetWarningManager(val warningManager: WarningMessagesManager) : GlobalAction()
|
||||
data class SetFeedbackManager(val feedbackManager: FeedbackManager) : GlobalAction()
|
||||
data class SetTopUpController(val topUpController: TopUpController) : GlobalAction()
|
||||
|
||||
data class SendEmail(val feedbackData: FeedbackData) : GlobalAction()
|
||||
data class OpenChat(val feedbackData: FeedbackData, val chatConfig: ChatConfig? = null) : GlobalAction()
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import com.tangem.tap.scope
|
|||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.tap.userTokensRepository
|
||||
import com.tangem.tap.walletCurrenciesManager
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
|
|
@ -194,6 +195,9 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di
|
|||
}
|
||||
}
|
||||
}
|
||||
is GlobalAction.SetTopUpController -> {
|
||||
walletCurrenciesManager.addListener(action.topUpController)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ fun globalReducer(action: Action, state: AppState, appStateHolder: AppStateHolde
|
|||
globalState.copy(configManager = action.configManager)
|
||||
}
|
||||
is GlobalAction.SetWarningManager -> globalState.copy(warningManager = action.warningManager)
|
||||
is GlobalAction.SetTopUpController -> globalState.copy(topUpController = action.topUpController)
|
||||
is GlobalAction.UpdateWalletSignedHashes -> {
|
||||
val card = globalState.scanResponse?.card ?: return globalState
|
||||
val wallet = card.wallets
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.common.redux.global
|
|||
|
||||
import com.tangem.datasource.config.ConfigManager
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.analytics.topup.TopUpController
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.feedback.FeedbackManager
|
||||
import com.tangem.tap.common.redux.StateDialog
|
||||
|
|
@ -28,6 +29,7 @@ data class GlobalState(
|
|||
val exchangeManager: CurrencyExchangeManager = CurrencyExchangeManager.dummy(),
|
||||
val userCountryCode: String? = null,
|
||||
val userWalletsListManager: UserWalletsListManager? = null,
|
||||
val topUpController: TopUpController? = null,
|
||||
) : StateType
|
||||
|
||||
typealias CryptoCurrencyName = String
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import com.tangem.tap.features.disclaimer.createDisclaimer
|
|||
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.middlewares.handleBasicAnalyticsEvent
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
|
|
@ -71,7 +70,7 @@ class TapWalletManager {
|
|||
.doOnSuccess {
|
||||
Timber.d("Wallet stores fetched for ${userWallet.walletId}")
|
||||
store.dispatchOnMain(WalletAction.LoadData.Success)
|
||||
handleBasicAnalyticsEvent()
|
||||
store.state.globalState.topUpController?.loadDataSuccess()
|
||||
}
|
||||
.doOnFailure { error ->
|
||||
val errorAction = when (error) {
|
||||
|
|
|
|||
|
|
@ -56,7 +56,11 @@ private class BlockchainNetworkWalletStoreBuilderImpl(
|
|||
override fun build(): WalletStoreModel {
|
||||
val cardDerivationStyle = userWallet.scanResponse.card.derivationStyle
|
||||
val blockchainWalletData = blockchainNetwork.getBlockchainWalletData(walletManager, cardDerivationStyle)
|
||||
val tokensWalletsData = blockchainNetwork.getTokensWalletsData(walletManager, cardDerivationStyle)
|
||||
val tokensWalletsData = blockchainNetwork.getTokensWalletsData(
|
||||
walletManager = walletManager,
|
||||
cardDerivationStyle = cardDerivationStyle,
|
||||
primaryToken = userWallet.scanResponse.cardTypesResolver.getPrimaryToken(),
|
||||
)
|
||||
|
||||
return WalletStoreModel(
|
||||
userWalletId = userWallet.walletId,
|
||||
|
|
@ -78,7 +82,10 @@ private class WalletMangerWalletStoreBuilderImpl(
|
|||
override fun build(): WalletStoreModel {
|
||||
val wallet = walletManager.wallet
|
||||
val blockchainWalletData = wallet.blockchain.toBlockchainWalletData(walletManager)
|
||||
val tokenWalletsData = wallet.getTokens().firstOrNull()?.toTokenWalletData(walletManager)
|
||||
val tokenWalletsData = wallet.getTokens().firstOrNull()?.toTokenWalletData(
|
||||
walletManager = walletManager,
|
||||
primaryToken = userWallet.scanResponse.cardTypesResolver.getPrimaryToken(),
|
||||
)
|
||||
|
||||
return WalletStoreModel(
|
||||
userWalletId = userWallet.walletId,
|
||||
|
|
@ -115,6 +122,7 @@ private fun BlockchainNetwork.getBlockchainWalletData(
|
|||
private fun BlockchainNetwork.getTokensWalletsData(
|
||||
walletManager: WalletManager?,
|
||||
cardDerivationStyle: DerivationStyle?,
|
||||
primaryToken: Token?,
|
||||
): List<WalletDataModel> {
|
||||
return this.tokens
|
||||
.map { token ->
|
||||
|
|
@ -129,7 +137,7 @@ private fun BlockchainNetwork.getTokensWalletsData(
|
|||
walletAddresses = walletManager?.wallet?.createAddressesData().orEmpty(),
|
||||
existentialDeposit = getExistentialDeposit(walletManager),
|
||||
fiatRate = null,
|
||||
isCardSingleToken = walletManager?.cardTokens?.contains(token) ?: false,
|
||||
isCardSingleToken = token == primaryToken,
|
||||
isCustom = currency.isCustomCurrency(cardDerivationStyle),
|
||||
historyTransactions = walletManager?.getTokenTxHistory(token),
|
||||
)
|
||||
|
|
@ -153,7 +161,10 @@ private fun Blockchain.toBlockchainWalletData(walletManager: WalletManager): Wal
|
|||
)
|
||||
}
|
||||
|
||||
private fun Token.toTokenWalletData(walletManager: WalletManager): WalletDataModel {
|
||||
private fun Token.toTokenWalletData(
|
||||
walletManager: WalletManager,
|
||||
primaryToken: Token?,
|
||||
): WalletDataModel {
|
||||
val wallet = walletManager.wallet
|
||||
return WalletDataModel(
|
||||
currency = Currency.Token(
|
||||
|
|
@ -165,7 +176,7 @@ private fun Token.toTokenWalletData(walletManager: WalletManager): WalletDataMod
|
|||
walletAddresses = wallet.createAddressesData(),
|
||||
existentialDeposit = getExistentialDeposit(walletManager),
|
||||
fiatRate = null,
|
||||
isCardSingleToken = walletManager.cardTokens.contains(this),
|
||||
isCardSingleToken = this == primaryToken,
|
||||
isCustom = false,
|
||||
historyTransactions = walletManager.getTokenTxHistory(this),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -69,6 +69,31 @@ interface WalletCurrenciesManager {
|
|||
currenciesToRemove: List<Currency>,
|
||||
): CompletionResult<Unit>
|
||||
|
||||
/**
|
||||
* Add a callback [Listener]
|
||||
*
|
||||
* @param listener The callback that will add
|
||||
*/
|
||||
fun addListener(listener: Listener)
|
||||
|
||||
/**
|
||||
* Remove a callback [Listener]
|
||||
*
|
||||
* @param listener The callback that will removed
|
||||
*/
|
||||
fun removeListener(listener: Listener)
|
||||
|
||||
/**
|
||||
* Interface definition for a callbacks
|
||||
*/
|
||||
interface Listener {
|
||||
fun willUpdate(userWallet: UserWallet, currency: Currency) {}
|
||||
fun didUpdate(userWallet: UserWallet, currency: Currency) {}
|
||||
fun willCurrenciesAdd(userWallet: UserWallet, currenciesToAdd: List<Currency>) {}
|
||||
fun willCurrenciesRemove(userWallet: UserWallet, currenciesToRemove: List<Currency>) {}
|
||||
fun willCurrencyRemove(userWallet: UserWallet, currencyToRemove: Currency) {}
|
||||
}
|
||||
|
||||
// For provider
|
||||
companion object
|
||||
}
|
||||
|
|
@ -30,23 +30,31 @@ internal class DefaultWalletCurrenciesManager(
|
|||
private val walletManagersRepository: WalletManagersRepository,
|
||||
private val appCurrencyProvider: () -> FiatCurrency,
|
||||
) : WalletCurrenciesManager {
|
||||
|
||||
private val listeners = mutableListOf<WalletCurrenciesManager.Listener>()
|
||||
|
||||
override suspend fun update(
|
||||
userWallet: UserWallet,
|
||||
currency: Currency,
|
||||
): CompletionResult<Unit> = withContext(Dispatchers.Default) {
|
||||
listeners.forEach { it.willUpdate(userWallet, currency) }
|
||||
val walletStore = walletStoresRepository.getSync(userWallet.walletId)
|
||||
.find {
|
||||
it.blockchain == currency.blockchain &&
|
||||
it.derivationPath?.rawPath == currency.derivationPath
|
||||
}
|
||||
|
||||
if (walletStore != null) {
|
||||
val updateResult = if (walletStore == null) {
|
||||
CompletionResult.Success(Unit)
|
||||
} else {
|
||||
walletAmountsRepository.updateAmountsForWalletStore(
|
||||
walletStore = walletStore,
|
||||
userWallet = userWallet,
|
||||
fiatCurrency = appCurrencyProvider(),
|
||||
)
|
||||
} else CompletionResult.Success(Unit)
|
||||
}
|
||||
listeners.forEach { it.didUpdate(userWallet, currency) }
|
||||
updateResult
|
||||
}
|
||||
|
||||
override suspend fun addCurrencies(
|
||||
|
|
@ -55,6 +63,7 @@ internal class DefaultWalletCurrenciesManager(
|
|||
): CompletionResult<Unit> = withContext(Dispatchers.Default) {
|
||||
val card = userWallet.scanResponse.card
|
||||
val currenciesToAddWithMissingBlockchains = currenciesToAdd.addMissingBlockchainsIfNeeded(card)
|
||||
listeners.forEach { it.willCurrenciesAdd(userWallet, currenciesToAddWithMissingBlockchains) }
|
||||
|
||||
updateWalletStores(
|
||||
userWallet = userWallet,
|
||||
|
|
@ -77,6 +86,7 @@ internal class DefaultWalletCurrenciesManager(
|
|||
userWallet: UserWallet,
|
||||
currenciesToRemove: List<Currency>,
|
||||
): CompletionResult<Unit> = withContext(Dispatchers.Default) {
|
||||
listeners.forEach { it.willCurrenciesRemove(userWallet, currenciesToRemove) }
|
||||
val card = userWallet.scanResponse.card
|
||||
val remainingCurrencies = getSavedCurrencies(userWallet.walletId)
|
||||
.filter { it !in currenciesToRemove }
|
||||
|
|
@ -96,9 +106,18 @@ internal class DefaultWalletCurrenciesManager(
|
|||
userWallet: UserWallet,
|
||||
currencyToRemove: Currency,
|
||||
): CompletionResult<Unit> {
|
||||
listeners.forEach { it.willCurrencyRemove(userWallet, currencyToRemove) }
|
||||
return removeCurrencies(userWallet, listOf(currencyToRemove))
|
||||
}
|
||||
|
||||
override fun addListener(listener: WalletCurrenciesManager.Listener) {
|
||||
listeners.add(listener)
|
||||
}
|
||||
|
||||
override fun removeListener(listener: WalletCurrenciesManager.Listener) {
|
||||
listeners.remove(listener)
|
||||
}
|
||||
|
||||
private suspend fun getSavedCurrencies(userWalletId: UserWalletId): List<Currency> {
|
||||
return withContext(Dispatchers.Default) {
|
||||
walletStoresRepository.getSync(userWalletId)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class WalletConnectRepository(val context: Application) {
|
|||
.hexToUtf8()
|
||||
walletConnectAdapter.fromJson(json)!!.map { it.toSession() }
|
||||
} catch (exception: Exception) {
|
||||
Timber.e(exception)
|
||||
Timber.w(exception)
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.common.doOnSuccess
|
|||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.AnalyticsEvent
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
import com.tangem.tap.common.analytics.events.IntroductionProcess
|
||||
import com.tangem.tap.common.analytics.events.Shop
|
||||
import com.tangem.tap.common.entities.IndeterminateProgressButton
|
||||
|
|
@ -23,6 +24,7 @@ import com.tangem.tap.features.home.BELARUS_COUNTRY_CODE
|
|||
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
|
||||
import com.tangem.tap.features.home.redux.HomeMiddleware.BUY_WALLET_URL
|
||||
import com.tangem.tap.features.send.redux.states.ButtonState
|
||||
import com.tangem.tap.features.signin.redux.SignInAction
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -112,9 +114,10 @@ fun proceedWithScanResponse(scanResponse: ScanResponse) {
|
|||
Timber.e(error, "Unable to save user wallet")
|
||||
}
|
||||
.doOnSuccess {
|
||||
scope.launch { store.onUserWalletSelected(userWallet) }
|
||||
scope.launch { store.onUserWalletSelected(userWallet = userWallet) }
|
||||
}
|
||||
.doOnResult {
|
||||
store.dispatchOnMain(SignInAction.SetSignInType(Basic.SignedIn.SignInType.Card))
|
||||
navigateTo(AppScreen.Wallet)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch
|
|||
onboardingManager.activationFinished(card.cardId)
|
||||
postUi(DELAY_SDK_DIALOG_CLOSE) { store.dispatch(OnboardingNoteAction.Confetti.Show) }
|
||||
}
|
||||
else -> {}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
is OnboardingNoteAction.CreateWallet -> {
|
||||
|
|
@ -110,11 +110,10 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch
|
|||
val updatedResponse = scanResponse.copy(card = result.data.card)
|
||||
onboardingManager.scanResponse = updatedResponse
|
||||
onboardingManager.activationStarted(updatedResponse.card.cardId)
|
||||
store.state.globalState.topUpController?.registerEmptyWallet(updatedResponse)
|
||||
store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.TopUpWallet))
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
// do nothing
|
||||
}
|
||||
is CompletionResult.Failure -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -159,6 +158,7 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch
|
|||
}
|
||||
is OnboardingNoteAction.Balance.Set -> {
|
||||
if (action.balance.balanceIsToppedUp()) {
|
||||
store.state.globalState.topUpController?.send(scanResponse, AnalyticsParam.CardBalanceState.Full)
|
||||
store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.Done))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ private fun handleOtherCardsAction(action: Action) {
|
|||
val updatedCard = updatedResponse.card
|
||||
onboardingManager.scanResponse = updatedResponse
|
||||
onboardingManager.activationStarted(updatedCard.cardId)
|
||||
store.state.globalState.topUpController?.registerEmptyWallet(updatedResponse)
|
||||
|
||||
val primaryBlockchain = updatedResponse.cardTypesResolver.getBlockchain()
|
||||
val blockchainNetworks = if (primaryBlockchain != Blockchain.Unknown) {
|
||||
|
|
|
|||
|
|
@ -205,6 +205,8 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
is Result.Success -> {
|
||||
Analytics.send(Onboarding.Twins.SetupFinished())
|
||||
updateScanResponse(result.data)
|
||||
store.state.globalState.topUpController?.registerEmptyWallet(result.data)
|
||||
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
withMainContext {
|
||||
when (twinCardsState.mode) {
|
||||
|
|
@ -261,9 +263,8 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
}
|
||||
is TwinCardsAction.Balance.Set -> {
|
||||
if (action.balance.balanceIsToppedUp()) {
|
||||
scope.launch {
|
||||
withMainContext { store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Done)) }
|
||||
}
|
||||
store.state.globalState.topUpController?.send(getScanResponse(), AnalyticsParam.CardBalanceState.Full)
|
||||
store.dispatchOnMain(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Done))
|
||||
}
|
||||
}
|
||||
is TwinCardsAction.ShowAddressInfoDialog -> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.tap.features.onboarding.products.wallet.redux
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import org.rekotlin.Action
|
||||
|
||||
|
|
@ -22,10 +21,9 @@ sealed class OnboardingWalletAction : Action {
|
|||
|
||||
sealed class BackupAction : Action {
|
||||
|
||||
object DetermineBackupStep : BackupAction()
|
||||
object IntroduceBackup : BackupAction()
|
||||
object StartBackup : BackupAction()
|
||||
object DismissBackup : BackupAction()
|
||||
object SkipBackup : BackupAction()
|
||||
|
||||
object StartAddingPrimaryCard : BackupAction()
|
||||
object ScanPrimaryCard : BackupAction()
|
||||
|
|
@ -41,13 +39,6 @@ sealed class BackupAction : Action {
|
|||
object Success : BackupAction()
|
||||
}
|
||||
|
||||
data class LoadBackupCardArtwork(
|
||||
val cardId: CardId,
|
||||
val cardPublicKey: ByteArray,
|
||||
) : BackupAction() {
|
||||
data class Success(val cardId: CardId, val artwork: Bitmap)
|
||||
}
|
||||
|
||||
object FinishAddingBackupCards : BackupAction()
|
||||
|
||||
object ShowAccessCodeInfoScreen : BackupAction()
|
||||
|
|
@ -55,7 +46,6 @@ sealed class BackupAction : Action {
|
|||
data class CheckAccessCode(val accessCode: String) : BackupAction()
|
||||
data class SetAccessCodeError(val error: AccessCodeError?) : BackupAction()
|
||||
data class SaveFirstAccessCode(val accessCode: String) : BackupAction()
|
||||
object ShowReenterAccessCodeScreen : BackupAction()
|
||||
data class SaveAccessCodeConfirmation(val accessCodeConfirmation: String) : BackupAction()
|
||||
object OnAccessCodeDialogClosed : BackupAction()
|
||||
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ private fun handleWalletAction(action: Action, state: () -> AppState?, dispatch:
|
|||
primaryCard = result.data.primaryCard,
|
||||
)
|
||||
onboardingManager.scanResponse = updatedResponse
|
||||
store.state.globalState.topUpController?.registerEmptyWallet(updatedResponse)
|
||||
|
||||
val blockchainNetworks = if (DemoHelper.isDemoCardId(result.data.card.cardId)) {
|
||||
DemoHelper.config.demoBlockchains
|
||||
|
|
@ -145,9 +146,7 @@ private fun handleWalletAction(action: Action, state: () -> AppState?, dispatch:
|
|||
startCardActivation(updatedResponse)
|
||||
store.dispatch(OnboardingWalletAction.ResumeBackup)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
// do nothing
|
||||
}
|
||||
is CompletionResult.Failure -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -190,7 +189,7 @@ private fun handleWalletAction(action: Action, state: () -> AppState?, dispatch:
|
|||
newAction?.let { store.dispatch(it) }
|
||||
}
|
||||
OnboardingWalletAction.OnBackPressed -> handleOnBackPressed(onboardingWalletState)
|
||||
else -> {}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -249,8 +248,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
|
|||
is CompletionResult.Success -> {
|
||||
store.dispatchOnMain(BackupAction.StartAddingBackupCards)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
}
|
||||
is CompletionResult.Failure -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -267,8 +265,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
|
|||
is CompletionResult.Success -> {
|
||||
store.dispatchOnMain(BackupAction.AddBackupCard.Success)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
}
|
||||
is CompletionResult.Failure -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -308,8 +305,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
|
|||
is CompletionResult.Success -> {
|
||||
store.dispatchOnMain(BackupAction.PrepareToWriteBackupCard(1))
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
}
|
||||
is CompletionResult.Failure -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -324,20 +320,10 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
|
|||
store.dispatchOnMain(BackupAction.PrepareToWriteBackupCard(action.cardNumber + 1))
|
||||
}
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
}
|
||||
is CompletionResult.Failure -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
is BackupAction.FinishBackup -> {
|
||||
if (action.withAnalytics) {
|
||||
Analytics.send(Onboarding.Backup.Finished(backupState.backupCardsNumber))
|
||||
}
|
||||
if (!onboardingWalletState.isSaltPay) {
|
||||
Analytics.send(Onboarding.Finished())
|
||||
finishCardActivation(backupState, card)
|
||||
}
|
||||
}
|
||||
is BackupAction.DiscardBackup -> {
|
||||
backupService.discardSavedBackup()
|
||||
}
|
||||
|
|
@ -362,15 +348,36 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
|
|||
)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingWallet))
|
||||
}
|
||||
is BackupAction.DismissBackup -> {
|
||||
is BackupAction.SkipBackup -> {
|
||||
Analytics.send(Onboarding.Backup.Skipped())
|
||||
if (onboardingWalletState.isSaltPay) throw UnsupportedOperationException()
|
||||
store.dispatch(BackupAction.FinishBackup())
|
||||
if (onboardingWalletState.isSaltPay) return
|
||||
|
||||
Analytics.send(Onboarding.Finished())
|
||||
finishCardActivation(gatherCardIds(backupState, card))
|
||||
}
|
||||
else -> {}
|
||||
is BackupAction.FinishBackup -> {
|
||||
if (action.withAnalytics) {
|
||||
Analytics.send(Onboarding.Backup.Finished(backupState.backupCardsNumber))
|
||||
}
|
||||
if (onboardingWalletState.isSaltPay) return
|
||||
|
||||
val notActivatedCardIds = gatherCardIds(backupState, card)
|
||||
.mapNotNull { if (cardActivationIsFinished(it)) null else it }
|
||||
|
||||
// All cardIds may already be activated if the backup was skipped before.
|
||||
if (notActivatedCardIds.isEmpty()) return
|
||||
|
||||
Analytics.send(Onboarding.Finished())
|
||||
finishCardActivation(notActivatedCardIds)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun cardActivationIsFinished(cardId: String): Boolean {
|
||||
return preferencesStorage.usedCardsPrefStorage.isActivationFinished(cardId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard Wallet cards start activation at OnboardingWalletAction.CreateWallet
|
||||
* SaltPay cards start activation at OnboardingWalletAction.StartSaltPayCardActivation
|
||||
|
|
@ -380,16 +387,19 @@ private fun startCardActivation(scanResponse: ScanResponse) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Standard Wallet cards finish activation at BackupAction.FinishBackup
|
||||
* Standard Wallet cards finish activation at BackupAction.SkipBackup and BackupAction.FinishBackup
|
||||
* SaltPay cards finish activation at OnboardingWalletAction.FinishOnboarding
|
||||
*/
|
||||
internal fun finishCardActivation(backupState: BackupState, card: CardDTO?) {
|
||||
(listOf(backupState.primaryCardId, card?.cardId) + backupState.backupCardIds)
|
||||
.distinct()
|
||||
internal fun finishCardActivation(cardIds: List<String>) {
|
||||
cardIds.forEach { cardId ->
|
||||
preferencesStorage.usedCardsPrefStorage.activationFinished(cardId)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun gatherCardIds(backupState: BackupState, card: CardDTO?): List<String> {
|
||||
return (listOf(backupState.primaryCardId, card?.cardId) + backupState.backupCardIds)
|
||||
.filterNotNull()
|
||||
.forEach { cardId ->
|
||||
preferencesStorage.usedCardsPrefStorage.activationFinished(cardId)
|
||||
}
|
||||
.distinct()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -415,7 +425,8 @@ private fun initSaltPayOnBackupFinishedIfNeeded(
|
|||
card = scanResponse.card,
|
||||
).create()
|
||||
store.dispatchOnMain(OnboardingSaltPayAction.SetDependencies(manager))
|
||||
store.dispatchOnMain(OnboardingSaltPayAction.Update(false))
|
||||
store.dispatchOnMain(OnboardingSaltPayAction.Update(withAnalytics = false))
|
||||
store.dispatchOnMain(OnboardingSaltPayAction.OnSwitchedToSaltPayProcess)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ private object BackupReducer {
|
|||
} else {
|
||||
state.copy(backupStep = BackupStep.WriteBackupCard(action.cardNumber))
|
||||
}
|
||||
is BackupAction.SkipBackup -> state.copy(backupStep = BackupStep.Finished)
|
||||
is BackupAction.FinishBackup -> state.copy(backupStep = BackupStep.Finished)
|
||||
BackupAction.OnAccessCodeDialogClosed -> state.copy(backupStep = BackupStep.AddBackupCards)
|
||||
BackupAction.DiscardBackup -> BackupState()
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import com.tangem.tap.features.demo.DemoHelper
|
|||
import com.tangem.tap.features.onboarding.OnboardingManager
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.finishCardActivation
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.gatherCardIds
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.AllSymbolsTheSameFilter
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.SaltPayActivationManager
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.SaltPayExceptionHandler
|
||||
|
|
@ -79,7 +80,7 @@ private fun handleOnboardingSaltPayAction(anyAction: Action, appState: () -> App
|
|||
}
|
||||
}
|
||||
is OnboardingSaltPayAction.OnSwitchedToSaltPayProcess -> {
|
||||
sendAnalyticsScreenOpened(getState().step, newStep = SaltPayActivationStep.None)
|
||||
sendAnalyticsScreenOpened(SaltPayActivationStep.None, newStep = getState().step)
|
||||
}
|
||||
is OnboardingSaltPayAction.Update -> {
|
||||
handleInProgress = true
|
||||
|
|
@ -279,10 +280,8 @@ private fun handleOnboardingSaltPayAction(anyAction: Action, appState: () -> App
|
|||
store.dispatchDebugErrorNotification("OnboardingManager can't be NULL")
|
||||
return
|
||||
}
|
||||
finishCardActivation(
|
||||
getOnboardingWalletState().backupState,
|
||||
onboardingManager.scanResponse.card,
|
||||
)
|
||||
val cardIds = gatherCardIds(getOnboardingWalletState().backupState, onboardingManager.scanResponse.card)
|
||||
finishCardActivation(cardIds)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ class OnboardingWalletFragment : BaseFragment(R.layout.fragment_onboarding_walle
|
|||
btnWalletMainAction.setOnClickListener { store.dispatch(BackupAction.StartBackup) }
|
||||
|
||||
btnWalletAlternativeAction.text = getText(R.string.onboarding_button_skip_backup)
|
||||
btnWalletAlternativeAction.setOnClickListener { store.dispatch(BackupAction.DismissBackup) }
|
||||
btnWalletAlternativeAction.setOnClickListener { store.dispatch(BackupAction.SkipBackup) }
|
||||
btnWalletAlternativeAction.show(state.canSkipBackup && !isSaltPay)
|
||||
}
|
||||
animator.showBackupIntro(state)
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ internal class SaveWalletMiddleware {
|
|||
.build()
|
||||
?: return@launch
|
||||
|
||||
provideBiometricUserWalletsListManager()
|
||||
provideLockableUserWalletsListManagerIfNot()
|
||||
|
||||
val isFirstSavedWallet = !userWalletsListManager.hasUserWallets
|
||||
|
||||
|
|
@ -113,8 +113,12 @@ internal class SaveWalletMiddleware {
|
|||
.doOnSuccess {
|
||||
preferencesStorage.shouldSaveUserWallets = true
|
||||
// Enable saving access codes only if this is the first time user save the wallet
|
||||
preferencesStorage.shouldSaveAccessCodes = isFirstSavedWallet ||
|
||||
preferencesStorage.shouldSaveAccessCodes
|
||||
if (isFirstSavedWallet) {
|
||||
preferencesStorage.shouldSaveAccessCodes = true
|
||||
tangemSdkManager.setAccessCodeRequestPolicy(
|
||||
useBiometricsForAccessCode = userWallet.hasAccessCode,
|
||||
)
|
||||
}
|
||||
|
||||
store.dispatchWithMain(SaveWalletAction.Save.Success)
|
||||
store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Wallet))
|
||||
|
|
@ -123,7 +127,7 @@ internal class SaveWalletMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun provideBiometricUserWalletsListManager() {
|
||||
private suspend fun provideLockableUserWalletsListManagerIfNot() {
|
||||
if (store.state.globalState.userWalletsListManager?.isLockable == true) return
|
||||
|
||||
val context = foregroundActivityObserver.foregroundActivity?.applicationContext.guard {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.tap.features.signin.redux
|
||||
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
import org.rekotlin.Action
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
sealed interface SignInAction : Action {
|
||||
|
||||
data class SetSignInType(val type: Basic.SignedIn.SignInType) : SignInAction
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.tap.features.signin.redux
|
||||
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
object SignInMiddleware {
|
||||
val middleware: Middleware<AppState> = { _, _ ->
|
||||
{ next ->
|
||||
{ action -> next(action) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.tap.features.signin.redux
|
||||
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import org.rekotlin.Action
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
object SignInReducer {
|
||||
fun reduce(action: Action, state: AppState): SignInState {
|
||||
if (action !is SignInAction) return state.signInState
|
||||
|
||||
return when (action) {
|
||||
is SignInAction.SetSignInType -> state.signInState.copy(type = action.type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.tap.features.signin.redux
|
||||
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class SignInState(val type: Basic.SignedIn.SignInType? = null)
|
||||
|
|
@ -19,9 +19,6 @@ import com.tangem.tap.store
|
|||
import org.rekotlin.StateType
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
|
||||
/**
|
||||
* @property derivationsCheckIsScheduled - used only for analytics
|
||||
*/
|
||||
data class WalletState(
|
||||
val cardId: String = "",
|
||||
val state: ProgressState = ProgressState.Done,
|
||||
|
|
@ -37,7 +34,6 @@ data class WalletState(
|
|||
val totalBalance: TotalBalance? = null,
|
||||
val showBackupWarning: Boolean = false,
|
||||
val missingDerivations: List<BlockchainNetwork> = emptyList(),
|
||||
val derivationsCheckIsScheduled: Boolean = false,
|
||||
val loadingUserTokens: Boolean = false,
|
||||
val walletCardsCount: Int? = null,
|
||||
val canSaveUserWallets: Boolean = false,
|
||||
|
|
|
|||
|
|
@ -91,13 +91,14 @@ class MultiWalletMiddleware {
|
|||
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingWallet))
|
||||
}
|
||||
is WalletAction.MultiWallet.AddMissingDerivations -> {
|
||||
scope.launch { handleBasicAnalyticsEvent() }
|
||||
store.state.globalState.topUpController?.addMissingDerivations(action.blockchains)
|
||||
}
|
||||
is WalletAction.MultiWallet.ScanToGetDerivations -> {
|
||||
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard {
|
||||
Timber.e("Unable to scan to get derivations, no user wallet selected")
|
||||
return
|
||||
}
|
||||
store.state.globalState.topUpController?.scanToGetDerivations()
|
||||
scanAndUpdateCard(selectedUserWallet, walletState)
|
||||
}
|
||||
else -> {}
|
||||
|
|
@ -108,7 +109,6 @@ class MultiWalletMiddleware {
|
|||
selectedUserWallet: UserWallet,
|
||||
state: WalletState?,
|
||||
) = scope.launch(Dispatchers.Default) {
|
||||
dispatchOnMain(WalletAction.MultiWallet.ScheduleCheckForMissingDerivation)
|
||||
tangemSdkManager.scanProduct(
|
||||
cardId = selectedUserWallet.cardId,
|
||||
userTokensRepository = userTokensRepository,
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ import com.tangem.common.CompletionResult
|
|||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||
import com.tangem.tap.common.analytics.converters.BasicEventsPreChecker
|
||||
import com.tangem.tap.common.analytics.converters.BasicEventsSourceData
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
import com.tangem.tap.common.analytics.events.MainScreen
|
||||
|
|
@ -26,7 +24,6 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
|
|||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.model.WalletDataModel
|
||||
import com.tangem.tap.domain.model.WalletStoreModel
|
||||
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
|
||||
import com.tangem.tap.domain.userWalletList.lockIfLockable
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.home.redux.HomeAction
|
||||
|
|
@ -45,7 +42,6 @@ import com.tangem.tap.store
|
|||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.tap.totalFiatBalanceCalculator
|
||||
import com.tangem.tap.userWalletsListManager
|
||||
import com.tangem.tap.walletStoresManager
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -180,7 +176,7 @@ class WalletMiddleware {
|
|||
}
|
||||
is WalletAction.UserWalletChanged -> Unit
|
||||
is WalletAction.WalletStoresChanged -> {
|
||||
store.dispatchOnMain(WalletAction.MultiWallet.ScheduleCheckForMissingDerivation)
|
||||
store.state.globalState.topUpController?.walletStoresChanged(action.walletStores)
|
||||
updateWalletStores(action.walletStores, walletState)
|
||||
fetchTotalFiatBalance(action.walletStores)
|
||||
findMissedDerivations(action.walletStores)
|
||||
|
|
@ -343,19 +339,4 @@ class WalletMiddleware {
|
|||
tokenRate = tokenRate,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun handleBasicAnalyticsEvent() {
|
||||
val scanResponse = store.state.globalState.scanResponse ?: return
|
||||
|
||||
val biometricsWalletDataModels = if (preferencesStorage.shouldSaveUserWallets) {
|
||||
UserWalletIdBuilder.scanResponse(scanResponse).build()?.let { userWalletId ->
|
||||
walletStoresManager.getSync(userWalletId).map { it.walletsData }.flatten()
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val converterData = BasicEventsSourceData(scanResponse, store.state.walletState, biometricsWalletDataModels)
|
||||
BasicEventsPreChecker().tryToSend(converterData)
|
||||
}
|
||||
|
|
@ -15,12 +15,8 @@ class MultiWalletReducer {
|
|||
state.copy(selectedCurrency = action.currency)
|
||||
}
|
||||
is WalletAction.MultiWallet.TryToRemoveWallet -> state
|
||||
is WalletAction.MultiWallet.ScheduleCheckForMissingDerivation -> state.copy(
|
||||
derivationsCheckIsScheduled = true,
|
||||
)
|
||||
is WalletAction.MultiWallet.AddMissingDerivations -> state.copy(
|
||||
missingDerivations = action.blockchains,
|
||||
derivationsCheckIsScheduled = false,
|
||||
)
|
||||
is WalletAction.MultiWallet.BackupWallet -> state
|
||||
is WalletAction.MultiWallet.ScanToGetDerivations -> state.copy(state = ProgressState.Loading)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import com.tangem.core.ui.utils.OneTouchClickListener
|
|||
import com.tangem.feature.swap.api.SwapFeatureToggleManager
|
||||
import com.tangem.feature.swap.domain.SwapInteractor
|
||||
import com.tangem.tap.MainActivity
|
||||
import com.tangem.tap.common.analytics.events.MainScreen
|
||||
import com.tangem.tap.common.analytics.events.Portfolio
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.recyclerView.SpaceItemDecoration
|
||||
|
|
@ -73,12 +72,14 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
(WalletState::totalBalance) { totalBalance ->
|
||||
totalBalance?.state?.let {
|
||||
viewModel.onBalanceLoaded(totalBalance)
|
||||
store.state.globalState.topUpController?.totalBalanceStateChanged(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
lifecycle.addObserver(viewModel)
|
||||
lifecycleScope.launchWhenCreated {
|
||||
viewModel.launch()
|
||||
}
|
||||
|
|
@ -88,7 +89,6 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
super.onCreate(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
|
||||
Analytics.send(MainScreen.ScreenOpened())
|
||||
activity?.onBackPressedDispatcher?.addCallback(
|
||||
this,
|
||||
object : OnBackPressedCallback(true) {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
package com.tangem.tap.features.wallet.ui
|
||||
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
import com.tangem.tap.common.analytics.events.MainScreen
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.domain.userWalletList.UserWalletsListManager
|
||||
import com.tangem.tap.features.wallet.models.TotalBalance
|
||||
|
|
@ -28,7 +32,7 @@ import javax.inject.Inject
|
|||
@HiltViewModel
|
||||
internal class WalletViewModel @Inject constructor(
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : ViewModel(), StoreSubscriber<UserWalletsListManager?> {
|
||||
) : ViewModel(), StoreSubscriber<UserWalletsListManager?>, DefaultLifecycleObserver {
|
||||
private var observeWalletStoresUpdatesJob: Job? = null
|
||||
set(value) {
|
||||
field?.cancel()
|
||||
|
|
@ -52,6 +56,27 @@ internal class WalletViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
val scanResponse = store.state.globalState.scanResponse
|
||||
if (scanResponse != null) {
|
||||
val currency = ParamCardCurrencyConverter().convert(scanResponse.cardTypesResolver)
|
||||
val signInType = store.state.signInState.type
|
||||
if (currency != null && signInType != null) {
|
||||
analyticsEventHandler.send(
|
||||
Basic.SignedIn(
|
||||
currency = currency,
|
||||
batch = scanResponse.card.batchId,
|
||||
signInType = signInType,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart(owner: LifecycleOwner) {
|
||||
analyticsEventHandler.send(MainScreen.ScreenOpened())
|
||||
}
|
||||
|
||||
fun launch() {
|
||||
val manager = store.state.globalState.userWalletsListManager
|
||||
if (manager != null) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package com.tangem.tap.features.wallet.ui.wallet
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.Token
|
||||
import com.tangem.tap.common.extensions.beginDelayedTransition
|
||||
import com.tangem.tap.common.extensions.fitChipsByGroupWidth
|
||||
import com.tangem.tap.common.extensions.getQuantityString
|
||||
|
|
@ -118,6 +120,8 @@ class SingleWalletView : WalletView() {
|
|||
}
|
||||
}
|
||||
lAddress.btnShowQr.setOnClickListener {
|
||||
Analytics.send(Token.ShowWalletAddress)
|
||||
|
||||
walletData.walletAddresses?.selectedAddress?.let { selectedAddress ->
|
||||
store.dispatch(
|
||||
WalletAction.DialogAction.QrCode(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import com.tangem.common.core.TangemError
|
|||
import org.rekotlin.Action
|
||||
|
||||
internal sealed interface WelcomeAction : Action {
|
||||
object OnCreate : WelcomeAction
|
||||
object ProceedWithBiometrics : WelcomeAction {
|
||||
object Success : WelcomeAction
|
||||
data class Error(val error: TangemError) : WelcomeAction
|
||||
|
|
|
|||
|
|
@ -4,13 +4,10 @@ import android.content.Intent
|
|||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.doOnFailure
|
||||
import com.tangem.common.doOnSuccess
|
||||
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.events.SignIn
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.extensions.eraseContext
|
||||
import com.tangem.tap.common.extensions.onUserWalletSelected
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
|
|
@ -19,6 +16,7 @@ import com.tangem.tap.domain.model.builders.UserWalletBuilder
|
|||
import com.tangem.tap.domain.scanCard.ScanCardProcessor
|
||||
import com.tangem.tap.domain.userWalletList.unlockIfLockable
|
||||
import com.tangem.tap.features.onboarding.products.wallet.saltPay.message.SaltPayActivationError
|
||||
import com.tangem.tap.features.signin.redux.SignInAction
|
||||
import com.tangem.tap.intentHandler
|
||||
import com.tangem.tap.preferencesStorage
|
||||
import com.tangem.tap.scope
|
||||
|
|
@ -44,10 +42,6 @@ internal class WelcomeMiddleware {
|
|||
|
||||
private fun handleAction(action: WelcomeAction, state: WelcomeState) {
|
||||
when (action) {
|
||||
is WelcomeAction.OnCreate -> {
|
||||
Analytics.eraseContext()
|
||||
Analytics.send(SignIn.ScreenOpened())
|
||||
}
|
||||
is WelcomeAction.ProceedWithBiometrics -> {
|
||||
proceedWithBiometrics(state)
|
||||
}
|
||||
|
|
@ -74,9 +68,10 @@ internal class WelcomeMiddleware {
|
|||
store.dispatchOnMain(WelcomeAction.ProceedWithBiometrics.Error(error))
|
||||
}
|
||||
.doOnSuccess { selectedUserWallet ->
|
||||
store.dispatchOnMain(SignInAction.SetSignInType(Basic.SignedIn.SignInType.Biometric))
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
store.dispatchOnMain(WelcomeAction.ProceedWithBiometrics.Success)
|
||||
store.onUserWalletSelected(selectedUserWallet)
|
||||
store.onUserWalletSelected(userWallet = selectedUserWallet)
|
||||
|
||||
intentHandler.handleWalletConnectLink(state.intent)
|
||||
}
|
||||
|
|
@ -93,9 +88,10 @@ internal class WelcomeMiddleware {
|
|||
store.dispatchOnMain(WelcomeAction.ProceedWithCard.Error(error))
|
||||
}
|
||||
.doOnSuccess {
|
||||
store.dispatchOnMain(SignInAction.SetSignInType(Basic.SignedIn.SignInType.Card))
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
store.dispatchOnMain(WelcomeAction.ProceedWithCard.Success)
|
||||
store.onUserWalletSelected(userWallet)
|
||||
store.onUserWalletSelected(userWallet = userWallet)
|
||||
|
||||
intentHandler.handleWalletConnectLink(state.intent)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.tap.features.welcome.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
|
|
@ -19,22 +18,25 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.ui.components.SystemBarsEffect
|
||||
import com.tangem.core.ui.fragments.ComposeFragment
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.tap.common.analytics.events.SignIn
|
||||
import com.tangem.tap.common.extensions.eraseContext
|
||||
import com.tangem.tap.features.details.ui.cardsettings.resolveReference
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeAction
|
||||
import com.tangem.tap.features.welcome.ui.components.WarningDialog
|
||||
import com.tangem.tap.features.welcome.ui.components.WelcomeScreenContent
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
internal class WelcomeFragment : ComposeFragment<WelcomeScreenState>() {
|
||||
|
||||
private val viewModel by viewModels<WelcomeViewModel>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
store.dispatch(WelcomeAction.OnCreate)
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
Analytics.eraseContext()
|
||||
Analytics.send(SignIn.ScreenOpened())
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.tap.persistence
|
|||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import com.tangem.tap.common.analytics.filters.BasicTopUpFilter
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
|
|
@ -14,23 +14,23 @@ class ToppedUpWalletStorage(
|
|||
private val jsonConverter: MoshiJsonConverter,
|
||||
) {
|
||||
|
||||
private val walletList: MutableSet<BasicTopUpFilter.Data> = mutableSetOf()
|
||||
private val walletList: MutableSet<TopupInfo> = mutableSetOf()
|
||||
|
||||
init {
|
||||
walletList.addAll(restore())
|
||||
}
|
||||
|
||||
fun save(userWalletInfo: BasicTopUpFilter.Data): Boolean {
|
||||
fun save(userWalletInfo: TopupInfo): Boolean {
|
||||
walletList.removeAll { it.walletId == userWalletInfo.walletId }
|
||||
walletList.add(userWalletInfo)
|
||||
return save(walletList)
|
||||
}
|
||||
|
||||
fun restore(walletId: String): BasicTopUpFilter.Data? {
|
||||
fun restore(walletId: String): TopupInfo? {
|
||||
return walletList.firstOrNull { it.walletId == walletId }
|
||||
}
|
||||
|
||||
private fun save(userWallets: MutableSet<BasicTopUpFilter.Data>): Boolean {
|
||||
private fun save(userWallets: MutableSet<TopupInfo>): Boolean {
|
||||
return try {
|
||||
val json = jsonConverter.toJson(userWallets)
|
||||
preferences.edit(true) { putString(KEY, json) }
|
||||
|
|
@ -41,11 +41,11 @@ class ToppedUpWalletStorage(
|
|||
}
|
||||
}
|
||||
|
||||
private fun restore(): MutableSet<BasicTopUpFilter.Data> {
|
||||
private fun restore(): MutableSet<TopupInfo> {
|
||||
val json = preferences.getString(KEY, null) ?: return mutableSetOf()
|
||||
return try {
|
||||
val typedList = jsonConverter.typedList(BasicTopUpFilter.Data::class.java)
|
||||
val listData = jsonConverter.fromJson<List<BasicTopUpFilter.Data>>(json, typedList)!!
|
||||
val typedList = jsonConverter.typedList(TopupInfo::class.java)
|
||||
val listData = jsonConverter.fromJson<List<TopupInfo>>(json, typedList)!!
|
||||
listData.toMutableSet()
|
||||
} catch (ex: Exception) {
|
||||
preferences.edit(true) { remove(KEY) }
|
||||
|
|
@ -53,6 +53,13 @@ class ToppedUpWalletStorage(
|
|||
}
|
||||
}
|
||||
|
||||
data class TopupInfo(
|
||||
val walletId: String,
|
||||
val cardBalanceState: AnalyticsParam.CardBalanceState,
|
||||
) {
|
||||
val isToppedUp: Boolean = cardBalanceState == AnalyticsParam.CardBalanceState.Full
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val KEY = "userWalletsInfo"
|
||||
}
|
||||
|
|
|
|||
6
app/src/main/res/values/strings.xml
Normal file
6
app/src/main/res/values/strings.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="tangem_app_name" translatable="false">Tangem</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -10,6 +10,67 @@ plugins {
|
|||
alias(deps.plugins.firebase.crashlytics) apply false
|
||||
}
|
||||
|
||||
tasks.register("clean", Delete::class) {
|
||||
val clean by tasks.registering {
|
||||
delete(rootProject.buildDir)
|
||||
}
|
||||
|
||||
interface Injected {
|
||||
@get:Inject
|
||||
val fs: FileSystemOperations
|
||||
}
|
||||
|
||||
val assembleInternalQA by tasks.registering {
|
||||
group = "build"
|
||||
description = "Builds internal APK to 'build/outputs' directory"
|
||||
|
||||
val appOutputApkDir = "$projectDir/app/build/outputs/apk/internal"
|
||||
val rootOutputApkDir = "$buildDir/outputs"
|
||||
val injected = objects.newInstance<Injected>()
|
||||
|
||||
dependsOn(":app:assembleInternal")
|
||||
|
||||
doFirst {
|
||||
injected.fs.delete {
|
||||
delete(appOutputApkDir)
|
||||
delete("$rootOutputApkDir/app-internal.apk")
|
||||
}
|
||||
}
|
||||
doLast {
|
||||
injected.fs.copy {
|
||||
from("$appOutputApkDir/app-internal.apk")
|
||||
into(rootOutputApkDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val assembleExternalQA by tasks.registering {
|
||||
group = "build"
|
||||
description = "Builds external APK to 'build/outputs' directory"
|
||||
|
||||
val appOutputApkDir = "$projectDir/app/build/outputs/apk/external"
|
||||
val rootOutputApkDir = "$buildDir/outputs"
|
||||
val injected = objects.newInstance<Injected>()
|
||||
|
||||
dependsOn(":app:assembleExternal")
|
||||
|
||||
doFirst {
|
||||
injected.fs.delete {
|
||||
delete(appOutputApkDir)
|
||||
delete("$rootOutputApkDir/app-external.apk")
|
||||
}
|
||||
}
|
||||
doLast {
|
||||
injected.fs.copy {
|
||||
from("$appOutputApkDir/app-external.apk")
|
||||
into(rootOutputApkDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val assembleQA by tasks.registering {
|
||||
group = "build"
|
||||
description = "Builds internal and external APKs to 'build/outputs' directory"
|
||||
|
||||
dependsOn(assembleInternalQA)
|
||||
dependsOn(assembleExternalQA)
|
||||
}
|
||||
|
|
@ -8,5 +8,4 @@ open class AnalyticsEvent(
|
|||
val event: String,
|
||||
var params: Map<String, String> = mapOf(),
|
||||
val error: Throwable? = null,
|
||||
var filterData: Any? = null,
|
||||
)
|
||||
|
|
@ -358,7 +358,6 @@
|
|||
<string name="swapping_token_list_title">Choose token</string>
|
||||
<string name="swapping_token_list_your_tokens">Your tokens</string>
|
||||
<string name="swapping_token_not_available">not available</string>
|
||||
<string name="tangem_app_name">Tangem</string>
|
||||
<string name="token_details_hide_alert_hide">Hide</string>
|
||||
<string name="token_details_hide_alert_message">You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page.</string>
|
||||
<string name="token_details_hide_alert_title">Hide %s</string>
|
||||
|
|
|
|||
|
|
@ -358,7 +358,6 @@
|
|||
<string name="swapping_token_list_title">Choose token</string>
|
||||
<string name="swapping_token_list_your_tokens">Your tokens</string>
|
||||
<string name="swapping_token_not_available">not available</string>
|
||||
<string name="tangem_app_name">Tangem</string>
|
||||
<string name="token_details_hide_alert_hide">Hide</string>
|
||||
<string name="token_details_hide_alert_message">You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page.</string>
|
||||
<string name="token_details_hide_alert_title">Hide %s</string>
|
||||
|
|
|
|||
|
|
@ -358,7 +358,6 @@
|
|||
<string name="swapping_token_list_title">Choose token</string>
|
||||
<string name="swapping_token_list_your_tokens">Your tokens</string>
|
||||
<string name="swapping_token_not_available">not available</string>
|
||||
<string name="tangem_app_name">Tangem</string>
|
||||
<string name="token_details_hide_alert_hide">Hide</string>
|
||||
<string name="token_details_hide_alert_message">You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page.</string>
|
||||
<string name="token_details_hide_alert_title">Hide %s</string>
|
||||
|
|
|
|||
|
|
@ -359,7 +359,6 @@
|
|||
<string name="swapping_token_list_title">Выберите токен</string>
|
||||
<string name="swapping_token_list_your_tokens">Ваши токены</string>
|
||||
<string name="swapping_token_not_available">не доступен</string>
|
||||
<string name="tangem_app_name">Tangem</string>
|
||||
<string name="token_details_hide_alert_hide">Скрыть</string>
|
||||
<string name="token_details_hide_alert_message">Вы скрываете токен с главного экрана, но в любой момент сможете добавить его обратно через страницу управления токенами.</string>
|
||||
<string name="token_details_hide_alert_title">Скрыть %s</string>
|
||||
|
|
|
|||
|
|
@ -358,7 +358,6 @@
|
|||
<string name="swapping_token_list_title">選擇代幣</string>
|
||||
<string name="swapping_token_list_your_tokens">您的代幣</string>
|
||||
<string name="swapping_token_not_available">無法使用</string>
|
||||
<string name="tangem_app_name">Tangem</string>
|
||||
<string name="token_details_hide_alert_hide">隱藏</string>
|
||||
<string name="token_details_hide_alert_message">您即將在主屏幕上隱藏此代幣。您可以隨時通過管理代幣頁面將其添加回來。</string>
|
||||
<string name="token_details_hide_alert_title">隱藏 %s</string>
|
||||
|
|
|
|||
|
|
@ -359,7 +359,6 @@
|
|||
<string name="swapping_token_list_title">Choose token</string>
|
||||
<string name="swapping_token_list_your_tokens">Your tokens</string>
|
||||
<string name="swapping_token_not_available">not available</string>
|
||||
<string name="tangem_app_name">Tangem</string>
|
||||
<string name="token_details_hide_alert_hide">Hide</string>
|
||||
<string name="token_details_hide_alert_message">You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page.</string>
|
||||
<string name="token_details_hide_alert_title">Hide %s</string>
|
||||
|
|
|
|||
|
|
@ -79,8 +79,7 @@ object TapWorkarounds {
|
|||
"Now blockchain is read form files (CardTypesResolver.getBlockchain), " +
|
||||
"but for previously saved cards this method is still used",
|
||||
)
|
||||
fun CardDTO.getTangemNoteBlockchain(): Blockchain? =
|
||||
tangemNoteBatches[batchId] ?: if (isSaltPay) Blockchain.Gnosis else null
|
||||
fun CardDTO.getTangemNoteBlockchain(): Blockchain? = tangemNoteBatches[batchId]
|
||||
|
||||
fun isStart2CoinIssuer(cardIssuer: String?): Boolean {
|
||||
return cardIssuer?.lowercase(Locale.US) == START_2_COIN_ISSUER
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ package com.tangem.feature.referral.analytics
|
|||
|
||||
import com.tangem.core.analytics.AnalyticsEvent
|
||||
|
||||
sealed class ReferralEvents(
|
||||
event: String,
|
||||
) : AnalyticsEvent(REFERRAL_PROGRAM_CATEGORY, event) {
|
||||
sealed class ReferralEvents(event: String) : AnalyticsEvent(REFERRAL_PROGRAM_CATEGORY, event) {
|
||||
|
||||
object ReferralScreenOpened : ReferralEvents(event = "Referral Screen Opened")
|
||||
object ClickParticipate : ReferralEvents(event = "Button - Participate")
|
||||
|
|
@ -13,4 +11,4 @@ sealed class ReferralEvents(
|
|||
object ClickTaC : ReferralEvents(event = "Link - TaC")
|
||||
}
|
||||
|
||||
private const val REFERRAL_PROGRAM_CATEGORY = "Referral program"
|
||||
private const val REFERRAL_PROGRAM_CATEGORY = "Referral Program"
|
||||
|
|
@ -15,16 +15,16 @@ sealed class SwapEvents(
|
|||
object SendTokenBalanceClicked : SwapEvents(event = "Send Token Balance Clicked")
|
||||
object ReceiveTokenClicked : SwapEvents(event = "Receive Token Clicked")
|
||||
object ChooseTokenScreenOpened : SwapEvents(event = "Choose Token Screen Opened")
|
||||
object SearchTokenClicked : SwapEvents(event = "Search Token Clicked")
|
||||
object SearchTokenClicked : SwapEvents(event = "Searched Token Clicked")
|
||||
data class ButtonSwapClicked(val sendToken: String, val receiveToken: String) : SwapEvents(
|
||||
event = "Button - Swap",
|
||||
params = mapOf("Send Token" to sendToken, "Receive Token" to receiveToken),
|
||||
)
|
||||
|
||||
object ButtonGivePermissionClicked : SwapEvents(event = "Button - Give permission") // ?
|
||||
object ButtonGivePermissionClicked : SwapEvents(event = "Button - Give permission")
|
||||
object ButtonPermissionApproveClicked : SwapEvents(event = "Button - Permission Approve")
|
||||
object ButtonPermissionCancelClicked : SwapEvents(event = "Button - Permission Cancel")
|
||||
object ButtonPermitAndSwapClicked : SwapEvents(event = "Button - Permit and Swap") // ?
|
||||
object ButtonPermitAndSwapClicked : SwapEvents(event = "Button - Permit and Swap")
|
||||
object ButtonSwipeClicked : SwapEvents(event = "Button - Swipe")
|
||||
object SwapInProgressScreen : SwapEvents(event = "Swap in Progress Screen Opened")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ internal enum class BuildType(
|
|||
* Build type for QA
|
||||
*
|
||||
* Features:
|
||||
* - Env: beta
|
||||
* - Env: dev
|
||||
* - Signing config: debug
|
||||
* - Logs
|
||||
* - Tester menu
|
||||
|
|
@ -46,7 +46,7 @@ internal enum class BuildType(
|
|||
appIdSuffix = "internal",
|
||||
versionSuffix = "internal",
|
||||
configFields = listOf(
|
||||
BuildConfigField.Environment(value = "beta"),
|
||||
BuildConfigField.Environment(value = "dev"),
|
||||
BuildConfigField.TestActionEnabled(isEnabled = true),
|
||||
BuildConfigField.LogEnabled(isEnabled = true),
|
||||
BuildConfigField.TesterMenuAvailability(isEnabled = true),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue