refactor: settings in params

This commit is contained in:
Carlos Valente
2024-12-15 12:02:04 +01:00
committed by Carlos Valente
parent e91d5f5cd9
commit 41fe213ebb
6 changed files with 113 additions and 115 deletions
@@ -32,6 +32,10 @@ export function getTimerByType(freezeEnd: boolean, timerObject?: TimerTypeParams
} }
} }
/**
* Parses a string to semantically verify if it represents a true value
* Used in the context of parsing search params and local storage items which can be strings or null
*/
export function isStringBoolean(text: string | null) { export function isStringBoolean(text: string | null) {
if (text === null) { if (text === null) {
return false; return false;
+8 -8
View File
@@ -18,7 +18,7 @@ import CuesheetHeader from './cuesheet-table-elements/CuesheetHeader';
import DelayRow from './cuesheet-table-elements/DelayRow'; import DelayRow from './cuesheet-table-elements/DelayRow';
import EventRow from './cuesheet-table-elements/EventRow'; import EventRow from './cuesheet-table-elements/EventRow';
import CuesheetTableSettings from './cuesheet-table-settings/CuesheetTableSettings'; import CuesheetTableSettings from './cuesheet-table-settings/CuesheetTableSettings';
import { useCuesheetSettings } from './store/cuesheetSettingsStore'; import { useCuesheetOptions } from './cuesheet.options';
import useColumnManager from './useColumnManager'; import useColumnManager from './useColumnManager';
import style from './Cuesheet.module.scss'; import style from './Cuesheet.module.scss';
@@ -40,7 +40,7 @@ export default function Cuesheet({
selectedId, selectedId,
currentBlockId, currentBlockId,
}: CuesheetProps) { }: CuesheetProps) {
const { followSelected, showDelayBlock, showPrevious, showIndexColumn } = useCuesheetSettings(); const { followSelected, hideDelays, hidePast, hideIndexColumn } = useCuesheetOptions();
const { const {
columnVisibility, columnVisibility,
columnOrder, columnOrder,
@@ -118,7 +118,7 @@ export default function Cuesheet({
/> />
<div ref={tableContainerRef} className={style.cuesheetContainer}> <div ref={tableContainerRef} className={style.cuesheetContainer}>
<table className={style.cuesheet}> <table className={style.cuesheet}>
<CuesheetHeader headerGroups={headerGroups} saveColumnOrder={reorder} showIndexColumn={showIndexColumn} /> <CuesheetHeader headerGroups={headerGroups} saveColumnOrder={reorder} showIndexColumn={!hideIndexColumn} />
<tbody> <tbody>
{rowModel.rows.map((row) => { {rowModel.rows.map((row) => {
const key = row.original.id; const key = row.original.id;
@@ -128,17 +128,17 @@ export default function Cuesheet({
} }
if (isOntimeBlock(row.original)) { if (isOntimeBlock(row.original)) {
if (isPast && !showPrevious && key !== currentBlockId) { if (isPast && hidePast && key !== currentBlockId) {
return null; return null;
} }
return <BlockRow key={key} title={row.original.title} />; return <BlockRow key={key} title={row.original.title} />;
} }
if (isOntimeDelay(row.original)) { if (isOntimeDelay(row.original)) {
if (isPast && !showPrevious) { if (isPast && hidePast) {
return null; return null;
} }
const delayVal = row.original.duration; const delayVal = row.original.duration;
if (!showDelayBlock || delayVal === 0) { if (hideDelays || delayVal === 0) {
return null; return null;
} }
@@ -148,7 +148,7 @@ export default function Cuesheet({
eventIndex++; eventIndex++;
const isSelected = key === selectedId; const isSelected = key === selectedId;
if (isPast && !showPrevious) { if (isPast && hidePast) {
return null; return null;
} }
@@ -173,7 +173,7 @@ export default function Cuesheet({
selectedRef={isSelected ? selectedRef : undefined} selectedRef={isSelected ? selectedRef : undefined}
skip={row.original.skip} skip={row.original.skip}
colour={row.original.colour} colour={row.original.colour}
showIndexColumn={showIndexColumn} showIndexColumn={!hideIndexColumn}
> >
{row.getVisibleCells().map((cell) => { {row.getVisibleCells().map((cell) => {
return ( return (
@@ -1,4 +1,5 @@
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import { IconButton, useDisclosure } from '@chakra-ui/react'; import { IconButton, useDisclosure } from '@chakra-ui/react';
import { IoApps } from '@react-icons/all-files/io5/IoApps'; import { IoApps } from '@react-icons/all-files/io5/IoApps';
import { IoSettingsOutline } from '@react-icons/all-files/io5/IoSettingsOutline'; import { IoSettingsOutline } from '@react-icons/all-files/io5/IoSettingsOutline';
@@ -6,6 +7,7 @@ import { CustomFieldLabel, isOntimeEvent, OntimeEvent } from 'ontime-types';
import ProductionNavigationMenu from '../../common/components/navigation-menu/ProductionNavigationMenu'; import ProductionNavigationMenu from '../../common/components/navigation-menu/ProductionNavigationMenu';
import EmptyPage from '../../common/components/state/EmptyPage'; import EmptyPage from '../../common/components/state/EmptyPage';
import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor';
import { useEventAction } from '../../common/hooks/useEventAction'; import { useEventAction } from '../../common/hooks/useEventAction';
import { useCuesheet } from '../../common/hooks/useSocket'; import { useCuesheet } from '../../common/hooks/useSocket';
import { useWindowTitle } from '../../common/hooks/useWindowTitle'; import { useWindowTitle } from '../../common/hooks/useWindowTitle';
@@ -14,8 +16,8 @@ import { useFlatRundown } from '../../common/hooks-query/useRundown';
import { CuesheetOverview } from '../../features/overview/Overview'; import { CuesheetOverview } from '../../features/overview/Overview';
import CuesheetProgress from './cuesheet-progress/CuesheetProgress'; import CuesheetProgress from './cuesheet-progress/CuesheetProgress';
import { useCuesheetSettings } from './store/cuesheetSettingsStore';
import Cuesheet from './Cuesheet'; import Cuesheet from './Cuesheet';
import { cuesheetOptions } from './cuesheet.options';
import { makeCuesheetColumns } from './cuesheetCols'; import { makeCuesheetColumns } from './cuesheetCols';
import styles from './CuesheetPage.module.scss'; import styles from './CuesheetPage.module.scss';
@@ -24,15 +26,21 @@ export default function CuesheetPage() {
// TODO: can we use the normalised rundown for the table? // TODO: can we use the normalised rundown for the table?
const { data: flatRundown, status: rundownStatus } = useFlatRundown(); const { data: flatRundown, status: rundownStatus } = useFlatRundown();
const { data: customFields } = useCustomFields(); const { data: customFields } = useCustomFields();
const [searchParams, setSearchParams] = useSearchParams();
const { isOpen: isMenuOpen, onOpen, onClose } = useDisclosure(); const { isOpen: isMenuOpen, onOpen, onClose } = useDisclosure();
const { updateCustomField, updateEvent } = useEventAction(); const { updateCustomField, updateEvent } = useEventAction();
const featureData = useCuesheet(); const featureData = useCuesheet();
const columns = useMemo(() => makeCuesheetColumns(customFields), [customFields]); const columns = useMemo(() => makeCuesheetColumns(customFields), [customFields]);
const toggleSettings = useCuesheetSettings((state) => state.toggleSettings);
useWindowTitle('Cuesheet'); useWindowTitle('Cuesheet');
/** Handles showing the view params edit drawer */
const showEditFormDrawer = useCallback(() => {
searchParams.set('edit', 'true');
setSearchParams(searchParams);
}, [searchParams, setSearchParams]);
/** /**
* Handles updating a custom field * Handles updating a custom field
*/ */
@@ -99,6 +107,7 @@ export default function CuesheetPage() {
return ( return (
<div className={styles.tableWrapper} data-testid='cuesheet'> <div className={styles.tableWrapper} data-testid='cuesheet'>
<ProductionNavigationMenu isMenuOpen={isMenuOpen} onMenuClose={onClose} /> <ProductionNavigationMenu isMenuOpen={isMenuOpen} onMenuClose={onClose} />
<ViewParamsEditor viewOptions={cuesheetOptions} />
<CuesheetOverview> <CuesheetOverview>
<IconButton <IconButton
aria-label='Toggle navigation' aria-label='Toggle navigation'
@@ -112,7 +121,7 @@ export default function CuesheetPage() {
variant='ontime-subtle-white' variant='ontime-subtle-white'
size='lg' size='lg'
icon={<IoSettingsOutline />} icon={<IoSettingsOutline />}
onClick={() => toggleSettings()} onClick={showEditFormDrawer}
/> />
</CuesheetOverview> </CuesheetOverview>
<CuesheetProgress /> <CuesheetProgress />
@@ -1,19 +1,90 @@
import { OntimeEntryCommonKeys, OntimeEvent } from 'ontime-types'; import { useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import { ViewOption } from '../../common/components/view-params-editor/types';
import { isStringBoolean } from '../../features/viewers/common/viewUtils';
/** /**
* @description set default column order * In the specific case of the cuesheet options
* we save the user preferences in the local storage
*/ */
export const defaultColumnOrder: OntimeEntryCommonKeys[] = [ export const cuesheetOptions: ViewOption[] = [
'isPublic', { section: 'Table options' },
'cue', {
'timeStart', id: 'hideTableSeconds',
'timeEnd', title: 'Hide seconds in table',
'duration', description: 'Whether to hide seconds in the time fields displayed in the table',
'title', type: 'boolean',
'note', defaultValue: false,
},
{
id: 'followSelected',
title: 'Follow selected event',
description: 'Whether the view should automatically scroll to the selected event',
type: 'boolean',
defaultValue: false,
},
{
id: 'hidePast',
title: 'Hide Past Events',
description: 'Whether to hide events that have passed',
type: 'boolean',
defaultValue: false,
},
{
id: 'hideIndexColumn',
title: 'Hide index column',
description: 'Whether the hide the event indexes in the table',
type: 'boolean',
defaultValue: false,
},
{ section: 'Delay flow' },
{
id: 'showDelayedTimes',
title: 'Show delayed times',
description: 'Whether the time fields should include delays',
type: 'boolean',
defaultValue: false,
},
{
id: 'hideDelays',
title: 'Hide delays',
description: 'Whether to hide the rows containing scheduled delays',
type: 'boolean',
defaultValue: false,
},
]; ];
type CuesheetOptions = {
hideTableSeconds: boolean;
followSelected: boolean;
hidePast: boolean;
hideIndexColumn: boolean;
showDelayedTimes: boolean;
hideDelays: boolean;
};
/** /**
* @description set default hidden columns * Utility extract the view options from URL Params
* the names and fallbacks are manually matched with cuesheetOptions
*/ */
export const defaultHiddenColumns: (keyof OntimeEvent)[] = []; export function getOptionsFromParams(searchParams: URLSearchParams): CuesheetOptions {
// we manually make an object that matches the key above
return {
hideTableSeconds: isStringBoolean(searchParams.get('hideTableSeconds')),
followSelected: isStringBoolean(searchParams.get('followSelected')),
hidePast: isStringBoolean(searchParams.get('hidePast')),
hideIndexColumn: isStringBoolean(searchParams.get('hideIndexColumn')),
showDelayedTimes: isStringBoolean(searchParams.get('showDelayedTimes')),
hideDelays: isStringBoolean(searchParams.get('hideDelays')),
};
}
/**
* Hook exposes the cuesheet view options
*/
export function useCuesheetOptions(): CuesheetOptions {
const [searchParams] = useSearchParams();
const options = useMemo(() => getOptionsFromParams(searchParams), [searchParams]);
return options;
}
@@ -8,7 +8,7 @@ import RunningTime from '../../features/viewers/common/running-time/RunningTime'
import MultiLineCell from './cuesheet-table-elements/MultiLineCell'; import MultiLineCell from './cuesheet-table-elements/MultiLineCell';
import SingleLineCell from './cuesheet-table-elements/SingleLineCell'; import SingleLineCell from './cuesheet-table-elements/SingleLineCell';
import { useCuesheetSettings } from './store/cuesheetSettingsStore'; import { useCuesheetOptions } from './cuesheet.options';
import style from './Cuesheet.module.scss'; import style from './Cuesheet.module.scss';
@@ -35,27 +35,26 @@ function MakePublic({ row, column, table }: CellContext<OntimeRundownEntry, unkn
} }
function MakeTimer({ getValue, row: { original } }: CellContext<OntimeRundownEntry, unknown>) { function MakeTimer({ getValue, row: { original } }: CellContext<OntimeRundownEntry, unknown>) {
const showDelayedTimes = useCuesheetSettings((state) => state.showDelayedTimes); const { showDelayedTimes, hideTableSeconds } = useCuesheetOptions();
const hideSeconds = useCuesheetSettings((state) => state.hideSeconds);
const cellValue = (getValue() as number | null) ?? 0; const cellValue = (getValue() as number | null) ?? 0;
const delayValue = (original as OntimeEvent)?.delay ?? 0; const delayValue = (original as OntimeEvent)?.delay ?? 0;
return ( return (
<span className={style.time}> <span className={style.time}>
<DelayIndicator delayValue={delayValue} /> <DelayIndicator delayValue={delayValue} />
<RunningTime value={cellValue} hideSeconds={hideSeconds} /> <RunningTime value={cellValue} hideSeconds={hideTableSeconds} />
{delayValue !== 0 && showDelayedTimes && ( {delayValue !== 0 && showDelayedTimes && (
<RunningTime className={style.delayedTime} value={cellValue + delayValue} hideSeconds={hideSeconds} /> <RunningTime className={style.delayedTime} value={cellValue + delayValue} hideSeconds={hideTableSeconds} />
)} )}
</span> </span>
); );
} }
function MakeDuration({ getValue }: CellContext<OntimeRundownEntry, unknown>) { function MakeDuration({ getValue }: CellContext<OntimeRundownEntry, unknown>) {
const hideSeconds = useCuesheetSettings((state) => state.hideSeconds); const { hideTableSeconds } = useCuesheetOptions();
const cellValue = (getValue() as number | null) ?? 0; const cellValue = (getValue() as number | null) ?? 0;
return <RunningTime value={cellValue} hideSeconds={hideSeconds} />; return <RunningTime value={cellValue} hideSeconds={hideTableSeconds} />;
} }
function MakeMultiLineField({ row, column, table }: CellContext<OntimeRundownEntry, unknown>) { function MakeMultiLineField({ row, column, table }: CellContext<OntimeRundownEntry, unknown>) {
@@ -1,85 +0,0 @@
import { create } from 'zustand';
import { booleanFromLocalStorage } from '../../../common/utils/localStorage';
interface CuesheetSettingsStore {
showSettings: boolean;
showIndexColumn: boolean;
followSelected: boolean;
showPrevious: boolean;
showDelayBlock: boolean;
showDelayedTimes: boolean;
hideSeconds: boolean;
toggleSettings: (newValue?: boolean) => void;
toggleFollow: (newValue?: boolean) => void;
togglePreviousVisibility: (newValue?: boolean) => void;
toggleIndexColumn: (newValue?: boolean) => void;
toggleDelayVisibility: (newValue?: boolean) => void;
toggleDelayedTimes: (newValue?: boolean) => void;
toggleSecondsVisibility: (newValue?: boolean) => void;
}
function toggle(oldValue: boolean, value?: boolean) {
if (typeof value === 'undefined') {
return !oldValue;
}
return value;
}
enum CuesheetKeys {
Follow = 'ontime-cuesheet-follow-selected',
DelayVisibility = 'ontime-cuesheet-show-delay',
PreviousVisibility = 'ontime-cuesheet-show-previous',
ColumnIndex = 'ontime-cuesheet-show-index-column',
DelayedTimes = 'ontime-cuesheet-show-delayed',
Seconds = 'ontime-cuesheet-hide-sceconds',
}
export const useCuesheetSettings = create<CuesheetSettingsStore>()((set) => ({
showSettings: false,
showIndexColumn: booleanFromLocalStorage(CuesheetKeys.ColumnIndex, true),
followSelected: booleanFromLocalStorage(CuesheetKeys.Follow, false),
showPrevious: booleanFromLocalStorage(CuesheetKeys.PreviousVisibility, true),
showDelayBlock: booleanFromLocalStorage(CuesheetKeys.DelayVisibility, true),
showDelayedTimes: booleanFromLocalStorage(CuesheetKeys.DelayedTimes, false),
hideSeconds: booleanFromLocalStorage(CuesheetKeys.Seconds, false),
toggleSettings: (newValue?: boolean) => set((state) => ({ showSettings: toggle(state.showSettings, newValue) })),
toggleFollow: (newValue?: boolean) =>
set((state) => {
const followSelected = toggle(state.followSelected, newValue);
localStorage.setItem(CuesheetKeys.Follow, String(followSelected));
return { followSelected };
}),
toggleIndexColumn: (newValue?: boolean) =>
set((state) => {
const showIndexColumn = toggle(state.showIndexColumn, newValue);
localStorage.setItem(CuesheetKeys.ColumnIndex, String(showIndexColumn));
return { showIndexColumn };
}),
togglePreviousVisibility: (newValue?: boolean) =>
set((state) => {
const showPrevious = toggle(state.showPrevious, newValue);
localStorage.setItem(CuesheetKeys.PreviousVisibility, String(showPrevious));
return { showPrevious };
}),
toggleDelayVisibility: (newValue?: boolean) =>
set((state) => {
const showDelayBlock = toggle(state.showDelayBlock, newValue);
localStorage.setItem(CuesheetKeys.DelayVisibility, String(showDelayBlock));
return { showDelayBlock };
}),
toggleDelayedTimes: (newValue?: boolean) =>
set((state) => {
const showDelayedTimes = toggle(state.showDelayedTimes, newValue);
localStorage.setItem(CuesheetKeys.DelayedTimes, String(showDelayedTimes));
return { showDelayedTimes };
}),
toggleSecondsVisibility: (newValue?: boolean) =>
set((state) => {
const hideSeconds = toggle(state.hideSeconds, newValue);
localStorage.setItem(CuesheetKeys.Seconds, String(hideSeconds));
return { hideSeconds };
}),
}));