Updated on 2026-08-14
This commit is contained in:
parent
03691635a2
commit
e2b489f0b9
20 changed files with 144 additions and 637 deletions
|
|
@ -31,7 +31,6 @@ import com.tangem.core.ui.decompose.ComposableContentComponent
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.features.account.PortfolioSelectorComponent
|
||||
import com.tangem.features.onramp.hottokens.model.HotCryptoModel
|
||||
import com.tangem.features.onramp.hottokens.portfolio.OnrampAddToPortfolioComponent
|
||||
|
|
@ -48,38 +47,29 @@ internal class DefaultHotCryptoComponent @AssistedInject constructor(
|
|||
@Assisted context: AppComponentContext,
|
||||
@Assisted private val params: HotCryptoComponent.Params,
|
||||
private val onrampAddToPortfolioComponentFactory: OnrampAddToPortfolioComponent.Factory,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
portfolioSelectorComponentFactory: PortfolioSelectorComponent.Factory,
|
||||
addTokenComponentFactory: OnrampAddTokenComponent.Factory,
|
||||
) : HotCryptoComponent, AppComponentContext by context {
|
||||
|
||||
private val model: HotCryptoModel = getOrCreateModel(params)
|
||||
|
||||
private val portfolioSelectorComponent: PortfolioSelectorComponent? by lazy {
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
portfolioSelectorComponentFactory.create(
|
||||
context = child("portfolioSelectorComponent"),
|
||||
params = PortfolioSelectorComponent.Params(
|
||||
portfolioFetcher = requireNotNull(model.portfolioFetcher),
|
||||
controller = model.portfolioSelectorController,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
private val portfolioSelectorComponent: PortfolioSelectorComponent by lazy {
|
||||
portfolioSelectorComponentFactory.create(
|
||||
context = child("portfolioSelectorComponent"),
|
||||
params = PortfolioSelectorComponent.Params(
|
||||
portfolioFetcher = model.portfolioFetcher,
|
||||
controller = model.portfolioSelectorController,
|
||||
),
|
||||
)
|
||||
}
|
||||
private val addTokenComponent: OnrampAddTokenComponent? by lazy {
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
addTokenComponentFactory.create(
|
||||
context = child("addTokenComponent"),
|
||||
params = OnrampAddTokenComponent.Params(
|
||||
callbacks = model,
|
||||
tokenToAdd = model.hotCryptoToAddDataFlow,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
private val addTokenComponent: OnrampAddTokenComponent by lazy {
|
||||
addTokenComponentFactory.create(
|
||||
context = child("addTokenComponent"),
|
||||
params = OnrampAddTokenComponent.Params(
|
||||
callbacks = model,
|
||||
tokenToAdd = model.hotCryptoToAddDataFlow,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private val bottomSheetSlot = childSlot(
|
||||
|
|
@ -106,9 +96,7 @@ internal class DefaultHotCryptoComponent @AssistedInject constructor(
|
|||
HotCrypto(state, modifier)
|
||||
|
||||
bottomSheet.child?.instance?.BottomSheet()
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
AddHotCryptoBottomSheet()
|
||||
}
|
||||
AddHotCryptoBottomSheet()
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -133,7 +121,7 @@ internal class DefaultHotCryptoComponent @AssistedInject constructor(
|
|||
content = TangemBottomSheetConfigContent.Empty,
|
||||
),
|
||||
containerColor = TangemTheme.colors.background.tertiary,
|
||||
title = { state ->
|
||||
title = { _ ->
|
||||
AnimatedContent(targetState = contentStack.value) { stack ->
|
||||
BottomSheetTitle(
|
||||
stack = stack,
|
||||
|
|
@ -142,7 +130,7 @@ internal class DefaultHotCryptoComponent @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
},
|
||||
content = { state ->
|
||||
content = { _ ->
|
||||
AnimatedContent(targetState = contentStack.value) { stack ->
|
||||
val paddingModifier = Modifier.padding(
|
||||
start = 16.dp,
|
||||
|
|
@ -221,8 +209,8 @@ internal class DefaultHotCryptoComponent @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private fun contentChild(config: OnrampAddTokenRoute): ComposableContentComponent = when (config) {
|
||||
OnrampAddTokenRoute.AddToken -> requireNotNull(addTokenComponent)
|
||||
OnrampAddTokenRoute.PortfolioSelector -> requireNotNull(portfolioSelectorComponent)
|
||||
OnrampAddTokenRoute.AddToken -> addTokenComponent
|
||||
OnrampAddTokenRoute.PortfolioSelector -> portfolioSelectorComponent
|
||||
OnrampAddTokenRoute.Empty -> ComposableContentComponent.EMPTY
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
package com.tangem.features.onramp.hottokens.model
|
||||
|
||||
import arrow.core.getOrElse
|
||||
import com.arkivanov.decompose.router.slot.SlotNavigation
|
||||
import com.arkivanov.decompose.router.slot.activate
|
||||
import com.arkivanov.decompose.router.slot.dismiss
|
||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||
import com.arkivanov.decompose.router.stack.popToFirst
|
||||
import com.arkivanov.decompose.router.stack.pushNew
|
||||
|
|
@ -13,7 +10,6 @@ import com.tangem.blockchainsdk.utils.toCoinId
|
|||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.data.common.currency.getCoinId
|
||||
|
|
@ -21,17 +17,11 @@ import com.tangem.data.common.currency.getTokenId
|
|||
import com.tangem.data.common.currency.isCustomCoin
|
||||
import com.tangem.data.common.currency.isCustomToken
|
||||
import com.tangem.data.common.network.NetworkFactory
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.onramp.GetHotCryptoUseCase
|
||||
import com.tangem.domain.onramp.model.HotCryptoCurrency
|
||||
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
|
||||
import com.tangem.features.account.PortfolioFetcher
|
||||
import com.tangem.features.account.PortfolioSelectorController
|
||||
import com.tangem.features.onramp.hottokens.HotCryptoComponent
|
||||
|
|
@ -51,7 +41,6 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -59,9 +48,6 @@ import javax.inject.Inject
|
|||
* Hot crypto model
|
||||
*
|
||||
* @param paramsContainer params container
|
||||
* @param getHotCryptoUseCase use case for getting hot crypto
|
||||
* @param getSelectedAppCurrencyUseCase use case for getting selected app currency
|
||||
* @property getCryptoCurrencyStatusSyncUseCase use case for getting crypto currency status by id
|
||||
* @property dispatchers dispatchers
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -70,22 +56,17 @@ import javax.inject.Inject
|
|||
@ModelScoped
|
||||
internal class HotCryptoModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
private val getHotCryptoUseCase: GetHotCryptoUseCase,
|
||||
private val callbackDelegate: HotCryptoModelCallbackDelegate,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val hotCryptoPortfolioDataLoader: HotCryptoPortfolioDataLoader,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
val portfolioSelectorController: PortfolioSelectorController,
|
||||
private val networkFactory: NetworkFactory,
|
||||
private val portfolioFetcherFactory: PortfolioFetcher.Factory,
|
||||
portfolioFetcherFactory: PortfolioFetcher.Factory,
|
||||
) : Model(), OnrampAddTokenComponent.Callbacks by callbackDelegate {
|
||||
|
||||
val bottomSheetNavigation: SlotNavigation<OnrampAddToPortfolioBSConfig> = SlotNavigation()
|
||||
|
||||
val portfolioFetcher: PortfolioFetcher?
|
||||
val portfolioFetcher: PortfolioFetcher
|
||||
val bottomSheetNavigationV2 = StackNavigation<OnrampAddTokenRoute>()
|
||||
private val addHotCryptoJob = JobHolder()
|
||||
val hotCryptoToAddDataFlow: MutableSharedFlow<AddHotCryptoData> = MutableSharedFlow(
|
||||
|
|
@ -99,53 +80,30 @@ internal class HotCryptoModel @Inject constructor(
|
|||
private val params: HotCryptoComponent.Params = paramsContainer.require()
|
||||
|
||||
init {
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
portfolioFetcher = portfolioFetcherFactory.create(
|
||||
mode = PortfolioFetcher.Mode.Wallet(params.userWalletId),
|
||||
scope = modelScope,
|
||||
)
|
||||
combineData()
|
||||
} else {
|
||||
portfolioFetcher = null
|
||||
combineDataOld()
|
||||
}
|
||||
portfolioFetcher = portfolioFetcherFactory.create(
|
||||
mode = PortfolioFetcher.Mode.Wallet(params.userWalletId),
|
||||
scope = modelScope,
|
||||
)
|
||||
combineData()
|
||||
}
|
||||
|
||||
private fun combineData() {
|
||||
combine(
|
||||
flow = hotCryptoPortfolioDataLoader.loadPortfolioData(params.userWalletId),
|
||||
flow2 = isAccountsModeEnabledUseCase.invoke(),
|
||||
transform = { data, isAccountMode ->
|
||||
hotCryptoPortfolioDataLoader.loadPortfolioData(params.userWalletId)
|
||||
.map { data ->
|
||||
HotTokenItemStateConverter(
|
||||
appCurrency = data.appCurrency,
|
||||
onItemClick = { tokenItemState, hotCryptoCurrency ->
|
||||
onItemClick = { _, hotCryptoCurrency ->
|
||||
startAddTokenFlow(currency = hotCryptoCurrency, hotCryptoPortfolioData = data)
|
||||
},
|
||||
)
|
||||
.convertList(data.allHotCrypto)
|
||||
.map(TokensListItemUM::Token)
|
||||
},
|
||||
)
|
||||
}
|
||||
.onEach { items -> state.update { HotCryptoUM(items = it.buildItems(items)) } }
|
||||
.flowOn(dispatchers.default)
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun combineDataOld() {
|
||||
combine(
|
||||
flow = getSelectedAppCurrencyUseCase().map { it.getOrElse { AppCurrency.Default } },
|
||||
flow2 = getHotCryptoUseCase(params.userWalletId),
|
||||
) { appCurrency, currencies ->
|
||||
HotTokenItemStateConverter(appCurrency = appCurrency, onItemClick = ::onTokenClick)
|
||||
.convertList(currencies)
|
||||
.map(TokensListItemUM::Token)
|
||||
}
|
||||
.onEach { items ->
|
||||
state.update { HotCryptoUM(items = it.buildItems(items)) }
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun HotCryptoUM.buildItems(items: List<TokensListItemUM.Token>): ImmutableList<TokensListItemUM> =
|
||||
buildList {
|
||||
if (items.isNotEmpty()) {
|
||||
|
|
@ -163,16 +121,6 @@ internal class HotCryptoModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun onTokenClick(tokenItemState: TokenItemState, currency: HotCryptoCurrency) {
|
||||
bottomSheetNavigation.activate(
|
||||
configuration = OnrampAddToPortfolioBSConfig.AddToPortfolio(
|
||||
cryptoCurrency = currency.cryptoCurrency,
|
||||
currencyIconState = tokenItemState.iconState,
|
||||
onSuccessAdding = ::onSuccessAdding,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun startAddTokenFlow(currency: HotCryptoCurrency, hotCryptoPortfolioData: HotCryptoPortfolioData) {
|
||||
hotCryptoToAddDataFlow.resetReplayCache()
|
||||
fun closeNavigationFlow() = bottomSheetNavigationV2.replaceAll(OnrampAddTokenRoute.Empty)
|
||||
|
|
@ -200,7 +148,7 @@ internal class HotCryptoModel @Inject constructor(
|
|||
bottomSheetNavigationV2.replaceAll(OnrampAddTokenRoute.PortfolioSelector)
|
||||
|
||||
val tokenToAddStateFlow = portfolioSelectorController
|
||||
.selectedAccountWithData(requireNotNull(portfolioFetcher))
|
||||
.selectedAccountWithData(portfolioFetcher)
|
||||
.filterNotNull()
|
||||
.map { (_, selectedAccount) ->
|
||||
val cryptoCurrency = updateCryptoCurrency(
|
||||
|
|
@ -246,20 +194,6 @@ internal class HotCryptoModel @Inject constructor(
|
|||
.saveIn(addHotCryptoJob)
|
||||
}
|
||||
|
||||
private fun onSuccessAdding(id: CryptoCurrency.ID) {
|
||||
modelScope.launch {
|
||||
getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync(
|
||||
userWalletId = params.userWalletId,
|
||||
cryptoCurrencyId = id,
|
||||
)
|
||||
.onRight { status ->
|
||||
bottomSheetNavigation.dismiss()
|
||||
params.onTokenClick(status)
|
||||
}
|
||||
.onLeft { Timber.d("Unable to get CryptoCurrencyStatus[$id]: $it") }
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupPortfolioSelector(hotCrypto: HotCryptoCurrency, hotCryptoPortfolioData: HotCryptoPortfolioData) {
|
||||
portfolioSelectorController.selectAccount(null)
|
||||
portfolioSelectorController.isEnabled.value = isEnabled@{ _, accountStatus ->
|
||||
|
|
|
|||
|
|
@ -1,34 +1,64 @@
|
|||
package com.tangem.features.onramp.swap.availablepairs.entity.transformers
|
||||
|
||||
import com.tangem.common.ui.account.AccountCryptoPortfolioItemStateConverter
|
||||
import com.tangem.common.ui.account.TokensListPortfolioItemConverter
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUM
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUMData
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUMTransformer
|
||||
import com.tangem.features.onramp.tokenlist.entity.utils.OnrampTokenItemStateConverterFactory
|
||||
import com.tangem.features.onramp.tokenlist.entity.utils.addHeader
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class SetNoAvailablePairsTransformer(
|
||||
private val appCurrency: AppCurrency,
|
||||
private val unavailableStatuses: List<CryptoCurrencyStatus>,
|
||||
private val accountList: Map<Account.CryptoPortfolio, List<CryptoCurrencyStatus>>,
|
||||
private val isBalanceHidden: Boolean,
|
||||
private val unavailableTokensHeaderReference: TextReference,
|
||||
private val isAccountsMode: Boolean,
|
||||
private val unavailableErrorText: TextReference,
|
||||
) : TokenListUMTransformer {
|
||||
private val unavailableConverter = OnrampTokenItemStateConverterFactory
|
||||
.createUnavailableItemConverterV2(appCurrency = appCurrency, unavailableErrorText = unavailableErrorText)
|
||||
|
||||
override fun transform(prevState: TokenListUM): TokenListUM {
|
||||
val unavailableItems = OnrampTokenItemStateConverterFactory.createUnavailableItemConverter(appCurrency)
|
||||
.convertList(unavailableStatuses)
|
||||
.map(TokensListItemUM::Token)
|
||||
val totalTokensCount = accountList.values.sumOf { it.size }
|
||||
|
||||
return prevState.copy(
|
||||
availableItems = persistentListOf(),
|
||||
unavailableItems = unavailableItems.addHeader(textReference = unavailableTokensHeaderReference),
|
||||
unavailableItems = persistentListOf(),
|
||||
tokensListData = if (isAccountsMode) {
|
||||
TokenListUMData.AccountList(
|
||||
tokensList = accountList.map { (account, cryptoCurrencies) ->
|
||||
TokensListPortfolioItemConverter(
|
||||
tokenItemUM = AccountCryptoPortfolioItemStateConverter(
|
||||
appCurrency = appCurrency,
|
||||
account = account,
|
||||
onItemClick = null,
|
||||
).convert(TotalFiatBalance.Failed),
|
||||
isExpanded = true,
|
||||
isCollapsable = false,
|
||||
tokens = unavailableConverter.convertList(cryptoCurrencies)
|
||||
.map(TokensListItemUM::Token)
|
||||
.toPersistentList(),
|
||||
).convert(Unit)
|
||||
}.toPersistentList(),
|
||||
totalTokensCount = totalTokensCount,
|
||||
)
|
||||
} else {
|
||||
TokenListUMData.TokenList(
|
||||
tokensList = accountList.flatMap { (_, cryptoCurrencies) ->
|
||||
unavailableConverter.convertList(cryptoCurrencies)
|
||||
.map(TokensListItemUM::Token)
|
||||
}.toPersistentList(),
|
||||
totalTokensCount = totalTokensCount,
|
||||
)
|
||||
},
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
warning = NotificationUM.Warning.SwapNoAvailablePair,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
package com.tangem.features.onramp.swap.availablepairs.entity.transformers
|
||||
|
||||
import com.tangem.common.ui.account.AccountCryptoPortfolioItemStateConverter
|
||||
import com.tangem.common.ui.account.TokensListPortfolioItemConverter
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUM
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUMData
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUMTransformer
|
||||
import com.tangem.features.onramp.tokenlist.entity.utils.OnrampTokenItemStateConverterFactory
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
internal class SetNoAvailablePairsTransformerV2(
|
||||
private val appCurrency: AppCurrency,
|
||||
private val accountList: Map<Account.CryptoPortfolio, List<CryptoCurrencyStatus>>,
|
||||
private val isBalanceHidden: Boolean,
|
||||
private val isAccountsMode: Boolean,
|
||||
private val unavailableErrorText: TextReference,
|
||||
) : TokenListUMTransformer {
|
||||
private val unavailableConverter = OnrampTokenItemStateConverterFactory
|
||||
.createUnavailableItemConverterV2(appCurrency = appCurrency, unavailableErrorText = unavailableErrorText)
|
||||
|
||||
override fun transform(prevState: TokenListUM): TokenListUM {
|
||||
val totalTokensCount = accountList.values.sumOf { it.size }
|
||||
|
||||
return prevState.copy(
|
||||
availableItems = persistentListOf(),
|
||||
unavailableItems = persistentListOf(),
|
||||
tokensListData = if (isAccountsMode) {
|
||||
TokenListUMData.AccountList(
|
||||
tokensList = accountList.map { (account, cryptoCurrencies) ->
|
||||
TokensListPortfolioItemConverter(
|
||||
tokenItemUM = AccountCryptoPortfolioItemStateConverter(
|
||||
appCurrency = appCurrency,
|
||||
account = account,
|
||||
onItemClick = null,
|
||||
).convert(TotalFiatBalance.Failed),
|
||||
isExpanded = true,
|
||||
isCollapsable = false,
|
||||
tokens = unavailableConverter.convertList(cryptoCurrencies)
|
||||
.map(TokensListItemUM::Token)
|
||||
.toPersistentList(),
|
||||
).convert(Unit)
|
||||
}.toPersistentList(),
|
||||
totalTokensCount = totalTokensCount,
|
||||
)
|
||||
} else {
|
||||
TokenListUMData.TokenList(
|
||||
tokensList = accountList.flatMap { (_, cryptoCurrencies) ->
|
||||
unavailableConverter.convertList(cryptoCurrencies)
|
||||
.map(TokensListItemUM::Token)
|
||||
}.toPersistentList(),
|
||||
totalTokensCount = totalTokensCount,
|
||||
)
|
||||
},
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
warning = NotificationUM.Warning.SwapNoAvailablePair,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -13,10 +13,7 @@ import com.tangem.core.decompose.model.ParamsContainer
|
|||
import com.tangem.core.ui.components.fields.InputManager
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.capitalize
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
||||
|
|
@ -26,7 +23,6 @@ import com.tangem.domain.appcurrency.model.AppCurrency
|
|||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.card.common.extensions.hotWalletExcludedBlockchains
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.utils.getOrElse
|
||||
import com.tangem.domain.core.utils.lceContent
|
||||
import com.tangem.domain.core.utils.lceError
|
||||
import com.tangem.domain.core.utils.lceLoading
|
||||
|
|
@ -41,7 +37,6 @@ import com.tangem.domain.models.currency.CryptoCurrency
|
|||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.feature.swap.domain.GetAvailablePairsUseCase
|
||||
import com.tangem.feature.swap.domain.models.domain.LeastTokenInfo
|
||||
|
|
@ -51,9 +46,7 @@ import com.tangem.features.feed.components.market.details.portfolio.add.AddToPor
|
|||
import com.tangem.features.onramp.impl.R
|
||||
import com.tangem.features.onramp.swap.availablepairs.AvailableSwapPairsComponent
|
||||
import com.tangem.features.onramp.swap.availablepairs.entity.transformers.SetErrorWarningTransformer
|
||||
import com.tangem.features.onramp.swap.availablepairs.entity.transformers.SetLoadingTokenItemsTransformer
|
||||
import com.tangem.features.onramp.swap.availablepairs.entity.transformers.SetNoAvailablePairsTransformer
|
||||
import com.tangem.features.onramp.swap.availablepairs.entity.transformers.SetNoAvailablePairsTransformerV2
|
||||
import com.tangem.features.onramp.swap.availablepairs.market.SwapMarketsListBatchFlowManager
|
||||
import com.tangem.features.onramp.swap.availablepairs.market.state.SwapMarketState
|
||||
import com.tangem.features.onramp.swap.entity.AccountAvailabilityUM
|
||||
|
|
@ -61,7 +54,9 @@ import com.tangem.features.onramp.swap.entity.AccountCurrencyUM
|
|||
import com.tangem.features.onramp.tokenlist.entity.TokenListUM
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUMController
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUMTransformer
|
||||
import com.tangem.features.onramp.tokenlist.entity.transformer.*
|
||||
import com.tangem.features.onramp.tokenlist.entity.transformer.SetLoadingAccountTokenListTransformer
|
||||
import com.tangem.features.onramp.tokenlist.entity.transformer.SetNothingToFoundStateTransformer
|
||||
import com.tangem.features.onramp.tokenlist.entity.transformer.UpdateAccountTokenListTransformer
|
||||
import com.tangem.features.onramp.tokenlist.entity.utils.OnrampTokenItemStateConverterFactory
|
||||
import com.tangem.features.onramp.utils.ClearSearchBarTransformer
|
||||
import com.tangem.features.onramp.utils.UpdateSearchBarActiveStateTransformer
|
||||
|
|
@ -87,7 +82,6 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val getTokenListUseCase: GetTokenListUseCase,
|
||||
private val tokenListUMController: TokenListUMController,
|
||||
private val searchManager: InputManager,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
|
|
@ -95,7 +89,6 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
private val getAvailablePairsUseCase: GetAvailablePairsUseCase,
|
||||
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val getMarketsTokenListFlowUseCase: GetMarketsTokenListFlowUseCase,
|
||||
private val addToPortfolioManagerFactory: AddToPortfolioManager.Factory,
|
||||
private val excludedBlockchains: ExcludedBlockchains,
|
||||
|
|
@ -120,7 +113,6 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
}
|
||||
private val addToPortfolioJobHolder = JobHolder()
|
||||
|
||||
private val tokenListFlow = getTokenListUseCaseFlow()
|
||||
private val accountListFlow = getAccountListUseCaseFlow()
|
||||
private val availablePairsByNetworkFlow = MutableStateFlow<Map<LeastTokenInfo, AvailablePairsState>>(emptyMap())
|
||||
|
||||
|
|
@ -157,11 +149,7 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
private val visibleDefaultMarketItemIds = MutableStateFlow<List<CryptoCurrency.RawID>>(emptyList())
|
||||
|
||||
init {
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
subscribeOnUpdateStateV2()
|
||||
} else {
|
||||
subscribeOnUpdateState()
|
||||
}
|
||||
subscribeOnUpdateState()
|
||||
|
||||
initializeSearchBarCallbacks()
|
||||
subscribeOnSelectedStatusChange()
|
||||
|
|
@ -173,18 +161,6 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getTokenListUseCaseFlow(): SharedFlow<List<CryptoCurrencyStatus>> {
|
||||
return getTokenListUseCase.launch(userWalletId = params.userWalletId)
|
||||
.distinctUntilChanged()
|
||||
.map { maybeTokenList ->
|
||||
maybeTokenList.getOrElse(
|
||||
ifLoading = { it ?: TokenList.Empty },
|
||||
ifError = { TokenList.Empty },
|
||||
).flattenCurrencies()
|
||||
}
|
||||
.shareIn(scope = modelScope, started = SharingStarted.Eagerly, replay = 1)
|
||||
}
|
||||
|
||||
private fun getAccountListUseCaseFlow(): SharedFlow<List<AccountStatus>> {
|
||||
return singleAccountStatusListSupplier(SingleAccountStatusListProducer.Params(params.userWalletId))
|
||||
.distinctUntilChanged()
|
||||
|
|
@ -214,42 +190,6 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun subscribeOnUpdateState() {
|
||||
combine(
|
||||
flow = tokenListFlow,
|
||||
flow2 = getAppCurrencyAndBalanceHidingFlow(),
|
||||
flow3 = params.selectedStatus,
|
||||
flow4 = searchManager.query,
|
||||
flow5 = availablePairsByNetworkFlow
|
||||
.map { it[params.selectedStatus.value?.toLeastTokenInfo()] }
|
||||
.distinctUntilChanged(),
|
||||
) { currencies, appCurrencyAndBalanceHiding, selectedStatus, query, availablePairsState ->
|
||||
availablePairsState?.fold(
|
||||
ifLoading = { SetLoadingTokenItemsTransformer(currencies) },
|
||||
ifContent = { pairs ->
|
||||
handleContentState(
|
||||
appCurrencyAndBalanceHiding = appCurrencyAndBalanceHiding,
|
||||
currencies = currencies,
|
||||
selectedStatus = selectedStatus,
|
||||
query = query,
|
||||
availablePairs = pairs,
|
||||
)
|
||||
},
|
||||
ifError = { throwable ->
|
||||
handleErrorState(
|
||||
cause = throwable,
|
||||
networkInfo = params.selectedStatus.value?.toLeastTokenInfo(),
|
||||
currencies = currencies,
|
||||
)
|
||||
},
|
||||
)
|
||||
?: SetLoadingTokenItemsTransformer(currencies)
|
||||
}
|
||||
.onEach(tokenListUMController::update)
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnUpdateStateV2() {
|
||||
combine(
|
||||
flow = getAccountsAndModeFlow(),
|
||||
flow2 = getAppCurrencyAndBalanceHidingFlow(),
|
||||
|
|
@ -269,7 +209,7 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
)
|
||||
},
|
||||
ifContent = { pairs ->
|
||||
handleContentStateV2(
|
||||
handleContentState(
|
||||
appCurrencyAndBalanceHiding = appCurrencyAndBalanceHiding,
|
||||
accountList = accountList,
|
||||
selectedStatus = selectedStatus,
|
||||
|
|
@ -279,7 +219,7 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
)
|
||||
},
|
||||
ifError = { throwable ->
|
||||
handleErrorStateV2(
|
||||
handleErrorState(
|
||||
cause = throwable,
|
||||
networkInfo = params.selectedStatus.value?.toLeastTokenInfo(),
|
||||
accountList = accountList,
|
||||
|
|
@ -297,52 +237,6 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleContentState(
|
||||
appCurrencyAndBalanceHiding: Pair<AppCurrency, Boolean>,
|
||||
currencies: List<CryptoCurrencyStatus>,
|
||||
selectedStatus: CryptoCurrencyStatus?,
|
||||
query: String,
|
||||
availablePairs: List<SwapPairLeast>,
|
||||
): TokenListUMTransformer {
|
||||
val (appCurrency, isBalanceHidden) = appCurrencyAndBalanceHiding
|
||||
|
||||
if (availablePairs.isEmpty()) {
|
||||
return SetNoAvailablePairsTransformer(
|
||||
appCurrency = appCurrency,
|
||||
unavailableStatuses = currencies,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
unavailableTokensHeaderReference = resourceReference(
|
||||
id = R.string.tokens_list_unavailable_to_swap_header,
|
||||
wrappedList(selectedStatus?.currency?.name?.capitalize().orEmpty()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
val filterByQueryTokenList = currencies
|
||||
.filter { it.currency != selectedStatus?.currency }
|
||||
.filterByQuery(query = query)
|
||||
|
||||
return if (query.isNotEmpty() && filterByQueryTokenList.isEmpty()) {
|
||||
SetNothingToFoundStateTransformer(
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
emptySearchMessageReference = resourceReference(
|
||||
id = R.string.action_buttons_swap_empty_search_message,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
UpdateTokenItemsTransformer(
|
||||
appCurrency = appCurrency,
|
||||
onItemClick = ::onPortfolioTokenClick,
|
||||
statuses = filterByQueryTokenList.filterByAvailability(availablePairs = availablePairs),
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
unavailableTokensHeaderReference = resourceReference(
|
||||
id = R.string.tokens_list_unavailable_to_swap_header,
|
||||
wrappedList(selectedStatus?.currency?.name?.capitalize().orEmpty()),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleContentStateV2(
|
||||
appCurrencyAndBalanceHiding: Pair<AppCurrency, Boolean>,
|
||||
accountList: List<AccountStatus>,
|
||||
selectedStatus: CryptoCurrencyStatus?,
|
||||
|
|
@ -371,7 +265,7 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
.filterValues { it.isNotEmpty() }
|
||||
|
||||
if (availablePairs.isEmpty()) {
|
||||
return SetNoAvailablePairsTransformerV2(
|
||||
return SetNoAvailablePairsTransformer(
|
||||
appCurrency = appCurrency,
|
||||
accountList = filterByQueryAccountList,
|
||||
unavailableErrorText = resourceReference(R.string.tokens_list_unavailable_to_swap_source_header),
|
||||
|
|
@ -381,7 +275,7 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
}
|
||||
|
||||
return if (query.isNotEmpty() && filterByQueryAccountList.isEmpty()) {
|
||||
SetNothingToFoundStateTransformerV2(
|
||||
SetNothingToFoundStateTransformer(
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
emptySearchMessageReference = resourceReference(
|
||||
id = R.string.action_buttons_swap_empty_search_message,
|
||||
|
|
@ -400,23 +294,6 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun handleErrorState(
|
||||
cause: Throwable,
|
||||
networkInfo: LeastTokenInfo?,
|
||||
currencies: List<CryptoCurrencyStatus>,
|
||||
): SetErrorWarningTransformer {
|
||||
return SetErrorWarningTransformer(
|
||||
cause = cause,
|
||||
onRefresh = {
|
||||
modelScope.launch {
|
||||
if (networkInfo != null) {
|
||||
updateAvailablePairs(networkInfo, currencies)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleErrorStateV2(
|
||||
cause: Throwable,
|
||||
networkInfo: LeastTokenInfo?,
|
||||
accountList: List<AccountStatus>,
|
||||
|
|
@ -450,19 +327,14 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
val isAlreadyLoaded = availablePairsByNetworkFlow.value[networkInfo]?.isContent() == true
|
||||
if (isAlreadyLoaded) return@collectLatest
|
||||
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
val accountList = accountListFlow.firstOrNull() ?: return@collectLatest
|
||||
updateAvailablePairs(
|
||||
networkInfo = networkInfo,
|
||||
statuses = accountList.filterCryptoPortfolio()
|
||||
.flatMap { accountStatus ->
|
||||
accountStatus.flattenCurrencies()
|
||||
}.toSet().toList(),
|
||||
)
|
||||
} else {
|
||||
val statuses = tokenListFlow.firstOrNull() ?: return@collectLatest
|
||||
updateAvailablePairs(networkInfo = networkInfo, statuses = statuses)
|
||||
}
|
||||
val accountList = accountListFlow.firstOrNull() ?: return@collectLatest
|
||||
updateAvailablePairs(
|
||||
networkInfo = networkInfo,
|
||||
statuses = accountList.filterCryptoPortfolio()
|
||||
.flatMap { accountStatus ->
|
||||
accountStatus.flattenCurrencies()
|
||||
}.toSet().toList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -540,19 +412,6 @@ internal class AvailableSwapPairsModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun List<CryptoCurrencyStatus>.filterByAvailability(
|
||||
availablePairs: List<SwapPairLeast>,
|
||||
): Map<Boolean, List<CryptoCurrencyStatus>> {
|
||||
return groupBy { status ->
|
||||
val isAvailable = availablePairs.map(SwapPairLeast::to).contains(status.toLeastTokenInfo())
|
||||
|
||||
isAvailable &&
|
||||
status.value !is CryptoCurrencyStatus.MissedDerivation &&
|
||||
status.value !is CryptoCurrencyStatus.Unreachable &&
|
||||
!status.currency.isCustom
|
||||
}
|
||||
}
|
||||
|
||||
private fun Map<Account.CryptoPortfolio, List<CryptoCurrencyStatus>>.filterByAvailability(
|
||||
availablePairs: List<SwapPairLeast>,
|
||||
): List<AccountAvailabilityUM> {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import com.tangem.core.decompose.model.Model
|
|||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
|
||||
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
|
|
@ -37,7 +36,6 @@ internal class SwapSelectTokensModel @Inject constructor(
|
|||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
) : Model() {
|
||||
|
||||
val state: StateFlow<SwapSelectTokensUM> = controller.state
|
||||
|
|
@ -77,14 +75,10 @@ internal class SwapSelectTokensModel @Inject constructor(
|
|||
selectedTokenItemState = selectedTokenItemState,
|
||||
onRemoveClick = ::onRemoveFromTokenClick,
|
||||
isAccountsMode = isAccountsMode,
|
||||
account = if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
getAccountCurrencyStatusUseCase.invokeSync(
|
||||
userWalletId = params.userWalletId,
|
||||
currency = status.currency,
|
||||
).getOrNull()?.account
|
||||
} else {
|
||||
null
|
||||
},
|
||||
account = getAccountCurrencyStatusUseCase.invokeSync(
|
||||
userWalletId = params.userWalletId,
|
||||
currency = status.currency,
|
||||
).getOrNull()?.account,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -108,14 +102,10 @@ internal class SwapSelectTokensModel @Inject constructor(
|
|||
transformer = SelectToTokenTransformer(
|
||||
selectedTokenItemState = selectedTokenItemState,
|
||||
isAccountsMode = isAccountsMode,
|
||||
account = if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
getAccountCurrencyStatusUseCase.invokeSync(
|
||||
userWalletId = params.userWalletId,
|
||||
currency = status.currency,
|
||||
).getOrNull()?.account
|
||||
} else {
|
||||
null
|
||||
},
|
||||
account = getAccountCurrencyStatusUseCase.invokeSync(
|
||||
userWalletId = params.userWalletId,
|
||||
currency = status.currency,
|
||||
).getOrNull()?.account,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ package com.tangem.features.onramp.tokenlist.entity.transformer
|
|||
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.features.onramp.impl.R
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUM
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUMData
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUMTransformer
|
||||
|
|
@ -16,22 +14,18 @@ internal class SetNothingToFoundStateTransformer(
|
|||
|
||||
override fun transform(prevState: TokenListUM): TokenListUM {
|
||||
return prevState.copy(
|
||||
availableItems = persistentListOf(
|
||||
createGroupTitle(
|
||||
textReference = resourceReference(id = R.string.exchange_tokens_available_tokens_header),
|
||||
),
|
||||
TokensListItemUM.Text(
|
||||
id = emptySearchMessageReference.hashCode(),
|
||||
text = emptySearchMessageReference,
|
||||
),
|
||||
),
|
||||
availableItems = persistentListOf(),
|
||||
unavailableItems = persistentListOf(),
|
||||
tokensListData = TokenListUMData.EmptyList,
|
||||
tokensListData = TokenListUMData.TokenList(
|
||||
tokensList = persistentListOf(
|
||||
TokensListItemUM.Text(
|
||||
id = emptySearchMessageReference.hashCode(),
|
||||
text = emptySearchMessageReference,
|
||||
),
|
||||
),
|
||||
totalTokensCount = 0,
|
||||
),
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createGroupTitle(textReference: TextReference): TokensListItemUM.GroupTitle {
|
||||
return TokensListItemUM.GroupTitle(id = textReference.hashCode(), text = textReference)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package com.tangem.features.onramp.tokenlist.entity.transformer
|
||||
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUM
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUMData
|
||||
import com.tangem.features.onramp.tokenlist.entity.TokenListUMTransformer
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal class SetNothingToFoundStateTransformerV2(
|
||||
private val isBalanceHidden: Boolean,
|
||||
private val emptySearchMessageReference: TextReference,
|
||||
) : TokenListUMTransformer {
|
||||
|
||||
override fun transform(prevState: TokenListUM): TokenListUM {
|
||||
return prevState.copy(
|
||||
availableItems = persistentListOf(),
|
||||
unavailableItems = persistentListOf(),
|
||||
tokensListData = TokenListUMData.TokenList(
|
||||
tokensList = persistentListOf(
|
||||
TokensListItemUM.Text(
|
||||
id = emptySearchMessageReference.hashCode(),
|
||||
text = emptySearchMessageReference,
|
||||
),
|
||||
),
|
||||
totalTokensCount = 0,
|
||||
),
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,6 @@ import com.tangem.core.ui.components.fields.InputManager
|
|||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
|
||||
import com.tangem.domain.account.models.AccountStatusList
|
||||
import com.tangem.domain.account.status.producer.SingleAccountStatusListProducer
|
||||
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
|
||||
|
|
@ -16,19 +15,14 @@ import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
|
|||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.utils.getOrElse
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.models.TotalFiatBalance
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
||||
import com.tangem.domain.settings.usercountry.models.UserCountry
|
||||
import com.tangem.domain.tokens.GetAssetRequirementsUseCase
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.features.onramp.impl.R
|
||||
|
|
@ -36,7 +30,9 @@ import com.tangem.features.onramp.swap.entity.AccountAvailabilityUM
|
|||
import com.tangem.features.onramp.swap.entity.AccountCurrencyUM
|
||||
import com.tangem.features.onramp.tokenlist.OnrampTokenListComponent
|
||||
import com.tangem.features.onramp.tokenlist.entity.*
|
||||
import com.tangem.features.onramp.tokenlist.entity.transformer.*
|
||||
import com.tangem.features.onramp.tokenlist.entity.transformer.SetLoadingAccountTokenListTransformer
|
||||
import com.tangem.features.onramp.tokenlist.entity.transformer.SetNothingToFoundStateTransformer
|
||||
import com.tangem.features.onramp.tokenlist.entity.transformer.UpdateAccountTokenListTransformer
|
||||
import com.tangem.features.onramp.utils.ClearSearchBarTransformer
|
||||
import com.tangem.features.onramp.utils.UpdateSearchBarActiveStateTransformer
|
||||
import com.tangem.features.onramp.utils.UpdateSearchBarCallbacksTransformer
|
||||
|
|
@ -60,12 +56,10 @@ internal class OnrampTokenListModel @Inject constructor(
|
|||
private val searchManager: InputManager,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
private val getTokenListUseCase: GetTokenListUseCase,
|
||||
private val getWalletsUseCase: GetWalletsUseCase,
|
||||
private val rampStateManager: RampStateManager,
|
||||
private val getUserCountryUseCase: GetUserCountryUseCase,
|
||||
private val getAssetRequirementsUseCase: GetAssetRequirementsUseCase,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
|
||||
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
|
||||
) : Model() {
|
||||
|
|
@ -84,68 +78,10 @@ internal class OnrampTokenListModel @Inject constructor(
|
|||
onActiveChange = ::onSearchBarActiveChange,
|
||||
),
|
||||
)
|
||||
if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
subscribeOnUpdateStateV2()
|
||||
} else {
|
||||
subscribeOnUpdateState()
|
||||
}
|
||||
subscribeOnUpdateState()
|
||||
}
|
||||
|
||||
private fun subscribeOnUpdateState() {
|
||||
combine(
|
||||
flow = getTokenListUseCase.launch(userWalletId = params.userWalletId).distinctUntilChanged(),
|
||||
flow2 = getSelectedAppCurrencyUseCase().map { it.getOrElse { AppCurrency.Default } }.distinctUntilChanged(),
|
||||
flow3 = getBalanceHidingSettingsUseCase().map { it.isBalanceHidden }.distinctUntilChanged(),
|
||||
flow4 = searchManager.query,
|
||||
flow5 = hasRestrictionForSellFlow(),
|
||||
) { maybeTokenList, appCurrency, isBalanceHidden, query, hasRestrictionForSell ->
|
||||
val currencies = maybeTokenList.getOrElse(
|
||||
ifLoading = { it ?: TokenList.Empty },
|
||||
ifError = { TokenList.Empty },
|
||||
)
|
||||
.flattenCurrencies()
|
||||
|
||||
val filterByQueryTokenList = currencies
|
||||
.filterByQuery(query = query)
|
||||
|
||||
if (query.isNotEmpty() && filterByQueryTokenList.isEmpty()) {
|
||||
SetNothingToFoundStateTransformer(
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
emptySearchMessageReference = getEmptySearchMessageReference(),
|
||||
)
|
||||
} else {
|
||||
val isInsufficientBalanceForSell = if (params.filterOperation == OnrampOperation.SELL) {
|
||||
maybeTokenList.isInsufficientBalanceForSell()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
UpdateTokenItemsTransformer(
|
||||
appCurrency = appCurrency,
|
||||
onItemClick = ::onTokenClick,
|
||||
statuses = filterByQueryTokenList.let { statuses ->
|
||||
if (hasRestrictionForSell || isInsufficientBalanceForSell) {
|
||||
mapOf(false to statuses)
|
||||
} else {
|
||||
statuses.filterByAvailability()
|
||||
}
|
||||
},
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
unavailableTokensHeaderReference = getUnavailableTokensHeaderReference(),
|
||||
warning = when {
|
||||
hasRestrictionForSell -> NotificationUM.Warning.SellingRegionalRestriction
|
||||
isInsufficientBalanceForSell -> NotificationUM.Warning.InsufficientBalanceForSelling
|
||||
else -> null
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
.onEach(::updateTokenListUM)
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnUpdateStateV2() {
|
||||
combine(
|
||||
flow = singleAccountStatusListSupplier(
|
||||
SingleAccountStatusListProducer.Params(params.userWalletId),
|
||||
|
|
@ -160,7 +96,7 @@ internal class OnrampTokenListModel @Inject constructor(
|
|||
|
||||
if (query.isNotEmpty() && filterByQueryAccountList.isEmpty()) {
|
||||
updateTokenListUM(
|
||||
SetNothingToFoundStateTransformerV2(
|
||||
SetNothingToFoundStateTransformer(
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
emptySearchMessageReference = getEmptySearchMessageReference(),
|
||||
),
|
||||
|
|
@ -211,16 +147,6 @@ internal class OnrampTokenListModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun Lce<TokenListError, TokenList>.isInsufficientBalanceForSell(): Boolean {
|
||||
return if (params.filterOperation == OnrampOperation.SELL) {
|
||||
isContent {
|
||||
(it.totalFiatBalance as? TotalFiatBalance.Loaded)?.amount?.isZero() == true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun AccountStatusList.isInsufficientBalanceForSell(): Boolean {
|
||||
return if (params.filterOperation == OnrampOperation.SELL) {
|
||||
(totalFiatBalance as? TotalFiatBalance.Loaded)?.amount?.isZero() == true
|
||||
|
|
@ -268,13 +194,8 @@ internal class OnrampTokenListModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun isFirstInitialization(prevState: TokenListUM, newState: TokenListUM): Boolean {
|
||||
return if (accountsFeatureToggles.isFeatureEnabled) {
|
||||
prevState.tokensListData == TokenListUMData.EmptyList &&
|
||||
newState.tokensListData != TokenListUMData.EmptyList
|
||||
} else {
|
||||
prevState.availableItems.isEmpty() && prevState.unavailableItems.isEmpty() &&
|
||||
(newState.availableItems.isNotEmpty() || newState.unavailableItems.isNotEmpty())
|
||||
}
|
||||
return prevState.tokensListData == TokenListUMData.EmptyList &&
|
||||
newState.tokensListData != TokenListUMData.EmptyList
|
||||
}
|
||||
|
||||
private fun onTokenClick(tokenItemState: TokenItemState, status: CryptoCurrencyStatus) {
|
||||
|
|
@ -333,41 +254,6 @@ internal class OnrampTokenListModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun List<CryptoCurrencyStatus>.filterByAvailability(): Map<Boolean, List<CryptoCurrencyStatus>> {
|
||||
return coroutineScope {
|
||||
map { status ->
|
||||
async {
|
||||
val isOperationAvailable = checkAvailabilityByOperation(status = status)
|
||||
val isNotMissedDerivation = status.value !is CryptoCurrencyStatus.MissedDerivation
|
||||
val isNotLoading = status.value !is CryptoCurrencyStatus.Loading
|
||||
|
||||
val requirements = getAssetRequirementsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
currency = status.currency,
|
||||
).getOrNull()
|
||||
|
||||
val isAvailableForBuy = rampStateManager.checkAssetRequirements(requirements)
|
||||
val isNotUnreachable = status.value !is CryptoCurrencyStatus.Unreachable
|
||||
|
||||
val isAvailable = when (params.filterOperation) {
|
||||
OnrampOperation.BUY -> {
|
||||
isAvailableForBuy
|
||||
} // unreachable state is available for Buy operation
|
||||
OnrampOperation.SELL -> isNotUnreachable
|
||||
OnrampOperation.SWAP -> {
|
||||
isNotUnreachable && isAvailableForBuy
|
||||
}
|
||||
}
|
||||
|
||||
status to (isOperationAvailable && isNotMissedDerivation && isNotLoading && isAvailable)
|
||||
}
|
||||
}
|
||||
.awaitAll()
|
||||
.groupBy(Pair<CryptoCurrencyStatus, Boolean>::second)
|
||||
.mapValues { it.value.map(Pair<CryptoCurrencyStatus, Boolean>::first) }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun AccountCryptoList.filterByAvailability(): List<AccountAvailabilityUM> {
|
||||
return coroutineScope {
|
||||
map { (account, currencies) ->
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue