93 lines
3.3 KiB
C#
93 lines
3.3 KiB
C#
using DeepDrftModels.DTOs;
|
|
using DeepDrftPublic.Client.Services;
|
|
using DeepDrftPublic.Client.ViewModels;
|
|
using Microsoft.AspNetCore.Components;
|
|
|
|
namespace DeepDrftPublic.Client.Pages;
|
|
|
|
public partial class TrackDetail : ComponentBase, IDisposable
|
|
{
|
|
private const string PersistKey = "track-detail";
|
|
|
|
[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()
|
|
{
|
|
// Carry the prerendered track across the prerender -> interactive (WASM) seam.
|
|
// Without this, the WASM pass gets a fresh scoped ViewModel, re-renders the
|
|
// skeleton, and re-fetches. Mirror the TracksView bridge: persist on the way
|
|
// out of prerender, restore on the interactive pass, and only fetch on a miss.
|
|
_persistingSubscription = PersistentState.RegisterOnPersisting(PersistTrack);
|
|
|
|
if (PersistentState.TryTakeFromJson<TrackDto>(PersistKey, out var restored) && restored is not null)
|
|
{
|
|
ViewModel.Track = restored;
|
|
ViewModel.IsLoading = false;
|
|
}
|
|
else
|
|
{
|
|
await ViewModel.Load(EntryKey);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
PersistentState.PersistAsJson(PersistKey, ViewModel.Track);
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private async Task PlayTrack()
|
|
{
|
|
if (ViewModel.Track is null) return;
|
|
|
|
// Resume the current track if it's merely paused; otherwise stream the new selection.
|
|
// SelectTrackStreaming is the live entry point — the buffered SelectTrack path is dead.
|
|
if (PlayerService.CurrentTrack?.Id == ViewModel.Track.Id && PlayerService.IsPaused)
|
|
{
|
|
await PlayerService.TogglePlayPause();
|
|
}
|
|
else
|
|
{
|
|
await PlayerService.SelectTrackStreaming(ViewModel.Track);
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_persistingSubscription.Dispose();
|
|
|
|
if (_subscribedService != null)
|
|
{
|
|
_subscribedService.StateChanged -= OnPlayerStateChanged;
|
|
_subscribedService = null;
|
|
}
|
|
}
|
|
}
|