feat(android): TrackActions kebab on NowPlaying with hideQueueActions
NowPlaying gains the 7-item TrackActions menu in its bottom action row alongside the View Queue button. hideQueueActions=true suppresses Play next / Add to queue since the menu's track IS the playing one. Go to album / artist pops NowPlaying first (it's a full-screen overlay) before navigating, mirroring Flutter's shell-route hook. Adds a thin Scaffold around the screen body so the TrackActions transient messages have a SnackbarHost to surface in. MiniPlayer kebab still deferred — needs shell-level snackbar plumbing that lives outside this slice's scope. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,11 +17,16 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.SliderDefaults
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
@@ -40,8 +45,12 @@ import com.composables.icons.lucide.Pause
|
||||
import com.composables.icons.lucide.Play
|
||||
import com.composables.icons.lucide.SkipBack
|
||||
import com.composables.icons.lucide.SkipForward
|
||||
import com.fabledsword.minstrel.nav.AlbumDetail
|
||||
import com.fabledsword.minstrel.nav.ArtistDetail
|
||||
import com.fabledsword.minstrel.nav.Queue
|
||||
import com.fabledsword.minstrel.shared.widgets.EmptyState
|
||||
import com.fabledsword.minstrel.shared.widgets.track_actions.TrackActionsButton
|
||||
import com.fabledsword.minstrel.shared.widgets.track_actions.TrackActionsViewModel
|
||||
import com.fabledsword.minstrel.theme.FabledSwordFlatTokens
|
||||
import com.fabledsword.minstrel.theme.LocalActionColors
|
||||
|
||||
@@ -61,8 +70,15 @@ private const val SECONDS_PER_MINUTE = 60L
|
||||
fun NowPlayingScreen(
|
||||
navController: NavHostController,
|
||||
viewModel: PlayerViewModel = hiltViewModel(),
|
||||
trackActionsViewModel: TrackActionsViewModel = hiltViewModel(),
|
||||
) {
|
||||
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
LaunchedEffect(Unit) {
|
||||
trackActionsViewModel.transientMessages.collect { msg ->
|
||||
snackbarHostState.showSnackbar(msg)
|
||||
}
|
||||
}
|
||||
val track = state.currentTrack
|
||||
if (track == null) {
|
||||
EmptyState(
|
||||
@@ -71,35 +87,59 @@ fun NowPlayingScreen(
|
||||
)
|
||||
return
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 24.dp),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
NowPlayingCover(coverUrl = track.coverUrl, contentDescription = track.title)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
TrackHeader(
|
||||
title = track.title,
|
||||
artist = track.artistName,
|
||||
album = track.albumTitle,
|
||||
)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
ScrubberRow(
|
||||
positionMs = state.positionMs,
|
||||
durationMs = state.durationMs,
|
||||
onSeek = viewModel::seekTo,
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
TransportRow(
|
||||
isPlaying = state.isPlaying,
|
||||
onPrev = viewModel::skipToPrevious,
|
||||
onPlayPause = { if (state.isPlaying) viewModel.pause() else viewModel.play() },
|
||||
onNext = viewModel::skipToNext,
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
ViewQueueButton(onClick = { navController.navigate(Queue) })
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||
containerColor = MaterialTheme.colorScheme.background,
|
||||
) { inner ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(inner)
|
||||
.padding(horizontal = 24.dp),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
NowPlayingCover(coverUrl = track.coverUrl, contentDescription = track.title)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
TrackHeader(
|
||||
title = track.title,
|
||||
artist = track.artistName,
|
||||
album = track.albumTitle,
|
||||
)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
ScrubberRow(
|
||||
positionMs = state.positionMs,
|
||||
durationMs = state.durationMs,
|
||||
onSeek = viewModel::seekTo,
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
TransportRow(
|
||||
isPlaying = state.isPlaying,
|
||||
onPrev = viewModel::skipToPrevious,
|
||||
onPlayPause = { if (state.isPlaying) viewModel.pause() else viewModel.play() },
|
||||
onNext = viewModel::skipToNext,
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
ViewQueueButton(onClick = { navController.navigate(Queue) })
|
||||
TrackActionsButton(
|
||||
track = track,
|
||||
hideQueueActions = true,
|
||||
onNavigateToAlbum = { id ->
|
||||
navController.popBackStack()
|
||||
navController.navigate(AlbumDetail(id))
|
||||
},
|
||||
onNavigateToArtist = { id ->
|
||||
navController.popBackStack()
|
||||
navController.navigate(ArtistDetail(id))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user