diff --git a/shared/src/commonMain/kotlin/bzh/ajaury/chombev/App.kt b/shared/src/commonMain/kotlin/bzh/ajaury/chombev/App.kt index ad94b20..31ddf72 100644 --- a/shared/src/commonMain/kotlin/bzh/ajaury/chombev/App.kt +++ b/shared/src/commonMain/kotlin/bzh/ajaury/chombev/App.kt @@ -1,8 +1,6 @@ package bzh.ajaury.chombev import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.saveable.rememberSerializable @@ -13,8 +11,7 @@ import androidx.savedstate.compose.serialization.serializers.SnapshotStateListSe import bzh.ajaury.chombev.core.coroutines.di.coroutinesModule import bzh.ajaury.chombev.core.logging.di.loggingModule import bzh.ajaury.chombev.di.sharedModule -import bzh.ajaury.chombev.navigation.MainBottomBar -import bzh.ajaury.chombev.navigation.MainTopBar +import bzh.ajaury.chombev.navigation.MainScaffold import bzh.ajaury.chombev.navigation.Route import bzh.ajaury.chombev.navigation.WebPages import bzh.ajaury.chombev.player.ui.PlayerScreen @@ -49,38 +46,21 @@ fun App() { val darkTheme = isSystemInDarkTheme() - // The current top destination drives the shared chrome: the top and bottom bars are shown - // only on the main screens (Records and the web pages), not on the Player detail screen. - val currentRoute = backStack.lastOrNull() - val selectedPage = (currentRoute as? Route.Web)?.page - val isMainScreen = currentRoute is Route.Records || currentRoute is Route.Web - ChomBevTheme(darkTheme = darkTheme) { - Scaffold( - // Let each screen's bars/insets apply; keeps the Player's own scaffold from double-padding. - contentWindowInsets = WindowInsets(0, 0, 0, 0), - topBar = { - if (isMainScreen) { - MainTopBar() - } - }, - bottomBar = { - if (isMainScreen) { - MainBottomBar( - selectedPage = selectedPage, - onRecordsClick = { backStack.selectMainTab(null) }, - onDeepenClick = { backStack.selectMainTab(WebPages.DEEPEN) }, - onAboutClick = { backStack.selectMainTab(WebPages.ABOUT) }, - ) - } - }, - ) { innerPadding -> - NavDisplay( - backStack = backStack, - onBack = { backStack.removeLastOrNull() }, - entryProvider = { key -> - when (key) { - is Route.Records -> NavEntry(key) { + // Each destination owns its chrome (see MainScaffold) so navigating to the full-screen + // player animates as a whole scene without the main bars reflowing the outgoing screen. + NavDisplay( + backStack = backStack, + onBack = { backStack.removeLastOrNull() }, + entryProvider = { key -> + when (key) { + is Route.Records -> NavEntry(key) { + MainScaffold( + selectedPage = null, + onRecordsClick = { backStack.selectMainTab(null) }, + onDeepenClick = { backStack.selectMainTab(WebPages.DEEPEN) }, + onAboutClick = { backStack.selectMainTab(WebPages.ABOUT) }, + ) { innerPadding -> RecordsScreen( contentPadding = innerPadding, onRecordClick = { record -> @@ -88,15 +68,22 @@ fun App() { }, ) } + } - is Route.Player -> NavEntry(key) { - PlayerScreen( - recordId = key.recordId, - onBackClicked = { backStack.removeLastOrNull() }, - ) - } + is Route.Player -> NavEntry(key) { + PlayerScreen( + recordId = key.recordId, + onBackClicked = { backStack.removeLastOrNull() }, + ) + } - is Route.Web -> NavEntry(key) { + is Route.Web -> NavEntry(key) { + MainScaffold( + selectedPage = key.page, + onRecordsClick = { backStack.selectMainTab(null) }, + onDeepenClick = { backStack.selectMainTab(WebPages.DEEPEN) }, + onAboutClick = { backStack.selectMainTab(WebPages.ABOUT) }, + ) { innerPadding -> WebViewScreen( page = key.page, contentPadding = innerPadding, @@ -104,9 +91,9 @@ fun App() { ) } } - }, - ) - } + } + }, + ) } } } diff --git a/shared/src/commonMain/kotlin/bzh/ajaury/chombev/navigation/MainScaffold.kt b/shared/src/commonMain/kotlin/bzh/ajaury/chombev/navigation/MainScaffold.kt new file mode 100644 index 0000000..c8b393c --- /dev/null +++ b/shared/src/commonMain/kotlin/bzh/ajaury/chombev/navigation/MainScaffold.kt @@ -0,0 +1,40 @@ +package bzh.ajaury.chombev.navigation + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import bzh.ajaury.chombev.web.model.WebPage + +/** + * Scaffold shared by the main destinations (Records, Deepen, About): the app [MainTopBar] and the + * [MainBottomBar]. + * + * Each main destination wraps its own content in a [MainScaffold] rather than sitting inside one + * shared outer scaffold. This keeps the chrome part of the destination, so it animates with it and + * its content padding stays stable during navigation — avoiding the layout jump that happened when + * pushing a full-screen detail (the player) instantly removed the shared bars. + */ +@Composable +fun MainScaffold( + selectedPage: WebPage?, + onRecordsClick: () -> Unit = {}, + onDeepenClick: () -> Unit = {}, + onAboutClick: () -> Unit = {}, + content: @Composable (PaddingValues) -> Unit, +) { + Scaffold( + // Let each bar apply its own insets; matches the player's standalone scaffold. + contentWindowInsets = WindowInsets(0, 0, 0, 0), + topBar = { MainTopBar() }, + bottomBar = { + MainBottomBar( + selectedPage = selectedPage, + onRecordsClick = onRecordsClick, + onDeepenClick = onDeepenClick, + onAboutClick = onAboutClick, + ) + }, + content = content, + ) +}