docs: record 11.D (Archive URL filters + GenresView repoint) landed (P11 W4)
This commit is contained in:
@@ -8,6 +8,16 @@ Newest entries at the top. Group by phase/wave header (mirroring `PLAN.md` / `CM
|
||||
|
||||
## Phase 11 — Public Site Enhancements
|
||||
|
||||
### 11.D — Archive filters in the URL
|
||||
|
||||
**Landed:** 2026-06-16 on dev.
|
||||
|
||||
- **What:** `ArchiveView` filter state (`q`, `medium`, `genre`) is now URL-bound via `[SupplyParameterFromQuery]`, making every filtered archive view a shareable, bookmarkable address (`/archive?q=&medium=&genre=`). Filter handlers navigate only; the seed-and-fetch reaction moved to `OnParametersSetAsync` (history-driven, §5.3 Option A). A `_loadedFilterKey` idempotency guard composed from the three-axis filter triple makes same-route query changes (debounce/chip-nav races, back/forward history) a no-op when the filter set is unchanged. The `HasActiveFilter` prerender-persistence gate is preserved: a filtered direct-load fetches its own narrowed result; a plain `/archive` visit restores the bridged first page. `medium` is parsed leniently with `Enum.TryParse(ignoreCase: true)` + `Enum.IsDefined` so a stray token degrades to All. Folded-in cleanup: `GenresView` genre-tile click was repointed from the deleted `/tracks?genre=` route to `/archive?genre=`, closing the 11.C dead-link residual — no `/tracks?genre=` references remain in the codebase.
|
||||
- **Why:** Archive filters were held in component fields with no URL representation, so a filtered view had no shareable address and the browser's back button did not restore the previous filter state. URL-binding makes the filter model consistent with the TracksView `[SupplyParameterFromQuery]` pattern already in the codebase and is a prerequisite for 11.H (which re-types the addressing surface 11.D defines).
|
||||
- **Shape:** `ArchiveView.razor.cs` (`DeepDrftPublic.Client/Pages/`): added three `[SupplyParameterFromQuery]` properties (`QueryParam`, `MediumParam`, `GenreParam`); added `_loadedFilterKey` string field + `ComposeFilterKey()` method; moved the seed-and-fetch reaction from `OnInitializedAsync` to `OnParametersSetAsync` with the idempotency guard; filter handlers (`OnSearchInput`, `OnMediumSelected`, `OnGenreSelected`) rewritten to call `NavigateToFilter` (navigate-only). `SeedFromQuery()` private method maps query params onto the component's filter fields with lenient enum parsing. `GenresView.razor.cs` (`DeepDrftPublic.Client/Pages/`): genre-tile click repointed to `/archive?genre=` from the former `/tracks?genre=`.
|
||||
|
||||
---
|
||||
|
||||
### 11.E — release-level Share
|
||||
|
||||
**Landed:** 2026-06-16 on dev.
|
||||
|
||||
@@ -246,7 +246,7 @@ Sequenced as **eight waves**; the critical path is `11.A → 11.B → 11.C → 1
|
||||
- **11.G — release Description schema slice.** New `ReleaseEntity.Description` column + EF migration (**Daniel-gated apply**), `ReleaseDto` mirror, `TrackConverter` round-trip, write-path plumbing (`UpdateTrackMetadataRequest` + upload form + the unified services, threaded wherever `Genre` is), CMS `AlbumHeaderFields` multiline input (§3d). **Free-floating, can start cold day one** — the only gate is Daniel's migration go-ahead. The **detail-page render is NOT in this wave**: the Cut text block rides 11.A, the Session/Mix block is a small additive touch to those existing pages. Both degrade cleanly (null Description renders nothing), so render & schema can land in either order.
|
||||
- **11.H — release GUID identifiers (terminal public-site wave).** Front the release `long` PK with an app-minted GUID-string `EntryKey` column — the **same pattern tracks use** (`TrackEntity.EntryKey` is `required string`, app-minted `Guid.NewGuid().ToString()`, keeping the int PK private). New `ReleaseEntity.EntryKey` (`string`, unique index, minted at `FindOrCreateRelease`) + EF migration that **backfills a GUID-string `EntryKey` for every existing release row at migration time** (**Daniel-gated apply**); `ReleaseDto.EntryKey`; `TrackConverter` round-trip; **re-type the public addressing surface from `long` to the `EntryKey` handle** — detail routes (`:long`→`{EntryKey}`), the `/tracks/{id}` redirect, `ReleaseRoutes.DetailHref`, `SharePopover.ReleaseId`, the public read path, and the public release API params (`GET api/release/{id}` + the `releaseId` track-page query). Internal FKs (track→release, satellite→release), the `long` int PK (unused by the app), and the ApiKey-gated CMS endpoints **stay on the int**. **Depends on 11.B (landed), 11.C, 11.D, 11.E** — it sweeps the routes/resolver/share/cards those waves create or edit, so it is the **last** public-site wave (spec §3e.7). **Gating decision (Daniel, spec §3e.5(1)) — RESOLVED (additive `EntryKey`, track-pattern):** additive app-level GUID-string `EntryKey` column matching tracks; the `long` PK stays DB-only and unused by the app; existing rows are backfilled at migration time (not a dev reset). Daniel's rationale (2026-06-16): "long at the DB level with an app-level guid `EntryKey` for the releases just like tracks; PK is not used by the app; migrate the existing data to provide the entry key at migration time." The true PK retype is **declined** (framework fork of `Cerebellum.BlazorBlocks.Models` — `BaseEntity.Id` hardwired `long` — plus full FK rewrite; recorded as considered-and-declined per file convention). Still open: raw-GUID URL (recommended) vs. slug, and migration ordering after 11.G's snapshot.
|
||||
|
||||
**Landed:** 11.A (2026-06-16); 11.F (2026-06-16); 11.G (2026-06-16); 11.B (2026-06-16); 11.C (2026-06-16); 11.E (2026-06-16). The §3.4 PlayAlbum→`IQueueService` seam (deferred in 11.A, awaiting 11.F) is now closed: `CutDetail.razor` consumes the cascaded `IQueueService` — header Play calls `Queue.PlayRelease(ViewModel.Tracks, 0)`, per-row play calls `Queue.PlayRelease(ViewModel.Tracks, index)`, currently-playing row toggles play/pause, null-safe fallback to `SelectTrackStreaming` when the queue cascade is absent (2026-06-16). Migration `20260616035252_AddReleaseDescription` authored but not yet applied (Daniel-gated). Tracks 11.D, 11.H remain open (11.H still gated behind 11.D).
|
||||
**Landed:** 11.A (2026-06-16); 11.F (2026-06-16); 11.G (2026-06-16); 11.B (2026-06-16); 11.C (2026-06-16); 11.E (2026-06-16); 11.D (2026-06-16). The §3.4 PlayAlbum→`IQueueService` seam (deferred in 11.A, awaiting 11.F) is now closed: `CutDetail.razor` consumes the cascaded `IQueueService` — header Play calls `Queue.PlayRelease(ViewModel.Tracks, 0)`, per-row play calls `Queue.PlayRelease(ViewModel.Tracks, index)`, currently-playing row toggles play/pause, null-safe fallback to `SelectTrackStreaming` when the queue cascade is absent (2026-06-16). Migration `20260616035252_AddReleaseDescription` authored but not yet applied (Daniel-gated). Track 11.H remains open (the active terminal wave).
|
||||
|
||||
**Dependency shape:** `11.A → 11.B → 11.C → 11.H`; `11.B → 11.E`; **11.D, 11.F, 11.G parallel** (11.D coordinates with 11.C on `ArchiveView`; 11.F's "play album" is consumed by 11.A; 11.G's Description render rides 11.A + a Session/Mix touch, degrading on null). **11.H is terminal** — it re-types the public release-addressing surface (routes, `ReleaseRoutes`, `SharePopover`, cards, public API params) that 11.B–11.E create/edit, so it follows all of them; its migration is authored after 11.G's so the EF snapshot stays linear. The cold-start items are **11.A**, **11.F**, and **11.G** — kick 11.A + 11.F off first so "play album" works on first ship of the Cut page; 11.G runs alongside on its own track; 11.H waits for the addressing surface to settle.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user