Updated on 2026-08-14

This commit is contained in:
Tangem 2023-02-22 17:18:44 +03:00
parent cd7899ba3e
commit b119c29d09
19 changed files with 124 additions and 34 deletions

View file

@ -0,0 +1,26 @@
package com.tangem.tap.common.analytics.paramsInterceptor
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.api.ParamsInterceptor
import com.tangem.domain.common.ScanResponse
/**
[REDACTED_AUTHOR]
*/
class LinkedCardContextInterceptor(
private val scanResponse: ScanResponse,
val parent: LinkedCardContextInterceptor? = null,
) : ParamsInterceptor {
private val contextInterceptor = CardContextInterceptor(scanResponse)
override fun id(): String = LinkedCardContextInterceptor.id()
override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = contextInterceptor.canBeAppliedTo(event)
override fun intercept(params: MutableMap<String, String>) = contextInterceptor.intercept(params)
companion object {
fun id(): String = LinkedCardContextInterceptor::class.java.simpleName
}
}

View file

@ -2,15 +2,42 @@ package com.tangem.tap.common.extensions
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.ScanResponse
import com.tangem.tap.common.analytics.paramsInterceptor.CardContextInterceptor
import com.tangem.tap.common.analytics.paramsInterceptor.LinkedCardContextInterceptor
/**
[REDACTED_AUTHOR]
*/
fun Analytics.addCardContext(scanResponse: ScanResponse) {
addParamsInterceptor(CardContextInterceptor(scanResponse))
/**
* Sets the new context
*/
fun Analytics.setContext(scanResponse: ScanResponse) {
addParamsInterceptor(LinkedCardContextInterceptor(scanResponse))
}
fun Analytics.eraseCardContext() {
removeParamsInterceptor(CardContextInterceptor.id())
/**
* Erases the context
*/
fun Analytics.eraseContext() {
removeParamsInterceptor(LinkedCardContextInterceptor.id())
}
/**
* Adds a new context and keeps a previous context as the parent of the new one
*/
fun Analytics.addContext(scanResponse: ScanResponse) {
val currentContext = removeParamsInterceptor(LinkedCardContextInterceptor.id()) as? LinkedCardContextInterceptor
val newContext = LinkedCardContextInterceptor(scanResponse, parent = currentContext)
addParamsInterceptor(newContext)
}
/**
* Removes the current context and restores the previous one if it was present.
*/
fun Analytics.removeContext() {
val currentContext = removeParamsInterceptor(LinkedCardContextInterceptor.id()) as? LinkedCardContextInterceptor
val previousContext = currentContext?.parent ?: return
addParamsInterceptor(previousContext)
}

View file

@ -10,6 +10,7 @@ import com.tangem.blockchain.common.WalletManagerFactory
import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.common.services.Result
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.CardDTO
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
@ -19,6 +20,7 @@ import com.tangem.domain.common.extensions.withMainContext
import com.tangem.operations.attestation.Attestation
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.safeUpdate
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.domain.configurable.config.ConfigManager
import com.tangem.tap.domain.extensions.isMultiwalletAllowed
@ -94,6 +96,7 @@ class TapWalletManager {
}
suspend fun onWalletSelected(userWallet: UserWallet, refresh: Boolean) {
Analytics.setContext(userWallet.scanResponse)
val scanResponse = userWallet.scanResponse
val card = scanResponse.card
val attestationFailed = card.attestation.status == Attestation.Status.Failed

View file

@ -13,9 +13,11 @@ import com.tangem.domain.common.extensions.withMainContext
import com.tangem.operations.backup.BackupService
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
import com.tangem.tap.backupService
import com.tangem.tap.common.extensions.addCardContext
import com.tangem.tap.common.analytics.paramsInterceptor.CardContextInterceptor
import com.tangem.tap.common.extensions.addContext
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.primaryCardIsSaltPayVisa
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
@ -105,8 +107,15 @@ object ScanCardProcessor {
analyticsEvent: AnalyticsEvent?,
scanResponse: ScanResponse,
) {
Analytics.addCardContext(scanResponse)
analyticsEvent?.let { Analytics.send(it) }
analyticsEvent?.let {
// this workaround needed to send CardWasScannedEvent without adding a context
val interceptor = CardContextInterceptor(scanResponse)
val params = it.params.toMutableMap()
interceptor.intercept(params)
it.params = params.toMap()
Analytics.send(it)
}
}
/**
@ -196,6 +205,7 @@ object ScanCardProcessor {
withMainContext {
when (result) {
is Result.Success -> {
Analytics.addContext(scanResponse)
val isOnboardingCase = result.data
if (isOnboardingCase) {
onWalletNotCreated()
@ -222,16 +232,19 @@ object ScanCardProcessor {
SaltPayExceptionHandler.handle(error)
onFailure(TangemSdkError.ExceptionError(error))
} else {
Analytics.setContext(scanResponse)
onSuccess(scanResponse)
}
}
} else {
if (OnboardingHelper.isOnboardingCase(scanResponse)) {
Analytics.addContext(scanResponse)
onWalletNotCreated()
store.dispatchOnMain(GlobalAction.Onboarding.Start(scanResponse, canSkipBackup = true))
val appScreen = OnboardingHelper.whereToNavigate(scanResponse)
navigateTo(appScreen) { onProgressStateChange(it) }
} else {
Analytics.setContext(scanResponse)
if (scanResponse.twinsIsTwinned() && !preferencesStorage.wasTwinsOnboardingShown()) {
onWalletNotCreated()
store.dispatchOnMain(TwinCardsAction.SetStepOfScreen(TwinCardsStep.WelcomeOnly(scanResponse)))

View file

@ -10,7 +10,7 @@ import com.tangem.tap.common.analytics.events.Shop
import com.tangem.tap.common.entities.IndeterminateProgressButton
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchOpenUrl
import com.tangem.tap.common.extensions.eraseCardContext
import com.tangem.tap.common.extensions.eraseContext
import com.tangem.tap.common.extensions.onCardScanned
import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.common.redux.AppState
@ -54,7 +54,7 @@ private val homeMiddleware: Middleware<AppState> = { _, _ ->
private fun handleHomeAction(action: Action) {
when (action) {
is HomeAction.OnCreate -> {
Analytics.eraseCardContext()
Analytics.eraseContext()
Analytics.send(IntroductionProcess.ScreenOpened())
}
is HomeAction.Init -> {

View file

@ -1,9 +1,12 @@
package com.tangem.tap.features.onboarding
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.ProductType
import com.tangem.domain.common.ScanResponse
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.onCardScanned
import com.tangem.tap.common.extensions.removeContext
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.saveWallet.redux.SaveWalletAction
@ -47,7 +50,7 @@ class OnboardingHelper {
ProductType.Twins -> AppScreen.OnboardingTwins
ProductType.SaltPay -> AppScreen.OnboardingWallet
ProductType.Start2Coin -> throw java.lang.UnsupportedOperationException(
"Onboarding for Start2Coin cards is not supported"
"Onboarding for Start2Coin cards is not supported",
)
}
}
@ -57,6 +60,7 @@ class OnboardingHelper {
accessCode: String? = null,
backupCardsIds: List<String>? = null,
) {
Analytics.setContext(scanResponse)
when {
// When should save user wallets, then save card without navigate to save wallet screen
preferencesStorage.shouldSaveUserWallets -> scope.launch {
@ -94,5 +98,9 @@ class OnboardingHelper {
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
}
fun onInterrupted() {
Analytics.removeContext()
}
}
}

View file

@ -210,4 +210,9 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
TransitionManager.beginDelayedTransition(onboardingWalletContainer, transition)
}
}
override fun handleOnBackPressed() {
store.dispatch(OnboardingNoteAction.OnBackPressed)
super.handleOnBackPressed()
}
}

View file

@ -21,6 +21,7 @@ sealed class OnboardingNoteAction : Action {
data class SetWalletManager(val walletManager: WalletManager) : OnboardingNoteAction()
object DetermineStepOfScreen : OnboardingNoteAction()
object Done : OnboardingNoteAction()
object OnBackPressed : OnboardingNoteAction()
data class SetStepOfScreen(val step: OnboardingNoteStep) : OnboardingNoteAction()

View file

@ -2,9 +2,9 @@ package com.tangem.tap.features.onboarding.products.note.redux
import com.tangem.common.CompletionResult
import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
@ -190,5 +190,8 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch
store.dispatch(GlobalAction.Onboarding.Stop)
OnboardingHelper.trySaveWalletAndNavigateToWalletScreen(scanResponse)
}
OnboardingNoteAction.OnBackPressed -> {
OnboardingHelper.onInterrupted()
}
}
}

View file

@ -118,4 +118,9 @@ class OnboardingOtherCardsFragment : BaseOnboardingFragment<OnboardingOtherCards
transition.interpolator = OvershootInterpolator()
TransitionManager.beginDelayedTransition(onboardingWalletContainer, transition)
}
override fun handleOnBackPressed() {
store.dispatch(OnboardingOtherCardsAction.OnBackPressed)
super.handleOnBackPressed()
}
}

View file

@ -15,6 +15,7 @@ sealed class OnboardingOtherCardsAction : Action {
class SetArtworkUrl(val artworkUrl: String) : OnboardingOtherCardsAction()
object DetermineStepOfScreen : OnboardingOtherCardsAction()
object Done : OnboardingOtherCardsAction()
object OnBackPressed : OnboardingOtherCardsAction()
data class SetStepOfScreen(val step: OnboardingOtherCardsStep) : OnboardingOtherCardsAction()

View file

@ -2,9 +2,9 @@ package com.tangem.tap.features.onboarding.products.otherCards.redux
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.CompletionResult
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.postUi
import com.tangem.tap.common.redux.AppState
@ -119,9 +119,7 @@ private fun handleOtherCardsAction(action: Action) {
delay(DELAY_SDK_DIALOG_CLOSE)
store.dispatch(OnboardingOtherCardsAction.SetStepOfScreen(OnboardingOtherCardsStep.Done))
}
is CompletionResult.Failure -> {
// do nothing
}
is CompletionResult.Failure -> Unit
}
}
}
@ -130,9 +128,9 @@ private fun handleOtherCardsAction(action: Action) {
store.dispatch(GlobalAction.Onboarding.Stop)
OnboardingHelper.trySaveWalletAndNavigateToWalletScreen(onboardingManager.scanResponse)
}
is OnboardingOtherCardsAction.Confetti.Hide,
is OnboardingOtherCardsAction.SetArtworkUrl,
is OnboardingOtherCardsAction.Confetti.Show,
-> Unit
OnboardingOtherCardsAction.OnBackPressed -> {
OnboardingHelper.onInterrupted()
}
else -> Unit
}
}

View file

@ -28,9 +28,9 @@ sealed class TwinCardsAction : Action {
}
sealed class Wallet : TwinCardsAction() {
data class HandleOnBackPressed(
data class OnBackPressed(
// this is necessary for the correct animation of the return of cards to the Home screen
val shouldResetTwinCardsWidget: (should: Boolean, popAction: VoidCallback) -> Unit
val shouldResetTwinCardsWidget: (should: Boolean, popAction: VoidCallback) -> Unit,
) : TwinCardsAction()
data class ShowInterruptDialog(val onOk: VoidCallback) : TwinCardsAction(), StateDialog

View file

@ -3,10 +3,10 @@ package com.tangem.tap.features.onboarding.products.twins.redux
import com.tangem.blockchain.extensions.Result
import com.tangem.common.CompletionResult
import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
@ -26,8 +26,8 @@ import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
import com.tangem.tap.domain.model.builders.UserWalletIdBuilder
import com.tangem.tap.domain.twins.TwinCardsManager
import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletAction
@ -121,7 +121,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
}
}
}
is TwinCardsAction.Wallet.HandleOnBackPressed -> {
is TwinCardsAction.Wallet.OnBackPressed -> {
val shouldReturnCardBack = twinCardsState.mode == CreateTwinWalletMode.CreateWallet
&& twinCardsState.currentStep != TwinCardsStep.TopUpWallet
&& twinCardsState.currentStep != TwinCardsStep.Done
@ -136,6 +136,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
val stateDialog = TwinCardsAction.Wallet.ShowInterruptDialog(onInterruptPrompt)
store.dispatchDialogShow(stateDialog)
} else {
OnboardingHelper.onInterrupted()
store.dispatch(TwinCardsAction.CardsManager.Release)
action.shouldResetTwinCardsWidget(shouldReturnCardBack) {
store.dispatch(NavigationAction.PopBackTo())

View file

@ -13,12 +13,12 @@ import coil.load
import com.tangem.Message
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.extensions.VoidCallback
import com.tangem.core.analytics.Analytics
import com.tangem.core.ui.fragments.setStatusBarColor
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.TwinCardNumber
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
import com.tangem.tap.common.AndroidAssetReader
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.beginDelayedTransition
import com.tangem.tap.common.extensions.getDrawableCompat
@ -401,7 +401,7 @@ class TwinsCardsFragment : BaseOnboardingFragment<TwinCardsState>() {
override fun handleOnBackPressed() {
store.dispatch(
TwinCardsAction.Wallet.HandleOnBackPressed { should, popAction ->
TwinCardsAction.Wallet.OnBackPressed { should, popAction ->
store.dispatch(TwinCardsAction.Confetti.Hide)
showConfetti(false)
if (should) switchToCard(TwinCardNumber.First, true, popAction)

View file

@ -426,6 +426,7 @@ private fun handleOnBackPressed(state: OnboardingWalletState) {
if (state.isSaltPay) {
showInterruptOnboardingDialog()
} else {
OnboardingHelper.onInterrupted()
store.dispatch(NavigationAction.PopBackTo())
}
}
@ -436,6 +437,7 @@ private fun showInterruptOnboardingDialog() {
store.dispatchDialogShow(
OnboardingDialog.InterruptOnboarding(
onOk = {
OnboardingHelper.onInterrupted()
store.dispatch(BackupAction.DiscardBackup)
store.dispatch(NavigationAction.PopBackTo())
},

View file

@ -9,6 +9,7 @@ import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Token.ButtonRemoveToken
import com.tangem.tap.common.extensions.addContext
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain
@ -163,6 +164,7 @@ class MultiWalletMiddleware {
is WalletAction.MultiWallet.ShowWalletBackupWarning -> Unit
is WalletAction.MultiWallet.BackupWallet -> {
store.state.globalState.scanResponse?.let {
Analytics.addContext(it)
store.dispatch(GlobalAction.Onboarding.Start(it, canSkipBackup = false))
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingWallet))
}

View file

@ -12,7 +12,6 @@ import com.tangem.domain.common.util.UserWalletId
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.common.analytics.events.MyWallets
import com.tangem.tap.common.extensions.addCardContext
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.onUserWalletSelected
import com.tangem.tap.common.redux.AppState
@ -199,8 +198,6 @@ internal class WalletSelectorMiddleware {
.doOnSuccess {
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
if (selectedUserWallet != null) {
Analytics.addCardContext(selectedUserWallet.scanResponse)
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet))
store.onUserWalletSelected(selectedUserWallet)
}

View file

@ -9,9 +9,8 @@ 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.addCardContext
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.eraseCardContext
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
@ -44,7 +43,7 @@ internal class WelcomeMiddleware {
private fun handleAction(action: WelcomeAction, state: WelcomeState) {
when (action) {
is WelcomeAction.OnCreate -> {
Analytics.eraseCardContext()
Analytics.eraseContext()
Analytics.send(SignIn.ScreenOpened())
}
is WelcomeAction.ProceedWithBiometrics -> {
@ -71,7 +70,6 @@ internal class WelcomeMiddleware {
}
.doOnSuccess { selectedUserWallet ->
if (selectedUserWallet != null) {
Analytics.addCardContext(selectedUserWallet.scanResponse)
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
store.dispatchOnMain(WelcomeAction.ProceedWithBiometrics.Success)
store.onUserWalletSelected(selectedUserWallet)