Updated on 2026-08-14
This commit is contained in:
parent
e0a845a474
commit
9bcf93922c
12 changed files with 213 additions and 121 deletions
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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 ->
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue