diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/api/ChooseTokenBridge.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/api/ChooseTokenBridge.kt index ea5546654e..41c4423225 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/api/ChooseTokenBridge.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/api/ChooseTokenBridge.kt @@ -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, ) } } diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/DefaultChooseTokenBridge.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/DefaultChooseTokenBridge.kt index c4074529a6..39dfd2fdc1 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/DefaultChooseTokenBridge.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/DefaultChooseTokenBridge.kt @@ -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 diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/converter/ChooseTokenListItemConverter.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/converter/ChooseTokenListItemConverter.kt index 774df42256..296375932e 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/converter/ChooseTokenListItemConverter.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/converter/ChooseTokenListItemConverter.kt @@ -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.filterCurrencies(): List = filter { currency -> - currency.filterByQuery() && tokenFilter(account, currency) - } + private fun List.filterCurrencies(account: AccountStatus): List = + 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, + ): 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) + } } \ No newline at end of file diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/model/PortfolioFullBlockDelegate.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/model/PortfolioFullBlockDelegate.kt index 86ebb93646..c19b107c11 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/model/PortfolioFullBlockDelegate.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/model/PortfolioFullBlockDelegate.kt @@ -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() + private val onWalletSelected = Channel(capacity = Channel.BUFFERED) val selectedWalletFlow: SharedFlow = 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, diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/model/PortfolioListBlockDelegate.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/model/PortfolioListBlockDelegate.kt index 9c932731af..ccbbf72f16 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/model/PortfolioListBlockDelegate.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/choosetoken/impl/model/PortfolioListBlockDelegate.kt @@ -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, + @Assisted private val featureSettings: ChooseTokenBridge.Settings, ) : ClickIntents { private val onTokenItemClick: Channel> = Channel() val onTokenChosen: Channel = Channel() - val tokenFilter: MutableStateFlow<(AccountStatus.CryptoPortfolio, CryptoCurrencyStatus) -> Boolean> = + val tokenFilter: MutableStateFlow<(AccountStatus, CryptoCurrencyStatus) -> Boolean> = MutableStateFlow { _, _ -> true } val portfolioList: SharedFlow> = 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, modelScope: CoroutineScope): PortfolioListBlockDelegate + fun create( + searchQueryState: StateFlow, + modelScope: CoroutineScope, + featureSettings: ChooseTokenBridge.Settings, + ): PortfolioListBlockDelegate } }