95 lines
3.5 KiB
Plaintext
95 lines
3.5 KiB
Plaintext
@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">@_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">@_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 hero-stat-odometer">XXX</div>
|
|
<div class="hero-stat-label">Plays (Coming Soon)</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();
|
|
}
|