refactor: improve navigation and settings locks

This commit is contained in:
Carlos Valente
2025-08-11 06:35:54 +02:00
committed by Carlos Valente
parent 836fa52757
commit efceb930e5
8 changed files with 49 additions and 37 deletions
+10 -10
View File
@@ -11,7 +11,7 @@ import Log from './features/log/Log';
import Loader from './views/common/loader/Loader'; import Loader from './views/common/loader/Loader';
import NotFound from './views/common/not-found/NotFound'; import NotFound from './views/common/not-found/NotFound';
import ViewLoader from './views/ViewLoader'; import ViewLoader from './views/ViewLoader';
import { getIsViewLocked, sessionScope } from './externals'; import { getIsNavigationLocked, sessionScope } from './externals';
import { initializeSentry } from './sentry.config'; import { initializeSentry } from './sentry.config';
const Timer = lazy(() => import('./views/timer/Timer')); const Timer = lazy(() => import('./views/timer/Timer'));
@@ -45,7 +45,7 @@ export default function AppRouter() {
path='timer' path='timer'
element={ element={
<ViewLoader> <ViewLoader>
<ViewNavigationMenu isViewLocked={getIsViewLocked()} /> <ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} />
<Timer /> <Timer />
</ViewLoader> </ViewLoader>
} }
@@ -54,7 +54,7 @@ export default function AppRouter() {
path='countdown' path='countdown'
element={ element={
<ViewLoader> <ViewLoader>
<ViewNavigationMenu isViewLocked={getIsViewLocked()} /> <ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} />
<Countdown /> <Countdown />
</ViewLoader> </ViewLoader>
} }
@@ -63,7 +63,7 @@ export default function AppRouter() {
path='backstage' path='backstage'
element={ element={
<ViewLoader> <ViewLoader>
<ViewNavigationMenu isViewLocked={getIsViewLocked()} /> <ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} />
<Backstage /> <Backstage />
</ViewLoader> </ViewLoader>
} }
@@ -72,7 +72,7 @@ export default function AppRouter() {
path='studio' path='studio'
element={ element={
<ViewLoader> <ViewLoader>
<ViewNavigationMenu isViewLocked={getIsViewLocked()} /> <ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} />
<StudioClock /> <StudioClock />
</ViewLoader> </ViewLoader>
} }
@@ -81,7 +81,7 @@ export default function AppRouter() {
path='timeline' path='timeline'
element={ element={
<ViewLoader> <ViewLoader>
<ViewNavigationMenu isViewLocked={getIsViewLocked()} /> <ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} />
<Timeline /> <Timeline />
</ViewLoader> </ViewLoader>
} }
@@ -90,7 +90,7 @@ export default function AppRouter() {
path='info' path='info'
element={ element={
<ViewLoader> <ViewLoader>
<ViewNavigationMenu suppressSettings isViewLocked={getIsViewLocked()} /> <ViewNavigationMenu suppressSettings isNavigationLocked={getIsNavigationLocked()} />
<ProjectInfo /> <ProjectInfo />
</ViewLoader> </ViewLoader>
} }
@@ -198,7 +198,7 @@ function PresetView() {
if (!preset) { if (!preset) {
return ( return (
<> <>
<ViewNavigationMenu isViewLocked={!showNav} suppressSettings /> <ViewNavigationMenu isNavigationLocked={!showNav} suppressSettings />
<NotFound /> <NotFound />
</> </>
); );
@@ -212,7 +212,7 @@ function PresetView() {
return ( return (
<PresetContext value={preset}> <PresetContext value={preset}>
{preset.target !== OntimeView.Cuesheet && ( {preset.target !== OntimeView.Cuesheet && (
<ViewNavigationMenu isViewLocked={getIsViewLocked()} suppressSettings /> <ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} suppressSettings />
)} )}
{Component ? <Component /> : <NotFound />} {Component ? <Component /> : <NotFound />}
</PresetContext> </PresetContext>
@@ -243,7 +243,7 @@ function RedirectPreset() {
return ( return (
<> <>
<ViewNavigationMenu isViewLocked={getIsViewLocked()} suppressSettings /> <ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} suppressSettings />
<NotFound /> <NotFound />
</> </>
); );
@@ -8,14 +8,14 @@ import ViewLockedIcon from './view-locked-icon/ViewLockedIcon';
import NavigationMenu from './NavigationMenu'; import NavigationMenu from './NavigationMenu';
interface ViewNavigationMenuProps { interface ViewNavigationMenuProps {
/** prevent navigation and settings*/ /** prevent navigation */
isViewLocked?: boolean; isNavigationLocked?: boolean;
/** prevent showing settings */ /** prevent showing settings */
suppressSettings?: boolean; suppressSettings?: boolean;
} }
export default memo(ViewNavigationMenu); export default memo(ViewNavigationMenu);
function ViewNavigationMenu({ isViewLocked, suppressSettings }: ViewNavigationMenuProps) { function ViewNavigationMenu({ isNavigationLocked, suppressSettings }: ViewNavigationMenuProps) {
const [isMenuOpen, menuHandler] = useDisclosure(); const [isMenuOpen, menuHandler] = useDisclosure();
const { open: showEditFormDrawer } = useViewParamsEditorStore(); const { open: showEditFormDrawer } = useViewParamsEditorStore();
@@ -23,7 +23,7 @@ function ViewNavigationMenu({ isViewLocked, suppressSettings }: ViewNavigationMe
[ [
'Space', 'Space',
() => { () => {
if (isViewLocked) return; if (isNavigationLocked) return;
menuHandler.toggle(); menuHandler.toggle();
}, },
{ preventDefault: true }, { preventDefault: true },
@@ -31,24 +31,24 @@ function ViewNavigationMenu({ isViewLocked, suppressSettings }: ViewNavigationMe
[ [
'mod + ,', 'mod + ,',
() => { () => {
if (isViewLocked || suppressSettings) return; if (suppressSettings) return;
showEditFormDrawer(); showEditFormDrawer();
}, },
{ preventDefault: true }, { preventDefault: true },
], ],
]); ]);
if (isViewLocked) { if (isNavigationLocked && suppressSettings) {
return <ViewLockedIcon />; return <ViewLockedIcon />;
} }
return ( return (
<> <>
<FloatingNavigation <FloatingNavigation
toggleMenu={menuHandler.toggle} toggleMenu={isNavigationLocked ? undefined : menuHandler.toggle}
toggleSettings={suppressSettings ? undefined : () => showEditFormDrawer()} toggleSettings={suppressSettings ? undefined : showEditFormDrawer}
/> />
<NavigationMenu isOpen={isMenuOpen} onClose={menuHandler.close} /> {!isNavigationLocked && <NavigationMenu isOpen={isMenuOpen} onClose={menuHandler.close} />}
</> </>
); );
} }
@@ -8,7 +8,7 @@ import IconButton from '../../buttons/IconButton';
import style from './FloatingNavigation.module.scss'; import style from './FloatingNavigation.module.scss';
interface FloatingNavigationProps { interface FloatingNavigationProps {
toggleMenu: () => void; toggleMenu?: () => void;
toggleSettings?: () => void; toggleSettings?: () => void;
} }
@@ -20,15 +20,17 @@ export default function FloatingNavigation({ toggleMenu, toggleSettings }: Float
id='fadeable-navigation' id='fadeable-navigation'
className={cx([style.fadeable, style.buttonContainer, !isButtonShown && style.hidden])} className={cx([style.fadeable, style.buttonContainer, !isButtonShown && style.hidden])}
> >
<IconButton {toggleMenu && (
variant='subtle-white' <IconButton
size='xlarge' variant='subtle-white'
onClick={toggleMenu} size='xlarge'
aria-label='toggle menu' onClick={toggleMenu}
data-testid='navigation__toggle-menu' aria-label='toggle menu'
> data-testid='navigation__toggle-menu'
<IoApps /> >
</IconButton> <IoApps />
</IconButton>
)}
{toggleSettings && ( {toggleSettings && (
<IconButton <IconButton
variant='subtle-white' variant='subtle-white'
+1 -1
View File
@@ -79,7 +79,7 @@ function resolveBaseURI(): string {
* Resolves a session scope for the session * Resolves a session scope for the session
*/ */
export const sessionScope = resolveSessionScope(); export const sessionScope = resolveSessionScope();
export const getIsViewLocked = () => window.location.search.includes('n=1'); export const getIsNavigationLocked = () => new URLSearchParams(window.location.search).get('n') === '1';
/** /**
* The session scope is read from the cookie and will only exist if the app is password protected * The session scope is read from the cookie and will only exist if the app is password protected
@@ -1,13 +1,13 @@
import ViewNavigationMenu from '../../common/components/navigation-menu/ViewNavigationMenu'; import ViewNavigationMenu from '../../common/components/navigation-menu/ViewNavigationMenu';
import ProtectRoute from '../../common/components/protect-route/ProtectRoute'; import ProtectRoute from '../../common/components/protect-route/ProtectRoute';
import { getIsViewLocked } from '../../externals'; import { getIsNavigationLocked } from '../../externals';
import Operator from './Operator'; import Operator from './Operator';
export default function OperatorExport() { export default function OperatorExport() {
return ( return (
<ProtectRoute permission='operator'> <ProtectRoute permission='operator'>
<ViewNavigationMenu isViewLocked={getIsViewLocked()} /> <ViewNavigationMenu isNavigationLocked={getIsNavigationLocked()} />
<Operator /> <Operator />
</ProtectRoute> </ProtectRoute>
); );
@@ -55,10 +55,11 @@ function RundownExport() {
<ProtectRoute permission='editor'> <ProtectRoute permission='editor'>
<div className={cx([style.rundownExport, isExtracted && style.extracted])} data-testid='panel-rundown'> <div className={cx([style.rundownExport, isExtracted && style.extracted])} data-testid='panel-rundown'>
<FinderPlacement /> <FinderPlacement />
{isExtracted && <ViewNavigationMenu suppressSettings />}
<div className={style.rundown}> <div className={style.rundown}>
<div className={style.list}> <div className={style.list}>
<ErrorBoundary> <ErrorBoundary>
<Corner onClick={(event) => handleLinks('rundown', event)} /> {!isExtracted && <Corner onClick={(event) => handleLinks('rundown', event)} />}
<RundownContextMenu> <RundownContextMenu>
<RundownWrapper /> <RundownWrapper />
</RundownContextMenu> </RundownContextMenu>
@@ -227,16 +227,25 @@ export default function GenerateLinkForm({ hostOptions, pathOptions, presets, is
name='lockNav' name='lockNav'
checked={watch('lockNav')} checked={watch('lockNav')}
onCheckedChange={(checked) => setValue('lockNav', checked, { shouldDirty: true })} onCheckedChange={(checked) => setValue('lockNav', checked, { shouldDirty: true })}
disabled={watch('lockConfig')}
/> />
</Panel.ListItem> </Panel.ListItem>
{watch('path') !== OntimeView.Cuesheet && ( {watch('path') !== OntimeView.Cuesheet && (
<Panel.ListItem> <Panel.ListItem>
<Panel.Field title='Lock configuration' description='Whether to hide the configuration panel' /> <Panel.Field
title='Lock configuration'
description='Whether to hide the configuration panel (also hides navigation)'
/>
<Switch <Switch
size='large' size='large'
name='lockConfig' name='lockConfig'
checked={watch('lockConfig')} checked={watch('lockConfig')}
onCheckedChange={(checked) => setValue('lockConfig', checked, { shouldDirty: true })} onCheckedChange={(checked) => {
if (checked) {
setValue('lockNav', checked, { shouldDirty: true });
}
setValue('lockConfig', checked, { shouldDirty: true });
}}
/> />
</Panel.ListItem> </Panel.ListItem>
)} )}
@@ -4,7 +4,7 @@ import { useDisclosure } from '@mantine/hooks';
import IconButton from '../../common/components/buttons/IconButton'; import IconButton from '../../common/components/buttons/IconButton';
import NavigationMenu from '../../common/components/navigation-menu/NavigationMenu'; import NavigationMenu from '../../common/components/navigation-menu/NavigationMenu';
import { useWindowTitle } from '../../common/hooks/useWindowTitle'; import { useWindowTitle } from '../../common/hooks/useWindowTitle';
import { getIsViewLocked } from '../../externals'; import { getIsNavigationLocked } from '../../externals';
import CuesheetOverview from '../../features/overview/CuesheetOverview'; import CuesheetOverview from '../../features/overview/CuesheetOverview';
import CuesheetEditModal from './cuesheet-edit-modal/CuesheetEditModal'; import CuesheetEditModal from './cuesheet-edit-modal/CuesheetEditModal';
@@ -18,7 +18,7 @@ export default function CuesheetPage() {
useWindowTitle('Cuesheet'); useWindowTitle('Cuesheet');
const isLocked = getIsViewLocked(); const isLocked = getIsNavigationLocked();
return ( return (
<> <>