Deps migration (#1988)

* chore: migrate eslint to oxlint

* chore: migrate prettier to oxfmt

* chore: migrate typescript

* chore: toThrow should have a expected value

* chore: cast test value as Day

* chore: small title fix

* chore: mocks should be hoisted

* chore: incorrect async useage

* chore: test should be inside description

* chore: test sohuld include an expeced

* chore: oxfmt

---------

Co-authored-by: alex-Arc <omnivox@LAPTOP-RC5SNBVV.localdomain>
This commit is contained in:
Carlos Valente
2026-03-08 16:22:12 +01:00
committed by GitHub
parent 9516ac21bb
commit 1849b4d39f
501 changed files with 2678 additions and 4654 deletions
+3 -4
View File
@@ -1,20 +1,19 @@
import { BrowserRouter } from 'react-router';
import { Tooltip } from '@base-ui/react/tooltip';
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { BrowserRouter } from 'react-router';
// apply global axios config defaults
import './common/api/axios.config';
import AppRouter from './AppRouter';
import ErrorBoundary from './common/components/error-boundary/ErrorBoundary';
import IdentifyOverlay from './common/components/identify-overlay/IdentifyOverlay';
import { AppContextProvider } from './common/context/AppContext';
import { ontimeQueryClient } from './common/queryClient';
import { connectSocket } from './common/utils/socket';
import { baseURI } from './externals';
import KeepAwake from './features/keep-awake/KeepAwake';
import { TranslationProvider } from './translation/TranslationProvider';
import AppRouter from './AppRouter';
import { baseURI } from './externals';
connectSocket();
+5 -5
View File
@@ -1,18 +1,18 @@
import { ComponentType, lazy, Suspense, useEffect, useMemo } from 'react';
import { Navigate, Route, useLocation, useNavigate, useParams } from 'react-router';
import { OntimeView, OntimeViewPresettable, URLPreset } from 'ontime-types';
import { ComponentType, Suspense, lazy, useEffect, useMemo } from 'react';
import { Navigate, Route, useLocation, useNavigate, useParams } from 'react-router';
import ViewNavigationMenu from './common/components/navigation-menu/ViewNavigationMenu';
import { PresetContext } from './common/context/PresetContext';
import { useClientPath } from './common/hooks/useClientPath';
import useUrlPresets from './common/hooks-query/useUrlPresets';
import { useClientPath } from './common/hooks/useClientPath';
import { getRouteFromPreset } from './common/utils/urlPresets';
import { getIsNavigationLocked, sessionScope } from './externals';
import Log from './features/log/Log';
import { initializeSentry } from './sentry.config';
import Loader from './views/common/loader/Loader';
import NotFound from './views/common/not-found/NotFound';
import ViewLoader from './views/ViewLoader';
import { getIsNavigationLocked, sessionScope } from './externals';
import { initializeSentry } from './sentry.config';
const Timer = lazy(() => import('./views/timer/Timer'));
const Countdown = lazy(() => import('./views/countdown/Countdown'));
+1 -2
View File
@@ -2,8 +2,7 @@ import axios from 'axios';
import { TranslationObject } from 'ontime-types';
import { ontimeQueryClient } from '../../common/queryClient';
import { apiEntryUrl, customTranslationsURL, TRANSLATION } from './constants';
import { TRANSLATION, apiEntryUrl, customTranslationsURL } from './constants';
import type { RequestOptions } from './requestOptions';
const assetsPath = `${apiEntryUrl}/assets`;
-1
View File
@@ -1,7 +1,6 @@
import axios from 'axios';
import { apiRepoLatest } from '../../externals';
import type { RequestOptions } from './requestOptions';
export type HasUpdate = {
+1 -2
View File
@@ -2,8 +2,7 @@ import axios from 'axios';
import { OntimeReport } from 'ontime-types';
import { ontimeQueryClient } from '../../common/queryClient';
import { apiEntryUrl, REPORT } from './constants';
import { REPORT, apiEntryUrl } from './constants';
import type { RequestOptions } from './requestOptions';
export const reportUrl = `${apiEntryUrl}/report`;
+4 -8
View File
@@ -76,14 +76,10 @@ export const previewRundown = async (
};
export const getWorksheetNames = async (sheetId: string, requestOptions?: RequestOptions): Promise<string[]> => {
const response: AxiosResponse<string[]> = await axios.post(
`${sheetsPath}/${sheetId}/worksheets`,
undefined,
{
signal: requestOptions?.signal,
timeout: requestOptions?.timeout ?? axiosConfig.longTimeout,
},
);
const response: AxiosResponse<string[]> = await axios.post(`${sheetsPath}/${sheetId}/worksheets`, undefined, {
signal: requestOptions?.signal,
timeout: requestOptions?.timeout ?? axiosConfig.longTimeout,
});
return response.data;
};
@@ -1,5 +1,5 @@
import { IoCheckmark } from 'react-icons/io5';
import { Checkbox as BaseCheckbox } from '@base-ui/react/checkbox';
import { IoCheckmark } from 'react-icons/io5';
import style from './Checkbox.module.scss';
@@ -2,8 +2,8 @@ import { useState } from 'react';
import { IoArrowForward } from 'react-icons/io5';
import { navigatorConstants } from '../../../viewerConfig';
import { setClientRemote } from '../../hooks/useSocket';
import useUrlPresets from '../../hooks-query/useUrlPresets';
import { setClientRemote } from '../../hooks/useSocket';
import Button from '../buttons/Button';
import Dialog from '../dialog/Dialog';
import Info from '../info/Info';
@@ -1,6 +1,6 @@
import { Dialog as BaseDialog } from '@base-ui/react/dialog';
import type { ReactNode } from 'react';
import { IoClose } from 'react-icons/io5';
import { Dialog as BaseDialog } from '@base-ui/react/dialog';
import IconButton from '../buttons/IconButton';
@@ -1,6 +1,6 @@
import { Menu as BaseMenu } from '@base-ui/react/menu';
import { PropsWithChildren } from 'react';
import { IconType } from 'react-icons';
import { Menu as BaseMenu } from '@base-ui/react/menu';
import style from './DropdownMenu.module.scss';
@@ -1,7 +1,7 @@
/* eslint-disable react/destructuring-assignment */
import React from 'react';
// skipcq: JS-C1003 - sentry does not expose itself as an ES Module.
import * as Sentry from '@sentry/react';
/* eslint-disable react/destructuring-assignment */
import React from 'react';
import { hasConnected, reconnectAttempts } from '../../../common/utils/socket';
import { runtimeStore } from '../../stores/runtime';
@@ -1,6 +1,6 @@
import { MILLIS_PER_MINUTE } from 'ontime-utils';
import { useCallback, useEffect, useRef } from 'react';
import { createPortal } from 'react-dom';
import { MILLIS_PER_MINUTE } from 'ontime-utils';
import { setClientRemote } from '../../hooks/useSocket';
import { useClientStore } from '../../stores/clientStore';
@@ -1,6 +1,6 @@
import { RefObject, useEffect } from 'react';
// @ts-expect-error no types from library
import autosize from 'autosize/dist/autosize';
import { RefObject, useEffect } from 'react';
import Textarea, { type TextareaProps } from '../textarea/Textarea';
@@ -1,7 +1,7 @@
import { useController, UseControllerProps } from 'react-hook-form';
import { IoEyedrop } from 'react-icons/io5';
import { useDebouncedCallback } from '@mantine/hooks';
import { ViewSettings } from 'ontime-types';
import { UseControllerProps, useController } from 'react-hook-form';
import { IoEyedrop } from 'react-icons/io5';
import { cx, getAccessibleColour } from '../../../utils/styleUtils';
import PopoverPicker from '../popover-picker/PopoverPicker';
@@ -1,4 +1,4 @@
import { forwardRef, InputHTMLAttributes } from 'react';
import { InputHTMLAttributes, forwardRef } from 'react';
import { cx } from '../../../utils/styleUtils';
@@ -1,6 +1,6 @@
import { Popover } from '@base-ui/react/popover';
import { PropsWithChildren } from 'react';
import { HexAlphaColorPicker, HexColorInput } from 'react-colorful';
import { Popover } from '@base-ui/react/popover';
import PopoverContents from '../../popover/Popover';
@@ -1,5 +1,5 @@
import { HotkeyItem, getHotkeyHandler } from '@mantine/hooks';
import { ChangeEvent, KeyboardEvent, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { getHotkeyHandler, HotkeyItem } from '@mantine/hooks';
interface UseReactiveTextInputReturn {
value: string;
@@ -1,4 +1,4 @@
import { forwardRef, TextareaHTMLAttributes } from 'react';
import { TextareaHTMLAttributes, forwardRef } from 'react';
import { cx } from '../../../utils/styleUtils';
@@ -1,5 +1,5 @@
import { FocusEvent, KeyboardEvent, useCallback, useEffect, useRef, useState } from 'react';
import { millisToString, parseUserTime } from 'ontime-utils';
import { FocusEvent, KeyboardEvent, useCallback, useEffect, useRef, useState } from 'react';
import { cx } from '../../../utils/styleUtils';
import Input from '../input/Input';
@@ -1,5 +1,5 @@
import { FocusEvent, KeyboardEvent, useCallback, useEffect, useRef, useState } from 'react';
import { millisToString, parseUserTime } from 'ontime-utils';
import { FocusEvent, KeyboardEvent, useCallback, useEffect, useRef, useState } from 'react';
import { cx } from '../../../utils/styleUtils';
import Input from '../input/Input';
@@ -18,7 +18,7 @@
}
&:focus {
outline: none;
outline: none;
box-shadow: 0 1px 0 0 currentColor;
}
}
}
@@ -3,7 +3,7 @@
top: 10vh;
left: 50%;
transform: translateX(-50%);
padding-inline: 1rem;
min-width: min(880px, 90vw);
min-height: min(200px, 10vh);
@@ -1,6 +1,6 @@
import { Dialog as BaseDialog } from '@base-ui/react/dialog';
import type { ReactNode } from 'react';
import { IoClose } from 'react-icons/io5';
import { Dialog as BaseDialog } from '@base-ui/react/dialog';
import IconButton from '../buttons/IconButton';
@@ -1,9 +1,9 @@
import { Dialog } from '@base-ui/react/dialog';
import { useDisclosure, useFullscreen } from '@mantine/hooks';
import { memo } from 'react';
import { IoClose, IoContract, IoExpand, IoLockClosedOutline, IoSwapVertical } from 'react-icons/io5';
import { LuCoffee } from 'react-icons/lu';
import { useLocation } from 'react-router';
import { Dialog } from '@base-ui/react/dialog';
import { useDisclosure, useFullscreen } from '@mantine/hooks';
import { isLocalhost, supportsFullscreen } from '../../../externals';
import { canUseWakeLock, useKeepAwakeOptions } from '../../../features/keep-awake/useWakeLock';
@@ -13,7 +13,6 @@ import { useClientStore } from '../../stores/clientStore';
import { useViewOptionsStore } from '../../stores/viewOptions';
import IconButton from '../buttons/IconButton';
import { RenameClientModal } from '../client-modal/RenameClientModal';
import ClientLink from './client-link/ClientLink';
import EditorNavigation from './editor-navigation/EditorNavigation';
import NavigationMenuItem from './navigation-menu-item/NavigationMenuItem';
@@ -1,11 +1,10 @@
import { memo } from 'react';
import { useDisclosure, useHotkeys } from '@mantine/hooks';
import { memo } from 'react';
import { useViewParamsEditorStore } from '../view-params-editor/viewParamsEditor.store';
import FloatingNavigation from './floating-navigation/FloatingNavigation';
import ViewLockedIcon from './view-locked-icon/ViewLockedIcon';
import NavigationMenu from './NavigationMenu';
import ViewLockedIcon from './view-locked-icon/ViewLockedIcon';
interface ViewNavigationMenuProps {
/** prevent navigation */
@@ -24,7 +24,9 @@ export default function NavigationMenuItem({
role='button'
onClick={onClick}
onKeyDown={(event) => {
isKeyEnter(event) && onClick();
if (isKeyEnter(event)) {
onClick();
}
}}
>
{children}
@@ -5,7 +5,7 @@
color: $ui-white;
background-color: $gray-1250;
border-radius: 3px;
border-radius: 3px;
box-shadow: $box-shadow-l1;
border: 1px solid $gray-1000;
@@ -27,4 +27,3 @@
font-size: 1rem;
margin-bottom: 0.5rem;
}
@@ -1,5 +1,5 @@
import { PropsWithChildren } from 'react';
import { Popover } from '@base-ui/react/popover';
import { PropsWithChildren } from 'react';
import style from './Popover.module.scss';
@@ -2,7 +2,6 @@ import { PropsWithChildren, useCallback, useContext } from 'react';
import { isLocalhost } from '../../../externals';
import { AppContext } from '../../context/AppContext';
import PinPage from './PinPage';
interface ProtectRouteProps {
@@ -16,7 +16,7 @@ $thumb-color-hover: $white-60;
width: 100%;
overscroll-behavior: contain;
&:focus-visible {
&:focus-visible {
outline: 2px solid $blue-500;
outline-offset: -2px;
}
@@ -1,5 +1,5 @@
import { CSSProperties, PropsWithChildren, Ref } from 'react';
import { ScrollArea } from '@base-ui/react/scroll-area';
import { CSSProperties, PropsWithChildren, Ref } from 'react';
import { cx } from '../../utils/styleUtils';
@@ -1,6 +1,6 @@
import { Select as BaseSelect } from '@base-ui/react/select';
import { IoCheckmark } from 'react-icons/io5';
import { LuChevronsUpDown } from 'react-icons/lu';
import { Select as BaseSelect } from '@base-ui/react/select';
import { cx } from '../../utils/styleUtils';
@@ -10,7 +10,7 @@
.text {
display: block;
margin-inline: auto;;
margin-inline: auto;
font-weight: 600;
font-size: 2em;
max-width: 600px;
@@ -2,13 +2,13 @@
position: relative;
display: flex;
align-items: center;
/* Reset and base styles */
appearance: none;
border: 1px solid transparent;
border-radius: 99px;
background-color: $gray-1100;
/* Transitions */
transition: background-color 125ms cubic-bezier(0.26, 0.75, 0.38, 0.45);
@@ -1,7 +1,7 @@
.tooltip {
font-size: calc(1rem - 3px);
background-color: $ui-white;
color: $ui-black;
color: $ui-black;
padding: 0.125rem 0.5rem;
border-radius: 2px;
line-height: 1.25em;
@@ -1,5 +1,5 @@
import { PropsWithChildren } from 'react';
import { Tooltip as BaseTooltip } from '@base-ui/react/tooltip';
import { PropsWithChildren } from 'react';
import style from './Tooltip.module.scss';
@@ -6,7 +6,6 @@ import Checkbox from '../checkbox/Checkbox';
import Input from '../input/input/Input';
import Select, { SelectOption } from '../select/Select';
import Switch from '../switch/Switch';
import InlineColourPicker from './InlineColourPicker';
import { ParamField } from './viewParams.types';
@@ -44,7 +43,7 @@ export default function ParamInput({ paramField }: ParamInputProps) {
return (
<MultiOption
paramField={paramField}
options={optionFromParams.length ? optionFromParams : paramField.defaultValue ?? ['']}
options={optionFromParams.length ? optionFromParams : (paramField.defaultValue ?? [''])}
/>
);
}
@@ -1,15 +1,14 @@
import { Dialog } from '@base-ui/react/dialog';
import { OntimeView } from 'ontime-types';
import { FormEvent, memo } from 'react';
import { IoClose } from 'react-icons/io5';
import { useSearchParams } from 'react-router';
import { Dialog } from '@base-ui/react/dialog';
import { OntimeView } from 'ontime-types';
import { useIsSmallScreen } from '../../hooks/useIsSmallScreen';
import useViewSettings from '../../hooks-query/useViewSettings';
import { useIsSmallScreen } from '../../hooks/useIsSmallScreen';
import Button from '../buttons/Button';
import IconButton from '../buttons/IconButton';
import Info from '../info/Info';
import { ViewOption } from './viewParams.types';
import { getPreservedSearchParams, getURLSearchParamsFromObj } from './viewParams.utils';
import { useViewParamsEditorStore } from './viewParamsEditor.store';
@@ -1,5 +1,5 @@
import { useSearchParams } from 'react-router';
import { OntimeView, URLPreset } from 'ontime-types';
import { useSearchParams } from 'react-router';
import { useViewUrlPresets } from '../../hooks-query/useUrlPresets';
import { cx } from '../../utils/styleUtils';
@@ -47,4 +47,4 @@
.hidden {
display: none;
}
}
@@ -1,8 +1,7 @@
import { IoChevronDown } from 'react-icons/io5';
import { useLocalStorage } from '@mantine/hooks';
import { IoChevronDown } from 'react-icons/io5';
import { cx } from '../../utils/styleUtils';
import { OptionTitle } from './constants';
import ParamInput from './ParamInput';
import { type ParamField } from './viewParams.types';
@@ -1,7 +1,6 @@
import type { CustomFields, ProjectData } from 'ontime-types';
import type { SelectOption } from '../select/Select';
import type { MultiselectOption, ViewOption } from './viewParams.types';
/**
@@ -1,4 +1,4 @@
import { createContext, PropsWithChildren, useCallback, useEffect, useState } from 'react';
import { PropsWithChildren, createContext, useCallback, useEffect, useState } from 'react';
import useSettings from '../hooks-query/useSettings';
@@ -1,4 +1,4 @@
import { createContext, PropsWithChildren, useContext } from 'react';
import { PropsWithChildren, createContext, useContext } from 'react';
import { useEntryActions } from '../hooks/useEntryAction';
@@ -1,4 +1,4 @@
import { createContext } from 'react';
import { URLPreset } from 'ontime-types';
import { createContext } from 'react';
export const PresetContext = createContext<URLPreset | undefined>(undefined);
@@ -4,7 +4,7 @@ import { dayInMs } from 'ontime-utils';
import { version } from '../../../../../package.json';
import { isLocalhost } from '../../externals';
import { APP_VERSION } from '../api/constants';
import { getLatestVersion, HasUpdate } from '../api/external';
import { HasUpdate, getLatestVersion } from '../api/external';
const placeholder: HasUpdate & { hasUpdates: boolean } = { url: '', version: '', hasUpdates: false };
@@ -1,6 +1,6 @@
import { useMemo } from 'react';
import { useQuery } from '@tanstack/react-query';
import { ProjectFile, ProjectFileList, ProjectFileListResponse } from 'ontime-types';
import { useMemo } from 'react';
import { queryRefetchIntervalSlow } from '../../ontimeConfig';
import { PROJECT_LIST } from '../api/constants';
@@ -3,7 +3,14 @@ import { ProjectRundownsList } from 'ontime-types';
import { queryRefetchIntervalSlow } from '../../ontimeConfig';
import { PROJECT_RUNDOWNS } from '../api/constants';
import { createRundown, deleteRundown, duplicateRundown, fetchProjectRundownList, loadRundown, renameRundown } from '../api/rundown';
import {
createRundown,
deleteRundown,
duplicateRundown,
fetchProjectRundownList,
loadRundown,
renameRundown,
} from '../api/rundown';
/**
* Project rundowns
@@ -30,7 +37,7 @@ export function useMutateProjectRundowns() {
ontimeQueryClient.setQueryData(PROJECT_RUNDOWNS, response.data);
},
});
const { mutateAsync: duplicate } = useMutation({
mutationFn: duplicateRundown,
onMutate: () => {
@@ -40,7 +47,7 @@ export function useMutateProjectRundowns() {
ontimeQueryClient.setQueryData(PROJECT_RUNDOWNS, response.data);
},
});
const { mutateAsync: rename } = useMutation({
mutationFn: ([rundownId, title]: Parameters<typeof renameRundown>) => renameRundown(rundownId, title),
onMutate: () => {
@@ -1,6 +1,6 @@
import { useMemo } from 'react';
import { useQuery } from '@tanstack/react-query';
import { EntryId, OntimeEntry, Rundown } from 'ontime-types';
import { useMemo } from 'react';
import { queryRefetchIntervalSlow } from '../../ontimeConfig';
import { RUNDOWN } from '../api/constants';
@@ -1,6 +1,6 @@
import { useMemo } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { OntimeView, URLPreset } from 'ontime-types';
import { useMemo } from 'react';
import { queryRefetchIntervalSlow } from '../../ontimeConfig';
import { URL_PRESETS } from '../api/constants';
@@ -1,11 +1,10 @@
import { MessageTag } from 'ontime-types';
import { useEffect } from 'react';
import { useLocation, useNavigate } from 'react-router';
import { MessageTag } from 'ontime-types';
import { useShallow } from 'zustand/shallow';
import { useClientStore } from '../stores/clientStore';
import { sendSocket } from '../utils/socket';
import { useIsOnline } from './useSocket';
export const useClientPath = () => {
@@ -1,10 +1,7 @@
import { useCallback, useMemo } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import {
EntryId,
InsertOptions,
isOntimeEvent,
isOntimeGroup,
MaybeString,
OntimeDelay,
OntimeEntry,
@@ -16,8 +13,11 @@ import {
SupportedEntry,
TimeField,
TimeStrategy,
isOntimeEvent,
isOntimeGroup,
} from 'ontime-types';
import {
MILLIS_PER_SECOND,
addToRundown,
createDelay,
createEvent,
@@ -26,22 +26,22 @@ import {
dayInMs,
generateId,
getInsertAfterId,
MILLIS_PER_SECOND,
parseUserTime,
resolveInsertParent,
swapEventData,
} from 'ontime-utils';
import { useCallback, useMemo } from 'react';
import { moveDown, moveUp, orderEntries } from '../../features/rundown/rundown.utils';
import { RUNDOWN } from '../api/constants';
import {
ReorderEntry,
deleteEntries,
patchReorderEntry,
postAddEntry,
postCloneEntry,
putBatchEditEvents,
putEditEntry,
ReorderEntry,
requestApplyDelay,
requestDeleteAll,
requestEventSwap,
@@ -1,5 +1,5 @@
import { RefObject, useCallback, useEffect } from 'react';
import { MaybeString } from 'ontime-types';
import { RefObject, useCallback, useEffect } from 'react';
function scrollToComponent<ComponentRef extends HTMLElement, ScrollRef extends HTMLElement>(
componentRef: RefObject<ComponentRef>,
@@ -1,5 +1,5 @@
import { RefObject, useCallback, useEffect } from 'react';
import { EntryId } from 'ontime-types';
import { RefObject, useCallback, useEffect } from 'react';
function scrollToComponent<ComponentRef extends HTMLElement, ScrollRef extends HTMLElement>(
componentRef: RefObject<ComponentRef> | null,
@@ -1,5 +1,5 @@
import { useMemo } from 'react';
import { useOs, useViewportSize } from '@mantine/hooks';
import { useMemo } from 'react';
export function useIsMobileDevice(): boolean {
const { width } = useViewportSize();
@@ -1,5 +1,5 @@
import { useMemo } from 'react';
import { useViewportSize } from '@mantine/hooks';
import { useMemo } from 'react';
export function useIsMobileScreen(): boolean {
const { width } = useViewportSize();
@@ -1,5 +1,5 @@
import { useMemo } from 'react';
import { useOs, useViewportSize } from '@mantine/hooks';
import { useMemo } from 'react';
export function useIsSmallDevice(): boolean {
const { width } = useViewportSize();
@@ -1,5 +1,5 @@
import { useMemo } from 'react';
import { useViewportSize } from '@mantine/hooks';
import { useMemo } from 'react';
export function useIsSmallScreen(): boolean {
const { width } = useViewportSize();
@@ -1,5 +1,5 @@
import { EndAction, TimerType, TimeStrategy } from 'ontime-types';
import { validateEndAction, validateTimerType, validateTimeStrategy } from 'ontime-utils';
import { EndAction, TimeStrategy, TimerType } from 'ontime-types';
import { validateEndAction, validateTimeStrategy, validateTimerType } from 'ontime-utils';
import { create } from 'zustand';
import { booleanFromLocalStorage } from '../utils/localStorage';
+1 -1
View File
@@ -1,6 +1,6 @@
import { useCallback } from 'react';
import { Log, LogLevel, LogOrigin, MessageTag } from 'ontime-types';
import { generateId, millisToString } from 'ontime-utils';
import { useCallback } from 'react';
import { useStore } from 'zustand';
import { createStore } from 'zustand/vanilla';
+1 -1
View File
@@ -1,5 +1,5 @@
import isEqual from 'react-fast-compare';
import { RuntimeStore, runtimeStorePlaceholder } from 'ontime-types';
import isEqual from 'react-fast-compare';
import { createWithEqualityFn, useStoreWithEqualityFn } from 'zustand/traditional';
const deepCompare = <T>(a: T, b: T) => isEqual(a, b);
@@ -1,2 +1,4 @@
.test {}
.another {}
.test {
}
.another {
}
@@ -3,15 +3,19 @@ import { MILLIS_PER_HOUR, MILLIS_PER_MINUTE, MILLIS_PER_SECOND } from 'ontime-ut
import { formatDuration, formatTime, nowInMillis } from '../time';
describe('nowInMillis()', () => {
afterEach(() => {
vi.useRealTimers();
});
it('should return the current time in milliseconds', () => {
const mockDate = new Date(2022, 1, 1, 13, 0, 0); // This date corresponds to 13:00:00
const expectedMillis = 13 * 60 * 60 * 1000;
const dateSpy = vi.spyOn(global, 'Date').mockImplementation(() => mockDate as any);
vi.useFakeTimers();
vi.setSystemTime(mockDate);
const result = nowInMillis();
expect(result).toBe(expectedMillis);
dateSpy.mockRestore();
});
});
@@ -1,5 +1,5 @@
import { Path, resolvePath } from 'react-router';
import { OntimeView, URLPreset } from 'ontime-types';
import { Path, resolvePath } from 'react-router';
import {
arePathsEquivalent,
@@ -231,11 +231,11 @@ describe('generateUrlPresetOptions', () => {
});
it('throws on invalid URL', () => {
expect(() => generateUrlPresetOptions('test', 'invalid-url')).toThrow();
expect(() => generateUrlPresetOptions('test', 'invalid-url')).toThrow('Invalid target view:');
});
it('throws on invalid route', () => {
expect(() => generateUrlPresetOptions('test', 'www.getontime.no/somethingelse/')).toThrow();
expect(() => generateUrlPresetOptions('test', 'www.getontime.no/somethingelse/')).toThrow('Invalid target view:');
});
});
+1 -1
View File
@@ -1,5 +1,5 @@
import { MaybeNumber } from 'ontime-types';
import { formatFromMillis, MILLIS_PER_HOUR, MILLIS_PER_MINUTE } from 'ontime-utils';
import { MILLIS_PER_HOUR, MILLIS_PER_MINUTE, formatFromMillis } from 'ontime-utils';
/**
* Parses a value in millis to a string which encodes a delay
+10 -2
View File
@@ -2,14 +2,22 @@ import type { MouseEvent } from 'react';
import { baseURI, serverURL } from '../../externals';
type ElectronWindow = Window & {
process?: { type?: string };
ipcRenderer?: {
send: (channel: string, args?: string | object) => void;
};
};
/**
* Open an external URLs: specifically for a electron / browser case
* If electron: ask main process to call a new browser window
* If browser: open in new tab
*/
export function openLink(url: string) {
if (window.process?.type === 'renderer') {
window.ipcRenderer.send('send-to-link', url);
const electronWindow = window as ElectronWindow;
if (electronWindow.process?.type === 'renderer' && electronWindow.ipcRenderer) {
electronWindow.ipcRenderer.send('send-to-link', url);
} else {
window.open(url);
}
@@ -1,7 +1,4 @@
import {
isOntimeEvent,
isOntimeGroup,
isPlayableEvent,
MaybeString,
OntimeDelay,
OntimeEntry,
@@ -9,6 +6,9 @@ import {
OntimeMilestone,
PlayableEvent,
Rundown,
isOntimeEvent,
isOntimeGroup,
isPlayableEvent,
} from 'ontime-types';
import { checkIsNextDay, isNewLatest } from 'ontime-utils';
@@ -151,13 +151,13 @@ function processEntry(
if (isPlayableEvent(entry)) {
processedData.isNextDay = checkIsNextDay(entry, processedData.previousEvent);
if (!processedData.isPast && !processedData.isLoaded) {
/**
* isLinkToLoaded is a chain value that we maintain until we
* a) find an unlinked event
* b) find a countToEnd event
*/
*/
processedData.totalGap += entry.gap;
processedData.isLinkedToLoaded =
entry.linkStart && !processedData.previousEvent?.countToEnd && processedData.isLinkedToLoaded;
+2 -3
View File
@@ -1,17 +1,16 @@
import { MaybeNumber, OntimeEvent, Settings, TimeFormat } from 'ontime-types';
import {
formatFromMillis,
getExpectedStart,
MILLIS_PER_HOUR,
MILLIS_PER_MINUTE,
MILLIS_PER_SECOND,
formatFromMillis,
getExpectedStart,
} from 'ontime-utils';
import { FORMAT_12, FORMAT_24 } from '../../viewerConfig';
import { APP_SETTINGS } from '../api/constants';
import { useExpectedStartData } from '../hooks/useSocket';
import { ontimeQueryClient } from '../queryClient';
import { ExtendedEntry } from './rundownMetadata';
/**
+1 -1
View File
@@ -1,6 +1,6 @@
import { Path, resolvePath } from 'react-router';
import { OntimeView, OntimeViewPresettable, URLPreset } from 'ontime-types';
import { checkRegex } from 'ontime-utils';
import { Path, resolvePath } from 'react-router';
/**
* Validates a preset against defined parameters
+5
View File
@@ -5,6 +5,11 @@ declare module '*.scss' {
export default content;
}
declare module '*.css' {
const content: Record<string, string>;
export default content;
}
type ListenerType = (event: 'string', args: unknown[]) => void;
declare global {
+1 -1
View File
@@ -23,7 +23,7 @@ export const isProduction = import.meta.env.PROD;
export const isDev = import.meta.env.DEV;
export const currentHostName = window.location.hostname;
export const isLocalhost = currentHostName === 'localhost' || currentHostName === '127.0.0.1';
export const isOntimeCloud = document.querySelector('base')?.hasAttribute('data-is-cloud')
export const isOntimeCloud = document.querySelector('base')?.hasAttribute('data-is-cloud');
export const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
export const supportsFullscreen = document.fullscreenEnabled;
@@ -1,7 +1,8 @@
import { ErrorBoundary } from '@sentry/react';
import { useKeyDown } from '../../common/hooks/useKeyDown';
import PanelContent from './panel-content/PanelContent';
import PanelList from './panel-list/PanelList';
import AboutPanel from './panel/about-panel/AboutPanel';
import AutomationPanel from './panel/automations-panel/AutomationPanel';
import FeaturePanel from './panel/feature-panel/FeaturePanel';
@@ -10,8 +11,6 @@ import NetworkLogPanel from './panel/network-panel/NetworkLogPanel';
import ProjectPanel from './panel/project-panel/ProjectPanel';
import SettingsPanel from './panel/settings-panel/SettingsPanel';
import ShutdownPanel from './panel/shutdown-panel/ShutdownPanel';
import PanelContent from './panel-content/PanelContent';
import PanelList from './panel-list/PanelList';
import useAppSettingsNavigation from './useAppSettingsNavigation';
import style from './AppSettings.module.scss';
@@ -59,7 +59,9 @@ function PanelListItem(props: PanelListItemProps) {
key={panel.id}
onClick={() => setLocation(panel.id as SettingsOptionId)}
onKeyDown={(event) => {
isKeyEnter(event) && setLocation(panel.id as SettingsOptionId);
if (isKeyEnter(event)) {
setLocation(panel.id as SettingsOptionId);
}
}}
className={classes}
tabIndex={0}
@@ -75,7 +77,9 @@ function PanelListItem(props: PanelListItemProps) {
key={secondary.id + index}
onClick={() => setLocation(secondary.id as SettingsOptionId)}
onKeyDown={(event) => {
isKeyEnter(event) && setLocation(secondary.id as SettingsOptionId);
if (isKeyEnter(event)) {
setLocation(secondary.id as SettingsOptionId);
}
}}
className={secondaryClasses}
role='button'
@@ -9,7 +9,6 @@ import {
websiteUrl,
} from '../../../../externals';
import * as Panel from '../../panel-utils/PanelUtils';
import AppVersion from './AppVersion';
export default function AboutPanel() {
@@ -1,16 +1,16 @@
import { useEffect, useMemo } from 'react';
import { useFieldArray, useForm } from 'react-hook-form';
import { IoAdd, IoTrash } from 'react-icons/io5';
import {
Automation,
AutomationDTO,
HTTPOutput,
isHTTPOutput,
isOntimeAction,
isOSCOutput,
OntimeAction,
OSCOutput,
OntimeAction,
isHTTPOutput,
isOSCOutput,
isOntimeAction,
} from 'ontime-types';
import { useEffect, useMemo } from 'react';
import { useFieldArray, useForm } from 'react-hook-form';
import { IoAdd, IoTrash } from 'react-icons/io5';
import { addAutomation, editAutomation, testOutput } from '../../../../common/api/automation';
import { maybeAxiosError } from '../../../../common/api/utils';
@@ -27,10 +27,9 @@ import useCustomFields from '../../../../common/hooks-query/useCustomFields';
import { preventEscape } from '../../../../common/utils/keyEvent';
import { startsWithHttp } from '../../../../common/utils/regex';
import * as Panel from '../../panel-utils/PanelUtils';
import TemplateInput from './template-input/TemplateInput';
import { isAutomation, makeFieldList } from './automationUtils';
import OntimeActionForm from './OntimeActionForm';
import TemplateInput from './template-input/TemplateInput';
import style from './AutomationForm.module.scss';
@@ -1,8 +1,7 @@
import useScrollIntoView from '../../../../common/hooks/useScrollIntoView';
import useAutomationSettings from '../../../../common/hooks-query/useAutomationSettings';
import useScrollIntoView from '../../../../common/hooks/useScrollIntoView';
import type { PanelBaseProps } from '../../panel-list/PanelList';
import * as Panel from '../../panel-utils/PanelUtils';
import AutomationSettingsForm from './AutomationSettingsForm';
import AutomationsList from './AutomationsList';
import TriggersList from './TriggersList';
@@ -1,6 +1,6 @@
import { AutomationDTO, NormalisedAutomation } from 'ontime-types';
import { Fragment, useState } from 'react';
import { IoAdd, IoPencil, IoTrash } from 'react-icons/io5';
import { AutomationDTO, NormalisedAutomation } from 'ontime-types';
import { deleteAutomation } from '../../../../common/api/automation';
import { maybeAxiosError } from '../../../../common/api/utils';
@@ -9,7 +9,6 @@ import IconButton from '../../../../common/components/buttons/IconButton';
import Tag from '../../../../common/components/tag/Tag';
import useAutomationSettings from '../../../../common/hooks-query/useAutomationSettings';
import * as Panel from '../../panel-utils/PanelUtils';
import AutomationForm from './AutomationForm';
const automationPlaceholder: AutomationDTO = {
@@ -1,6 +1,6 @@
import { AutomationDTO, OntimeAction, OntimeActionKey, SecondarySource } from 'ontime-types';
import { PropsWithChildren, useState } from 'react';
import { UseFormRegister, UseFormSetValue, UseFormWatch } from 'react-hook-form';
import { AutomationDTO, OntimeAction, OntimeActionKey, SecondarySource } from 'ontime-types';
import Input from '../../../../common/components/input/input/Input';
import Select from '../../../../common/components/select/Select';
@@ -1,6 +1,6 @@
import { NormalisedAutomation, TimerLifeCycle, TriggerDTO } from 'ontime-types';
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { NormalisedAutomation, TimerLifeCycle, TriggerDTO } from 'ontime-types';
import { addTrigger, editTrigger } from '../../../../common/api/automation';
import { maybeAxiosError } from '../../../../common/api/utils';
@@ -9,7 +9,6 @@ import Input from '../../../../common/components/input/input/Input';
import Select from '../../../../common/components/select/Select';
import { preventEscape } from '../../../../common/utils/keyEvent';
import * as Panel from '../../panel-utils/PanelUtils';
import { cycles } from './automationUtils';
interface TriggerFormProps {
@@ -1,13 +1,12 @@
import { NormalisedAutomation, Trigger } from 'ontime-types';
import { Fragment, useMemo, useState } from 'react';
import { IoAdd } from 'react-icons/io5';
import { NormalisedAutomation, Trigger } from 'ontime-types';
import { deleteTrigger } from '../../../../common/api/automation';
import { maybeAxiosError } from '../../../../common/api/utils';
import Button from '../../../../common/components/buttons/Button';
import useAutomationSettings from '../../../../common/hooks-query/useAutomationSettings';
import * as Panel from '../../panel-utils/PanelUtils';
import { checkDuplicates } from './automationUtils';
import AutomationForm from './TriggerForm';
import TriggersListItem from './TriggersListItem';
@@ -1,11 +1,10 @@
import { NormalisedAutomation, TimerLifeCycle } from 'ontime-types';
import { useState } from 'react';
import { IoPencil, IoTrash, IoWarningOutline } from 'react-icons/io5';
import { NormalisedAutomation, TimerLifeCycle } from 'ontime-types';
import IconButton from '../../../../common/components/buttons/IconButton';
import Tag from '../../../../common/components/tag/Tag';
import * as Panel from '../../panel-utils/PanelUtils';
import { cycles } from './automationUtils';
import AutomationForm from './TriggerForm';
@@ -1,9 +1,8 @@
import { forwardRef, useMemo, useState } from 'react';
import { mergeRefs, useClickOutside } from '@mantine/hooks';
import { forwardRef, useMemo, useState } from 'react';
import Input, { type InputProps } from '../../../../../common/components/input/input/Input';
import useCustomFields from '../../../../../common/hooks-query/useCustomFields';
import { makeAutoCompleteList, matchRemaining, selectFromLastTemplate } from './templateInput.utils';
import style from './TemplateInput.module.scss';
@@ -4,7 +4,6 @@ import GenerateLinkFormExport from '../../../sharing/GenerateLinkFormExport';
import type { PanelBaseProps } from '../../panel-list/PanelList';
import * as Panel from '../../panel-utils/PanelUtils';
import InfoNif from '../network-panel/NetworkInterfaces';
import ReportSettings from './ReportSettings';
import URLPresets from './URLPresets';
@@ -9,7 +9,6 @@ import useRundown from '../../../../common/hooks-query/useRundown';
import { cx } from '../../../../common/utils/styleUtils';
import { formatTime } from '../../../../common/utils/time';
import * as Panel from '../../panel-utils/PanelUtils';
import { CombinedReport, getCombinedReport, makeReportCSV } from './reportSettings.utils';
import style from './ReportSettings.module.scss';
@@ -1,6 +1,6 @@
import { URLPreset } from 'ontime-types';
import { useState } from 'react';
import { IoAdd, IoOpenOutline, IoPencil, IoTrash } from 'react-icons/io5';
import { URLPreset } from 'ontime-types';
import Button from '../../../../common/components/buttons/Button';
import IconButton from '../../../../common/components/buttons/IconButton';
@@ -11,7 +11,6 @@ import Tag from '../../../../common/components/tag/Tag';
import useUrlPresets, { useUpdateUrlPreset } from '../../../../common/hooks-query/useUrlPresets';
import { handleLinks } from '../../../../common/utils/linkUtils';
import * as Panel from '../../panel-utils/PanelUtils';
import URLPresetForm from './composite/URLPresetForm';
type FormState = {
@@ -6,4 +6,4 @@
display: flex;
flex-direction: column;
gap: 1rem;
}
}
@@ -1,6 +1,6 @@
import { OntimeView, OntimeViewPresettable, URLPreset } from 'ontime-types';
import { useEffect, useRef } from 'react';
import { useForm } from 'react-hook-form';
import { OntimeView, OntimeViewPresettable, URLPreset } from 'ontime-types';
import { maybeAxiosError, unwrapError } from '../../../../../common/api/utils';
import Button from '../../../../../common/components/buttons/Button';
@@ -1,4 +1,4 @@
import { EntryId, isOntimeEvent, MaybeNumber, OntimeReport, RundownEntries } from 'ontime-types';
import { EntryId, MaybeNumber, OntimeReport, RundownEntries, isOntimeEvent } from 'ontime-types';
import { makeCSVFromArrayOfArrays } from '../../../../common/utils/csv';
import { formatTime } from '../../../../common/utils/time';
@@ -1,6 +1,6 @@
import { CustomField, CustomFieldKey } from 'ontime-types';
import { useState } from 'react';
import { IoAdd } from 'react-icons/io5';
import { CustomField, CustomFieldKey } from 'ontime-types';
import { deleteCustomField, editCustomField, postCustomField } from '../../../../common/api/customFields';
import Button from '../../../../common/components/buttons/Button';
@@ -10,7 +10,6 @@ import ExternalLink from '../../../../common/components/link/external-link/Exter
import useCustomFields from '../../../../common/hooks-query/useCustomFields';
import { customFieldsDocsUrl } from '../../../../externals';
import * as Panel from '../../panel-utils/PanelUtils';
import CustomFieldEntry from './composite/CustomFieldEntry';
import CustomFieldForm from './composite/CustomFieldForm';
@@ -1,11 +1,10 @@
import useScrollIntoView from '../../../../common/hooks/useScrollIntoView';
import type { PanelBaseProps } from '../../panel-list/PanelList';
import * as Panel from '../../panel-utils/PanelUtils';
import SourcesPanel from './sources-panel/SourcesPanel';
import CustomFieldSettings from './CustomFields';
import ManageRundowns from './ManageRundowns';
import RundownDefaultSettings from './RundownDefaultSettings';
import SourcesPanel from './sources-panel/SourcesPanel';
export default function ManagePanel({ location }: PanelBaseProps) {
const defaultsRef = useScrollIntoView<HTMLDivElement>('defaults', location);
@@ -1,3 +1,4 @@
import { useDisclosure } from '@mantine/hooks';
import { useState } from 'react';
import {
IoAdd,
@@ -8,7 +9,6 @@ import {
IoPencilOutline,
IoTrash,
} from 'react-icons/io5';
import { useDisclosure } from '@mantine/hooks';
import { downloadAsExcel } from '../../../../common/api/excel';
import { maybeAxiosError } from '../../../../common/api/utils';
@@ -20,7 +20,6 @@ import Tag from '../../../../common/components/tag/Tag';
import { useMutateProjectRundowns, useProjectRundowns } from '../../../../common/hooks-query/useProjectRundowns';
import { cx } from '../../../../common/utils/styleUtils';
import * as Panel from '../../panel-utils/PanelUtils';
import RundownRenameForm from './composite/RundownRenameForm';
import { ManageRundownForm } from './ManageRundownForm';
@@ -1,4 +1,4 @@
import { EndAction, TimerType, TimeStrategy } from 'ontime-types';
import { EndAction, TimeStrategy, TimerType } from 'ontime-types';
import { parseUserTime } from 'ontime-utils';
import TimeInput from '../../../../common/components/input/time-input/TimeInput';
@@ -1,13 +1,12 @@
import { CustomField, CustomFieldKey } from 'ontime-types';
import { useState } from 'react';
import { IoPencil, IoTrash } from 'react-icons/io5';
import { CustomField, CustomFieldKey } from 'ontime-types';
import IconButton from '../../../../../common/components/buttons/IconButton';
import CopyTag from '../../../../../common/components/copy-tag/CopyTag';
import Swatch from '../../../../../common/components/input/colour-input/Swatch';
import Tag from '../../../../../common/components/tag/Tag';
import * as Panel from '../../../panel-utils/PanelUtils';
import CustomFieldForm from './CustomFieldForm';
import style from '../ManagePanel.module.scss';
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { CustomField } from 'ontime-types';
import { checkRegex, customFieldLabelToKey } from 'ontime-utils';
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { maybeAxiosError } from '../../../../../common/api/utils';
import Button from '../../../../../common/components/buttons/Button';
@@ -1,6 +1,6 @@
import { checkRegex } from 'ontime-utils';
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { checkRegex } from 'ontime-utils';
import { maybeAxiosError } from '../../../../../common/api/utils';
import Button from '../../../../../common/components/buttons/Button';
@@ -6,9 +6,8 @@ const googleSheetDocsUrl = 'https://docs.getontime.no/features/import-spreadshee
export default function GSheetInfo() {
return (
<Info>
Ontime can import data from spreadsheets by: <br />
- importing the spreadsheet file in Ontime <br />
- synchronising your project with a Google Sheet
Ontime can import data from spreadsheets by: <br />- importing the spreadsheet file in Ontime <br />-
synchronising your project with a Google Sheet
<br />
<br />
To synchronise with a Google Sheet, you will need to allow Ontime to authenticate with your Google account.
@@ -8,7 +8,6 @@ import CopyTag from '../../../../../common/components/copy-tag/CopyTag';
import Input from '../../../../../common/components/input/input/Input';
import { openLink } from '../../../../../common/utils/linkUtils';
import * as Panel from '../../../panel-utils/PanelUtils';
import useGoogleSheet from './useGoogleSheet';
import { useSheetStore } from './useSheetStore';
@@ -1,12 +1,11 @@
import { useState } from 'react';
import { CustomFields, Rundown, RundownSummary } from 'ontime-types';
import { millisToString } from 'ontime-utils';
import { useState } from 'react';
import Button from '../../../../../common/components/buttons/Button';
import useRundown from '../../../../../common/hooks-query/useRundown';
import { formatDuration } from '../../../../../common/utils/time';
import * as Panel from '../../../panel-utils/PanelUtils';
import PreviewSpreadsheet from './preview/PreviewRundown';
import useGoogleSheet from './useGoogleSheet';
import { useSheetStore } from './useSheetStore';
@@ -1,6 +1,6 @@
import { ImportMap, getErrorMessage } from 'ontime-utils';
import { ChangeEvent, useRef, useState } from 'react';
import { IoCloudOutline, IoDownloadOutline } from 'react-icons/io5';
import { getErrorMessage, ImportMap } from 'ontime-utils';
import {
importRundownPreview as importRundownPreviewExcel,
@@ -12,10 +12,9 @@ import Button from '../../../../../common/components/buttons/Button';
import * as Editor from '../../../../../common/components/editor-utils/EditorUtils';
import { validateExcelImport } from '../../../../../common/utils/uploadUtils';
import * as Panel from '../../../panel-utils/PanelUtils';
import ImportMapForm from './import-map/ImportMapForm';
import GSheetInfo from './GSheetInfo';
import GSheetSetup from './GSheetSetup';
import ImportMapForm from './import-map/ImportMapForm';
import ImportReview from './ImportReview';
import useGoogleSheet from './useGoogleSheet';
import { useSheetStore } from './useSheetStore';
@@ -1,7 +1,7 @@
import { ImportMap, checkRegex } from 'ontime-utils';
import { useEffect, useState } from 'react';
import { useFieldArray, useForm } from 'react-hook-form';
import { IoAdd, IoTrash } from 'react-icons/io5';
import { checkRegex, ImportMap } from 'ontime-utils';
import Button from '../../../../../../common/components/buttons/Button';
import IconButton from '../../../../../../common/components/buttons/IconButton';
@@ -12,8 +12,7 @@ import Tooltip from '../../../../../../common/components/tooltip/Tooltip';
import * as Panel from '../../../../panel-utils/PanelUtils';
import useGoogleSheet from '../useGoogleSheet';
import { useSheetStore } from '../useSheetStore';
import { convertToImportMap, getPersistedOptions, NamedImportMap, persistImportMap } from './importMapUtils';
import { NamedImportMap, convertToImportMap, getPersistedOptions, persistImportMap } from './importMapUtils';
import style from '../SourcesPanel.module.scss';

Some files were not shown because too many files have changed in this diff Show More