refactor: extract MainScaffold for better separation of navigation chrome logic
This commit is contained in:
@@ -1,8 +1,6 @@
|
|||||||
package bzh.ajaury.chombev
|
package bzh.ajaury.chombev
|
||||||
|
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
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.Composable
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.compose.runtime.saveable.rememberSerializable
|
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.coroutines.di.coroutinesModule
|
||||||
import bzh.ajaury.chombev.core.logging.di.loggingModule
|
import bzh.ajaury.chombev.core.logging.di.loggingModule
|
||||||
import bzh.ajaury.chombev.di.sharedModule
|
import bzh.ajaury.chombev.di.sharedModule
|
||||||
import bzh.ajaury.chombev.navigation.MainBottomBar
|
import bzh.ajaury.chombev.navigation.MainScaffold
|
||||||
import bzh.ajaury.chombev.navigation.MainTopBar
|
|
||||||
import bzh.ajaury.chombev.navigation.Route
|
import bzh.ajaury.chombev.navigation.Route
|
||||||
import bzh.ajaury.chombev.navigation.WebPages
|
import bzh.ajaury.chombev.navigation.WebPages
|
||||||
import bzh.ajaury.chombev.player.ui.PlayerScreen
|
import bzh.ajaury.chombev.player.ui.PlayerScreen
|
||||||
@@ -49,38 +46,21 @@ fun App() {
|
|||||||
|
|
||||||
val darkTheme = isSystemInDarkTheme()
|
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) {
|
ChomBevTheme(darkTheme = darkTheme) {
|
||||||
Scaffold(
|
// Each destination owns its chrome (see MainScaffold) so navigating to the full-screen
|
||||||
// Let each screen's bars/insets apply; keeps the Player's own scaffold from double-padding.
|
// player animates as a whole scene without the main bars reflowing the outgoing screen.
|
||||||
contentWindowInsets = WindowInsets(0, 0, 0, 0),
|
NavDisplay(
|
||||||
topBar = {
|
backStack = backStack,
|
||||||
if (isMainScreen) {
|
onBack = { backStack.removeLastOrNull() },
|
||||||
MainTopBar()
|
entryProvider = { key ->
|
||||||
}
|
when (key) {
|
||||||
},
|
is Route.Records -> NavEntry(key) {
|
||||||
bottomBar = {
|
MainScaffold(
|
||||||
if (isMainScreen) {
|
selectedPage = null,
|
||||||
MainBottomBar(
|
onRecordsClick = { backStack.selectMainTab(null) },
|
||||||
selectedPage = selectedPage,
|
onDeepenClick = { backStack.selectMainTab(WebPages.DEEPEN) },
|
||||||
onRecordsClick = { backStack.selectMainTab(null) },
|
onAboutClick = { backStack.selectMainTab(WebPages.ABOUT) },
|
||||||
onDeepenClick = { backStack.selectMainTab(WebPages.DEEPEN) },
|
) { innerPadding ->
|
||||||
onAboutClick = { backStack.selectMainTab(WebPages.ABOUT) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
) { innerPadding ->
|
|
||||||
NavDisplay(
|
|
||||||
backStack = backStack,
|
|
||||||
onBack = { backStack.removeLastOrNull() },
|
|
||||||
entryProvider = { key ->
|
|
||||||
when (key) {
|
|
||||||
is Route.Records -> NavEntry(key) {
|
|
||||||
RecordsScreen(
|
RecordsScreen(
|
||||||
contentPadding = innerPadding,
|
contentPadding = innerPadding,
|
||||||
onRecordClick = { record ->
|
onRecordClick = { record ->
|
||||||
@@ -88,15 +68,22 @@ fun App() {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
is Route.Player -> NavEntry(key) {
|
is Route.Player -> NavEntry(key) {
|
||||||
PlayerScreen(
|
PlayerScreen(
|
||||||
recordId = key.recordId,
|
recordId = key.recordId,
|
||||||
onBackClicked = { backStack.removeLastOrNull() },
|
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(
|
WebViewScreen(
|
||||||
page = key.page,
|
page = key.page,
|
||||||
contentPadding = innerPadding,
|
contentPadding = innerPadding,
|
||||||
@@ -104,9 +91,9 @@ fun App() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
)
|
},
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user