mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-13 19:33:46 +00:00
refactor(navigation): suppress the Space hotkey by prop
Replaces the view hotkey store with a suppressSpaceHotkey prop on ViewNavigationMenu, alongside the suppressSettings prop which already exists for exactly this purpose: a view opting out of a navigation chrome behaviour. The store had one advantage, that it covered the preset route without the router naming the view. That is a single expression in PresetView, and paying for it with a global claim was the wrong trade: the claim was lifecycle state that had to be released on unmount to avoid stranding Space for the whole client, where a prop simply describes the route. Verified both routes: /teleprompter and a locked preset targeting the teleprompter leave Space to the view, while the timer and a preset targeting the timer still open the navigation menu with it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cb8RVPNQ2ETPJxdy4b8CHf
This commit is contained in:
@@ -100,7 +100,7 @@ export default function AppRouter() {
|
||||
path='teleprompter'
|
||||
element={
|
||||
<ViewLoader>
|
||||
<ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} />
|
||||
<ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} suppressSpaceHotkey />
|
||||
<Teleprompter />
|
||||
</ViewLoader>
|
||||
}
|
||||
@@ -225,7 +225,11 @@ function PresetView() {
|
||||
const Component = PresetViewMap[preset.target as OntimeViewPresettable];
|
||||
return (
|
||||
<PresetContext value={preset}>
|
||||
<ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} suppressSettings />
|
||||
<ViewNavigationMenu
|
||||
isNavigationLocked={getIsNavigationLocked()}
|
||||
suppressSettings
|
||||
suppressSpaceHotkey={preset.target === OntimeView.Teleprompter}
|
||||
/>
|
||||
{Component ? <Component /> : <NotFound />}
|
||||
</PresetContext>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,6 @@ import { memo } from 'react';
|
||||
import { useSearchParams } from 'react-router';
|
||||
|
||||
import { hasCustomParams } from '../../stores/savedViewParams';
|
||||
import { useViewHotkeysStore } from '../../stores/viewHotkeys';
|
||||
import { useViewParamsEditorStore } from '../view-params-editor/viewParamsEditor.store';
|
||||
import FloatingNavigation from './floating-navigation/FloatingNavigation';
|
||||
import NavigationMenu from './NavigationMenu';
|
||||
@@ -14,10 +13,12 @@ interface ViewNavigationMenuProps {
|
||||
isNavigationLocked?: boolean;
|
||||
/** prevent showing settings */
|
||||
suppressSettings?: boolean;
|
||||
/** leave Space to the view, for views which need the key themselves */
|
||||
suppressSpaceHotkey?: boolean;
|
||||
}
|
||||
|
||||
export default memo(ViewNavigationMenu);
|
||||
function ViewNavigationMenu({ isNavigationLocked, suppressSettings }: ViewNavigationMenuProps) {
|
||||
function ViewNavigationMenu({ isNavigationLocked, suppressSettings, suppressSpaceHotkey }: ViewNavigationMenuProps) {
|
||||
const [isMenuOpen, menuHandler] = useDisclosure();
|
||||
const { open: showEditFormDrawer } = useViewParamsEditorStore();
|
||||
const [searchParams] = useSearchParams();
|
||||
@@ -27,9 +28,7 @@ function ViewNavigationMenu({ isNavigationLocked, suppressSettings }: ViewNaviga
|
||||
[
|
||||
'Space',
|
||||
() => {
|
||||
if (isNavigationLocked) return;
|
||||
// a view can take over Space for itself, see viewHotkeys store
|
||||
if (useViewHotkeysStore.getState().spaceClaimed) return;
|
||||
if (isNavigationLocked || suppressSpaceHotkey) return;
|
||||
menuHandler.toggle();
|
||||
},
|
||||
{ preventDefault: true },
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { create } from 'zustand';
|
||||
|
||||
interface ViewHotkeysStore {
|
||||
/** a view has taken over the Space key and the navigation menu must not react to it */
|
||||
spaceClaimed: boolean;
|
||||
claimSpace: () => void;
|
||||
releaseSpace: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lets a view take ownership of a hotkey which the view chrome also binds.
|
||||
*
|
||||
* The navigation menu binds Space at document level, but the teleprompter needs
|
||||
* it for playback, which is the convention every prompter and every foot pedal
|
||||
* follows. Prop threading does not work here because AppRouter renders
|
||||
* ViewNavigationMenu generically for presets, so the claim lives in a store
|
||||
* which the view sets on mount and clears on unmount.
|
||||
*/
|
||||
export const useViewHotkeysStore = create<ViewHotkeysStore>((set) => ({
|
||||
spaceClaimed: false,
|
||||
claimSpace: () => set({ spaceClaimed: true }),
|
||||
releaseSpace: () => set({ spaceClaimed: false }),
|
||||
}));
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
import { useViewParamsEditorStore } from '../../common/components/view-params-editor/viewParamsEditor.store';
|
||||
import { useViewHotkeysStore } from '../../common/stores/viewHotkeys';
|
||||
import { resolveTeleprompterAction } from './teleprompter.keymap';
|
||||
import type { TeleprompterAction, TeleprompterController } from './teleprompter.types';
|
||||
|
||||
@@ -18,22 +17,15 @@ const ignoredTags = new Set(['INPUT', 'TEXTAREA', 'SELECT']);
|
||||
/**
|
||||
* Binds the prompter keymap.
|
||||
*
|
||||
* Space is claimed from the navigation menu for the lifetime of the view: it is
|
||||
* the universal run/stop key on prompter software and on the centre pedal of
|
||||
* every three pedal controller, so the view cannot leave it to the menu.
|
||||
* Space is the universal run/stop key on prompter software and the centre pedal
|
||||
* of every three pedal controller, so this view cannot leave it to the
|
||||
* navigation menu. The routes which render the teleprompter pass
|
||||
* suppressSpaceHotkey to ViewNavigationMenu so the menu stands down.
|
||||
*/
|
||||
export function useTeleprompterControls(args: UseTeleprompterControlsArgs) {
|
||||
const argsRef = useRef(args);
|
||||
argsRef.current = args;
|
||||
|
||||
const claimSpace = useViewHotkeysStore((state) => state.claimSpace);
|
||||
const releaseSpace = useViewHotkeysStore((state) => state.releaseSpace);
|
||||
|
||||
useEffect(() => {
|
||||
claimSpace();
|
||||
return releaseSpace;
|
||||
}, [claimSpace, releaseSpace]);
|
||||
|
||||
useEffect(() => {
|
||||
function applyAction(action: TeleprompterAction) {
|
||||
const { controller, onFlip, onFontSize, onResetFontSize, onToggleHelp } = argsRef.current;
|
||||
|
||||
Reference in New Issue
Block a user