From b225286f57d481f9a14cebe07b87163fb474e72b Mon Sep 17 00:00:00 2001 From: Uruk Date: Fri, 22 May 2026 00:53:17 +0200 Subject: [PATCH] docs(casting): add casting-player split design spec --- ...26-05-22-chromecast-player-split-design.md | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-22-chromecast-player-split-design.md diff --git a/docs/superpowers/specs/2026-05-22-chromecast-player-split-design.md b/docs/superpowers/specs/2026-05-22-chromecast-player-split-design.md new file mode 100644 index 000000000..37aed5171 --- /dev/null +++ b/docs/superpowers/specs/2026-05-22-chromecast-player-split-design.md @@ -0,0 +1,118 @@ +# Chromecast Player Split — Design + +**Date:** 2026-05-22 +**Branch:** `refactor-chromecast` (PR #1402) +**Sub-project:** C of the Chromecast refactor +**Status:** Approved design — pending implementation plan + +--- + +## 1. Problem + +`app/(auth)/casting-player.tsx` is a 1428-line god-component. One file owns: cast SDK +hooks, item fetching, live-progress tracking, slider/scrubbing, trickplay, track +selection, episode/season fetching, segment skipping, the dismiss gesture, and ~780 +lines of JSX. It is hard to read, hard to change safely, and every sub-project so far +has had to navigate it. + +## 2. Scope + +**In scope:** a purely structural decomposition of `casting-player.tsx` into 4 custom +hooks, 6 presentational components, and a thin orchestrator. + +**Out of scope — and a hard constraint:** **zero behaviour change.** This is a +mechanical extraction. No bug fixes, no feature changes, no logic edits. Known issues +in this code (the `loadEpisode` / `currentItem` race from sub-project A's review; the +trickplay window truncation, the progress-bar touch overlap, the time-label position) +are explicitly NOT touched here — they belong to a later UX sub-project, and are +easier to fix once the code lives in small focused files. + +## 3. Architecture + +`casting-player.tsx` becomes an orchestrator (~150-200 lines): it calls the hooks, +assembles the data, and renders the 6 components plus the 3 existing modal components. + +State flows orchestrator → components by **props** (approved approach 1) — no React +context. Presentational components take typed props; all logic lives in hooks. + +## 4. Hooks (`hooks/`) + +| Hook | Extracts | Returns (shape) | +|---|---|---| +| `useCastPlayerItem` | `fetchedItem` state, its fetch effect, the `currentItem` derivation | `{ fetchedItem, currentItem }` | +| `useCastPlayerProgress` | slider shared values, `isScrubbing`, `trickplayTime`, `scrubPercentage`, `liveProgress`, the sync refs/effects, the `useTrickplay` call | slider state, scrub handlers, `progress`, trickplay data | +| `useCastEpisodes` | `episodes` / `nextEpisode` / `seasonData` state, their fetch effects, `loadEpisode` | `{ episodes, nextEpisode, seasonData, loadEpisode }` | +| `useCastDismissGesture` | `translateY`, `context`, `panGesture`, `dismissModal`, `animatedStyle` | `{ panGesture, animatedStyle, dismissModal }` | + +Existing hooks are reused unchanged: `useCasting`, `useCastSelection`, +`useChromecastSegments`, `useTrickplay` (the latter called inside +`useCastPlayerProgress`). + +`useCastPlayerProgress` is the most intricate hook (shared values, refs, the +live-progress interpolation) — it must be extracted with care and reviewed closely. + +## 5. Components (`components/casting/player/`) + +| Component | Extracts (JSX section) | +|---|---| +| `CastPlayerHeader` | dismiss chevron, connection indicator, settings button | +| `CastPlayerTitle` | title + episode/season info | +| `CastPlayerPoster` | poster image, buffering overlay, skip intro/credits bar | +| `CastPlayerEpisodeControls` | the 4-button row (Episodes / Previous / Next / Stop) | +| `CastPlayerProgressBar` | slider, trickplay preview, time display | +| `CastPlayerTransportControls` | rewind / play-pause / forward | + +Each is a pure presentational component with typed props. The 3 modal components +(`ChromecastDeviceSheet`, `ChromecastEpisodeList`, `ChromecastSettingsMenu`) already +exist and stay as-is — the orchestrator keeps rendering them. + +## 6. Constraints + +- **Mechanical extraction only.** Each component's / hook's props and returns mirror + exactly what the inline code used. No new logic, no renamed behaviour, no fixes. +- Component prop interfaces are derived from what the extracted JSX references in the + original component scope. +- Follow existing repo conventions: hooks flat in `hooks/`; the new components grouped + under `components/casting/player/`. + +## 7. Verification + +The casting UI has no unit tests (consistent with the rest of the casting UI — it is +React Native / SDK-heavy). Verification is: + +- `bun run typecheck` — green after every task (catches wiring/prop errors). +- `bun test utils/casting/` — stays green (the pure-logic suites are untouched). +- A full **manual re-test** of the cast player after the split: cast a movie and an + episode, switch audio / subtitle / quality / version, navigate episodes, scrub the + progress bar, trigger trickplay, dismiss the player. Behaviour must be identical to + before the split. + +## 8. Files + +**Created:** +- `hooks/useCastPlayerItem.ts`, `hooks/useCastPlayerProgress.ts`, + `hooks/useCastEpisodes.ts`, `hooks/useCastDismissGesture.ts` +- `components/casting/player/CastPlayerHeader.tsx`, `CastPlayerTitle.tsx`, + `CastPlayerPoster.tsx`, `CastPlayerEpisodeControls.tsx`, + `CastPlayerProgressBar.tsx`, `CastPlayerTransportControls.tsx` + +**Modified:** +- `app/(auth)/casting-player.tsx` → thin orchestrator. + +## 9. Success criteria + +- `casting-player.tsx` is ~150-200 lines and contains no inline logic clusters or + large JSX sections — only hook calls and component composition. +- Each new file has one clear responsibility and a typed interface. +- `bun run typecheck` and `bun test utils/casting/` pass. +- The manual re-test shows behaviour identical to before the split. + +## 10. Risks + +- The hooks share state (progress feeds the slider, the time display, and segment + skipping). The extraction must preserve the exact data flow and effect ordering. + Mitigation: one hook / component per task, `bun run typecheck` after each, and a + full manual re-test at the end. +- `useCastPlayerProgress` carries reanimated shared values and timing refs — the + highest-risk extraction; its task gets the closest review. +- No unit tests guard the UI — the manual re-test is the only behavioural safety net.