ENGLISH·COURSE API
    Preparing search index...
    • The "offer to resume on the first play" rule, with no player library in it.

      Parameters

      • savedPositionSeconds: {
            savedPositionSeconds: number | null;
            durationSeconds: number;
            player: ResumablePlayer;
        }

        The stored position, or null while unread or absent

      Returns ResumeOnFirstPlay

      The open offer and the three actions that answer it

      The offer is made once per mount, and only once the learner's first play has actually begun. Landing on a lesson prompts nothing: the learner may have come for the notes. When playback starts, and the saved position clears isPositionResumable, the player is paused and the position is offered.

      The hold happens on playback start, not on the play request, and the distinction is the whole reason this hook has a handlePlaybackStarted rather than a handlePlay. A provider that drives a third-party embed can swallow a pause issued while its initial play request is still in flight and stall there for good — never emitting pause, never reaching playing, ignoring every later seek and play. Waiting for playback to begin puts the pause outside that window for every provider, at the cost of a few milliseconds of video. See design.md §D1 of fix-youtube-resume-stuck-buffering.

      Every answer ends with the video playing, because playback starting is what opened the overlay. Resuming seeks to the saved position; restarting — which is also where dismissal lands — seeks to 0. Either way the offer is spent and later plays go straight through.

      The saved position is read asynchronously by the caller, so it may arrive after playback has started. It is not offered retroactively: interrupting a learner who is already watching is worse than skipping the offer.

      const resume = useResumeOnFirstPlay({ savedPositionSeconds, durationSeconds, player });

      <MediaPlayer onPlaying={resume.handlePlaybackStarted}>
      {resume.offeredSeconds !== null ? (
      <LessonVideoResumeOverlay
      positionSeconds={resume.offeredSeconds}
      onResume={resume.resumeFromSavedPosition}
      onRestart={resume.restartFromBeginning}
      />
      ) : null}
      </MediaPlayer>