From 820fa0c39314b31fdcb1a17914fdc6969d7f79fd Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 15 Mar 2023 00:39:41 +0300 Subject: [PATCH 01/12] Updated on 2026-08-14 --- .../tangem/domain/common/TangemCardTypesResolver.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/domain/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt b/domain/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt index 41764fb5cf..5986ca0d5f 100644 --- a/domain/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt +++ b/domain/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt @@ -40,13 +40,16 @@ class TangemCardTypesResolver( return when (productType) { ProductType.Start2Coin -> if (card.isTestCard) Blockchain.BitcoinTestnet else Blockchain.Bitcoin ProductType.SaltPay -> Blockchain.SaltPay + ProductType.Note -> card.getTangemNoteBlockchain() ?: Blockchain.Unknown // todo restored old logic else -> { val blockchainName: String = walletData?.blockchain.guard { - if (productType == ProductType.Note) { - return card.getTangemNoteBlockchain() ?: Blockchain.Unknown - } else { - return Blockchain.Unknown - } + // TODO(hotfix [REDACTED_TASK_KEY], for note bnb, card determined as beacon blockchain, refactor later) + // + // if (productType == ProductType.Note) { + // return card.getTangemNoteBlockchain() ?: Blockchain.Unknown + // } else { + // } + return Blockchain.Unknown } Blockchain.fromId(blockchainName) } From f24a92f8595b87a81cae5bea53791e06571c9480 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 17 Mar 2023 18:35:53 +0300 Subject: [PATCH 02/12] Updated on 2026-08-14 --- .../saveWallet/redux/SaveWalletMiddleware.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt index 7c248c0f4f..a8fc4365aa 100644 --- a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt @@ -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 { From 9b6cb2f235c577b9abc7e57e31c9e7606f877a6f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 21 Mar 2023 23:07:50 +0800 Subject: [PATCH 03/12] Updated on 2026-08-14 --- .../java/com/tangem/tap/TapApplication.kt | 2 - .../converters/BasicEventConverter.kt | 22 +------- .../tap/common/analytics/events/Basic.kt | 10 +++- .../tap/common/analytics/events/Settings.kt | 2 +- .../tap/common/analytics/events/Token.kt | 5 ++ .../analytics/filters/BasicSignInFilter.kt | 27 --------- .../com/tangem/tap/common/redux/AppReducer.kt | 2 + .../com/tangem/tap/common/redux/AppState.kt | 4 ++ .../tap/features/home/redux/HomeMiddleware.kt | 5 +- .../note/redux/OnboardingNoteMiddleware.kt | 10 ++++ .../twins/redux/TwinCardsMiddleware.kt | 10 ++++ .../wallet/redux/OnboardingWalletAction.kt | 12 +--- .../redux/OnboardingWalletMiddleware.kt | 56 ++++++++++++------- .../wallet/redux/OnboardingWalletReducer.kt | 1 + .../redux/OnboardingSaltPayMiddleware.kt | 9 ++- .../wallet/ui/OnboardingWalletFragment.kt | 2 +- .../tap/features/signin/redux/SignInAction.kt | 12 ++++ .../features/signin/redux/SignInMiddleware.kt | 15 +++++ .../features/signin/redux/SignInReducer.kt | 17 ++++++ .../tap/features/signin/redux/SignInState.kt | 8 +++ .../redux/middlewares/WalletMiddleware.kt | 6 +- .../tap/features/wallet/ui/WalletFragment.kt | 3 +- .../tap/features/wallet/ui/WalletViewModel.kt | 29 +++++++++- .../wallet/ui/wallet/SingleWalletView.kt | 4 ++ .../features/welcome/redux/WelcomeAction.kt | 1 - .../welcome/redux/WelcomeMiddleware.kt | 14 ++--- .../features/welcome/ui/WelcomeFragment.kt | 14 +++-- .../referral/analytics/ReferralEvents.kt | 6 +- .../feature/swap/analytics/SwapEvents.kt | 6 +- 29 files changed, 195 insertions(+), 119 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/common/analytics/filters/BasicSignInFilter.kt create mode 100644 app/src/main/java/com/tangem/tap/features/signin/redux/SignInAction.kt create mode 100644 app/src/main/java/com/tangem/tap/features/signin/redux/SignInMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/signin/redux/SignInReducer.kt create mode 100644 app/src/main/java/com/tangem/tap/features/signin/redux/SignInState.kt diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 56b14e2732..05634eb5fb 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -22,7 +22,6 @@ 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 @@ -187,7 +186,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( diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/BasicEventConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/BasicEventConverter.kt index 79ce7f700a..1e1f9894c8 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/converters/BasicEventConverter.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/converters/BasicEventConverter.kt @@ -23,7 +23,6 @@ class BasicEventsPreChecker { fun tryToSend(converterData: BasicEventsSourceData) { if (!isReadyToSend(converterData)) return - BasicSignInEventConverter().convert(converterData)?.let { Analytics.send(it) } BasicTopUpEventConverter().convert(converterData)?.let { Analytics.send(it) } } @@ -99,12 +98,8 @@ data class BasicEventsSourceData( val walletState: WalletState, val biometricsWalletDataModels: List?, ) { - val batchId: String by lazy { scanResponse.card.batchId } - val userWalletIdStringValue: String? by lazy { - UserWalletIdBuilder.scanResponse(scanResponse) - .build() - ?.stringValue + UserWalletIdBuilder.scanResponse(scanResponse).build()?.stringValue } val paramCardCurrency: AnalyticsParam.CardCurrency? by lazy { @@ -164,21 +159,6 @@ private class BalanceCalculator( } } -class BasicSignInEventConverter : Converter { - - 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 { override fun convert(value: BasicEventsSourceData): Basic.ToppedUp? { diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt index 76b00ef40f..4a14854595 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt @@ -21,17 +21,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", diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Settings.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Settings.kt index 03cbb88ac6..df526f80d5 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Settings.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Settings.kt @@ -58,7 +58,7 @@ sealed class Settings( sealed class AppSettings( event: String, params: Map = mapOf(), - ) : Settings("Settings / App Settings", event, params) { + ) : Settings(category = "Settings / App Settings", event = event, params = params) { class SaveWalletSwitcherChanged(state: AnalyticsParam.OnOffState) : CardSettings( event = "Save Wallet Switcher Changed", diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt index 63dd109942..6247c504b3 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt @@ -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 = mapOf(), diff --git a/app/src/main/java/com/tangem/tap/common/analytics/filters/BasicSignInFilter.kt b/app/src/main/java/com/tangem/tap/common/analytics/filters/BasicSignInFilter.kt deleted file mode 100644 index 09c26d8d7d..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/filters/BasicSignInFilter.kt +++ /dev/null @@ -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() - - 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 -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index 3bcb47a092..ec25507402 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -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 @@ -44,6 +45,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), ) } diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index e9b253d90d..c4d49c0ae2 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -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 @@ -64,6 +66,7 @@ data class AppState( val saveWalletState: SaveWalletState = SaveWalletState(), val walletSelectorState: WalletSelectorState = WalletSelectorState(), val sprinklrState: SprinklrState = SprinklrState(), + val signInState: SignInState = SignInState() ) : StateType { val domainState: DomainState @@ -105,6 +108,7 @@ data class AppState( LockUserWalletsTimerMiddleware().middleware, AccessCodeRequestPolicyMiddleware().middleware, SprinklrMiddleware().middleware, + SignInMiddleware.middleware ) } } diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index 0ea4b412cf..3230f8b878 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -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) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index 28d62cf134..8ec11cf17c 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -6,6 +6,7 @@ import com.tangem.core.analytics.Analytics import com.tangem.domain.common.extensions.withMainContext import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE import com.tangem.tap.common.analytics.events.AnalyticsParam +import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.analytics.events.Onboarding import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.dispatchDialogShow @@ -159,6 +160,15 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch } is OnboardingNoteAction.Balance.Set -> { if (action.balance.balanceIsToppedUp()) { + Analytics.send( + event = Basic.ToppedUp( + currency = AnalyticsParam.CardCurrency.SingleCurrency( + type = AnalyticsParam.CurrencyType.Blockchain( + blockchain = scanResponse.cardTypesResolver.getBlockchain(), + ), + ), + ), + ) store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.Done)) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt index ff7ff2399e..f6d021d0f8 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt @@ -8,6 +8,7 @@ import com.tangem.domain.common.ScanResponse import com.tangem.domain.common.extensions.withMainContext import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE import com.tangem.tap.common.analytics.events.AnalyticsParam +import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.analytics.events.Onboarding import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.dispatchDialogShow @@ -261,6 +262,15 @@ private fun handle(action: Action, dispatch: DispatchFunction) { } is TwinCardsAction.Balance.Set -> { if (action.balance.balanceIsToppedUp()) { + Analytics.send( + event = Basic.ToppedUp( + currency = AnalyticsParam.CardCurrency.SingleCurrency( + type = AnalyticsParam.CurrencyType.Blockchain( + blockchain = getScanResponse().cardTypesResolver.getBlockchain(), + ), + ), + ), + ) scope.launch { withMainContext { store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Done)) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt index df5882a35c..47819aea80 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt @@ -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() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 4226d47b74..50a9f32619 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -329,15 +329,6 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) } } } - 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 +353,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)) + } + 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 -> {} } } +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 +392,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) { + cardIds.forEach { cardId -> + preferencesStorage.usedCardsPrefStorage.activationFinished(cardId) + } +} + +internal fun gatherCardIds(backupState: BackupState, card: CardDTO?): List { + return (listOf(backupState.primaryCardId, card?.cardId) + backupState.backupCardIds) .filterNotNull() - .forEach { cardId -> - preferencesStorage.usedCardsPrefStorage.activationFinished(cardId) - } + .distinct() } /** @@ -415,7 +430,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) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt index 5359efde9b..6e457704dd 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt @@ -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() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/redux/OnboardingSaltPayMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/redux/OnboardingSaltPayMiddleware.kt index 57b1f17f3c..5418a8d42e 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/redux/OnboardingSaltPayMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/saltPay/redux/OnboardingSaltPayMiddleware.kt @@ -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 -> { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt index d2202cc249..7125990ede 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt @@ -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) diff --git a/app/src/main/java/com/tangem/tap/features/signin/redux/SignInAction.kt b/app/src/main/java/com/tangem/tap/features/signin/redux/SignInAction.kt new file mode 100644 index 0000000000..efbcafb9df --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/signin/redux/SignInAction.kt @@ -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 +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/signin/redux/SignInMiddleware.kt b/app/src/main/java/com/tangem/tap/features/signin/redux/SignInMiddleware.kt new file mode 100644 index 0000000000..1f44dff0f7 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/signin/redux/SignInMiddleware.kt @@ -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 = { _, _ -> + { next -> + { action -> next(action) } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/signin/redux/SignInReducer.kt b/app/src/main/java/com/tangem/tap/features/signin/redux/SignInReducer.kt new file mode 100644 index 0000000000..3d5910774d --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/signin/redux/SignInReducer.kt @@ -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) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/signin/redux/SignInState.kt b/app/src/main/java/com/tangem/tap/features/signin/redux/SignInState.kt new file mode 100644 index 0000000000..95b132aab7 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/signin/redux/SignInState.kt @@ -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) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index 1866921b7e..9b5324ead5 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -364,6 +364,10 @@ suspend fun handleBasicAnalyticsEvent() { null } - val converterData = BasicEventsSourceData(scanResponse, store.state.walletState, biometricsWalletDataModels) + val converterData = BasicEventsSourceData( + scanResponse = scanResponse, + walletState = store.state.walletState, + biometricsWalletDataModels = biometricsWalletDataModels, + ) BasicEventsPreChecker().tryToSend(converterData) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index 3e99ac95ed..fae5543edf 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -22,7 +22,6 @@ import com.tangem.core.ui.fragments.setStatusBarColor import com.tangem.core.ui.utils.OneTouchClickListener 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 @@ -66,6 +65,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber { +internal class WalletViewModel : ViewModel(), StoreSubscriber, DefaultLifecycleObserver { private var observeWalletStoresUpdatesJob: Job? = null set(value) { field?.cancel() @@ -41,6 +47,27 @@ internal class WalletViewModel : ViewModel(), StoreSubscriber store.dispatch( WalletAction.DialogAction.QrCode( diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt index ddf2e403d3..0d248ec7af 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt @@ -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 diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index 84ff91eebf..495b112f1d 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -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) } diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt index e346adb6d1..81cad48aca 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeFragment.kt @@ -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() { + private val viewModel by viewModels() - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - store.dispatch(WelcomeAction.OnCreate) + override fun onStart() { + super.onStart() + Analytics.eraseContext() + Analytics.send(SignIn.ScreenOpened()) } @Composable diff --git a/features/referral/presentation/src/main/java/com/tangem/feature/referral/analytics/ReferralEvents.kt b/features/referral/presentation/src/main/java/com/tangem/feature/referral/analytics/ReferralEvents.kt index 41d4f8b1c0..e82990adf2 100644 --- a/features/referral/presentation/src/main/java/com/tangem/feature/referral/analytics/ReferralEvents.kt +++ b/features/referral/presentation/src/main/java/com/tangem/feature/referral/analytics/ReferralEvents.kt @@ -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" \ No newline at end of file +private const val REFERRAL_PROGRAM_CATEGORY = "Referral Program" \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/analytics/SwapEvents.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/analytics/SwapEvents.kt index 11dce2daa7..e808db167c 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/analytics/SwapEvents.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/analytics/SwapEvents.kt @@ -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") } From 9e99193f4d499d39fb48669e7ee5d87aad77d308 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 23 Mar 2023 19:30:24 +0300 Subject: [PATCH 04/12] Updated on 2026-08-14 --- .../src/main/java/com/tangem/domain/common/TapWorkarounds.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/domain/src/main/java/com/tangem/domain/common/TapWorkarounds.kt b/domain/src/main/java/com/tangem/domain/common/TapWorkarounds.kt index dabbd0e150..9ac3308197 100644 --- a/domain/src/main/java/com/tangem/domain/common/TapWorkarounds.kt +++ b/domain/src/main/java/com/tangem/domain/common/TapWorkarounds.kt @@ -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 From 268fcf5676a0a39945a013f7227652acd849a50c Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 23 Mar 2023 19:57:37 +0300 Subject: [PATCH 05/12] Updated on 2026-08-14 --- .../model/builders/WalletStoreBuilder.kt | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt b/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt index f5d0d3b6fd..565f8fb826 100644 --- a/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt +++ b/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt @@ -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 { 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), ) From d43e4fb40e87d32a15b0581555076d311bfd6aae Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 27 Mar 2023 13:53:49 +0300 Subject: [PATCH 06/12] Updated on 2026-08-14 --- .../tangem/tap/domain/walletconnect/WalletConnectRepository.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt index 12df7aeb8e..8a5490c31a 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt @@ -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() } } From db1299b6afa95f49dd1c4f7882472ad11990ff55 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 27 Mar 2023 13:54:20 +0300 Subject: [PATCH 07/12] Updated on 2026-08-14 --- .../java/com/tangem/tap/TapApplication.kt | 12 +- .../converters/BasicEventConverter.kt | 174 ------------------ .../analytics/converters/TopUpConverter.kt | 67 +++++++ .../analytics/filters/BasicTopUpFilter.kt | 50 ----- .../common/analytics/filters/TopUpFilter.kt | 72 ++++++++ .../common/analytics/topup/TopUpController.kt | 156 ++++++++++++++++ .../tap/common/extensions/Collections.kt | 4 + .../tap/common/redux/global/GlobalState.kt | 2 + .../com/tangem/tap/domain/TapWalletManager.kt | 3 +- .../WalletCurrenciesManager.kt | 25 +++ .../DefaultWalletCurrenciesManager.kt | 23 ++- .../tap/features/wallet/redux/WalletState.kt | 4 - .../middlewares/MultiWalletMiddleware.kt | 4 +- .../redux/middlewares/WalletMiddleware.kt | 25 +-- .../redux/reducers/MultiWalletReducer.kt | 4 - .../tap/features/wallet/ui/WalletFragment.kt | 10 + .../tap/persistence/ToppedUpWalletStorage.kt | 23 ++- 17 files changed, 386 insertions(+), 272 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/common/analytics/converters/BasicEventConverter.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/converters/TopUpConverter.kt delete mode 100644 app/src/main/java/com/tangem/tap/common/analytics/filters/BasicTopUpFilter.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/filters/TopUpFilter.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 05634eb5fb..4247167482 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -22,7 +22,7 @@ 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.BasicTopUpFilter +import com.tangem.tap.common.analytics.filters.TopUpFilter 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 @@ -158,6 +158,14 @@ class TapApplication : Application(), ImageLoaderFactory { appStateHolder.mainStore = store appStateHolder.userTokensRepository = userTokensRepository appStateHolder.walletStoresManager = walletStoresManager + initTopUpController() + } + + private fun initTopUpController() { + val topUpController = store.state.globalState.topUpController + topUpController.walletStoresManagerProvider = { walletStoresManager } + topUpController.scanResponseProvider = { store.state.globalState.scanResponse } + walletCurrenciesManager.addListener(topUpController) } override fun newImageLoader(): ImageLoader { @@ -186,7 +194,7 @@ class TapApplication : Application(), ImageLoaderFactory { factory.addHandlerBuilder(AppsFlyerAnalyticsHandler.Builder()) factory.addHandlerBuilder(FirebaseAnalyticsHandler.Builder()) - factory.addFilter(BasicTopUpFilter(preferencesStorage.toppedUpWalletStorage)) + factory.addFilter(TopUpFilter(preferencesStorage.toppedUpWalletStorage)) val buildData = AnalyticsHandlerBuilder.Data( application = application, diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/BasicEventConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/BasicEventConverter.kt deleted file mode 100644 index 1e1f9894c8..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/converters/BasicEventConverter.kt +++ /dev/null @@ -1,174 +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 - - 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?, -) { - 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, -) : 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.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 BasicTopUpEventConverter : Converter { - - 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 } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/TopUpConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/TopUpConverter.kt new file mode 100644 index 0000000000..98dc89b12e --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/converters/TopUpConverter.kt @@ -0,0 +1,67 @@ +package com.tangem.tap.common.analytics.converters + +import com.tangem.common.Converter +import com.tangem.common.extensions.isZero +import com.tangem.domain.common.CardTypesResolver +import com.tangem.tap.common.analytics.events.AnalyticsParam +import com.tangem.tap.common.analytics.events.Basic +import com.tangem.tap.common.analytics.filters.TopUpFilter +import com.tangem.tap.domain.model.WalletDataModel +import java.math.BigDecimal + +/** +[REDACTED_AUTHOR] + */ +class TopUpEventConverter : Converter { + + override fun convert(value: Data): Basic.ToppedUp? { + val (cardTypesResolver, walletDataModels, userWalletIdValue, isToppedUpInPast) = value + val paramCardCurrency = ParamCardCurrencyConverter().convert(cardTypesResolver) ?: return null + + val cardBalanceState = BalanceCalculator(walletDataModels).calculate().toCardBalanceState() + val filterData = TopUpFilter.Data( + walletId = userWalletIdValue, + cardBalanceState = cardBalanceState, + isToppedUpInPast = isToppedUpInPast, + ) + return Basic.ToppedUp(paramCardCurrency).apply { + this.filterData = filterData + } + } + + private fun BigDecimal.toCardBalanceState(): AnalyticsParam.CardBalanceState = when { + isZero() -> AnalyticsParam.CardBalanceState.Empty + else -> AnalyticsParam.CardBalanceState.Full + } + + data class Data( + val cardTypesResolver: CardTypesResolver, + val walletDataModels: List, + val userWalletIdValue: String, + val isToppedUpInPast: Boolean, + ) +} + +private interface IBalanceCalculator { + fun calculate(): BigDecimal +} + +private class BalanceCalculator( + private val walletDataModels: List, +) : 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.calculateTotalCryptoAmount(): BigDecimal = this + .map { it.status.amount } + .reduce(BigDecimal::plus) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/filters/BasicTopUpFilter.kt b/app/src/main/java/com/tangem/tap/common/analytics/filters/BasicTopUpFilter.kt deleted file mode 100644 index de89bf9e7e..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/filters/BasicTopUpFilter.kt +++ /dev/null @@ -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 - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/filters/TopUpFilter.kt b/app/src/main/java/com/tangem/tap/common/analytics/filters/TopUpFilter.kt new file mode 100644 index 0000000000..658ab218aa --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/filters/TopUpFilter.kt @@ -0,0 +1,72 @@ +package com.tangem.tap.common.analytics.filters + +import com.tangem.common.extensions.guard +import com.tangem.core.analytics.AnalyticsEvent +import com.tangem.core.analytics.api.AnalyticsEventFilter +import com.tangem.core.analytics.api.AnalyticsHandler +import com.tangem.tap.common.analytics.events.AnalyticsParam +import com.tangem.tap.common.analytics.events.Basic +import com.tangem.tap.common.analytics.topup.log +import com.tangem.tap.common.extensions.dispatchDebugErrorNotification +import com.tangem.tap.persistence.ToppedUpWalletStorage +import com.tangem.tap.store + +/** +[REDACTED_AUTHOR] + */ +class TopUpFilter( + 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 + + if (data.isToppedUpInPast) { + log("BasicTopUpFilter: [false]: sending is blocked") + val newWalletInfo = ToppedUpWalletStorage.Companion.TopupInfo( + walletId = data.walletId, + cardBalanceState = AnalyticsParam.CardBalanceState.Full, + ) + topupWalletStorage.save(newWalletInfo) + return false + } + + val topupInfo = topupWalletStorage.restore(data.walletId).guard { + log("BasicTopUpFilter: [false]: sending is blocked") + val topupInfo = ToppedUpWalletStorage.Companion.TopupInfo( + walletId = data.walletId, + cardBalanceState = data.cardBalanceState, + ) + topupWalletStorage.save(topupInfo) + return false + } + + if (topupInfo.isToppedUp) { + log("BasicTopUpFilter: [false]: sending is blocked") + return false + } + + return if (!topupInfo.isToppedUp && data.isToppedUp) { + log("BasicTopUpFilter: [TRUE]: !topupInfo.isToppedUp && data.isToppedUp") + log("BasicTopUpFilter: [TRUE]: SEND") + store.dispatchDebugErrorNotification("Topped UP sent") + topupWalletStorage.save(topupInfo.copy(cardBalanceState = AnalyticsParam.CardBalanceState.Full)) + true + } else { + log("BasicTopUpFilter: [false]: sending is blocked") + false + } + } + + override fun canBeConsumedByHandler(handler: AnalyticsHandler, event: AnalyticsEvent): Boolean = true + + data class Data( + val walletId: String, + val cardBalanceState: AnalyticsParam.CardBalanceState, + val isToppedUpInPast: Boolean, + ) { + val isToppedUp: Boolean = cardBalanceState == AnalyticsParam.CardBalanceState.Full + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt b/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt new file mode 100644 index 0000000000..3817d79ba5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt @@ -0,0 +1,156 @@ +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.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.scope +import kotlinx.coroutines.launch +import timber.log.Timber + +/** +[REDACTED_AUTHOR] + */ +class TopUpController( + var scanResponseProvider: (() -> ScanResponse?)? = null, + var walletStoresManagerProvider: (() -> WalletStoresManager)? = null, +) : WalletCurrenciesManager.Listener { + + private var hadMissedDerivations: Boolean = false + private val addedCurrencies = mutableListOf() + + override fun willUpdate(userWallet: UserWallet, currency: Currency) { + } + + override fun didUpdate(userWallet: UserWallet, currency: Currency) { + tryToNotify() + } + + override fun willCurrenciesAdd(userWallet: UserWallet, currenciesToAdd: List) { + addedCurrencies.addAll(currenciesToAdd.distinct()) + log("addCurrencies = [${addedCurrencies.joinToString()}]") + } + + override fun willCurrenciesRemove(userWallet: UserWallet, currenciesToRemove: List) { + } + + override fun willCurrencyRemove(userWallet: UserWallet, currencyToRemove: Currency) { + } + + fun walletStoresChanged(walletStores: List) { + val missedDerivations = walletStores + .flatMap { it.walletsData } + .map { it.status } + .filterIsInstance() + + hadMissedDerivations = missedDerivations.isNotEmpty() + log("walletStoresChanged: hadMissedDerivations = [${hadMissedDerivations}]") + } + + fun scanToGetDerivations() { + hadMissedDerivations = true + log("scanAndUpdateCard: hadMissedDerivations = [${hadMissedDerivations}]") + } + + fun addMissingDerivations(blockchains: List) { + hadMissedDerivations = blockchains.isNotEmpty() + log("addMissingDerivations: hadMissedDerivations = [${hadMissedDerivations}]") + } + + fun totalBalanceStateChanged(state: ProgressState) { + log("totalBalanceStateChanged = [${state.name}]") + if (state == ProgressState.Done) { + tryToNotify() + } + } + + fun loadDataSuccess() { + log("loadDataSuccess") + tryToNotify() + } + + private fun tryToNotify() { + if (hadMissedDerivations) { + log("tryToNotify: FAILED: derivationsCheckIsScheduled") + 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()) { + log("tryToNotify: FAILED: walletDataModels.size = [0]") + 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) { + log("tryToNotify: FAILED: by status") + return@launch + } + notify(userWalletId, walletDataModels, scanResponse.cardTypesResolver) + } + } + + private fun notify( + userWalletId: UserWalletId, + walletDataModels: List, + cardTypesResolver: CardTypesResolver, + ) { + val isToppedUpInPast = findToppedUpCurrenciesInPast(walletDataModels).isNotEmpty() + log("notify: currencies from manage tokens had toppedUp in the past = [${isToppedUpInPast}]") + + val data = TopUpEventConverter.Data( + cardTypesResolver = cardTypesResolver, + walletDataModels = walletDataModels, + userWalletIdValue = userWalletId.stringValue, + isToppedUpInPast = isToppedUpInPast, + ) + val event = TopUpEventConverter().convert(data).guard { + log("notify: TopUpEventConverter can't convert a data to an event") + return + } + + log("notify: Analytics.send(event)") + Analytics.send(event) + } + + private fun findToppedUpCurrenciesInPast(walletDataModels: List): List { + log("findToppedUpCurrenciesInPast: added new currencies = [${addedCurrencies.size}]") + 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 + } +} + +fun log(log: String) { + Timber.d("TopUp: %s", log) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt b/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt index 8492ae44f1..517d8ca5ed 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Collections.kt @@ -28,4 +28,8 @@ fun MutableList.replaceBy(item: T, predicate: (T) -> Boolean): Boolean { fun MutableList.replaceByOrAdd(item: T, predicate: (T) -> Boolean) { if (!replaceBy(item, predicate)) add(item) +} + +fun MutableList.copy(): MutableList { + return this.map { it }.toMutableList() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index 56059c6799..14eeac6618 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -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 = TopUpController(), ) : StateType typealias CryptoCurrencyName = String diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 15a3835869..c994e477f6 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -22,7 +22,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 @@ -67,7 +66,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) { diff --git a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/WalletCurrenciesManager.kt b/app/src/main/java/com/tangem/tap/domain/walletCurrencies/WalletCurrenciesManager.kt index 2738438ac1..47d92b26e0 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/WalletCurrenciesManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletCurrencies/WalletCurrenciesManager.kt @@ -69,6 +69,31 @@ interface WalletCurrenciesManager { currenciesToRemove: List, ): CompletionResult + /** + * 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) + fun willCurrenciesRemove(userWallet: UserWallet, currenciesToRemove: List) + fun willCurrencyRemove(userWallet: UserWallet, currencyToRemove: Currency) + } + // For provider companion object } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt b/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt index 5fe6dd8e22..97d9eb72e5 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt @@ -30,23 +30,31 @@ internal class DefaultWalletCurrenciesManager( private val walletManagersRepository: WalletManagersRepository, private val appCurrencyProvider: () -> FiatCurrency, ) : WalletCurrenciesManager { + + private val listeners = mutableListOf() + override suspend fun update( userWallet: UserWallet, currency: Currency, ): CompletionResult = 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 = 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, ): CompletionResult = 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 { + 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 { return withContext(Dispatchers.Default) { walletStoresRepository.getSync(userWalletId) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index 81b306448b..45013512af 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -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 = emptyList(), - val derivationsCheckIsScheduled: Boolean = false, val loadingUserTokens: Boolean = false, val walletCardsCount: Int? = null, val canSaveUserWallets: Boolean = false, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt index 2642cbeb50..52ebe9454f 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt @@ -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, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index 9b5324ead5..b401c9c0fe 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -6,8 +6,6 @@ import com.tangem.blockchain.common.AmountType import com.tangem.common.CompletionResult import com.tangem.common.extensions.guard import com.tangem.core.analytics.Analytics -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 @@ -25,7 +23,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 @@ -46,7 +43,6 @@ import com.tangem.tap.tangemSdkManager import com.tangem.tap.totalFiatBalanceCalculator import com.tangem.tap.userWalletsListManager import com.tangem.tap.userWalletsListManagerSafe -import com.tangem.tap.walletStoresManager import com.tangem.wallet.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -188,7 +184,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) @@ -351,23 +347,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 = scanResponse, - walletState = store.state.walletState, - biometricsWalletDataModels = biometricsWalletDataModels, - ) - BasicEventsPreChecker().tryToSend(converterData) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt index 2dcfeaf198..12d2923899 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt @@ -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) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index fae5543edf..e72b7e284e 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -17,6 +17,7 @@ import androidx.transition.TransitionInflater import by.kirich1409.viewbindingdelegate.viewBinding import coil.load import coil.size.Scale +import com.badoo.mvicore.modelWatcher import com.tangem.core.analytics.Analytics import com.tangem.core.ui.fragments.setStatusBarColor import com.tangem.core.ui.utils.OneTouchClickListener @@ -63,6 +64,14 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber() + private val totalBalanceWatcher = modelWatcher { + (WalletState::totalBalance) { totalBalance -> + totalBalance?.state?.let { + store.state.globalState.topUpController.totalBalanceStateChanged(it) + } + } + } + override fun onAttach(context: Context) { super.onAttach(context) lifecycle.addObserver(viewModel) @@ -167,6 +176,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber {} // we keep the same view unless we scan a card that requires a different view } + totalBalanceWatcher.invoke(state) walletView.swapInteractor = swapInteractor diff --git a/app/src/main/java/com/tangem/tap/persistence/ToppedUpWalletStorage.kt b/app/src/main/java/com/tangem/tap/persistence/ToppedUpWalletStorage.kt index 75759b8d75..c7234832fe 100644 --- a/app/src/main/java/com/tangem/tap/persistence/ToppedUpWalletStorage.kt +++ b/app/src/main/java/com/tangem/tap/persistence/ToppedUpWalletStorage.kt @@ -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 = mutableSetOf() + private val walletList: MutableSet = 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): Boolean { + private fun save(userWallets: MutableSet): Boolean { return try { val json = jsonConverter.toJson(userWallets) preferences.edit(true) { putString(KEY, json) } @@ -41,11 +41,11 @@ class ToppedUpWalletStorage( } } - private fun restore(): MutableSet { + private fun restore(): MutableSet { val json = preferences.getString(KEY, null) ?: return mutableSetOf() return try { - val typedList = jsonConverter.typedList(BasicTopUpFilter.Data::class.java) - val listData = jsonConverter.fromJson>(json, typedList)!! + val typedList = jsonConverter.typedList(TopupInfo::class.java) + val listData = jsonConverter.fromJson>(json, typedList)!! listData.toMutableSet() } catch (ex: Exception) { preferences.edit(true) { remove(KEY) } @@ -55,5 +55,12 @@ class ToppedUpWalletStorage( companion object { private const val KEY = "userWalletsInfo" + + data class TopupInfo( + val walletId: String, + val cardBalanceState: AnalyticsParam.CardBalanceState, + ) { + val isToppedUp: Boolean = cardBalanceState == AnalyticsParam.CardBalanceState.Full + } } } \ No newline at end of file From 9d8611cee3c70432e28903bd84ee9444ce271a2a Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 27 Mar 2023 17:47:53 +0300 Subject: [PATCH 08/12] Updated on 2026-08-14 --- .../java/com/tangem/tap/TapApplication.kt | 17 ++- .../analytics/converters/TopUpConverter.kt | 58 +------ .../common/analytics/filters/TopUpFilter.kt | 72 --------- .../common/analytics/topup/TopUpController.kt | 141 +++++++++++------- .../tap/common/redux/global/GlobalAction.kt | 2 + .../common/redux/global/GlobalMiddleware.kt | 4 + .../tap/common/redux/global/GlobalReducer.kt | 1 + .../tap/common/redux/global/GlobalState.kt | 2 +- .../com/tangem/tap/domain/TapWalletManager.kt | 2 +- .../WalletCurrenciesManager.kt | 10 +- .../note/redux/OnboardingNoteMiddleware.kt | 18 +-- .../redux/OnboardingOtherCardsMiddleware.kt | 1 + .../twins/redux/TwinCardsMiddleware.kt | 17 +-- .../redux/OnboardingWalletMiddleware.kt | 21 +-- .../middlewares/MultiWalletMiddleware.kt | 4 +- .../redux/middlewares/WalletMiddleware.kt | 2 +- .../tap/features/wallet/ui/WalletFragment.kt | 2 +- .../tap/persistence/ToppedUpWalletStorage.kt | 14 +- .../tangem/core/analytics/AnalyticsEvent.kt | 1 - 19 files changed, 146 insertions(+), 243 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/common/analytics/filters/TopUpFilter.kt diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 4247167482..42d008074f 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -22,10 +22,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.TopUpFilter 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 @@ -162,10 +162,15 @@ class TapApplication : Application(), ImageLoaderFactory { } private fun initTopUpController() { - val topUpController = store.state.globalState.topUpController - topUpController.walletStoresManagerProvider = { walletStoresManager } - topUpController.scanResponseProvider = { store.state.globalState.scanResponse } - walletCurrenciesManager.addListener(topUpController) + 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 { @@ -194,8 +199,6 @@ class TapApplication : Application(), ImageLoaderFactory { factory.addHandlerBuilder(AppsFlyerAnalyticsHandler.Builder()) factory.addHandlerBuilder(FirebaseAnalyticsHandler.Builder()) - factory.addFilter(TopUpFilter(preferencesStorage.toppedUpWalletStorage)) - val buildData = AnalyticsHandlerBuilder.Data( application = application, config = config, diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/TopUpConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/TopUpConverter.kt index 98dc89b12e..429b2e5ebc 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/converters/TopUpConverter.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/converters/TopUpConverter.kt @@ -1,67 +1,17 @@ package com.tangem.tap.common.analytics.converters import com.tangem.common.Converter -import com.tangem.common.extensions.isZero import com.tangem.domain.common.CardTypesResolver -import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Basic -import com.tangem.tap.common.analytics.filters.TopUpFilter -import com.tangem.tap.domain.model.WalletDataModel -import java.math.BigDecimal /** [REDACTED_AUTHOR] */ -class TopUpEventConverter : Converter { +class TopUpEventConverter : Converter { - override fun convert(value: Data): Basic.ToppedUp? { - val (cardTypesResolver, walletDataModels, userWalletIdValue, isToppedUpInPast) = value - val paramCardCurrency = ParamCardCurrencyConverter().convert(cardTypesResolver) ?: return null + override fun convert(value: CardTypesResolver): Basic.ToppedUp? { + val paramCardCurrency = ParamCardCurrencyConverter().convert(value) ?: return null - val cardBalanceState = BalanceCalculator(walletDataModels).calculate().toCardBalanceState() - val filterData = TopUpFilter.Data( - walletId = userWalletIdValue, - cardBalanceState = cardBalanceState, - isToppedUpInPast = isToppedUpInPast, - ) - return Basic.ToppedUp(paramCardCurrency).apply { - this.filterData = filterData - } + return Basic.ToppedUp(paramCardCurrency) } - - private fun BigDecimal.toCardBalanceState(): AnalyticsParam.CardBalanceState = when { - isZero() -> AnalyticsParam.CardBalanceState.Empty - else -> AnalyticsParam.CardBalanceState.Full - } - - data class Data( - val cardTypesResolver: CardTypesResolver, - val walletDataModels: List, - val userWalletIdValue: String, - val isToppedUpInPast: Boolean, - ) -} - -private interface IBalanceCalculator { - fun calculate(): BigDecimal -} - -private class BalanceCalculator( - private val walletDataModels: List, -) : 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.calculateTotalCryptoAmount(): BigDecimal = this - .map { it.status.amount } - .reduce(BigDecimal::plus) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/filters/TopUpFilter.kt b/app/src/main/java/com/tangem/tap/common/analytics/filters/TopUpFilter.kt deleted file mode 100644 index 658ab218aa..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/filters/TopUpFilter.kt +++ /dev/null @@ -1,72 +0,0 @@ -package com.tangem.tap.common.analytics.filters - -import com.tangem.common.extensions.guard -import com.tangem.core.analytics.AnalyticsEvent -import com.tangem.core.analytics.api.AnalyticsEventFilter -import com.tangem.core.analytics.api.AnalyticsHandler -import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.Basic -import com.tangem.tap.common.analytics.topup.log -import com.tangem.tap.common.extensions.dispatchDebugErrorNotification -import com.tangem.tap.persistence.ToppedUpWalletStorage -import com.tangem.tap.store - -/** -[REDACTED_AUTHOR] - */ -class TopUpFilter( - 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 - - if (data.isToppedUpInPast) { - log("BasicTopUpFilter: [false]: sending is blocked") - val newWalletInfo = ToppedUpWalletStorage.Companion.TopupInfo( - walletId = data.walletId, - cardBalanceState = AnalyticsParam.CardBalanceState.Full, - ) - topupWalletStorage.save(newWalletInfo) - return false - } - - val topupInfo = topupWalletStorage.restore(data.walletId).guard { - log("BasicTopUpFilter: [false]: sending is blocked") - val topupInfo = ToppedUpWalletStorage.Companion.TopupInfo( - walletId = data.walletId, - cardBalanceState = data.cardBalanceState, - ) - topupWalletStorage.save(topupInfo) - return false - } - - if (topupInfo.isToppedUp) { - log("BasicTopUpFilter: [false]: sending is blocked") - return false - } - - return if (!topupInfo.isToppedUp && data.isToppedUp) { - log("BasicTopUpFilter: [TRUE]: !topupInfo.isToppedUp && data.isToppedUp") - log("BasicTopUpFilter: [TRUE]: SEND") - store.dispatchDebugErrorNotification("Topped UP sent") - topupWalletStorage.save(topupInfo.copy(cardBalanceState = AnalyticsParam.CardBalanceState.Full)) - true - } else { - log("BasicTopUpFilter: [false]: sending is blocked") - false - } - } - - override fun canBeConsumedByHandler(handler: AnalyticsHandler, event: AnalyticsEvent): Boolean = true - - data class Data( - val walletId: String, - val cardBalanceState: AnalyticsParam.CardBalanceState, - val isToppedUpInPast: Boolean, - ) { - val isToppedUp: Boolean = cardBalanceState == AnalyticsParam.CardBalanceState.Full - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt b/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt index 3817d79ba5..e17985be26 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt @@ -7,6 +7,7 @@ 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 @@ -17,9 +18,10 @@ 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 timber.log.Timber +import java.math.BigDecimal /** [REDACTED_AUTHOR] @@ -27,27 +29,18 @@ import timber.log.Timber 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() - override fun willUpdate(userWallet: UserWallet, currency: Currency) { - } - override fun didUpdate(userWallet: UserWallet, currency: Currency) { - tryToNotify() + tryToSend() } override fun willCurrenciesAdd(userWallet: UserWallet, currenciesToAdd: List) { addedCurrencies.addAll(currenciesToAdd.distinct()) - log("addCurrencies = [${addedCurrencies.joinToString()}]") - } - - override fun willCurrenciesRemove(userWallet: UserWallet, currenciesToRemove: List) { - } - - override fun willCurrencyRemove(userWallet: UserWallet, currencyToRemove: Currency) { } fun walletStoresChanged(walletStores: List) { @@ -57,46 +50,34 @@ class TopUpController( .filterIsInstance() hadMissedDerivations = missedDerivations.isNotEmpty() - log("walletStoresChanged: hadMissedDerivations = [${hadMissedDerivations}]") } fun scanToGetDerivations() { hadMissedDerivations = true - log("scanAndUpdateCard: hadMissedDerivations = [${hadMissedDerivations}]") } fun addMissingDerivations(blockchains: List) { hadMissedDerivations = blockchains.isNotEmpty() - log("addMissingDerivations: hadMissedDerivations = [${hadMissedDerivations}]") } fun totalBalanceStateChanged(state: ProgressState) { - log("totalBalanceStateChanged = [${state.name}]") - if (state == ProgressState.Done) { - tryToNotify() - } + if (state == ProgressState.Done) tryToSend() } fun loadDataSuccess() { - log("loadDataSuccess") - tryToNotify() + tryToSend() } - private fun tryToNotify() { - if (hadMissedDerivations) { - log("tryToNotify: FAILED: derivationsCheckIsScheduled") - return - } + 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()) { - log("tryToNotify: FAILED: walletDataModels.size = [0]") - return@launch - } + if (walletDataModels.isEmpty()) return@launch + val isCorrectStatus = walletDataModels.any { it.status is WalletDataModel.Loading || it.status is WalletDataModel.NoAccount || @@ -104,39 +85,70 @@ class TopUpController( it.status is WalletDataModel.MissedDerivation || it.status.isErrorStatus } - if (isCorrectStatus) { - log("tryToNotify: FAILED: by status") + 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 } - notify(userWalletId, walletDataModels, scanResponse.cardTypesResolver) + + val cardBalanceState = BalanceCalculator(walletDataModels).calculate().toCardBalanceState() + send(userWalletId, cardBalanceState, scanResponse.cardTypesResolver) } } - private fun notify( + /** + * 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, - walletDataModels: List, + cardBalanceState: AnalyticsParam.CardBalanceState, cardTypesResolver: CardTypesResolver, ) { - val isToppedUpInPast = findToppedUpCurrenciesInPast(walletDataModels).isNotEmpty() - log("notify: currencies from manage tokens had toppedUp in the past = [${isToppedUpInPast}]") - - val data = TopUpEventConverter.Data( - cardTypesResolver = cardTypesResolver, - walletDataModels = walletDataModels, - userWalletIdValue = userWalletId.stringValue, - isToppedUpInPast = isToppedUpInPast, - ) - val event = TopUpEventConverter().convert(data).guard { - log("notify: TopUpEventConverter can't convert a data to an event") + val topupInfo = topupWalletStorage.restore(userWalletId.stringValue).guard { + val topupInfo = ToppedUpWalletStorage.TopupInfo( + walletId = userWalletId.stringValue, + cardBalanceState = cardBalanceState, + ) + topupWalletStorage.save(topupInfo) return } + if (topupInfo.isToppedUp) return - log("notify: Analytics.send(event)") - Analytics.send(event) + 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): List { - log("findToppedUpCurrenciesInPast: added new currencies = [${addedCurrencies.size}]") val currenciesToppedUpInPast = addedCurrencies.copy() .mapNotNull { currency -> val foundCurrencyModel = walletDataModels @@ -149,8 +161,35 @@ class TopUpController( 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 } -fun log(log: String) { - Timber.d("TopUp: %s", log) +private interface IBalanceCalculator { + fun calculate(): BigDecimal +} + +private class BalanceCalculator( + private val walletDataModels: List, +) : 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.calculateTotalCryptoAmount(): BigDecimal = this + .map { it.status.amount } + .reduce(BigDecimal::plus) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index f0f6e8244c..a140fb2d0c 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -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() diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt index b7fd1db3fe..5d99c5357e 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt @@ -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) + } } } diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt index 3ed3751c74..15b8d10c94 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt @@ -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 diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index 14eeac6618..1d6dddf647 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -29,7 +29,7 @@ data class GlobalState( val exchangeManager: CurrencyExchangeManager = CurrencyExchangeManager.dummy(), val userCountryCode: String? = null, val userWalletsListManager: UserWalletsListManager? = null, - val topUpController: TopUpController = TopUpController(), + val topUpController: TopUpController? = null, ) : StateType typealias CryptoCurrencyName = String diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index c994e477f6..509cd78033 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -66,7 +66,7 @@ class TapWalletManager { .doOnSuccess { Timber.d("Wallet stores fetched for ${userWallet.walletId}") store.dispatchOnMain(WalletAction.LoadData.Success) - store.state.globalState.topUpController.loadDataSuccess() + store.state.globalState.topUpController?.loadDataSuccess() } .doOnFailure { error -> val errorAction = when (error) { diff --git a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/WalletCurrenciesManager.kt b/app/src/main/java/com/tangem/tap/domain/walletCurrencies/WalletCurrenciesManager.kt index 47d92b26e0..fa2bbc0424 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/WalletCurrenciesManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletCurrencies/WalletCurrenciesManager.kt @@ -87,11 +87,11 @@ interface WalletCurrenciesManager { * 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) - fun willCurrenciesRemove(userWallet: UserWallet, currenciesToRemove: List) - fun willCurrencyRemove(userWallet: UserWallet, currencyToRemove: Currency) + fun willUpdate(userWallet: UserWallet, currency: Currency) {} + fun didUpdate(userWallet: UserWallet, currency: Currency) {} + fun willCurrenciesAdd(userWallet: UserWallet, currenciesToAdd: List) {} + fun willCurrenciesRemove(userWallet: UserWallet, currenciesToRemove: List) {} + fun willCurrencyRemove(userWallet: UserWallet, currencyToRemove: Currency) {} } // For provider diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index 8ec11cf17c..fddc46b3a8 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -6,7 +6,6 @@ import com.tangem.core.analytics.Analytics import com.tangem.domain.common.extensions.withMainContext import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.analytics.events.Onboarding import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.dispatchDialogShow @@ -98,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 -> { @@ -111,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 } } } @@ -160,15 +158,7 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch } is OnboardingNoteAction.Balance.Set -> { if (action.balance.balanceIsToppedUp()) { - Analytics.send( - event = Basic.ToppedUp( - currency = AnalyticsParam.CardCurrency.SingleCurrency( - type = AnalyticsParam.CurrencyType.Blockchain( - blockchain = scanResponse.cardTypesResolver.getBlockchain(), - ), - ), - ), - ) + store.state.globalState.topUpController?.send(scanResponse, AnalyticsParam.CardBalanceState.Full) store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.Done)) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt index faa3dd2441..73312bd8a9 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt @@ -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) { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt index f6d021d0f8..e7d4d1edf5 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt @@ -8,7 +8,6 @@ import com.tangem.domain.common.ScanResponse import com.tangem.domain.common.extensions.withMainContext import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.analytics.events.Onboarding import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.dispatchDialogShow @@ -206,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) { @@ -262,18 +263,8 @@ private fun handle(action: Action, dispatch: DispatchFunction) { } is TwinCardsAction.Balance.Set -> { if (action.balance.balanceIsToppedUp()) { - Analytics.send( - event = Basic.ToppedUp( - currency = AnalyticsParam.CardCurrency.SingleCurrency( - type = AnalyticsParam.CurrencyType.Blockchain( - blockchain = getScanResponse().cardTypesResolver.getBlockchain(), - ), - ), - ), - ) - 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 -> { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 50a9f32619..94fe803ccc 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -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,8 +320,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) store.dispatchOnMain(BackupAction.PrepareToWriteBackupCard(action.cardNumber + 1)) } } - is CompletionResult.Failure -> { - } + is CompletionResult.Failure -> Unit } } } @@ -375,7 +370,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) Analytics.send(Onboarding.Finished()) finishCardActivation(notActivatedCardIds) } - else -> {} + else -> Unit } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt index 52ebe9454f..f2cc59a816 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt @@ -91,14 +91,14 @@ class MultiWalletMiddleware { store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingWallet)) } is WalletAction.MultiWallet.AddMissingDerivations -> { - store.state.globalState.topUpController.addMissingDerivations(action.blockchains) + 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() + store.state.globalState.topUpController?.scanToGetDerivations() scanAndUpdateCard(selectedUserWallet, walletState) } else -> {} diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index b401c9c0fe..5d12f94a9a 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -184,7 +184,7 @@ class WalletMiddleware { } is WalletAction.UserWalletChanged -> Unit is WalletAction.WalletStoresChanged -> { - store.state.globalState.topUpController.walletStoresChanged(action.walletStores) + store.state.globalState.topUpController?.walletStoresChanged(action.walletStores) updateWalletStores(action.walletStores, walletState) fetchTotalFiatBalance(action.walletStores) findMissedDerivations(action.walletStores) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index e72b7e284e..f3a6eba304 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -67,7 +67,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber totalBalance?.state?.let { - store.state.globalState.topUpController.totalBalanceStateChanged(it) + store.state.globalState.topUpController?.totalBalanceStateChanged(it) } } } diff --git a/app/src/main/java/com/tangem/tap/persistence/ToppedUpWalletStorage.kt b/app/src/main/java/com/tangem/tap/persistence/ToppedUpWalletStorage.kt index c7234832fe..892b1a2370 100644 --- a/app/src/main/java/com/tangem/tap/persistence/ToppedUpWalletStorage.kt +++ b/app/src/main/java/com/tangem/tap/persistence/ToppedUpWalletStorage.kt @@ -53,14 +53,14 @@ 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" - - data class TopupInfo( - val walletId: String, - val cardBalanceState: AnalyticsParam.CardBalanceState, - ) { - val isToppedUp: Boolean = cardBalanceState == AnalyticsParam.CardBalanceState.Full - } } } \ No newline at end of file diff --git a/core/analytics/src/main/java/com/tangem/core/analytics/AnalyticsEvent.kt b/core/analytics/src/main/java/com/tangem/core/analytics/AnalyticsEvent.kt index e23a503460..1fbc89d977 100644 --- a/core/analytics/src/main/java/com/tangem/core/analytics/AnalyticsEvent.kt +++ b/core/analytics/src/main/java/com/tangem/core/analytics/AnalyticsEvent.kt @@ -8,5 +8,4 @@ open class AnalyticsEvent( val event: String, var params: Map = mapOf(), val error: Throwable? = null, - var filterData: Any? = null, ) \ No newline at end of file From ff30fb541f1a0521fd6bcc3cd345c2921b8f505c Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 29 Mar 2023 21:57:38 +0300 Subject: [PATCH 09/12] Updated on 2026-08-14 --- .../kotlin/com/tangem/plugin/configuration/model/BuildType.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/model/BuildType.kt b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/model/BuildType.kt index b872eb35ff..767e46ba25 100644 --- a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/model/BuildType.kt +++ b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/model/BuildType.kt @@ -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), From f775011234c7adcc6ab1c26929fd52459070b6a9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 29 Mar 2023 21:59:59 +0300 Subject: [PATCH 10/12] Updated on 2026-08-14 --- app/src/debug/res/values-ru/strings.xml | 6 ------ app/src/debug/res/values/strings.xml | 4 ++-- app/src/external/res/values-ru/strings.xml | 6 ------ app/src/external/res/values/strings.xml | 2 +- app/src/internal/res/values-ru/strings.xml | 6 ------ app/src/internal/res/values/strings.xml | 2 +- app/src/main/res/values/strings.xml | 6 ++++++ core/res/src/main/res/values-de/strings.xml | 1 - core/res/src/main/res/values-fr/strings.xml | 1 - core/res/src/main/res/values-it/strings.xml | 1 - core/res/src/main/res/values-ru/strings.xml | 1 - core/res/src/main/res/values-zh-rTW/strings.xml | 1 - core/res/src/main/res/values/strings.xml | 1 - 13 files changed, 10 insertions(+), 28 deletions(-) delete mode 100644 app/src/debug/res/values-ru/strings.xml delete mode 100644 app/src/external/res/values-ru/strings.xml delete mode 100644 app/src/internal/res/values-ru/strings.xml create mode 100644 app/src/main/res/values/strings.xml diff --git a/app/src/debug/res/values-ru/strings.xml b/app/src/debug/res/values-ru/strings.xml deleted file mode 100644 index 9c7f1adda2..0000000000 --- a/app/src/debug/res/values-ru/strings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - DebugTangem - - diff --git a/app/src/debug/res/values/strings.xml b/app/src/debug/res/values/strings.xml index 764dd201cc..0911a552b7 100644 --- a/app/src/debug/res/values/strings.xml +++ b/app/src/debug/res/values/strings.xml @@ -1,6 +1,6 @@ - DebugTangem + Debug Tangem - \ No newline at end of file + diff --git a/app/src/external/res/values-ru/strings.xml b/app/src/external/res/values-ru/strings.xml deleted file mode 100644 index 3a1e80c609..0000000000 --- a/app/src/external/res/values-ru/strings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - ExternalTangem - - diff --git a/app/src/external/res/values/strings.xml b/app/src/external/res/values/strings.xml index 3a1e80c609..1e2d2f4d56 100644 --- a/app/src/external/res/values/strings.xml +++ b/app/src/external/res/values/strings.xml @@ -1,6 +1,6 @@ - ExternalTangem + External Tangem diff --git a/app/src/internal/res/values-ru/strings.xml b/app/src/internal/res/values-ru/strings.xml deleted file mode 100644 index 76df805cb3..0000000000 --- a/app/src/internal/res/values-ru/strings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - InternalTangem - - diff --git a/app/src/internal/res/values/strings.xml b/app/src/internal/res/values/strings.xml index 76df805cb3..4ba0d6fad8 100644 --- a/app/src/internal/res/values/strings.xml +++ b/app/src/internal/res/values/strings.xml @@ -1,6 +1,6 @@ - InternalTangem + Internal Tangem diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000..250461aa6b --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,6 @@ + + + + Tangem + + diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index a0dd22a168..f4ba8d958a 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -358,7 +358,6 @@ Choose token Your tokens not available - Tangem Hide You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page. Hide %s diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index 43efbd281e..8c367d88a4 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -358,7 +358,6 @@ Choose token Your tokens not available - Tangem Hide You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page. Hide %s diff --git a/core/res/src/main/res/values-it/strings.xml b/core/res/src/main/res/values-it/strings.xml index b1209fc917..53671cbc35 100644 --- a/core/res/src/main/res/values-it/strings.xml +++ b/core/res/src/main/res/values-it/strings.xml @@ -358,7 +358,6 @@ Choose token Your tokens not available - Tangem Hide You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page. Hide %s diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 41175e518c..0d2c66f7d1 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -359,7 +359,6 @@ Выберите токен Ваши токены не доступен - Tangem Скрыть Вы скрываете токен с главного экрана, но в любой момент сможете добавить его обратно через страницу управления токенами. Скрыть %s diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml index dcc7b86b54..4758954e64 100644 --- a/core/res/src/main/res/values-zh-rTW/strings.xml +++ b/core/res/src/main/res/values-zh-rTW/strings.xml @@ -358,7 +358,6 @@ 選擇代幣 您的代幣 無法使用 - Tangem 隱藏 您即將在主屏幕上隱藏此代幣。您可以隨時通過管理代幣頁面將其添加回來。 隱藏 %s diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 7425f3b247..307ced0569 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -359,7 +359,6 @@ Choose token Your tokens not available - Tangem Hide You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page. Hide %s From 3f4c8acf8ed4a34004b3328f0e7fef328465d13a Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 29 Mar 2023 22:00:29 +0300 Subject: [PATCH 11/12] Updated on 2026-08-14 --- build.gradle.kts | 63 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index aa62254f1d..91904f4d34 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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() + + 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() + + 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) } \ No newline at end of file From 9580e9c3343428e12d561d8808cfaeeebd5a6a49 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 30 Mar 2023 16:13:16 +0300 Subject: [PATCH 12/12] Updated on 2026-08-14 --- .../converters/{TopUpConverter.kt => TopUpEventConverter.kt} | 0 .../tap/features/wallet/redux/middlewares/WalletMiddleware.kt | 1 - 2 files changed, 1 deletion(-) rename app/src/main/java/com/tangem/tap/common/analytics/converters/{TopUpConverter.kt => TopUpEventConverter.kt} (100%) diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/TopUpConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/TopUpEventConverter.kt similarity index 100% rename from app/src/main/java/com/tangem/tap/common/analytics/converters/TopUpConverter.kt rename to app/src/main/java/com/tangem/tap/common/analytics/converters/TopUpEventConverter.kt diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index b0d8086014..fafa99c7b3 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -42,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