docs(casting): add casting-player split design spec

This commit is contained in:
Uruk
2026-05-22 00:53:17 +02:00
parent 23b4f20d18
commit b225286f57

View File

@@ -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.