diff --git a/.github/workflows/ontime_cy.yml b/.github/workflows/ontime_cy.yml index 7952f0f35..627e4d693 100644 --- a/.github/workflows/ontime_cy.yml +++ b/.github/workflows/ontime_cy.yml @@ -43,7 +43,7 @@ jobs: # App - name: Electron - Install dependencies - run: yarn install && yarn setdb + run: yarn setup working-directory: ./server - name: Server - run tests diff --git a/client/.eslintrc b/client/.eslintrc index 27e0ad027..c1e08bcb1 100644 --- a/client/.eslintrc +++ b/client/.eslintrc @@ -12,12 +12,14 @@ "eslint:recommended", "plugin:react/recommended", "plugin:@typescript-eslint/recommended", - "eslint-config-prettier" + "eslint-config-prettier", + "plugin:@tanstack/eslint-plugin-query/recommended" ], "plugins": [ "react", "testing-library", - "simple-import-sort" + "simple-import-sort", + "@tanstack/query" ], "rules": { "no-useless-concat": "warn", diff --git a/client/index.html b/client/index.html index 4a2721dee..c6a234f36 100644 --- a/client/index.html +++ b/client/index.html @@ -32,6 +32,6 @@
- + diff --git a/client/package.json b/client/package.json index 5faa02bd9..79aa3f8c8 100644 --- a/client/package.json +++ b/client/package.json @@ -10,14 +10,15 @@ "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", "@react-icons/all-files": "^4.1.0", - "@tanstack/react-query": "^4.10.3", - "@tanstack/react-query-devtools": "^4.11.0", + "@tanstack/react-query": "^4.18.0", + "@tanstack/react-query-devtools": "^4.18.0", "autosize": "^5.0.1", - "axios": "^0.27.2", + "axios": "^1.2.0", "color": "^4.2.3", + "express": "^4.18.2", "framer-motion": "^7.5.3", - "jotai": "^1.8.5", - "luxon": "^3.0.1", + "jotai": "^1.10.0", + "luxon": "^3.1.0", "react": "^18.2.0", "react-beautiful-dnd": "^13.1.1", "react-dom": "^18.2.0", @@ -25,7 +26,7 @@ "react-qr-code": "^2.0.5", "react-router-dom": "^6.3.0", "react-table": "^7.7.0", - "socket.io-client": "^4.5.2", + "socket.io-client": "^4.5.4", "typeface-open-sans": "^1.1.13", "web-vitals": "^2.1.4" }, @@ -50,6 +51,7 @@ ] }, "devDependencies": { + "@tanstack/eslint-plugin-query": "^4.15.1", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.1.1", "@testing-library/react-hooks": "^8.0.0", diff --git a/client/src/App.jsx b/client/src/App.tsx similarity index 94% rename from client/src/App.jsx rename to client/src/App.tsx index b1dc2c2b5..ff3df35ea 100644 --- a/client/src/App.jsx +++ b/client/src/App.tsx @@ -13,12 +13,13 @@ import theme from './theme/theme'; import AppRouter from './AppRouter'; // Load Open Sans typeface +// @ts-expect-error no types from font import import('typeface-open-sans'); function App() { const { isElectron, sendToElectron } = useElectronEvent(); - const handleKeyPress = useCallback((event) => { + const handleKeyPress = useCallback((event:KeyboardEvent) => { // handle held key if (event.repeat) return; // check if the alt key is pressed diff --git a/client/src/AppRouter.jsx b/client/src/AppRouter.tsx similarity index 100% rename from client/src/AppRouter.jsx rename to client/src/AppRouter.tsx diff --git a/client/src/appConstants.js b/client/src/appConstants.ts similarity index 100% rename from client/src/appConstants.js rename to client/src/appConstants.ts diff --git a/client/src/common/api/apiConstants.js b/client/src/common/api/apiConstants.ts similarity index 100% rename from client/src/common/api/apiConstants.js rename to client/src/common/api/apiConstants.ts diff --git a/client/src/common/components/buttons/TooltipLoadingActionBtn.tsx b/client/src/common/components/buttons/TooltipLoadingActionBtn.tsx deleted file mode 100644 index b1832ffde..000000000 --- a/client/src/common/components/buttons/TooltipLoadingActionBtn.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useCallback, useState } from 'react'; -import { IconButton, IconButtonProps, Tooltip } from '@chakra-ui/react'; - -interface TooltipLoadingActionBtnProps extends IconButtonProps { - clickHandler: () => void; - tooltip: string; - openDelay?: number; -} - -export default function TooltipLoadingActionBtn(props: TooltipLoadingActionBtnProps) { - const { clickHandler, icon, size = 'xs', tooltip, openDelay = 0, ...rest } = props; - const [loading, setLoading] = useState(false); - - const handleClick = useCallback(() => { - setLoading(true); - clickHandler(); - }, [clickHandler, setLoading]); - - return ( - - - - ); -} - diff --git a/client/src/common/components/errorBoundary/ErrorBoundary.jsx b/client/src/common/components/errorBoundary/ErrorBoundary.jsx index 9f8e31c5e..373c5e872 100644 --- a/client/src/common/components/errorBoundary/ErrorBoundary.jsx +++ b/client/src/common/components/errorBoundary/ErrorBoundary.jsx @@ -28,7 +28,7 @@ class ErrorBoundary extends React.Component { try { this.context.emitError(error.toString()); } catch (e) { - console.log('Unable to emit error', error, e ) + console.log('Unable to emit error', error, e); } this.reportContent = `${error} ${info.componentStack}`; } @@ -36,11 +36,12 @@ class ErrorBoundary extends React.Component { render() { if (this.state.errorMessage) { return ( -
+

:/

Something went wrong

-

{ if (navigator.clipboard) { @@ -50,11 +51,12 @@ class ErrorBoundary extends React.Component { }} > Copy error -

-

+

{ - if (window.process.type === 'renderer') { + if (window?.process?.type === 'renderer') { window.ipcRenderer.send('reload'); } else { window.location.reload(); @@ -62,7 +64,7 @@ class ErrorBoundary extends React.Component { }} > Reload interface -

+
); diff --git a/client/src/common/components/navigation-menu/NavigationMenu.tsx b/client/src/common/components/navigation-menu/NavigationMenu.tsx index 1b7d33715..b74fcaebb 100644 --- a/client/src/common/components/navigation-menu/NavigationMenu.tsx +++ b/client/src/common/components/navigation-menu/NavigationMenu.tsx @@ -15,7 +15,6 @@ import { useKeyDown } from '../../hooks/useKeyDown'; import style from './NavigationMenu.module.scss'; -// Todo: restyle _main.scss links export default function NavigationMenu() { const location = useLocation(); @@ -65,6 +64,7 @@ export default function NavigationMenu() {
{ isKeyEnter(event) && handleFullscreen(); @@ -76,6 +76,7 @@ export default function NavigationMenu() {
{ isKeyEnter(event) && handleMirror(); diff --git a/client/src/common/components/state/Empty.jsx b/client/src/common/components/state/Empty.jsx deleted file mode 100644 index e35499d01..000000000 --- a/client/src/common/components/state/Empty.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import { ReactComponent as Emptyimage } from 'assets/images/empty.svg'; -import PropTypes from 'prop-types'; - -import style from './Empty.module.scss'; - -export default function Empty(props) { - const { text, dark, ...rest } = props; - return ( -
- - {text} -
- ); -} - -Empty.propTypes = { - text: PropTypes.string, - dark: PropTypes.bool, -} \ No newline at end of file diff --git a/client/src/common/components/state/Empty.module.scss b/client/src/common/components/state/Empty.module.scss index dcad02bbc..68a0515f3 100644 --- a/client/src/common/components/state/Empty.module.scss +++ b/client/src/common/components/state/Empty.module.scss @@ -1,26 +1,17 @@ -@use '../../../theme/main' as *; +@use '../../../theme/ontimeColours' as *; .emptyContainer { width: 100%; text-align: center; - color: $bg-black-200; + color: $gray-1350; .empty { width: 100%; - opacity: 0.3; + opacity: 0.6; } .text { font-weight: 600; font-size: 2em; } - - &.dark { - background: $bg-black; - color: $bg-gray-500; - - .empty { - opacity: 1; - } - } } diff --git a/client/src/common/components/state/Empty.tsx b/client/src/common/components/state/Empty.tsx new file mode 100644 index 000000000..7b3a42472 --- /dev/null +++ b/client/src/common/components/state/Empty.tsx @@ -0,0 +1,17 @@ +import { ReactComponent as Emptyimage } from 'assets/images/empty.svg'; + +import style from './Empty.module.scss'; + +interface EmptyProps { + text: string; +} + +export default function Empty(props: EmptyProps) { + const { text, ...rest } = props; + return ( +
+ + {text} +
+ ); +} diff --git a/client/src/common/context/CollapseContext.jsx b/client/src/common/context/CollapseContext.jsx deleted file mode 100644 index 6a09c56b6..000000000 --- a/client/src/common/context/CollapseContext.jsx +++ /dev/null @@ -1,72 +0,0 @@ -import { createContext, useCallback } from 'react'; - -import { useLocalStorage } from '../hooks/useLocalStorage'; - -export const CollapseContext = createContext({ - collapsed: {}, - setCollapsed: () => undefined, - clearCollapsed: () => undefined, - isCollapsed: () => undefined, -}); - -export const CollapseProvider = ({ children }) => { - const [collapsed, saveCollapsed] = useLocalStorage('collapsed', {}); - - /** - * @description Sets collapsed state for a single id - * @param {string} - id - * @param {boolean} - collapsed / not collapsed - */ - const setCollapsed = useCallback( - (id, isCollapsed) => { - if (isCollapsed) { - saveCollapsed((prev) => ({ ...prev, [id]: true })); - } else { - saveCollapsed((prev) => { - const newObject = { ...prev }; - delete newObject[id]; - return { ...newObject }; - }); - } - }, - [saveCollapsed] - ); - - /** - * @description Clears collapsed state in local-storage - */ - const collapseMultiple = useCallback( - (events) => { - const newOptions = {}; - for (const event of events) { - newOptions[event.id] = true; - } - saveCollapsed(newOptions); - }, - [saveCollapsed] - ); - - /** - * @description Clears collapsed state in local-storage - */ - const expandAll = useCallback(() => { - saveCollapsed({}); - }, [saveCollapsed]); - - /** - * @description Clears collapsed state in local-storage - * @return {boolean} - collapsed / not collapsed - */ - const isCollapsed = useCallback( - (id) => { - return id in collapsed; - }, - [collapsed] - ); - - return ( - - {children} - - ); -}; diff --git a/client/src/common/hooks-query/useAliases.ts b/client/src/common/hooks-query/useAliases.ts index 5750880f8..8ee0bc0c3 100644 --- a/client/src/common/hooks-query/useAliases.ts +++ b/client/src/common/hooks-query/useAliases.ts @@ -9,7 +9,13 @@ export default function useAliases() { status, isError, refetch, - } = useQuery(ALIASES, getAliases, { placeholderData: [] }); + } = useQuery({ + queryKey: ALIASES, + queryFn: getAliases, + placeholderData: [], + retry: 5, + retryDelay: attempt => attempt * 2500, + }); return { data, status, isError, refetch }; } \ No newline at end of file diff --git a/client/src/common/hooks-query/useEvent.ts b/client/src/common/hooks-query/useEvent.ts index 4c8650f54..0c3595685 100644 --- a/client/src/common/hooks-query/useEvent.ts +++ b/client/src/common/hooks-query/useEvent.ts @@ -10,7 +10,13 @@ export default function useEvent() { status, isError, refetch, - } = useQuery(EVENT_TABLE, fetchEvent, { placeholderData: eventDataPlaceholder }); + } = useQuery({ + queryKey: EVENT_TABLE, + queryFn: fetchEvent, + placeholderData: eventDataPlaceholder, + retry: 5, + retryDelay: attempt => attempt * 2500, + }); return { data, status, isError, refetch }; } \ No newline at end of file diff --git a/client/src/common/hooks-query/useInfo.ts b/client/src/common/hooks-query/useInfo.ts index a7df8ff48..e17e881ca 100644 --- a/client/src/common/hooks-query/useInfo.ts +++ b/client/src/common/hooks-query/useInfo.ts @@ -10,7 +10,13 @@ export default function useInfo() { status, isError, refetch, - } = useQuery(APP_INFO, getInfo, { placeholderData: ontimePlaceholderInfo }); + } = useQuery({ + queryKey: APP_INFO, + queryFn: getInfo, + placeholderData: ontimePlaceholderInfo, + retry: 5, + retryDelay: attempt => attempt * 2500, + }); return { data, status, isError, refetch }; } diff --git a/client/src/common/hooks-query/useOscSettings.ts b/client/src/common/hooks-query/useOscSettings.ts index 6d731cad0..dc44252c8 100644 --- a/client/src/common/hooks-query/useOscSettings.ts +++ b/client/src/common/hooks-query/useOscSettings.ts @@ -10,7 +10,13 @@ export default function useOscSettings() { status, isError, refetch, - } = useQuery(OSC_SETTINGS, getOSC, { placeholderData: oscPlaceholderSettings }); + } = useQuery({ + queryKey: OSC_SETTINGS, + queryFn: getOSC, + placeholderData: oscPlaceholderSettings, + retry: 5, + retryDelay: attempt => attempt * 2500, + }); return { data, status, isError, refetch }; } diff --git a/client/src/common/hooks-query/useRundown.ts b/client/src/common/hooks-query/useRundown.ts index 0e713e87d..b15fe71fd 100644 --- a/client/src/common/hooks-query/useRundown.ts +++ b/client/src/common/hooks-query/useRundown.ts @@ -9,7 +9,13 @@ export default function useRundown() { status, isError, refetch, - } = useQuery(RUNDOWN_TABLE, fetchRundown, { placeholderData: [] }); + } = useQuery({ + queryKey: RUNDOWN_TABLE, + queryFn: fetchRundown, + placeholderData: [], + retry: 5, + retryDelay: attempt => attempt * 2500, + }); return { data, status, isError, refetch }; } diff --git a/client/src/common/hooks-query/useSettings.ts b/client/src/common/hooks-query/useSettings.ts index 7240e9855..35f27762f 100644 --- a/client/src/common/hooks-query/useSettings.ts +++ b/client/src/common/hooks-query/useSettings.ts @@ -10,7 +10,13 @@ export default function useSettings() { status, isError, refetch, - } = useQuery(APP_SETTINGS, getSettings, { placeholderData: ontimePlaceholderSettings }); + } = useQuery({ + queryKey: APP_SETTINGS, + queryFn: getSettings, + placeholderData: ontimePlaceholderSettings, + retry: 5, + retryDelay: attempt => attempt * 2500, + }); return { data, status, isError, refetch }; } diff --git a/client/src/common/hooks-query/useUserFields.ts b/client/src/common/hooks-query/useUserFields.ts index fd2335a22..2c0f8078b 100644 --- a/client/src/common/hooks-query/useUserFields.ts +++ b/client/src/common/hooks-query/useUserFields.ts @@ -10,7 +10,13 @@ export default function useUserFields() { status, isError, refetch, - } = useQuery(USERFIELDS, getUserFields, { placeholderData: userFieldsPlaceholder }); + } = useQuery({ + queryKey: USERFIELDS, + queryFn: getUserFields, + placeholderData: userFieldsPlaceholder, + retry: 5, + retryDelay: attempt => attempt * 2500, + }); return { data, status, isError, refetch }; } diff --git a/client/src/common/hooks-query/useViewSettings.ts b/client/src/common/hooks-query/useViewSettings.ts index 6588a5774..6c4571f1d 100644 --- a/client/src/common/hooks-query/useViewSettings.ts +++ b/client/src/common/hooks-query/useViewSettings.ts @@ -10,7 +10,13 @@ export default function useViewSettings() { status, isError, refetch, - } = useQuery(VIEW_SETTINGS, getView, { placeholderData: viewsSettingsPlaceholder }); + } = useQuery({ + queryKey: VIEW_SETTINGS, + queryFn: getView, + placeholderData: viewsSettingsPlaceholder, + retry: 5, + retryDelay: attempt => attempt * 2500, + }); return { data, status, isError, refetch }; } diff --git a/client/src/common/hooks/useMutateEvents.js b/client/src/common/hooks/useMutateEvents.js deleted file mode 100644 index 842615069..000000000 --- a/client/src/common/hooks/useMutateEvents.js +++ /dev/null @@ -1,37 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query'; - -import { RUNDOWN_TABLE } from '../api/apiConstants'; - -/** - * @description utility hook to handle mutations in events - * @param mutation - */ -export default function useMutateEvents(mutation){ - const queryClient = useQueryClient(); - return useMutation(mutation, { - onMutate: async (newEvent) => { - // cancel ongoing queries - queryClient.cancelQueries(RUNDOWN_TABLE, { exact: true }); - - // Snapshot the previous value - const previousEvent = queryClient.getQueryData([RUNDOWN_TABLE, newEvent.id]); - - // optimistically update object - queryClient.setQueryData([RUNDOWN_TABLE, newEvent.id], newEvent); - - // Return a context with the previous and new event - return { previousEvent, newEvent }; - }, - - // Mutation fails, rollback undoes optimist update - onError: (error, newEvent, context) => { - queryClient.setQueryData([RUNDOWN_TABLE, context.newEvent.id], context.previousEvent); - }, - - // Mutation finished, failed or successful - // Fetch anyway, just to be sure - onSettled: (newEvent) => { - queryClient.invalidateQueries([RUNDOWN_TABLE, newEvent.id]); - }, - }); -} \ No newline at end of file diff --git a/client/src/common/hooks/useRuntimeStylesheet.js b/client/src/common/hooks/useRuntimeStylesheet.js index 209432142..0c9164d94 100644 --- a/client/src/common/hooks/useRuntimeStylesheet.js +++ b/client/src/common/hooks/useRuntimeStylesheet.js @@ -6,9 +6,9 @@ export const useRuntimeStylesheet = (pathToFile) => { useEffect(() => { const fetchData = async () => { - let response = await fetch(pathToFile); + const response = await fetch(pathToFile); if (response.ok) { - return await response.text(); + return response.text(); } }; diff --git a/client/src/common/hooks/useSocket.ts b/client/src/common/hooks/useSocket.ts index f5bb437ff..7d7ff117c 100644 --- a/client/src/common/hooks/useSocket.ts +++ b/client/src/common/hooks/useSocket.ts @@ -18,7 +18,7 @@ function createSocketHook(key: string, defaultValue: T | null = null) { // we need the null because useQuery can't receive undefined const fetcher = () => (queryClient.getQueryData([key]) ?? defaultValue) as T | null; - return () => useQuery([key], fetcher, { placeholderData: defaultValue }); + return () => useQuery({ queryKey: [key], queryFn: fetcher, placeholderData: defaultValue }); } const emptyRundown = { diff --git a/client/src/common/utils/__tests__/generate_id.test.js b/client/src/common/utils/__tests__/generate_id.test.js index e55315ec1..ca9b98abc 100644 --- a/client/src/common/utils/__tests__/generate_id.test.js +++ b/client/src/common/utils/__tests__/generate_id.test.js @@ -1,4 +1,4 @@ -import { generateId } from '../generate_id.js'; +import { generateId } from '../generate_id'; test('generate a valid 5 digit id', () => { const id = generateId(); diff --git a/client/src/common/utils/aliases.js b/client/src/common/utils/aliases.ts similarity index 94% rename from client/src/common/utils/aliases.js rename to client/src/common/utils/aliases.ts index a7b737291..a6a56bb11 100644 --- a/client/src/common/utils/aliases.js +++ b/client/src/common/utils/aliases.ts @@ -3,7 +3,7 @@ * @param {string} alias * @returns {{message: string, status: boolean}} */ -export const validateAlias = (alias) => { +export const validateAlias = (alias: string) => { const valid = { status: true, message: 'ok' }; diff --git a/client/src/common/utils/entryValidator.js b/client/src/common/utils/entryValidator.js deleted file mode 100644 index 3b4c1059c..000000000 --- a/client/src/common/utils/entryValidator.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * @description Validates two time entries - * @param {number} timeStart - * @param {number} timeEnd - * @returns {{catch: string, value: boolean}} - */ -export const validateTimes = (timeStart, timeEnd) => { - const validate = { value: true, catch: '' }; - if (timeStart > timeEnd) { - validate.catch = 'Start time later than end time'; - } - return validate; -}; diff --git a/client/src/common/utils/entryValidator.ts b/client/src/common/utils/entryValidator.ts new file mode 100644 index 000000000..e69de29bb diff --git a/client/src/common/utils/eventsManager.ts b/client/src/common/utils/eventsManager.ts index 4ff85a2a8..cb260e8dc 100644 --- a/client/src/common/utils/eventsManager.ts +++ b/client/src/common/utils/eventsManager.ts @@ -97,8 +97,8 @@ export const formatEventList = (events: OntimeEvent[], selectedId: string, nextI * @param {object} event * @return {object} clean event */ -type DuplicatedEvent = OntimeEvent | { after?: string }; -export const duplicateEvent = (event: OntimeEvent, after?: string): DuplicatedEvent => { +type ClonedEvent = OntimeEvent | { after?: string }; +export const cloneEvent = (event: OntimeEvent, after?: string): ClonedEvent => { return { type: 'event', title: event.title, diff --git a/client/src/common/utils/generate_id.js b/client/src/common/utils/generate_id.ts similarity index 99% rename from client/src/common/utils/generate_id.js rename to client/src/common/utils/generate_id.ts index 5d1e8c986..d33c292db 100644 --- a/client/src/common/utils/generate_id.js +++ b/client/src/common/utils/generate_id.ts @@ -1,4 +1,5 @@ import { customAlphabet } from 'nanoid'; + const nanoid = customAlphabet('1234567890abcdef', 5); /** diff --git a/client/src/common/utils/math.js b/client/src/common/utils/math.ts similarity index 79% rename from client/src/common/utils/math.js rename to client/src/common/utils/math.ts index c7a6bc5f8..0b5dd0c62 100644 --- a/client/src/common/utils/math.js +++ b/client/src/common/utils/math.ts @@ -5,5 +5,5 @@ * @param {number} max - max value * @returns {number} */ -export const clamp = (num, min, max) => +export const clamp = (num: number, min: number, max: number) => Math.max(Math.min(num, Math.max(min, max)), Math.min(min, max)); diff --git a/client/src/features/FeatureWrapper.jsx b/client/src/features/FeatureWrapper.tsx similarity index 58% rename from client/src/features/FeatureWrapper.jsx rename to client/src/features/FeatureWrapper.tsx index 2ad8e100a..038e86cc9 100644 --- a/client/src/features/FeatureWrapper.jsx +++ b/client/src/features/FeatureWrapper.tsx @@ -1,17 +1,17 @@ -import PropTypes from 'prop-types'; +import { ReactNode } from 'react'; import ProtectRoute from '../common/components/protectRoute/ProtectRoute'; import style from './FeatureWrapper.module.scss'; -export default function FeatureWrapper({ children }) { +interface FeatureWrapperProps { + children: ReactNode; +} + +export default function FeatureWrapper({ children }: FeatureWrapperProps) { return (
{children}
); } - -FeatureWrapper.propTypes = { - children: PropTypes.element, -}; diff --git a/client/src/features/editors/Editor.module.scss b/client/src/features/editors/Editor.module.scss index 7de2ab762..756323b3d 100644 --- a/client/src/features/editors/Editor.module.scss +++ b/client/src/features/editors/Editor.module.scss @@ -1,4 +1,5 @@ -@use '../../theme/main' as *; +@use '../../theme/ontimeColours' as *; +@use '../../theme/v2Styles' as *; @mixin absolute-top-right($distance) { position: absolute; @@ -15,13 +16,13 @@ } .mainContainer { - background: $bg-black; + background: $ui-black; width: 100%; height: 100%; margin: auto; - color: $title-white; - padding: max(16px, 2vh) max(8px, 1vh); - font-family: "Open Sans", "Segoe UI", sans-serif; + color: $ui-white; + padding: 16px 8px; + font-family: $ontime-font-family; display: grid; grid-template-rows: auto 1fr; @@ -124,7 +125,7 @@ .info { border-radius: 8px; height: 100%; - background-color: $bg-container-l1; + background-color: $bg-container-l2; padding: 0.8em 1.5em; display: flex; flex-direction: column; @@ -133,16 +134,16 @@ .eventEditor { border-radius: 8px 8px 0 0; - background-color: #202020; + background-color: $bg-container-l2; box-shadow: rgba(0, 0, 0, 0.35) 0 3px 6px 6px; - border-top: 1px solid $bg-gray-900; + border-top: 1px solid $white-10; position: absolute; bottom: 0; width: 100vw; left: 0; z-index: 10; color: white; - transition: bottom 0.3s; // $transition-time + transition: bottom $transition-time-feedback; &.noEvent { bottom: -500px; @@ -156,7 +157,7 @@ .header { background-color: #202020; padding: 8px; - border-left: 1px solid $bg-container-l3; + border-left: 1px solid $white-10; } } diff --git a/client/src/features/event-editor/EventEditorExport.jsx b/client/src/features/event-editor/EventEditorExport.tsx similarity index 97% rename from client/src/features/event-editor/EventEditorExport.jsx rename to client/src/features/event-editor/EventEditorExport.tsx index a24e32f9f..b08637bb4 100644 --- a/client/src/features/event-editor/EventEditorExport.jsx +++ b/client/src/features/event-editor/EventEditorExport.tsx @@ -27,7 +27,6 @@ export default function InfoExport() {
} onClick={() => setOpenId(null)} {...closeBtnStyle} diff --git a/client/src/features/info/Info.module.scss b/client/src/features/info/Info.module.scss index 5cbeb05d2..2baf6ffff 100644 --- a/client/src/features/info/Info.module.scss +++ b/client/src/features/info/Info.module.scss @@ -33,7 +33,7 @@ .interfaceList { display: flex; flex-wrap: wrap; - gap: $element-spacing; + gap: $section-spacing; .interface { @include action-link; @@ -41,7 +41,7 @@ } .linkIcon { - margin-left: 8px; + margin-left: $element-inner-spacing; display: inline-block; transform: rotate(45deg); } diff --git a/client/src/features/info/InfoExport.jsx b/client/src/features/info/InfoExport.tsx similarity index 100% rename from client/src/features/info/InfoExport.jsx rename to client/src/features/info/InfoExport.tsx diff --git a/client/src/features/info/InfoLogger.tsx b/client/src/features/info/InfoLogger.tsx index b19d4f1b0..29510bda5 100644 --- a/client/src/features/info/InfoLogger.tsx +++ b/client/src/features/info/InfoLogger.tsx @@ -71,7 +71,7 @@ export default function InfoLogger() { <>
diff --git a/client/src/features/rundown/Rundown.module.scss b/client/src/features/rundown/Rundown.module.scss index e13d287ad..fdcabbebd 100644 --- a/client/src/features/rundown/Rundown.module.scss +++ b/client/src/features/rundown/Rundown.module.scss @@ -1,12 +1,10 @@ -@use '../../theme/main' as *; -@use '../../theme/mixins' as *; - .eventContainer { margin-top: 1em; display: flex; flex-direction: column; - padding: 8px; + padding: 8px 4px 8px 0; overflow-y: scroll; + -ms-overflow-style: -ms-autohiding-scrollbar; height: 100%; } @@ -23,7 +21,7 @@ .alignCenter { text-align: center; flex-direction: column; - button { + .spaceTop { margin-top: 24px; } } diff --git a/client/src/features/rundown/RundownEntry.tsx b/client/src/features/rundown/RundownEntry.tsx index 64c930774..95d22c8a2 100644 --- a/client/src/features/rundown/RundownEntry.tsx +++ b/client/src/features/rundown/RundownEntry.tsx @@ -8,7 +8,7 @@ import { LoggingContext } from 'common/context/LoggingContext'; import { useEventAction } from 'common/hooks/useEventAction'; import { OntimeEvent, OntimeRundownEntry } from 'common/models/EventTypes'; import { Playstate } from 'common/models/OntimeTypes'; -import { duplicateEvent } from 'common/utils/eventsManager'; +import { cloneEvent } from 'common/utils/eventsManager'; import { calculateDuration } from 'common/utils/timesManager'; import { useAtom, useAtomValue } from 'jotai'; @@ -40,7 +40,17 @@ interface RundownEntryProps { } export default function RundownEntry(props: RundownEntryProps) { - const { index, eventIndex, data, selected, hasCursor, next, delay, previousEnd, playback } = props; + const { + index, + eventIndex, + data, + selected, + hasCursor, + next, + delay, + previousEnd, + playback, + } = props; const { emitError } = useContext(LoggingContext); const startTimeIsLastEnd = useAtomValue(startTimeIsLastEndAtom); const defaultPublic = useAtomValue(defaultPublicAtom); @@ -88,7 +98,7 @@ export default function RundownEntry(props: RundownEntryProps) { break; } case 'clone': { - const newEvent = duplicateEvent(data as OntimeEvent, data.id); + const newEvent = cloneEvent(data as OntimeEvent, data.id); addEvent(newEvent); break; } @@ -161,7 +171,6 @@ export default function RundownEntry(props: RundownEntryProps) { return ; } else if (data.type === 'delay') { return ; - } else { - return null; } + return null; }; diff --git a/client/src/features/rundown/RundownWrapper.tsx b/client/src/features/rundown/RundownWrapper.tsx index 5986541a2..5b4e40683 100644 --- a/client/src/features/rundown/RundownWrapper.tsx +++ b/client/src/features/rundown/RundownWrapper.tsx @@ -1,6 +1,4 @@ -import { useContext, useEffect } from 'react'; import Empty from 'common/components/state/Empty'; -import { LoggingContext } from 'common/context/LoggingContext'; import RundownMenu from 'features/menu/RundownMenu'; import useRundown from '../../common/hooks-query/useRundown'; @@ -10,14 +8,7 @@ import Rundown from './Rundown'; import styles from '../editors/Editor.module.scss'; export default function RundownWrapper() { - const { emitError } = useContext(LoggingContext); - const { data, status, isError } = useRundown(); - - useEffect(() => { - if (isError) { - emitError('Error fetching data'); - } - }, [emitError, isError]); + const { data, status } = useRundown(); return ( <> diff --git a/client/src/features/rundown/_blockMixins.scss b/client/src/features/rundown/_blockMixins.scss index f16e5c817..d66951e2c 100644 --- a/client/src/features/rundown/_blockMixins.scss +++ b/client/src/features/rundown/_blockMixins.scss @@ -14,10 +14,10 @@ $secondary-block-height: 40px; @mixin block-styling() { box-sizing: content-box; background-color: $block-bg; - border: 1px solid #383838; - box-shadow: $block-box-shadow; + border: 1px solid $white-10; font-family: $ontime-font-family; border-radius: $block-border-radius; + margin: 4px 2px; } @mixin block-spacing() { diff --git a/client/src/features/rundown/block-block/BlockBlock.module.scss b/client/src/features/rundown/block-block/BlockBlock.module.scss index 661924724..f0f03e12f 100644 --- a/client/src/features/rundown/block-block/BlockBlock.module.scss +++ b/client/src/features/rundown/block-block/BlockBlock.module.scss @@ -3,10 +3,9 @@ .block { @include block-spacing; @include block-styling; - box-sizing: content-box; display: grid; - grid-template-columns: 32px 1fr; + grid-template-columns: 32px 1fr auto; align-items: center; height: $secondary-block-height; } diff --git a/client/src/features/rundown/event-block/EventBlock.module.scss b/client/src/features/rundown/event-block/EventBlock.module.scss index a83fb0880..2589f605d 100644 --- a/client/src/features/rundown/event-block/EventBlock.module.scss +++ b/client/src/features/rundown/event-block/EventBlock.module.scss @@ -1,4 +1,5 @@ @use '../../../theme/v2Styles' as *; +@use '../../../theme/ontimeColours' as *; @use '../blockMixins' as *; .eventBlock { @@ -15,20 +16,19 @@ grid-template-columns: $block-binder-width auto 1fr auto; grid-template-rows: 4px 36px 36px 36px 4px; align-items: center; - margin: 4px 2px; padding-right: $block-clearance; gap: 2px; &.selected { - background-color: #101010; + background-color: $gray-1350; } &.hasCursor { - outline: 1px solid #779BE7; + outline: 1px solid $blue-400; } &.skip { - border: 1px solid rgba(white, 0.03); + border: 1px solid $white-3; box-shadow: none; .delayNote, @@ -52,7 +52,7 @@ border-radius: $block-border-radius 0 0 $block-border-radius; - background-color: #303030; // to override inline + background-color: $gray-1050; // to override inline color: $section-white; font-size: 17px; @@ -81,7 +81,7 @@ .delayNote { font-size: 12px; line-height: 14px; - color: #E69056; + color: $ontime-delay-text; } } @@ -112,7 +112,7 @@ .progressBg { grid-area: progb; border-radius: 2px; - background-color: #282828; + background-color: $gray-1100; opacity: 1; height: 100%; } @@ -155,18 +155,10 @@ .statusIcon { width: 16px; height: 16px; - color: #404040; + color: $gray-1000; } - .statusNext.enabled { - color: $playback-start; - } - - .statusDelay.enabled { - color: $ontime-delay; - } - - .statusPublic.enabled { - color: $action-blue; + .statusIcon.active { + color: $active-indicator; } } diff --git a/client/src/features/rundown/event-block/EventBlock.tsx b/client/src/features/rundown/event-block/EventBlock.tsx index e37736cff..f928c1fcb 100644 --- a/client/src/features/rundown/event-block/EventBlock.tsx +++ b/client/src/features/rundown/event-block/EventBlock.tsx @@ -1,8 +1,8 @@ import { useCallback, useEffect, useState } from 'react'; import { Draggable } from 'react-beautiful-dnd'; import { Editable, EditableInput, EditablePreview, Tooltip } from '@chakra-ui/react'; -import { FiUsers } from '@react-icons/all-files/fi/FiUsers'; import { IoOptions } from '@react-icons/all-files/io5/IoOptions'; +import { IoPeople } from '@react-icons/all-files/io5/IoPeople'; import { IoPlay } from '@react-icons/all-files/io5/IoPlay'; import { IoPlayOutline } from '@react-icons/all-files/io5/IoPlayOutline'; import { IoPlaySkipForward } from '@react-icons/all-files/io5/IoPlaySkipForward'; @@ -10,7 +10,6 @@ import { IoReload } from '@react-icons/all-files/io5/IoReload'; import { IoRemoveCircle } from '@react-icons/all-files/io5/IoRemoveCircle'; import { IoRemoveCircleOutline } from '@react-icons/all-files/io5/IoRemoveCircleOutline'; import { IoReorderTwo } from '@react-icons/all-files/io5/IoReorderTwo'; -import { IoTimerOutline } from '@react-icons/all-files/io5/IoTimerOutline'; import { editorEventId } from 'common/atoms/LocalEventSettings'; import TooltipActionBtn from 'common/components/buttons/TooltipActionBtn'; import { cx, getAccessibleColour } from 'common/utils/styleUtils'; @@ -84,7 +83,6 @@ export default function EventBlock(props: EventBlockProps) { const [blockTitle, setBlockTitle] = useState(title || ''); const binderColours = colour && getAccessibleColour(colour); - const hasDelay = delay !== 0 && delay !== null; // Todo: could I re-render the item without causing a state change here? // ?? use refs instead? @@ -173,7 +171,7 @@ export default function EventBlock(props: EventBlockProps) { {...blockBtnStyle} clickHandler={() => setEventPlayback.startEvent(eventId)} backgroundColor={eventIsPlaying ? '#58A151' : undefined} - _hover={{backgroundColor: eventIsPlaying ? '#58A151' : undefined}} + _hover={{ backgroundColor: eventIsPlaying ? '#58A151' : undefined }} tabIndex={-1} />
@@ -207,25 +205,14 @@ export default function EventBlock(props: EventBlockProps) { shouldWrapChildren {...tooltipProps} > - - - + className={`${style.statusIcon} ${next ? style.active : ''}`} /> - +
diff --git a/client/src/features/rundown/event-block/composite/BlockActionMenu.tsx b/client/src/features/rundown/event-block/composite/BlockActionMenu.tsx index 52cf2b6c1..3b2a1ebc2 100644 --- a/client/src/features/rundown/event-block/composite/BlockActionMenu.tsx +++ b/client/src/features/rundown/event-block/composite/BlockActionMenu.tsx @@ -9,6 +9,7 @@ import { } from '@chakra-ui/react'; import { IoAdd } from '@react-icons/all-files/io5/IoAdd'; import { IoDuplicateOutline } from '@react-icons/all-files/io5/IoDuplicateOutline'; +import { IoEllipsisHorizontal } from '@react-icons/all-files/io5/IoEllipsisHorizontal'; import { IoRemoveCircleOutline } from '@react-icons/all-files/io5/IoRemoveCircleOutline'; import { IoTimerOutline } from '@react-icons/all-files/io5/IoTimerOutline'; import { IoTrashBinSharp } from '@react-icons/all-files/io5/IoTrashBinSharp'; @@ -34,7 +35,7 @@ export default function BlockActionMenu(props: BlockActionMenuProps) { } + icon={} tabIndex={-1} variant='ontime-subtle' color='#f6f6f6' diff --git a/client/src/features/rundown/quick-add-block/QuickAddBlock.module.scss b/client/src/features/rundown/quick-add-block/QuickAddBlock.module.scss index e6d3ceb03..513a6389e 100644 --- a/client/src/features/rundown/quick-add-block/QuickAddBlock.module.scss +++ b/client/src/features/rundown/quick-add-block/QuickAddBlock.module.scss @@ -15,19 +15,7 @@ .quickBtn { width: auto; - padding: 0 16px; - } - - .createEvent { - color: $light-bg; - } - - .createDelay { - color: $block-delay-color; - } - - .createBlock { - color: $block-block-color; + padding: 0 32px; } } diff --git a/client/src/features/rundown/quick-add-block/QuickAddBlock.tsx b/client/src/features/rundown/quick-add-block/QuickAddBlock.tsx index 5020c65fd..4317f2619 100644 --- a/client/src/features/rundown/quick-add-block/QuickAddBlock.tsx +++ b/client/src/features/rundown/quick-add-block/QuickAddBlock.tsx @@ -67,32 +67,32 @@ export default function QuickAddBlock(props: QuickAddBlockProps) {
diff --git a/client/src/features/table/OntimeTable.jsx b/client/src/features/table/OntimeTable.jsx index 52248edc2..7bcb98f6c 100644 --- a/client/src/features/table/OntimeTable.jsx +++ b/client/src/features/table/OntimeTable.jsx @@ -54,7 +54,7 @@ export default function OntimeTable({ tableData, userFields, selectedId, handleU columns, data: tableData, initialState: { - hiddenColumns: hiddenColumns, + hiddenColumns, }, handleUpdate, }, diff --git a/client/src/features/table/TableWrapper.jsx b/client/src/features/table/TableWrapper.jsx index 4bea5e94b..e1d5376b4 100644 --- a/client/src/features/table/TableWrapper.jsx +++ b/client/src/features/table/TableWrapper.jsx @@ -1,8 +1,7 @@ import { useCallback, useContext, useEffect } from 'react'; -import { requestPatchEvent } from '../../common/api/eventsApi'; import { TableSettingsContext } from '../../common/context/TableSettingsContext'; -import useMutateEvents from '../../common/hooks/useMutateEvents'; +import { useEventAction } from '../../common/hooks/useEventAction'; import { useCuesheet } from '../../common/hooks/useSocket'; import useRundown from '../../common/hooks-query/useRundown'; import useUserFields from '../../common/hooks-query/useUserFields'; @@ -17,9 +16,9 @@ export default function TableWrapper() { const { data: rundown } = useRundown(); const { data: userFields } = useUserFields(); const { data: featureData } = useCuesheet(); - - const mutation = useMutateEvents(requestPatchEvent); + const { updateEvent } = useEventAction(); const { theme } = useContext(TableSettingsContext); + // Set window title useEffect(() => { document.title = 'ontime - Cuesheet'; @@ -55,11 +54,11 @@ export default function TableWrapper() { // submit try { - await mutation.mutateAsync(mutationObject); + await updateEvent(mutationObject); } catch (error) { console.error(error); } - }, [mutation, rundown]); + }, [updateEvent, rundown]); const exportHandler = useCallback( (headerData) => { diff --git a/client/src/features/viewers/ViewWrapper.jsx b/client/src/features/viewers/ViewWrapper.jsx index ddbcc68bf..960b20390 100644 --- a/client/src/features/viewers/ViewWrapper.jsx +++ b/client/src/features/viewers/ViewWrapper.jsx @@ -56,10 +56,9 @@ const withSocket = (Component) => { const publicEvents = useMemo(() => { if (Array.isArray(eventsData)) { return eventsData.filter((d) => d.type === 'event' && d.title !== '' && d.isPublic); - } else { - return []; } - },[eventsData]) + return []; + }, [eventsData]); /********************************************/ /*** + titleManager ***/ diff --git a/client/src/features/viewers/countdown/Countdown.jsx b/client/src/features/viewers/countdown/Countdown.jsx index 6319ec6a9..8cd1e5ace 100644 --- a/client/src/features/viewers/countdown/Countdown.jsx +++ b/client/src/features/viewers/countdown/Countdown.jsx @@ -46,7 +46,7 @@ export default function Countdown(props) { const eventId = searchParams.get('eventid'); const eventIndex = searchParams.get('event'); - let followThis = undefined; + let followThis; const events = [...backstageEvents].filter((event) => event.type === 'event'); if (eventId !== null) { @@ -100,7 +100,7 @@ export default function Countdown(props) { Select an event to follow