From 0a1a59d421df62ff6a122d22c8d5d91491fc4b10 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 13 Nov 2025 14:15:45 +0700 Subject: [PATCH] Updated on 2026-08-14 --- ...ccountCryptoPortfolioItemStateConverter.kt | 9 ++- .../account/ExpandedAccountsHolder.kt | 15 +++- .../presentation/wallet/ui/WalletScreen.kt | 70 +++++++++++++++++-- .../ui/components/common/WalletContent.kt | 6 +- .../MultiCurrencyAccountContent.kt | 38 +++++----- .../multicurrency/MultiCurrencyContent.kt | 5 +- 6 files changed, 105 insertions(+), 38 deletions(-) diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountCryptoPortfolioItemStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountCryptoPortfolioItemStateConverter.kt index 099ff971f8..c24dd49e89 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/account/AccountCryptoPortfolioItemStateConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountCryptoPortfolioItemStateConverter.kt @@ -16,6 +16,7 @@ import com.tangem.domain.core.lce.Lce import com.tangem.domain.models.StatusSource 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.quote.PriceChange import com.tangem.utils.converter.Converter import com.tangem.utils.extensions.isZero @@ -49,7 +50,7 @@ class AccountCryptoPortfolioItemStateConverter( ) } return TokenItemState.Content( - id = account.accountId.value, + id = account.accountId.toItemId(), iconState = AccountIconItemStateConverter.convert(this), titleState = TokenItemState.TitleState.Content( text = accountName.toUM().value, @@ -75,7 +76,7 @@ class AccountCryptoPortfolioItemStateConverter( private fun Account.CryptoPortfolio.mapToLoadingState(): TokenItemState.Content { return TokenItemState.Content( - id = account.accountId.value, + id = account.accountId.toItemId(), iconState = AccountIconItemStateConverter.convert(account), titleState = TokenItemState.TitleState.Content( text = accountName.toUM().value, @@ -97,7 +98,7 @@ class AccountCryptoPortfolioItemStateConverter( private fun Account.CryptoPortfolio.mapToUnreachableState(): TokenItemState.Unreachable { return TokenItemState.Unreachable( - id = account.accountId.value, + id = account.accountId.toItemId(), iconState = AccountIconItemStateConverter.convert(account), titleState = TokenItemState.TitleState.Content( text = accountName.toUM().value, @@ -119,6 +120,8 @@ class AccountCryptoPortfolioItemStateConverter( ) } + private fun AccountId.toItemId() = this.userWalletId.stringValue + this.value + private fun BigDecimal.getPriceChangeType(): PriceChangeType = PriceChangeConverter.fromBigDecimal(value = this) private fun StatusSource.isFlickering(): Boolean = this == StatusSource.CACHE diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/account/ExpandedAccountsHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/account/ExpandedAccountsHolder.kt index fc7ca61d39..2ae1796e77 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/account/ExpandedAccountsHolder.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/account/ExpandedAccountsHolder.kt @@ -4,6 +4,7 @@ import com.tangem.core.decompose.di.ModelScoped import com.tangem.domain.account.models.AccountList import com.tangem.domain.account.producer.SingleAccountListProducer import com.tangem.domain.account.supplier.SingleAccountListSupplier +import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase import com.tangem.domain.models.account.AccountId import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId @@ -13,13 +14,20 @@ import javax.inject.Inject @ModelScoped internal class ExpandedAccountsHolder @Inject constructor( private val singleAccountListSupplier: SingleAccountListSupplier, + private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase, ) { private val expandedAccounts = MutableStateFlow>>(mapOf()) fun expandedAccounts(userWallet: UserWallet): Flow> = channelFlow { - walletAccounts(userWallet) - .onEach { accountList -> + combine( + flow = walletAccounts(userWallet), + flow2 = isAccountsModeEnabledUseCase.invoke(), + transform = { accountList, isAccountsMode -> + if (!isAccountsMode) { + expandedAccounts.update { emptyMap() } + return@combine + } val isSingleAccount = accountList.accounts.size == 1 val defaultExpanded = when { isSingleAccount -> setOf(accountList.mainAccount.accountId) @@ -31,7 +39,8 @@ internal class ExpandedAccountsHolder @Inject constructor( if (isSingleAccount) expandedSet = defaultExpanded map.plus(userWallet.walletId to expandedSet) } - }.launchIn(this) + }, + ).launchIn(this) expandedAccounts .mapNotNull { map -> map[userWallet.walletId] } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index 47408a1b03..dadfe61af4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.* import androidx.compose.runtime.* +import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind @@ -55,6 +56,7 @@ import com.tangem.core.ui.components.rememberIsKeyboardVisible import com.tangem.core.ui.components.sheetscaffold.* import com.tangem.core.ui.components.snackbar.CopiedTextSnackbar import com.tangem.core.ui.components.snackbar.TangemSnackbar +import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.event.StateEvent import com.tangem.core.ui.extensions.stringResourceSafe @@ -76,6 +78,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistor import com.tangem.feature.wallet.presentation.wallet.ui.components.TokenActionsBottomSheet import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList import com.tangem.feature.wallet.presentation.wallet.ui.components.common.* +import com.tangem.feature.wallet.presentation.wallet.ui.components.fastForEach import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.nftCollections import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.organizeTokensButton import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.marketPriceBlock @@ -143,7 +146,7 @@ private fun WalletContent( */ val selectedWalletIndex by remember(state.selectedWalletIndex) { mutableIntStateOf(state.selectedWalletIndex) } val selectedWallet = state.wallets.getOrElse(selectedWalletIndex) { state.wallets[state.selectedWalletIndex] } - val selectedWalletChanged = rememberChangedOnce(selectedWalletIndex) + val (expandedState, collapsedState) = getExpandPortfolioStates(selectedWallet) val listState = rememberLazyListState() @@ -241,10 +244,16 @@ private fun WalletContent( contentItems( state = selectedWallet, - selectedWalletChanged = selectedWalletChanged, txHistoryItems = txHistoryItems, isBalanceHidden = state.isHidingMode, modifier = movableItemModifier, + portfolioVisibleState = { + findPortfolioVisibleState( + portfolio = it, + expandedState = expandedState, + collapsedState = collapsedState, + ) + }, ) nftCollections(state = selectedWallet, itemModifier = itemModifier) @@ -292,12 +301,59 @@ private fun WalletContent( ) } +private fun findPortfolioVisibleState( + portfolio: TokensListItemUM.Portfolio, + expandedState: SnapshotStateMap>, + collapsedState: SnapshotStateMap>, +): MutableTransitionState { + val portfolioKey = portfolio.id + + fun forceVisible() = MutableTransitionState(true).apply { targetState = true } + val shouldExpand = portfolio.isExpanded + return if (shouldExpand) { + collapsedState[portfolioKey] ?: forceVisible() + } else { + expandedState[portfolioKey] ?: forceVisible() + } +} + @Composable -private fun rememberChangedOnce(selectedWalletIndex: Int): Boolean { - var prev by remember { mutableIntStateOf(selectedWalletIndex) } - val changed = prev != selectedWalletIndex - SideEffect { prev = selectedWalletIndex } - return changed +private fun getExpandPortfolioStates( + state: WalletState, +): Pair< + SnapshotStateMap>, + SnapshotStateMap>, + > { + val expandedTransitionState = + remember { mutableStateMapOf>() } + val collapsedTransitionState = + remember { mutableStateMapOf>() } + + val portfolioContent = state is WalletState.MultiCurrency.Content && + state.tokensListState is WalletTokensListState.ContentState.PortfolioContent + if (!portfolioContent) { + return expandedTransitionState to collapsedTransitionState + } + + state.tokensListState.items.fastForEach { portfolio -> + val portfolioKey = portfolio.id + val shouldExpand = portfolio.isExpanded + + fun toggleVisible() = MutableTransitionState(false).apply { targetState = true } + fun forceVisible() = MutableTransitionState(true).apply { targetState = true } + + val isFirstCall = collapsedTransitionState[portfolioKey] == null || + expandedTransitionState[portfolioKey] == null + when { + isFirstCall -> { + collapsedTransitionState[portfolioKey] = forceVisible() + expandedTransitionState[portfolioKey] = forceVisible() + } + shouldExpand -> expandedTransitionState[portfolioKey] = toggleVisible() + else -> collapsedTransitionState[portfolioKey] = toggleVisible() + } + } + return expandedTransitionState to collapsedTransitionState } @Suppress("LongParameterList", "LongMethod", "CyclomaticComplexMethod") diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt index 469152ee3a..6c19807add 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt @@ -1,8 +1,10 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common +import androidx.compose.animation.core.MutableTransitionState import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.ui.Modifier import androidx.paging.compose.LazyPagingItems +import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.components.transactions.txHistoryItems import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState @@ -22,11 +24,11 @@ internal fun LazyListScope.contentItems( txHistoryItems: LazyPagingItems?, isBalanceHidden: Boolean, modifier: Modifier = Modifier, - selectedWalletChanged: Boolean, + portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState, ) { when (state) { is WalletState.MultiCurrency -> { - tokensListItems(state.tokensListState, modifier, isBalanceHidden, selectedWalletChanged) + tokensListItems(state.tokensListState, modifier, isBalanceHidden, portfolioVisibleState) } is WalletState.SingleCurrency -> { txHistoryItems(state.txHistoryState, txHistoryItems, isBalanceHidden, modifier) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyAccountContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyAccountContent.kt index 065c5aaa6f..88df5c8e1d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyAccountContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyAccountContent.kt @@ -8,9 +8,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.platform.testTag import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp @@ -27,7 +25,7 @@ internal fun LazyListScope.portfolioContentItems( items: ImmutableList, modifier: Modifier = Modifier, isBalanceHidden: Boolean, - selectedWalletChanged: Boolean, + portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState, ) { items.forEachIndexed { index, item -> portfolioTokensList( @@ -35,7 +33,7 @@ internal fun LazyListScope.portfolioContentItems( modifier = modifier, portfolioIndex = index, isBalanceHidden = isBalanceHidden, - selectedWalletChanged = selectedWalletChanged, + portfolioVisibleState = portfolioVisibleState, ) } } @@ -45,7 +43,7 @@ internal fun LazyListScope.portfolioTokensList( modifier: Modifier, portfolioIndex: Int, isBalanceHidden: Boolean, - selectedWalletChanged: Boolean, + portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState, ) { val tokens = portfolio.tokens val isExpanded = portfolio.isExpanded @@ -55,7 +53,7 @@ internal fun LazyListScope.portfolioTokensList( modifier = modifier, portfolioIndex = portfolioIndex, isBalanceHidden = isBalanceHidden, - selectedWalletChanged = selectedWalletChanged, + portfolioVisibleState = portfolioVisibleState, ) if (!isExpanded) return if (tokens.isEmpty()) { @@ -63,17 +61,22 @@ internal fun LazyListScope.portfolioTokensList( key = "$NON_CONTENT_TOKENS_LIST_KEY account-${portfolio.id}", contentType = "$NON_CONTENT_TOKENS_LIST_KEY account-${portfolio.id}", ) { - NonContentItemContent( - modifier = Modifier + val appear = portfolioVisibleState(portfolio) + SlideInItemVisibility( + modifier = modifier .animateItem() .roundedShapeItemDecoration( radius = TangemTheme.dimens.radius14, currentIndex = 1, lastIndex = 1, backgroundColor = TangemTheme.colors.background.primary, - ) - .padding(vertical = TangemTheme.dimens.spacing28), - ) + ), + visibleState = appear, + ) { + NonContentItemContent( + modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing28), + ) + } } return } @@ -84,10 +87,7 @@ internal fun LazyListScope.portfolioTokensList( itemContent = { tokenIndex, token -> val indexWithHeader = tokenIndex.inc() val lastIndex = tokens.lastIndex.inc() - val isPreview = LocalInspectionMode.current - val appear = remember { - MutableTransitionState(selectedWalletChanged || isPreview).apply { targetState = true } - } + val appear = portfolioVisibleState(portfolio) SlideInItemVisibility( modifier = modifier .testModifier(indexWithHeader) @@ -116,7 +116,7 @@ private fun LazyListScope.portfolioItem( modifier: Modifier, portfolioIndex: Int, isBalanceHidden: Boolean, - selectedWalletChanged: Boolean, + portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState, ) { val tokens = portfolio.tokens val isExpanded = portfolio.isExpanded @@ -140,11 +140,7 @@ private fun LazyListScope.portfolioItem( lastIndex = lastIndex, backgroundColor = TangemTheme.colors.background.primary, ) - val isPreview = LocalInspectionMode.current - val appear = remember { - MutableTransitionState(selectedWalletChanged || isPreview || tokens.isEmpty()) - .apply { targetState = true } - } + val appear = portfolioVisibleState(portfolio) if (isExpanded) { SlideInItemVisibility( modifier = anchorModifier, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt index edf6506e26..ef1d8252e5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt @@ -1,5 +1,6 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency +import androidx.compose.animation.core.MutableTransitionState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding @@ -40,14 +41,14 @@ internal fun LazyListScope.tokensListItems( state: WalletTokensListState, modifier: Modifier = Modifier, isBalanceHidden: Boolean, - selectedWalletChanged: Boolean, + portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState, ) { when (state) { is WalletTokensListState.ContentState.PortfolioContent -> portfolioContentItems( items = state.items, isBalanceHidden = isBalanceHidden, modifier = modifier, - selectedWalletChanged = selectedWalletChanged, + portfolioVisibleState = portfolioVisibleState, ) is WalletTokensListState.ContentState.Content, is WalletTokensListState.ContentState.Loading,