mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-05-09 14:30:48 +00:00
Merge branch 'development' of https://github.com/Stremio/stremio-web into refactor/player-volume-playback-speed-shortcuts
This commit is contained in:
commit
3f0950df08
10 changed files with 37 additions and 127 deletions
|
|
@ -5,7 +5,7 @@ const React = require('react');
|
|||
const { useTranslation } = require('react-i18next');
|
||||
const { useCore } = require('stremio/core');
|
||||
const { Router } = require('stremio-router');
|
||||
const { Shell, Chromecast, KeyboardShortcuts, ServicesProvider, GamepadProvider } = require('stremio/services');
|
||||
const { Shell, Chromecast, ServicesProvider, GamepadProvider } = require('stremio/services');
|
||||
const { NotFound } = require('stremio/routes');
|
||||
const { FullscreenProvider, PlatformProvider, ToastProvider, TooltipProvider, ShortcutsProvider, CONSTANTS, useShell, useBinaryState, useProfile, withCoreSuspender, onFileDrop } = require('stremio/common');
|
||||
const ServicesToaster = require('./ServicesToaster');
|
||||
|
|
@ -33,13 +33,12 @@ const App = () => {
|
|||
return {
|
||||
shell: new Shell(),
|
||||
chromecast: new Chromecast(),
|
||||
keyboardShortcuts: new KeyboardShortcuts(),
|
||||
};
|
||||
}, []);
|
||||
const [shortcutModalOpen,, closeShortcutsModal, toggleShortcutModal] = useBinaryState(false);
|
||||
const [gamepadModalOpen,, closeGamepadModal, toggleGamepadModal] = useBinaryState(false);
|
||||
|
||||
const onShortcut = React.useCallback((name) => {
|
||||
const onShortcut = React.useCallback((name, combo, key) => {
|
||||
switch (name) {
|
||||
case 'shortcuts':
|
||||
toggleShortcutModal();
|
||||
|
|
@ -47,6 +46,18 @@ const App = () => {
|
|||
case 'gamepadGuide':
|
||||
toggleGamepadModal();
|
||||
break;
|
||||
case 'navigateSearch':
|
||||
window.location = '#/search';
|
||||
break;
|
||||
case 'navigateTabs': {
|
||||
const routes = ['', 'discover', 'library', 'calendar', 'addons', 'settings'];
|
||||
const index = key - 1;
|
||||
if (index in routes) window.location = `#/${routes[index]}`;
|
||||
break;
|
||||
}
|
||||
case 'navigateHistory':
|
||||
combo === 0 ? window.history.back() : window.history.forward();
|
||||
break;
|
||||
}
|
||||
}, [toggleShortcutModal, toggleGamepadModal]);
|
||||
|
||||
|
|
@ -90,12 +101,10 @@ const App = () => {
|
|||
services.chromecast.on('stateChanged', onChromecastStateChange);
|
||||
services.shell.start();
|
||||
services.chromecast.start();
|
||||
services.keyboardShortcuts.start();
|
||||
window.services = services;
|
||||
return () => {
|
||||
services.shell.stop();
|
||||
services.chromecast.stop();
|
||||
services.keyboardShortcuts.stop();
|
||||
services.chromecast.off('stateChanged', onChromecastStateChange);
|
||||
};
|
||||
}, []);
|
||||
|
|
|
|||
|
|
@ -11,16 +11,6 @@ type Props = {
|
|||
children: React.ReactNode,
|
||||
};
|
||||
|
||||
const isTextInputFocused = () => {
|
||||
const activeElement = document.activeElement;
|
||||
|
||||
return activeElement instanceof HTMLElement &&
|
||||
(activeElement.tagName === 'INPUT' ||
|
||||
activeElement.tagName === 'TEXTAREA' ||
|
||||
activeElement.tagName === 'SELECT' ||
|
||||
activeElement.isContentEditable);
|
||||
};
|
||||
|
||||
const FullscreenProvider = ({ children }: Props) => {
|
||||
const shell = useShell();
|
||||
const [settings] = useSettings();
|
||||
|
|
@ -57,12 +47,7 @@ const FullscreenProvider = ({ children }: Props) => {
|
|||
fullscreen ? exitFullscreen() : requestFullscreen();
|
||||
}, [fullscreen, exitFullscreen, requestFullscreen]);
|
||||
|
||||
const toggleFullscreenFromShortcut = useCallback(() => {
|
||||
if (isTextInputFocused()) return;
|
||||
toggleFullscreen();
|
||||
}, [toggleFullscreen]);
|
||||
|
||||
onShortcut('fullscreen', toggleFullscreenFromShortcut, [toggleFullscreenFromShortcut]);
|
||||
onShortcut('fullscreen', toggleFullscreen, [toggleFullscreen]);
|
||||
|
||||
useEffect(() => {
|
||||
const onWindowVisibilityChanged = (state: WindowVisibility) => {
|
||||
|
|
|
|||
|
|
@ -16,16 +16,26 @@ const ShortcutsContext = createContext<ShortcutsContext>({} as ShortcutsContext)
|
|||
|
||||
type Props = {
|
||||
children: JSX.Element,
|
||||
onShortcut: (name: ShortcutName) => void,
|
||||
onShortcut: (name: ShortcutName, combo: number, key: string) => void,
|
||||
};
|
||||
|
||||
const REPEAT_THROTTLE_MS = 130;
|
||||
|
||||
const isInputFocused = () => {
|
||||
const inputElements = ['INPUT', 'TEXTAREA', 'SELECT'];
|
||||
const activeElement = document.activeElement;
|
||||
|
||||
return activeElement instanceof HTMLElement &&
|
||||
(inputElements.includes(activeElement.tagName) || activeElement.isContentEditable);
|
||||
};
|
||||
|
||||
const ShortcutsProvider = ({ children, onShortcut }: Props) => {
|
||||
const listeners = useRef<Map<ShortcutName, Set<ShortcutListener>>>(new Map());
|
||||
const lastRepeatTime = useRef<Map<string, number>>(new Map());
|
||||
|
||||
const onKeyDown = useCallback(({ ctrlKey, shiftKey, altKey, metaKey, code, key, repeat }: KeyboardEvent) => {
|
||||
if (isInputFocused()) return;
|
||||
|
||||
if (repeat) {
|
||||
const now = Date.now();
|
||||
const last = lastRepeatTime.current.get(code) ?? 0;
|
||||
|
|
@ -43,7 +53,7 @@ const ShortcutsProvider = ({ children, onShortcut }: Props) => {
|
|||
const combo = combos.indexOf(keys);
|
||||
listeners.current.get(name)?.forEach((listener) => listener(combo));
|
||||
|
||||
onShortcut(name as ShortcutName);
|
||||
onShortcut(name as ShortcutName, combo, key);
|
||||
}
|
||||
}));
|
||||
}, [onShortcut]);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@
|
|||
"label": "SETTINGS_SHORTCUT_GO_TO_SEARCH",
|
||||
"combos": [["0"]]
|
||||
},
|
||||
{
|
||||
"name": "navigateHistory",
|
||||
"label": "SETTINGS_SHORTCUT_NAVIGATE_HISTORY",
|
||||
"combos": [["Backspace"], ["Ctrl", "Backspace"]]
|
||||
},
|
||||
{
|
||||
"name": "fullscreen",
|
||||
"label": "SETTINGS_SHORTCUT_FULLSCREEN",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
.combos {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
row-gap: 1rem;
|
||||
justify-content: end;
|
||||
overflow: visible;
|
||||
|
||||
.combo {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
.shortcuts-group {
|
||||
flex: 1 1 0;
|
||||
position: relative;
|
||||
width: 30rem;
|
||||
width: 35rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
.shortcut {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 2rem;
|
||||
overflow: visible;
|
||||
|
|
@ -35,7 +35,6 @@
|
|||
position: relative;
|
||||
font-size: 1rem;
|
||||
color: var(--primary-foreground-color);
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,93 +0,0 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
const EventEmitter = require('eventemitter3');
|
||||
|
||||
function KeyboardShortcuts() {
|
||||
let active = false;
|
||||
|
||||
const events = new EventEmitter();
|
||||
|
||||
function onKeyDown(event) {
|
||||
if (event.keyboardShortcutPrevented || event.target.tagName === 'INPUT' || event.ctrlKey || event.altKey || event.shiftKey || event.metaKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.code) {
|
||||
case 'Digit0': {
|
||||
event.preventDefault();
|
||||
window.location = '#/search';
|
||||
break;
|
||||
}
|
||||
case 'Digit1': {
|
||||
event.preventDefault();
|
||||
window.location = '#/';
|
||||
break;
|
||||
}
|
||||
case 'Digit2': {
|
||||
event.preventDefault();
|
||||
window.location = '#/discover';
|
||||
break;
|
||||
}
|
||||
case 'Digit3': {
|
||||
event.preventDefault();
|
||||
window.location = '#/library';
|
||||
break;
|
||||
}
|
||||
case 'Digit4': {
|
||||
event.preventDefault();
|
||||
window.location = '#/calendar';
|
||||
break;
|
||||
}
|
||||
case 'Digit5': {
|
||||
event.preventDefault();
|
||||
window.location = '#/addons';
|
||||
break;
|
||||
}
|
||||
case 'Digit6': {
|
||||
event.preventDefault();
|
||||
window.location = '#/settings';
|
||||
break;
|
||||
}
|
||||
case 'Backspace': {
|
||||
event.preventDefault();
|
||||
if (event.ctrlKey) {
|
||||
window.history.forward();
|
||||
} else {
|
||||
window.history.back();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
function onStateChanged() {
|
||||
events.emit('stateChanged');
|
||||
}
|
||||
|
||||
Object.defineProperties(this, {
|
||||
active: {
|
||||
configurable: false,
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return active;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.start = function() {
|
||||
if (active) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', onKeyDown);
|
||||
active = true;
|
||||
onStateChanged();
|
||||
};
|
||||
this.stop = function() {
|
||||
window.removeEventListener('keydown', onKeyDown);
|
||||
active = false;
|
||||
onStateChanged();
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = KeyboardShortcuts;
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
const KeyboardShortcuts = require('./KeyboardShortcuts');
|
||||
|
||||
module.exports = KeyboardShortcuts;
|
||||
1
src/services/ServicesContext/types.d.ts
vendored
1
src/services/ServicesContext/types.d.ts
vendored
|
|
@ -1,5 +1,4 @@
|
|||
type ServicesContext = {
|
||||
shell: any,
|
||||
chromecast: any,
|
||||
keyboardShortcuts: any,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
// Copyright (C) 2017-2023 Smart code 203358507
|
||||
|
||||
const Chromecast = require('./Chromecast');
|
||||
const KeyboardShortcuts = require('./KeyboardShortcuts');
|
||||
const { ServicesProvider, useServices } = require('./ServicesContext');
|
||||
const { GamepadProvider, useGamepad } = require('./GamepadContext');
|
||||
const Shell = require('./Shell');
|
||||
|
||||
module.exports = {
|
||||
Chromecast,
|
||||
KeyboardShortcuts,
|
||||
ServicesProvider,
|
||||
useServices,
|
||||
Shell,
|
||||
|
|
|
|||
Loading…
Reference in a new issue