Function
Keys the stored position
This component is composition and nothing else. Both rules it depends on live in hooks that know no player library:
useResumeOnFirstPlay decides whether, and when, to offer the saved
position — on the learner's first play, never on mount. Landing on a
lesson prompts nothing; a learner who came for the notes is not stopped.
It hangs off onPlaying, not onPlay: the offer holds the player by
pausing it, and a provider driving a third-party embed stalls for good if
that pause lands while its initial play request is still in flight.
onPlay keeps the two things that are about the learner's intent to
watch — the write gate and the gold-cover callback — which are right to
fire a beat before the first frame.usePersistPlaybackPosition owns the write cadence (debounced
time-update, immediate pause/seeking/ended, flush on unmount and
beforeunload) and the gate that keeps a cold load from overwriting a
stored position with 0.useCompleteWhenWatched marks the lesson complete once playback reaches
the end, through the same path the manual button uses. It carries its own
gate, for the mirror-image reason: opening a lesson whose stored position
is already past the threshold must not record a completion the learner
did not earn on this visit.The saved position is read once on mount. That read is pure — it feeds the offer and writes nothing.
The overlay renders as a child of the player, so it covers the video frame
and leaves the rest of the lesson page live. While it is up, the player's
own keyboard shortcuts are disabled: otherwise Space on the Resume button
would press it and toggle playback underneath.
LessonVideoSkeleton is a sibling, not a child, and that is the point.
Children of the player mount when the player does, which is exactly the gap
the placeholder exists to cover; a sibling ships inside the server-rendered
HTML and dresses the frame from the first paint. It is retired on
useMediaState("canPlay") rather than on hydration — this component owns the
player ref, so it is the only place that can read readiness — and the hook
answers false both on the server and on the client's hydration render,
which is what keeps the two in agreement.
The lesson player with its memory: it remembers where the learner stopped, and offers to take them back there.