feat: artist cards with image in search + fix action bar overflow

Search: replace SmallArtistChip (text pill) with ArtistCard showing
circular profile image (72dp) + name below; works for both Navidrome
artists (uses coverArt) and iTunes artists (uses artworkUrl100 if present)

ArtistDetailScreen: convert action bar Row → LazyRow so Play / Shuffle /
Konzerte pills sit comfortably with horizontal scroll instead of cramming

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-06-15 00:24:18 +02:00
1 parent 13e9ec9d9a
commit 3855207530
2 files changed
+46 -25

No files matched your search

@@ -197,22 +197,27 @@ fun ArtistDetailScreen(
// Action bar // Action bar
item { item {
Row( LazyRow(
contentPadding = PaddingValues(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier modifier = Modifier.padding(bottom = 8.dp),
.padding(horizontal = 16.dp)
.padding(bottom = 8.dp),
) { ) {
ArtistActionPill(Icons.Default.PlayArrow, "Top Songs") { item {
if (state.topSongs.isNotEmpty()) onPlay(state.topSongs, 0) ArtistActionPill(Icons.Default.PlayArrow, "Top Songs") {
if (state.topSongs.isNotEmpty()) onPlay(state.topSongs, 0)
}
} }
ArtistActionPill(Icons.Default.Shuffle, "Shuffle") { item {
if (state.topSongs.isNotEmpty()) onPlay(state.topSongs.shuffled(), 0) ArtistActionPill(Icons.Default.Shuffle, "Shuffle") {
if (state.topSongs.isNotEmpty()) onPlay(state.topSongs.shuffled(), 0)
}
} }
val eventCount = state.events.size item {
val konzertLabel = if (eventCount > 0) "Konzerte ($eventCount)" else "Konzerte" val eventCount = state.events.size
ArtistActionPill(Icons.Default.Event, konzertLabel) { val konzertLabel = if (eventCount > 0) "Konzerte ($eventCount)" else "Konzerte"
vm.openEventsSheet() ArtistActionPill(Icons.Default.Event, konzertLabel) {
vm.openEventsSheet()
}
} }
} }
} }
@@ -280,12 +280,13 @@ private fun LibrarySearchContent(
item { item {
LazyRow( LazyRow(
contentPadding = PaddingValues(horizontal = 16.dp), contentPadding = PaddingValues(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp), horizontalArrangement = Arrangement.spacedBy(16.dp),
) { ) {
items(artists, key = { it.id }) { a -> items(artists, key = { it.id }) { a ->
SmallArtistChip(a.name) { onArtistClick(a) } ArtistCard(name = a.name, imageUrl = coverUrl(a.coverArt)) { onArtistClick(a) }
} }
} }
Spacer(Modifier.height(8.dp))
} }
} }
if (albums.isNotEmpty()) { if (albums.isNotEmpty()) {
@@ -408,12 +409,16 @@ private fun GlobalExploreContent(
item { item {
LazyRow( LazyRow(
contentPadding = PaddingValues(horizontal = 16.dp), contentPadding = PaddingValues(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp), horizontalArrangement = Arrangement.spacedBy(16.dp),
) { ) {
items(state.artists) { a -> items(state.artists) { a ->
SmallArtistChip(a.artistName ?: "") { vm.openArtistDetail(a) } ArtistCard(
name = a.artistName ?: "",
imageUrl = a.artworkUrl100?.replace("100x100bb", "300x300bb"),
) { vm.openArtistDetail(a) }
} }
} }
Spacer(Modifier.height(8.dp))
} }
} }
if (state.albums.isNotEmpty()) { if (state.albums.isNotEmpty()) {
@@ -585,17 +590,28 @@ private fun ItunesTrackRow(
} }
@Composable @Composable
private fun SmallArtistChip(name: String, onClick: () -> Unit) { private fun ArtistCard(name: String, imageUrl: String?, onClick: () -> Unit) {
Box( Column(
contentAlignment = Alignment.Center, horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier modifier = Modifier
.clip(RoundedCornerShape(20.dp)) .width(80.dp)
.background(NsSurface2) .clickable(onClick = onClick),
.clickable(onClick = onClick)
.padding(horizontal = 14.dp, vertical = 8.dp),
) { ) {
Text(name, fontFamily = Outfit, fontSize = 13.sp, color = NsOnSurface, AsyncImage(
maxLines = 1, overflow = TextOverflow.Ellipsis) model = imageUrl,
contentDescription = name,
contentScale = ContentScale.Crop,
modifier = Modifier
.size(72.dp)
.clip(androidx.compose.foundation.shape.CircleShape)
.background(NsSurface2),
)
Spacer(Modifier.height(6.dp))
Text(
name, fontFamily = Outfit, fontSize = 12.sp, fontWeight = FontWeight.Medium,
color = NsOnSurface, maxLines = 2, overflow = TextOverflow.Ellipsis,
textAlign = androidx.compose.ui.text.style.TextAlign.Center,
)
} }
} }