Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-03 11:21:00 +04:00
parent dff5b1f27c
commit 978d7c1da4
20 changed files with 202 additions and 855 deletions

View file

@ -1,113 +1,80 @@
package com.tangem.feature.swap.converters
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.feature.swap.domain.models.domain.CryptoCurrencySwapInfo
import com.tangem.feature.swap.models.CurrenciesGroupWithFromCurrency
import com.tangem.feature.swap.domain.models.ui.CurrenciesGroup
import com.tangem.feature.swap.models.SwapSelectTokenStateHolder
import com.tangem.feature.swap.models.TokenBalanceData
import com.tangem.feature.swap.models.SwapStateHolder
import com.tangem.feature.swap.models.TokenListUMData
import com.tangem.feature.swap.models.TokenToSelectState
import com.tangem.feature.swap.presentation.R
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.collections.immutable.plus
import kotlinx.collections.immutable.toPersistentList
internal class TokensDataConverter(
private val onSearchEntered: (String) -> Unit,
private val onTokenSelected: (String) -> Unit,
private val isBalanceHiddenProvider: Provider<Boolean>,
private val appCurrencyProvider: Provider<AppCurrency>,
) : Converter<CurrenciesGroupWithFromCurrency, SwapSelectTokenStateHolder> {
private val tokensDataState: CurrenciesGroup,
private val isBalanceHidden: Boolean,
private val isAccountsMode: Boolean,
appCurrencyProvider: Provider<AppCurrency>,
) : Transformer<SwapStateHolder> {
override fun convert(value: CurrenciesGroupWithFromCurrency): SwapSelectTokenStateHolder {
val group = value.group
val availableTitle = TokenToSelectState.Title(
resourceReference(R.string.exchange_tokens_available_tokens_header),
)
val allTokens = group.available + group.unavailable
return SwapSelectTokenStateHolder(
availableTokens = allTokens.map { tokenWithBalanceToTokenToSelect(it, true) }
.toMutableList()
.apply {
if (this.isNotEmpty()) {
this.add(0, availableTitle)
private val accountListItemConverter = AccountTokenItemConverter(
appCurrency = appCurrencyProvider(),
unavailableErrorText = resourceReference(R.string.tokens_list_unavailable_to_swap_source_header),
onItemClick = onTokenSelected,
)
override fun transform(prevState: SwapStateHolder): SwapStateHolder {
val accountList = tokensDataState.accountCurrencyList
val currentMarketsState = prevState.selectTokenState?.marketsState
return prevState.copy(
selectTokenState = SwapSelectTokenStateHolder(
availableTokens = persistentListOf(),
unavailableTokens = persistentListOf(),
tokensListData = if (isAccountsMode) {
val portfolioList = accountListItemConverter.convertList(accountList).toPersistentList()
val totalTokensCount = portfolioList.sumOf { it.tokens.size }
if (totalTokensCount > 0) {
TokenListUMData.AccountList(
tokensList = portfolioList,
totalTokensCount = totalTokensCount,
)
} else {
TokenListUMData.EmptyList
}
}
.toImmutableList(),
unavailableTokens = persistentListOf(),
tokensListData = TokenListUMData.EmptyList,
onSearchEntered = onSearchEntered,
onTokenSelected = onTokenSelected,
isBalanceHidden = isBalanceHiddenProvider(),
isAfterSearch = group.isAfterSearch,
)
}
} else {
val tokensList = accountList.flatMap { (_, currencyList) ->
currencyList.asSequence().map { accountSwapCurrency ->
accountListItemConverter.createAvailableItemConverter()
.convert(accountSwapCurrency.cryptoCurrencyStatus)
}.map(TokensListItemUM::Token).toPersistentList()
}.toPersistentList()
private fun tokenWithBalanceToTokenToSelect(
cryptoCurrencySwapInfo: CryptoCurrencySwapInfo,
isAvailable: Boolean,
): TokenToSelectState {
val cryptoCurrencyStatus = cryptoCurrencySwapInfo.currencyStatus
return TokenToSelectState.TokenToSelect(
id = cryptoCurrencyStatus.currency.id.value,
name = cryptoCurrencyStatus.currency.name,
symbol = cryptoCurrencyStatus.currency.symbol,
isAvailable = isAvailable,
tokenIcon = convertIcon(cryptoCurrencyStatus.currency, isAvailable),
addedTokenBalanceData = TokenBalanceData(
amount = formatCryptoAmount(cryptoCurrencyStatus),
amountEquivalent = formatFiatAmount(cryptoCurrencyStatus, appCurrencyProvider.invoke()),
isBalanceHidden = isBalanceHiddenProvider.invoke(),
if (tokensList.isNotEmpty()) {
TokenListUMData.TokenList(
tokensList = persistentListOf(
TokensListItemUM.GroupTitle(
id = "available_tokens_title",
text = resourceReference(R.string.exchange_tokens_available_tokens_header),
),
) + tokensList,
totalTokensCount = tokensList.size,
)
} else {
TokenListUMData.EmptyList
}
},
marketsState = currentMarketsState,
onSearchEntered = onSearchEntered,
onTokenSelected = onTokenSelected,
isBalanceHidden = isBalanceHidden,
isAfterSearch = tokensDataState.isAfterSearch,
),
)
}
private fun convertIcon(currency: CryptoCurrency, isAvailable: Boolean): CurrencyIconState {
return when (currency) {
is CryptoCurrency.Coin -> {
CurrencyIconState.CoinIcon(
url = currency.iconUrl,
fallbackResId = currency.networkIconResId,
isGrayscale = !isAvailable,
shouldShowCustomBadge = currency.isCustom,
)
}
is CryptoCurrency.Token -> {
val isGrayscale = currency.network.isTestnet
val background = currency.tryGetBackgroundForTokenIcon(isGrayscale)
val tint = getTintForTokenIcon(background)
CurrencyIconState.TokenIcon(
url = currency.iconUrl,
isGrayscale = !isAvailable,
shouldShowCustomBadge = currency.isCustom,
topBadgeIconResId = currency.networkIconResId,
fallbackTint = tint,
fallbackBackground = background,
)
}
}
}
private fun formatCryptoAmount(cryptoCurrencyStatus: CryptoCurrencyStatus): String {
return cryptoCurrencyStatus.value.amount.format {
crypto(cryptoCurrencyStatus.currency)
}
}
private fun formatFiatAmount(cryptoCurrencyStatus: CryptoCurrencyStatus, appCurrency: AppCurrency): String {
return cryptoCurrencyStatus.value.fiatAmount.format {
fiat(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
)
}
}
}

View file

@ -1,80 +0,0 @@
package com.tangem.feature.swap.converters
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.feature.swap.domain.models.ui.CurrenciesGroup
import com.tangem.feature.swap.models.SwapSelectTokenStateHolder
import com.tangem.feature.swap.models.SwapStateHolder
import com.tangem.feature.swap.models.TokenListUMData
import com.tangem.feature.swap.presentation.R
import com.tangem.utils.Provider
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.plus
import kotlinx.collections.immutable.toPersistentList
internal class TokensDataConverterV2(
private val onSearchEntered: (String) -> Unit,
private val onTokenSelected: (String) -> Unit,
private val tokensDataState: CurrenciesGroup,
private val isBalanceHidden: Boolean,
private val isAccountsMode: Boolean,
appCurrencyProvider: Provider<AppCurrency>,
) : Transformer<SwapStateHolder> {
private val accountListItemConverter = AccountTokenItemConverter(
appCurrency = appCurrencyProvider(),
unavailableErrorText = resourceReference(R.string.tokens_list_unavailable_to_swap_source_header),
onItemClick = onTokenSelected,
)
override fun transform(prevState: SwapStateHolder): SwapStateHolder {
val accountList = tokensDataState.accountCurrencyList
val currentMarketsState = prevState.selectTokenState?.marketsState
return prevState.copy(
selectTokenState = SwapSelectTokenStateHolder(
availableTokens = persistentListOf(),
unavailableTokens = persistentListOf(),
tokensListData = if (isAccountsMode) {
val portfolioList = accountListItemConverter.convertList(accountList).toPersistentList()
val totalTokensCount = portfolioList.sumOf { it.tokens.size }
if (totalTokensCount > 0) {
TokenListUMData.AccountList(
tokensList = portfolioList,
totalTokensCount = totalTokensCount,
)
} else {
TokenListUMData.EmptyList
}
} else {
val tokensList = accountList.flatMap { (_, currencyList) ->
currencyList.asSequence().map { accountSwapCurrency ->
accountListItemConverter.createAvailableItemConverter()
.convert(accountSwapCurrency.cryptoCurrencyStatus)
}.map(TokensListItemUM::Token).toPersistentList()
}.toPersistentList()
if (tokensList.isNotEmpty()) {
TokenListUMData.TokenList(
tokensList = persistentListOf(
TokensListItemUM.GroupTitle(
id = "available_tokens_title",
text = resourceReference(R.string.exchange_tokens_available_tokens_header),
),
) + tokensList,
totalTokensCount = tokensList.size,
)
} else {
TokenListUMData.EmptyList
}
},
marketsState = currentMarketsState,
onSearchEntered = onSearchEntered,
onTokenSelected = onTokenSelected,
isBalanceHidden = isBalanceHidden,
isAfterSearch = tokensDataState.isAfterSearch,
),
)
}
}

View file

@ -14,8 +14,6 @@ import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionState.InProgress.getApproveTypeOrNull
import com.tangem.features.approval.api.GiveApprovalComponent
import com.tangem.features.approval.api.GiveApprovalFeatureToggles
import com.tangem.common.ui.markets.models.MarketsListItemUM
import com.tangem.core.analytics.api.AnalyticsErrorHandler
import com.tangem.core.analytics.api.AnalyticsEventHandler
@ -36,7 +34,6 @@ import com.tangem.core.ui.message.EventMessageAction
import com.tangem.core.ui.utils.InputNumberFormatter
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.datasource.local.appsflyer.AppsFlyerStore
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.account.status.model.AccountCryptoCurrencyStatus
import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
@ -103,6 +100,8 @@ import com.tangem.feature.swap.router.SwapNavScreen
import com.tangem.feature.swap.router.SwapRouter
import com.tangem.feature.swap.ui.StateBuilder
import com.tangem.feature.swap.utils.formatToUIRepresentation
import com.tangem.features.approval.api.GiveApprovalComponent
import com.tangem.features.approval.api.GiveApprovalFeatureToggles
import com.tangem.features.feed.components.market.details.portfolio.add.AddToPortfolioComponent
import com.tangem.features.feed.components.market.details.portfolio.add.AddToPortfolioManager
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
@ -114,8 +113,6 @@ import com.tangem.utils.Provider
import com.tangem.utils.TangemBlogUrlBuilder.RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP
import com.tangem.utils.coroutines.*
import com.tangem.utils.isNullOrZero
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.delay
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import timber.log.Timber
@ -156,7 +153,6 @@ internal class SwapModel @Inject constructor(
router: AppRouter,
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
private val accountsFeatureToggles: AccountsFeatureToggles,
private val getTangemPayCurrencyStatusUseCase: GetTangemPayCurrencyStatusUseCase,
private val tangemPayWithdrawUseCase: TangemPayWithdrawUseCase,
private val iGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork,
@ -244,12 +240,12 @@ internal class SwapModel @Inject constructor(
private var toAccountCurrencyStatus: AccountCryptoCurrencyStatus? = null
/**
* If accountsFeatureToggles is off OR user came from Tangem Pay -> fromAccountCurrencyStatus == null
* If accountsFeatureToggles is on AND user didn't come from Tangem Pay -> fromAccountCurrencyStatus != null
* If user came from Tangem Pay -> fromAccountCurrencyStatus == null
* If user didn't come from Tangem Pay -> fromAccountCurrencyStatus != null
*
* Remove when accounts are integrated into Tangem Pay
*/
private val canUseFromAccountCurrencyStatus = accountsFeatureToggles.isFeatureEnabled && tangemPayInput == null
private val canUseFromAccountCurrencyStatus = tangemPayInput == null
private val isUserResolvableError: (SwapState) -> Boolean = { swapState ->
swapState is SwapState.SwapError &&
@ -514,25 +510,25 @@ internal class SwapModel @Inject constructor(
}.onSuccess { state ->
updateTokensState(state)
val (selectedCurrency, selectedAccount) = if (accountsFeatureToggles.isFeatureEnabled) {
val selectedAccountCurrency = toAccountCurrencyStatus ?: swapInteractor.getInitialCurrencyToSwapV2(
initialCryptoCurrency = initialCurrencyFrom,
state = state,
isReverseFromTo = isReverseFromTo,
)?.let { accountSwapCurrency ->
AccountCryptoCurrencyStatus(
account = accountSwapCurrency.account,
status = accountSwapCurrency.cryptoCurrencyStatus,
val (selectedCurrency, selectedAccount) = run {
var selectedAccountCurrency = toAccountCurrencyStatus
if (selectedAccountCurrency == null) {
val amountSwapCurrency = swapInteractor.getInitialCurrencyToSwap(
initialCryptoCurrency = initialCurrencyFrom,
state = state,
isReverseFromTo = isReverseFromTo,
)
if (amountSwapCurrency != null) {
selectedAccountCurrency = AccountCryptoCurrencyStatus(
account = amountSwapCurrency.account,
status = amountSwapCurrency.cryptoCurrencyStatus,
)
}
}
selectedAccountCurrency?.status to selectedAccountCurrency?.account
} else {
val selectedCurrency = initialToStatus ?: swapInteractor.getInitialCurrencyToSwap(
initialCryptoCurrency = initialCurrencyFrom,
state = state,
isReverseFromTo = isReverseFromTo,
)
selectedCurrency to null
}
applyInitialTokenChoice(
@ -695,19 +691,11 @@ internal class SwapModel @Inject constructor(
private fun updateTokensState(tokenDataState: TokensDataStateExpress) {
val tokensDataState = if (isOrderReversed) tokenDataState.fromGroup else tokenDataState.toGroup
uiState = if (accountsFeatureToggles.isFeatureEnabled) {
stateBuilder.addTokensToStateV2(
uiState = uiState,
tokensDataState = tokensDataState,
isAccountsMode = isAccountsMode,
)
} else {
stateBuilder.addTokensToState(
uiState = uiState,
tokensDataState = tokensDataState,
fromToken = dataState.fromCryptoCurrency?.currency ?: initialCurrencyFrom,
)
}
uiState = stateBuilder.addTokensToStateV2(
uiState = uiState,
tokensDataState = tokensDataState,
isAccountsMode = isAccountsMode,
)
latestMarketsState?.let(::applyMarketsState)
}
@ -1366,11 +1354,7 @@ internal class SwapModel @Inject constructor(
fromToken = foundToken
fromAccount = foundAccount
toToken = initialFromStatus
toAccount = if (accountsFeatureToggles.isFeatureEnabled) {
fromAccountCurrencyStatus?.account
} else {
null
}
toAccount = fromAccountCurrencyStatus?.account
val newToken = fromToken.currency as? CryptoCurrency.Coin
if (newToken != null) {
@ -1384,11 +1368,7 @@ internal class SwapModel @Inject constructor(
}
} else {
fromToken = initialFromStatus
fromAccount = if (accountsFeatureToggles.isFeatureEnabled) {
fromAccountCurrencyStatus?.account
} else {
null
}
fromAccount = fromAccountCurrencyStatus?.account
toToken = foundToken
toAccount = foundAccount
@ -1446,26 +1426,16 @@ internal class SwapModel @Inject constructor(
tokens: TokensDataStateExpress,
id: String,
): Pair<CryptoCurrencyStatus?, Account.CryptoPortfolio?> {
return if (accountsFeatureToggles.isFeatureEnabled) {
val accountCryptoCurrencyStatus = if (isOrderReversed) {
tokens.fromGroup
} else {
tokens.toGroup
}.accountCurrencyList.firstNotNullOfOrNull { accountSwapAvailability ->
accountSwapAvailability.currencyList.firstOrNull { accountSwapCurrency ->
accountSwapCurrency.cryptoCurrencyStatus.currency.id.value == id
}
}
accountCryptoCurrencyStatus?.cryptoCurrencyStatus to accountCryptoCurrencyStatus?.account
val accountCryptoCurrencyStatus = if (isOrderReversed) {
tokens.fromGroup
} else {
if (isOrderReversed) {
tokens.fromGroup
} else {
tokens.toGroup
}.available.firstOrNull { swapAvailability ->
swapAvailability.currencyStatus.currency.id.value == id
}?.currencyStatus to null
tokens.toGroup
}.accountCurrencyList.firstNotNullOfOrNull { accountSwapAvailability ->
accountSwapAvailability.currencyList.firstOrNull { accountSwapCurrency ->
accountSwapCurrency.cryptoCurrencyStatus.currency.id.value == id
}
}
return accountCryptoCurrencyStatus?.cryptoCurrencyStatus to accountCryptoCurrencyStatus?.account
}
@Suppress("LongMethod", "CyclomaticComplexMethod")
@ -1476,79 +1446,44 @@ internal class SwapModel @Inject constructor(
) {
Timber.d("Subscribe to ${coin.id} balance updates")
if (accountsFeatureToggles.isFeatureEnabled) {
getAccountCurrencyStatusUseCase(
userWalletId = userWalletId,
currency = coin,
).distinctUntilChanged { old, new -> old.status.value.amount == new.status.value.amount } // Check only balance changes
.onEach { (account, currencyStatus) ->
Timber.d("${coin.id} balance is ${currencyStatus.value.amount ?: "null"}")
getAccountCurrencyStatusUseCase(
userWalletId = userWalletId,
currency = coin,
).distinctUntilChanged { old, new -> old.status.value.amount == new.status.value.amount } // Check only balance changes
.onEach { (account, currencyStatus) ->
Timber.d("${coin.id} balance is ${currencyStatus.value.amount ?: "null"}")
if (isFromCurrency) {
dataState = dataState.copy(
feePaidCryptoCurrency = getFeePaidCryptoCurrencyStatusSyncUseCase(
userWalletId = userWalletId,
cryptoCurrencyStatus = currencyStatus,
).getOrNull() ?: currencyStatus,
)
}
uiState = when {
isFromCurrency && currencyStatus.currency.id == dataState.fromCryptoCurrency?.currency?.id -> {
dataState = dataState.copy(
fromCryptoCurrency = currencyStatus,
fromAccount = account,
)
stateBuilder.updateSendCurrencyBalance(uiState, currencyStatus)
}
!isFromCurrency && currencyStatus.currency.id == dataState.toCryptoCurrency?.currency?.id -> {
dataState = dataState.copy(
toCryptoCurrency = currencyStatus,
toAccount = account,
)
stateBuilder.updateReceiveCurrencyBalance(uiState, currencyStatus)
}
else -> {
uiState
}
}
startLoadingQuotesFromLastState(isSilent = true)
if (isFromCurrency) {
dataState = dataState.copy(
feePaidCryptoCurrency = getFeePaidCryptoCurrencyStatusSyncUseCase(
userWalletId = userWalletId,
cryptoCurrencyStatus = currencyStatus,
).getOrNull() ?: currencyStatus,
)
}
} else {
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet(
userWalletId = userWalletId,
currencyId = coin.id,
isSingleWalletWithTokens = false,
).mapNotNull { either -> (either as? Either.Right)?.value }
.distinctUntilChanged { old, new -> old.value.amount == new.value.amount } // Check only balance changes
.onEach { status ->
Timber.d("${coin.id} balance is ${status.value.amount ?: "null"}")
if (isFromCurrency) {
uiState = when {
isFromCurrency && currencyStatus.currency.id == dataState.fromCryptoCurrency?.currency?.id -> {
dataState = dataState.copy(
feePaidCryptoCurrency = getFeePaidCryptoCurrencyStatusSyncUseCase(
userWalletId = userWalletId,
cryptoCurrencyStatus = status,
).getOrNull() ?: status,
fromCryptoCurrency = currencyStatus,
fromAccount = account,
)
stateBuilder.updateSendCurrencyBalance(uiState, currencyStatus)
}
uiState = when {
isFromCurrency && status.currency.id == dataState.fromCryptoCurrency?.currency?.id -> {
dataState = dataState.copy(fromCryptoCurrency = status)
stateBuilder.updateSendCurrencyBalance(uiState, status)
}
!isFromCurrency && status.currency.id == dataState.toCryptoCurrency?.currency?.id -> {
dataState = dataState.copy(toCryptoCurrency = status)
stateBuilder.updateReceiveCurrencyBalance(uiState, status)
}
else -> {
uiState
}
!isFromCurrency && currencyStatus.currency.id == dataState.toCryptoCurrency?.currency?.id -> {
dataState = dataState.copy(
toCryptoCurrency = currencyStatus,
toAccount = account,
)
stateBuilder.updateReceiveCurrencyBalance(uiState, currencyStatus)
}
else -> {
uiState
}
startLoadingQuotesFromLastState(isSilent = true)
}
}.flowOn(dispatchers.main)
startLoadingQuotesFromLastState(isSilent = true)
}
.flowOn(dispatchers.main)
.launchIn(modelScope)
.saveIn(if (isFromCurrency) fromTokenBalanceJobHolder else toTokenBalanceJobHolder)
}
@ -2027,18 +1962,12 @@ internal class SwapModel @Inject constructor(
toToken.currency.id.value
}
return if (accountsFeatureToggles.isFeatureEnabled) {
groupToFind.accountCurrencyList.firstNotNullOfOrNull { (_, currencyList) ->
currencyList.find { accountSwapCurrency ->
idToFind == accountSwapCurrency.cryptoCurrencyStatus.currency.id.value &&
accountSwapCurrency.isAvailable
}
}?.providers
} else {
groupToFind.available.find { swapAvailability ->
idToFind == swapAvailability.currencyStatus.currency.id.value
}?.providers
}
return groupToFind.accountCurrencyList.firstNotNullOfOrNull { (_, currencyList) ->
currencyList.find { accountSwapCurrency ->
idToFind == accountSwapCurrency.cryptoCurrencyStatus.currency.id.value &&
accountSwapCurrency.isAvailable
}
}?.providers
?.filterForTangemPayWithdrawal()
.orEmpty()
}
@ -2082,16 +2011,10 @@ internal class SwapModel @Inject constructor(
val group = if (isReverseFromTo) state.fromGroup else state.toGroup
val idToFind = selectedCurrency.currency.id.value
return if (accountsFeatureToggles.isFeatureEnabled) {
group.accountCurrencyList.any { (_, currencyList) ->
currencyList.any { accountSwapCurrency ->
idToFind == accountSwapCurrency.cryptoCurrencyStatus.currency.id.value &&
accountSwapCurrency.isAvailable
}
}
} else {
group.available.any { swapAvailability ->
idToFind == swapAvailability.currencyStatus.currency.id.value
return group.accountCurrencyList.any { (_, currencyList) ->
currencyList.any { accountSwapCurrency ->
idToFind == accountSwapCurrency.cryptoCurrencyStatus.currency.id.value &&
accountSwapCurrency.isAvailable
}
}
}
@ -2128,14 +2051,10 @@ internal class SwapModel @Inject constructor(
val chosen = if (isOrderReversed) from else to
return if (accountsFeatureToggles.isFeatureEnabled) {
currenciesGroup.accountCurrencyList.flatMap { accountSwapAvailability ->
accountSwapAvailability.currencyList.map { accountSwapCurrency ->
accountSwapCurrency.cryptoCurrencyStatus
}
return currenciesGroup.accountCurrencyList.flatMap { accountSwapAvailability ->
accountSwapAvailability.currencyList.map { accountSwapCurrency ->
accountSwapCurrency.cryptoCurrencyStatus
}
} else {
currenciesGroup.available.map { swapAvailability -> swapAvailability.currencyStatus }
}.map { currencyStatus -> currencyStatus.currency }.contains(chosen.currency)
}

View file

@ -9,6 +9,7 @@ import com.tangem.common.ui.bottomsheet.permission.state.*
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.common.ui.swapStoriesScreen.SwapStoriesFactory
import com.tangem.common.ui.userwallet.ext.walletInterationIcon
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.extensions.*
@ -20,13 +21,11 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.domain.promo.models.StoryContent
import com.tangem.domain.transaction.usecase.gasless.IsGaslessFeeSupportedForNetwork
import com.tangem.feature.swap.converters.TokensDataConverter
import com.tangem.feature.swap.converters.TokensDataConverterV2
import com.tangem.feature.swap.domain.models.ExpressDataError
import com.tangem.feature.swap.domain.models.SwapAmount
import com.tangem.feature.swap.domain.models.domain.ExchangeProviderType
@ -62,7 +61,7 @@ internal class StateBuilder(
private val appCurrencyProvider: Provider<AppCurrency>,
private val isAccountsModeProvider: Provider<Boolean>,
private val iGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork,
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
) {
private val isHoldToConfirmEnabled: Boolean =
@ -70,13 +69,6 @@ internal class StateBuilder(
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
private val tokensDataConverter = TokensDataConverter(
onSearchEntered = actions.onSearchEntered,
onTokenSelected = actions.onTokenSelected,
isBalanceHiddenProvider = isBalanceHiddenProvider,
appCurrencyProvider = appCurrencyProvider,
)
private val notificationsFactory by lazy(LazyThreadSafetyMode.NONE) {
SwapNotificationsFactory(actions, iGaslessFeeSupportedForNetwork)
}
@ -677,28 +669,12 @@ internal class StateBuilder(
)
}
fun addTokensToState(
uiState: SwapStateHolder,
fromToken: CryptoCurrency,
tokensDataState: CurrenciesGroup,
): SwapStateHolder {
val currentMarketsState = uiState.selectTokenState?.marketsState
return uiState.copy(
selectTokenState = tokensDataConverter.convert(
value = CurrenciesGroupWithFromCurrency(
fromCurrency = fromToken,
group = tokensDataState,
),
).copy(marketsState = currentMarketsState),
)
}
fun addTokensToStateV2(
uiState: SwapStateHolder,
tokensDataState: CurrenciesGroup,
isAccountsMode: Boolean,
): SwapStateHolder {
return TokensDataConverterV2(
return TokensDataConverter(
onSearchEntered = actions.onSearchEntered,
onTokenSelected = actions.onTokenSelected,
appCurrencyProvider = appCurrencyProvider,