Merge p11-w1-playalbum-seam into dev (P11 W1: wire CutDetail Play to IQueueService, §3.4 seam closed)

This commit is contained in:
daniel-c-harvey
2026-06-16 10:26:57 -04:00
+25 -15
View File
@@ -69,10 +69,8 @@ else
} }
<div class="cut-detail-actions"> <div class="cut-detail-actions">
@* Header Play starts the album's first track. Wired to the single-slot player @* Header Play loads the full album into the queue at index 0 (§3.4 seam,
today; the §3.4 queue seam means a future swap to QueueService.PlayRelease closed P11 W1). Disabled until at least one streamable track is resolved. *@
is a one-line change inside PlayAlbum, not a markup edit. Disabled until a
streamable track is resolved. *@
<MudButton Variant="Variant.Filled" <MudButton Variant="Variant.Filled"
Color="Color.Secondary" Color="Color.Secondary"
StartIcon="@Icons.Material.Filled.PlayArrow" StartIcon="@Icons.Material.Filled.PlayArrow"
@@ -112,15 +110,17 @@ else
else else
{ {
<div class="cut-detail-tracklist"> <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"> <div class="cut-detail-track-row">
<span class="cut-detail-track-number">@track.TrackNumber</span> <span class="cut-detail-track-number">@track.TrackNumber</span>
<div class="cut-detail-track-play"> <div class="cut-detail-track-play">
<PlayStateIcon Track="@track" <PlayStateIcon Track="@track"
Size="Size.Medium" Size="Size.Medium"
Color="Color.Secondary" Color="Color.Secondary"
OnToggle="@(() => PlayTrack(track))" /> OnToggle="@(() => PlayTrack(track, index))" />
</div> </div>
<span class="cut-detail-track-name text-truncate">@track.TrackName</span> <span class="cut-detail-track-name text-truncate">@track.TrackName</span>
</div> </div>
@@ -133,20 +133,23 @@ else
@code { @code {
[CascadingParameter] public IStreamingPlayerService? PlayerService { get; set; } [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 // Header Play: load the full album into the queue starting at track 0.
// 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.
private Task PlayAlbum() private Task PlayAlbum()
{ {
var first = ViewModel.Tracks.Count > 0 ? ViewModel.Tracks[0] : null; if (ViewModel.Tracks.Count == 0) return Task.CompletedTask;
return first is null ? Task.CompletedTask : PlayTrack(first); 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 // Row play: toggle if this track is already playing/paused, otherwise load the album at this
// scaffold's own PlayTrack wiring (SessionDetail uses the same idiom for its diverged layout). // row's index so playback continues to the end from the chosen track.
private async Task PlayTrack(TrackDto track) private async Task PlayTrack(TrackDto track, int index)
{ {
if (PlayerService is null) return; if (PlayerService is null) return;
@@ -154,9 +157,16 @@ else
if (isThisTrack && (PlayerService.IsPlaying || PlayerService.IsPaused)) if (isThisTrack && (PlayerService.IsPlaying || PlayerService.IsPaused))
{ {
await PlayerService.TogglePlayPause(); await PlayerService.TogglePlayPause();
return;
}
if (Queue is not null)
{
await Queue.PlayRelease(ViewModel.Tracks, index);
} }
else else
{ {
// Queue cascade absent: fall back to direct single-track play.
await PlayerService.SelectTrackStreaming(track); await PlayerService.SelectTrackStreaming(track);
} }
} }