mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 15:11:58 +01:00
polish library controls and song ordering
This commit is contained in:
@@ -26,7 +26,7 @@ internal const val SONGS_SQL =
|
||||
"LEFT JOIN albums ON albums.sourceId = songs.sourceId AND albums.id = songs.albumId " +
|
||||
"WHERE songs.sourceId = :sourceId " +
|
||||
"ORDER BY albums.albumArtist COLLATE NOCASE, songs.album COLLATE NOCASE, songs.disc, songs.track, " +
|
||||
"songs.title COLLATE NOCASE, songs.id"
|
||||
"songs.title COLLATE NOCASE, songs.album COLLATE NOCASE, songs.disc, songs.track, songs.id"
|
||||
|
||||
@Dao
|
||||
@DaoReturnTypeConverters(PagingSourceDaoReturnTypeConverter::class)
|
||||
@@ -350,7 +350,7 @@ interface LibraryDao {
|
||||
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
|
||||
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
|
||||
"ORDER BY albums.albumArtist COLLATE NOCASE, songs.album COLLATE NOCASE, songs.disc, songs.track, " +
|
||||
"songs.title COLLATE NOCASE, songs.id",
|
||||
"songs.title COLLATE NOCASE, songs.album COLLATE NOCASE, songs.disc, songs.track, songs.id",
|
||||
)
|
||||
fun songs(
|
||||
sourceId: Long,
|
||||
@@ -367,7 +367,7 @@ interface LibraryDao {
|
||||
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
|
||||
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
|
||||
"ORDER BY albums.albumArtist COLLATE NOCASE DESC, songs.album COLLATE NOCASE DESC, songs.disc DESC, " +
|
||||
"songs.track DESC, songs.title COLLATE NOCASE DESC, songs.id DESC",
|
||||
"songs.track DESC, songs.title COLLATE NOCASE DESC, songs.album COLLATE NOCASE, songs.disc, songs.track, songs.id DESC",
|
||||
)
|
||||
fun songsByAlbumReversed(
|
||||
sourceId: Long,
|
||||
@@ -383,7 +383,7 @@ interface LibraryDao {
|
||||
"(:starredFilter = 2 AND songs.starred IS NULL)) " +
|
||||
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
|
||||
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
|
||||
"ORDER BY songs.title COLLATE NOCASE, songs.id",
|
||||
"ORDER BY songs.title COLLATE NOCASE, songs.album COLLATE NOCASE, songs.disc, songs.track, songs.id",
|
||||
)
|
||||
fun songsByTitle(
|
||||
sourceId: Long,
|
||||
@@ -399,7 +399,7 @@ interface LibraryDao {
|
||||
"(:starredFilter = 2 AND songs.starred IS NULL)) " +
|
||||
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
|
||||
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
|
||||
"ORDER BY songs.title COLLATE NOCASE DESC, songs.id",
|
||||
"ORDER BY songs.title COLLATE NOCASE DESC, songs.album COLLATE NOCASE, songs.disc, songs.track, songs.id",
|
||||
)
|
||||
fun songsByTitleReversed(
|
||||
sourceId: Long,
|
||||
@@ -447,7 +447,7 @@ interface LibraryDao {
|
||||
"(:starredFilter = 2 AND songs.starred IS NULL)) " +
|
||||
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
|
||||
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
|
||||
"ORDER BY songs.starred IS NULL, songs.starred DESC, songs.title COLLATE NOCASE, songs.id",
|
||||
"ORDER BY songs.starred IS NULL, songs.starred DESC, songs.album COLLATE NOCASE, songs.disc, songs.track, songs.id",
|
||||
)
|
||||
fun songsByStarred(
|
||||
sourceId: Long,
|
||||
@@ -463,7 +463,7 @@ interface LibraryDao {
|
||||
"(:starredFilter = 2 AND songs.starred IS NULL)) " +
|
||||
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
|
||||
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
|
||||
"ORDER BY songs.starred IS NULL, songs.starred ASC, songs.title COLLATE NOCASE, songs.id",
|
||||
"ORDER BY songs.starred IS NULL, songs.starred ASC, songs.album COLLATE NOCASE, songs.disc, songs.track, songs.id",
|
||||
)
|
||||
fun songsByStarredReversed(
|
||||
sourceId: Long,
|
||||
@@ -479,7 +479,7 @@ interface LibraryDao {
|
||||
"(:starredFilter = 2 AND songs.starred IS NULL)) " +
|
||||
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
|
||||
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
|
||||
"ORDER BY songs.created DESC, songs.title COLLATE NOCASE, songs.id",
|
||||
"ORDER BY songs.created DESC, songs.album COLLATE NOCASE, songs.disc, songs.track, songs.id",
|
||||
)
|
||||
fun songsByAdded(
|
||||
sourceId: Long,
|
||||
@@ -495,7 +495,7 @@ interface LibraryDao {
|
||||
"(:starredFilter = 2 AND songs.starred IS NULL)) " +
|
||||
"AND (:search = '' OR instr(lower(songs.title), lower(:search)) > 0 " +
|
||||
"OR instr(lower(songs.artist), lower(:search)) > 0 OR instr(lower(songs.album), lower(:search)) > 0) " +
|
||||
"ORDER BY songs.created ASC, songs.title COLLATE NOCASE, songs.id",
|
||||
"ORDER BY songs.created ASC, songs.album COLLATE NOCASE, songs.disc, songs.track, songs.id",
|
||||
)
|
||||
fun songsByAddedReversed(
|
||||
sourceId: Long,
|
||||
|
||||
@@ -19,7 +19,6 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -137,6 +136,8 @@ private fun MainNavigation() {
|
||||
LaunchedEffect(Unit) { if (nowPlayingOpen) nowPlayingProgress = 1f }
|
||||
|
||||
val playerVisible = playback.item != null
|
||||
var librarySearching by rememberSaveable { mutableStateOf(false) }
|
||||
val showMiniPlayer = playerVisible && !librarySearching
|
||||
LaunchedEffect(playerVisible) {
|
||||
if (!playerVisible) {
|
||||
settleJob?.cancel()
|
||||
@@ -148,7 +149,7 @@ private fun MainNavigation() {
|
||||
val navBarInset = with(density) { WindowInsets.navigationBars.getBottom(this).toDp() }
|
||||
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
Column(Modifier.fillMaxSize().imePadding()) {
|
||||
Column(Modifier.fillMaxSize()) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
modifier =
|
||||
@@ -185,7 +186,8 @@ private fun MainNavigation() {
|
||||
onArtistClick = { artist -> navController.navigate(Routes.artist(artist.id, artist.coverArt)) },
|
||||
onPlaylistClick = { playlist -> navController.navigate(Routes.playlist(playlist.id)) },
|
||||
onOpenSettings = { navController.navigate(Routes.SETTINGS) },
|
||||
bottomInset = if (playerVisible) 0.dp else navBarInset,
|
||||
onSearchActiveChange = { librarySearching = it },
|
||||
bottomInset = if (showMiniPlayer) 0.dp else navBarInset,
|
||||
)
|
||||
}
|
||||
composable(Routes.SETTINGS) {
|
||||
@@ -254,7 +256,7 @@ private fun MainNavigation() {
|
||||
}
|
||||
}
|
||||
|
||||
if (playerVisible) {
|
||||
if (showMiniPlayer) {
|
||||
val miniArt = playbackController.coverArt(playback.item, thumbnail = true)
|
||||
MiniPlayer(
|
||||
state = playback,
|
||||
|
||||
@@ -50,12 +50,13 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -164,6 +165,7 @@ fun LibraryRoute(
|
||||
onArtistClick: (Artist) -> Unit,
|
||||
onPlaylistClick: (Playlist) -> Unit,
|
||||
onOpenSettings: () -> Unit,
|
||||
onSearchActiveChange: (Boolean) -> Unit,
|
||||
bottomInset: Dp,
|
||||
viewModel: LibraryViewModel = koinViewModel(),
|
||||
) {
|
||||
@@ -195,6 +197,7 @@ fun LibraryRoute(
|
||||
onSongClick = viewModel::playSong,
|
||||
onSync = viewModel::sync,
|
||||
onOpenSettings = onOpenSettings,
|
||||
onSearchActiveChange = onSearchActiveChange,
|
||||
playingSongId = playingSongId,
|
||||
listQuery = listQuery,
|
||||
resetKeys = resetKeys,
|
||||
@@ -249,6 +252,7 @@ fun LibraryScreen(
|
||||
onClearFilters: () -> Unit = {},
|
||||
search: String = "",
|
||||
onSearchChange: (String) -> Unit = {},
|
||||
onSearchActiveChange: (Boolean) -> Unit = {},
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val pagerState =
|
||||
@@ -260,6 +264,11 @@ fun LibraryScreen(
|
||||
var searchActive by rememberSaveable { mutableStateOf(false) }
|
||||
val searchFocus = remember { FocusRequester() }
|
||||
|
||||
DisposableEffect(searchActive) {
|
||||
onSearchActiveChange(searchActive)
|
||||
onDispose { onSearchActiveChange(false) }
|
||||
}
|
||||
|
||||
LaunchedEffect(pagerState) {
|
||||
snapshotFlow { pagerState.currentPage }.collect { page ->
|
||||
onTabSelected(LibraryTab.entries[page])
|
||||
@@ -524,10 +533,11 @@ private fun ListOptionsSheet(
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
if (listQuery.starred != StarredFilter.Any) {
|
||||
IconButton(onClick = onClearFilters) {
|
||||
Icon(Icons.Rounded.FilterAltOff, contentDescription = "Clear filters")
|
||||
}
|
||||
IconButton(
|
||||
onClick = onClearFilters,
|
||||
enabled = listQuery.starred != StarredFilter.Any,
|
||||
) {
|
||||
Icon(Icons.Rounded.FilterAltOff, contentDescription = "Clear filters")
|
||||
}
|
||||
}
|
||||
FlowRow(
|
||||
@@ -582,21 +592,24 @@ private fun SearchField(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LaunchedEffect(Unit) { focusRequester.requestFocus() }
|
||||
OutlinedTextField(
|
||||
TextField(
|
||||
value = value,
|
||||
onValueChange = onValueChange,
|
||||
singleLine = true,
|
||||
label = { Text("Search") },
|
||||
placeholder = { Text("Search") },
|
||||
leadingIcon = { Icon(Icons.Rounded.Search, contentDescription = null) },
|
||||
trailingIcon = {
|
||||
IconButton(onClick = onClose) {
|
||||
Icon(Icons.Rounded.Close, contentDescription = "Close search")
|
||||
}
|
||||
},
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
colors =
|
||||
OutlinedTextFieldDefaults.colors(
|
||||
focusedContainerColor = MaterialTheme.colorScheme.background,
|
||||
unfocusedContainerColor = MaterialTheme.colorScheme.background,
|
||||
TextFieldDefaults.colors(
|
||||
focusedContainerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
unfocusedContainerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
),
|
||||
modifier = modifier.focusRequester(focusRequester),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user