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:
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,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user