From 31084b09a49a35f2ca854616466772e84e73c77a Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Mon, 15 Jun 2026 11:26:21 -0400 Subject: [PATCH] fix(cms): stabilize _specialColumns allocation and refresh stale comments Allocate _specialColumns once in OnInitialized; update RowActions references to SpecialColumns in the medium browsers and base class. --- .../Pages/Tracks/CmsCutBrowser.razor | 4 ++-- .../Pages/Tracks/CmsMediumBrowserBase.cs | 6 +++--- .../Pages/Tracks/CmsMixBrowser.razor | 18 ++++++++++++------ .../Pages/Tracks/CmsSessionBrowser.razor | 18 ++++++++++++------ 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/DeepDrftManager/Components/Pages/Tracks/CmsCutBrowser.razor b/DeepDrftManager/Components/Pages/Tracks/CmsCutBrowser.razor index 4e6ced7..b2c23db 100644 --- a/DeepDrftManager/Components/Pages/Tracks/CmsCutBrowser.razor +++ b/DeepDrftManager/Components/Pages/Tracks/CmsCutBrowser.razor @@ -4,8 +4,8 @@ @* CUTS tab content (Phase 9 §8.A/§8.C): the rich CmsAlbumBrowser grid filtered to Cut releases, so the tab carries expand-tracks, delete, the Type chip, and per-row edit identically to the ALL tab — no - forked grid. Cuts have no medium-specific row action, so no RowActions slot is supplied; the grid - renders its shared edit/delete only. Embedded as tab content only; no standalone @page route. *@ + forked grid. Cuts have no medium-specific action, so no SpecialColumns are supplied; the grid renders + its shared edit/delete only. Embedded as tab content only; no standalone @page route. *@ diff --git a/DeepDrftManager/Components/Pages/Tracks/CmsMediumBrowserBase.cs b/DeepDrftManager/Components/Pages/Tracks/CmsMediumBrowserBase.cs index 72b15b0..0d93a73 100644 --- a/DeepDrftManager/Components/Pages/Tracks/CmsMediumBrowserBase.cs +++ b/DeepDrftManager/Components/Pages/Tracks/CmsMediumBrowserBase.cs @@ -13,7 +13,7 @@ namespace DeepDrftManager.Components.Pages.Tracks; /// it (§8.C parity — reuse, don't fork). This base owns the loading flag, the medium-filtered load, the /// per-release row projection, and a cover-thumbnail helper; subclasses supply the , /// an error noun, and their bespoke per-row action (Session hero upload, Mix waveform generate) via the -/// rich grid's RowActions slot, looking their action-state row up with . +/// rich grid's SpecialColumns column model, looking their action-state row up with . /// /// The subclass's row model wrapping a plus its /// medium-specific action state (upload/generate flags). The rich grid renders from the bare @@ -42,8 +42,8 @@ public abstract class CmsMediumBrowserBase : ComponentBase where TRow : cl // it never sees TRow. Rebuilt on every (re)load so the grid re-projects against a fresh reference. protected IReadOnlyList Releases { get; private set; } = Array.Empty(); - // release.Id → action-state row, so a RowActions fragment (which the grid hands a ReleaseDto) can - // recover its TRow. Rebuilt alongside Rows so a refresh never leaves a stale row behind. + // release.Id → action-state row, so a SpecialColumns cell delegate (which the grid hands a ReleaseDto) + // can recover its TRow. Rebuilt alongside Rows so a refresh never leaves a stale row behind. private Dictionary _rowsById = new(); protected override async Task OnInitializedAsync() => await LoadAsync(); diff --git a/DeepDrftManager/Components/Pages/Tracks/CmsMixBrowser.razor b/DeepDrftManager/Components/Pages/Tracks/CmsMixBrowser.razor index e2d89a6..d291411 100644 --- a/DeepDrftManager/Components/Pages/Tracks/CmsMixBrowser.razor +++ b/DeepDrftManager/Components/Pages/Tracks/CmsMixBrowser.razor @@ -8,9 +8,10 @@ @* Embedded as the MIXES tab content of the Release Archive (Phase 9 §8.A), and still routable at /tracks/mixes for direct-URL access. The grid is the rich CmsAlbumBrowser filtered to Mixes (§8.C parity: expand-tracks, delete, Type chip, per-row edit), with the Mix waveform generate supplied as - its medium-specific RowActions slot so that affordance survives the move off the thin table. When - embedded, the page chrome (title, container, the now-meaningless "Back to Release Archive" button) is - suppressed; the standalone route keeps it. The waveform affordance (9.5.E) is preserved in both. *@ + its medium-specific special-action column so that affordance survives the move off the thin table. + When embedded, the page chrome (title, container, the now-meaningless "Back to Release Archive" + button) is suppressed; the standalone route keeps it. The waveform affordance (9.5.E) is preserved + in both. *@ @if (Embedded) { @GridContent @@ -60,10 +61,15 @@ else OnReleasesChanged="ReloadAsync" SpecialColumns="_specialColumns" />; - private IReadOnlyList _specialColumns => new[] + // Allocated once per component instance in OnInitialized (field initializers cannot reference + // instance members, so initialization is deferred to the first lifecycle hook). + private IReadOnlyList _specialColumns = Array.Empty(); + + protected override void OnInitialized() { - new SpecialActionColumn("Waveform", WaveformCell) - }; + _specialColumns = new[] { new SpecialActionColumn("Waveform", WaveformCell) }; + base.OnInitialized(); + } // Per-row cell for the dedicated "Waveform" column: status icon plus generate/regenerate button with // progress. Recovers the typed MixRow via RowFor; skips rendering for a release not on the page. diff --git a/DeepDrftManager/Components/Pages/Tracks/CmsSessionBrowser.razor b/DeepDrftManager/Components/Pages/Tracks/CmsSessionBrowser.razor index 11e2d8d..7e56266 100644 --- a/DeepDrftManager/Components/Pages/Tracks/CmsSessionBrowser.razor +++ b/DeepDrftManager/Components/Pages/Tracks/CmsSessionBrowser.razor @@ -9,9 +9,10 @@ @* Embedded as the SESSIONS tab content of the Release Archive (Phase 9 §8.A), and still routable at /tracks/sessions for direct-URL access. The grid is the rich CmsAlbumBrowser filtered to Sessions (§8.C parity: expand-tracks, delete, Type chip, per-row edit), with the Session hero upload supplied - as its medium-specific RowActions slot so that affordance survives the move off the thin table. When - embedded, the page chrome (title, container, the now-meaningless "Back to Release Archive" button) is - suppressed; the standalone route keeps it. The hero affordance (9.5.E) is preserved in both contexts. *@ + as its medium-specific special-action column so that affordance survives the move off the thin table. + When embedded, the page chrome (title, container, the now-meaningless "Back to Release Archive" + button) is suppressed; the standalone route keeps it. The hero affordance (9.5.E) is preserved in + both contexts. *@ @if (Embedded) { @GridContent @@ -61,10 +62,15 @@ else OnReleasesChanged="ReloadAsync" SpecialColumns="_specialColumns" />; - private IReadOnlyList _specialColumns => new[] + // Allocated once per component instance in OnInitialized (field initializers cannot reference + // instance members, so initialization is deferred to the first lifecycle hook). + private IReadOnlyList _specialColumns = Array.Empty(); + + protected override void OnInitialized() { - new SpecialActionColumn("Hero", HeroCell) - }; + _specialColumns = new[] { new SpecialActionColumn("Hero", HeroCell) }; + base.OnInitialized(); + } // Per-row cell for the dedicated "Hero" column: thumbnail preview plus set/replace upload button with // progress. Recovers the typed SessionRow via RowFor; skips rendering for a release not on the page.