fix(player): the player's own focus grabs leave a tabbed-to control alone
Turning ready, the pointer leaving the video, the window regaining focus, a wheel over it and leaving fullscreen each put the focus back on the card unconditionally, so a viewer who had tabbed to a control right after opening started over from the first button on the next Tab. They now take it back only when it sits in the iframe or off the card.
This commit is contained in:
@@ -81,11 +81,11 @@ function loadYouTubeApi(): Promise<YTNamespace> {
|
||||
export function useYtVolume({
|
||||
playerRef,
|
||||
dialogRef,
|
||||
focusModal,
|
||||
reclaimFocus,
|
||||
}: {
|
||||
playerRef: RefObject<YTPlayer | null>;
|
||||
dialogRef: RefObject<HTMLDivElement | null>;
|
||||
focusModal: () => void;
|
||||
reclaimFocus: () => void;
|
||||
}): {
|
||||
/** Level (0–100) to flash in the on-player overlay, or null when hidden. */
|
||||
volumeUi: number | null;
|
||||
@@ -168,7 +168,7 @@ export function useYtVolume({
|
||||
)
|
||||
return;
|
||||
e.preventDefault();
|
||||
focusModal(); // re-grab focus if it had slipped into the native controls
|
||||
reclaimFocus(); // re-grab the focus if it had slipped into the native controls
|
||||
nudgeVolume(e.deltaY < 0 ? 5 : -5);
|
||||
});
|
||||
useEffect(() => {
|
||||
@@ -204,11 +204,11 @@ export function useYtVolume({
|
||||
export function useNativeControlsYield({
|
||||
playerRef,
|
||||
stageRef,
|
||||
focusModal,
|
||||
reclaimFocus,
|
||||
}: {
|
||||
playerRef: RefObject<YTPlayer | null>;
|
||||
stageRef: RefObject<HTMLDivElement | null>;
|
||||
focusModal: () => void;
|
||||
reclaimFocus: () => void;
|
||||
}): {
|
||||
nativeControls: boolean;
|
||||
setNativeControls: (on: boolean) => void;
|
||||
@@ -221,7 +221,7 @@ export function useNativeControlsYield({
|
||||
});
|
||||
const rearmOurControls = useEffectEvent(() => {
|
||||
setNativeControls(false);
|
||||
focusModal();
|
||||
reclaimFocus();
|
||||
});
|
||||
useEffect(() => {
|
||||
// activeElement settles on the tick after blur, so defer the check.
|
||||
@@ -249,11 +249,11 @@ export function useNativeControlsYield({
|
||||
export function useYtBigView({
|
||||
stageRef,
|
||||
playerRef,
|
||||
focusModal,
|
||||
reclaimFocus,
|
||||
}: {
|
||||
stageRef: RefObject<HTMLDivElement | null>;
|
||||
playerRef: RefObject<YTPlayer | null>;
|
||||
focusModal: () => void;
|
||||
reclaimFocus: () => void;
|
||||
}): {
|
||||
isFullscreen: boolean;
|
||||
maximized: boolean;
|
||||
@@ -299,7 +299,7 @@ export function useYtBigView({
|
||||
const ours = !!fsEl && (fsEl === stageRef.current || fsEl === frame);
|
||||
isFullscreenRef.current = ours; // read by the once-bound keydown handler
|
||||
setIsFullscreen(ours); // drives the overlay's pointer-events
|
||||
if (!fsEl) focusModal();
|
||||
if (!fsEl) reclaimFocus();
|
||||
});
|
||||
useEffect(() => {
|
||||
return onFullscreenChange(() => syncFullscreen()); // both spellings — see lib/fullscreen
|
||||
@@ -348,7 +348,7 @@ export function useYtProgress({
|
||||
setPlayerError,
|
||||
setLiveData,
|
||||
reapplyStored,
|
||||
focusModal,
|
||||
reclaimFocus,
|
||||
advanceOnEnd,
|
||||
sessionPosRef,
|
||||
qc,
|
||||
@@ -364,7 +364,7 @@ export function useYtProgress({
|
||||
setPlayerError: (code: number | null) => void;
|
||||
setLiveData: (d: { title?: string; author?: string } | null) => void;
|
||||
reapplyStored: () => void;
|
||||
focusModal: () => void;
|
||||
reclaimFocus: () => void;
|
||||
advanceOnEnd: () => void;
|
||||
// Positions this player session has actually STORED, per video id. Written from the server's
|
||||
// answer, read back by the modal's resume so stepping next→prev inside one session does not
|
||||
@@ -382,7 +382,7 @@ export function useYtProgress({
|
||||
setPlayerError,
|
||||
setLiveData,
|
||||
reapplyStored,
|
||||
focusModal,
|
||||
reclaimFocus,
|
||||
advanceOnEnd,
|
||||
qc,
|
||||
}));
|
||||
@@ -396,7 +396,7 @@ export function useYtProgress({
|
||||
setPlayerError,
|
||||
setLiveData,
|
||||
reapplyStored,
|
||||
focusModal,
|
||||
reclaimFocus,
|
||||
advanceOnEnd,
|
||||
qc,
|
||||
} = itemSnapshot();
|
||||
@@ -458,7 +458,7 @@ export function useYtProgress({
|
||||
},
|
||||
events: {
|
||||
onReady: () => {
|
||||
focusModal();
|
||||
reclaimFocus();
|
||||
reapplyStored();
|
||||
},
|
||||
onStateChange: (e: YTPlayerEvent) => {
|
||||
@@ -554,7 +554,7 @@ export function useYtProgress({
|
||||
// and refs (which never change).
|
||||
//
|
||||
// The render values the body uses — `resumeAt`, `setWatched`, `advanceOnEnd`, `reapplyStored`,
|
||||
// `focusModal`, `qc`, … — are a PER-ITEM SNAPSHOT, taken once through `itemSnapshot` when this
|
||||
// `reclaimFocus`, `qc`, … — are a PER-ITEM SNAPSHOT, taken once through `itemSnapshot` when this
|
||||
// player is built: they describe the video it was built for, and re-reading them mid-session
|
||||
// would be the bug, not the fix. `setWatched` is the clearest case: bound to the item, it lets
|
||||
// this cleanup's late auto-mark land on the item being LEFT, which the modal then ignores. What
|
||||
|
||||
Reference in New Issue
Block a user