import { useCallback, useEffect, useRef, useState, type MutableRefObject, type RefObject, type WheelEvent as ReactWheelEvent, } from "react"; import type { QueryClient } from "@tanstack/react-query"; import Hls from "hls.js"; import { exitFullscreen, fullscreenElement, onFullscreenChange, requestFullscreen, } from "./fullscreen"; import { api, type PlexItemDetail, type PlexMarker } from "./api"; import { qk } from "./queryKeys"; import { clamp01, type PlexPlayerPrefs } from "./plexPlayerPrefs"; // A live wall clock for the top bar, re-rendered every 15s (minute resolution is enough for the // HH:MM display, and a coarse tick keeps the whole player from re-rendering each second). export function useWallClock(): Date { const [now, setNow] = useState(() => new Date()); useEffect(() => { const iv = window.setInterval(() => setNow(new Date()), 15000); return () => window.clearInterval(iv); }, []); return now; } // Fullscreen toggle for `el` (the player wrapper) + a reactive `fs` flag. // Via lib/fullscreen, not the raw API: WebKit (Safari/iPadOS) ships only the prefixed spelling, so // reading `document.fullscreenElement` alone made this player believe it is never fullscreen — the // button did nothing, `fs` never flipped, and Escape read "not fullscreen" as permission to close the // whole player instead of just leaving fullscreen. export function useFullscreen(wrapRef: RefObject): { fs: boolean; toggleFs: () => void; } { const [fs, setFs] = useState(false); const toggleFs = useCallback(() => { const el = wrapRef.current; if (!el) return; if (fullscreenElement()) exitFullscreen(); else requestFullscreen(el); }, [wrapRef]); useEffect(() => onFullscreenChange(() => setFs(!!fullscreenElement())), []); return { fs, toggleFs }; } // Auto-hiding control bar: `uiVisible` drives the chrome opacity; `wake` reveals it and (re)arms the // 3s hide timer. Reads the caller's live refs so the timer callback sees fresh state without a // closure rebuild: barAutoHide (per-user "never auto-hide" setting), whether a menu is open (keep the // bar up so it can't vanish mid-choice), and the