feature: Phase 22 SEO metadata component for public site
One presentational SeoHead renders the full OG/Twitter/JSON-LD head surface at prerender via typed schema.org builders. Per-medium release schema, config-sourced canonicals, 404 noindex. Zero CMS change.
This commit is contained in:
@@ -2,8 +2,9 @@
|
||||
@using DeepDrftPublic.Client.Controls
|
||||
@implements IAsyncDisposable
|
||||
@inject IJSRuntime JsRuntime
|
||||
@inject SeoOptions Seo
|
||||
|
||||
<PageTitle>The Collective - Deep DRFT</PageTitle>
|
||||
<SeoHead Model="@SeoModel.ForAbout(Seo)" />
|
||||
|
||||
@* ──────────────────────────────────────────────────────────────────────────────
|
||||
THE LINER NOTES — a numbered three-movement editorial essay.
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
@page "/cuts"
|
||||
@using DeepDrftModels.Enums
|
||||
@using DeepDrftPublic.Client.Controls
|
||||
@inject SeoOptions Seo
|
||||
|
||||
<PageTitle>DeepDrft Cuts</PageTitle>
|
||||
<SeoHead Model="@SeoModel.ForBrowse(Seo, ReleaseMedium.Cut, "/cuts")" />
|
||||
|
||||
@* The shared release-card grid; each card routes to /cuts/{entryKey} via the one ReleaseRoutes table.
|
||||
Cuts show a track count where other media show the artist, supplied via SubtitleResolver. *@
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
@page "/archive"
|
||||
@using DeepDrftModels.Enums
|
||||
@using DeepDrftPublic.Client.Controls
|
||||
@inject SeoOptions Seo
|
||||
|
||||
<PageTitle>DeepDrft Archive</PageTitle>
|
||||
<SeoHead Model="@SeoModel.ForBrowse(Seo, null, "/archive")" />
|
||||
|
||||
<div>
|
||||
<MudContainer MaxWidth="MaxWidth.Large" Class="archive-view-container">
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
@using DeepDrftPublic.Client.Controls
|
||||
@using DeepDrftPublic.Client.Services
|
||||
@inherits CutDetailBase
|
||||
|
||||
<PageTitle>@(ViewModel.Release?.Title ?? "Cut") - DeepDrft</PageTitle>
|
||||
@inject SeoOptions Seo
|
||||
|
||||
@if (ViewModel.IsLoading)
|
||||
{
|
||||
@@ -37,6 +36,10 @@ else
|
||||
var hasYear = release.ReleaseDate is not null;
|
||||
var firstTrack = ViewModel.Tracks.Count > 0 ? ViewModel.Tracks[0] : null;
|
||||
|
||||
@* SEO head — fed from the same bridged release + ordered tracks, so the prerender and WASM passes
|
||||
render identical tags (AC6). MusicAlbum/StudioAlbum with the ordered track list (§3.4). *@
|
||||
<SeoHead Model="@SeoModel.ForRelease(Seo, release, ViewModel.Tracks)" />
|
||||
|
||||
@* Full-screen content body (Phase 20 Wave 2 §1): the scaffold has no Class param, so a thin wrapper
|
||||
carries the min-height. dd-detail-fill keeps the body >= viewport height (below the nav) so the
|
||||
ambient visualizer reads full-screen and the site footer is pushed below the fold. *@
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
@page "/"
|
||||
@using DeepDrftPublic.Client.Controls
|
||||
@using DeepDrftPublic.Client.Services
|
||||
@inject SeoOptions Seo
|
||||
|
||||
<PageTitle>Deep DRFT - Electronic Music Collective</PageTitle>
|
||||
<SeoHead Model="@SeoModel.ForHome(Seo)" />
|
||||
|
||||
@* Hero - split 50/50 *@
|
||||
<section class="hero">
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
@using DeepDrftPublic.Client.Controls
|
||||
@using DeepDrftPublic.Client.Services
|
||||
@inherits ReleaseDetailBase
|
||||
|
||||
<PageTitle>@(ViewModel.Release?.Title ?? "Mix") - DeepDrft</PageTitle>
|
||||
@inject SeoOptions Seo
|
||||
|
||||
@if (ViewModel.IsLoading)
|
||||
{
|
||||
@@ -32,6 +31,11 @@ else if (ViewModel.NotFound || ViewModel.Release is null)
|
||||
else
|
||||
{
|
||||
var release = ViewModel.Release;
|
||||
var mixTracks = ViewModel.Track is not null ? new[] { ViewModel.Track } : null;
|
||||
|
||||
@* SEO head — fed from the same bridged release + single track, so prerender and WASM render identical
|
||||
tags (AC6). MusicRecording with ISO-8601 duration from the track (§3.4). *@
|
||||
<SeoHead Model="@SeoModel.ForRelease(Seo, release, mixTracks)" />
|
||||
|
||||
@* Full-page waveform sits behind the scaffold content. The scaffold's container is positioned
|
||||
above it via the mix-detail-foreground stacking context. TrackId lets the visualizer couple to
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
@page "/mixes"
|
||||
@using DeepDrftModels.Enums
|
||||
@using DeepDrftPublic.Client.Controls
|
||||
@inherits MediumBrowseBase
|
||||
@inject SeoOptions Seo
|
||||
|
||||
<PageTitle>DeepDrft Mixes</PageTitle>
|
||||
<SeoHead Model="@SeoModel.ForBrowse(Seo, ReleaseMedium.Mix, "/mixes")" />
|
||||
|
||||
<ReleaseGallery Releases="@Releases"
|
||||
Loading="@Loading"
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
@page "/404"
|
||||
@using DeepDrftPublic.Client.Controls
|
||||
@inject SeoOptions Seo
|
||||
|
||||
@* The 404 must not be indexed (AC8): noindex,follow — no canonical, no JSON-LD. *@
|
||||
<SeoHead Model="@SeoModel.ForNotFound(Seo)" />
|
||||
|
||||
<MudText Typo="Typo.h3">
|
||||
Not Found
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
@using DeepDrftPublic.Client.Controls
|
||||
@using DeepDrftPublic.Client.Services
|
||||
@inherits ReleaseDetailBase
|
||||
|
||||
<PageTitle>@(ViewModel.Release?.Title ?? "Session") - DeepDrft</PageTitle>
|
||||
@inject SeoOptions Seo
|
||||
|
||||
@if (ViewModel.IsLoading)
|
||||
{
|
||||
@@ -40,6 +39,10 @@ else
|
||||
// Hero image precedence: the session's dedicated hero, then the release cover, then a placeholder.
|
||||
var heroImage = !string.IsNullOrEmpty(heroKey) ? heroKey : release.ImagePath;
|
||||
|
||||
@* SEO head — fed from the same bridged release, so prerender and WASM render identical tags (AC6).
|
||||
MusicAlbum/LiveAlbum (a session is a live release, §3.4/OQ6). *@
|
||||
<SeoHead Model="@SeoModel.ForRelease(Seo, release)" />
|
||||
|
||||
@* Ambient living waveform behind the hero overlay (Phase 12 §3e option b / §3f mode B). Session does
|
||||
NOT compose ReleaseDetailScaffold, so it mounts the shared engine directly with its own thin
|
||||
full-bleed wrapper — the engine is single-source either way, only the mount differs (§3b). The
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
@page "/sessions"
|
||||
@using DeepDrftModels.Enums
|
||||
@using DeepDrftPublic.Client.Controls
|
||||
@inherits MediumBrowseBase
|
||||
@inject SeoOptions Seo
|
||||
|
||||
<PageTitle>DeepDrft Sessions</PageTitle>
|
||||
<SeoHead Model="@SeoModel.ForBrowse(Seo, ReleaseMedium.Session, "/sessions")" />
|
||||
|
||||
<ReleaseGallery Releases="@Releases"
|
||||
Loading="@Loading"
|
||||
|
||||
Reference in New Issue
Block a user