Android v1 polish + Web UI flavor pass #65
@@ -101,7 +101,12 @@ import javax.inject.Inject
|
||||
private const val SHARE_STOP_TIMEOUT_MS = 5_000L
|
||||
private const val PLAYLIST_FETCH_TIMEOUT_MS = 8_000L
|
||||
private const val BOTTOM_PADDING_FOR_MINIPLAYER_DP = 140
|
||||
private const val RECENTLY_ADDED_CHUNK = 25
|
||||
// Recently Added is laid out in a multi-row LazyHorizontalGrid that
|
||||
// scrolls as one panel (same pattern as Most Played). Two rows trades
|
||||
// a tall block for fewer columns to scroll past — a 200dp tile × 2 =
|
||||
// ~440dp section still leaves room for the rest of Home.
|
||||
private const val RECENTLY_ADDED_GRID_ROWS = 2
|
||||
private const val RECENTLY_ADDED_GRID_HEIGHT_DP = 440
|
||||
|
||||
// ─── State ───────────────────────────────────────────────────────────
|
||||
|
||||
@@ -512,23 +517,75 @@ private fun LazyListScope.recentlyAddedSection(
|
||||
}
|
||||
return
|
||||
}
|
||||
// Chunk into stacked carousels of 25 so a large library reads as
|
||||
// multiple rows instead of one unwieldy LazyRow. Only the first
|
||||
// chunk carries the section title; the rest are continuation rows.
|
||||
val chunks = albums.chunked(RECENTLY_ADDED_CHUNK)
|
||||
itemsIndexed(
|
||||
items = chunks,
|
||||
key = { index, chunk -> "recent-$index-${chunk.first().id}" },
|
||||
) { index, chunk ->
|
||||
AlbumsRow(
|
||||
title = if (index == 0) "Recently added" else "",
|
||||
albums = chunk,
|
||||
item {
|
||||
RecentlyAddedGrid(
|
||||
albums = albums,
|
||||
onAlbumClick = onAlbumClick,
|
||||
onPlayAlbum = onPlayAlbum,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi-row horizontal grid for Recently Added, mirroring the
|
||||
* Most Played pattern: tiles laid out column-major across
|
||||
* [RECENTLY_ADDED_GRID_ROWS] rows inside one LazyHorizontalGrid so
|
||||
* every row scrolls together as one panel. Replaces the previous
|
||||
* stacked-LazyRows layout which scrolled each chunk independently.
|
||||
*/
|
||||
@Composable
|
||||
private fun RecentlyAddedGrid(
|
||||
albums: List<HomeTile<AlbumRef>>,
|
||||
onAlbumClick: (String) -> Unit,
|
||||
onPlayAlbum: suspend (String) -> Unit,
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
text = "Recently added",
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
)
|
||||
// Re-flatten column-major to match Web's "ranks across the
|
||||
// top row, then across the middle, then bottom" reading
|
||||
// order. LazyHorizontalGrid otherwise fills column-major
|
||||
// directly from a flat list and would scramble per-rank
|
||||
// expectations.
|
||||
val perRow = (albums.size + RECENTLY_ADDED_GRID_ROWS - 1) / RECENTLY_ADDED_GRID_ROWS
|
||||
val rows = albums.chunked(perRow.coerceAtLeast(1))
|
||||
val cols = rows.maxOfOrNull { it.size } ?: 0
|
||||
val ordered = buildList {
|
||||
for (c in 0 until cols) {
|
||||
for (r in 0 until RECENTLY_ADDED_GRID_ROWS) {
|
||||
rows.getOrNull(r)?.getOrNull(c)?.let { add(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
LazyHorizontalGrid(
|
||||
rows = GridCells.Fixed(RECENTLY_ADDED_GRID_ROWS),
|
||||
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(RECENTLY_ADDED_GRID_HEIGHT_DP.dp),
|
||||
) {
|
||||
gridItems(items = ordered, key = { it.id }) { tile ->
|
||||
val album = tile.value
|
||||
if (album == null) {
|
||||
SkeletonAlbumTile()
|
||||
} else {
|
||||
AlbumCard(
|
||||
album = album,
|
||||
onClick = { onAlbumClick(album.id) },
|
||||
onPlay = { onPlayAlbum(album.id) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.rediscoverSection(
|
||||
albums: List<HomeTile<AlbumRef>>,
|
||||
artists: List<HomeTile<ArtistRef>>,
|
||||
|
||||
Reference in New Issue
Block a user