Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.flipcash.app.core.ui.rememberNavigationBarState
import com.flipcash.app.featureflags.FeatureFlag
import com.flipcash.app.featureflags.LocalFeatureFlags
import com.flipcash.app.session.LocalSessionController
import com.getcode.manager.BottomBarManager
import com.getcode.navigation.core.CodeNavigator
import com.getcode.theme.CodeTheme
import kotlinx.coroutines.flow.flowOf
Expand Down Expand Up @@ -54,6 +55,11 @@ internal fun AppNavigationBar(
val selectedTab = navigator.backStack.firstNotNullOfOrNull { (it as? AppRoute)?.asNavBarTab() }
val topTab = (navigator.currentRouteKey as? AppRoute)?.asNavBarTab()

// A BottomBar modal (e.g. Add Money) renders in the nav content, above this bar; hide the bar so
// it doesn't draw over the modal. Safe because the bar is a bottom overlay (not a scaffold
// bottomBar), so hiding it doesn't resize the content beneath (see NewAppContent).
val bottomBarMessages by BottomBarManager.messages.collectAsStateWithLifecycle()

// A bill/tip card renders at the app root above everything; hide the bar so it doesn't show
// beneath the presented bill.
val session = LocalSessionController.current
Expand All @@ -66,11 +72,8 @@ internal fun AppNavigationBar(
.then(modifier),
contentAlignment = Alignment.BottomCenter,
) {
// Keep the bar in place for BottomBar modals (e.g. Add Money): the modal's scrim renders over
// it via z-order, and hiding it would collapse the scaffold's bottom slot and re-lay-out the
// tab content beneath (visibly re-fanning the wallet's collapsing card stack).
AnimatedVisibility(
visible = topTab != null && !billUp,
visible = topTab != null && bottomBarMessages.isEmpty() && !billUp,
enter = slideInVertically { it } + fadeIn(),
exit = slideOutVertically { it } + fadeOut(),
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,17 @@ import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation3.runtime.NavKey
import dev.chrisbanes.haze.hazeSource
import dev.chrisbanes.haze.rememberHazeState
Expand All @@ -30,6 +38,7 @@ import com.getcode.navigation.core.CodeNavigator
import com.getcode.navigation.results.NavResultStateRegistry
import com.getcode.navigation.scenes.ModalBottomSheetSceneStrategy
import com.getcode.ui.components.bars.BarManager
import com.getcode.ui.core.measured
import com.getcode.ui.theme.CodeScaffold
import dev.theolm.rinku.DeepLink

Expand Down Expand Up @@ -135,16 +144,22 @@ internal fun NewAppContent(
onPendingAction: (DeeplinkAction) -> Unit = {},
) {
val hazeState = rememberHazeState()
CodeScaffold(
bottomBar = {
AppNavigationBar(navigator = codeNavigator, hazeState = hazeState)
}
) { padding ->
CompositionLocalProvider(LocalTabBarPadding provides padding) {
// Mark the nav content as the haze source so the frosted tab bar blurs what scrolls
// beneath it.
Box(modifier = Modifier.hazeSource(hazeState)) {
AppNavHost(
// The tab bar is a bottom OVERLAY, not a scaffold bottomBar: the nav content stays full-height, so
// hiding the bar for a modal (or bill) never resizes it — which otherwise re-fanned the wallet's
// collapsing card stack — and content scrolls edge-to-edge under the frosted bar. Reserve space for
// it via LocalTabBarPadding, latched to the tallest measured height and only on tab homes, so a
// modal-driven hide doesn't shrink the inset.
var tabBarHeight by remember { mutableStateOf(0.dp) }
val onTabHome = (codeNavigator.currentRouteKey as? AppRoute)?.asNavBarTab() != null
val tabBarPadding = if (onTabHome) PaddingValues(bottom = tabBarHeight) else PaddingValues()

CodeScaffold { _ ->
Box(modifier = Modifier.fillMaxSize()) {
CompositionLocalProvider(LocalTabBarPadding provides tabBarPadding) {
// Mark the nav content as the haze source so the frosted tab bar blurs what scrolls
// beneath it.
Box(modifier = Modifier.hazeSource(hazeState)) {
AppNavHost(
navigator = codeNavigator,
resultStateRegistry = resultStateRegistry,
decorators = listOf(
Expand Down Expand Up @@ -207,7 +222,18 @@ internal fun NewAppContent(
onPendingAction = onPendingAction,
),
)
}
}

// Bottom overlay over the full-height nav content. Latch the tallest height so the
// reserved inset above stays stable when the bar hides for a modal/bill.
AppNavigationBar(
navigator = codeNavigator,
hazeState = hazeState,
modifier = Modifier
.align(Alignment.BottomCenter)
.measured { if (it.height > tabBarHeight) tabBarHeight = it.height },
)
}
}
}
Loading