From 9fd13fdc4676039369ce6ecaa43ba5c68c448508 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 21 Jan 2026 14:21:10 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../appsflyer/AppsFlyerDeepLinkListener.kt | 2 +- .../appsflyer/AppsFlyerAnalyticsClient.kt | 4 +- .../AppsFlyerDeepLinkListenerTest.kt | 90 +++++++++---------- .../appsflyer/AppsFlyerConversionStore.kt | 2 + .../DefaultAppsFlyerConversionStore.kt | 11 +++ .../wallets/DefaultWalletsPromoRepository.kt | 89 ++++++++---------- .../data/wallets/di/WalletsDataModule.kt | 2 - .../repository/WalletsPromoRepository.kt | 9 +- .../usecase/BindRefcodeWithWalletUseCase.kt | 60 +++---------- .../deeplink/DefaultPromoDeeplinkHandler.kt | 38 ++++---- .../DefaultPromoDeeplinkHandlerTest.kt | 18 ++++ 11 files changed, 149 insertions(+), 176 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/analytics/appsflyer/AppsFlyerDeepLinkListener.kt b/app/src/main/java/com/tangem/tap/common/analytics/appsflyer/AppsFlyerDeepLinkListener.kt index 2b3e47076b..f656143400 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/appsflyer/AppsFlyerDeepLinkListener.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/appsflyer/AppsFlyerDeepLinkListener.kt @@ -73,7 +73,7 @@ class AppsFlyerDeepLinkListener @Inject constructor( private fun storeConversionData(refcode: String, campaign: String?) { coroutineScope.launch { mutex.withLock { - appsFlyerConversionStore.store( + appsFlyerConversionStore.storeIfAbsent( value = AppsFlyerConversionData(refcode = refcode, campaign = campaign), ) } diff --git a/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsflyer/AppsFlyerAnalyticsClient.kt b/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsflyer/AppsFlyerAnalyticsClient.kt index 27ced0a864..07b0b64fc1 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsflyer/AppsFlyerAnalyticsClient.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsflyer/AppsFlyerAnalyticsClient.kt @@ -30,10 +30,10 @@ class AppsFlyerClient @AssistedInject constructor( setAppId(context.packageName) setDebugLog(true) - init(apiKey, ConversionListener, context) - subscribeForDeepLink(appsFlyerDeepLinkListener) + init(apiKey, ConversionListener, context) + Timber.i("Starting AppsFlyer SDK") start(context, apiKey, InitializationListener) Timber.i("AppsFlyer SDK started") diff --git a/app/src/test/kotlin/com/tangem/tap/common/analytics/appsflyer/AppsFlyerDeepLinkListenerTest.kt b/app/src/test/kotlin/com/tangem/tap/common/analytics/appsflyer/AppsFlyerDeepLinkListenerTest.kt index d940d4fdfa..cce586770c 100644 --- a/app/src/test/kotlin/com/tangem/tap/common/analytics/appsflyer/AppsFlyerDeepLinkListenerTest.kt +++ b/app/src/test/kotlin/com/tangem/tap/common/analytics/appsflyer/AppsFlyerDeepLinkListenerTest.kt @@ -39,57 +39,57 @@ class AppsFlyerDeepLinkListenerTest { fun onDeepLinking(model: OnDeepLinkingModel) = runTest { listener.onDeepLinking(p0 = model.deepLinkResult) - if (model.shouldStore) { - val value = AppsFlyerConversionData(refcode = SUCCESS_REFCODE, campaign = SUCCESS_CAMPAIGN) - coVerify { appsFlyerConversionStore.store(value = value) } - } else { - coVerify(inverse = true) { appsFlyerConversionStore.store(value = any()) } - } + if (model.shouldStore) { + val value = AppsFlyerConversionData(refcode = SUCCESS_REFCODE, campaign = SUCCESS_CAMPAIGN) + coVerify { appsFlyerConversionStore.storeIfAbsent(value = value) } + } else { + coVerify(inverse = true) { appsFlyerConversionStore.storeIfAbsent(value = any()) } } + } private fun provideTestModels(): List { - return listOf( - // DeepLinkResult.Status.NOT_FOUND - OnDeepLinkingModel( - deepLinkResult = DeepLinkResult(null, null), - shouldStore = false, + return listOf( + // DeepLinkResult.Status.NOT_FOUND + OnDeepLinkingModel( + deepLinkResult = DeepLinkResult(null, null), + shouldStore = false, + ), + // DeepLinkResult.Status.ERROR + OnDeepLinkingModel( + deepLinkResult = DeepLinkResult(null, DeepLinkResult.Error.NETWORK), + shouldStore = false, + ), + // DeepLinkResult.Status.FOUND + OnDeepLinkingModel( + deepLinkResult = createFoundDeepLink( + deepLinkValue = AppsFlyerDeepLinkListener.REFERRAL_DEEP_LINK_VALUE, + refcode = SUCCESS_REFCODE, + campaign = SUCCESS_CAMPAIGN, ), - // DeepLinkResult.Status.ERROR - OnDeepLinkingModel( - deepLinkResult = DeepLinkResult(null, DeepLinkResult.Error.NETWORK), - shouldStore = false, + shouldStore = true, + ), + OnDeepLinkingModel( + deepLinkResult = createFoundDeepLink( + deepLinkValue = AppsFlyerDeepLinkListener.REFERRAL_DEEP_LINK_VALUE, + refcode = "", + campaign = SUCCESS_CAMPAIGN, ), - // DeepLinkResult.Status.FOUND - OnDeepLinkingModel( - deepLinkResult = createFoundDeepLink( - deepLinkValue = AppsFlyerDeepLinkListener.REFERRAL_DEEP_LINK_VALUE, - refcode = SUCCESS_REFCODE, - campaign = SUCCESS_CAMPAIGN, - ), - shouldStore = true, + shouldStore = false, + ), + OnDeepLinkingModel( + deepLinkResult = createFoundDeepLink( + deepLinkValue = AppsFlyerDeepLinkListener.REFERRAL_DEEP_LINK_VALUE, + refcode = null, + campaign = SUCCESS_CAMPAIGN, ), - OnDeepLinkingModel( - deepLinkResult = createFoundDeepLink( - deepLinkValue = AppsFlyerDeepLinkListener.REFERRAL_DEEP_LINK_VALUE, - refcode = "", - campaign = SUCCESS_CAMPAIGN, - ), - shouldStore = false, - ), - OnDeepLinkingModel( - deepLinkResult = createFoundDeepLink( - deepLinkValue = AppsFlyerDeepLinkListener.REFERRAL_DEEP_LINK_VALUE, - refcode = null, - campaign = SUCCESS_CAMPAIGN, - ), - shouldStore = false, - ), - OnDeepLinkingModel( - deepLinkResult = createFoundDeepLink(deepLinkValue = "some_other_deep_link_value"), - shouldStore = false, - ), - ) - } + shouldStore = false, + ), + OnDeepLinkingModel( + deepLinkResult = createFoundDeepLink(deepLinkValue = "some_other_deep_link_value"), + shouldStore = false, + ), + ) + } data class OnDeepLinkingModel( val deepLinkResult: DeepLinkResult, diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/appsflyer/AppsFlyerConversionStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/appsflyer/AppsFlyerConversionStore.kt index 99fa70fc9b..300c4c6f20 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/appsflyer/AppsFlyerConversionStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/appsflyer/AppsFlyerConversionStore.kt @@ -7,4 +7,6 @@ interface AppsFlyerConversionStore { suspend fun get(): AppsFlyerConversionData? suspend fun store(value: AppsFlyerConversionData) + + suspend fun storeIfAbsent(value: AppsFlyerConversionData) } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/appsflyer/DefaultAppsFlyerConversionStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/appsflyer/DefaultAppsFlyerConversionStore.kt index 50a7e53100..c6a8044516 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/appsflyer/DefaultAppsFlyerConversionStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/appsflyer/DefaultAppsFlyerConversionStore.kt @@ -29,6 +29,17 @@ internal class DefaultAppsFlyerConversionStore( appPreferencesStore.storeObject(KEY, dto) } + override suspend fun storeIfAbsent(value: AppsFlyerConversionData) { + Timber.i("Storing conversion data to store if absent: $value") + + val dto = appPreferencesStore.getObjectSyncOrNull(KEY) + + if (dto == null) { + Timber.i("Conversion data is absent, storing $value") + appPreferencesStore.storeObject(KEY, ConversionDataConverter.convert(value)) + } + } + private companion object { val KEY = stringPreferencesKey("APPS_FLYER_CONVERSION_DATA") diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsPromoRepository.kt b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsPromoRepository.kt index 23738373d2..de2bbc191e 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsPromoRepository.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsPromoRepository.kt @@ -1,15 +1,11 @@ package com.tangem.data.wallets import androidx.datastore.preferences.core.stringPreferencesKey -import arrow.core.Option -import arrow.core.toOption import com.squareup.moshi.Json import com.squareup.moshi.JsonClass import com.tangem.datasource.api.common.response.ApiResponse -import com.tangem.datasource.api.common.response.getOrThrow import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.api.tangemTech.models.BindWalletsByReferralCodeBody -import com.tangem.datasource.local.appsflyer.AppsFlyerConversionStore import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull import com.tangem.datasource.local.preferences.utils.storeObject @@ -17,7 +13,6 @@ import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.wallets.models.AppsFlyerConversionData import com.tangem.domain.wallets.repository.WalletsPromoRepository import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import com.tangem.utils.coroutines.runSuspendCatching import kotlinx.coroutines.withContext import timber.log.Timber @@ -25,69 +20,57 @@ internal class DefaultWalletsPromoRepository( private val appPreferencesStore: AppPreferencesStore, private val tangemTechApi: TangemTechApi, private val userWalletsStore: UserWalletsStore, - private val appsFlyerConversionStore: AppsFlyerConversionStore, private val dispatchers: CoroutineDispatcherProvider, ) : WalletsPromoRepository { - override suspend fun getConversionData(): Option { - return runSuspendCatching { appsFlyerConversionStore.get() }.getOrNull().toOption() + override suspend fun bindRefcodeWithWallets(conversionData: AppsFlyerConversionData) = withContext(dispatchers.io) { + val savedBindingData = getSavedBindingData() + + when { + savedBindingData == null -> { + // first time binding + saveBindingData(refcode = conversionData.refcode, campaign = conversionData.campaign) + bind(refcode = conversionData.refcode, campaign = conversionData.campaign) + } + !savedBindingData.isDone -> { + // try again + bind(refcode = savedBindingData.refcode, campaign = savedBindingData.campaign) + } + } } - override suspend fun saveConversionData(refcode: String, campaign: String?) { - val data = AppsFlyerConversionData(refcode = refcode, campaign = campaign) + override suspend fun retryBindRefcodeWithWallets() { + val savedBindingData = getSavedBindingData() - appsFlyerConversionStore.store(data) + if (savedBindingData != null) { + bind(refcode = savedBindingData.refcode, campaign = savedBindingData.campaign) + } else { + Timber.i("retryBindRefcodeWithWallets: Binding data isn't required") + } } - override suspend fun bindRefcodeWithWallets(refcode: String, campaign: String?) = withContext(dispatchers.io) { + private suspend fun getSavedBindingData(): ReferralWalletsBindingData? { + return appPreferencesStore.getObjectSyncOrNull(key = REFERRAL_WALLETS_BINDING_DATA_KEY) + } + + private suspend fun saveBindingData(refcode: String, campaign: String?) { + val bindingData = ReferralWalletsBindingData(refcode = refcode, campaign = campaign, isDone = false) + + appPreferencesStore.storeObject(key = REFERRAL_WALLETS_BINDING_DATA_KEY, value = bindingData) + } + + private suspend fun bind(refcode: String, campaign: String?) { val walletIds = userWalletsStore.userWalletsSync.map { it.walletId.stringValue } val result = tangemTechApi.bindWalletsByReferralCode( - body = BindWalletsByReferralCodeBody( - walletIds = walletIds, - refcode = refcode, - campaign = campaign, - ), + body = BindWalletsByReferralCodeBody(walletIds = walletIds, refcode = refcode, campaign = campaign), ) - val bindingData = ReferralWalletsBindingData( - refcode = refcode, - campaign = campaign, - isDone = result is ApiResponse.Success, - ) + if (result is ApiResponse.Success) { + val successData = ReferralWalletsBindingData(refcode = refcode, campaign = campaign, isDone = true) - appPreferencesStore.storeObject(key = REFERRAL_WALLETS_BINDING_DATA_KEY, value = bindingData) - - result.getOrThrow() - } - - override suspend fun bindSavedRefcodeWithWallets(): AppsFlyerConversionData { - val bindingData = appPreferencesStore.getObjectSyncOrNull( - key = REFERRAL_WALLETS_BINDING_DATA_KEY, - ) - - if (bindingData == null) { - val exception = IllegalStateException("No saved referral wallets binding data found") - Timber.e(exception) - throw exception + appPreferencesStore.storeObject(key = REFERRAL_WALLETS_BINDING_DATA_KEY, value = successData) } - - val conversionData = AppsFlyerConversionData( - refcode = bindingData.refcode, - campaign = bindingData.campaign, - ) - - if (bindingData.isDone) { - Timber.i("Referral code ${bindingData.refcode} already bound with wallets") - return conversionData - } - - bindRefcodeWithWallets( - refcode = bindingData.refcode, - campaign = bindingData.campaign, - ) - - return conversionData } @JsonClass(generateAdapter = true) diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt b/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt index 5023a6099e..b36b580da9 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt @@ -75,14 +75,12 @@ internal object WalletsDataModule { appPreferencesStore: AppPreferencesStore, tangemTechApi: TangemTechApi, userWalletsStore: UserWalletsStore, - appsFlyerConversionStore: AppsFlyerConversionStore, dispatchers: CoroutineDispatcherProvider, ): WalletsPromoRepository { return DefaultWalletsPromoRepository( appPreferencesStore = appPreferencesStore, tangemTechApi = tangemTechApi, userWalletsStore = userWalletsStore, - appsFlyerConversionStore = appsFlyerConversionStore, dispatchers = dispatchers, ) } diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsPromoRepository.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsPromoRepository.kt index e07fb8e436..d14afae786 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsPromoRepository.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletsPromoRepository.kt @@ -1,15 +1,10 @@ package com.tangem.domain.wallets.repository -import arrow.core.Option import com.tangem.domain.wallets.models.AppsFlyerConversionData interface WalletsPromoRepository { - suspend fun getConversionData(): Option + suspend fun bindRefcodeWithWallets(conversionData: AppsFlyerConversionData) - suspend fun saveConversionData(refcode: String, campaign: String?) - - suspend fun bindRefcodeWithWallets(refcode: String, campaign: String?) - - suspend fun bindSavedRefcodeWithWallets(): AppsFlyerConversionData + suspend fun retryBindRefcodeWithWallets() } \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/BindRefcodeWithWalletUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/BindRefcodeWithWalletUseCase.kt index ec454d0aa9..50ea60f794 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/BindRefcodeWithWalletUseCase.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/BindRefcodeWithWalletUseCase.kt @@ -1,10 +1,8 @@ package com.tangem.domain.wallets.usecase import arrow.core.Either -import arrow.core.raise.Raise import arrow.core.raise.catch import arrow.core.raise.either -import arrow.core.raise.ensure import com.tangem.domain.wallets.models.AppsFlyerConversionData import com.tangem.domain.wallets.repository.WalletsPromoRepository import kotlinx.coroutines.sync.Mutex @@ -17,59 +15,21 @@ class BindRefcodeWithWalletUseCase @Inject constructor( private val mutex = Mutex() - suspend operator fun invoke(refcode: String, campaign: String?): Either = either { - ensure(refcode.isNotBlank()) { Error.InvalidRefcode } - + suspend operator fun invoke(conversionData: AppsFlyerConversionData): Either = either { mutex.withLock { - checkSavedRefcode() - - bindRefcode(refcode = refcode, campaign = campaign) - - saveConversionData(refcode = refcode, campaign = campaign) + catch( + block = { walletsPromoRepository.bindRefcodeWithWallets(conversionData) }, + catch = ::raise, + ) } } - suspend fun retry(): Either = either { + suspend fun retry(): Either = either { mutex.withLock { - val conversionData = tryBindRefcodeAgain() - - saveConversionData(refcode = conversionData.refcode, campaign = conversionData.campaign) + catch( + block = { walletsPromoRepository.retryBindRefcodeWithWallets() }, + catch = ::raise, + ) } } - - private suspend fun Raise.checkSavedRefcode() { - walletsPromoRepository.getConversionData().onSome { - raise(Error.RefcodeAlreadySaved) - } - } - - private suspend fun Raise.bindRefcode(refcode: String, campaign: String?) { - catch( - block = { walletsPromoRepository.bindRefcodeWithWallets(refcode, campaign) }, - catch = { raise(Error.DataError(it)) }, - ) - } - - private suspend fun Raise.tryBindRefcodeAgain(): AppsFlyerConversionData { - return catch( - block = { walletsPromoRepository.bindSavedRefcodeWithWallets() }, - catch = { raise(Error.DataError(it)) }, - ) - } - - private suspend fun Raise.saveConversionData(refcode: String, campaign: String?) { - catch( - block = { walletsPromoRepository.saveConversionData(refcode, campaign) }, - catch = { raise(Error.DataError(it)) }, - ) - } - - sealed interface Error { - - data object InvalidRefcode : Error - - data object RefcodeAlreadySaved : Error - - data class DataError(val throwable: Throwable) : Error - } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/deeplink/DefaultPromoDeeplinkHandler.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/deeplink/DefaultPromoDeeplinkHandler.kt index 2c0d3c82e8..1592886d35 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/deeplink/DefaultPromoDeeplinkHandler.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/deeplink/DefaultPromoDeeplinkHandler.kt @@ -10,6 +10,7 @@ import com.tangem.core.ui.extensions.mask import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.message.DialogMessage import com.tangem.core.ui.message.GlobalLoadingMessage +import com.tangem.datasource.local.appsflyer.AppsFlyerConversionStore import com.tangem.domain.models.network.NetworkStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.networks.multi.MultiNetworkStatusProducer @@ -18,6 +19,7 @@ import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesProducer import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier import com.tangem.domain.wallets.PromoCodeActivationResult import com.tangem.domain.wallets.PromoCodeActivationResult.* +import com.tangem.domain.wallets.models.AppsFlyerConversionData import com.tangem.domain.wallets.models.errors.ActivatePromoCodeError import com.tangem.domain.wallets.usecase.ActivateBitcoinPromocodeUseCase import com.tangem.domain.wallets.usecase.BindRefcodeWithWalletUseCase @@ -46,6 +48,7 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor( private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, private val activateBitcoinPromocodeUseCase: ActivateBitcoinPromocodeUseCase, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, + private val appsFlyerConversionStore: AppsFlyerConversionStore, private val bindRefcodeWithWalletUseCase: BindRefcodeWithWalletUseCase, private val analyticsEventsHandler: AnalyticsEventHandler, private val dispatchers: CoroutineDispatcherProvider, @@ -62,6 +65,8 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor( } else { findSelectedWallet(promoCode) } + + saveAndBindRefcode() } private fun findSelectedWallet(promoCode: String) { @@ -92,13 +97,9 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor( Timber.tag(LOG_TAG).d("All user network statuses ${networkStatuses?.size}") - val cryptoCurrencies = multiWalletCryptoCurrenciesSupplier - .getSyncOrNull( - MultiWalletCryptoCurrenciesProducer.Params( - userWallet - .walletId, - ), - ) + val cryptoCurrencies = multiWalletCryptoCurrenciesSupplier.getSyncOrNull( + MultiWalletCryptoCurrenciesProducer.Params(userWallet.walletId), + ) Timber.tag(LOG_TAG).d("All user cryptoCurrencies on main ${cryptoCurrencies?.size}") @@ -106,8 +107,7 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor( Timber.tag(LOG_TAG).d("BitcoinCurrency $bitcoinCurrency") val bitcoinStatus = networkStatuses?.firstOrNull { status -> - status.network.id == bitcoinCurrency - ?.network?.id + status.network.id == bitcoinCurrency?.network?.id } Timber.tag(LOG_TAG).d("BitcoinStatus $bitcoinStatus") @@ -129,13 +129,6 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor( "Start activation promoCode ${promoCode.mask()} address ${bitcoinAddress.mask()}", ) - if (refcode != null) { - launch { - bindRefcodeWithWalletUseCase(refcode = refcode, campaign = campaign) - .onLeft { Timber.e("Failed to bind refcode with wallets: $it") } - } - } - activatePromoCode(bitcoinAddress = bitcoinAddress, promoCode = promoCode) } else { uiMessageSender.send(GlobalLoadingMessage(false)) @@ -196,6 +189,19 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor( ) } + private fun saveAndBindRefcode() { + scope.launch(dispatchers.default) { + if (!refcode.isNullOrBlank()) { + val conversionData = AppsFlyerConversionData(refcode = refcode, campaign = campaign) + + appsFlyerConversionStore.storeIfAbsent(value = conversionData) + + bindRefcodeWithWalletUseCase(conversionData) + .onLeft { Timber.e("Failed to bind refcode with wallets: $it") } + } + } + } + @AssistedFactory interface Factory : PromoDeeplinkHandler.Factory { override fun create( diff --git a/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/presentation/wallet/deeplink/DefaultPromoDeeplinkHandlerTest.kt b/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/presentation/wallet/deeplink/DefaultPromoDeeplinkHandlerTest.kt index 126be8d9a7..1c8c5e7577 100644 --- a/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/presentation/wallet/deeplink/DefaultPromoDeeplinkHandlerTest.kt +++ b/features/wallet/impl/src/test/kotlin/com/tangem/feature/wallet/presentation/wallet/deeplink/DefaultPromoDeeplinkHandlerTest.kt @@ -12,6 +12,7 @@ import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.message.DialogMessage +import com.tangem.datasource.local.appsflyer.AppsFlyerConversionStore import com.tangem.domain.models.StatusSource import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network @@ -70,6 +71,9 @@ class DefaultPromoDeeplinkHandlerTest { @MockK private lateinit var bindRefcodeWithWalletUseCase: BindRefcodeWithWalletUseCase + @MockK + private lateinit var appsFlyerConversionStore: AppsFlyerConversionStore + private lateinit var messages: MutableList @Before @@ -110,6 +114,7 @@ class DefaultPromoDeeplinkHandlerTest { activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase, + appsFlyerConversionStore = appsFlyerConversionStore, analyticsEventsHandler = analyticsEventHandler, dispatchers = dispatcherProvider, ) @@ -143,6 +148,7 @@ class DefaultPromoDeeplinkHandlerTest { activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase, + appsFlyerConversionStore = appsFlyerConversionStore, analyticsEventsHandler = analyticsEventHandler, dispatchers = TestingCoroutineDispatcherProvider(), ) @@ -175,6 +181,7 @@ class DefaultPromoDeeplinkHandlerTest { activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase, + appsFlyerConversionStore = appsFlyerConversionStore, analyticsEventsHandler = analyticsEventHandler, dispatchers = TestingCoroutineDispatcherProvider(), ) @@ -213,6 +220,7 @@ class DefaultPromoDeeplinkHandlerTest { activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase, + appsFlyerConversionStore = appsFlyerConversionStore, analyticsEventsHandler = analyticsEventHandler, dispatchers = dispatcherProvider, ) @@ -255,6 +263,7 @@ class DefaultPromoDeeplinkHandlerTest { activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase, + appsFlyerConversionStore = appsFlyerConversionStore, analyticsEventsHandler = analyticsEventHandler, dispatchers = dispatcherProvider, ) @@ -372,6 +381,7 @@ class DefaultPromoDeeplinkHandlerTest { activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase, + appsFlyerConversionStore = appsFlyerConversionStore, analyticsEventsHandler = analyticsEventHandler, dispatchers = dispatcherProvider, ) @@ -404,6 +414,7 @@ class DefaultPromoDeeplinkHandlerTest { activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase, + appsFlyerConversionStore = appsFlyerConversionStore, analyticsEventsHandler = analyticsEventHandler, dispatchers = dispatcherProvider, ) @@ -445,6 +456,7 @@ class DefaultPromoDeeplinkHandlerTest { activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase, + appsFlyerConversionStore = appsFlyerConversionStore, analyticsEventsHandler = analyticsEventHandler, dispatchers = dispatcherProvider, ) @@ -507,6 +519,7 @@ class DefaultPromoDeeplinkHandlerTest { activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase, + appsFlyerConversionStore = appsFlyerConversionStore, analyticsEventsHandler = analyticsEventHandler, dispatchers = dispatcherProvider, ) @@ -560,6 +573,7 @@ class DefaultPromoDeeplinkHandlerTest { activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase, + appsFlyerConversionStore = appsFlyerConversionStore, analyticsEventsHandler = analyticsEventHandler, dispatchers = dispatcherProvider, ) @@ -619,6 +633,7 @@ class DefaultPromoDeeplinkHandlerTest { activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase, + appsFlyerConversionStore = appsFlyerConversionStore, analyticsEventsHandler = analyticsEventHandler, dispatchers = dispatcherProvider, ) @@ -677,6 +692,7 @@ class DefaultPromoDeeplinkHandlerTest { activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase, + appsFlyerConversionStore = appsFlyerConversionStore, analyticsEventsHandler = analyticsEventHandler, dispatchers = dispatcherProvider, ) @@ -731,6 +747,7 @@ class DefaultPromoDeeplinkHandlerTest { activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase, + appsFlyerConversionStore = appsFlyerConversionStore, analyticsEventsHandler = analyticsEventHandler, dispatchers = dispatcherProvider, ) @@ -785,6 +802,7 @@ class DefaultPromoDeeplinkHandlerTest { activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase, + appsFlyerConversionStore = appsFlyerConversionStore, analyticsEventsHandler = analyticsEventHandler, dispatchers = dispatcherProvider, )