Updated on 2026-08-14

This commit is contained in:
Tangem 2026-02-26 14:32:14 +04:00
parent dcaae36f60
commit c6578aa652
27 changed files with 111 additions and 1782 deletions

View file

@ -1,6 +1,6 @@
package com.tangem.feature.referral.domain
import com.tangem.domain.models.PortfolioId
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
@ -11,7 +11,7 @@ interface ReferralInteractor {
suspend fun getReferralStatus(userWalletId: UserWalletId): ReferralData
suspend fun startReferral(portfolioId: PortfolioId): ReferralData
suspend fun startReferral(accountId: AccountId): ReferralData
suspend fun getCryptoCurrency(
userWalletId: UserWalletId,

View file

@ -1,30 +1,22 @@
package com.tangem.feature.referral.domain
import arrow.core.getOrElse
import com.tangem.common.core.TangemSdkError
import com.tangem.domain.account.producer.SingleAccountProducer
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
import com.tangem.domain.account.supplier.SingleAccountSupplier
import com.tangem.domain.models.PortfolioId
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.feature.referral.domain.errors.ReferralError
import com.tangem.feature.referral.domain.models.ReferralData
import com.tangem.feature.referral.domain.models.TokenData
import timber.log.Timber
@Suppress("LongParameterList")
internal class ReferralInteractorImpl(
private val repository: ReferralRepository,
private val derivePublicKeysUseCase: DerivePublicKeysUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase,
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
private val singleAccountSupplier: SingleAccountSupplier,
private val walletManagersFacade: WalletManagersFacade,
@ -40,64 +32,41 @@ internal class ReferralInteractorImpl(
return referralData
}
override suspend fun startReferral(portfolioId: PortfolioId): ReferralData {
override suspend fun startReferral(accountId: AccountId): ReferralData {
if (tokensForReferral.isEmpty()) error("Tokens for ref is empty")
val tokenData = tokensForReferral.first()
val userWalletId = portfolioId.userWalletId
val userWallet = getUserWalletUseCase(userWalletId).getOrElse {
error("Failed to get user wallet $userWalletId: $it")
}
val userWalletId = accountId.userWalletId
val accountIndex = when (portfolioId) {
is PortfolioId.Account -> {
val account = singleAccountSupplier.getSyncOrNull(
params = SingleAccountProducer.Params(accountId = portfolioId.accountId),
)
?: error("Account not found: ${portfolioId.accountId}")
val account = singleAccountSupplier.getSyncOrNull(
params = SingleAccountProducer.Params(accountId = accountId),
)
?: error("Account not found: $accountId")
when (account) {
is Account.CryptoPortfolio -> account.derivationIndex
is Account.Payment -> TODO("[REDACTED_JIRA]")
}
}
is PortfolioId.Wallet -> null
val accountIndex = when (account) {
is Account.CryptoPortfolio -> account.derivationIndex
is Account.Payment -> TODO("[REDACTED_JIRA]")
}
val cryptoCurrency = getCryptoCurrency(
userWalletId = portfolioId.userWalletId,
userWalletId = accountId.userWalletId,
tokenData = tokenData,
accountIndex = accountIndex,
)
?: error("Failed to create crypto currency")
when (portfolioId) {
is PortfolioId.Account -> {
manageCryptoCurrenciesUseCase(
accountId = portfolioId.accountId,
add = cryptoCurrency,
skipDerivationErrors = false,
).mapLeft {
it.mapToDomainError()
}.onLeft { error ->
if (error is ReferralError.UserCancelledException) {
throw error
}
manageCryptoCurrenciesUseCase(
accountId = accountId,
add = cryptoCurrency,
skipDerivationErrors = false,
)
.mapLeft { it.mapToDomainError() }
.onLeft { error ->
Timber.e(error)
if (error is ReferralError.UserCancelledException) {
throw error
}
}
is PortfolioId.Wallet -> {
derivePublicKeysUseCase(userWallet.walletId, listOf(cryptoCurrency)).getOrElse { throwable ->
Timber.e("Failed to derive public keys: $throwable")
throw throwable.mapToDomainError()
}
addCryptoCurrenciesUseCase(
userWalletId = userWallet.walletId,
currency = cryptoCurrency,
)
}
}
.onLeft(Timber::e)
val publicAddress = walletManagersFacade.getDefaultAddress(
userWalletId = userWalletId,

View file

@ -4,10 +4,7 @@ import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
import com.tangem.domain.account.supplier.SingleAccountSupplier
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.feature.referral.domain.ReferralInteractor
import com.tangem.feature.referral.domain.ReferralInteractorImpl
import com.tangem.feature.referral.domain.ReferralRepository
@ -23,18 +20,12 @@ class ReferralDomainModule {
@ModelScoped
fun provideReferralInteractor(
referralRepository: ReferralRepository,
derivePublicKeysUseCase: DerivePublicKeysUseCase,
getUserWalletUseCase: GetUserWalletUseCase,
addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase,
manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
singleAccountSupplier: SingleAccountSupplier,
walletManagersFacade: WalletManagersFacade,
): ReferralInteractor {
return ReferralInteractorImpl(
repository = referralRepository,
derivePublicKeysUseCase = derivePublicKeysUseCase,
getUserWalletUseCase = getUserWalletUseCase,
addCryptoCurrenciesUseCase = addCryptoCurrenciesUseCase,
manageCryptoCurrenciesUseCase = manageCryptoCurrenciesUseCase,
singleAccountSupplier = singleAccountSupplier,
walletManagersFacade = walletManagersFacade,

View file

@ -15,7 +15,6 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.account.models.AccountStatusList
import com.tangem.domain.account.status.model.AccountCryptoCurrency
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
@ -24,8 +23,6 @@ import com.tangem.domain.account.status.usecase.GetAccountCurrencyByAddressUseCa
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.demo.IsDemoCardUseCase
import com.tangem.domain.models.PortfolioId
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
@ -44,10 +41,12 @@ import com.tangem.features.account.PortfolioFetcher
import com.tangem.features.account.PortfolioSelectorComponent
import com.tangem.features.account.PortfolioSelectorController
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject
@OptIn(ExperimentalCoroutinesApi::class)
@Suppress("LongParameterList")
@Stable
@ModelScoped
@ -60,7 +59,6 @@ internal class ReferralModel @Inject constructor(
private val urlOpener: UrlOpener,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val isDemoCardUseCase: IsDemoCardUseCase,
private val accountsFeatureToggles: AccountsFeatureToggles,
private val portfolioFetcherFactory: PortfolioFetcher.Factory,
val portfolioSelectorController: PortfolioSelectorController,
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
@ -92,26 +90,24 @@ internal class ReferralModel @Inject constructor(
init {
analyticsEventHandler.send(ReferralEvents.ReferralScreenOpened())
if (accountsFeatureToggles.isFeatureEnabled) {
combine(
flow = referralData.filterNotNull().onEach(::selectAccount),
flow2 = portfolioSelectorController.isAccountMode,
transform = { referralData, isAccountMode -> referralData to isAccountMode },
).transformLatest { (referralData, isAccountMode) ->
when (isAccountMode) {
false -> showContent(referralData)
true -> combineAccountUI(referralData)
combine(
flow = referralData.filterNotNull().onEach(::selectAccount),
flow2 = portfolioSelectorController.isAccountMode,
transform = { referralData, isAccountMode -> referralData to isAccountMode },
)
.transformLatest { (referralData, isAccountMode) ->
if (!isAccountMode) {
showContent(referralData)
} else {
combineAccountUI(referralData)
.map { referralData to it }
.collect(::emit)
}
}
.onEach { (referralData, accountAward) -> showContent(referralData, accountAward) }
.launchIn(modelScope)
} else {
referralData.filterNotNull()
.onEach(::showContent)
.launchIn(modelScope)
}
.onEach { (referralData, accountAward) -> showContent(referralData, accountAward) }
.launchIn(modelScope)
loadReferralData()
}
@ -123,12 +119,7 @@ internal class ReferralModel @Inject constructor(
flow3 = getSelectedAppCurrencyUseCase.invokeOrDefault(),
flow4 = portfolioFetcher.data,
) { pair, isBalanceHidden, appCurrency, portfolios ->
val selectedAccount = pair?.second ?: return@combine null
val cryptoPortfolio = when (selectedAccount) {
is AccountStatus.CryptoPortfolio -> selectedAccount
is AccountStatus.Payment -> TODO("[REDACTED_JIRA]")
}
val cryptoPortfolio = pair?.second ?: return@combine null
val awardCryptoCurrency = referralInteractor.getCryptoCurrency(
userWalletId = params.userWalletId,
@ -185,11 +176,8 @@ internal class ReferralModel @Inject constructor(
val lastInfoState = uiState.referralInfoState
uiState = uiState.copy(referralInfoState = ReferralInfoState.Loading)
modelScope.launch {
val portfolioId = when (accountsFeatureToggles.isFeatureEnabled) {
true -> PortfolioId(requireNotNull(portfolioSelectorController.selectedAccountSync))
false -> PortfolioId(params.userWalletId)
}
runCatching { referralInteractor.startReferral(portfolioId) }
val accountId = requireNotNull(portfolioSelectorController.selectedAccountSync)
runCatching { referralInteractor.startReferral(accountId) }
.onSuccess { referral ->
analyticsEventHandler.send(ReferralEvents.ParticipateSuccessful())
referralData.value = referral