From e0d1662f86fd91c71fe1555c8c9636072086e43e Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 1 Apr 2025 12:23:42 +0200 Subject: [PATCH 1/3] feat(shell): implement escape key fullscreen behavior --- src/common/index.js | 4 +- src/common/useFullscreen.js | 32 --------- src/common/useFullscreen.ts | 66 +++++++++++++++++++ .../useSettings.js => common/useSettings.ts} | 16 +++-- src/common/useShell.ts | 6 +- .../HorizontalNavBar/HorizontalNavBar.js | 2 +- src/routes/Player/Player.js | 6 +- src/routes/Player/useSettings.d.ts | 2 - src/routes/Settings/Settings.js | 26 ++++---- 9 files changed, 99 insertions(+), 61 deletions(-) delete mode 100644 src/common/useFullscreen.js create mode 100644 src/common/useFullscreen.ts rename src/{routes/Player/useSettings.js => common/useSettings.ts} (54%) delete mode 100644 src/routes/Player/useSettings.d.ts diff --git a/src/common/index.js b/src/common/index.js index 82f7a6a0c..55ccfe045 100644 --- a/src/common/index.js +++ b/src/common/index.js @@ -14,12 +14,13 @@ const languages = require('./languages'); const routesRegexp = require('./routesRegexp'); const useAnimationFrame = require('./useAnimationFrame'); const useBinaryState = require('./useBinaryState'); -const useFullscreen = require('./useFullscreen'); +const { default: useFullscreen } = require('./useFullscreen'); const useLiveRef = require('./useLiveRef'); const useModelState = require('./useModelState'); const useNotifications = require('./useNotifications'); const useOnScrollToBottom = require('./useOnScrollToBottom'); const useProfile = require('./useProfile'); +const { default: useSettings } = require('./useSettings'); const { default: useShell } = require('./useShell'); const useStreamingServer = require('./useStreamingServer'); const useTorrent = require('./useTorrent'); @@ -52,6 +53,7 @@ module.exports = { useNotifications, useOnScrollToBottom, useProfile, + useSettings, useShell, useStreamingServer, useTorrent, diff --git a/src/common/useFullscreen.js b/src/common/useFullscreen.js deleted file mode 100644 index caf7a0219..000000000 --- a/src/common/useFullscreen.js +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2017-2023 Smart code 203358507 - -const React = require('react'); - -const useFullscreen = () => { - const [fullscreen, setFullscreen] = React.useState(document.fullscreenElement === document.documentElement); - const requestFullscreen = React.useCallback(() => { - document.documentElement.requestFullscreen(); - }, []); - const exitFullscreen = React.useCallback(() => { - document.exitFullscreen(); - }, []); - const toggleFullscreen = React.useCallback(() => { - if (fullscreen) { - exitFullscreen(); - } else { - requestFullscreen(); - } - }, [fullscreen]); - React.useEffect(() => { - const onFullscreenChange = () => { - setFullscreen(document.fullscreenElement === document.documentElement); - }; - document.addEventListener('fullscreenchange', onFullscreenChange); - return () => { - document.removeEventListener('fullscreenchange', onFullscreenChange); - }; - }, []); - return [fullscreen, requestFullscreen, exitFullscreen, toggleFullscreen]; -}; - -module.exports = useFullscreen; diff --git a/src/common/useFullscreen.ts b/src/common/useFullscreen.ts new file mode 100644 index 000000000..5ccb69790 --- /dev/null +++ b/src/common/useFullscreen.ts @@ -0,0 +1,66 @@ +// Copyright (C) 2017-2023 Smart code 203358507 + +import { useCallback, useEffect, useState } from 'react'; +import useShell, { type WindowVisibilityState } from './useShell'; +import useSettings from './useSettings'; + +const useFullscreen = () => { + const shell = useShell(); + const [settings] = useSettings(); + + const [fullscreen, setFullscreen] = useState(false); + + const requestFullscreen = useCallback(() => { + if (shell.active) { + shell.send('win-set-visibility', { fullscreen: true }); + } else { + document.documentElement.requestFullscreen(); + } + }, []); + + const exitFullscreen = useCallback(() => { + if (shell.active) { + shell.send('win-set-visibility', { fullscreen: false }); + } else { + document.exitFullscreen(); + } + }, []); + + const toggleFullscreen = useCallback(() => { + fullscreen ? exitFullscreen() : requestFullscreen(); + }, [fullscreen]); + + useEffect(() => { + const onWindowVisibilityChanged = (state: WindowVisibilityState) => { + setFullscreen(state.isFullscreen === true); + }; + + const onFullscreenChange = () => { + setFullscreen(document.fullscreenElement === document.documentElement); + }; + + const onKeyDown = (event: KeyboardEvent) => { + if (event.code === 'Escape' && settings.escExitFullscreen) { + exitFullscreen(); + } + + if ((event.code === 'F11' || event.code === 'KeyF') && 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); + }; + }, [settings.escExitFullscreen]); + + return [fullscreen, requestFullscreen, exitFullscreen, toggleFullscreen]; +}; + +export default useFullscreen; diff --git a/src/routes/Player/useSettings.js b/src/common/useSettings.ts similarity index 54% rename from src/routes/Player/useSettings.js rename to src/common/useSettings.ts index e6976cd9d..16dc2cd2f 100644 --- a/src/routes/Player/useSettings.js +++ b/src/common/useSettings.ts @@ -1,13 +1,14 @@ -// Copyright (C) 2017-2023 Smart code 203358507 +// Copyright (C) 2017-2025 Smart code 203358507 -const React = require('react'); -const { useServices } = require('stremio/services'); -const { useProfile } = require('stremio/common'); +import { useCallback } from 'react'; +import { useServices } from 'stremio/services'; +import useProfile from './useProfile'; -const useSettings = () => { +const useSettings = (): [Settings, (settings: Settings) => void] => { const { core } = useServices(); const profile = useProfile(); - const updateSettings = React.useCallback((settings) => { + + const updateSettings = useCallback((settings: Settings) => { core.transport.dispatch({ action: 'Ctx', args: { @@ -19,7 +20,8 @@ const useSettings = () => { } }); }, [profile]); + return [profile.settings, updateSettings]; }; -module.exports = useSettings; +export default useSettings; diff --git a/src/common/useShell.ts b/src/common/useShell.ts index 1a7bcb6ee..7ef7ce0a4 100644 --- a/src/common/useShell.ts +++ b/src/common/useShell.ts @@ -17,6 +17,10 @@ type ShellEvent = { args: string[]; }; +export type WindowVisibilityState = { + isFullscreen: boolean; +}; + const createId = () => Math.floor(Math.random() * 9999) + 1; const useShell = () => { @@ -28,7 +32,7 @@ const useShell = () => { events.off(name, listener); }; - const send = (method: string, ...args: (string | number)[]) => { + const send = (method: string, ...args: (string | number | object)[]) => { try { transport?.postMessage(JSON.stringify({ id: createId(), diff --git a/src/components/NavBar/HorizontalNavBar/HorizontalNavBar.js b/src/components/NavBar/HorizontalNavBar/HorizontalNavBar.js index 0cedd8638..6be35cd5d 100644 --- a/src/components/NavBar/HorizontalNavBar/HorizontalNavBar.js +++ b/src/components/NavBar/HorizontalNavBar/HorizontalNavBar.js @@ -5,7 +5,7 @@ const PropTypes = require('prop-types'); const classnames = require('classnames'); const { default: Icon } = require('@stremio/stremio-icons/react'); const { Button, Image } = require('stremio/components'); -const useFullscreen = require('stremio/common/useFullscreen'); +const { default: useFullscreen } = require('stremio/common/useFullscreen'); const usePWA = require('stremio/common/usePWA'); const SearchBar = require('./SearchBar'); const NavMenu = require('./NavMenu'); diff --git a/src/routes/Player/Player.js b/src/routes/Player/Player.js index 1369f814a..e631e3000 100644 --- a/src/routes/Player/Player.js +++ b/src/routes/Player/Player.js @@ -8,7 +8,7 @@ const langs = require('langs'); const { useTranslation } = require('react-i18next'); const { useRouteFocused } = require('stremio-router'); const { useServices } = require('stremio/services'); -const { onFileDrop, useFullscreen, useBinaryState, useToast, useStreamingServer, withCoreSuspender, CONSTANTS } = require('stremio/common'); +const { onFileDrop, useSettings, useFullscreen, useBinaryState, useToast, useStreamingServer, withCoreSuspender, CONSTANTS } = require('stremio/common'); const { HorizontalNavBar, Transition, ContextMenu } = require('stremio/components'); const BufferingLoader = require('./BufferingLoader'); const VolumeChangeIndicator = require('./VolumeChangeIndicator'); @@ -23,7 +23,6 @@ const SpeedMenu = require('./SpeedMenu'); const { default: SideDrawerButton } = require('./SideDrawerButton'); const { default: SideDrawer } = require('./SideDrawer'); const usePlayer = require('./usePlayer'); -const useSettings = require('./useSettings'); const useStatistics = require('./useStatistics'); const useVideo = require('./useVideo'); const styles = require('./styles'); @@ -565,6 +564,7 @@ const Player = ({ urlParams, queryParams }) => { } case 'Escape': { closeMenus(); + !settings.escExitFullscreen && window.history.back(); break; } } @@ -595,7 +595,7 @@ const Player = ({ urlParams, queryParams }) => { window.removeEventListener('keyup', onKeyUp); window.removeEventListener('wheel', onWheel); }; - }, [player.metaItem, player.selected, streamingServer.statistics, settings.seekTimeDuration, settings.seekShortTimeDuration, routeFocused, menusOpen, nextVideoPopupOpen, video.state.paused, video.state.time, video.state.volume, video.state.audioTracks, video.state.subtitlesTracks, video.state.extraSubtitlesTracks, video.state.playbackSpeed, toggleSubtitlesMenu, toggleStatisticsMenu, toggleSideDrawer]); + }, [player.metaItem, player.selected, streamingServer.statistics, settings.seekTimeDuration, settings.seekShortTimeDuration, settings.escExitFullscreen, routeFocused, menusOpen, nextVideoPopupOpen, video.state.paused, video.state.time, video.state.volume, video.state.audioTracks, video.state.subtitlesTracks, video.state.extraSubtitlesTracks, video.state.playbackSpeed, toggleSubtitlesMenu, toggleStatisticsMenu, toggleSideDrawer]); React.useEffect(() => { video.events.on('error', onError); diff --git a/src/routes/Player/useSettings.d.ts b/src/routes/Player/useSettings.d.ts deleted file mode 100644 index e3daf32cd..000000000 --- a/src/routes/Player/useSettings.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -declare const useSettings: () => [Settings, (settings: any) => void]; -export = useSettings; diff --git a/src/routes/Settings/Settings.js b/src/routes/Settings/Settings.js index bddbcbe2a..1d695c177 100644 --- a/src/routes/Settings/Settings.js +++ b/src/routes/Settings/Settings.js @@ -342,6 +342,18 @@ const Settings = () => { /> } + { + shell.active && +
+
+
{ t('SETTINGS_FULLSCREEN_EXIT') }
+
+ +
+ }
{ t('SETTINGS_BLUR_UNWATCHED_IMAGE') }
@@ -368,20 +380,6 @@ const Settings = () => { {...subtitlesLanguageSelect} />
- { - shell.active ? -
-
-
{ t('SETTINGS_FULLSCREEN_EXIT') }
-
- -
- : - null - }
{ t('SETTINGS_SUBTITLES_SIZE') }
From ad11609bee15f34c37ada475a695ab03a96a2073 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 1 Apr 2025 14:43:47 +0200 Subject: [PATCH 2/3] fix(NavMenu): incorrect import --- .../NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js b/src/components/NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js index 59946165a..de0a02212 100644 --- a/src/components/NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js +++ b/src/components/NavBar/HorizontalNavBar/NavMenu/NavMenuContent.js @@ -7,7 +7,7 @@ const { useTranslation } = require('react-i18next'); const { default: Icon } = require('@stremio/stremio-icons/react'); const { useServices } = require('stremio/services'); const { Button } = require('stremio/components'); -const useFullscreen = require('stremio/common/useFullscreen'); +const { default: useFullscreen } = require('stremio/common/useFullscreen'); const useProfile = require('stremio/common/useProfile'); const usePWA = require('stremio/common/usePWA'); const useTorrent = require('stremio/common/useTorrent'); From 5dcd6f48cdcdb43ad5d41d56905b53b7c19451b9 Mon Sep 17 00:00:00 2001 From: Tim Date: Tue, 1 Apr 2025 15:20:23 +0200 Subject: [PATCH 3/3] fix(useFullscreen): remove handling of F keyboard key --- src/common/useFullscreen.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/useFullscreen.ts b/src/common/useFullscreen.ts index 5ccb69790..d81003266 100644 --- a/src/common/useFullscreen.ts +++ b/src/common/useFullscreen.ts @@ -44,7 +44,7 @@ const useFullscreen = () => { exitFullscreen(); } - if ((event.code === 'F11' || event.code === 'KeyF') && shell.active) { + if (event.code === 'F11' && shell.active) { toggleFullscreen(); } };