Updated on 2026-08-14
This commit is contained in:
parent
9836b18ff4
commit
050c7b6106
13 changed files with 369 additions and 222 deletions
|
|
@ -218,6 +218,7 @@ private fun LazyItemScope.DraggableItem(
|
|||
is DraggableItem.Portfolio -> ExpandedPortfolioHeader(
|
||||
state = item.tokenItemState,
|
||||
isCollapsable = false,
|
||||
composables = null,
|
||||
modifier = modifierWithBackground
|
||||
.padding(top = 8.dp)
|
||||
.fillMaxWidth(),
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ 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
|
||||
|
|
@ -57,7 +56,6 @@ 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
|
||||
|
|
@ -79,7 +77,6 @@ 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
|
||||
|
|
@ -154,7 +151,6 @@ private fun WalletContent(
|
|||
*/
|
||||
val selectedWalletIndex by remember(state.selectedWalletIndex) { mutableIntStateOf(state.selectedWalletIndex) }
|
||||
val selectedWallet = state.wallets.getOrElse(selectedWalletIndex) { state.wallets[state.selectedWalletIndex] }
|
||||
val (expandedState, collapsedState) = getExpandPortfolioStates(selectedWallet)
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
|
|
@ -199,6 +195,7 @@ private fun WalletContent(
|
|||
contentType = state.wallets.map { it.walletCardState.id },
|
||||
) {
|
||||
WalletsList(
|
||||
modifier = Modifier.animateItem(fadeInSpec = null, fadeOutSpec = null),
|
||||
lazyListState = walletsListState,
|
||||
wallets = state.wallets.map(WalletState::walletCardState).toImmutableList(),
|
||||
isBalanceHidden = state.isHidingMode,
|
||||
|
|
@ -263,13 +260,6 @@ private fun WalletContent(
|
|||
txHistoryItems = txHistoryItems,
|
||||
isBalanceHidden = state.isHidingMode,
|
||||
modifier = movableItemModifier,
|
||||
portfolioVisibleState = {
|
||||
findPortfolioVisibleState(
|
||||
portfolio = it,
|
||||
expandedState = expandedState,
|
||||
collapsedState = collapsedState,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
nftCollections(state = selectedWallet, itemModifier = itemModifier)
|
||||
|
|
@ -307,61 +297,6 @@ private fun WalletContent(
|
|||
)
|
||||
}
|
||||
|
||||
private fun findPortfolioVisibleState(
|
||||
portfolio: TokensListItemUM.Portfolio,
|
||||
expandedState: SnapshotStateMap<String, MutableTransitionState<Boolean>>,
|
||||
collapsedState: SnapshotStateMap<String, MutableTransitionState<Boolean>>,
|
||||
): MutableTransitionState<Boolean> {
|
||||
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 getExpandPortfolioStates(
|
||||
state: WalletState,
|
||||
): Pair<
|
||||
SnapshotStateMap<String, MutableTransitionState<Boolean>>,
|
||||
SnapshotStateMap<String, MutableTransitionState<Boolean>>,
|
||||
> {
|
||||
val expandedTransitionState =
|
||||
remember { mutableStateMapOf<String, MutableTransitionState<Boolean>>() }
|
||||
val collapsedTransitionState =
|
||||
remember { mutableStateMapOf<String, MutableTransitionState<Boolean>>() }
|
||||
|
||||
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")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -47,13 +47,14 @@ internal fun WalletsList(
|
|||
lazyListState: LazyListState,
|
||||
wallets: ImmutableList<WalletCardState>,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val horizontalCardPadding = TangemTheme.dimens.spacing16
|
||||
val screenWidth = LocalWindowSize.current.width
|
||||
val itemWidth by remember(screenWidth) { derivedStateOf { screenWidth - horizontalCardPadding * 2 } }
|
||||
|
||||
LazyRow(
|
||||
modifier = Modifier.background(color = TangemTheme.colors.background.secondary),
|
||||
modifier = modifier.background(color = TangemTheme.colors.background.secondary),
|
||||
state = lazyListState,
|
||||
contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16),
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
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
|
||||
|
|
@ -24,11 +22,10 @@ internal fun LazyListScope.contentItems(
|
|||
txHistoryItems: LazyPagingItems<TxHistoryState.TxHistoryItemState>?,
|
||||
isBalanceHidden: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState<Boolean>,
|
||||
) {
|
||||
when (state) {
|
||||
is WalletState.MultiCurrency -> {
|
||||
tokensListItems(state.tokensListState, modifier, isBalanceHidden, portfolioVisibleState)
|
||||
tokensListItems(state.tokensListState, modifier, isBalanceHidden)
|
||||
}
|
||||
is WalletState.SingleCurrency -> {
|
||||
txHistoryItems(state.txHistoryState, txHistoryItems, isBalanceHidden, modifier)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||
import androidx.compose.animation.core.MutableTransitionState
|
||||
import androidx.compose.animation.core.tween
|
||||
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.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
|
|
@ -20,12 +24,12 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
import com.tangem.core.ui.test.MainScreenTestTags
|
||||
import com.tangem.core.ui.utils.lazyListItemPosition
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
internal fun LazyListScope.portfolioContentItems(
|
||||
items: ImmutableList<TokensListItemUM.Portfolio>,
|
||||
modifier: Modifier = Modifier,
|
||||
isBalanceHidden: Boolean,
|
||||
portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState<Boolean>,
|
||||
) {
|
||||
items.forEachIndexed { index, item ->
|
||||
portfolioTokensList(
|
||||
|
|
@ -33,7 +37,6 @@ internal fun LazyListScope.portfolioContentItems(
|
|||
modifier = modifier,
|
||||
portfolioIndex = index,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
portfolioVisibleState = portfolioVisibleState,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -43,35 +46,34 @@ internal fun LazyListScope.portfolioTokensList(
|
|||
modifier: Modifier,
|
||||
portfolioIndex: Int,
|
||||
isBalanceHidden: Boolean,
|
||||
portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState<Boolean>,
|
||||
) {
|
||||
val tokens = portfolio.tokens
|
||||
val isExpanded = portfolio.isExpanded
|
||||
val lastIndex = tokens.lastIndex.inc()
|
||||
|
||||
portfolioItem(
|
||||
portfolio = portfolio,
|
||||
modifier = modifier,
|
||||
portfolioIndex = portfolioIndex,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
portfolioVisibleState = portfolioVisibleState,
|
||||
)
|
||||
if (!isExpanded) return
|
||||
if (tokens.isEmpty()) {
|
||||
item(
|
||||
key = "$NON_CONTENT_TOKENS_LIST_KEY account-${portfolio.id}",
|
||||
contentType = "$NON_CONTENT_TOKENS_LIST_KEY account-${portfolio.id}",
|
||||
) {
|
||||
val appear = portfolioVisibleState(portfolio)
|
||||
SlideInItemVisibility(
|
||||
currentIndex = 1,
|
||||
lastIndex = lastIndex,
|
||||
modifier = modifier
|
||||
.animateItem()
|
||||
.animateItem(fadeInSpec = null, placementSpec = null, fadeOutSpec = null)
|
||||
.roundedShapeItemDecoration(
|
||||
radius = TangemTheme.dimens.radius14,
|
||||
currentIndex = 1,
|
||||
lastIndex = 1,
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
),
|
||||
visibleState = appear,
|
||||
visible = isExpanded,
|
||||
) {
|
||||
NonContentItemContent(
|
||||
modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing28),
|
||||
|
|
@ -86,19 +88,19 @@ internal fun LazyListScope.portfolioTokensList(
|
|||
contentType = { _, item -> item::class.java },
|
||||
itemContent = { tokenIndex, token ->
|
||||
val indexWithHeader = tokenIndex.inc()
|
||||
val lastIndex = tokens.lastIndex.inc()
|
||||
val appear = portfolioVisibleState(portfolio)
|
||||
SlideInItemVisibility(
|
||||
currentIndex = tokenIndex,
|
||||
lastIndex = lastIndex,
|
||||
modifier = modifier
|
||||
.testModifier(indexWithHeader)
|
||||
.animateItem()
|
||||
.animateItem(fadeInSpec = null, placementSpec = null, fadeOutSpec = null)
|
||||
.roundedShapeItemDecoration(
|
||||
radius = TangemTheme.dimens.radius14,
|
||||
currentIndex = indexWithHeader,
|
||||
lastIndex = lastIndex,
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
),
|
||||
visibleState = appear,
|
||||
visible = isExpanded,
|
||||
) {
|
||||
val modifier = if (indexWithHeader == lastIndex) Modifier.padding(bottom = 8.dp) else Modifier
|
||||
PortfolioTokensListItem(
|
||||
|
|
@ -116,11 +118,9 @@ private fun LazyListScope.portfolioItem(
|
|||
modifier: Modifier,
|
||||
portfolioIndex: Int,
|
||||
isBalanceHidden: Boolean,
|
||||
portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState<Boolean>,
|
||||
) {
|
||||
val tokens = portfolio.tokens
|
||||
val isExpanded = portfolio.isExpanded
|
||||
|
||||
val lastIndex = when {
|
||||
isExpanded && tokens.isEmpty() -> 1
|
||||
isExpanded -> tokens.lastIndex.inc()
|
||||
|
|
@ -128,60 +128,61 @@ private fun LazyListScope.portfolioItem(
|
|||
}
|
||||
|
||||
item(
|
||||
key = "account-${portfolio.id}-isExpanded$isExpanded",
|
||||
contentType = "account-isExpanded$isExpanded",
|
||||
key = "account-${portfolio.id}",
|
||||
contentType = "account-content",
|
||||
) {
|
||||
val anchorModifier = modifier
|
||||
.testModifier(portfolioIndex)
|
||||
.animateItem()
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = 0,
|
||||
radius = TangemTheme.dimens.radius14,
|
||||
lastIndex = lastIndex,
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
)
|
||||
val appear = portfolioVisibleState(portfolio)
|
||||
if (isExpanded) {
|
||||
SlideInItemVisibility(
|
||||
modifier = anchorModifier,
|
||||
visibleState = appear,
|
||||
) {
|
||||
PortfolioListItem(
|
||||
state = portfolio,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = Modifier.padding(vertical = 8.dp),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
AnimatedVisibility(
|
||||
modifier = anchorModifier,
|
||||
visibleState = appear,
|
||||
enter = fadeIn(),
|
||||
exit = ExitTransition.None,
|
||||
) {
|
||||
PortfolioListItem(
|
||||
state = portfolio,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
)
|
||||
var lastIndexProxy by remember { mutableIntStateOf(lastIndex) }
|
||||
|
||||
// When collapsing the portfolio, we delay updating lastIndexProxy to allow
|
||||
// shrinking animation to complete before changing the shape.
|
||||
LaunchedEffect(lastIndex) {
|
||||
if (lastIndex != 0) {
|
||||
lastIndexProxy = lastIndex
|
||||
return@LaunchedEffect
|
||||
}
|
||||
delay(timeMillis = (50 * tokens.size).toLong())
|
||||
lastIndexProxy = 0
|
||||
}
|
||||
|
||||
PortfolioListItem(
|
||||
state = portfolio,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = modifier
|
||||
.testModifier(portfolioIndex)
|
||||
.roundedShapeItemDecoration(
|
||||
currentIndex = 0,
|
||||
radius = TangemTheme.dimens.radius14,
|
||||
lastIndex = lastIndexProxy,
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun SlideInItemVisibility(
|
||||
visibleState: MutableTransitionState<Boolean>,
|
||||
visible: Boolean,
|
||||
currentIndex: Int,
|
||||
lastIndex: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable() AnimatedVisibilityScope.() -> Unit,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
modifier = modifier,
|
||||
visibleState = visibleState,
|
||||
enter = slideInVertically(
|
||||
animationSpec = tween(easing = LinearOutSlowInEasing),
|
||||
initialOffsetY = { it },
|
||||
) + fadeIn(),
|
||||
exit = ExitTransition.None,
|
||||
visible = visible,
|
||||
enter = fadeIn(
|
||||
tween(100, delayMillis = 50 * currentIndex),
|
||||
) + expandVertically(
|
||||
tween(100, delayMillis = 50 * currentIndex),
|
||||
expandFrom = Alignment.Top,
|
||||
),
|
||||
exit = fadeOut(
|
||||
tween(100, delayMillis = 50 * (lastIndex - currentIndex - 1)),
|
||||
) + shrinkVertically(
|
||||
tween(100, delayMillis = 50 * (lastIndex - currentIndex - 1)),
|
||||
shrinkTowards = Alignment.Top,
|
||||
),
|
||||
) {
|
||||
content()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
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
|
||||
|
|
@ -41,14 +40,12 @@ internal fun LazyListScope.tokensListItems(
|
|||
state: WalletTokensListState,
|
||||
modifier: Modifier = Modifier,
|
||||
isBalanceHidden: Boolean,
|
||||
portfolioVisibleState: (portfolio: TokensListItemUM.Portfolio) -> MutableTransitionState<Boolean>,
|
||||
) {
|
||||
when (state) {
|
||||
is WalletTokensListState.ContentState.PortfolioContent -> portfolioContentItems(
|
||||
items = state.items,
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
modifier = modifier,
|
||||
portfolioVisibleState = portfolioVisibleState,
|
||||
)
|
||||
is WalletTokensListState.ContentState.Content,
|
||||
is WalletTokensListState.ContentState.Loading,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue