mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 23:21:59 +01:00
rework the artist detail header
- drop the passive star badge from the artists list - add an album count, star, more and placeholder bio under the artist image - move the more action down from the top bar into that row - put a shuffle button beside the artist name, which can now wrap to two lines - remove the now-unused StarredBadge component
This commit is contained in:
@@ -17,11 +17,8 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Star
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -304,22 +301,6 @@ fun FilteredEmptyState(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun StarredBadge(
|
||||
starred: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
size: Dp = 14.dp,
|
||||
) {
|
||||
if (starred) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Star,
|
||||
contentDescription = "Starred",
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = modifier.size(size),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Modifier.statusBarScrim(): Modifier {
|
||||
val top = with(LocalDensity.current) { WindowInsets.statusBars.getTop(this).toDp() }
|
||||
|
||||
@@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
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.WindowInsets
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
@@ -18,7 +19,9 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||
@@ -29,6 +32,9 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
|
||||
import androidx.compose.material.icons.rounded.MoreHoriz
|
||||
import androidx.compose.material.icons.rounded.Shuffle
|
||||
import androidx.compose.material.icons.rounded.Star
|
||||
import androidx.compose.material.icons.rounded.StarBorder
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
@@ -53,10 +59,12 @@ import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shadow
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.subtracks.data.model.Album
|
||||
@@ -140,6 +148,8 @@ fun ArtistDetailRoute(
|
||||
onAlbumClick = onAlbumClick,
|
||||
onAlbumLongClick = { contextMenuHost?.show(it, actions) },
|
||||
onMore = { artist?.let { contextMenuHost?.show(MenuTarget.Artist(it, artThumbnail ?: art), actions) } },
|
||||
starred = artist?.starred != null,
|
||||
onToggleStar = artist?.let { a -> { setStar(StarType.Artist, a.id, a.starred == null) } },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -156,6 +166,9 @@ fun ArtistDetailScreen(
|
||||
onAlbumClick: (Album) -> Unit,
|
||||
onAlbumLongClick: (MenuTarget) -> Unit = {},
|
||||
onMore: () -> Unit = {},
|
||||
starred: Boolean = false,
|
||||
onToggleStar: (() -> Unit)? = null,
|
||||
onShuffle: () -> Unit = {},
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val listState = rememberLazyGridState()
|
||||
@@ -230,41 +243,84 @@ fun ArtistDetailScreen(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
item(span = { GridItemSpan(maxLineSpan) }) {
|
||||
Box(
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.onSizeChanged { imageHeightPx = it.height.toFloat() }
|
||||
.padding(bottom = 12.dp),
|
||||
.onSizeChanged { imageHeightPx = it.height.toFloat() },
|
||||
) {
|
||||
CoverArt(
|
||||
ref = art,
|
||||
name = artist?.name.orEmpty(),
|
||||
thumbnailRef = artThumbnail,
|
||||
showPlaceholder = art == null,
|
||||
modifier = Modifier.fillMaxWidth().height(ART_HEIGHT),
|
||||
)
|
||||
if (nameScrimAlpha > 0f) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.fillMaxWidth()
|
||||
.height(ART_HEIGHT * 0.4f)
|
||||
.graphicsLayer { alpha = nameScrimAlpha }
|
||||
.background(
|
||||
Brush.verticalGradient(
|
||||
listOf(Color.Transparent, Color.Black.copy(alpha = 0.6f)),
|
||||
),
|
||||
).testTag(ARTIST_NAME_SCRIM_TAG),
|
||||
Box(Modifier.fillMaxWidth()) {
|
||||
CoverArt(
|
||||
ref = art,
|
||||
name = artist?.name.orEmpty(),
|
||||
thumbnailRef = artThumbnail,
|
||||
showPlaceholder = art == null,
|
||||
modifier = Modifier.fillMaxWidth().height(ART_HEIGHT),
|
||||
)
|
||||
if (nameScrimAlpha > 0f) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.fillMaxWidth()
|
||||
.height(ART_HEIGHT * 0.4f)
|
||||
.graphicsLayer { alpha = nameScrimAlpha }
|
||||
.background(
|
||||
Brush.verticalGradient(
|
||||
listOf(Color.Transparent, Color.Black.copy(alpha = 0.6f)),
|
||||
),
|
||||
).testTag(ARTIST_NAME_SCRIM_TAG),
|
||||
)
|
||||
}
|
||||
Box(Modifier.align(Alignment.BottomStart).fillMaxWidth()) {
|
||||
Text(
|
||||
text = artist?.name.orEmpty(),
|
||||
style = imageNameStyle,
|
||||
color = Color.White,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.fillMaxWidth().padding(TITLE_INSET),
|
||||
)
|
||||
IconButton(
|
||||
onClick = onShuffle,
|
||||
modifier = Modifier.align(Alignment.BottomEnd).padding(end = 8.dp, bottom = 8.dp),
|
||||
) {
|
||||
ShadowedIcon(Icons.Rounded.Shuffle, "Shuffle artist", size = 28.dp)
|
||||
}
|
||||
}
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(start = 16.dp, end = 8.dp, top = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = "${artist?.albumCount ?: 0} ${if (artist?.albumCount == 1L) "album" else "albums"}",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
IconButton(onClick = { onToggleStar?.invoke() }, enabled = onToggleStar != null) {
|
||||
Icon(
|
||||
imageVector = if (starred) Icons.Rounded.Star else Icons.Rounded.StarBorder,
|
||||
contentDescription = if (starred) "Unstar" else "Star",
|
||||
tint =
|
||||
if (starred) {
|
||||
MaterialTheme.colorScheme.primary
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSurfaceVariant
|
||||
},
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onMore) {
|
||||
Icon(Icons.Rounded.MoreHoriz, contentDescription = "More options")
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = artist?.name.orEmpty(),
|
||||
style = imageNameStyle,
|
||||
color = Color.White,
|
||||
maxLines = 1,
|
||||
text = "Artist biography coming soon.",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.align(Alignment.BottomStart).padding(TITLE_INSET),
|
||||
modifier = Modifier.fillMaxWidth().padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -336,11 +392,6 @@ fun ArtistDetailScreen(
|
||||
Icon(Icons.AutoMirrored.Rounded.ArrowBack, contentDescription = "Back")
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = onMore, modifier = Modifier.graphicsLayer { alpha = barFraction }) {
|
||||
Icon(Icons.Rounded.MoreHoriz, contentDescription = "More options")
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent),
|
||||
modifier =
|
||||
Modifier
|
||||
@@ -355,3 +406,25 @@ fun ArtistDetailScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShadowedIcon(
|
||||
imageVector: ImageVector,
|
||||
contentDescription: String,
|
||||
size: Dp = 24.dp,
|
||||
) {
|
||||
Box(Modifier.size(size)) {
|
||||
Icon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = null,
|
||||
tint = Color.Black.copy(alpha = 0.5f),
|
||||
modifier = Modifier.matchParentSize().offset(x = 0.5.dp, y = 1.5.dp),
|
||||
)
|
||||
Icon(
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription,
|
||||
tint = Color.White,
|
||||
modifier = Modifier.matchParentSize(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package com.subtracks.ui.library
|
||||
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
@@ -15,7 +13,6 @@ import androidx.compose.material3.ListItem
|
||||
import androidx.compose.material3.ListItemDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -33,7 +30,6 @@ import com.subtracks.ui.components.FilteredEmptyState
|
||||
import com.subtracks.ui.components.LoadingState
|
||||
import com.subtracks.ui.components.MenuTarget
|
||||
import com.subtracks.ui.components.ResetScrollOnChange
|
||||
import com.subtracks.ui.components.StarredBadge
|
||||
import com.subtracks.ui.components.rememberViewportFill
|
||||
import com.subtracks.ui.theme.PrefetchArtworkSeeds
|
||||
|
||||
@@ -79,18 +75,11 @@ fun ArtistsContent(
|
||||
PrefetchArtworkSeeds(coverArt(artist.coverArt, true))
|
||||
ListItem(
|
||||
headlineContent = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = artist.name,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f, fill = false),
|
||||
)
|
||||
StarredBadge(artist.starred != null)
|
||||
}
|
||||
Text(
|
||||
text = artist.name,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
},
|
||||
supportingContent = {
|
||||
Text(
|
||||
|
||||
Reference in New Issue
Block a user