diff --git a/apps/client/src/common/hooks/useLocalStorage.ts b/apps/client/src/common/hooks/useLocalStorage.ts deleted file mode 100644 index 3519821b9..000000000 --- a/apps/client/src/common/hooks/useLocalStorage.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { useSyncExternalStore } from 'react'; - -const STORAGE_EVENT = 'ontime-storage'; - -function getSnapshot(key: string): string | null { - try { - return window.localStorage.getItem(`ontime-${key}`); - } catch { - return null; - } -} - -function getParsedJson(localStorageValue: string | null, initialValue: T): T { - try { - return localStorageValue ? JSON.parse(localStorageValue) : initialValue; - } catch { - return initialValue; - } -} - -export const useLocalStorage = (key: string, initialValue: T) => { - const localStorageValue = useSyncExternalStore(subscribe, () => getSnapshot(key)); - const parsedLocalStorageValue = getParsedJson(localStorageValue, initialValue); - - /** - * @description Set value to local storage - * @param value - */ - const setLocalStorageValue = (value: T | ((val: T) => T)) => { - // Allow value to be a function so we have same API as useState - const valueToStore = value instanceof Function ? value(parsedLocalStorageValue) : value; - - localStorage.setItem(`ontime-${key}`, JSON.stringify(valueToStore)); - window.dispatchEvent(new StorageEvent(STORAGE_EVENT)); - }; - - return [parsedLocalStorageValue, setLocalStorageValue] as const; -}; - -function subscribe(callback: () => void) { - window.addEventListener(STORAGE_EVENT, callback); - - return () => { - window.removeEventListener(STORAGE_EVENT, callback); - }; -} diff --git a/apps/client/src/features/control/playback/add-time/AddTime.tsx b/apps/client/src/features/control/playback/add-time/AddTime.tsx index d497aa71c..4bb9f1046 100644 --- a/apps/client/src/features/control/playback/add-time/AddTime.tsx +++ b/apps/client/src/features/control/playback/add-time/AddTime.tsx @@ -1,11 +1,11 @@ import { Tooltip } from '@chakra-ui/react'; +import { useLocalStorage } from '@mantine/hooks'; import { IoAdd } from '@react-icons/all-files/io5/IoAdd'; import { IoRemove } from '@react-icons/all-files/io5/IoRemove'; import { Playback } from 'ontime-types'; import { MILLIS_PER_HOUR, MILLIS_PER_SECOND, parseUserTime } from 'ontime-utils'; import TimeInput from '../../../../common/components/input/time-input/TimeInput'; -import { useLocalStorage } from '../../../../common/hooks/useLocalStorage'; import { setPlayback } from '../../../../common/hooks/useSocket'; import { tooltipDelayMid } from '../../../../ontimeConfig'; import TapButton from '../tap-button/TapButton'; @@ -18,7 +18,7 @@ interface AddTimeProps { export default function AddTime(props: AddTimeProps) { const { playback } = props; - const [time, setTime] = useLocalStorage('add-time', 300_000); // 5 minutes + const [time, setTime] = useLocalStorage({ key: 'add-time', defaultValue: 300_000 }); // 5 minutes const handleTimeChange = (_field: string, value: string) => { const newTime = parseUserTime(value); diff --git a/apps/client/src/features/cuesheet/Cuesheet.tsx b/apps/client/src/features/cuesheet/Cuesheet.tsx index bd9ed3358..abf4a4f55 100644 --- a/apps/client/src/features/cuesheet/Cuesheet.tsx +++ b/apps/client/src/features/cuesheet/Cuesheet.tsx @@ -1,10 +1,10 @@ -import { useRef } from 'react'; +import { useCallback, useEffect, useRef } from 'react'; +import { useLocalStorage } from '@mantine/hooks'; import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'; import Color from 'color'; import { isOntimeBlock, isOntimeDelay, isOntimeEvent, OntimeRundown, OntimeRundownEntry } from 'ontime-types'; import useFollowComponent from '../../common/hooks/useFollowComponent'; -import { useLocalStorage } from '../../common/hooks/useLocalStorage'; import { getAccessibleColour } from '../../common/utils/styleUtils'; import BlockRow from './cuesheet-table-elements/BlockRow'; @@ -27,14 +27,23 @@ interface CuesheetProps { export default function Cuesheet({ data, columns, handleUpdate, selectedId }: CuesheetProps) { const { followSelected, showSettings, showDelayBlock, showPrevious } = useCuesheetSettings(); - const [columnVisibility, setColumnVisibility] = useLocalStorage('table-hidden', {}); - const [columnOrder, saveColumnOrder] = useLocalStorage('table-order', initialColumnOrder); - const [columnSizing, setColumnSizing] = useLocalStorage('table-sizes', {}); + const [columnVisibility, setColumnVisibility] = useLocalStorage({ key: 'table-hidden', defaultValue: {} }); + const [columnOrder, saveColumnOrder] = useLocalStorage({ + key: 'table-order', + defaultValue: initialColumnOrder, + }); + const [columnSizing, setColumnSizing] = useLocalStorage({ key: 'table-sizes', defaultValue: {} }); const selectedRef = useRef(null); const tableContainerRef = useRef(null); useFollowComponent({ followRef: selectedRef, scrollRef: tableContainerRef, doFollow: followSelected }); + // keep column order in sync with columns + useEffect(() => { + const order = columns.map((col) => col.id as string); + saveColumnOrder(order); + }, [columns, saveColumnOrder]); + const table = useReactTable({ data, columns, @@ -64,6 +73,26 @@ export default function Cuesheet({ data, columns, handleUpdate, selectedId }: Cu setColumnSizing({}); }; + const reorder = useCallback( + (fromId: string, toId: string) => { + // get index of from + const fromIndex = columnOrder.indexOf(fromId); + + // get index of to + const toIndex = columnOrder.indexOf(toId); + + if (toIndex === -1) { + return; + } + + const reorderedCols = [...columnOrder]; + const reorderedItem = reorderedCols.splice(fromIndex, 1); + reorderedCols.splice(toIndex, 0, reorderedItem[0]); + saveColumnOrder(reorderedCols); + }, + [columnOrder, saveColumnOrder], + ); + const headerGroups = table.getHeaderGroups(); const rowModel = table.getRowModel(); const allLeafColumns = table.getAllLeafColumns(); @@ -83,7 +112,7 @@ export default function Cuesheet({ data, columns, handleUpdate, selectedId }: Cu )}
- + {rowModel.rows.map((row) => { const key = row.original.id; diff --git a/apps/client/src/features/cuesheet/cuesheet-table-elements/CuesheetHeader.tsx b/apps/client/src/features/cuesheet/cuesheet-table-elements/CuesheetHeader.tsx index cd6bcd905..5387ccc52 100644 --- a/apps/client/src/features/cuesheet/cuesheet-table-elements/CuesheetHeader.tsx +++ b/apps/client/src/features/cuesheet/cuesheet-table-elements/CuesheetHeader.tsx @@ -1,4 +1,3 @@ -import { memo, useEffect } from 'react'; import { Tooltip } from '@chakra-ui/react'; import { closestCenter, @@ -14,10 +13,8 @@ import { horizontalListSortingStrategy, SortableContext, sortableKeyboardCoordin import { flexRender, HeaderGroup } from '@tanstack/react-table'; import { OntimeRundownEntry } from 'ontime-types'; -import { useLocalStorage } from '../../../common/hooks/useLocalStorage'; import { getAccessibleColour } from '../../../common/utils/styleUtils'; import { tooltipDelayFast } from '../../../ontimeConfig'; -import { initialColumnOrder } from '../cuesheetCols'; import { SortableCell } from './SortableCell'; @@ -25,17 +22,11 @@ import style from '../Cuesheet.module.scss'; interface CuesheetHeaderProps { headerGroups: HeaderGroup[]; + saveColumnOrder: (fromId: string, toId: string) => void; } -function CuesheetHeader(props: CuesheetHeaderProps) { - const { headerGroups } = props; - const [columnOrder, saveColumnOrder] = useLocalStorage('table-order', initialColumnOrder); - - useEffect(() => { - if (!localStorage.getItem('table-order')) { - saveColumnOrder(initialColumnOrder); - } - }, [saveColumnOrder]); +export default function CuesheetHeader(props: CuesheetHeaderProps) { + const { headerGroups, saveColumnOrder } = props; const handleOnDragEnd = (event: DragEndEvent) => { const { delta, active, over } = event; @@ -45,21 +36,7 @@ function CuesheetHeader(props: CuesheetHeaderProps) { // cancel if we do not have an over id if (over?.id == null) return; - // get index of from - const fromIndex = columnOrder.indexOf(active.id as string); - - // get index of to - const toIndex = columnOrder.indexOf(over.id as string); - - if (toIndex === -1) { - return; - } - - const reorderedCols = [...columnOrder]; - const reorderedItem = reorderedCols.splice(fromIndex, 1); - reorderedCols.splice(toIndex, 0, reorderedItem[0]); - - saveColumnOrder(reorderedCols); + saveColumnOrder(active.id as string, over.id as string); }; const sensors = useSensors( @@ -119,5 +96,3 @@ function CuesheetHeader(props: CuesheetHeaderProps) { ); } - -export default memo(CuesheetHeader);