home polish: genres by most played in a wrapping flow, tighter album corners, spaced left-aligned artists

This commit is contained in:
austinried
2026-10-04 00:30:06 +00:00
parent 796e5e2827
commit ff236836a2
7 changed files with 56 additions and 30 deletions
@@ -306,17 +306,17 @@ interface LibraryDao {
@Query(
"SELECT genre FROM (" +
"SELECT song_genres.genre AS genre, songs.played AS played FROM song_genres " +
"SELECT song_genres.genre AS genre, songs.playCount AS plays FROM song_genres " +
"JOIN songs ON songs.sourceId = song_genres.sourceId AND songs.id = song_genres.songId " +
"WHERE song_genres.sourceId = :sourceId " +
"UNION ALL " +
"SELECT songs.genre AS genre, songs.played AS played FROM songs " +
"SELECT songs.genre AS genre, songs.playCount AS plays FROM songs " +
"WHERE songs.sourceId = :sourceId AND songs.genre IS NOT NULL AND songs.genre != '' " +
"AND NOT EXISTS (SELECT 1 FROM song_genres " +
"WHERE song_genres.sourceId = songs.sourceId AND song_genres.songId = songs.id)" +
") GROUP BY genre ORDER BY MAX(played) DESC NULLS LAST, genre COLLATE NOCASE",
") GROUP BY genre ORDER BY SUM(plays) DESC, genre COLLATE NOCASE",
)
fun genresByRecentPlay(sourceId: Long): Flow<List<String>>
fun genresByMostPlayed(sourceId: Long): Flow<List<String>>
@Query(
"SELECT DISTINCT (year / 10) * 10 AS decade FROM albums " +
@@ -79,7 +79,7 @@ class LibraryRepository(
limit: Int,
): Flow<List<AlbumSongItem>> = db.libraryDao().recentlyStarredSongs(sourceId, limit)
fun genresByRecentPlay(sourceId: Long): Flow<List<String>> = db.libraryDao().genresByRecentPlay(sourceId)
fun genresByMostPlayed(sourceId: Long): Flow<List<String>> = db.libraryDao().genresByMostPlayed(sourceId)
fun decades(sourceId: Long): Flow<List<Long>> = db.libraryDao().decades(sourceId)
@@ -97,7 +97,7 @@ class HomeListViewModel(
val genres: StateFlow<List<String>> =
sourceId
.flatMapLatest { libraryRepository.genresByRecentPlay(it) }
.flatMapLatest { libraryRepository.genresByMostPlayed(it) }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList())
val decades: StateFlow<List<Long>> =
@@ -3,6 +3,8 @@ package com.subtracks.ui.home
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -246,6 +248,7 @@ private fun HomeSectionHeader(
private val TILE_SIZE = 132.dp
private val ARTIST_TILE_SIZE = 104.dp
private val TILE_SPACING = 8.dp
private val ARTIST_SPACING = 16.dp
private fun LazyListScope.albumRow(
section: HomeSection,
@@ -292,7 +295,7 @@ private fun LazyListScope.artistRow(
item(key = "${section.name}-row") {
LazyRow(
contentPadding = PaddingValues(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(TILE_SPACING),
horizontalArrangement = Arrangement.spacedBy(ARTIST_SPACING),
) {
items(artists, key = { it.id }) { artist ->
val ref = coverArt(artist.coverArt, true)
@@ -307,6 +310,7 @@ private fun LazyListScope.artistRow(
}
}
@OptIn(ExperimentalLayoutApi::class)
private fun LazyListScope.genreBlock(
section: HomeSection,
genres: List<String>,
@@ -318,16 +322,13 @@ private fun LazyListScope.genreBlock(
HomeSectionHeader(section.title) { onMore(section) }
}
item(key = "${section.name}-row") {
LazyRow(
contentPadding = PaddingValues(horizontal = 16.dp),
FlowRow(
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(TILE_SPACING),
verticalArrangement = Arrangement.spacedBy(TILE_SPACING),
) {
items(genres.chunked(GENRE_COLUMN_HEIGHT), key = { it.first() }) { column ->
Column(verticalArrangement = Arrangement.spacedBy(TILE_SPACING)) {
column.forEach { genre ->
AssistChip(onClick = { onGenreClick(genre) }, label = { Text(genre) })
}
}
genres.forEach { genre ->
AssistChip(onClick = { onGenreClick(genre) }, label = { Text(genre) })
}
}
}
@@ -384,8 +385,6 @@ private fun LazyListScope.starredSongs(
}
}
private const val GENRE_COLUMN_HEIGHT = 3
@Composable
private fun AlbumTile(
album: Album,
@@ -399,7 +398,7 @@ private fun AlbumTile(
CoverArt(
ref = ref,
name = album.name,
modifier = Modifier.size(TILE_SIZE).clip(RoundedCornerShape(4.dp)),
modifier = Modifier.size(TILE_SIZE).clip(RoundedCornerShape(2.dp)),
)
Spacer(Modifier.height(6.dp))
Text(
@@ -427,7 +426,7 @@ private fun ArtistTile(
) {
Column(
modifier = Modifier.width(ARTIST_TILE_SIZE).combinedClickable(onClick = onClick, onLongClick = onLongClick),
horizontalAlignment = Alignment.CenterHorizontally,
horizontalAlignment = Alignment.Start,
) {
CoverArt(
ref = ref,
@@ -67,7 +67,7 @@ class HomeViewModel(
}
val discovery =
combine(
libraryRepository.genresByRecentPlay(sourceId),
libraryRepository.genresByMostPlayed(sourceId),
libraryRepository.decades(sourceId),
libraryRepository.recentlyStarredSongs(sourceId, HOME_STARRED_LIMIT),
libraryRepository.recentlyAddedAlbums(sourceId, HOME_ROW_LIMIT),