Release: web UX overhaul + Android native port + server polish #59

Merged
bvandeusen merged 298 commits from dev into main 2026-06-01 16:11:21 -04:00
Showing only changes of commit ef8374753a - Show all commits
@@ -9,7 +9,9 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
@@ -869,16 +871,34 @@ private fun MostPlayedRow(
}
private const val MOST_PLAYED_ROWS = 3
private const val MOST_PLAYED_TILE_WIDTH_DP = 176
private const val MOST_PLAYED_TILE_HEIGHT_DP = 56
private const val MOST_PLAYED_COVER_DP = 48
// 3 rows of CompactTrackTile (~188dp intrinsic each: 140dp cover +
// 8dp spacer + 2 lines of text) + 2 * 8dp inter-row spacing = ~580dp;
// 600dp gives breathing room without leaving an obvious gap.
private const val MOST_PLAYED_GRID_HEIGHT_DP = 600
// 3 rows of MOST_PLAYED_TILE_HEIGHT_DP + 2 * 8dp inter-row spacing,
// rounded up. Mirrors Flutter (`CompactTrackCard` in
// flutter_client/lib/library/widgets/compact_track_card.dart) which
// uses a horizontal-row card pattern - much denser than the square
// per-track tiles that web uses (operator request 2026-06-01: "in the
// flutter iteration the tiles were different and smaller so more of
// them could fit in a smaller space").
private const val MOST_PLAYED_GRID_HEIGHT_DP = 200
@Composable
private fun SkeletonCompactTrackTile() {
Column(modifier = Modifier.width(140.dp)) {
SkeletonAlbumTile()
Row(
modifier = Modifier
.width(MOST_PLAYED_TILE_WIDTH_DP.dp)
.height(MOST_PLAYED_TILE_HEIGHT_DP.dp)
.padding(horizontal = 8.dp, vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Box(
modifier = Modifier
.size(MOST_PLAYED_COVER_DP.dp)
.clip(RoundedCornerShape(4.dp))
.background(MaterialTheme.colorScheme.surfaceVariant),
)
}
}
@@ -887,15 +907,18 @@ private fun CompactTrackTile(
track: TrackRef,
onClick: () -> Unit,
) {
Column(
Row(
modifier = Modifier
.width(140.dp)
.clickable(onClick = onClick),
.width(MOST_PLAYED_TILE_WIDTH_DP.dp)
.height(MOST_PLAYED_TILE_HEIGHT_DP.dp)
.clickable(onClick = onClick)
.padding(horizontal = 8.dp, vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Box(
modifier = Modifier
.size(140.dp)
.clip(RoundedCornerShape(6.dp))
.size(MOST_PLAYED_COVER_DP.dp)
.clip(RoundedCornerShape(4.dp))
.background(MaterialTheme.colorScheme.surfaceVariant),
contentAlignment = Alignment.Center,
) {
@@ -904,7 +927,7 @@ private fun CompactTrackTile(
imageVector = Lucide.Music,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.size(56.dp),
modifier = Modifier.size(20.dp),
)
} else {
AsyncImage(
@@ -915,21 +938,28 @@ private fun CompactTrackTile(
)
}
}
Spacer(Modifier.height(8.dp))
Text(
text = track.title,
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.onSurface,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
text = track.artistName.ifEmpty { " " },
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Spacer(Modifier.width(8.dp))
Column(
modifier = Modifier
.weight(1f)
.fillMaxHeight(),
verticalArrangement = Arrangement.Center,
) {
Text(
text = track.title,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurface,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(
text = track.artistName.ifEmpty { " " },
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
}
}