feat(release): front int PK with app-minted GUID EntryKey on the public addressing surface (P11 W5, 11.H)
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
@* Full-page scrolling Mix waveform background (Phase 9, 8.K). A windowed slice of the mix's loudness
|
||||
datum scrolls bottom-to-top, coupled to playback; a zoom slider controls the visible time-span (and
|
||||
so the apparent scroll speed, Guitar-Hero style). Strictly read-only: it self-fetches its datum from
|
||||
ReleaseId, takes playback as one-way input only, and never seeks or writes back. The rAF loop and all
|
||||
ReleaseEntryKey, takes playback as one-way input only, and never seeks or writes back. The rAF loop and all
|
||||
scroll/zoom/compositing math live in the MixVisualizer.ts interop module; this component is a thin
|
||||
bridge that feeds it datum + playback + zoom + theme. Deliberately NOT the player-bar peak-bar idiom. *@
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace DeepDrftPublic.Client.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// Full-page scrolling Mix waveform background. Standalone and reusable: give it a
|
||||
/// <see cref="ReleaseId"/> and it fetches its own loudness datum. The rendering itself — a windowed,
|
||||
/// <see cref="ReleaseEntryKey"/> and it fetches its own loudness datum. The rendering itself — a windowed,
|
||||
/// bottom-to-top, playback-coupled scroll with a glassy theme-aware gradient — lives in the
|
||||
/// MixVisualizer.ts interop module; this component is the bridge that feeds it datum, playback
|
||||
/// position, zoom, and theme, and owns the module lifecycle.
|
||||
@@ -36,8 +36,8 @@ public partial class MixWaveformVisualizer : ComponentBase, IAsyncDisposable
|
||||
// us, and OnAfterRender pushes fresh palette colours into the module.
|
||||
[CascadingParameter] public DarkModeSettings? DarkMode { get; set; }
|
||||
|
||||
/// <summary>The Mix release whose waveform datum to fetch and render.</summary>
|
||||
[Parameter] public required long ReleaseId { get; set; }
|
||||
/// <summary>The opaque public EntryKey of the Mix release whose waveform datum to fetch and render.</summary>
|
||||
[Parameter] public required string ReleaseEntryKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The id of this mix's playable track. Used to gate the cascaded player as the live source: we
|
||||
@@ -75,7 +75,7 @@ public partial class MixWaveformVisualizer : ComponentBase, IAsyncDisposable
|
||||
|
||||
private IStreamingPlayerService? _subscribedService;
|
||||
private WaveformProfileDto? _profile;
|
||||
private long? _loadedReleaseId;
|
||||
private string? _loadedReleaseKey;
|
||||
|
||||
// Whether we are subscribed to the shared control state's Changed event. The controls row (a
|
||||
// sibling component) mutates ControlState and raises Changed; we push the affected uniforms.
|
||||
@@ -114,21 +114,21 @@ public partial class MixWaveformVisualizer : ComponentBase, IAsyncDisposable
|
||||
_subscribedService.StateChanged -= OnPlayerStateChanged;
|
||||
PlayerService.StateChanged += OnPlayerStateChanged;
|
||||
_subscribedService = PlayerService;
|
||||
DebugLog($"subscribed to player StateChanged. ReleaseId={ReleaseId}, TrackId={TrackId?.ToString() ?? "null"}.");
|
||||
DebugLog($"subscribed to player StateChanged. ReleaseEntryKey={ReleaseEntryKey}, TrackId={TrackId?.ToString() ?? "null"}.");
|
||||
}
|
||||
else if (PlayerService is null)
|
||||
{
|
||||
DebugLog($"NO player cascade — playback will never couple. ReleaseId={ReleaseId}, TrackId={TrackId?.ToString() ?? "null"}.");
|
||||
DebugLog($"NO player cascade — playback will never couple. ReleaseEntryKey={ReleaseEntryKey}, TrackId={TrackId?.ToString() ?? "null"}.");
|
||||
}
|
||||
|
||||
// ReleaseId is the only fetch input; fetch once per id. Position/zoom/theme changes re-render
|
||||
// but must not refetch, and a release with no datum must not refetch either — so the guard
|
||||
// keys on the fetched id, not on whether a profile came back.
|
||||
if (_loadedReleaseId == ReleaseId) return;
|
||||
_loadedReleaseId = ReleaseId;
|
||||
// ReleaseEntryKey is the only fetch input; fetch once per key. Position/zoom/theme changes
|
||||
// re-render but must not refetch, and a release with no datum must not refetch either — so the
|
||||
// guard keys on the fetched key, not on whether a profile came back.
|
||||
if (_loadedReleaseKey == ReleaseEntryKey) return;
|
||||
_loadedReleaseKey = ReleaseEntryKey;
|
||||
|
||||
DebugLog($"fetching mix waveform datum for ReleaseId={ReleaseId}…");
|
||||
var result = await ReleaseData.GetMixWaveform(ReleaseId);
|
||||
DebugLog($"fetching mix waveform datum for ReleaseEntryKey={ReleaseEntryKey}…");
|
||||
var result = await ReleaseData.GetMixWaveform(ReleaseEntryKey);
|
||||
if (result is { Success: true, Value: { } profile } && profile.BucketCount > 0 && profile.Data.Length > 0)
|
||||
{
|
||||
_profile = profile;
|
||||
@@ -140,7 +140,7 @@ public partial class MixWaveformVisualizer : ComponentBase, IAsyncDisposable
|
||||
// renders its content over a plain background.
|
||||
_profile = null;
|
||||
DebugLog(result.Success
|
||||
? $"datum fetch returned EMPTY/absent (no stored datum for ReleaseId={ReleaseId}) — backdrop stays blank."
|
||||
? $"datum fetch returned EMPTY/absent (no stored datum for ReleaseEntryKey={ReleaseEntryKey}) — backdrop stays blank."
|
||||
: $"datum fetch FAILED ({result.GetMessage() ?? "unknown error"}) — backdrop stays blank.");
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
the parent supplies it one of two ways:
|
||||
- DetailRoute (the simple default): every card links /{DetailRoute}/{id} (Sessions, Mixes).
|
||||
- HrefResolver (per-card): each card links HrefResolver(release), so Archive routes each card by
|
||||
its own medium through the one ReleaseRoutes table, and Cuts routes to /cuts/{id}.
|
||||
its own medium through the one ReleaseRoutes table, and Cuts routes to /cuts/{entryKey}.
|
||||
HrefResolver wins when both are supplied. The card subtitle defaults to the artist; SubtitleResolver
|
||||
overrides it (Cuts shows a track count instead). Fully controlled by the parent: loading and item
|
||||
state are passed in. *@
|
||||
@@ -82,7 +82,7 @@
|
||||
/// <summary>
|
||||
/// Per-card href resolver. When supplied, a card links to its result instead of the
|
||||
/// <see cref="DetailRoute"/>-based href, letting Archive route each card by its own medium and
|
||||
/// Cuts route to /cuts/{id} (both via <c>ReleaseRoutes.DetailHref</c>).
|
||||
/// Cuts route to /cuts/{entryKey} (both via <c>ReleaseRoutes.DetailHref</c>).
|
||||
/// </summary>
|
||||
[Parameter] public Func<DeepDrftModels.DTOs.ReleaseDto, string>? HrefResolver { get; set; }
|
||||
|
||||
@@ -95,5 +95,5 @@
|
||||
[Parameter] public string EmptyMessage { get; set; } = "Nothing here yet";
|
||||
|
||||
private string CardHref(DeepDrftModels.DTOs.ReleaseDto release)
|
||||
=> HrefResolver?.Invoke(release) ?? $"/{DetailRoute}/{release.Id}";
|
||||
=> HrefResolver?.Invoke(release) ?? $"/{DetailRoute}/{release.EntryKey}";
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace DeepDrftPublic.Client.Controls;
|
||||
/// <summary>
|
||||
/// Share affordance with two modes from one source of clipboard/popover-chrome logic
|
||||
/// (Phase 11 §3b). Track mode (<see cref="EntryKey"/> set) offers a canonical-link copy plus an
|
||||
/// optional iframe embed snippet. Release mode (<see cref="ReleaseId"/> set) is copy-link-only —
|
||||
/// optional iframe embed snippet. Release mode (<see cref="ReleaseEntryKey"/> set) is copy-link-only —
|
||||
/// it copies the absolute form of the release's canonical detail URL and hides the embed
|
||||
/// affordance, since a release page is not a single-track embed. Clipboard writes go through
|
||||
/// navigator.clipboard; each copy shows a transient "Copied!" confirmation that resets after a
|
||||
@@ -19,8 +19,8 @@ public partial class SharePopover : ComponentBase, IDisposable
|
||||
/// <summary>Track mode: the vault entry key of the track to share. Mutually exclusive with the release target.</summary>
|
||||
[Parameter] public string? EntryKey { get; set; }
|
||||
|
||||
/// <summary>Release mode: the release id to share. When set (with <see cref="ReleaseMedium"/>), the popover shares the release detail URL and omits the embed option.</summary>
|
||||
[Parameter] public long? ReleaseId { get; set; }
|
||||
/// <summary>Release mode: the release's opaque public EntryKey to share. When set (with <see cref="ReleaseMedium"/>), the popover shares the release detail URL and omits the embed option.</summary>
|
||||
[Parameter] public string? ReleaseEntryKey { get; set; }
|
||||
|
||||
/// <summary>Release mode: the medium of the release, used to resolve its canonical detail route.</summary>
|
||||
[Parameter] public ReleaseMedium ReleaseMedium { get; set; }
|
||||
@@ -28,7 +28,7 @@ public partial class SharePopover : ComponentBase, IDisposable
|
||||
[Inject] public required NavigationManager Navigation { get; set; }
|
||||
[Inject] public required IJSRuntime JS { get; set; }
|
||||
|
||||
private bool IsReleaseMode => ReleaseId is not null;
|
||||
private bool IsReleaseMode => ReleaseEntryKey is not null;
|
||||
|
||||
private bool _open;
|
||||
private bool _embed;
|
||||
@@ -51,7 +51,7 @@ public partial class SharePopover : ComponentBase, IDisposable
|
||||
// route (which carries a leading slash) and composes it against BaseUri (which carries a
|
||||
// trailing slash) — trim one to avoid a doubled separator.
|
||||
private string LinkUrl => IsReleaseMode
|
||||
? $"{Navigation.BaseUri.TrimEnd('/')}{ReleaseRoutes.DetailHref(ReleaseId!.Value, ReleaseMedium)}"
|
||||
? $"{Navigation.BaseUri.TrimEnd('/')}{ReleaseRoutes.DetailHref(ReleaseEntryKey!, ReleaseMedium)}"
|
||||
: TrackUrl;
|
||||
|
||||
private string TrackUrl => $"{Navigation.BaseUri}track/{EntryKey}";
|
||||
|
||||
Reference in New Issue
Block a user