diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt index a8dbb984ad..deb34c8d4d 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt @@ -45,14 +45,15 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.* import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsTopAppBarUM.TitleState import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsBalanceBlock import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.ZeroBalanceActionsBlock -import com.tangem.features.markets.token.block.TokenMarketBlockComponent import com.tangem.features.marketing.api.MarketingBannerComponent +import com.tangem.features.markets.token.block.TokenMarketBlockComponent import com.tangem.features.rating.RatingComponent import com.tangem.features.tokendetails.ExpressTransactionsComponent import com.tangem.features.txhistory.component.TxHistoryComponent import com.tangem.features.txhistory.entity.TxHistoryItemsUM import com.tangem.features.txhistory.entity.TxHistoryUM import com.tangem.features.yield.supply.api.YieldSupplyComponent +import dev.chrisbanes.haze.HazeState import dev.chrisbanes.haze.rememberHazeState import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf @@ -81,6 +82,7 @@ internal fun TokenDetailsScreen( val statusBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getTop(this).toDp() } val topBarTotalHeight = TopBarHeight + statusBarHeight val hazeState = rememberHazeState() + val buttonsHazeState = rememberHazeState() val rootBackground = TangemTheme.colors2.surface.level2 var marketBlockHeight by remember { mutableStateOf(0.dp) } @@ -92,6 +94,13 @@ internal fun TokenDetailsScreen( .fillMaxSize() .background(rootBackground), ) { + Box( + modifier = Modifier + .fillMaxSize() + .background(rootBackground) + .hazeSourceTangem(state = buttonsHazeState, zIndex = -1f), + ) + TangemPullToRefreshSlidingContainer( config = tokenDetailsUM.pullToRefreshConfig, indicatorOffset = topBarTotalHeight, @@ -104,6 +113,7 @@ internal fun TokenDetailsScreen( expressTransactionsToDisplay = expressState.transactionsToDisplay, marketingBannerComponent = marketingBannerComponent, rootBackground = rootBackground, + buttonsHazeState = buttonsHazeState, topContentPadding = topBarTotalHeight, bottomContentPadding = effectiveBottomPadding, modifier = Modifier.fillMaxSize(), @@ -166,6 +176,7 @@ private fun TokenDetailsBody( expressTransactionsToDisplay: PersistentList, marketingBannerComponent: MarketingBannerComponent, rootBackground: Color, + buttonsHazeState: HazeState, topContentPadding: Dp, bottomContentPadding: Dp, modifier: Modifier = Modifier, @@ -194,11 +205,13 @@ private fun TokenDetailsBody( contentPadding = PaddingValues(top = topContentPadding, bottom = bottomContentPadding), ) { item(key = "balance_block") { - TokenDetailsBalanceBlock( - balanceBlockUM = tokenDetailsUM.balanceBlockUM, - isBalanceHidden = tokenDetailsUM.isBalanceHidden, - modifier = Modifier.fillMaxWidth(), - ) + CompositionLocalProvider(LocalHazeState provides buttonsHazeState) { + TokenDetailsBalanceBlock( + balanceBlockUM = tokenDetailsUM.balanceBlockUM, + isBalanceHidden = tokenDetailsUM.isBalanceHidden, + modifier = Modifier.fillMaxWidth(), + ) + } } val balance = tokenDetailsUM.balanceBlockUM notifications( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen2.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen2.kt index f680c12d18..c6ea59ccf1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen2.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen2.kt @@ -52,7 +52,6 @@ import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheetDraggab import com.tangem.core.ui.components.bottomsheets.state.BottomSheetState import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshSlidingContainer import com.tangem.core.ui.components.containers.pullToRefresh.getPullToRefreshIndicatorOffset -import com.tangem.core.ui.components.haze.hazeEffectTangem import com.tangem.core.ui.components.haze.hazeSourceTangem import com.tangem.core.ui.components.rememberIsKeyboardVisible import com.tangem.core.ui.components.sheetscaffold.* @@ -79,8 +78,7 @@ import com.tangem.features.tangempay.component.TangemPayMainBlockComponent import com.tangem.features.tangempay.entity.TangemPayMainUM import com.tangem.features.virtualaccount.main.component.VirtualAccountMainBlockComponent import com.tangem.features.virtualaccount.main.entity.VirtualAccountMainUM -import dev.chrisbanes.haze.HazeProgressive -import dev.chrisbanes.haze.HazeTint +import dev.chrisbanes.haze.rememberHazeState import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.flow.collectLatest @@ -90,6 +88,9 @@ import kotlin.math.abs private const val MARKET_HINT_THRESHOLD = 0.5f +// collapsedFraction below this is treated as "fully expanded" (user is at the very top of the list) +private const val FULLY_EXPANDED_THRESHOLD = 0.01f + @OptIn(ExperimentalDecomposeApi::class) @Suppress("LongParameterList") @Composable @@ -231,161 +232,168 @@ private fun WalletContent2( val canPagerScroll by remember { derivedStateOf { behavior.state.heightOffset == 0f } } val pullToRefreshState = rememberPullToRefreshState() + val wallpaperHazeState = rememberHazeState() BoxWithConstraints( modifier = Modifier .fillMaxSize() .hazeSourceTangem(zIndex = -2f), ) { - Box( - modifier = Modifier - .matchParentSize() - .background(TangemTheme.colors3.bg.primary), - ) val isSheetExpanded by remember { derivedStateOf { bottomSheetState.targetValue == TangemSheetValue.Expanded } } - if (!isSheetExpanded) { - NorthernLightsBackground( - containerColor = TangemTheme.colors3.bg.primary, - modifier = Modifier - .graphicsLayer { alpha = 1 - behavior.state.collapsedFraction * 2 } - .matchParentSize(), - ) - } - - WalletPagerIndicator( - pagerState = walletsPagerState, - pullToRefreshState = pullToRefreshState, - pullToRefreshConfig = pullToRefreshConfig, - behavior = behavior, - topOffset = subtitleBottom + 8.dp, - ) - - val overlay = TangemTheme.colors3.material.tint.solid - - // Root-coordinates bounds of the "Add & Manage" button per pager page, reported by the - // button itself, so the markets hint and tooltip can avoid covering it val organizeButtonBounds = remember { mutableStateMapOf() } - - HorizontalPager( - state = walletsPagerState, - userScrollEnabled = canPagerScroll, - beyondViewportPageCount = 1, - modifier = Modifier.hazeEffectTangem { - fallbackTint = HazeTint(color = overlay) - progressive = HazeProgressive.verticalGradient( - startIntensity = 1f, - endIntensity = 1f, - preferPerformance = true, - ) - }, - ) { currentWalletIndex -> - val listState = listStates[currentWalletIndex] ?: rememberLazyListState() - - val currentWallet = state.wallets2.getOrElse(currentWalletIndex) { - state.wallets2[state.selectedWalletIndex] - } - val currentWalletId = currentWallet.walletsBalanceUM.id.stringValue - - LaunchedEffect(walletsPagerState.currentPage, currentWallet.walletsBalanceUM) { - if (walletsPagerState.currentPage == currentWalletIndex) { - walletBalance = - (currentWallet.walletsBalanceUM as? WalletBalanceUM.Content)?.balanceInAppBar - } - } - LaunchedEffect(walletsPagerState.currentPage, currentWallet.pullToRefreshConfig) { - if (walletsPagerState.currentPage == currentWalletIndex) { - pullToRefreshConfig = currentWallet.pullToRefreshConfig - } - } - - val isShowMarketsHint by remember { - derivedStateOf { - behavior.state.collapsedFraction > MARKET_HINT_THRESHOLD && - listState.layoutInfo.totalItemsCount > 0 && - !listState.canScrollBackward && !listState.canScrollForward || - listState.canScrollBackward && !listState.canScrollForward - } - } - - val pageSlideAlpha by rememberPageAlpha(walletsPagerState, currentWalletIndex) - - val pullToRefreshContentOffset = getPullToRefreshIndicatorOffset( - pullToRefreshConfig = currentWallet.pullToRefreshConfig, - pullToRefreshState = pullToRefreshState, - ) - - TangemSharedTransitionLayout( + CompositionLocalProvider(LocalHazeState provides wallpaperHazeState) { + Box( modifier = Modifier .fillMaxSize() - .alpha(pageSlideAlpha), + .hazeSourceTangem(zIndex = -1f), ) { - TangemPullToRefreshSlidingContainer( - state = pullToRefreshState, - config = currentWallet.pullToRefreshConfig, - indicatorOffset = with(LocalDensity.current) { - behavior.state.partialHeightLimit.toDp() - }, - ) { - TangemCollapsingTopBar( - state = behavior.state, - collapsingPart = { - val balanceBlockHeight = with(LocalDensity.current) { - -behavior.state.heightOffsetLimit.toDp() + Box( + modifier = Modifier + .fillMaxSize() + .background(TangemTheme.colors3.bg.primary), + ) + if (!isSheetExpanded) { + NorthernLightsBackground( + containerColor = TangemTheme.colors3.bg.primary, + modifier = Modifier + .graphicsLayer { alpha = 1 - behavior.state.collapsedFraction * 2 } + .fillMaxSize(), + ) + } + } + + WalletPagerIndicator( + pagerState = walletsPagerState, + pullToRefreshState = pullToRefreshState, + pullToRefreshConfig = pullToRefreshConfig, + behavior = behavior, + topOffset = subtitleBottom + 8.dp, + ) + + HorizontalPager( + state = walletsPagerState, + userScrollEnabled = canPagerScroll, + beyondViewportPageCount = 1, + ) { currentWalletIndex -> + val listState = listStates[currentWalletIndex] ?: rememberLazyListState() + + val currentWallet = state.wallets2.getOrElse(currentWalletIndex) { + state.wallets2[state.selectedWalletIndex] + } + val currentWalletId = currentWallet.walletsBalanceUM.id.stringValue + + LaunchedEffect(walletsPagerState.currentPage, currentWallet.walletsBalanceUM) { + if (walletsPagerState.currentPage == currentWalletIndex) { + walletBalance = + (currentWallet.walletsBalanceUM as? WalletBalanceUM.Content)?.balanceInAppBar + } + } + LaunchedEffect(walletsPagerState.currentPage, currentWallet.pullToRefreshConfig) { + if (walletsPagerState.currentPage == currentWalletIndex) { + pullToRefreshConfig = currentWallet.pullToRefreshConfig + } + } + + LaunchedEffect(listState) { + snapshotFlow { listState.layoutInfo.totalItemsCount } + .collectLatest { + if (behavior.state.collapsedFraction < FULLY_EXPANDED_THRESHOLD && + listState.firstVisibleItemIndex != 0 + ) { + listState.scrollToItem(index = 0) } - WalletBalance( - behavior = behavior, - walletBalanceUM = currentWallet.walletsBalanceUM, - buttons = currentWallet.buttons, - isBalanceHidden = state.isHidingMode, - modifier = Modifier.height(balanceBlockHeight), - onSubtitleBottomChange = { newValue -> - if (pullToRefreshContentOffset == 0.dp && newValue > subtitleBottom) { - subtitleBottom = newValue - } - }, - ) + } + } + + val isShowMarketsHint by remember { + derivedStateOf { + behavior.state.collapsedFraction > MARKET_HINT_THRESHOLD && + listState.layoutInfo.totalItemsCount > 0 && + !listState.canScrollBackward && !listState.canScrollForward || + listState.canScrollBackward && !listState.canScrollForward + } + } + + val pageSlideAlpha by rememberPageAlpha(walletsPagerState, currentWalletIndex) + + val pullToRefreshContentOffset = getPullToRefreshIndicatorOffset( + pullToRefreshConfig = currentWallet.pullToRefreshConfig, + pullToRefreshState = pullToRefreshState, + ) + + TangemSharedTransitionLayout( + modifier = Modifier + .fillMaxSize() + .alpha(pageSlideAlpha), + ) { + TangemPullToRefreshSlidingContainer( + state = pullToRefreshState, + config = currentWallet.pullToRefreshConfig, + indicatorOffset = with(LocalDensity.current) { + behavior.state.partialHeightLimit.toDp() }, - body = { - WalletListContent( - currentWallet = currentWallet, - listState = listState, - isBalanceHidden = state.isHidingMode, - contentPadding = contentPadding, - tangemPayComponent = tangemPayComponent, - promoBannersBlockComponent = promoBannersBlockComponent, - walletId = currentWalletId, - virtualAccountComponent = virtualAccountComponent, - onOrganizeButtonBoundsChange = remember(currentWalletIndex) { - { bounds -> - if (bounds != null) { - organizeButtonBounds[currentWalletIndex] = bounds - } else { - organizeButtonBounds.remove(currentWalletIndex) + ) { + TangemCollapsingTopBar( + state = behavior.state, + collapsingPart = { + val balanceBlockHeight = with(LocalDensity.current) { + -behavior.state.heightOffsetLimit.toDp() + } + WalletBalance( + behavior = behavior, + walletBalanceUM = currentWallet.walletsBalanceUM, + buttons = currentWallet.buttons, + isBalanceHidden = state.isHidingMode, + modifier = Modifier.height(balanceBlockHeight), + onSubtitleBottomChange = { newValue -> + if (pullToRefreshContentOffset == 0.dp && newValue > subtitleBottom) { + subtitleBottom = newValue } - } - }, - modifier = Modifier - .fillMaxSize() - .nestedScroll(behavior.nestedScrollConnection), - ) + }, + ) + }, + body = { + WalletListContent( + currentWallet = currentWallet, + listState = listState, + isBalanceHidden = state.isHidingMode, + contentPadding = contentPadding, + tangemPayComponent = tangemPayComponent, + promoBannersBlockComponent = promoBannersBlockComponent, + walletId = currentWalletId, + virtualAccountComponent = virtualAccountComponent, + onOrganizeButtonBoundsChange = remember(currentWalletIndex) { + { bounds -> + if (bounds != null) { + organizeButtonBounds[currentWalletIndex] = bounds + } else { + organizeButtonBounds.remove(currentWalletIndex) + } + } + }, + modifier = Modifier + .fillMaxSize() + .nestedScroll(behavior.nestedScrollConnection), + ) + }, + ) + } + MarketsHint( + modifier = Modifier + .align(Alignment.BottomCenter) + .fillMaxWidth(fraction = .6f) + .padding(bottom = paddingValues.calculateBottomPadding()) + .onSizeChanged { size -> + marketsHintHeight = with(density) { size.height.toDp() } + }, + isVisible = isShowMarketsHint, + obstacleBounds = remember(currentWalletIndex) { + { organizeButtonBounds[currentWalletIndex] } }, ) } - MarketsHint( - modifier = Modifier - .align(Alignment.BottomCenter) - .fillMaxWidth(fraction = .6f) - .padding(bottom = paddingValues.calculateBottomPadding()) - .onSizeChanged { size -> - marketsHintHeight = with(density) { size.height.toDp() } - }, - isVisible = isShowMarketsHint, - obstacleBounds = remember(currentWalletIndex) { - { organizeButtonBounds[currentWalletIndex] } - }, - ) } }