Updated on 2026-08-14

This commit is contained in:
Tangem 2026-01-26 18:15:16 +02:00
commit 895f304420
43 changed files with 1296 additions and 131 deletions

View file

@ -103,6 +103,7 @@ internal class WalletModel @Inject constructor(
private val singleAccountListSupplier: SingleAccountListSupplier,
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
private val feedFeatureToggle: FeedFeatureToggle,
private val bindRefcodeWithWalletUseCase: BindRefcodeWithWalletUseCase,
val screenLifecycleProvider: ScreenLifecycleProvider,
val innerWalletRouter: InnerWalletRouter,
) : Model() {
@ -137,6 +138,11 @@ internal class WalletModel @Inject constructor(
enableNotificationsIfNeeded()
clickIntents.initialize(innerWalletRouter, modelScope)
modelScope.launch {
bindRefcodeWithWalletUseCase.retry()
.onLeft { Timber.e("Failed to bind refcode with wallets: $it") }
}
}
fun onResume() {

View file

@ -1,6 +1,7 @@
package com.tangem.feature.wallet.deeplink
import com.tangem.blockchain.common.Blockchain
import com.tangem.common.routing.deeplink.DeeplinkConst
import com.tangem.common.routing.deeplink.DeeplinkConst.PROMO_CODE_KEY
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.GlobalUiMessageSender
@ -9,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
@ -17,8 +19,10 @@ 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
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.feature.wallet.deeplink.analytics.PromoActivationAnalytics
import com.tangem.feature.wallet.impl.R
@ -44,10 +48,15 @@ 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,
) : PromoDeeplinkHandler {
private val refcode: String? = queryParams[DeeplinkConst.REF_KEY]
private val campaign: String? = queryParams[DeeplinkConst.CAMPAIGN_KEY]
init {
analyticsEventsHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart())
val promoCode = queryParams[PROMO_CODE_KEY].orEmpty()
@ -56,6 +65,8 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor(
} else {
findSelectedWallet(promoCode)
}
saveAndBindRefcode()
}
private fun findSelectedWallet(promoCode: String) {
@ -86,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}")
@ -100,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")
@ -122,6 +128,7 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor(
Timber.tag(LOG_TAG).d(
"Start activation promoCode ${promoCode.mask()} address ${bitcoinAddress.mask()}",
)
activatePromoCode(bitcoinAddress = bitcoinAddress, promoCode = promoCode)
} else {
uiMessageSender.send(GlobalLoadingMessage(false))
@ -182,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(

View file

@ -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
@ -25,6 +26,7 @@ import com.tangem.domain.wallets.PromoCodeActivationResult
import com.tangem.domain.wallets.models.GetUserWalletError
import com.tangem.domain.wallets.models.errors.ActivatePromoCodeError
import com.tangem.domain.wallets.usecase.ActivateBitcoinPromocodeUseCase
import com.tangem.domain.wallets.usecase.BindRefcodeWithWalletUseCase
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.feature.wallet.deeplink.DefaultPromoDeeplinkHandler
import com.tangem.feature.wallet.deeplink.analytics.PromoActivationAnalytics
@ -66,6 +68,12 @@ class DefaultPromoDeeplinkHandlerTest {
@MockK
private lateinit var analyticsEventHandler: AnalyticsEventHandler
@MockK
private lateinit var bindRefcodeWithWalletUseCase: BindRefcodeWithWalletUseCase
@MockK
private lateinit var appsFlyerConversionStore: AppsFlyerConversionStore
private lateinit var messages: MutableList<UiMessage>
@Before
@ -105,6 +113,8 @@ class DefaultPromoDeeplinkHandlerTest {
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase,
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase,
appsFlyerConversionStore = appsFlyerConversionStore,
analyticsEventsHandler = analyticsEventHandler,
dispatchers = dispatcherProvider,
)
@ -137,6 +147,8 @@ class DefaultPromoDeeplinkHandlerTest {
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase,
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase,
appsFlyerConversionStore = appsFlyerConversionStore,
analyticsEventsHandler = analyticsEventHandler,
dispatchers = TestingCoroutineDispatcherProvider(),
)
@ -168,6 +180,8 @@ class DefaultPromoDeeplinkHandlerTest {
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase,
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase,
appsFlyerConversionStore = appsFlyerConversionStore,
analyticsEventsHandler = analyticsEventHandler,
dispatchers = TestingCoroutineDispatcherProvider(),
)
@ -205,6 +219,8 @@ class DefaultPromoDeeplinkHandlerTest {
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase,
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase,
appsFlyerConversionStore = appsFlyerConversionStore,
analyticsEventsHandler = analyticsEventHandler,
dispatchers = dispatcherProvider,
)
@ -246,6 +262,8 @@ class DefaultPromoDeeplinkHandlerTest {
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase,
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase,
appsFlyerConversionStore = appsFlyerConversionStore,
analyticsEventsHandler = analyticsEventHandler,
dispatchers = dispatcherProvider,
)
@ -362,6 +380,8 @@ class DefaultPromoDeeplinkHandlerTest {
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase,
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase,
appsFlyerConversionStore = appsFlyerConversionStore,
analyticsEventsHandler = analyticsEventHandler,
dispatchers = dispatcherProvider,
)
@ -393,6 +413,8 @@ class DefaultPromoDeeplinkHandlerTest {
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase,
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase,
appsFlyerConversionStore = appsFlyerConversionStore,
analyticsEventsHandler = analyticsEventHandler,
dispatchers = dispatcherProvider,
)
@ -433,6 +455,8 @@ class DefaultPromoDeeplinkHandlerTest {
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase,
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase,
appsFlyerConversionStore = appsFlyerConversionStore,
analyticsEventsHandler = analyticsEventHandler,
dispatchers = dispatcherProvider,
)
@ -494,6 +518,8 @@ class DefaultPromoDeeplinkHandlerTest {
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase,
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase,
appsFlyerConversionStore = appsFlyerConversionStore,
analyticsEventsHandler = analyticsEventHandler,
dispatchers = dispatcherProvider,
)
@ -546,6 +572,8 @@ class DefaultPromoDeeplinkHandlerTest {
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase,
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase,
appsFlyerConversionStore = appsFlyerConversionStore,
analyticsEventsHandler = analyticsEventHandler,
dispatchers = dispatcherProvider,
)
@ -604,6 +632,8 @@ class DefaultPromoDeeplinkHandlerTest {
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase,
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase,
appsFlyerConversionStore = appsFlyerConversionStore,
analyticsEventsHandler = analyticsEventHandler,
dispatchers = dispatcherProvider,
)
@ -661,6 +691,8 @@ class DefaultPromoDeeplinkHandlerTest {
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase,
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase,
appsFlyerConversionStore = appsFlyerConversionStore,
analyticsEventsHandler = analyticsEventHandler,
dispatchers = dispatcherProvider,
)
@ -714,6 +746,8 @@ class DefaultPromoDeeplinkHandlerTest {
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase,
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase,
appsFlyerConversionStore = appsFlyerConversionStore,
analyticsEventsHandler = analyticsEventHandler,
dispatchers = dispatcherProvider,
)
@ -767,6 +801,8 @@ class DefaultPromoDeeplinkHandlerTest {
multiWalletCryptoCurrenciesSupplier = multiWalletCryptoCurrenciesSupplier,
activateBitcoinPromocodeUseCase = activateBitcoinPromocodeUseCase,
getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase,
bindRefcodeWithWalletUseCase = bindRefcodeWithWalletUseCase,
appsFlyerConversionStore = appsFlyerConversionStore,
analyticsEventsHandler = analyticsEventHandler,
dispatchers = dispatcherProvider,
)