queue the whole starred list from both starred song lists

This commit is contained in:
austinried
2026-10-04 00:30:06 +00:00
parent 3affe7e049
commit 796e5e2827
6 changed files with 49 additions and 12 deletions
@@ -704,6 +704,12 @@ interface LibraryDao {
)
fun starredSongs(sourceId: Long): PagingSource<Int, AlbumSongItem>
@Query(
"SELECT songs.id FROM songs WHERE sourceId = :sourceId AND starred IS NOT NULL " +
"ORDER BY starred DESC, title COLLATE NOCASE, id",
)
suspend fun starredSongIds(sourceId: Long): List<String>
@Query(
"SELECT songs.*, albums.coverArt AS coverArt FROM songs " +
"LEFT JOIN albums ON albums.sourceId = songs.sourceId AND albums.id = songs.albumId " +
@@ -88,6 +88,8 @@ class LibraryRepository(
fun starredSongs(sourceId: Long): Flow<PagingData<AlbumSongItem>> = pager(60) { db.libraryDao().starredSongs(sourceId) }
suspend fun starredSongIds(sourceId: Long): List<String> = db.libraryDao().starredSongIds(sourceId)
fun songsByGenre(
sourceId: Long,
genre: String,
@@ -111,6 +111,10 @@ class HomeListViewModel(
): CoverArtRef? = sourceRepository.coverArt(coverArt, thumbnail)
fun play(song: Song) {
viewModelScope.playSongInContext(libraryRepository, playbackController, song)
if (request.section == HomeSection.RecentlyStarredSongs) {
viewModelScope.playStarredList(libraryRepository, playbackController, song)
} else {
viewModelScope.playSongInContext(libraryRepository, playbackController, song)
}
}
}
@@ -16,7 +16,6 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.AssistChip
@@ -122,7 +121,7 @@ fun HomeScreen(
bottomInset: Dp = 0.dp,
onAlbumClick: (Album) -> Unit = {},
onArtistClick: (Artist) -> Unit = {},
onPlayStarred: (List<Song>, Int) -> Unit = { _, _ -> },
onPlayStarred: (Song) -> Unit = {},
onLongClick: (MenuTarget) -> Unit = {},
onMore: (HomeSection) -> Unit = {},
onGenreClick: (String) -> Unit = {},
@@ -361,7 +360,7 @@ private fun LazyListScope.starredSongs(
songs: List<AlbumSongItem>,
coverArt: (String?, Boolean) -> CoverArtRef?,
playingSongId: String?,
onPlayStarred: (List<Song>, Int) -> Unit,
onPlayStarred: (Song) -> Unit,
onLongClick: (MenuTarget) -> Unit,
onMore: (HomeSection) -> Unit,
) {
@@ -369,7 +368,7 @@ private fun LazyListScope.starredSongs(
item(key = "${section.name}-header") {
HomeSectionHeader(section.title) { onMore(section) }
}
itemsIndexed(songs, key = { _, item -> item.song.id }) { index, item ->
items(songs, key = { it.song.id }) { item ->
SongRow(
song = item.song,
coverArtId = item.coverArt,
@@ -378,7 +377,7 @@ private fun LazyListScope.starredSongs(
durationSeconds = item.song.duration,
modifier =
Modifier.combinedClickable(
onClick = { onPlayStarred(songs.map { it.song }, index) },
onClick = { onPlayStarred(item.song) },
onLongClick = { onLongClick(MenuTarget.Song(item.song, coverArt(item.coverArt, true))) },
),
)
@@ -101,12 +101,8 @@ class HomeViewModel(
thumbnail: Boolean,
): CoverArtRef? = sourceRepository.coverArt(coverArt, thumbnail)
fun playStarred(
songs: List<Song>,
startIndex: Int,
) {
val sourceId = songs.getOrNull(startIndex)?.sourceId ?: return
playbackController.playSongs(sourceId, songs.map { it.id }, startIndex)
fun playStarred(song: Song) {
viewModelScope.playStarredList(libraryRepository, playbackController, song)
}
fun sync() = syncManager.requestSync()
@@ -166,3 +162,14 @@ internal fun CoroutineScope.playSongInContext(
}
}
}
internal fun CoroutineScope.playStarredList(
libraryRepository: LibraryRepository,
playbackController: PlaybackController,
song: Song,
) {
launch {
val ids = libraryRepository.starredSongIds(song.sourceId)
playbackController.playSongs(song.sourceId, ids, ids.indexOf(song.id).coerceAtLeast(0))
}
}
@@ -316,6 +316,25 @@ class LibraryHomeDaoTest {
assertEquals("cover-1", page.first().coverArt)
}
@Test
fun starredSongIdsMatchTheDisplayedOrderSoTheStartTrackIsRight() =
runTest {
val sourceId = source()
val dao = db.libraryDao()
dao.upsertSongs(
listOf(
song(sourceId, "s1", "First", starred = 100),
song(sourceId, "s2", "Second", starred = 300),
song(sourceId, "s3", "Third", starred = null),
song(sourceId, "s4", "Fourth", starred = 200),
),
)
val displayed = dao.starredSongs(sourceId).page().map { it.song.id }
assertEquals(listOf("s2", "s4", "s1"), displayed)
assertEquals(displayed, dao.starredSongIds(sourceId))
}
@Test
fun songsByGenrePagingFiltersAndJoinsAlbumCoverArt() =
runTest {