From a37fe4c1671140e0b3f23a278a3e6bf14334cad7 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Thu, 28 May 2026 08:21:58 -0400 Subject: [PATCH] feat(android): ArtistDetail avatar falls back to first album cover (parity map) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ports flutter_client/lib/library/artist_detail_screen.dart:177-208 _ArtistAvatar: server coverUrl wins; when empty, use the first loaded album's /api/albums/{id}/cover; only show the bare Lucide.User icon when the artist has neither. Threads detail.albums.firstOrNull()?.id through ArtistHeader → ArtistAvatar. (Seeded-loading header passes null since the seed carries no album list.) Co-Authored-By: Claude Opus 4.7 (1M context) --- .../minstrel/library/ui/ArtistDetailScreen.kt | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/android/app/src/main/java/com/fabledsword/minstrel/library/ui/ArtistDetailScreen.kt b/android/app/src/main/java/com/fabledsword/minstrel/library/ui/ArtistDetailScreen.kt index 7f3fa839..4b14ce38 100644 --- a/android/app/src/main/java/com/fabledsword/minstrel/library/ui/ArtistDetailScreen.kt +++ b/android/app/src/main/java/com/fabledsword/minstrel/library/ui/ArtistDetailScreen.kt @@ -141,6 +141,7 @@ private fun ArtistBody( ArtistHeader( artist = detail.artist, artistLiked = artistLiked, + fallbackAlbumId = detail.albums.firstOrNull()?.id, onPlay = onPlay, onToggleLike = onToggleLike, ) @@ -161,6 +162,7 @@ private fun ArtistBody( private fun ArtistHeader( artist: ArtistRef, artistLiked: Boolean, + fallbackAlbumId: String?, onPlay: () -> Unit, onToggleLike: () -> Unit, ) { @@ -171,7 +173,7 @@ private fun ArtistHeader( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(12.dp), ) { - ArtistAvatar(artist) + ArtistAvatar(artist, fallbackAlbumId) Column(modifier = Modifier.weight(1f)) { Text( text = artist.name, @@ -198,7 +200,16 @@ private fun ArtistHeader( } @Composable -private fun ArtistAvatar(artist: ArtistRef) { +private fun ArtistAvatar(artist: ArtistRef, fallbackAlbumId: String? = null) { + // Server coverUrl wins; otherwise mirror the server's "use the + // first album's cover" rule via the loaded album list (Flutter + // _ArtistAvatar). Bare Lucide.User only when neither exists. + val model = when { + artist.coverUrl.isNotEmpty() -> artist.coverUrl + fallbackAlbumId != null -> + "http://placeholder.invalid/api/albums/$fallbackAlbumId/cover" + else -> null + } Box( modifier = Modifier .size(96.dp) @@ -206,7 +217,7 @@ private fun ArtistAvatar(artist: ArtistRef) { .background(MaterialTheme.colorScheme.surfaceVariant), contentAlignment = Alignment.Center, ) { - if (artist.coverUrl.isEmpty()) { + if (model == null) { Icon( imageVector = Lucide.User, contentDescription = null, @@ -215,7 +226,7 @@ private fun ArtistAvatar(artist: ArtistRef) { ) } else { AsyncImage( - model = artist.coverUrl, + model = model, contentDescription = artist.name, modifier = Modifier.fillMaxSize(), contentScale = ContentScale.Crop,