118 lines
4.0 KiB
C#
118 lines
4.0 KiB
C#
using DeepDrftModels.DTOs;
|
|
using DeepDrftPublic.Client.Services;
|
|
using DeepDrftPublic.Client.ViewModels;
|
|
using Microsoft.AspNetCore.Components;
|
|
using Models.Common;
|
|
|
|
namespace DeepDrftPublic.Client.Pages;
|
|
|
|
public partial class TracksView : ComponentBase, IDisposable
|
|
{
|
|
private const string PersistKey = "tracks-page";
|
|
|
|
[Inject] public required TracksViewModel ViewModel { get; set; }
|
|
[Inject] public required PersistentComponentState PersistentState { get; set; }
|
|
[CascadingParameter] public required IStreamingPlayerService PlayerService { get; set; }
|
|
|
|
private TrackDto? _selectedTrack = null;
|
|
private IStreamingPlayerService? _subscribedService;
|
|
private PersistingComponentStateSubscription _persistingSubscription;
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
// Carry the prerendered page across the prerender -> interactive (WASM) seam.
|
|
// Without this, the WASM pass gets a fresh scoped ViewModel (Page == null),
|
|
// re-renders the skeleton, re-fetches, and replaces the gallery DOM a few
|
|
// seconds in — replaying TrackCard entrance animations. Mirror the dark-mode
|
|
// PersistentComponentState bridge: persist on the way out of prerender,
|
|
// restore on the interactive pass, and only fetch on a miss.
|
|
_persistingSubscription = PersistentState.RegisterOnPersisting(PersistTracks);
|
|
|
|
if (PersistentState.TryTakeFromJson<PagedResult<TrackDto>>(PersistKey, out var restored) && restored is not null)
|
|
{
|
|
ViewModel.Page = restored;
|
|
ViewModel.PageNumber = restored.Page;
|
|
}
|
|
else
|
|
{
|
|
await SetPage(ViewModel.PageNumber);
|
|
}
|
|
}
|
|
|
|
protected override void OnParametersSet()
|
|
{
|
|
// The Stop/Close buttons on the player bar reset the player directly,
|
|
// bypassing PlayTrack — so the gallery's selection must follow player
|
|
// state rather than only its own clicks. Subscribe to the multicast
|
|
// side-channel (the cascade is IsFixed, so provider re-renders don't
|
|
// reach us) and clear the highlight when nothing is loaded.
|
|
if (PlayerService != null && !ReferenceEquals(PlayerService, _subscribedService))
|
|
{
|
|
if (_subscribedService != null)
|
|
_subscribedService.StateChanged -= OnPlayerStateChanged;
|
|
|
|
PlayerService.StateChanged += OnPlayerStateChanged;
|
|
_subscribedService = PlayerService;
|
|
}
|
|
}
|
|
|
|
private void OnPlayerStateChanged()
|
|
{
|
|
// Sync the gallery selection to the player. When the player is no longer
|
|
// loaded (stopped/closed/ended) drop the highlight; guard against a
|
|
// redundant re-render when nothing actually changed.
|
|
if (!PlayerService.IsLoaded && _selectedTrack != null)
|
|
{
|
|
_selectedTrack = null;
|
|
InvokeAsync(StateHasChanged);
|
|
}
|
|
}
|
|
|
|
private Task PersistTracks()
|
|
{
|
|
if (ViewModel.Page is not null)
|
|
{
|
|
PersistentState.PersistAsJson(PersistKey, ViewModel.Page);
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private async Task SetPage(int newPage)
|
|
{
|
|
var result = await ViewModel.TrackData.GetPage(newPage, ViewModel.PageSize, ViewModel.SortBy, ViewModel.IsDescending);
|
|
|
|
if (result is { Success: true, Value: PagedResult<TrackDto> pageResult })
|
|
{
|
|
ViewModel.Page = pageResult;
|
|
ViewModel.PageSize = pageResult.PageSize;
|
|
}
|
|
}
|
|
|
|
private async Task PlayTrack(TrackDto? track)
|
|
{
|
|
if (track == null && _selectedTrack == null || track?.Id == _selectedTrack?.Id) return;
|
|
|
|
if (track is null)
|
|
{
|
|
await PlayerService.Unload();
|
|
}
|
|
else
|
|
{
|
|
await PlayerService.SelectTrack(track);
|
|
}
|
|
|
|
_selectedTrack = track;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_persistingSubscription.Dispose();
|
|
|
|
if (_subscribedService != null)
|
|
{
|
|
_subscribedService.StateChanged -= OnPlayerStateChanged;
|
|
_subscribedService = null;
|
|
}
|
|
}
|
|
}
|