Phase 9 Wave 4: ARCHIVE nav + Cuts/Sessions/Mixes pages + MixWaveformVisualizer
Replaces flat RELEASES/SESSIONS/MIXES nav with ARCHIVE dropdown (PageRoute.Children,
one-level cap, dual-role node). Adds /archive overview, /cuts (AlbumsView + medium
filter; /albums redirects), /sessions + /sessions/{id} (hero-dominant), /mixes +
/mixes/{id} (MixWaveformVisualizer full-page background). Extracts ReleaseDetailScaffold
from TrackDetail (invariant trio). PersistentComponentState bridge on all new pages.
Click-to-seek seam designed on MixWaveformVisualizer (inert until wired).
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
using DeepDrftModels.DTOs;
|
||||
using DeepDrftPublic.Client.Services;
|
||||
using DeepDrftPublic.Client.ViewModels;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
@@ -12,9 +11,7 @@ public partial class TrackDetail : ComponentBase, IDisposable
|
||||
[Parameter] public required string EntryKey { get; set; }
|
||||
[Inject] public required TrackDetailViewModel ViewModel { get; set; }
|
||||
[Inject] public required PersistentComponentState PersistentState { get; set; }
|
||||
[CascadingParameter] public required IStreamingPlayerService PlayerService { get; set; }
|
||||
|
||||
private IStreamingPlayerService? _subscribedService;
|
||||
private PersistingComponentStateSubscription _persistingSubscription;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
@@ -36,24 +33,6 @@ public partial class TrackDetail : ComponentBase, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
// The play button's icon reads off the player's live state (CurrentTrack /
|
||||
// IsPlaying / IsPaused), which mutates outside this component's render path.
|
||||
// The cascade is IsFixed, so the provider's re-render never reaches us —
|
||||
// subscribe to the multicast side-channel and re-render on every state change.
|
||||
if (PlayerService != null && !ReferenceEquals(PlayerService, _subscribedService))
|
||||
{
|
||||
if (_subscribedService != null)
|
||||
_subscribedService.StateChanged -= OnPlayerStateChanged;
|
||||
|
||||
PlayerService.StateChanged += OnPlayerStateChanged;
|
||||
_subscribedService = PlayerService;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPlayerStateChanged() => InvokeAsync(StateHasChanged);
|
||||
|
||||
private Task PersistTrack()
|
||||
{
|
||||
if (ViewModel.Track is not null)
|
||||
@@ -63,33 +42,5 @@ public partial class TrackDetail : ComponentBase, IDisposable
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task PlayTrack()
|
||||
{
|
||||
if (ViewModel.Track is null) return;
|
||||
|
||||
var isThisTrack = PlayerService.CurrentTrack?.Id == ViewModel.Track.Id;
|
||||
|
||||
// Toggle play/pause if this track is already the active one (playing or paused);
|
||||
// otherwise start a fresh stream. SelectTrackStreaming is the live entry point —
|
||||
// the buffered SelectTrack path is dead.
|
||||
if (isThisTrack && (PlayerService.IsPlaying || PlayerService.IsPaused))
|
||||
{
|
||||
await PlayerService.TogglePlayPause();
|
||||
}
|
||||
else
|
||||
{
|
||||
await PlayerService.SelectTrackStreaming(ViewModel.Track);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_persistingSubscription.Dispose();
|
||||
|
||||
if (_subscribedService != null)
|
||||
{
|
||||
_subscribedService.StateChanged -= OnPlayerStateChanged;
|
||||
_subscribedService = null;
|
||||
}
|
||||
}
|
||||
public void Dispose() => _persistingSubscription.Dispose();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user