Updated on 2026-08-14
This commit is contained in:
parent
1326b5bab3
commit
778b255d2d
5 changed files with 120 additions and 35 deletions
|
|
@ -27,22 +27,25 @@ interface ChooseTokenBridge : ChooseTokenBridgeLegacy, ChooseTokenBridgeInternal
|
|||
/**
|
||||
* for some Feature specific tokens filtering
|
||||
*/
|
||||
val tokenFilter: MutableStateFlow<(AccountStatus.CryptoPortfolio, CryptoCurrencyStatus) -> Boolean>
|
||||
val tokenFilter: MutableStateFlow<(AccountStatus, CryptoCurrencyStatus) -> Boolean>
|
||||
|
||||
fun selectWalletTab(walletId: UserWalletId)
|
||||
|
||||
data class Settings(
|
||||
val title: TextReference,
|
||||
val isShowMarketBlock: Boolean,
|
||||
val isShowPaymentAccount: Boolean,
|
||||
) {
|
||||
companion object {
|
||||
val SwapFrom = Settings(
|
||||
title = resourceReference(R.string.swapping_from_title),
|
||||
isShowMarketBlock = false,
|
||||
isShowPaymentAccount = true,
|
||||
)
|
||||
val SwapTo = Settings(
|
||||
title = resourceReference(R.string.swapping_to_title),
|
||||
isShowMarketBlock = true,
|
||||
isShowPaymentAccount = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ internal class DefaultChooseTokenBridge @AssistedInject constructor(
|
|||
private val portfolioListBlockDelegate: PortfolioListBlockDelegate = portfolioListBlockDelegateFactory.create(
|
||||
modelScope = modelScope,
|
||||
searchQueryState = searchQueryState,
|
||||
featureSettings = settings,
|
||||
)
|
||||
|
||||
private val portfolioFullBlockDelegate: PortfolioFullBlockDelegate = portfolioFullBlockDelegateFactory.create(
|
||||
|
|
@ -52,7 +53,7 @@ internal class DefaultChooseTokenBridge @AssistedInject constructor(
|
|||
portfolioListBlockDelegate = portfolioListBlockDelegate,
|
||||
)
|
||||
|
||||
override val tokenFilter: MutableStateFlow<(AccountStatus.CryptoPortfolio, CryptoCurrencyStatus) -> Boolean>
|
||||
override val tokenFilter: MutableStateFlow<(AccountStatus, CryptoCurrencyStatus) -> Boolean>
|
||||
get() = portfolioListBlockDelegate.tokenFilter
|
||||
|
||||
override val fullPortfolioBlock: StateFlow<ChooseTokenPortfolioFullBlockUM?>
|
||||
|
|
|
|||
|
|
@ -2,19 +2,25 @@ package com.tangem.feature.swap.choosetoken.impl.converter
|
|||
|
||||
import com.tangem.common.ui.account.AccountCryptoPortfolioItemStateConverter
|
||||
import com.tangem.common.ui.account.TokensListPortfolioItemConverter
|
||||
import com.tangem.common.ui.account.toUM
|
||||
import com.tangem.common.ui.tokens.TokenConverterParams
|
||||
import com.tangem.common.ui.tokens.TokenItemGrouping.toGroupedItems
|
||||
import com.tangem.common.ui.tokens.TokenItemGrouping.toUngroupedItems
|
||||
import com.tangem.common.ui.tokens.TokenItemStateConverter
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
|
||||
import com.tangem.core.ui.components.icons.IconTint
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState.FiatAmountState
|
||||
import com.tangem.core.ui.components.tokenlist.state.PortfolioTokensListItemUM
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.pluralReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
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.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.account.filterCryptoPortfolio
|
||||
import com.tangem.domain.models.account.PaymentAccountStatusValue
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.feature.swap.choosetoken.api.ChooseTokenBridgeInternal.SearchQuery
|
||||
|
|
@ -29,7 +35,8 @@ internal class ChooseTokenListItemConverter(
|
|||
private val params: TokenConverterParams,
|
||||
private val clickIntents: ClickIntents,
|
||||
private val searchQuery: SearchQuery,
|
||||
private val tokenFilter: (AccountStatus.CryptoPortfolio, CryptoCurrencyStatus) -> Boolean,
|
||||
private val tokenFilter: (AccountStatus, CryptoCurrencyStatus) -> Boolean,
|
||||
private val isShowPaymentAccount: Boolean,
|
||||
) {
|
||||
|
||||
private val isSearchingState: Boolean get() = searchQuery.isSearchingState
|
||||
|
|
@ -39,6 +46,24 @@ internal class ChooseTokenListItemConverter(
|
|||
clickIntents.onTokenItemClick(account, currencyStatus)
|
||||
}
|
||||
|
||||
private val onAccountItemClick: (Account, isExpanded: Boolean) -> Unit = { clickedAccount, isExpanded ->
|
||||
if (isExpanded) {
|
||||
clickIntents.onAccountCollapseClick(clickedAccount)
|
||||
} else {
|
||||
clickIntents.onAccountExpandClick(clickedAccount)
|
||||
}
|
||||
}
|
||||
|
||||
private val fiatAmountStateProvider: ((TotalFiatBalance, isExpanded: Boolean) -> FiatAmountState?) =
|
||||
{ totalBalance, isExpanded ->
|
||||
when {
|
||||
isSearchingState -> FiatAmountState.Empty
|
||||
!isExpanded -> FiatAmountState.Icon(R.drawable.ic_chewron_down_20, IconTint.Informative)
|
||||
else -> AccountCryptoPortfolioItemStateConverter
|
||||
.createFiatAmountState(totalBalance, appCurrency)
|
||||
}
|
||||
}
|
||||
|
||||
private fun tokenStatusConverter(account: AccountStatus) = TokenItemStateConverter(
|
||||
appCurrency = appCurrency,
|
||||
onItemClick = { _, status -> onTokenClick(account, status) },
|
||||
|
|
@ -58,8 +83,12 @@ internal class ChooseTokenListItemConverter(
|
|||
private fun convertAccountList(params: TokenConverterParams.Account): TokenListUMData {
|
||||
val accountList = params.accountList
|
||||
val accountItems = accountList.accountStatuses
|
||||
.filterCryptoPortfolio()
|
||||
.map { accountStatus -> accountStatus.toPortfolioItem(params) }
|
||||
.mapNotNull { accountStatus ->
|
||||
when (accountStatus) {
|
||||
is AccountStatus.CryptoPortfolio -> accountStatus.toPortfolioItem(params)
|
||||
is AccountStatus.Payment -> accountStatus.createPaymentAccountItem(params.expandedAccounts)
|
||||
}
|
||||
}
|
||||
.filter { portfolio -> portfolio.tokens.isNotEmpty() }
|
||||
if (accountItems.isEmpty()) {
|
||||
return TokenListUMData.EmptyList
|
||||
|
|
@ -77,19 +106,7 @@ internal class ChooseTokenListItemConverter(
|
|||
val account: Account.CryptoPortfolio = this.account
|
||||
val isExpanded = isSearchingState || params.expandedAccounts.contains(account.accountId)
|
||||
val onItemClick: (Account.CryptoPortfolio) -> Unit = { clickedAccount ->
|
||||
if (isExpanded) {
|
||||
clickIntents.onAccountCollapseClick(clickedAccount)
|
||||
} else {
|
||||
clickIntents.onAccountExpandClick(clickedAccount)
|
||||
}
|
||||
}
|
||||
val fiatAmountStateProvider: ((TotalFiatBalance) -> FiatAmountState?) = { totalBalance ->
|
||||
when {
|
||||
isSearchingState -> FiatAmountState.Empty
|
||||
!isExpanded -> FiatAmountState.Icon(R.drawable.ic_chewron_down_20, IconTint.Informative)
|
||||
else -> AccountCryptoPortfolioItemStateConverter
|
||||
.createFiatAmountState(totalBalance, appCurrency)
|
||||
}
|
||||
onAccountItemClick(clickedAccount, isExpanded)
|
||||
}
|
||||
|
||||
val converter = AccountCryptoPortfolioItemStateConverter(
|
||||
|
|
@ -97,7 +114,7 @@ internal class ChooseTokenListItemConverter(
|
|||
account = account,
|
||||
onItemClick = onItemClick.takeIf { !isSearchingState },
|
||||
priceChangeLce = this.priceChangeLce,
|
||||
fiatAmountStateProvider = fiatAmountStateProvider,
|
||||
fiatAmountStateProvider = { fiatBalance -> fiatAmountStateProvider(fiatBalance, isExpanded) },
|
||||
subtitle2StateProvider = { _ -> null },
|
||||
)
|
||||
val accountItem = converter.convert(tokenList.totalFiatBalance)
|
||||
|
|
@ -130,21 +147,20 @@ internal class ChooseTokenListItemConverter(
|
|||
}
|
||||
}
|
||||
|
||||
private fun filterTokenList(tokenList: TokenList, account: AccountStatus.CryptoPortfolio): TokenList {
|
||||
fun List<CryptoCurrencyStatus>.filterCurrencies(): List<CryptoCurrencyStatus> = filter { currency ->
|
||||
currency.filterByQuery() && tokenFilter(account, currency)
|
||||
}
|
||||
private fun List<CryptoCurrencyStatus>.filterCurrencies(account: AccountStatus): List<CryptoCurrencyStatus> =
|
||||
filter { currency -> currency.filterByQuery() && tokenFilter(account, currency) }
|
||||
|
||||
private fun filterTokenList(tokenList: TokenList, account: AccountStatus.CryptoPortfolio): TokenList {
|
||||
return when (tokenList) {
|
||||
TokenList.Empty -> TokenList.Empty
|
||||
is TokenList.Ungrouped -> {
|
||||
val filtered = tokenList.currencies.filterCurrencies()
|
||||
val filtered = tokenList.currencies.filterCurrencies(account)
|
||||
if (filtered.isEmpty()) TokenList.Empty else tokenList.copy(currencies = filtered)
|
||||
}
|
||||
is TokenList.GroupedByNetwork -> {
|
||||
val filteredGroups = tokenList.groups
|
||||
.map { group ->
|
||||
val filteredCurrencies = group.currencies.filterCurrencies()
|
||||
val filteredCurrencies = group.currencies.filterCurrencies(account)
|
||||
group.copy(currencies = filteredCurrencies)
|
||||
}
|
||||
.filter { group -> group.currencies.isNotEmpty() }
|
||||
|
|
@ -159,4 +175,56 @@ internal class ChooseTokenListItemConverter(
|
|||
currency.symbol.contains(searchQuery.value, ignoreCase = true)
|
||||
return isSearchFilter
|
||||
}
|
||||
|
||||
private fun AccountStatus.Payment.createPaymentAccountItem(
|
||||
expandedAccounts: Set<AccountId>,
|
||||
): TokensListItemUM.Portfolio? {
|
||||
if (!isShowPaymentAccount) return null
|
||||
val paymentCurrency: CryptoCurrencyStatus = when (val status = this.value) {
|
||||
is PaymentAccountStatusValue.Error,
|
||||
is PaymentAccountStatusValue.IssuingCard,
|
||||
PaymentAccountStatusValue.NotCreated,
|
||||
is PaymentAccountStatusValue.UnderReview,
|
||||
PaymentAccountStatusValue.Loading,
|
||||
-> return null
|
||||
is PaymentAccountStatusValue.Loaded -> status.cryptoCurrencyStatus
|
||||
is PaymentAccountStatusValue.Locked -> status.cryptoCurrencyStatus
|
||||
}
|
||||
val account = this.account
|
||||
val tokensCount = 1
|
||||
val isExpanded = isSearchingState || expandedAccounts.contains(account.accountId)
|
||||
val onItemClick: (TokenItemState) -> Unit = {
|
||||
onAccountItemClick(account, isExpanded)
|
||||
}
|
||||
val fiatBalance: TotalFiatBalance = this.value.totalFiatBalance
|
||||
val fiatAmountState = fiatAmountStateProvider(fiatBalance, isExpanded)
|
||||
val paymentAccountItem = TokenItemState.Content(
|
||||
id = account.accountId.value,
|
||||
iconState = CurrencyIconState.PaymentAccount(),
|
||||
titleState = TokenItemState.TitleState.Content(text = account.accountName.toUM().value),
|
||||
subtitleState = TokenItemState.SubtitleState.TextContent(
|
||||
value = pluralReference(
|
||||
R.plurals.common_tokens_count,
|
||||
count = tokensCount,
|
||||
formatArgs = wrappedList(tokensCount),
|
||||
),
|
||||
isAvailable = false,
|
||||
),
|
||||
onItemClick = onItemClick.takeIf { !isSearchingState },
|
||||
fiatAmountState = fiatAmountState,
|
||||
subtitle2State = null,
|
||||
onItemLongClick = null,
|
||||
)
|
||||
val tokenConverter = tokenStatusConverter(this)
|
||||
val filtered = listOf(paymentCurrency)
|
||||
.filterCurrencies(this)
|
||||
.map { currency -> TokensListItemUM.Token(tokenConverter.convert(currency)) }
|
||||
|
||||
return TokensListPortfolioItemConverter(
|
||||
tokenItemUM = paymentAccountItem,
|
||||
isExpanded = isExpanded,
|
||||
isCollapsable = !isSearchingState,
|
||||
tokens = filtered.toPersistentList(),
|
||||
).convert(Unit)
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
|
|
@ -36,7 +37,7 @@ internal class PortfolioFullBlockDelegate @AssistedInject constructor(
|
|||
) {
|
||||
|
||||
private val isSearchingState: Boolean get() = searchQueryState.isSearchingState
|
||||
private val onWalletSelected = Channel<UserWalletId>()
|
||||
private val onWalletSelected = Channel<UserWalletId>(capacity = Channel.BUFFERED)
|
||||
|
||||
val selectedWalletFlow: SharedFlow<UserWallet> = onWalletSelected.receiveAsFlow()
|
||||
.distinctUntilChanged()
|
||||
|
|
@ -47,14 +48,18 @@ internal class PortfolioFullBlockDelegate @AssistedInject constructor(
|
|||
.flowOn(dispatchers.default)
|
||||
.stateIn(modelScope, SharingStarted.Eagerly, initialValue = null)
|
||||
|
||||
private fun buildFlow() = flow {
|
||||
val isNoHaveSelectedWallet = selectedWalletFlow.replayCache.isEmpty()
|
||||
if (isNoHaveSelectedWallet) {
|
||||
val firstSelectedWallet = selectedWalletUseCase.sync().getOrNull()
|
||||
?: getWalletsUseCase.invokeSync().first()
|
||||
selectWalletTab(firstSelectedWallet.walletId)
|
||||
init {
|
||||
val globalSelectedWallet = selectedWalletUseCase.sync().getOrNull()
|
||||
val allWallets = getWalletsUseCase.invokeSync().filter { it.isMultiCurrency }
|
||||
val firstSelectedWallet = when {
|
||||
globalSelectedWallet?.isMultiCurrency == true -> globalSelectedWallet
|
||||
allWallets.isNotEmpty() -> allWallets.first()
|
||||
else -> null
|
||||
}
|
||||
if (firstSelectedWallet != null) selectWalletTab(firstSelectedWallet.walletId)
|
||||
}
|
||||
|
||||
private fun buildFlow() = flow {
|
||||
val fullPortfolioBlockFlow = combine(
|
||||
flow = getWalletsUseCase.invokeAsMap(),
|
||||
flow2 = portfolioListBlockDelegate.portfolioList,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.feature.swap.choosetoken.api.ChooseTokenAnalyticsPayload
|
||||
import com.tangem.feature.swap.choosetoken.api.ChooseTokenBridge
|
||||
import com.tangem.feature.swap.choosetoken.api.ChooseTokenBridgeInternal.SearchQuery
|
||||
import com.tangem.feature.swap.choosetoken.api.ChooseTokenBridgeInternal.SearchQuery.Companion.isSearchingState
|
||||
import com.tangem.feature.swap.choosetoken.api.ChooseTokenResult
|
||||
|
|
@ -26,6 +27,7 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class PortfolioListBlockDelegate @AssistedInject constructor(
|
||||
private val expandedAccountsHolder: ChooseTokenExpandedAccountsHolder,
|
||||
private val settingContext: SettingContextUseCase,
|
||||
|
|
@ -33,12 +35,13 @@ internal class PortfolioListBlockDelegate @AssistedInject constructor(
|
|||
private val getWalletsUseCase: GetWalletsUseCase,
|
||||
@Assisted private val modelScope: CoroutineScope,
|
||||
@Assisted private val searchQueryState: StateFlow<SearchQuery>,
|
||||
@Assisted private val featureSettings: ChooseTokenBridge.Settings,
|
||||
) : ClickIntents {
|
||||
|
||||
private val onTokenItemClick: Channel<Pair<AccountStatus, CryptoCurrencyStatus>> = Channel()
|
||||
|
||||
val onTokenChosen: Channel<ChooseTokenResult> = Channel()
|
||||
val tokenFilter: MutableStateFlow<(AccountStatus.CryptoPortfolio, CryptoCurrencyStatus) -> Boolean> =
|
||||
val tokenFilter: MutableStateFlow<(AccountStatus, CryptoCurrencyStatus) -> Boolean> =
|
||||
MutableStateFlow { _, _ -> true }
|
||||
|
||||
val portfolioList: SharedFlow<Map<UserWalletId, TokenListUMData>> = buildDataFlow()
|
||||
|
|
@ -89,6 +92,7 @@ internal class PortfolioListBlockDelegate @AssistedInject constructor(
|
|||
clickIntents = this@PortfolioListBlockDelegate,
|
||||
searchQuery = searchQuery,
|
||||
tokenFilter = tokenFilter,
|
||||
isShowPaymentAccount = featureSettings.isShowPaymentAccount,
|
||||
).convert()
|
||||
|
||||
um
|
||||
|
|
@ -132,7 +136,11 @@ internal class PortfolioListBlockDelegate @AssistedInject constructor(
|
|||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(searchQueryState: StateFlow<SearchQuery>, modelScope: CoroutineScope): PortfolioListBlockDelegate
|
||||
fun create(
|
||||
searchQueryState: StateFlow<SearchQuery>,
|
||||
modelScope: CoroutineScope,
|
||||
featureSettings: ChooseTokenBridge.Settings,
|
||||
): PortfolioListBlockDelegate
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue