From 09764e24d408701c8e31d9cf8607c84c6cfe89ee Mon Sep 17 00:00:00 2001 From: 4gray <4gray@users.noreply.github.com> Date: Tue, 7 Jul 2026 08:20:34 +0200 Subject: [PATCH] feat(portals): back button on the in-portal search page (#1142) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(portals): back button on the in-portal search page Opening a "grey" (unmatched) film from an actor page navigates to the portal search prefilled with ?q=, but the search page had no way back — users had to re-navigate through the sidebar to reach the actor's filmography again. SearchLayoutComponent gains a showBackButton input + backClick output; both the Xtream in-portal search (SearchResultsComponent) and the Stalker search wire it to Location.back(). Shown only on the nested in-portal search — the top-level global search (sidebar destination) and the dialog mode (own close button) are excluded. location.back() unwinds the full history, so search → actor → movie detail → … all chain correctly. Tests: search-layout spec covers the conditional button + backClick. Docs: CLAUDE.md actor-pages note updated. Co-Authored-By: Claude Fable 5 * fix(portals): address search back-button review (dialog guard, RTL-safe spacing) - showBackButton now also checks !dialogRef, so a hypothetical dialog opened with isGlobalSearch:false from a workspace route can't show a back arrow that navigates the page behind the dialog (greptile). - Replaced the fragile negative margin with a header-title-group flex wrapper (gap: 4px) that keeps the arrow and title together regardless of the outer gap or Material padding, and is RTL-safe (greptile). Co-Authored-By: Claude Fable 5 --------- Co-authored-by: Claude Fable 5 --- CLAUDE.md | 2 +- .../search-layout.component.html | 14 ++++++++++++- .../search-layout.component.scss | 12 +++++++++++ .../search-layout.component.spec.ts | 20 +++++++++++++++++++ .../search-layout/search-layout.component.ts | 10 ++++++++++ .../stalker-search.component.html | 2 ++ .../stalker-search.component.ts | 7 +++++++ .../search-results.component.html | 2 ++ .../search-results.component.ts | 18 +++++++++++++++++ 9 files changed, 85 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a18670bde..a68c0c124 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -648,7 +648,7 @@ This project uses modern Angular signal-based APIs and patterns. **ALWAYS** use - "Similar" rail in ALL detail views: TMDB recommendations matched against the provider catalog by normalized title, two-tier — exact form first, year-stripped fallback gated on year compatibility (`libs/portal/xtream/feature/src/lib/tmdb-similar.util.ts`, `normalizeTitleKeys`); cross-portal matches from other imported Xtream playlists supplement the Xtream rail and fully power the Stalker rail (`CrossPortalSimilarService` in `libs/services`, batched `DB_MATCH_TITLES`, Electron only); detail components re-initialize on route param changes since the router reuses them for detail→detail navigation - Season/episode enrichment: opening a season lazily fetches `/tv/{id}/season/{n}` and overlays real episode names, overviews and stills via `mergeEpisodesWithTmdb` (Xtream: `XtreamStore.enrichSelectedSerialSeason`; Stalker: overlay in the series view's `mappedSeasons`) - Dashboard: opt-in "Trending this week" rail (weekly TMDB trending matched against imported Xtream playlists via one batched `DB_MATCH_TITLES` request; Electron-only, `dashboardRails.tmdbTrending` toggle) and hero TMDB extras (backdrop fallback, rating + genre badges, memoized per session; series heroes show the tracked S/E badge from playback positions) — `DashboardTrendingService` in `libs/workspace/dashboard/data-access`, `DashboardHeroTmdbService` in `libs/workspace/dashboard/feature`; both load async after first paint -- Actor pages: cast avatar chips are clickable (TMDB person id) and open `actor/:personId` inside the current portal — TMDB person bio + full filmography; Xtream matches titles against the loaded catalog (direct navigation), unmatched titles and all Stalker titles open the portal search prefilled (`?q=`); shared UI in `libs/ui/shared-portals` (`ActorViewComponent`) +- Actor pages: cast avatar chips are clickable (TMDB person id) and open `actor/:personId` inside the current portal — TMDB person bio + full filmography; Xtream matches titles against the loaded catalog (direct navigation), unmatched titles and all Stalker titles open the portal search prefilled (`?q=`); the in-portal search page shows a Back button (`SearchLayoutComponent.showBackButton` → `Location.back()`) so users can return to the actor page; shared UI in `libs/ui/shared-portals` (`ActorViewComponent`) - Actor page "All portals" scope (Electron only): batched `DB_MATCH_TITLES` worker op (trigram FTS over all imported Xtream playlists, `apps/electron-backend/src/app/database/operations/title-match.operations.ts`); `normalizeTitle` is shared renderer/worker via `libs/shared/interfaces/src/lib/title-normalization.util.ts` - Opt-in via `Settings > Metadata (TMDB)` (sends titles to TMDB); optional user API key overrides the embedded default (`DEFAULT_TMDB_API_KEY` in `libs/services/src/lib/tmdb/tmdb-config.ts` — an empty placeholder in the repo by design; the real key lives in the `TMDB_API_KEY` GitHub Actions secret and is injected at CI build time by `tools/tmdb/inject-tmdb-key.mjs`) - Match confidence: provider `tmdb_id` trusted fully; otherwise normalized-title + year (±1) search with a strict gate — no confident match means no enrichment diff --git a/libs/portal/shared/ui/src/lib/components/search-layout/search-layout.component.html b/libs/portal/shared/ui/src/lib/components/search-layout/search-layout.component.html index 7901f338c..419b28f15 100644 --- a/libs/portal/shared/ui/src/lib/components/search-layout/search-layout.component.html +++ b/libs/portal/shared/ui/src/lib/components/search-layout/search-layout.component.html @@ -7,7 +7,19 @@