Updated on 2026-08-14

This commit is contained in:
Tangem 2025-10-16 16:25:39 +04:00
parent e0a845a474
commit 9bcf93922c
12 changed files with 213 additions and 121 deletions

View file

@ -39,7 +39,8 @@ import com.tangem.features.swap.SwapComponent
import com.tangem.features.swap.v2.api.SendWithSwapComponent
import com.tangem.features.tangempay.components.TangemPayDetailsComponent
import com.tangem.features.tangempay.components.TangemPayOnboardingComponent
import com.tangem.features.tangempay.components.TangemPayOnboardingComponent.Params.*
import com.tangem.features.tangempay.components.TangemPayOnboardingComponent.Params.ContinueOnboarding
import com.tangem.features.tangempay.components.TangemPayOnboardingComponent.Params.Deeplink
import com.tangem.features.tokendetails.TokenDetailsComponent
import com.tangem.features.wallet.WalletEntryComponent
import com.tangem.features.walletconnect.components.WalletConnectEntryComponent
@ -141,7 +142,6 @@ internal class ChildFactory @Inject constructor(
is AppRoute.ManageTokens -> {
val source = when (route.source) {
AppRoute.ManageTokens.Source.SETTINGS -> ManageTokensSource.SETTINGS
AppRoute.ManageTokens.Source.ONBOARDING -> ManageTokensSource.ONBOARDING
AppRoute.ManageTokens.Source.STORIES -> ManageTokensSource.STORIES
}

View file

@ -126,9 +126,12 @@ sealed class AppRoute(val path: String) : Route {
val portfolioId: PortfolioId? = null,
) : AppRoute(path = "${source.name.lowercase()}/manage_tokens/${portfolioId?.stringValue}") {
/**
* Source of launching the screen.
* ManageTokens screen launched from Onboarding by another route. See `OnboardingRoute.ManageTokens`.
*/
enum class Source {
STORIES,
ONBOARDING,
SETTINGS,
}
}

View file

@ -6,6 +6,7 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.blockchainsdk.utils.toNetworkId
import com.tangem.data.common.account.WalletAccountsFetcher
import com.tangem.data.common.api.safeApiCall
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
import com.tangem.data.common.currency.UserTokensResponseFactory
@ -21,15 +22,13 @@ import com.tangem.datasource.local.config.testnet.TestnetTokensStorage
import com.tangem.datasource.local.token.UserTokensResponseStore
import com.tangem.datasource.local.userwallet.UserWalletsStore
import com.tangem.domain.card.common.TapWorkarounds.isTestCard
import com.tangem.domain.card.common.extensions.canHandleBlockchain
import com.tangem.domain.card.common.extensions.canHandleToken
import com.tangem.domain.card.common.extensions.hotWalletExcludedBlockchains
import com.tangem.domain.card.common.extensions.supportedBlockchains
import com.tangem.domain.card.common.extensions.supportedTokens
import com.tangem.domain.card.common.extensions.*
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.managetokens.model.*
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
import com.tangem.domain.managetokens.repository.ManageTokensRepository
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
@ -40,7 +39,7 @@ import com.tangem.pagination.fetcher.LimitOffsetBatchFetcher.Request
import com.tangem.pagination.toBatchFlow
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@Suppress("LongParameterList")
@Suppress("LongParameterList", "LargeClass")
internal class DefaultManageTokensRepository(
private val tangemTechApi: TangemTechApi,
private val userWalletsStore: UserWalletsStore,
@ -51,6 +50,7 @@ internal class DefaultManageTokensRepository(
private val excludedBlockchains: ExcludedBlockchains,
private val cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
private val dispatchers: CoroutineDispatcherProvider,
private val walletAccountsFetcher: WalletAccountsFetcher,
networkFactory: NetworkFactory,
) : ManageTokensRepository {
@ -94,7 +94,6 @@ internal class DefaultManageTokensRepository(
},
)
@Suppress("ComplexCondition")
private suspend fun fetchCurrencies(
userWallet: UserWallet?,
request: Request<ManageTokensListConfig>,
@ -128,20 +127,22 @@ internal class DefaultManageTokensRepository(
)
val tokensResponse = request.params.userWalletId?.let { userWalletId ->
if (loadUserTokensFromRemote && userWallet != null) {
safeApiCall({ tangemTechApi.getUserTokens(userWalletId.stringValue).bind() }) {
// save tokens response only if loadUserTokensFromRemote is true and it means onboarding call
createAndSaveDefaultUserTokensResponse(userWallet = userWallet)
when (val params = request.params) {
is ManageTokensListConfig.Account -> {
fetchUserTokens(userWallet, userWalletId, params, loadUserTokensFromRemote)
}
is ManageTokensListConfig.Wallet -> {
fetchUserTokensLegacy(userWallet, userWalletId, loadUserTokensFromRemote)
}
} else {
getSavedUserTokensResponseSync(userWalletId)
}
}
val items = if (isFirstBatchFetching &&
val isCreateWithCustom = isFirstBatchFetching &&
tokensResponse != null &&
userWallet != null &&
query == null
) {
val items = if (isCreateWithCustom) {
managedCryptoCurrencyFactory.createWithCustomTokens(
coinsResponse = updatedCoinsResponse,
tokensResponse = tokensResponse,
@ -162,6 +163,56 @@ internal class DefaultManageTokensRepository(
)
}
private suspend fun fetchUserTokens(
userWallet: UserWallet?,
userWalletId: UserWalletId,
params: ManageTokensListConfig.Account,
loadUserTokensFromRemote: Boolean,
): UserTokensResponse? {
val accountId = when {
params.accountId == null -> {
return null
}
loadUserTokensFromRemote -> {
AccountId.forCryptoPortfolio(userWalletId = userWalletId, derivationIndex = DerivationIndex.Main)
}
else -> requireNotNull(params.accountId)
}
val response = if (loadUserTokensFromRemote && userWallet != null) {
runCatching { walletAccountsFetcher.fetch(userWalletId = userWallet.walletId) }.getOrNull()
} else {
walletAccountsFetcher.getSaved(userWalletId)
}
val account = response?.accounts?.firstOrNull { it.id == accountId.value }
?: return null
return UserTokensResponse(
group = response.wallet.group,
sort = response.wallet.sort,
tokens = account.tokens.orEmpty(),
)
}
private suspend fun fetchUserTokensLegacy(
userWallet: UserWallet?,
userWalletId: UserWalletId,
loadUserTokensFromRemote: Boolean,
): UserTokensResponse? {
return if (loadUserTokensFromRemote && userWallet != null) {
safeApiCall(
call = { tangemTechApi.getUserTokens(userWalletId.stringValue).bind() },
onError = {
// save tokens response only if loadUserTokensFromRemote is true and it means onboarding call
createAndSaveDefaultUserTokensResponse(userWallet = userWallet)
},
)
} else {
getSavedUserTokensResponseSync(userWalletId)
}
}
private suspend fun createAndSaveDefaultUserTokensResponse(userWallet: UserWallet): UserTokensResponse {
val userTokensResponse = createDefaultUserTokensResponse(userWallet)
userTokenSaver.store(userWallet.walletId, userTokensResponse, useEnricher = false)

View file

@ -1,6 +1,7 @@
package com.tangem.data.managetokens.di
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.data.common.account.WalletAccountsFetcher
import com.tangem.data.common.currency.CardCryptoCurrencyFactory
import com.tangem.data.common.currency.UserTokensSaver
import com.tangem.data.common.network.NetworkFactory
@ -38,6 +39,7 @@ internal object ManageTokensDataModule {
excludedBlockchains: ExcludedBlockchains,
cardCryptoCurrencyFactory: CardCryptoCurrencyFactory,
networkFactory: NetworkFactory,
walletAccountsFetcher: WalletAccountsFetcher,
): ManageTokensRepository {
return DefaultManageTokensRepository(
tangemTechApi = tangemTechApi,
@ -50,6 +52,7 @@ internal object ManageTokensDataModule {
cardCryptoCurrencyFactory = cardCryptoCurrencyFactory,
networkFactory = networkFactory,
dispatchers = dispatchers,
walletAccountsFetcher = walletAccountsFetcher,
)
}

View file

@ -53,25 +53,27 @@ internal class DefaultSingleAccountStatusListProducer @AssistedInject constructo
params = SingleAccountListProducer.Params(params.userWalletId),
)
@Suppress("UnusedFlow")
return accountListFlow.flatMapLatest { accountList ->
val accountStatusFlows = accountList.accounts
.filterIsInstance<Account.CryptoPortfolio>()
.map { account ->
if (account.cryptoCurrencies.isEmpty()) {
createEmptyAccountStatusFlow(account)
} else {
val userWallet = userWalletsListRepository.userWalletsSync().first {
it.walletId == params.userWalletId
}
val accountStatusFlows = accountList.accounts.mapNotNull { account ->
if (account !is Account.CryptoPortfolio) return@mapNotNull null
getAccountStatusFlow(
userWallet = userWallet,
account = account,
groupType = accountList.groupType,
sortType = accountList.sortType,
)
if (account.cryptoCurrencies.isEmpty()) {
createEmptyAccountStatusFlow(account)
} else {
val userWallet = userWalletsListRepository.userWalletsSync().first {
it.walletId == params.userWalletId
}
getAccountStatusFlow(
userWallet = userWallet,
account = account,
groupType = accountList.groupType,
sortType = accountList.sortType,
)
}
.distinctUntilChanged()
}
combine(accountStatusFlows) { accountStatuses ->
val balances = accountStatuses.map { it.tokenList.totalFiatBalance }
@ -84,6 +86,7 @@ internal class DefaultSingleAccountStatusListProducer @AssistedInject constructo
)
}
}
.distinctUntilChanged()
.flowOn(dispatchers.default)
}
@ -108,6 +111,7 @@ internal class DefaultSingleAccountStatusListProducer @AssistedInject constructo
): Flow<AccountStatus.CryptoPortfolio> {
val statusesFlows = account.cryptoCurrencies.map { currency ->
cryptoCurrencyStatusesFlowFactory.create(userWallet = userWallet, currency = currency)
.distinctUntilChanged()
}
return combine(statusesFlows) { statuses ->
@ -123,6 +127,7 @@ internal class DefaultSingleAccountStatusListProducer @AssistedInject constructo
priceChangeLce = PriceChangeCalculator.calculate(statuses = statusList),
)
}
.distinctUntilChanged()
}
@AssistedFactory

View file

@ -62,7 +62,6 @@ internal class CryptoCurrencyStatusesFlowFactory @Inject constructor(
CryptoCurrencyStatus(currency = currency, value = CryptoCurrencyStatus.Loading),
)
}
.conflate()
.distinctUntilChanged()
}
@ -88,6 +87,7 @@ internal class CryptoCurrencyStatusesFlowFactory @Inject constructor(
val quoteStatusFlow = currency.id.rawCurrencyId?.let(::getQuoteStatusFlow)
return combine(networkStatusFlow, yieldBalanceFlow, quoteStatusFlow)
.distinctUntilChanged()
}
private fun combine(
@ -120,7 +120,6 @@ internal class CryptoCurrencyStatusesFlowFactory @Inject constructor(
return singleNetworkStatusSupplier(
params = SingleNetworkStatusProducer.Params(userWalletId = userWalletId, network = network),
)
.conflate()
.distinctUntilChanged()
}
@ -128,7 +127,6 @@ internal class CryptoCurrencyStatusesFlowFactory @Inject constructor(
return singleQuoteStatusSupplier(
params = SingleQuoteStatusProducer.Params(rawCurrencyId = rawCurrencyId),
)
.conflate()
.distinctUntilChanged()
}
@ -147,7 +145,6 @@ internal class CryptoCurrencyStatusesFlowFactory @Inject constructor(
singleYieldBalanceSupplier(
params = SingleYieldBalanceProducer.Params(userWalletId = userWalletId, stakingId = stakingId),
)
.conflate()
.distinctUntilChanged()
} else {
flowOf(null)

View file

@ -1,8 +1,26 @@
package com.tangem.domain.managetokens.model
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.wallet.UserWalletId
data class ManageTokensListConfig(
val userWalletId: UserWalletId?,
val searchText: String?,
)
sealed interface ManageTokensListConfig {
val userWalletId: UserWalletId?
val searchText: String?
// old way
data class Wallet(
override val userWalletId: UserWalletId?,
override val searchText: String?,
) : ManageTokensListConfig
// new way
data class Account(
val accountId: AccountId?,
override val searchText: String?,
) : ManageTokensListConfig {
override val userWalletId: UserWalletId?
get() = accountId?.userWalletId
}
}

View file

@ -9,9 +9,13 @@ import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.domain.managetokens.CreateCryptoCurrencyUseCase
import com.tangem.domain.managetokens.FindTokenUseCase
import com.tangem.domain.managetokens.ValidateTokenFormUseCase
import com.tangem.domain.managetokens.model.exceptoin.CustomTokenFormValidationException
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.wallets.usecase.ColdWalletAndHasMissedDerivationsUseCase
import com.tangem.features.managetokens.analytics.CustomTokenAnalyticsEvent
import com.tangem.features.managetokens.component.CustomTokenFormComponent
import com.tangem.features.managetokens.entity.customtoken.ClickableFieldUM
@ -41,6 +45,10 @@ internal class CustomTokenFormModel @Inject constructor(
private val messageSender: UiMessageSender,
private val customTokenFormManager: CustomCurrencyFormBuilder,
private val analyticsEventHandler: AnalyticsEventHandler,
private val coldWalletAndHasMissedDerivationsUseCase: ColdWalletAndHasMissedDerivationsUseCase,
private val createCryptoCurrencyUseCase: CreateCryptoCurrencyUseCase,
private val findTokenUseCase: FindTokenUseCase,
private val validateTokenFormUseCase: ValidateTokenFormUseCase,
paramsContainer: ParamsContainer,
customTokenFormUseCasesFacadeFactory: CustomTokenFormUseCasesFacade.Factory,
) : Model() {
@ -48,7 +56,13 @@ internal class CustomTokenFormModel @Inject constructor(
private val params: CustomTokenFormComponent.Params = paramsContainer.require()
private var createdCurrency: CryptoCurrency? = null
private var useCasesFacade: CustomTokenFormUseCasesFacade = customTokenFormUseCasesFacadeFactory.create(params.mode)
private val customCurrencyValidator = CustomCurrencyValidator(useCasesFacade)
private val customCurrencyValidator = CustomCurrencyValidator(
userWalletId = params.mode.userWalletId,
useCasesFacade = useCasesFacade,
createCryptoCurrencyUseCase = createCryptoCurrencyUseCase,
findTokenUseCase = findTokenUseCase,
validateTokenFormUseCase = validateTokenFormUseCase,
)
val state: MutableStateFlow<CustomTokenFormUM> = MutableStateFlow(
value = getInitialState(),
@ -164,8 +178,9 @@ internal class CustomTokenFormModel @Inject constructor(
isAlreadyAdded: Boolean,
isCustom: Boolean,
) = modelScope.launch {
val needColdWalletInteraction = useCasesFacade.needColdWalletInteraction(
network = mapOf(currency.network.backendId to getDerivationPath().value),
val needColdWalletInteraction = coldWalletAndHasMissedDerivationsUseCase.invoke(
userWalletId = params.mode.userWalletId,
networksWithDerivationPath = mapOf(currency.network.backendId to getDerivationPath().value),
)
state.update { state ->

View file

@ -1,11 +1,15 @@
package com.tangem.features.managetokens.utils
import arrow.core.getOrElse
import com.tangem.domain.managetokens.CreateCryptoCurrencyUseCase
import com.tangem.domain.managetokens.FindTokenUseCase
import com.tangem.domain.managetokens.ValidateTokenFormUseCase
import com.tangem.domain.managetokens.model.AddCustomTokenForm
import com.tangem.domain.managetokens.model.exceptoin.CustomTokenFormValidationException
import com.tangem.domain.managetokens.model.exceptoin.FindTokenException
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.managetokens.utils.list.CustomTokenFormUseCasesFacade
import com.tangem.utils.coroutines.JobHolder
import com.tangem.utils.coroutines.saveInAndJoin
@ -15,7 +19,11 @@ import kotlinx.coroutines.launch
import timber.log.Timber
internal class CustomCurrencyValidator(
private val userWalletId: UserWalletId,
private val useCasesFacade: CustomTokenFormUseCasesFacade,
private val createCryptoCurrencyUseCase: CreateCryptoCurrencyUseCase,
private val findTokenUseCase: FindTokenUseCase,
private val validateTokenFormUseCase: ValidateTokenFormUseCase,
) {
private val validateFormJobHolder = JobHolder()
@ -41,10 +49,7 @@ internal class CustomCurrencyValidator(
) = coroutineScope {
updateStatus(Status.Validating)
val result = useCasesFacade.validateTokenFormUseCase(
networkId = networkId,
formValues = formValues,
)
val result = validateTokenFormUseCase(networkId = networkId, formValues = formValues)
val validatedForm = result.getOrElse { e ->
updateStatus(Status.FormValidationException(e))
@ -90,7 +95,8 @@ internal class CustomCurrencyValidator(
updateStatus(Status.SearchingToken)
val foundToken = useCasesFacade.findTokenUseCase(
val foundToken = findTokenUseCase.invoke(
userWalletId = userWalletId,
contractAddress = validatedForm.contractAddress,
networkId = networkId,
derivationPath = derivationPath,
@ -121,7 +127,8 @@ internal class CustomCurrencyValidator(
) {
updateStatus(Status.SearchingToken)
val token = useCasesFacade.findTokenUseCase(
val token = findTokenUseCase.invoke(
userWalletId = userWalletId,
contractAddress = validatedForm.contractAddress,
networkId = networkId,
derivationPath = derivationPath,
@ -148,7 +155,8 @@ internal class CustomCurrencyValidator(
derivationPath: Network.DerivationPath,
validatedForm: AddCustomTokenForm.Validated.All?,
) {
val currency = useCasesFacade.createCryptoCurrencyUseCase(
val currency = createCryptoCurrencyUseCase.invoke(
userWalletId = userWalletId,
networkId = networkId,
derivationPath = derivationPath,
formValues = validatedForm,

View file

@ -1,47 +1,30 @@
package com.tangem.features.managetokens.utils.list
import arrow.core.Either
import arrow.core.NonEmptyList
import arrow.core.left
import arrow.core.right
import com.tangem.domain.account.producer.SingleAccountProducer
import com.tangem.domain.account.status.usecase.SaveCryptoCurrenciesUseCase
import com.tangem.domain.account.supplier.SingleAccountSupplier
import com.tangem.domain.managetokens.CheckIsCurrencyNotAddedUseCase
import com.tangem.domain.managetokens.CreateCryptoCurrencyUseCase
import com.tangem.domain.managetokens.FindTokenUseCase
import com.tangem.domain.managetokens.ValidateTokenFormUseCase
import com.tangem.domain.managetokens.model.AddCustomTokenForm
import com.tangem.domain.managetokens.model.exceptoin.CustomTokenFormValidationException
import com.tangem.domain.managetokens.model.exceptoin.FindTokenException
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
import com.tangem.domain.wallets.usecase.ColdWalletAndHasMissedDerivationsUseCase
import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
import com.tangem.features.managetokens.component.AddCustomTokenMode
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@Suppress("LongParameterList")
internal class CustomTokenFormUseCasesFacade @AssistedInject constructor(
private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase,
private val derivePublicKeysUseCase: DerivePublicKeysUseCase,
private val validateTokenFormUseCase: ValidateTokenFormUseCase,
private val createCryptoCurrencyUseCase: CreateCryptoCurrencyUseCase,
private val findTokenUseCase: FindTokenUseCase,
private val checkIsCurrencyNotAddedUseCase: CheckIsCurrencyNotAddedUseCase,
private val coldWalletAndHasMissedDerivationsUseCase: ColdWalletAndHasMissedDerivationsUseCase,
private val saveCryptoCurrenciesUseCase: SaveCryptoCurrenciesUseCase,
private val singleAccountSupplier: SingleAccountSupplier,
@Assisted private val mode: AddCustomTokenMode,
) {
suspend fun needColdWalletInteraction(network: Map<String, String?>): Boolean = when (mode) {
is AddCustomTokenMode.Account -> TODO("Account")
is AddCustomTokenMode.Wallet -> coldWalletAndHasMissedDerivationsUseCase.invoke(
userWalletId = mode.userWalletId,
networksWithDerivationPath = network,
)
}
suspend fun addCryptoCurrenciesUseCase(currency: CryptoCurrency): Either<Throwable, Unit> = when (mode) {
is AddCustomTokenMode.Account -> {
saveCryptoCurrenciesUseCase(accountId = mode.accountId, add = currency)
@ -67,7 +50,19 @@ internal class CustomTokenFormUseCasesFacade @AssistedInject constructor(
derivationPath: Network.DerivationPath,
contractAddress: String?,
): Either<Throwable, Boolean> = when (mode) {
is AddCustomTokenMode.Account -> TODO("Account")
is AddCustomTokenMode.Account -> {
val account = singleAccountSupplier.getSyncOrNull(
params = SingleAccountProducer.Params(accountId = mode.accountId),
)
?: return IllegalStateException("Account not found").left()
account.cryptoCurrencies.none { currency ->
networkId == currency.network.id &&
derivationPath == currency.network.derivationPath &&
contractAddress.equals(currency.id.contractAddress, ignoreCase = true)
}
.right()
}
is AddCustomTokenMode.Wallet -> checkIsCurrencyNotAddedUseCase.invoke(
userWalletId = mode.userWalletId,
networkId = networkId,
@ -76,45 +71,6 @@ internal class CustomTokenFormUseCasesFacade @AssistedInject constructor(
)
}
suspend fun createCryptoCurrencyUseCase(
networkId: Network.ID,
derivationPath: Network.DerivationPath,
formValues: AddCustomTokenForm.Validated.All?,
): Either<Throwable, CryptoCurrency> = when (mode) {
is AddCustomTokenMode.Account -> TODO("Account")
is AddCustomTokenMode.Wallet -> createCryptoCurrencyUseCase.invoke(
userWalletId = mode.userWalletId,
networkId = networkId,
derivationPath = derivationPath,
formValues = formValues,
)
}
suspend fun findTokenUseCase(
contractAddress: String,
networkId: Network.ID,
derivationPath: Network.DerivationPath,
): Either<FindTokenException, CryptoCurrency.Token> = when (mode) {
is AddCustomTokenMode.Account -> TODO("Account")
is AddCustomTokenMode.Wallet -> findTokenUseCase.invoke(
userWalletId = mode.userWalletId,
contractAddress = contractAddress,
networkId = networkId,
derivationPath = derivationPath,
)
}
suspend fun validateTokenFormUseCase(
networkId: Network.ID,
formValues: AddCustomTokenForm.Raw,
): Either<NonEmptyList<CustomTokenFormValidationException>, AddCustomTokenForm.Validated> = when (mode) {
is AddCustomTokenMode.Account -> TODO("Account")
is AddCustomTokenMode.Wallet -> validateTokenFormUseCase.invoke(
networkId = networkId,
formValues = formValues,
)
}
@AssistedFactory
interface Factory {
fun create(mode: AddCustomTokenMode): CustomTokenFormUseCasesFacade

View file

@ -90,8 +90,9 @@ internal class ManageTokensListManager @AssistedInject constructor(
*/
suspend fun launchPagination(isCollapsed: Boolean) = coroutineScope {
val loadUserTokensFromRemote = when (mode) {
is ManageTokensMode.Wallet -> source == ManageTokensSource.ONBOARDING
is ManageTokensMode.Wallet,
is ManageTokensMode.Account,
-> source == ManageTokensSource.ONBOARDING
ManageTokensMode.None,
-> false
}

View file

@ -2,7 +2,11 @@ package com.tangem.features.managetokens.utils.list
import arrow.core.Either
import arrow.core.left
import arrow.core.right
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.account.producer.SingleAccountProducer
import com.tangem.domain.account.status.usecase.SaveCryptoCurrenciesUseCase
import com.tangem.domain.account.supplier.SingleAccountSupplier
import com.tangem.domain.managetokens.*
import com.tangem.domain.managetokens.model.CurrencyUnsupportedState
import com.tangem.domain.managetokens.model.ManageTokensListConfig
@ -28,6 +32,8 @@ internal class ManageTokensUseCasesFacade @AssistedInject constructor(
private val saveManagedTokensUseCase: SaveManagedTokensUseCase,
private val saveCryptoCurrenciesUseCase: SaveCryptoCurrenciesUseCase,
private val customTokensRepository: CustomTokensRepository,
private val accountsFeatureToggles: AccountsFeatureToggles,
private val singleAccountSupplier: SingleAccountSupplier,
@Assisted private val mode: ManageTokensMode,
) {
@ -35,12 +41,21 @@ internal class ManageTokensUseCasesFacade @AssistedInject constructor(
get() = IllegalStateException("Unsupported")
fun manageTokensListConfig(searchText: String?): ManageTokensListConfig {
val userWalletId: UserWalletId? = when (mode) {
is ManageTokensMode.Account -> TODO("Account")
ManageTokensMode.None -> null
is ManageTokensMode.Wallet -> mode.userWalletId
return when (mode) {
is ManageTokensMode.Account -> {
ManageTokensListConfig.Account(accountId = mode.accountId, searchText = searchText)
}
is ManageTokensMode.Wallet -> {
ManageTokensListConfig.Wallet(userWalletId = mode.userWalletId, searchText = searchText)
}
ManageTokensMode.None -> {
if (accountsFeatureToggles.isFeatureEnabled) {
ManageTokensListConfig.Account(accountId = null, searchText = searchText)
} else {
ManageTokensListConfig.Wallet(userWalletId = null, searchText = searchText)
}
}
}
return ManageTokensListConfig(userWalletId, searchText)
}
suspend fun removeCustomCurrencyUseCase(customCurrency: ManagedCryptoCurrency.Custom): Either<Throwable, Unit> {
@ -67,7 +82,21 @@ internal class ManageTokensUseCasesFacade @AssistedInject constructor(
tempRemovedTokens: Map<ManagedCryptoCurrency.Token, Set<Network>>,
): Either<Throwable, Boolean> {
return when (mode) {
is ManageTokensMode.Account -> TODO("Account")
is ManageTokensMode.Account -> {
val added = tempAddedTokens.mapToCryptoCurrencies(userWalletId = mode.accountId.userWalletId)
val removed = tempRemovedTokens.mapToCryptoCurrencies(userWalletId = mode.accountId.userWalletId)
val account = singleAccountSupplier.getSyncOrNull(
params = SingleAccountProducer.Params(accountId = mode.accountId),
)
?: return IllegalStateException("Account not found").left()
(account.cryptoCurrencies + added - removed).any {
it is CryptoCurrency.Token && it.network.backendId == network.backendId &&
it.network.derivationPath == network.derivationPath
}
.right()
}
is ManageTokensMode.Wallet -> checkHasLinkedTokensUseCase.invoke(
userWalletId = mode.userWalletId,
network = network,
@ -82,7 +111,10 @@ internal class ManageTokensUseCasesFacade @AssistedInject constructor(
sourceNetwork: ManagedCryptoCurrency.SourceNetwork,
): Either<Throwable, CurrencyUnsupportedState?> {
return when (mode) {
is ManageTokensMode.Account -> TODO("Account")
is ManageTokensMode.Account -> checkCurrencyUnsupportedUseCase.invoke(
userWalletId = mode.accountId.userWalletId,
sourceNetwork = sourceNetwork,
)
is ManageTokensMode.Wallet -> checkCurrencyUnsupportedUseCase.invoke(
userWalletId = mode.userWalletId,
sourceNetwork = sourceNetwork,
@ -92,7 +124,10 @@ internal class ManageTokensUseCasesFacade @AssistedInject constructor(
}
suspend fun needColdWalletInteraction(network: Map<String, Nothing?>): Boolean = when (mode) {
is ManageTokensMode.Account -> TODO("Account")
is ManageTokensMode.Account -> coldWalletAndHasMissedDerivationsUseCase.invoke(
userWalletId = mode.accountId.userWalletId,
networksWithDerivationPath = network,
)
is ManageTokensMode.Wallet -> coldWalletAndHasMissedDerivationsUseCase.invoke(
userWalletId = mode.userWalletId,
networksWithDerivationPath = network,