feat(cuts): wire PlayAlbum/PlayTrack to IQueueService.PlayRelease (§3.4 seam, P11 W1)

Header Play loads full album at index 0; row play loads at that row's index with same-track
toggle preserved; null-safe cascade fallback to direct SelectTrackStreaming when queue absent.
This commit is contained in:
daniel-c-harvey
2026-06-16 10:22:59 -04:00
parent 364450885b
commit 6ac943ca09
+25 -15
View File
@@ -69,10 +69,8 @@ else
}
<div class="cut-detail-actions">
@* Header Play starts the album's first track. Wired to the single-slot player
today; the §3.4 queue seam means a future swap to QueueService.PlayRelease
is a one-line change inside PlayAlbum, not a markup edit. Disabled until a
streamable track is resolved. *@
@* Header Play loads the full album into the queue at index 0 (§3.4 seam,
closed P11 W1). Disabled until at least one streamable track is resolved. *@
<MudButton Variant="Variant.Filled"
Color="Color.Secondary"
StartIcon="@Icons.Material.Filled.PlayArrow"
@@ -112,15 +110,17 @@ else
else
{
<div class="cut-detail-tracklist">
@foreach (var track in ViewModel.Tracks)
@for (var i = 0; i < ViewModel.Tracks.Count; i++)
{
var track = ViewModel.Tracks[i];
var index = i;
<div class="cut-detail-track-row">
<span class="cut-detail-track-number">@track.TrackNumber</span>
<div class="cut-detail-track-play">
<PlayStateIcon Track="@track"
Size="Size.Medium"
Color="Color.Secondary"
OnToggle="@(() => PlayTrack(track))" />
OnToggle="@(() => PlayTrack(track, index))" />
</div>
<span class="cut-detail-track-name text-truncate">@track.TrackName</span>
</div>
@@ -133,20 +133,23 @@ else
@code {
[CascadingParameter] public IStreamingPlayerService? PlayerService { get; set; }
[CascadingParameter] public IQueueService? Queue { get; set; }
// Header Play: start the album's first track. The §3.4 queue seam lives here — swapping this body
// to `Queue.PlayRelease(ViewModel.Tracks)` once IQueueService (track 11.F) lands is a one-line
// change with no other edit to this page. The queue type is not referenced here because it does
// not exist in this worktree.
// Header Play: load the full album into the queue starting at track 0.
private Task PlayAlbum()
{
var first = ViewModel.Tracks.Count > 0 ? ViewModel.Tracks[0] : null;
return first is null ? Task.CompletedTask : PlayTrack(first);
if (ViewModel.Tracks.Count == 0) return Task.CompletedTask;
if (Queue is not null) return Queue.PlayRelease(ViewModel.Tracks, 0);
// Queue cascade absent (prerender or non-interactive): fall back to direct single-track play.
return PlayerService is not null
? PlayerService.SelectTrackStreaming(ViewModel.Tracks[0])
: Task.CompletedTask;
}
// Row play: toggle if this track is already active, otherwise start a fresh stream. Mirrors the
// scaffold's own PlayTrack wiring (SessionDetail uses the same idiom for its diverged layout).
private async Task PlayTrack(TrackDto track)
// Row play: toggle if this track is already playing/paused, otherwise load the album at this
// row's index so playback continues to the end from the chosen track.
private async Task PlayTrack(TrackDto track, int index)
{
if (PlayerService is null) return;
@@ -154,9 +157,16 @@ else
if (isThisTrack && (PlayerService.IsPlaying || PlayerService.IsPaused))
{
await PlayerService.TogglePlayPause();
return;
}
if (Queue is not null)
{
await Queue.PlayRelease(ViewModel.Tracks, index);
}
else
{
// Queue cascade absent: fall back to direct single-track play.
await PlayerService.SelectTrackStreaming(track);
}
}