feat(ui): mini-player buffering pose + one-tap retry on error
- Equalizer chip dims while BUFFERING/INITIALIZING (no false 'playing' signal during reconnect) - On ERROR the bar's subtitle becomes 'Couldn't reach · Retry' — tapping replays the current station without leaving the tab - NowPlayingViewModel.retry() re-plays current URL
This commit is contained in:
parent
918aeaf8d3
commit
e2ed74a47d
2 changed files with 42 additions and 2 deletions
|
|
@ -65,6 +65,10 @@ fun NowPlayingBar(
|
||||||
val isLocal by viewModel.isLocal.collectAsState()
|
val isLocal by viewModel.isLocal.collectAsState()
|
||||||
val streamMeta by viewModel.streamMeta.collectAsState()
|
val streamMeta by viewModel.streamMeta.collectAsState()
|
||||||
val localMedia by viewModel.localMedia.collectAsState()
|
val localMedia by viewModel.localMedia.collectAsState()
|
||||||
|
val isError = bufferState == PlaybackManager.BufferState.ERROR
|
||||||
|
val isBuffering =
|
||||||
|
bufferState == PlaybackManager.BufferState.BUFFERING ||
|
||||||
|
bufferState == PlaybackManager.BufferState.INITIALIZING
|
||||||
// Swipe-down (or quick drag-down) dismisses the bar until a *new*
|
// Swipe-down (or quick drag-down) dismisses the bar until a *new*
|
||||||
// source starts playing — a floating thing should answer to gestures.
|
// source starts playing — a floating thing should answer to gestures.
|
||||||
var dismissedFor by remember { mutableStateOf<String?>(null) }
|
var dismissedFor by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
@ -145,13 +149,22 @@ fun NowPlayingBar(
|
||||||
.background(LocalTokens.current.accentFill),
|
.background(LocalTokens.current.accentFill),
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
if (isPlaying) {
|
if (isPlaying && !isBuffering) {
|
||||||
EqualizerBars(
|
EqualizerBars(
|
||||||
active = true,
|
active = true,
|
||||||
color = LocalTokens.current.accentOnFill,
|
color = LocalTokens.current.accentOnFill,
|
||||||
barWidth = 3.dp,
|
barWidth = 3.dp,
|
||||||
modifier = Modifier.size(22.dp),
|
modifier = Modifier.size(22.dp),
|
||||||
)
|
)
|
||||||
|
} else if (isBuffering && !isError) {
|
||||||
|
EqualizerBars(
|
||||||
|
active = false,
|
||||||
|
color = LocalTokens.current.accentOnFill.copy(alpha = 0.7f),
|
||||||
|
barWidth = 3.dp,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(22.dp)
|
||||||
|
.padding(2.dp),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
Icon(
|
Icon(
|
||||||
if (isLocal) Icons.Default.MusicNote else Icons.Default.Radio,
|
if (isLocal) Icons.Default.MusicNote else Icons.Default.Radio,
|
||||||
|
|
@ -186,7 +199,28 @@ fun NowPlayingBar(
|
||||||
color = LocalTokens.current.text,
|
color = LocalTokens.current.text,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (subtitle.isNotEmpty()) {
|
if (isError) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.clickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = null,
|
||||||
|
onClick = { viewModel.retry() },
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
"Couldn't reach · ",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
"Retry",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = tokens.accentText,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (subtitle.isNotEmpty()) {
|
||||||
AnimatedContent(
|
AnimatedContent(
|
||||||
targetState = subtitle,
|
targetState = subtitle,
|
||||||
transitionSpec = { metadataEnter(reduce) togetherWith metadataExit(reduce) },
|
transitionSpec = { metadataEnter(reduce) togetherWith metadataExit(reduce) },
|
||||||
|
|
|
||||||
|
|
@ -23,4 +23,10 @@ class NowPlayingViewModel @Inject constructor(
|
||||||
fun togglePlayPause() {
|
fun togglePlayPause() {
|
||||||
playbackManager.togglePlayPause()
|
playbackManager.togglePlayPause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Reconnect the current station (mini-player error state). */
|
||||||
|
fun retry() {
|
||||||
|
val url = playbackManager.currentStationUrl ?: return
|
||||||
|
playbackManager.playStation(url, playbackManager.currentStationName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue