mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-05-17 22:31:53 +00:00
feat(common): add FullscreenProvider + context module
Introduce a single, app-root-owned source of truth for fullscreen state, mirroring the existing provider pattern (ToastProvider, FileDropProvider). The provider centralizes the fullscreenchange / win-visibility-changed / keydown listeners and exposes the same [fullscreen, requestFullscreen, exitFullscreen, toggleFullscreen] tuple that consumers already destructure. Not yet wired up - both the legacy src/common/useFullscreen hook and the new module coexist. Subsequent commits mount the provider in App.js and switch consumers over. Made-with: Cursor
This commit is contained in:
parent
e2177938d1
commit
60df6860d7
4 changed files with 140 additions and 0 deletions
20
src/common/Fullscreen/FullscreenContext.ts
Normal file
20
src/common/Fullscreen/FullscreenContext.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright (C) 2017-2023 Smart code 203358507
|
||||||
|
|
||||||
|
import { createContext } from 'react';
|
||||||
|
|
||||||
|
export type FullscreenContextValue = readonly [
|
||||||
|
boolean,
|
||||||
|
() => Promise<void> | void,
|
||||||
|
() => void,
|
||||||
|
() => void,
|
||||||
|
];
|
||||||
|
|
||||||
|
const noop = () => { /* no-op */ };
|
||||||
|
|
||||||
|
const defaultValue: FullscreenContextValue = [false, noop, noop, noop];
|
||||||
|
|
||||||
|
const FullscreenContext = createContext<FullscreenContextValue>(defaultValue);
|
||||||
|
|
||||||
|
FullscreenContext.displayName = 'FullscreenContext';
|
||||||
|
|
||||||
|
export default FullscreenContext;
|
||||||
105
src/common/Fullscreen/FullscreenProvider.tsx
Normal file
105
src/common/Fullscreen/FullscreenProvider.tsx
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
// Copyright (C) 2017-2023 Smart code 203358507
|
||||||
|
|
||||||
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
import useShell, { type WindowVisibility } from '../useShell';
|
||||||
|
import useSettings from '../useSettings';
|
||||||
|
import FullscreenContext, { type FullscreenContextValue } from './FullscreenContext';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: React.ReactNode,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Single source of truth for fullscreen state. Mounted once at the app root so
|
||||||
|
// the value survives route remounts (fixes desync where switching tabs while in
|
||||||
|
// fullscreen would leave the UI thinking we were still windowed).
|
||||||
|
const FullscreenProvider = ({ children }: Props) => {
|
||||||
|
const shell = useShell();
|
||||||
|
const [settings] = useSettings();
|
||||||
|
|
||||||
|
const [fullscreen, setFullscreen] = useState<boolean>(() => {
|
||||||
|
if (typeof document === 'undefined') return false;
|
||||||
|
return document.fullscreenElement === document.documentElement;
|
||||||
|
});
|
||||||
|
|
||||||
|
const requestFullscreen = useCallback(async () => {
|
||||||
|
if (shell.active) {
|
||||||
|
shell.send('win-set-visibility', { fullscreen: true });
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await document.documentElement.requestFullscreen();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error enabling fullscreen', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [shell]);
|
||||||
|
|
||||||
|
const exitFullscreen = useCallback(() => {
|
||||||
|
if (shell.active) {
|
||||||
|
shell.send('win-set-visibility', { fullscreen: false });
|
||||||
|
} else {
|
||||||
|
if (document.fullscreenElement === document.documentElement) {
|
||||||
|
document.exitFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [shell]);
|
||||||
|
|
||||||
|
const toggleFullscreen = useCallback(() => {
|
||||||
|
fullscreen ? exitFullscreen() : requestFullscreen();
|
||||||
|
}, [fullscreen, exitFullscreen, requestFullscreen]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onWindowVisibilityChanged = (state: WindowVisibility) => {
|
||||||
|
setFullscreen(state.isFullscreen === true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFullscreenChange = () => {
|
||||||
|
setFullscreen(document.fullscreenElement === document.documentElement);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onKeyDown = (event: KeyboardEvent) => {
|
||||||
|
const activeElement = document.activeElement as HTMLElement;
|
||||||
|
|
||||||
|
const inputFocused =
|
||||||
|
activeElement &&
|
||||||
|
(activeElement.tagName === 'INPUT' ||
|
||||||
|
activeElement.tagName === 'TEXTAREA' ||
|
||||||
|
activeElement.tagName === 'SELECT' ||
|
||||||
|
activeElement.isContentEditable);
|
||||||
|
|
||||||
|
if (event.code === 'Escape' && settings.escExitFullscreen) {
|
||||||
|
exitFullscreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.code === 'KeyF' && !inputFocused) {
|
||||||
|
toggleFullscreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.code === 'F11' && shell.active) {
|
||||||
|
toggleFullscreen();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
shell.on('win-visibility-changed', onWindowVisibilityChanged);
|
||||||
|
document.addEventListener('keydown', onKeyDown);
|
||||||
|
document.addEventListener('fullscreenchange', onFullscreenChange);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
shell.off('win-visibility-changed', onWindowVisibilityChanged);
|
||||||
|
document.removeEventListener('keydown', onKeyDown);
|
||||||
|
document.removeEventListener('fullscreenchange', onFullscreenChange);
|
||||||
|
};
|
||||||
|
}, [shell, settings.escExitFullscreen, toggleFullscreen, exitFullscreen]);
|
||||||
|
|
||||||
|
const value = useMemo<FullscreenContextValue>(
|
||||||
|
() => [fullscreen, requestFullscreen, exitFullscreen, toggleFullscreen],
|
||||||
|
[fullscreen, requestFullscreen, exitFullscreen, toggleFullscreen]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FullscreenContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</FullscreenContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FullscreenProvider;
|
||||||
7
src/common/Fullscreen/index.ts
Normal file
7
src/common/Fullscreen/index.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
// Copyright (C) 2017-2023 Smart code 203358507
|
||||||
|
|
||||||
|
import FullscreenProvider from './FullscreenProvider';
|
||||||
|
import useFullscreen from './useFullscreen';
|
||||||
|
|
||||||
|
export { FullscreenProvider, useFullscreen };
|
||||||
|
export default useFullscreen;
|
||||||
8
src/common/Fullscreen/useFullscreen.ts
Normal file
8
src/common/Fullscreen/useFullscreen.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Copyright (C) 2017-2023 Smart code 203358507
|
||||||
|
|
||||||
|
import { useContext } from 'react';
|
||||||
|
import FullscreenContext from './FullscreenContext';
|
||||||
|
|
||||||
|
const useFullscreen = () => useContext(FullscreenContext);
|
||||||
|
|
||||||
|
export default useFullscreen;
|
||||||
Loading…
Reference in a new issue