71 lines
2.9 KiB
C#
71 lines
2.9 KiB
C#
using DeepDrftModels.DTOs;
|
|
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; }
|
|
|
|
private PersistingComponentStateSubscription _persistingSubscription;
|
|
|
|
// The entry key the ViewModel currently holds. Tracks param-only navigations
|
|
// (e.g. /track/A -> /track/B) which reuse this component instance and fire
|
|
// OnParametersSet without re-running OnInitialized — without this, the page keeps
|
|
// the prior track and Play streams the wrong audio.
|
|
private string? _loadedEntryKey;
|
|
|
|
protected override void OnInitialized()
|
|
// 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);
|
|
|
|
protected override async Task OnParametersSetAsync()
|
|
{
|
|
// Re-run whenever the route key changes. Component instances are reused across
|
|
// same-template navigations, so the load decision must live here, not in
|
|
// OnInitialized (which fires once per instance).
|
|
if (_loadedEntryKey == EntryKey) return;
|
|
|
|
// Capture the key synchronously before any await so that a re-entrant call
|
|
// (rapid navigation or a re-render that changes EntryKey while Load is in flight)
|
|
// sees the correct guard state. Without this, a second OnParametersSetAsync
|
|
// for the same EntryKey would bypass the guard above and start a second Load,
|
|
// causing two ViewModel.Load calls to race on the single scoped instance.
|
|
_loadedEntryKey = EntryKey;
|
|
|
|
// Guard the bridge on the key: a payload for a different track must not seed this
|
|
// page (stale-bridge bleed across navigation).
|
|
if (PersistentState.TryTakeFromJson<TrackDto>(PersistKey, out var restored)
|
|
&& restored is not null
|
|
&& restored.EntryKey == EntryKey)
|
|
{
|
|
ViewModel.Track = restored;
|
|
ViewModel.NotFound = false;
|
|
ViewModel.IsLoading = false;
|
|
}
|
|
else
|
|
{
|
|
await ViewModel.Load(EntryKey);
|
|
}
|
|
}
|
|
|
|
private Task PersistTrack()
|
|
{
|
|
if (ViewModel.Track is not null)
|
|
{
|
|
PersistentState.PersistAsJson(PersistKey, ViewModel.Track);
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public void Dispose() => _persistingSubscription.Dispose();
|
|
}
|