Updated on 2026-08-14
This commit is contained in:
parent
dff5b1f27c
commit
978d7c1da4
20 changed files with 202 additions and 855 deletions
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue