From 2316bbebacb85a3f15480bc37c6886f6c0361f1e Mon Sep 17 00:00:00 2001 From: Carlos Valente <34649812+cpvalente@users.noreply.github.com> Date: Sat, 18 Nov 2023 08:35:54 +0100 Subject: [PATCH] chore: type improvements (#598) * chore: type improvements * ci: typechecking in pipeline --- .github/workflows/test_v2.yml | 12 +++---- .../components/input/text-input/TextInput.tsx | 6 ++-- .../src/common/hooks-query/useOscSettings.ts | 2 ++ .../src/common/hooks-query/useRundown.ts | 6 ++-- apps/client/src/common/hooks/useFullscreen.ts | 2 ++ .../CuesheetTableSettings.tsx | 4 +-- .../src/features/cuesheet/cuesheetUtils.ts | 3 +- .../modals/integration-modal/OscSettings.tsx | 2 ++ .../modals/settings-modal/AliasesForm.tsx | 2 +- .../modals/settings-modal/AppSettings.tsx | 6 ++-- .../modals/settings-modal/ProjectDataForm.tsx | 2 +- .../settings-modal/ViewSettingsForm.tsx | 2 +- .../event-block/composite/BlockActionMenu.tsx | 2 +- .../src/features/viewers/ViewWrapper.tsx | 35 ++++++++++++++++--- .../features/viewers/countdown/Countdown.tsx | 4 ++- .../__test__/DataProvider.test.ts | 22 ++++++++---- .../src/utils/__tests__/parserUtils.test.ts | 6 ++-- 17 files changed, 84 insertions(+), 34 deletions(-) diff --git a/.github/workflows/test_v2.yml b/.github/workflows/test_v2.yml index e3a70a76b..ac849f32d 100644 --- a/.github/workflows/test_v2.yml +++ b/.github/workflows/test_v2.yml @@ -28,19 +28,19 @@ jobs: run: pnpm install --frozen-lockfile # Run code quality per package - - name: React - Run linter + - name: React - Run linter + TypeScript checks if: always() - run: pnpm lint + run: pnpm lint && tsc --noEmit working-directory: ./apps/client - - name: Server - Run linter + - name: Server - Run linter + TypeScript checks if: always() - run: pnpm lint + run: pnpm lint && tsc --noEmit working-directory: ./apps/server - - name: Utils - Run linter + - name: Utils - Run linter + TypeScript checks if: always() - run: pnpm lint + run: pnpm lint && tsc --noEmit working-directory: ./packages/utils - name: Types - Run linter diff --git a/apps/client/src/common/components/input/text-input/TextInput.tsx b/apps/client/src/common/components/input/text-input/TextInput.tsx index bfc429ad3..3502bb4a0 100644 --- a/apps/client/src/common/components/input/text-input/TextInput.tsx +++ b/apps/client/src/common/components/input/text-input/TextInput.tsx @@ -19,9 +19,11 @@ interface TextInputProps extends BaseProps { isTextArea?: false; } +type ResizeOptions = 'horizontal' | 'vertical' | 'none'; + interface TextAreaProps extends BaseProps { isTextArea: true; - resize?: 'horizontal' | 'vertical' | 'none'; + resize?: ResizeOptions; } type InputProps = TextInputProps | TextAreaProps; @@ -35,7 +37,7 @@ export default function TextInput(props: InputProps) { const textInputProps = useReactiveTextInput(initialText, submitCallback, { submitOnEnter: true }); const textAreaProps = useReactiveTextInput(initialText, submitCallback); - let resize = 'none'; + let resize: ResizeOptions = 'none'; if (isTextArea) { resize = (props as TextAreaProps)?.resize ?? 'none'; } diff --git a/apps/client/src/common/hooks-query/useOscSettings.ts b/apps/client/src/common/hooks-query/useOscSettings.ts index 67becece2..09e84f0da 100644 --- a/apps/client/src/common/hooks-query/useOscSettings.ts +++ b/apps/client/src/common/hooks-query/useOscSettings.ts @@ -1,3 +1,5 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +//@ts-nocheck -- working on it import { useMutation, useQuery } from '@tanstack/react-query'; import { OSCSettings } from 'ontime-types'; diff --git a/apps/client/src/common/hooks-query/useRundown.ts b/apps/client/src/common/hooks-query/useRundown.ts index cd3a02253..d213eddc0 100644 --- a/apps/client/src/common/hooks-query/useRundown.ts +++ b/apps/client/src/common/hooks-query/useRundown.ts @@ -9,21 +9,21 @@ const cachedRundownPlaceholder = { rundown: [], revision: -1 }; // TODO: can we leverage structural sharing to see if data has changed? export default function useRundown() { - return useQuery({ + const { data, status, isError, refetch, isFetching } = useQuery({ queryKey: RUNDOWN, queryFn: fetchCachedRundown, placeholderData: cachedRundownPlaceholder, retry: 5, - select: (data) => data.rundown, retryDelay: (attempt) => attempt * 2500, refetchInterval: queryRefetchInterval, networkMode: 'always', // structuralSharing: (oldData: GetRundownCached | undefined, newData: GetRundownCached) => { // if (oldData === undefined) { - // cachedRundownPlaceholder; + // return cachedRundownPlaceholder; // } // const hasDataChanged = oldData?.revision === newData.revision; // return hasDataChanged ? oldData : newData; // }, }); + return { data: data?.rundown ?? [], status, isError, refetch, isFetching }; } diff --git a/apps/client/src/common/hooks/useFullscreen.ts b/apps/client/src/common/hooks/useFullscreen.ts index 57f8e64aa..51e7fa790 100644 --- a/apps/client/src/common/hooks/useFullscreen.ts +++ b/apps/client/src/common/hooks/useFullscreen.ts @@ -1,3 +1,5 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +//@ts-nocheck -- working on it import { useCallback, useEffect, useState } from 'react'; interface WebkitDocument extends Document { diff --git a/apps/client/src/features/cuesheet/cuesheet-table-settings/CuesheetTableSettings.tsx b/apps/client/src/features/cuesheet/cuesheet-table-settings/CuesheetTableSettings.tsx index a3b125507..e292a681c 100644 --- a/apps/client/src/features/cuesheet/cuesheet-table-settings/CuesheetTableSettings.tsx +++ b/apps/client/src/features/cuesheet/cuesheet-table-settings/CuesheetTableSettings.tsx @@ -1,4 +1,4 @@ -import { memo } from 'react'; +import { memo, ReactNode } from 'react'; import { Button, Checkbox, Switch } from '@chakra-ui/react'; import { Column } from '@tanstack/react-table'; import { OntimeRundownEntry } from 'ontime-types'; @@ -44,7 +44,7 @@ function CuesheetTableSettings(props: CuesheetTableSettingsProps) { defaultChecked={visible} onChange={column.getToggleVisibilityHandler()} /> - {columnHeader} + {columnHeader as ReactNode} ); })} diff --git a/apps/client/src/features/cuesheet/cuesheetUtils.ts b/apps/client/src/features/cuesheet/cuesheetUtils.ts index a85f707fc..13619dfac 100644 --- a/apps/client/src/features/cuesheet/cuesheetUtils.ts +++ b/apps/client/src/features/cuesheet/cuesheetUtils.ts @@ -9,7 +9,7 @@ import { millisToString } from 'ontime-utils'; * @return {string} */ -export const parseField = (field: keyof OntimeRundown, data: unknown): string => { +export const parseField = (field: T, data: unknown): string => { let val; switch (field) { case 'timeStart': @@ -96,6 +96,7 @@ export const makeTable = (headerData: ProjectData, rundown: OntimeRundown, userF rundown.forEach((entry) => { const row: string[] = []; + // @ts-expect-error -- not sure how to type this fieldOrder.forEach((field) => row.push(parseField(field, entry[field]))); data.push(row); }); diff --git a/apps/client/src/features/modals/integration-modal/OscSettings.tsx b/apps/client/src/features/modals/integration-modal/OscSettings.tsx index 4c2d83659..fccd8403b 100644 --- a/apps/client/src/features/modals/integration-modal/OscSettings.tsx +++ b/apps/client/src/features/modals/integration-modal/OscSettings.tsx @@ -1,3 +1,5 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +//@ts-nocheck -- working on it import { useEffect } from 'react'; import { useForm } from 'react-hook-form'; import { FormControl, Input, Switch } from '@chakra-ui/react'; diff --git a/apps/client/src/features/modals/settings-modal/AliasesForm.tsx b/apps/client/src/features/modals/settings-modal/AliasesForm.tsx index b9a155cf2..cf3e1f12e 100644 --- a/apps/client/src/features/modals/settings-modal/AliasesForm.tsx +++ b/apps/client/src/features/modals/settings-modal/AliasesForm.tsx @@ -48,7 +48,7 @@ export default function AliasesForm() { useEffect(() => { if (data) { - reset(data); + reset({ aliases: data }); } }, [data, reset]); diff --git a/apps/client/src/features/modals/settings-modal/AppSettings.tsx b/apps/client/src/features/modals/settings-modal/AppSettings.tsx index 983ac3d4a..2a697cd8c 100644 --- a/apps/client/src/features/modals/settings-modal/AppSettings.tsx +++ b/apps/client/src/features/modals/settings-modal/AppSettings.tsx @@ -50,7 +50,7 @@ export default function AppSettingsModal() { reset(data); }; - const disableInputs = status === 'loading'; + const disableInputs = status === 'pending'; if (isFetching) { return ; @@ -87,7 +87,7 @@ export default function AppSettingsModal() { description='Protect the editor with a pin code' error={errors.editorKey?.message} > - + - +
; diff --git a/apps/client/src/features/modals/settings-modal/ViewSettingsForm.tsx b/apps/client/src/features/modals/settings-modal/ViewSettingsForm.tsx index bdce1ce4e..76c8806e6 100644 --- a/apps/client/src/features/modals/settings-modal/ViewSettingsForm.tsx +++ b/apps/client/src/features/modals/settings-modal/ViewSettingsForm.tsx @@ -94,7 +94,7 @@ export default function ViewSettingsForm() { CSS Override Ontime will use the CSS file at its install location.
- {info.cssOverride} + {info?.cssOverride} For more information, see the docs
diff --git a/apps/client/src/features/rundown/event-block/composite/BlockActionMenu.tsx b/apps/client/src/features/rundown/event-block/composite/BlockActionMenu.tsx index c5ecc0f69..4f0b2470e 100644 --- a/apps/client/src/features/rundown/event-block/composite/BlockActionMenu.tsx +++ b/apps/client/src/features/rundown/event-block/composite/BlockActionMenu.tsx @@ -13,7 +13,7 @@ import { EventItemActions } from '../../RundownEntry'; interface BlockActionMenuProps { enableDelete?: boolean; showClone?: boolean; - actionHandler: (action: EventItemActions, payload?: unknown) => void; + actionHandler: (action: EventItemActions, payload?: any) => void; className?: string; } diff --git a/apps/client/src/features/viewers/ViewWrapper.tsx b/apps/client/src/features/viewers/ViewWrapper.tsx index 01bbe9b51..c51069d67 100644 --- a/apps/client/src/features/viewers/ViewWrapper.tsx +++ b/apps/client/src/features/viewers/ViewWrapper.tsx @@ -1,6 +1,6 @@ -/* eslint-disable react/display-name */ import { ComponentType, useMemo } from 'react'; -import { SupportedEvent } from 'ontime-types'; +import { TimeManagerType } from 'common/models/TimeManager.type'; +import { Message, OntimeEvent, ProjectData, SupportedEvent, TimerMessage, ViewSettings } from 'ontime-types'; import { useStore } from 'zustand'; import useProjectData from '../../common/hooks-query/useProjectData'; @@ -9,8 +9,32 @@ import useViewSettings from '../../common/hooks-query/useViewSettings'; import { runtime } from '../../common/stores/runtime'; import { useViewOptionsStore } from '../../common/stores/viewOptions'; -const withData =

(Component: ComponentType

) => { - return (props: Partial

) => { +type WithDataProps = { + isMirrored: boolean; + pres: TimerMessage; + publ: Message; + lower: Message; + eventNow: OntimeEvent | null; + publicEventNow: OntimeEvent | null; + eventNext: OntimeEvent | null; + publicEventNext: OntimeEvent | null; + time: TimeManagerType; + events: OntimeEvent[]; + backstageEvents: OntimeEvent[]; + selectedId: string | null; + publicSelectedId: string | null; + nextId: string | null; + general: ProjectData; + viewSettings: ViewSettings; + onAir: boolean; +}; + +function getDisplayName(Component: React.ComponentType): string { + return Component.displayName || Component.name || 'Component'; +} + +const withData =

(Component: ComponentType

) => { + const WithDataComponent = (props: P) => { // persisted app state const isMirrored = useViewOptionsStore((state) => state.mirror); @@ -84,6 +108,9 @@ const withData =

(Component: ComponentType

) => { /> ); }; + + WithDataComponent.displayName = `WithData(${getDisplayName(Component)})`; + return WithDataComponent; }; export default withData; diff --git a/apps/client/src/features/viewers/countdown/Countdown.tsx b/apps/client/src/features/viewers/countdown/Countdown.tsx index c43ea097d..441ded578 100644 --- a/apps/client/src/features/viewers/countdown/Countdown.tsx +++ b/apps/client/src/features/viewers/countdown/Countdown.tsx @@ -121,7 +121,9 @@ export default function Countdown(props: CountdownProps) {

{clock}
-
{getLocalizedString(`countdown.${runningMessage}`)}
+ {runningMessage !== TimerMessage.unhandled && ( +
{getLocalizedString(`countdown.${runningMessage}`)}
+ )} {formattedTimer} diff --git a/apps/server/src/classes/data-provider/__test__/DataProvider.test.ts b/apps/server/src/classes/data-provider/__test__/DataProvider.test.ts index da717cc46..bcd83db7a 100644 --- a/apps/server/src/classes/data-provider/__test__/DataProvider.test.ts +++ b/apps/server/src/classes/data-provider/__test__/DataProvider.test.ts @@ -1,3 +1,4 @@ +import { Alias, DatabaseModel, OntimeRundown, Settings } from 'ontime-types'; import { safeMerge } from '../DataProvider.utils.js'; describe('safeMerge', () => { @@ -5,8 +6,10 @@ describe('safeMerge', () => { rundown: [], project: { title: 'existing title', + description: 'existing description', publicUrl: 'existing public URL', backstageUrl: 'existing backstageUrl', + publicInfo: 'existing backstageInfo', backstageInfo: 'existing backstageInfo', }, settings: { @@ -42,7 +45,7 @@ describe('safeMerge', () => { onFinish: [], }, }, - }; + } as DatabaseModel; it('returns existing data if new data is not provided', () => { const mergedData = safeMerge(existing, undefined); @@ -51,7 +54,7 @@ describe('safeMerge', () => { it('merges the rundown key', () => { const newData = { - rundown: [{ name: 'item 1' }, { name: 'item 2' }], + rundown: [{ title: 'item 1' }, { title: 'item 2' }] as OntimeRundown, }; const mergedData = safeMerge(existing, newData); expect(mergedData.rundown).toEqual(newData.rundown); @@ -64,9 +67,11 @@ describe('safeMerge', () => { publicInfo: 'new public info', }, }; + // @ts-expect-error -- just testing const mergedData = safeMerge(existing, newData); expect(mergedData.project).toEqual({ title: 'new title', + description: 'existing description', publicUrl: 'existing public URL', publicInfo: 'new public info', backstageUrl: 'existing backstageUrl', @@ -79,7 +84,7 @@ describe('safeMerge', () => { settings: { serverPort: 3000, language: 'pt', - }, + } as Settings, }; const mergedData = safeMerge(existing, newData); expect(mergedData.settings).toEqual({ @@ -108,6 +113,7 @@ describe('safeMerge', () => { }, }, }; + //@ts-expect-error -- testing partial merge const mergedData = safeMerge(existing, newData); expect(mergedData.osc).toEqual({ portIn: 7777, @@ -135,7 +141,7 @@ describe('safeMerge', () => { it('should merge the aliases key when present', () => { const existingData = { rundown: [], - event: { + project: { title: '', publicUrl: '', publicInfo: '', @@ -183,10 +189,13 @@ describe('safeMerge', () => { onFinish: [], }, }, - }; + } as DatabaseModel; const newData = { - aliases: ['alias1', 'alias2'], + aliases: [ + { enabled: true, alias: 'alias1', pathAndParams: '' }, + { enabled: true, alias: 'alias2', pathAndParams: '' }, + ] as Alias[], }; const mergedData = safeMerge(existingData, newData); @@ -217,6 +226,7 @@ describe('safeMerge', () => { user3: 'David', }; + //@ts-expect-error -- testing partial merge const result = safeMerge(existing, newData); expect(result.userFields).toEqual(expected); }); diff --git a/apps/server/src/utils/__tests__/parserUtils.test.ts b/apps/server/src/utils/__tests__/parserUtils.test.ts index a69957328..79f8f5db5 100644 --- a/apps/server/src/utils/__tests__/parserUtils.test.ts +++ b/apps/server/src/utils/__tests__/parserUtils.test.ts @@ -34,13 +34,13 @@ describe('mergeObject()', () => { third: 'yes', }; const b = { - first: 0, + first: 'no', second: null, third: '', }; const merged = mergeObject(a, b); expect(merged).toStrictEqual({ - first: 0, + first: 'no', second: null, third: '', }); @@ -57,6 +57,7 @@ describe('mergeObject()', () => { third: '', forth: 'not-this', }; + // @ts-expect-error -- testing changing type const merged = mergeObject(a, b); expect(merged).toStrictEqual({ first: 0, @@ -83,6 +84,7 @@ describe('mergeObject()', () => { }, }; + // @ts-expect-error -- testing missing property const merged = mergeObject(a, b); expect(merged.name).toBe('Doe');