refactor(android): extract MinstrelTopAppBar (DRY 3b)
Ten screens duplicated the same TopAppBar + Text(title) + optional-back-arrow + MainAppBarActions sandwich. Extract into MinstrelTopAppBar(title, navController, currentRouteName, onBack, actions) at shared/widgets/. Library's tab row still composes above it inside a Column; Library's Shuffle button slots into the extra actions trailing-lambda; drill-down screens pass onBack for the back arrow; root tabs leave onBack null. Net: -48 lines across 10 screens, +50 lines for the helper. SearchScreen keeps its own TopAppBar (title is a SearchField composable, not a string). Per-screen TrackActionsViewModel snackbar wiring (Library/Search/ detail screens) was intentionally NOT consolidated here: each hiltViewModel() returns a different VM instance than the shell- scoped one already in ShellScaffold, so dedup belongs in 3c (shell-scope the VM via CompositionLocal) rather than freezing the current duplicate into a helper.
This commit is contained in:
@@ -11,13 +11,10 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material3.ElevatedCard
|
import androidx.compose.material3.ElevatedCard
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -29,7 +26,6 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import com.composables.icons.lucide.ArrowLeft
|
|
||||||
import com.composables.icons.lucide.Inbox
|
import com.composables.icons.lucide.Inbox
|
||||||
import com.composables.icons.lucide.Lucide
|
import com.composables.icons.lucide.Lucide
|
||||||
import com.composables.icons.lucide.TriangleAlert
|
import com.composables.icons.lucide.TriangleAlert
|
||||||
@@ -44,7 +40,7 @@ import com.fabledsword.minstrel.nav.AdminRequests
|
|||||||
import com.fabledsword.minstrel.nav.AdminUsers
|
import com.fabledsword.minstrel.nav.AdminUsers
|
||||||
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
||||||
import com.fabledsword.minstrel.shared.widgets.LoadingCentered
|
import com.fabledsword.minstrel.shared.widgets.LoadingCentered
|
||||||
import com.fabledsword.minstrel.shared.widgets.MainAppBarActions
|
import com.fabledsword.minstrel.shared.widgets.MinstrelTopAppBar
|
||||||
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@@ -104,7 +100,6 @@ class AdminLandingViewModel @Inject constructor(
|
|||||||
|
|
||||||
// ─── Screen ──────────────────────────────────────────────────────────
|
// ─── Screen ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AdminLandingScreen(
|
fun AdminLandingScreen(
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
@@ -114,19 +109,11 @@ fun AdminLandingScreen(
|
|||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
MinstrelTopAppBar(
|
||||||
title = { Text("Admin") },
|
title = "Admin",
|
||||||
navigationIcon = {
|
navController = navController,
|
||||||
IconButton(onClick = { navController.popBackStack() }) {
|
currentRouteName = Admin::class.qualifiedName,
|
||||||
Icon(Lucide.ArrowLeft, contentDescription = "Back")
|
onBack = { navController.popBackStack() },
|
||||||
}
|
|
||||||
},
|
|
||||||
actions = {
|
|
||||||
MainAppBarActions(
|
|
||||||
navController = navController,
|
|
||||||
currentRouteName = Admin::class.qualifiedName,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { inner ->
|
) { inner ->
|
||||||
|
|||||||
+6
-19
@@ -12,14 +12,11 @@ import androidx.compose.material3.AssistChip
|
|||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -29,13 +26,11 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import com.composables.icons.lucide.ArrowLeft
|
|
||||||
import com.composables.icons.lucide.Lucide
|
|
||||||
import com.fabledsword.minstrel.models.AdminQuarantineItemRef
|
import com.fabledsword.minstrel.models.AdminQuarantineItemRef
|
||||||
import com.fabledsword.minstrel.nav.AdminQuarantine
|
import com.fabledsword.minstrel.nav.AdminQuarantine
|
||||||
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
||||||
import com.fabledsword.minstrel.shared.widgets.LoadingCentered
|
import com.fabledsword.minstrel.shared.widgets.LoadingCentered
|
||||||
import com.fabledsword.minstrel.shared.widgets.MainAppBarActions
|
import com.fabledsword.minstrel.shared.widgets.MinstrelTopAppBar
|
||||||
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -48,19 +43,11 @@ fun AdminQuarantineScreen(
|
|||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
MinstrelTopAppBar(
|
||||||
title = { Text("Admin · Quarantine") },
|
title = "Admin · Quarantine",
|
||||||
navigationIcon = {
|
navController = navController,
|
||||||
IconButton(onClick = { navController.popBackStack() }) {
|
currentRouteName = AdminQuarantine::class.qualifiedName,
|
||||||
Icon(Lucide.ArrowLeft, contentDescription = "Back")
|
onBack = { navController.popBackStack() },
|
||||||
}
|
|
||||||
},
|
|
||||||
actions = {
|
|
||||||
MainAppBarActions(
|
|
||||||
navController = navController,
|
|
||||||
currentRouteName = AdminQuarantine::class.qualifiedName,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { inner ->
|
) { inner ->
|
||||||
|
|||||||
@@ -12,13 +12,10 @@ import androidx.compose.material3.AssistChip
|
|||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -28,13 +25,11 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import com.composables.icons.lucide.ArrowLeft
|
|
||||||
import com.composables.icons.lucide.Lucide
|
|
||||||
import com.fabledsword.minstrel.models.RequestRef
|
import com.fabledsword.minstrel.models.RequestRef
|
||||||
import com.fabledsword.minstrel.nav.AdminRequests
|
import com.fabledsword.minstrel.nav.AdminRequests
|
||||||
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
||||||
import com.fabledsword.minstrel.shared.widgets.LoadingCentered
|
import com.fabledsword.minstrel.shared.widgets.LoadingCentered
|
||||||
import com.fabledsword.minstrel.shared.widgets.MainAppBarActions
|
import com.fabledsword.minstrel.shared.widgets.MinstrelTopAppBar
|
||||||
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -47,19 +42,11 @@ fun AdminRequestsScreen(
|
|||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
MinstrelTopAppBar(
|
||||||
title = { Text("Admin · Requests") },
|
title = "Admin · Requests",
|
||||||
navigationIcon = {
|
navController = navController,
|
||||||
IconButton(onClick = { navController.popBackStack() }) {
|
currentRouteName = AdminRequests::class.qualifiedName,
|
||||||
Icon(Lucide.ArrowLeft, contentDescription = "Back")
|
onBack = { navController.popBackStack() },
|
||||||
}
|
|
||||||
},
|
|
||||||
actions = {
|
|
||||||
MainAppBarActions(
|
|
||||||
navController = navController,
|
|
||||||
currentRouteName = AdminRequests::class.qualifiedName,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { inner ->
|
) { inner ->
|
||||||
|
|||||||
@@ -14,12 +14,10 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import com.composables.icons.lucide.Lucide
|
import com.composables.icons.lucide.Lucide
|
||||||
import com.composables.icons.lucide.ArrowLeft
|
|
||||||
import com.composables.icons.lucide.Copy
|
import com.composables.icons.lucide.Copy
|
||||||
import com.composables.icons.lucide.KeyRound
|
import com.composables.icons.lucide.KeyRound
|
||||||
import com.composables.icons.lucide.Plus
|
import com.composables.icons.lucide.Plus
|
||||||
import com.composables.icons.lucide.Trash2
|
import com.composables.icons.lucide.Trash2
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
@@ -30,7 +28,6 @@ import androidx.compose.material3.Scaffold
|
|||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import androidx.compose.ui.platform.ClipEntry
|
import androidx.compose.ui.platform.ClipEntry
|
||||||
import androidx.compose.ui.platform.LocalClipboard
|
import androidx.compose.ui.platform.LocalClipboard
|
||||||
@@ -52,11 +49,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import com.fabledsword.minstrel.models.AdminUserRef
|
import com.fabledsword.minstrel.models.AdminUserRef
|
||||||
import com.fabledsword.minstrel.nav.AdminUsers
|
import com.fabledsword.minstrel.nav.AdminUsers
|
||||||
import com.fabledsword.minstrel.shared.widgets.MainAppBarActions
|
import com.fabledsword.minstrel.shared.widgets.MinstrelTopAppBar
|
||||||
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AdminUsersScreen(
|
fun AdminUsersScreen(
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
@@ -117,7 +113,6 @@ fun AdminUsersScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun AdminUsersScaffold(
|
private fun AdminUsersScaffold(
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
@@ -134,19 +129,11 @@ private fun AdminUsersScaffold(
|
|||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
MinstrelTopAppBar(
|
||||||
title = { Text("Admin · Users") },
|
title = "Admin · Users",
|
||||||
navigationIcon = {
|
navController = navController,
|
||||||
IconButton(onClick = { navController.popBackStack() }) {
|
currentRouteName = AdminUsers::class.qualifiedName,
|
||||||
Icon(Lucide.ArrowLeft, contentDescription = "Back")
|
onBack = { navController.popBackStack() },
|
||||||
}
|
|
||||||
},
|
|
||||||
actions = {
|
|
||||||
MainAppBarActions(
|
|
||||||
navController = navController,
|
|
||||||
currentRouteName = AdminUsers::class.qualifiedName,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { inner ->
|
) { inner ->
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import androidx.compose.material3.Scaffold
|
|||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
@@ -35,7 +34,6 @@ import androidx.compose.ui.text.input.ImeAction
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import com.composables.icons.lucide.ArrowLeft
|
|
||||||
import com.composables.icons.lucide.Lucide
|
import com.composables.icons.lucide.Lucide
|
||||||
import com.composables.icons.lucide.Search as LucideSearch
|
import com.composables.icons.lucide.Search as LucideSearch
|
||||||
import com.fabledsword.minstrel.discover.data.RequestOutcome
|
import com.fabledsword.minstrel.discover.data.RequestOutcome
|
||||||
@@ -44,7 +42,7 @@ import com.fabledsword.minstrel.models.LidarrRequestKind
|
|||||||
import com.fabledsword.minstrel.models.LidarrSearchResultRef
|
import com.fabledsword.minstrel.models.LidarrSearchResultRef
|
||||||
import com.fabledsword.minstrel.nav.Discover
|
import com.fabledsword.minstrel.nav.Discover
|
||||||
import com.fabledsword.minstrel.shared.widgets.LoadingCentered
|
import com.fabledsword.minstrel.shared.widgets.LoadingCentered
|
||||||
import com.fabledsword.minstrel.shared.widgets.MainAppBarActions
|
import com.fabledsword.minstrel.shared.widgets.MinstrelTopAppBar
|
||||||
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -63,19 +61,11 @@ fun DiscoverScreen(
|
|||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
MinstrelTopAppBar(
|
||||||
title = { Text("Discover") },
|
title = "Discover",
|
||||||
navigationIcon = {
|
navController = navController,
|
||||||
IconButton(onClick = { navController.popBackStack() }) {
|
currentRouteName = Discover::class.qualifiedName,
|
||||||
Icon(Lucide.ArrowLeft, contentDescription = "Back")
|
onBack = { navController.popBackStack() },
|
||||||
}
|
|
||||||
},
|
|
||||||
actions = {
|
|
||||||
MainAppBarActions(
|
|
||||||
navController = navController,
|
|
||||||
currentRouteName = Discover::class.qualifiedName,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
snackbarHost = { SnackbarHost(snackbar) },
|
snackbarHost = { SnackbarHost(snackbar) },
|
||||||
|
|||||||
@@ -21,14 +21,12 @@ import androidx.compose.foundation.lazy.LazyRow
|
|||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -70,7 +68,7 @@ import com.fabledsword.minstrel.playlists.widgets.PlaylistPlaceholderCard
|
|||||||
import com.fabledsword.minstrel.shared.UiState
|
import com.fabledsword.minstrel.shared.UiState
|
||||||
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
||||||
import com.fabledsword.minstrel.shared.widgets.HorizontalScrollRow
|
import com.fabledsword.minstrel.shared.widgets.HorizontalScrollRow
|
||||||
import com.fabledsword.minstrel.shared.widgets.MainAppBarActions
|
import com.fabledsword.minstrel.shared.widgets.MinstrelTopAppBar
|
||||||
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
||||||
import com.fabledsword.minstrel.shared.widgets.SkeletonAlbumTile
|
import com.fabledsword.minstrel.shared.widgets.SkeletonAlbumTile
|
||||||
import com.fabledsword.minstrel.shared.widgets.SkeletonArtistTile
|
import com.fabledsword.minstrel.shared.widgets.SkeletonArtistTile
|
||||||
@@ -236,7 +234,6 @@ class HomeViewModel @Inject constructor(
|
|||||||
|
|
||||||
// ─── Screen ──────────────────────────────────────────────────────────
|
// ─── Screen ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeScreen(
|
fun HomeScreen(
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
@@ -249,14 +246,10 @@ fun HomeScreen(
|
|||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
MinstrelTopAppBar(
|
||||||
title = { Text("Minstrel") },
|
title = "Minstrel",
|
||||||
actions = {
|
navController = navController,
|
||||||
MainAppBarActions(
|
currentRouteName = Home::class.qualifiedName,
|
||||||
navController = navController,
|
|
||||||
currentRouteName = Home::class.qualifiedName,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import androidx.compose.material3.SnackbarHost
|
|||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.Tab
|
import androidx.compose.material3.Tab
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -49,7 +48,7 @@ import com.composables.icons.lucide.Shuffle
|
|||||||
import com.fabledsword.minstrel.shared.UiState
|
import com.fabledsword.minstrel.shared.UiState
|
||||||
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
||||||
import com.fabledsword.minstrel.shared.widgets.ErrorRetry
|
import com.fabledsword.minstrel.shared.widgets.ErrorRetry
|
||||||
import com.fabledsword.minstrel.shared.widgets.MainAppBarActions
|
import com.fabledsword.minstrel.shared.widgets.MinstrelTopAppBar
|
||||||
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
||||||
import com.fabledsword.minstrel.shared.widgets.SkeletonAlbumTile
|
import com.fabledsword.minstrel.shared.widgets.SkeletonAlbumTile
|
||||||
import com.fabledsword.minstrel.shared.widgets.SkeletonArtistTile
|
import com.fabledsword.minstrel.shared.widgets.SkeletonArtistTile
|
||||||
@@ -88,8 +87,10 @@ fun LibraryScreen(
|
|||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
Column {
|
Column {
|
||||||
TopAppBar(
|
MinstrelTopAppBar(
|
||||||
title = { Text("Library") },
|
title = "Library",
|
||||||
|
navController = navController,
|
||||||
|
currentRouteName = Library::class.qualifiedName,
|
||||||
actions = {
|
actions = {
|
||||||
IconButton(onClick = viewModel::shuffleAll) {
|
IconButton(onClick = viewModel::shuffleAll) {
|
||||||
Icon(
|
Icon(
|
||||||
@@ -98,10 +99,6 @@ fun LibraryScreen(
|
|||||||
tint = MaterialTheme.colorScheme.onSurface,
|
tint = MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
MainAppBarActions(
|
|
||||||
navController = navController,
|
|
||||||
currentRouteName = Library::class.qualifiedName,
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
PrimaryScrollableTabRow(
|
PrimaryScrollableTabRow(
|
||||||
|
|||||||
+5
-12
@@ -9,11 +9,9 @@ import androidx.compose.foundation.lazy.grid.GridCells
|
|||||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
import androidx.compose.foundation.lazy.grid.items
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -33,7 +31,7 @@ import com.fabledsword.minstrel.shared.UiState
|
|||||||
import com.fabledsword.minstrel.playlists.widgets.PlaylistCard
|
import com.fabledsword.minstrel.playlists.widgets.PlaylistCard
|
||||||
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
||||||
import com.fabledsword.minstrel.shared.widgets.LoadingCentered
|
import com.fabledsword.minstrel.shared.widgets.LoadingCentered
|
||||||
import com.fabledsword.minstrel.shared.widgets.MainAppBarActions
|
import com.fabledsword.minstrel.shared.widgets.MinstrelTopAppBar
|
||||||
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
@@ -95,7 +93,6 @@ class PlaylistsListViewModel @Inject constructor(
|
|||||||
|
|
||||||
// ─── Screen ──────────────────────────────────────────────────────────
|
// ─── Screen ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PlaylistsListScreen(
|
fun PlaylistsListScreen(
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
@@ -104,14 +101,10 @@ fun PlaylistsListScreen(
|
|||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
MinstrelTopAppBar(
|
||||||
title = { Text("Playlists") },
|
title = "Playlists",
|
||||||
actions = {
|
navController = navController,
|
||||||
MainAppBarActions(
|
currentRouteName = Playlists::class.qualifiedName,
|
||||||
navController = navController,
|
|
||||||
currentRouteName = Playlists::class.qualifiedName,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { inner ->
|
) { inner ->
|
||||||
|
|||||||
@@ -14,16 +14,13 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.AssistChip
|
import androidx.compose.material3.AssistChip
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -36,7 +33,6 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import com.composables.icons.lucide.ArrowLeft
|
|
||||||
import com.composables.icons.lucide.Disc3
|
import com.composables.icons.lucide.Disc3
|
||||||
import com.composables.icons.lucide.LibraryBig
|
import com.composables.icons.lucide.LibraryBig
|
||||||
import com.composables.icons.lucide.Lucide
|
import com.composables.icons.lucide.Lucide
|
||||||
@@ -51,10 +47,9 @@ import com.fabledsword.minstrel.nav.ArtistDetail
|
|||||||
import com.fabledsword.minstrel.nav.Requests
|
import com.fabledsword.minstrel.nav.Requests
|
||||||
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
||||||
import com.fabledsword.minstrel.shared.widgets.LoadingCentered
|
import com.fabledsword.minstrel.shared.widgets.LoadingCentered
|
||||||
import com.fabledsword.minstrel.shared.widgets.MainAppBarActions
|
import com.fabledsword.minstrel.shared.widgets.MinstrelTopAppBar
|
||||||
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
import com.fabledsword.minstrel.shared.widgets.PullToRefreshScaffold
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun RequestsScreen(
|
fun RequestsScreen(
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
@@ -64,19 +59,11 @@ fun RequestsScreen(
|
|||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
MinstrelTopAppBar(
|
||||||
title = { Text("Your requests") },
|
title = "Your requests",
|
||||||
navigationIcon = {
|
navController = navController,
|
||||||
IconButton(onClick = { navController.popBackStack() }) {
|
currentRouteName = Requests::class.qualifiedName,
|
||||||
Icon(Lucide.ArrowLeft, contentDescription = "Back")
|
onBack = { navController.popBackStack() },
|
||||||
}
|
|
||||||
},
|
|
||||||
actions = {
|
|
||||||
MainAppBarActions(
|
|
||||||
navController = navController,
|
|
||||||
currentRouteName = Requests::class.qualifiedName,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { inner ->
|
) { inner ->
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ import androidx.compose.material3.SegmentedButtonDefaults
|
|||||||
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -55,11 +54,10 @@ import com.fabledsword.minstrel.nav.Admin
|
|||||||
import com.fabledsword.minstrel.nav.Requests
|
import com.fabledsword.minstrel.nav.Requests
|
||||||
import com.fabledsword.minstrel.nav.Settings as SettingsRoute
|
import com.fabledsword.minstrel.nav.Settings as SettingsRoute
|
||||||
import com.fabledsword.minstrel.nav.ServerUrl
|
import com.fabledsword.minstrel.nav.ServerUrl
|
||||||
import com.fabledsword.minstrel.shared.widgets.MainAppBarActions
|
import com.fabledsword.minstrel.shared.widgets.MinstrelTopAppBar
|
||||||
import com.fabledsword.minstrel.theme.ThemeMode
|
import com.fabledsword.minstrel.theme.ThemeMode
|
||||||
import com.fabledsword.minstrel.theme.ThemePreferenceViewModel
|
import com.fabledsword.minstrel.theme.ThemePreferenceViewModel
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreen(
|
fun SettingsScreen(
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
@@ -82,14 +80,10 @@ fun SettingsScreen(
|
|||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
MinstrelTopAppBar(
|
||||||
title = { Text("Settings") },
|
title = "Settings",
|
||||||
actions = {
|
navController = navController,
|
||||||
MainAppBarActions(
|
currentRouteName = SettingsRoute::class.qualifiedName,
|
||||||
navController = navController,
|
|
||||||
currentRouteName = SettingsRoute::class.qualifiedName,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { inner ->
|
) { inner ->
|
||||||
|
|||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
package com.fabledsword.minstrel.shared.widgets
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import com.composables.icons.lucide.ArrowLeft
|
||||||
|
import com.composables.icons.lucide.Lucide
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The standard Minstrel top app bar — a `Text(title)` on the left, the
|
||||||
|
* universal [MainAppBarActions] row on the right, optionally preceded
|
||||||
|
* by extra screen-specific actions and a back arrow.
|
||||||
|
*
|
||||||
|
* Replaces the TopAppBar + title + navigationIcon + MainAppBarActions
|
||||||
|
* sandwich that was duplicated across ~10 screens. Library wraps this
|
||||||
|
* in a `Column` with its tab row; drill-down screens pass [onBack]
|
||||||
|
* for the back arrow; Library passes its Shuffle button via [actions].
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun MinstrelTopAppBar(
|
||||||
|
title: String,
|
||||||
|
navController: NavHostController,
|
||||||
|
currentRouteName: String?,
|
||||||
|
onBack: (() -> Unit)? = null,
|
||||||
|
actions: @Composable RowScope.() -> Unit = {},
|
||||||
|
) {
|
||||||
|
TopAppBar(
|
||||||
|
title = { Text(title) },
|
||||||
|
navigationIcon = {
|
||||||
|
if (onBack != null) {
|
||||||
|
IconButton(onClick = onBack) {
|
||||||
|
Icon(Lucide.ArrowLeft, contentDescription = "Back")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
actions()
|
||||||
|
MainAppBarActions(
|
||||||
|
navController = navController,
|
||||||
|
currentRouteName = currentRouteName,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user