fix(android): split MinstrelNavGraph into NavGraphBuilder extensions

detekt LongMethod (97/60 lines). Body had no actual logic — just 13
sequential composable<T>() route declarations. Split by route category
(matches the existing comment headers):

  - inShellTopLevel(navController, expandPlayer) — 8 in-shell tabs
  - inShellDetail(expandPlayer) — 6 push-on-top detail screens
  - outsideShell() — NowPlaying (with slide-up transitions), Queue,
    ServerUrl, Login

`MinstrelNavGraph` itself is now 15 lines (NavHost shell that delegates
to the three groups), and each helper sits well under the 60-line cap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-24 13:26:55 -04:00
parent ab05b7ab7b
commit c33c1178ab
@@ -5,6 +5,7 @@ import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
@@ -28,123 +29,115 @@ fun MinstrelNavGraph(
modifier: Modifier = Modifier,
) {
val expandPlayer = { navController.navigate(NowPlaying) }
NavHost(
navController = navController,
startDestination = Library,
modifier = modifier.fillMaxSize(),
) {
// ── In-shell top-level destinations ───────────────────────────
inShellTopLevel(navController, expandPlayer)
inShellDetail(expandPlayer)
outsideShell()
}
}
composable<Home> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Home", "Lands in the next design-drift sub-task.")
}
}
composable<Library> {
ShellScaffold(onExpandPlayer = expandPlayer) {
LibraryScreen(navController = navController)
}
}
composable<Search> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Search", "Lands in a later phase.")
}
}
composable<Discover> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Discover", "Lands in Phase 10 (Lidarr requests).")
}
}
composable<Playlists> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Playlists", "Lands in Phase 7.")
}
}
composable<Settings> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Settings", "Lands in Phase 11.")
}
}
composable<Admin> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Admin", "Admin landing — lands in a later phase.")
}
}
composable<Requests> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Requests", "Your Lidarr requests — Phase 10.")
}
}
// ── In-shell detail screens (push-on-top) ─────────────────────
composable<AlbumDetail> { backStackEntry ->
val route: AlbumDetail = backStackEntry.toRoute()
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Album", "Album detail for ${route.id} — later 5.x slice.")
}
}
composable<ArtistDetail> { backStackEntry ->
val route: ArtistDetail = backStackEntry.toRoute()
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Artist", "Artist detail for ${route.id} — later 5.x slice.")
}
}
composable<PlaylistDetail> { backStackEntry ->
val route: PlaylistDetail = backStackEntry.toRoute()
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Playlist", "Playlist detail for ${route.id} — Phase 7.")
}
}
composable<AdminRequests> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Admin · Requests", "Lands with admin slice.")
}
}
composable<AdminQuarantine> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Admin · Quarantine", "Lands with admin slice.")
}
}
composable<AdminUsers> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Admin · Users", "Lands with admin slice.")
}
}
// ── Outside-shell full-screen routes ──────────────────────────
composable<NowPlaying>(
// Slide up from the bottom on enter; back down on dismiss.
// Mirrors the Flutter CustomTransitionPage (280ms easeOutCubic).
enterTransition = { slideInVertically(initialOffsetY = { it }) },
exitTransition = { slideOutVertically(targetOffsetY = { it }) },
popEnterTransition = { slideInVertically(initialOffsetY = { it }) },
popExitTransition = { slideOutVertically(targetOffsetY = { it }) },
) {
NowPlayingScreen()
}
composable<Queue> { ComingSoon("Queue", "Lands in a later 6.x slice.") }
composable<ServerUrl> {
ComingSoon("Connect to your Minstrel", "Server-URL entry — Phase 11.")
}
composable<Login> {
ComingSoon("Sign in", "Login flow — Phase 11.")
private fun NavGraphBuilder.inShellTopLevel(
navController: NavHostController,
expandPlayer: () -> Unit,
) {
composable<Home> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Home", "Lands in the next design-drift sub-task.")
}
}
composable<Library> {
ShellScaffold(onExpandPlayer = expandPlayer) {
LibraryScreen(navController = navController)
}
}
composable<Search> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Search", "Lands in a later phase.")
}
}
composable<Discover> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Discover", "Lands in Phase 10 (Lidarr requests).")
}
}
composable<Playlists> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Playlists", "Lands in Phase 7.")
}
}
composable<Settings> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Settings", "Lands in Phase 11.")
}
}
composable<Admin> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Admin", "Admin landing — lands in a later phase.")
}
}
composable<Requests> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Requests", "Your Lidarr requests — Phase 10.")
}
}
}
private fun NavGraphBuilder.inShellDetail(expandPlayer: () -> Unit) {
composable<AlbumDetail> { backStackEntry ->
val route: AlbumDetail = backStackEntry.toRoute()
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Album", "Album detail for ${route.id} — later 5.x slice.")
}
}
composable<ArtistDetail> { backStackEntry ->
val route: ArtistDetail = backStackEntry.toRoute()
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Artist", "Artist detail for ${route.id} — later 5.x slice.")
}
}
composable<PlaylistDetail> { backStackEntry ->
val route: PlaylistDetail = backStackEntry.toRoute()
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Playlist", "Playlist detail for ${route.id} — Phase 7.")
}
}
composable<AdminRequests> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Admin · Requests", "Lands with admin slice.")
}
}
composable<AdminQuarantine> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Admin · Quarantine", "Lands with admin slice.")
}
}
composable<AdminUsers> {
ShellScaffold(onExpandPlayer = expandPlayer) {
ComingSoon("Admin · Users", "Lands with admin slice.")
}
}
}
private fun NavGraphBuilder.outsideShell() {
composable<NowPlaying>(
// Slide up from the bottom on enter; back down on dismiss.
// Mirrors the Flutter CustomTransitionPage (280ms easeOutCubic).
enterTransition = { slideInVertically(initialOffsetY = { it }) },
exitTransition = { slideOutVertically(targetOffsetY = { it }) },
popEnterTransition = { slideInVertically(initialOffsetY = { it }) },
popExitTransition = { slideOutVertically(targetOffsetY = { it }) },
) {
NowPlayingScreen()
}
composable<Queue> { ComingSoon("Queue", "Lands in a later 6.x slice.") }
composable<ServerUrl> {
ComingSoon("Connect to your Minstrel", "Server-URL entry — Phase 11.")
}
composable<Login> { ComingSoon("Sign in", "Login flow — Phase 11.") }
}
@Composable