address the review: source title after a queue reorder, scrim timing, thumbnail painter

- derive the now playing fallback title from the current item for every kind except the songs list, so a track reordered into the album queue (now a Song entry) still shows its album
- reset the resolved title to the fallback when the queue context changes, so crossing albums does not briefly show the previous name
- start the now playing scrim fade later (0.85) so it darkens the sheet, not the library, while the sheet is still sliding up
- reuse the thumbnail's onSuccess painter ratio instead of loading a second thumbnail painter for the aspect ratio
- drop the now-unneeded ExperimentalLayoutApi opt-in on AddSourceScreen (imePadding is stable)
This commit is contained in:
austinried
2026-10-04 00:29:58 +00:00
parent 2e925f2f78
commit 216990181e
4 changed files with 20 additions and 11 deletions
@@ -94,7 +94,7 @@ private const val OVERLAY_DURATION_MS = 200
private const val EXPAND_FADE = 0.1f
private const val FLING_VELOCITY = 1000f
private const val MINI_PLAYER_ANIM_MS = 200
private const val SCRIM_FADE_START = 0.6f
private const val SCRIM_FADE_START = 0.85f
private object Routes {
const val LIBRARY = "library"
@@ -49,6 +49,7 @@ fun CoverArt(
val context = LocalPlatformContext.current
var failed by remember(ref, thumbnailRef) { mutableStateOf(false) }
var thumbnailLoaded by remember(ref, thumbnailRef) { mutableStateOf(false) }
var thumbnailRatio by remember(ref, thumbnailRef) { mutableStateOf<Float?>(null) }
val model = remember(ref, thumbnailRef) { ref?.let { imageRequest(context, it, crossfade = thumbnailRef != null) } }
val frameModifier =
Modifier
@@ -65,6 +66,7 @@ fun CoverArt(
failed = failed,
thumbnailLoaded = thumbnailLoaded,
onThumbnailLoaded = { thumbnailLoaded = true },
onThumbnailRatio = { thumbnailRatio = it },
contentScale = ContentScale.Crop,
) {
if (ref != null) {
@@ -85,15 +87,10 @@ fun CoverArt(
onError = { failed = true },
contentScale = ContentScale.Fit,
)
val thumbnailPainter =
rememberAsyncImagePainter(
model = remember(thumbnailRef) { thumbnailRef?.let { imageRequest(context, it, crossfade = false) } },
contentScale = ContentScale.Fit,
)
BoxWithConstraints(modifier, contentAlignment = Alignment.Center) {
val ratio =
painter.intrinsicSize.ratioOrNull()
?: thumbnailPainter.intrinsicSize.ratioOrNull()
?: thumbnailRatio
?: 1f
val width = if (ratio > maxWidth.value / maxHeight.value) maxWidth else maxHeight * ratio
Box(Modifier.size(width, width / ratio).then(frameModifier)) {
@@ -105,6 +102,7 @@ fun CoverArt(
failed = failed,
thumbnailLoaded = thumbnailLoaded,
onThumbnailLoaded = { thumbnailLoaded = true },
onThumbnailRatio = { thumbnailRatio = it },
contentScale = ContentScale.Fit,
) {
if (ref != null) {
@@ -130,6 +128,7 @@ private fun BoxScope.CoverArtContent(
failed: Boolean,
thumbnailLoaded: Boolean,
onThumbnailLoaded: () -> Unit,
onThumbnailRatio: (Float) -> Unit,
contentScale: ContentScale,
main: @Composable () -> Unit,
) {
@@ -147,7 +146,12 @@ private fun BoxScope.CoverArtContent(
model = remember(thumbnailRef) { imageRequest(context, thumbnailRef, crossfade = false) },
contentDescription = null,
contentScale = contentScale,
onSuccess = { onThumbnailLoaded() },
onSuccess = { success ->
onThumbnailLoaded()
success.painter.intrinsicSize
.ratioOrNull()
?.let(onThumbnailRatio)
},
modifier = Modifier.fillMaxSize(),
)
}
@@ -83,8 +83,14 @@ fun NowPlayingRoute(
val art = controller.coverArt(state.item)
val thumbnail = controller.coverArt(state.item, thumbnail = true)
val queueContext = state.context
val fallbackTitle = if (queueContext?.kind == QueueKind.Album) state.item?.album.orEmpty() else "Library"
val fallbackTitle =
if (queueContext?.kind == QueueKind.Songs) {
"Library"
} else {
state.item?.album?.takeIf { it.isNotBlank() } ?: "Library"
}
val sourceTitle by produceState<String>(initialValue = fallbackTitle, queueContext) {
value = fallbackTitle
value = controller.sourceTitle(queueContext) ?: fallbackTitle
}
LaunchedEffect(state.item?.id, state.hasNext) {
@@ -2,7 +2,6 @@ package com.subtracks.ui.settings
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -58,7 +57,7 @@ fun AddSourceRoute(
)
}
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AddSourceScreen(
state: AddSourceState,