Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | 5x 5x 69x 69x 69x 69x 12x 12x 12x 3x 12x 12x 69x 24x 24x 24x 24x 24x 69x 69x 3898x | // SPDX-License-Identifier: GPL-3.0-only
// Copyright (C) 2026 Max Boksem. See NOTICE for additional terms under GPLv3 section 7.
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { createChangeTracker } from "@/lib/change-tracker";
import {
RECHECK_MS,
followWindowFullscreen,
setWindowFullscreen,
type FullscreenFollower,
} from "@/lib/fullscreen";
interface FullscreenContextValue {
isFullscreen: boolean;
/**
* Puts the player in or out of full screen. This hides or shows the
* sidebar and sets the window to match, so every way in or out, such as
* the button, Escape or closing the player, leaves the two in step. The
* state also follows the window when it changes some other way.
*/
setFullscreen: (on: boolean) => void;
}
export const FullscreenContext = createContext<FullscreenContextValue>({
isFullscreen: false,
setFullscreen: () => {},
});
export const FullscreenProvider = ({ children }: { children: React.ReactNode }) => {
const [isFullscreen, setIsFullscreen] = useState(false);
// The app's own changes, so that a read of the window sent before one
// can't undo it.
const [changes] = useState(createChangeTracker);
const followerRef = useRef<FullscreenFollower | null>(null);
const setFullscreen = useCallback(
(on: boolean) => {
setIsFullscreen(on);
const done = changes.begin();
setWindowFullscreen(on)
.catch((e) => {
console.error(`[fullscreen] setFullscreen(${on}) failed:`, e);
})
.finally(() => {
done();
// macOS can turn the change down, and then the window
// doesn't resize, so nothing else would read it.
setTimeout(() => followerRef.current?.recheck(), RECHECK_MS);
});
},
[changes]
);
// The window can go in or out of full screen without the app, such as
// with the green button on macOS. A change asked for during the animation
// of another one only lands when that ends. Following the window keeps
// the sidebar, the button and Escape in step with what's on screen.
useEffect(() => {
const follower = followWindowFullscreen(setIsFullscreen, changes);
followerRef.current = follower;
return () => {
follower.stop();
followerRef.current = null;
};
}, [changes]);
const value = useMemo(() => ({ isFullscreen, setFullscreen }), [isFullscreen, setFullscreen]);
return <FullscreenContext.Provider value={value}>{children}</FullscreenContext.Provider>;
};
export const useFullscreen = () => useContext(FullscreenContext);
|