Wire NowPlayingStats to live aggregates: add SQL track duration column, stats endpoint, and duration backfill
This commit is contained in:
@@ -1,14 +1,94 @@
|
||||
@using DeepDrftModels.DTOs
|
||||
@using DeepDrftModels.Enums
|
||||
@using DeepDrftPublic.Client.Helpers
|
||||
@using DeepDrftPublic.Client.Services
|
||||
@implements IDisposable
|
||||
|
||||
<div class="hero-stat-row">
|
||||
@* Studio Cuts — primary figure is the total Cut-medium track count; the secondary breakdown lists
|
||||
per-ReleaseType Cut release counts, zero-count types already suppressed server-side. *@
|
||||
<div class="hero-stat">
|
||||
<div class="hero-stat-num">47+</div>
|
||||
<div class="hero-stat-label">Live Sessions</div>
|
||||
<div class="hero-stat-num">@_stats.CutTrackCount</div>
|
||||
<div class="hero-stat-label">Studio Cuts</div>
|
||||
@if (_stats.CutReleaseTypeCounts.Count > 0)
|
||||
{
|
||||
<div class="hero-stat-breakdown">
|
||||
@foreach (var row in _stats.CutReleaseTypeCounts)
|
||||
{
|
||||
<span class="hero-stat-breakdown-item">@row.Count @PluralizeReleaseType(row.ReleaseType, row.Count)</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@* Mixes — primary figure is the Mix release count labelled "Sets"; the secondary figure is total
|
||||
mix runtime as hh:mm. *@
|
||||
<div class="hero-stat">
|
||||
<div class="hero-stat-num">2</div>
|
||||
<div class="hero-stat-label">Members</div>
|
||||
<div class="hero-stat-num">@_stats.MixReleaseCount</div>
|
||||
<div class="hero-stat-label">Sets</div>
|
||||
<div class="hero-stat-sub">@RuntimeFormat.ToHoursMinutes(_stats.MixRuntimeSeconds) runtime</div>
|
||||
</div>
|
||||
|
||||
@* Plays — static placeholder (real play/share tracking is a future phase). Odometer treatment over
|
||||
the existing card style; copy is placeholder pending sign-off. *@
|
||||
<div class="hero-stat">
|
||||
<div class="hero-stat-num">∞</div>
|
||||
<div class="hero-stat-label">Drift Points</div>
|
||||
<div class="hero-stat-num hero-stat-odometer">XXX</div>
|
||||
<div class="hero-stat-label">Plays (Coming Soon)</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Inject] public required IStatsDataService StatsData { get; set; }
|
||||
[Inject] public required PersistentComponentState PersistentState { get; set; }
|
||||
|
||||
private const string PersistKey = "home-stats";
|
||||
|
||||
private HomeStatsDto _stats = new();
|
||||
private bool _loaded;
|
||||
private PersistingComponentStateSubscription _persistingSubscription;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_persistingSubscription = PersistentState.RegisterOnPersisting(Persist);
|
||||
|
||||
// Bridge the prerendered fetch across the prerender -> WASM seam so the WASM boot does not
|
||||
// re-fetch and flicker the figures (the TracksView persistent-state seam, applied to stats).
|
||||
if (PersistentState.TryTakeFromJson<HomeStatsDto>(PersistKey, out var restored) && restored is not null)
|
||||
{
|
||||
_stats = restored;
|
||||
_loaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await StatsData.GetHomeStats();
|
||||
if (result is { Success: true, Value: { } stats })
|
||||
{
|
||||
_stats = stats;
|
||||
_loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Only bridge a successful fetch. If prerender failed, persist nothing so the WASM pass re-fetches
|
||||
// rather than restoring zeros — mirrors the guard on the medium-browse persist path.
|
||||
private Task Persist()
|
||||
{
|
||||
if (_loaded)
|
||||
PersistentState.PersistAsJson(PersistKey, _stats);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private static string PluralizeReleaseType(ReleaseType type, int count)
|
||||
{
|
||||
var label = type switch
|
||||
{
|
||||
ReleaseType.Single => "Single",
|
||||
ReleaseType.EP => "EP",
|
||||
ReleaseType.Album => "Album",
|
||||
_ => type.ToString()
|
||||
};
|
||||
// EP pluralizes as "EPs"; Single/Album take a plain trailing s.
|
||||
return count == 1 ? label : label + "s";
|
||||
}
|
||||
|
||||
public void Dispose() => _persistingSubscription.Dispose();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user