mirror of
https://github.com/DustinBrett/daedalOS.git
synced 2026-01-15 12:15:02 +00:00
Fix fullscreen issues
This commit is contained in:
@@ -7,7 +7,6 @@ import {
|
||||
ZoomOut,
|
||||
} from "components/apps/Photos/PhotoIcons";
|
||||
import StyledPhotos from "components/apps/Photos/StyledPhotos";
|
||||
import useFullscreen from "components/apps/Photos/useFullscreen";
|
||||
import usePanZoom, { panZoomConfig } from "components/apps/Photos/usePanZoom";
|
||||
import type { ComponentProcessProps } from "components/system/Apps/RenderComponent";
|
||||
import useFileDrop from "components/system/Files/FileManager/useFileDrop";
|
||||
@@ -33,6 +32,7 @@ import {
|
||||
imgDataToBuffer,
|
||||
label,
|
||||
} from "utils/functions";
|
||||
import { useViewport } from "contexts/viewport";
|
||||
|
||||
const { maxScale, minScale } = panZoomConfig;
|
||||
|
||||
@@ -94,7 +94,7 @@ const Photos: FC<ComponentProcessProps> = ({ id }) => {
|
||||
imageRef.current,
|
||||
imageContainerRef.current
|
||||
);
|
||||
const { fullscreen, toggleFullscreen } = useFullscreen(containerRef.current);
|
||||
const { fullscreenElement, toggleFullscreen } = useViewport();
|
||||
const loadPhoto = useCallback(async (): Promise<void> => {
|
||||
let fileContents: Buffer | string = await readFile(url);
|
||||
const ext = getExtension(url);
|
||||
@@ -225,10 +225,14 @@ const Photos: FC<ComponentProcessProps> = ({ id }) => {
|
||||
<nav className="bottom">
|
||||
<Button
|
||||
disabled={!url}
|
||||
onClick={() => toggleFullscreen("show")}
|
||||
onClick={() => toggleFullscreen(containerRef.current, "show")}
|
||||
{...label("Full-screen")}
|
||||
>
|
||||
{fullscreen ? <ExitFullscreen /> : <Fullscreen />}
|
||||
{fullscreenElement === containerRef.current ? (
|
||||
<ExitFullscreen />
|
||||
) : (
|
||||
<Fullscreen />
|
||||
)}
|
||||
</Button>
|
||||
</nav>
|
||||
</StyledPhotos>
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
type Fullscreen = {
|
||||
fullscreen: boolean;
|
||||
toggleFullscreen: (navigationUI?: FullscreenNavigationUI) => void;
|
||||
};
|
||||
|
||||
type FullscreenDocument = Document & {
|
||||
mozCancelFullScreen: () => Promise<void>;
|
||||
mozFullScreenElement: HTMLElement;
|
||||
mozFullScreenEnabled: boolean;
|
||||
webkitExitFullscreen: () => Promise<void>;
|
||||
webkitFullscreenElement: HTMLElement;
|
||||
webkitFullscreenEnabled: boolean;
|
||||
};
|
||||
|
||||
type FullscreenElement = HTMLElement & {
|
||||
mozRequestFullScreen?: (options?: FullscreenOptions) => Promise<void>;
|
||||
webkitRequestFullscreen?: (options?: FullscreenOptions) => Promise<void>;
|
||||
};
|
||||
|
||||
const isFullscreen = (): boolean => {
|
||||
const { fullscreenElement, mozFullScreenElement, webkitFullscreenElement } =
|
||||
document as FullscreenDocument;
|
||||
|
||||
return Boolean(
|
||||
fullscreenElement || mozFullScreenElement || webkitFullscreenElement
|
||||
);
|
||||
};
|
||||
|
||||
const useFullscreen = (element?: HTMLElement | null): Fullscreen => {
|
||||
const [fullscreen, setFullscreen] = useState(false);
|
||||
const toggleFullscreen = async (
|
||||
navigationUI?: FullscreenNavigationUI
|
||||
): Promise<void> => {
|
||||
if (fullscreen) {
|
||||
const fullscreenDocument = document as FullscreenDocument;
|
||||
|
||||
try {
|
||||
if (fullscreenDocument.exitFullscreen) {
|
||||
await fullscreenDocument.exitFullscreen();
|
||||
} else if (fullscreenDocument.mozCancelFullScreen) {
|
||||
await fullscreenDocument.mozCancelFullScreen();
|
||||
} else if (fullscreenDocument.webkitExitFullscreen) {
|
||||
await fullscreenDocument.webkitExitFullscreen();
|
||||
}
|
||||
} catch {
|
||||
// Ignore failure while exiting fullscreen
|
||||
}
|
||||
} else {
|
||||
const fullScreenElement = (element ||
|
||||
document.documentElement) as FullscreenElement;
|
||||
const fullscreenOptions: FullscreenOptions = {
|
||||
navigationUI: navigationUI || "hide",
|
||||
};
|
||||
|
||||
try {
|
||||
if (fullScreenElement.requestFullscreen) {
|
||||
await fullScreenElement.requestFullscreen(fullscreenOptions);
|
||||
} else if (fullScreenElement.mozRequestFullScreen) {
|
||||
await fullScreenElement.mozRequestFullScreen(fullscreenOptions);
|
||||
} else if (fullScreenElement.webkitRequestFullscreen) {
|
||||
await fullScreenElement.webkitRequestFullscreen(fullscreenOptions);
|
||||
}
|
||||
} catch {
|
||||
// Ignore failure while entering fullscreen
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const monitorFullscreenState = (): void => setFullscreen(isFullscreen());
|
||||
|
||||
document.addEventListener("fullscreenchange", monitorFullscreenState);
|
||||
|
||||
return () =>
|
||||
document.removeEventListener("fullscreenchange", monitorFullscreenState);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
fullscreen,
|
||||
toggleFullscreen,
|
||||
};
|
||||
};
|
||||
|
||||
export default useFullscreen;
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useMemo } from "react";
|
||||
import useFullscreen from "components/apps/Photos/useFullscreen";
|
||||
import { useMenu } from "contexts/menu";
|
||||
import type {
|
||||
ContextMenuCapture,
|
||||
@@ -9,12 +8,13 @@ import { useProcesses } from "contexts/process";
|
||||
import { useProcessesRef } from "hooks/useProcessesRef";
|
||||
import { MENU_SEPERATOR } from "utils/constants";
|
||||
import { toggleShowDesktop } from "utils/functions";
|
||||
import { useViewport } from "contexts/viewport";
|
||||
|
||||
const useTaskbarContextMenu = (onStartButton = false): ContextMenuCapture => {
|
||||
const { contextMenu } = useMenu();
|
||||
const { minimize, open } = useProcesses();
|
||||
const processesRef = useProcessesRef();
|
||||
const { fullscreen, toggleFullscreen } = useFullscreen();
|
||||
const { fullscreenElement, toggleFullscreen } = useViewport();
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
@@ -54,7 +54,10 @@ const useTaskbarContextMenu = (onStartButton = false): ContextMenuCapture => {
|
||||
menuItems.unshift(
|
||||
{
|
||||
action: () => toggleFullscreen(),
|
||||
label: fullscreen ? "Exit full screen" : "Enter full screen",
|
||||
label:
|
||||
fullscreenElement === document.documentElement
|
||||
? "Exit full screen"
|
||||
: "Enter full screen",
|
||||
},
|
||||
MENU_SEPERATOR
|
||||
);
|
||||
@@ -64,7 +67,7 @@ const useTaskbarContextMenu = (onStartButton = false): ContextMenuCapture => {
|
||||
}),
|
||||
[
|
||||
contextMenu,
|
||||
fullscreen,
|
||||
fullscreenElement,
|
||||
minimize,
|
||||
onStartButton,
|
||||
open,
|
||||
|
||||
6
contexts/viewport/index.tsx
Normal file
6
contexts/viewport/index.tsx
Normal file
@@ -0,0 +1,6 @@
|
||||
import useViewportContextState from "contexts/viewport/useViewportContextState";
|
||||
import contextFactory from "contexts/contextFactory";
|
||||
|
||||
const { Provider, useContext } = contextFactory(useViewportContextState);
|
||||
|
||||
export { Provider as ViewportProvider, useContext as useViewport };
|
||||
26
contexts/viewport/types.ts
Normal file
26
contexts/viewport/types.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export type ViewportContextState = {
|
||||
fullscreenElement: Element | null;
|
||||
toggleFullscreen: (
|
||||
element?: HTMLElement | null,
|
||||
navigationUI?: FullscreenNavigationUI
|
||||
) => void;
|
||||
};
|
||||
|
||||
export type FullscreenDocument = Document & {
|
||||
mozCancelFullScreen: () => Promise<void>;
|
||||
mozFullScreenElement: Element | null;
|
||||
webkitExitFullscreen: () => Promise<void>;
|
||||
webkitFullscreenElement: Element | null;
|
||||
};
|
||||
|
||||
export type FullscreenElement = HTMLElement & {
|
||||
mozRequestFullScreen?: (options?: FullscreenOptions) => Promise<void>;
|
||||
webkitRequestFullscreen?: (options?: FullscreenOptions) => Promise<void>;
|
||||
};
|
||||
|
||||
export type NavigatorWithKeyboard = Navigator & {
|
||||
keyboard?: {
|
||||
lock?: (keys?: string[]) => Promise<void>;
|
||||
unlock?: () => void;
|
||||
};
|
||||
};
|
||||
106
contexts/viewport/useViewportContextState.ts
Normal file
106
contexts/viewport/useViewportContextState.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import type {
|
||||
ViewportContextState,
|
||||
FullscreenDocument,
|
||||
NavigatorWithKeyboard,
|
||||
FullscreenElement,
|
||||
} from "contexts/viewport/types";
|
||||
import { isFirefox } from "utils/functions";
|
||||
|
||||
const FULLSCREEN_LOCKED_KEYS = ["MetaLeft", "MetaRight", "Escape"];
|
||||
|
||||
const enterFullscreen = async (
|
||||
element: FullscreenElement,
|
||||
options: FullscreenOptions
|
||||
): Promise<void> => {
|
||||
try {
|
||||
if (element.requestFullscreen) {
|
||||
await element.requestFullscreen(options);
|
||||
} else if (element.mozRequestFullScreen) {
|
||||
await element.mozRequestFullScreen(options);
|
||||
} else if (element.webkitRequestFullscreen) {
|
||||
await element.webkitRequestFullscreen(options);
|
||||
}
|
||||
} catch {
|
||||
// Ignore failure while entering fullscreen
|
||||
}
|
||||
};
|
||||
|
||||
const exitFullscreen = async (): Promise<void> => {
|
||||
const fullscreenDocument = document as FullscreenDocument;
|
||||
|
||||
try {
|
||||
if (fullscreenDocument.exitFullscreen) {
|
||||
await fullscreenDocument.exitFullscreen();
|
||||
} else if (fullscreenDocument.mozCancelFullScreen) {
|
||||
await fullscreenDocument.mozCancelFullScreen();
|
||||
} else if (fullscreenDocument.webkitExitFullscreen) {
|
||||
await fullscreenDocument.webkitExitFullscreen();
|
||||
}
|
||||
} catch {
|
||||
// Ignore failure while exiting fullscreen
|
||||
}
|
||||
};
|
||||
|
||||
const toggleKeyboardLock = async (
|
||||
fullscreenElement: Element | null
|
||||
): Promise<void> => {
|
||||
try {
|
||||
if (fullscreenElement) {
|
||||
await (navigator as NavigatorWithKeyboard)?.keyboard?.lock?.(
|
||||
FULLSCREEN_LOCKED_KEYS
|
||||
);
|
||||
} else {
|
||||
(navigator as NavigatorWithKeyboard)?.keyboard?.unlock?.();
|
||||
}
|
||||
} catch {
|
||||
// Ignore failure to lock keys
|
||||
}
|
||||
};
|
||||
|
||||
const useViewportContextState = (): ViewportContextState => {
|
||||
const [fullscreenElement, setFullscreenElement] = useState<Element | null>(
|
||||
// eslint-disable-next-line unicorn/no-null
|
||||
null
|
||||
);
|
||||
const toggleFullscreen = async (
|
||||
element?: HTMLElement | null,
|
||||
navigationUI?: FullscreenNavigationUI
|
||||
): Promise<void> => {
|
||||
if (fullscreenElement && (!element || element === fullscreenElement)) {
|
||||
await exitFullscreen();
|
||||
} else {
|
||||
if (fullscreenElement && isFirefox()) await exitFullscreen();
|
||||
|
||||
await enterFullscreen(element || document.documentElement, {
|
||||
navigationUI: navigationUI || "hide",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const onFullscreenChange = (): void => {
|
||||
const { mozFullScreenElement, webkitFullscreenElement } =
|
||||
document as FullscreenDocument;
|
||||
const currentFullscreenElement =
|
||||
document.fullscreenElement ||
|
||||
mozFullScreenElement ||
|
||||
webkitFullscreenElement;
|
||||
|
||||
toggleKeyboardLock(currentFullscreenElement).then(() =>
|
||||
setFullscreenElement(currentFullscreenElement)
|
||||
);
|
||||
};
|
||||
|
||||
document.addEventListener("fullscreenchange", onFullscreenChange, {
|
||||
passive: true,
|
||||
});
|
||||
|
||||
return () =>
|
||||
document.removeEventListener("fullscreenchange", onFullscreenChange);
|
||||
}, []);
|
||||
|
||||
return { fullscreenElement, toggleFullscreen };
|
||||
};
|
||||
|
||||
export default useViewportContextState;
|
||||
@@ -1,16 +1,9 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import useFullscreen from "components/apps/Photos/useFullscreen";
|
||||
import { useProcesses } from "contexts/process";
|
||||
import { useSession } from "contexts/session";
|
||||
import { useProcessesRef } from "hooks/useProcessesRef";
|
||||
import { haltEvent, toggleShowDesktop } from "utils/functions";
|
||||
|
||||
type NavigatorWithKeyboard = Navigator & {
|
||||
keyboard?: {
|
||||
lock?: (keys?: string[]) => void;
|
||||
unlock?: () => void;
|
||||
};
|
||||
};
|
||||
import { useViewport } from "contexts/viewport";
|
||||
|
||||
const openStartMenu = (): void =>
|
||||
(
|
||||
@@ -42,7 +35,7 @@ const useGlobalKeyboardShortcuts = (): void => {
|
||||
const { closeWithTransition, maximize, minimize, open } = useProcesses();
|
||||
const processesRef = useProcessesRef();
|
||||
const { foregroundId } = useSession();
|
||||
const { fullscreen, toggleFullscreen } = useFullscreen();
|
||||
const { fullscreenElement, toggleFullscreen } = useViewport();
|
||||
const altBindingsRef = useRef<Record<string, () => void>>({});
|
||||
const shiftBindingsRef = useRef<Record<string, () => void>>({
|
||||
E: () => open("FileExplorer"),
|
||||
@@ -82,17 +75,14 @@ const useGlobalKeyboardShortcuts = (): void => {
|
||||
);
|
||||
} else if (ctrlKey && altKey && altBindingsRef.current?.[keyName]) {
|
||||
altBindingsRef.current?.[keyName]?.();
|
||||
} else if (fullscreen) {
|
||||
} else if (fullscreenElement === document.documentElement) {
|
||||
if (keyName === "META") metaDown = true;
|
||||
else if (altKey && altBindingsRef.current?.[keyName]) {
|
||||
haltEvent(event);
|
||||
altBindingsRef.current?.[keyName]?.();
|
||||
} else if (keyName === "ESCAPE") {
|
||||
setTimeout(
|
||||
// eslint-disable-next-line unicorn/consistent-destructuring
|
||||
() => !event.defaultPrevented && document.exitFullscreen(),
|
||||
0
|
||||
);
|
||||
if (document.pointerLockElement) document.exitPointerLock();
|
||||
else toggleFullscreen();
|
||||
} else if (
|
||||
metaDown &&
|
||||
metaCombos.has(keyName) &&
|
||||
@@ -105,29 +95,16 @@ const useGlobalKeyboardShortcuts = (): void => {
|
||||
}
|
||||
};
|
||||
const onKeyUp = (event: KeyboardEvent): void => {
|
||||
if (metaDown && fullscreen && event.key?.toUpperCase() === "META") {
|
||||
if (
|
||||
metaDown &&
|
||||
fullscreenElement === document.documentElement &&
|
||||
event.key?.toUpperCase() === "META"
|
||||
) {
|
||||
metaDown = false;
|
||||
if (metaComboUsed) metaComboUsed = false;
|
||||
else openStartMenu();
|
||||
}
|
||||
};
|
||||
const onFullScreen = ({ target }: Event): void => {
|
||||
if (target === document.documentElement) {
|
||||
try {
|
||||
if (fullscreen) {
|
||||
(navigator as NavigatorWithKeyboard)?.keyboard?.lock?.([
|
||||
"MetaLeft",
|
||||
"MetaRight",
|
||||
"Escape",
|
||||
]);
|
||||
} else {
|
||||
(navigator as NavigatorWithKeyboard)?.keyboard?.unlock?.();
|
||||
}
|
||||
} catch {
|
||||
// Ignore failure to lock keys
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("keydown", onKeyDown, {
|
||||
capture: true,
|
||||
@@ -136,16 +113,16 @@ const useGlobalKeyboardShortcuts = (): void => {
|
||||
capture: true,
|
||||
passive: true,
|
||||
});
|
||||
document.addEventListener("fullscreenchange", onFullScreen, {
|
||||
passive: true,
|
||||
});
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("keydown", onKeyDown);
|
||||
document.removeEventListener("keyup", onKeyUp);
|
||||
document.removeEventListener("fullscreenchange", onFullScreen);
|
||||
document.removeEventListener("keydown", onKeyDown, {
|
||||
capture: true,
|
||||
});
|
||||
document.removeEventListener("keyup", onKeyUp, {
|
||||
capture: true,
|
||||
});
|
||||
};
|
||||
}, [fullscreen, toggleFullscreen]);
|
||||
}, [fullscreenElement, toggleFullscreen]);
|
||||
|
||||
useEffect(() => {
|
||||
altBindingsRef.current = {
|
||||
|
||||
@@ -6,22 +6,25 @@ import { FileSystemProvider } from "contexts/fileSystem";
|
||||
import { MenuProvider } from "contexts/menu";
|
||||
import { ProcessProvider } from "contexts/process";
|
||||
import { SessionProvider } from "contexts/session";
|
||||
import { ViewportProvider } from "contexts/viewport";
|
||||
|
||||
const App = ({ Component, pageProps }: AppProps): React.ReactElement => (
|
||||
<ProcessProvider>
|
||||
<FileSystemProvider>
|
||||
<SessionProvider>
|
||||
<ErrorBoundary>
|
||||
<Metadata />
|
||||
<StyledApp>
|
||||
<MenuProvider>
|
||||
<Component {...pageProps} />
|
||||
</MenuProvider>
|
||||
</StyledApp>
|
||||
</ErrorBoundary>
|
||||
</SessionProvider>
|
||||
</FileSystemProvider>
|
||||
</ProcessProvider>
|
||||
<ViewportProvider>
|
||||
<ProcessProvider>
|
||||
<FileSystemProvider>
|
||||
<SessionProvider>
|
||||
<ErrorBoundary>
|
||||
<Metadata />
|
||||
<StyledApp>
|
||||
<MenuProvider>
|
||||
<Component {...pageProps} />
|
||||
</MenuProvider>
|
||||
</StyledApp>
|
||||
</ErrorBoundary>
|
||||
</SessionProvider>
|
||||
</FileSystemProvider>
|
||||
</ProcessProvider>
|
||||
</ViewportProvider>
|
||||
);
|
||||
|
||||
export default App;
|
||||
|
||||
Reference in New Issue
Block a user