v2: refactor (#264)

* chore: upgrade dependencies

* chore: remove unused

* fix: issue with missing index data

* fix: discrepancy on counting events

* fix: swapped classes

* fix: prevent attempting load in empty list

* chore: cleanup completed

* chore: remove unused

* refactor: convert to typescript

* refactor: migrate to new endpoints

* refactor: convert to typescript

* fix: prevent issue with undefined process

* fix: small code smells

* fix: issue with missing version variable on build

* refactor: configure retries on data fetching

* style: design review

* fix: prevent cursor out of range

* lint: react query linting

* style: checkbox styles
This commit is contained in:
Carlos Valente
2022-12-07 09:58:11 +01:00
committed by GitHub
parent d486d78594
commit c56c5a636d
76 changed files with 718 additions and 519 deletions
+1 -1
View File
@@ -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
+4 -2
View File
@@ -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",
+1 -1
View File
@@ -32,6 +32,6 @@
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id='root'></div>
<script type='module' src='/src/index.jsx'></script>
<script type='module' src='/src/index.tsx'></script>
</body>
</html>
+8 -6
View File
@@ -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",
+2 -1
View File
@@ -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
@@ -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 (
<Tooltip label={tooltip} shouldWrapChildren={loading} openDelay={openDelay}>
<IconButton
{...rest}
aria-label={tooltip}
size={size}
icon={icon}
onClick={handleClick}
disabled={loading}
isLoading={loading}
/>
</Tooltip>
);
}
@@ -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 (
<div className={style.errorContainer} data-testid="error-container">
<div className={style.errorContainer} data-testid='error-container'>
<div>
<p className={style.error}>:/</p>
<p>Something went wrong</p>
<p
<div
role='button'
className={style.report}
onClick={() => {
if (navigator.clipboard) {
@@ -50,11 +51,12 @@ class ErrorBoundary extends React.Component {
}}
>
Copy error
</p>
<p
</div>
<div
role='button'
className={style.report}
onClick={() => {
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
</p>
</div>
</div>
</div>
);
@@ -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() {
<div
className={style.link}
tabIndex={0}
role='button'
onClick={handleFullscreen}
onKeyDown={(event) => {
isKeyEnter(event) && handleFullscreen();
@@ -76,6 +76,7 @@ export default function NavigationMenu() {
<div
className={style.link}
tabIndex={0}
role='button'
onClick={handleMirror}
onKeyDown={(event) => {
isKeyEnter(event) && handleMirror();
@@ -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 (
<div className={`${style.emptyContainer} ${dark ? style.dark : ''}`} {...rest}>
<Emptyimage className={style.empty} />
<span className={style.text}>{text}</span>
</div>
);
}
Empty.propTypes = {
text: PropTypes.string,
dark: PropTypes.bool,
}
@@ -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;
}
}
}
@@ -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 (
<div className={style.emptyContainer} {...rest}>
<Emptyimage className={style.empty} />
<span className={style.text}>{text}</span>
</div>
);
}
@@ -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 (
<CollapseContext.Provider value={{ setCollapsed, collapseMultiple, expandAll, isCollapsed }}>
{children}
</CollapseContext.Provider>
);
};
+7 -1
View File
@@ -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 };
}
+7 -1
View File
@@ -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 };
}
+7 -1
View File
@@ -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 };
}
@@ -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 };
}
+7 -1
View File
@@ -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 };
}
+7 -1
View File
@@ -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 };
}
@@ -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 };
}
@@ -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 };
}
@@ -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]);
},
});
}
@@ -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();
}
};
+1 -1
View File
@@ -18,7 +18,7 @@ function createSocketHook<T>(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 = {
@@ -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();
@@ -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' };
-13
View File
@@ -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;
};
+2 -2
View File
@@ -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,
@@ -1,4 +1,5 @@
import { customAlphabet } from 'nanoid';
const nanoid = customAlphabet('1234567890abcdef', 5);
/**
@@ -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));
@@ -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 (
<ProtectRoute>
<div className={style.wrapper}>{children}</div>
</ProtectRoute>
);
}
FeatureWrapper.propTypes = {
children: PropTypes.element,
};
+11 -10
View File
@@ -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;
}
}
@@ -27,7 +27,6 @@ export default function InfoExport() {
<div className={style.header}>
<IconButton
aria-label='Close Menu'
size='lg'
icon={<FiX />}
onClick={() => setOpenId(null)}
{...closeBtnStyle}
+2 -2
View File
@@ -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);
}
+7 -7
View File
@@ -71,7 +71,7 @@ export default function InfoLogger() {
<>
<div className={style.buttonBar}>
<Button
variant={showUser ? 'ontime-filled' : 'ontime-outlined'}
variant={showUser ? 'ontime-filled' : 'ontime-subtle'}
size='xs'
onClick={() => setShowUser((s) => !s)}
onAuxClick={() => disableOthers(LOG_FILTER.USER)}
@@ -80,7 +80,7 @@ export default function InfoLogger() {
USER
</Button>
<Button
variant={showClient ? 'ontime-filled' : 'ontime-outlined'}
variant={showClient ? 'ontime-filled' : 'ontime-subtle'}
size='xs'
onClick={() => setShowClient((s) => !s)}
onAuxClick={() => disableOthers(LOG_FILTER.CLIENT)}
@@ -89,7 +89,7 @@ export default function InfoLogger() {
CLIENT
</Button>
<Button
variant={showServer ? 'ontime-filled' : 'ontime-outlined'}
variant={showServer ? 'ontime-filled' : 'ontime-subtle'}
size='xs'
onClick={() => setShowServer((s) => !s)}
onAuxClick={() => disableOthers(LOG_FILTER.SERVER)}
@@ -98,7 +98,7 @@ export default function InfoLogger() {
SERVER
</Button>
<Button
variant={showPlayback ? 'ontime-filled' : 'ontime-outlined'}
variant={showPlayback ? 'ontime-filled' : 'ontime-subtle'}
size='xs'
onClick={() => setShowPlayback((s) => !s)}
onAuxClick={() => disableOthers(LOG_FILTER.PLAYBACK)}
@@ -107,7 +107,7 @@ export default function InfoLogger() {
PLAYBACK
</Button>
<Button
variant={showRx ? 'ontime-filled' : 'ontime-outlined'}
variant={showRx ? 'ontime-filled' : 'ontime-subtle'}
size='xs'
onClick={() => setShowRx((s) => !s)}
onAuxClick={() => disableOthers(LOG_FILTER.RX)}
@@ -116,7 +116,7 @@ export default function InfoLogger() {
RX
</Button>
<Button
variant={showTx ? 'ontime-filled' : 'ontime-outlined'}
variant={showTx ? 'ontime-filled' : 'ontime-subtle'}
size='xs'
onClick={() => setShowTx((s) => !s)}
onAuxClick={() => disableOthers(LOG_FILTER.TX)}
@@ -125,7 +125,7 @@ export default function InfoLogger() {
TX
</Button>
<Button
variant='ontime-subtle'
variant='ontime-outlined'
size='xs'
onClick={clearLog}
>
@@ -1,12 +1,15 @@
@use '../../theme/v2Styles' as *;
@use '../../theme/ontimeColours' as *;
.headerButtons {
align-content: center;
justify-content: space-between;
gap: 8px;
padding-top: 24px;
}
.labelledSwitch {
display: flex;
align-items: center;
gap: 8px;
}
gap: $element-spacing;
color: $gray-100;
}
+5 -5
View File
@@ -61,21 +61,21 @@ const RundownMenu = () => {
size='sm'
variant='ontime-subtle'
>
Add event
Event...
</MenuButton>
<MenuList>
<MenuItem icon={<IoAdd />} onClick={() => eventAction('event')}>
Event at start
Add event at start
</MenuItem>
<MenuItem icon={<IoTimerOutline />} onClick={() => eventAction('delay')}>
Delay at start
Add delay at start
</MenuItem>
<MenuItem icon={<FiMinusCircle />} onClick={() => eventAction('block')}>
Block at start
Add block at start
</MenuItem>
<MenuDivider />
<MenuItem icon={<FiTrash2 />} onClick={() => eventAction('delete-all')} color='#D20300'>
Delete All
Delete all events
</MenuItem>
</MenuList>
</Menu>
+9 -7
View File
@@ -1,6 +1,7 @@
import { createRef, Fragment, useCallback, useContext, useEffect } from 'react';
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
import { Button } from '@chakra-ui/react';
import { IoAdd } from '@react-icons/all-files/io5/IoAdd';
import {
defaultPublicAtom,
showQuickEntryAtom,
@@ -10,7 +11,7 @@ import Empty from 'common/components/state/Empty';
import { CursorContext } from 'common/context/CursorContext';
import { useEventAction } from 'common/hooks/useEventAction';
import { useRundownEditor } from 'common/hooks/useSocket';
import { duplicateEvent } from 'common/utils/eventsManager';
import { cloneEvent } from 'common/utils/eventsManager';
import { useAtomValue } from 'jotai';
import PropTypes from 'prop-types';
@@ -34,7 +35,7 @@ export default function Rundown(props) {
const insertAtCursor = useCallback(
(type, cursor) => {
if (cursor === -1) {
addEvent({ type: type });
addEvent({ type });
} else {
const previousEvent = entries?.[cursor];
const nextEvent = entries?.[cursor + 1];
@@ -43,7 +44,7 @@ export default function Rundown(props) {
const isPreviousDifferent = previousEvent?.type !== type;
const isNextDifferent = nextEvent?.type !== type;
if (type === 'clone' && previousEvent) {
const newEvent = duplicateEvent(previousEvent);
const newEvent = cloneEvent(previousEvent);
newEvent.after = previousEvent.id;
addEvent(newEvent);
} else if (type === 'event') {
@@ -57,7 +58,7 @@ export default function Rundown(props) {
};
addEvent(newEvent, options);
} else if (isPreviousDifferent && isNextDifferent) {
addEvent({ type: type, after: previousEvent.id });
addEvent({ type, after: previousEvent.id });
}
}
},
@@ -73,7 +74,7 @@ export default function Rundown(props) {
if (event.altKey && (!event.ctrlKey || !event.shiftKey)) {
// Arrow down
if (event.keyCode === 40) {
if (cursor < entries.length - 1) moveCursorDown();
if (cursor < entries.length - 2) moveCursorDown();
}
// Arrow up
if (event.keyCode === 38) {
@@ -165,8 +166,9 @@ export default function Rundown(props) {
<Empty text='No data yet' style={{ marginTop: '7vh' }} />
<Button
onClick={() => insertAtCursor('event', cursor)}
variant='solid'
colorScheme='blue'
variant='ontime-filled'
className={style.spaceTop}
leftIcon={<IoAdd />}
>
Create Event
</Button>
@@ -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;
}
}
+14 -5
View File
@@ -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 <BlockBlock index={index} data={data} actionHandler={actionHandler} />;
} else if (data.type === 'delay') {
return <DelayBlock index={index} data={data} actionHandler={actionHandler} />;
} else {
return null;
}
return null;
};
+1 -10
View File
@@ -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 (
<>
@@ -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() {
@@ -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;
}
@@ -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;
}
}
@@ -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<string>(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}
/>
</div>
@@ -207,25 +205,14 @@ export default function EventBlock(props: EventBlockProps) {
shouldWrapChildren {...tooltipProps}
>
<IoPlaySkipForward
className={`${style.statusIcon} ${style.statusNext} ${next ? style.enabled : ''}`} />
</Tooltip>
<Tooltip
label='Event has delay'
isDisabled={!hasDelay}
shouldWrapChildren {...tooltipProps}
>
<IoTimerOutline className={`${style.statusIcon} ${style.statusDelay} ${
hasDelay ? style.enabled : ''
}`} />
className={`${style.statusIcon} ${next ? style.active : ''}`} />
</Tooltip>
<Tooltip
label={`${isPublic ? 'Event is public' : 'Event is private'}`}
{...tooltipProps}
shouldWrapChildren
>
<FiUsers className={`${style.statusIcon} ${style.statusPublic} ${
isPublic ? style.enabled : ''
}`} />
<IoPeople className={`${style.statusIcon} ${isPublic ? style.active : ''}`} />
</Tooltip>
</div>
</div>
@@ -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) {
<MenuButton
as={IconButton}
aria-label='Event options'
icon={<IoAdd />}
icon={<IoEllipsisHorizontal />}
tabIndex={-1}
variant='ontime-subtle'
color='#f6f6f6'
@@ -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;
}
}
@@ -67,32 +67,32 @@ export default function QuickAddBlock(props: QuickAddBlockProps) {
<Button
onClick={() => handleCreateEvent('event')}
size='xs'
variant='ontime-subtle'
className={`${style.quickBtn} ${style.createBlock}`}
variant='ontime-subtle-white'
className={style.quickBtn}
>
E{showKbd && <span className={style.keyboard}>Alt + E</span>}
Event {showKbd && <span className={style.keyboard}>Alt + E</span>}
</Button>
</Tooltip>
<Tooltip label='Add Delay' openDelay={tooltipDelayMid}>
<Button
onClick={() => handleCreateEvent('delay')}
size='xs'
variant='ontime-subtle'
variant='ontime-subtle-white'
disabled={disableAddDelay}
className={`${style.quickBtn} ${style.createDelay}`}
className={style.quickBtn}
>
D{showKbd && <span className={style.keyboard}>Alt + D</span>}
Delay {showKbd && <span className={style.keyboard}>Alt + D</span>}
</Button>
</Tooltip>
<Tooltip label='Add Block' openDelay={tooltipDelayMid}>
<Button
onClick={() => handleCreateEvent('block')}
size='xs'
variant='ontime-subtle'
variant='ontime-subtle-white'
disabled={disableAddBlock}
className={`${style.quickBtn} ${style.createEvent}`}
className={style.quickBtn}
>
B{showKbd && <span className={style.keyboard}>Alt + B</span>}
Block {showKbd && <span className={style.keyboard}>Alt + B</span>}
</Button>
</Tooltip>
</div>
+1 -1
View File
@@ -54,7 +54,7 @@ export default function OntimeTable({ tableData, userFields, selectedId, handleU
columns,
data: tableData,
initialState: {
hiddenColumns: hiddenColumns,
hiddenColumns,
},
handleUpdate,
},
+5 -6
View File
@@ -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) => {
+2 -3
View File
@@ -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 ***/
@@ -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) {
<span className='event-select__title'>Select an event to follow</span>
<ul className='event-select__events'>
{backstageEvents.length === 0 ? (
<Empty dark text='No events in database' />
<Empty text='No events in database' />
) : (
backstageEvents
.filter((e) => e.type === 'event')
@@ -1,3 +1,4 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
@@ -5,10 +6,10 @@ import App from './App';
import './index.scss';
const container = document.getElementById('root');
const root = createRoot(container);
const root = createRoot(container as Element);
root.render(
// <StrictMode>
<StrictMode>
<App />
// </StrictMode>
</StrictMode>
);
+5 -4
View File
@@ -10,8 +10,8 @@ $component-border-radius-sm: 2px;
$action-blue: #3182ce;
$action-text-color: $blue-400;
$ontime-color: #ff7597;
$error-red: $red-700;
$warning-orange: $orange-700;
$error-red: $red-500;
$warning-orange: $orange-500;
$opacity-disabled: 0.4;
// playback colours
@@ -22,10 +22,11 @@ $ontime-delay-text: #E69056;
$ontime-paused: #c05621;
$ontime-stop: #E4281E;
$playback-negative: $red-500;
$active-indicator: #899948;
// interface panels
// interface panels
$bg-container-l1: $gray-1350;
$bg-container-l2: $gray-1100;
$bg-container-l2: $gray-1200;
$bg-container-l3: $gray-1350;
$box-shadow-l1: rgba(0, 0, 0, 0.15) 0 3px 3px 0;
+3 -2
View File
@@ -22,10 +22,11 @@ export const ontimeButtonFilled = {
export const ontimeButtonOutlined = {
...commonStyles,
backgroundColor: '#2d2d2d', // $gray-1100
color: '#779BE7', // $blue-400
border: '1px solid #779BE7', // $blue-400
color: '#9d9d9d', // $blue-400
border: '1px solid rgba(255, 255, 255, 0.10)', // white-10
_hover: {
backgroundColor: '#404040', // $gray-1000
color: '#e2e2e2', // $gray-200
},
_active: {
backgroundColor: '#2d2d2d', // $gray-1100
+1 -1
View File
@@ -15,7 +15,7 @@ export const ontimeCheckboxOnDark = {
fontWeight: '200',
color: '#9d9d9d', // $gray-500
_checked: {
color: '#779BE7', //$blue-400
color: '#cfcfcf', // $gray-300
}
},
};
+395 -52
View File
@@ -1527,33 +1527,38 @@
"@svgr/hast-util-to-babel-ast" "^6.5.1"
svg-parser "^2.0.4"
"@tanstack/match-sorter-utils@^8.1.1":
version "8.5.14"
resolved "https://registry.yarnpkg.com/@tanstack/match-sorter-utils/-/match-sorter-utils-8.5.14.tgz#12efcd536abe491d09521e0242bc4d51442f8a8a"
integrity sha512-lVNhzTcOJ2bZ4IU+PeCPQ36vowBHvviJb2ZfdRFX5uhy7G0jM8N34zAMbmS5ZmVH8D2B7oU82OWo0e/5ZFzQrw==
"@tanstack/eslint-plugin-query@^4.15.1":
version "4.15.1"
resolved "https://registry.yarnpkg.com/@tanstack/eslint-plugin-query/-/eslint-plugin-query-4.15.1.tgz#0be92fe6ffe10e7b0e47d192fa94af1d7fbb7b65"
integrity sha512-SMj7OsOkutuJQ9LQLIoXxW5u0kx84V7RICmJEcjpKNkHsBd31QttJvtG1hLx9GXb3ZuFLZ0Y1dOD/D9b/GIzTg==
"@tanstack/match-sorter-utils@8.1.1":
version "8.1.1"
resolved "https://registry.yarnpkg.com/@tanstack/match-sorter-utils/-/match-sorter-utils-8.1.1.tgz#895f407813254a46082a6bbafad9b39b943dc834"
integrity sha512-IdmEekEYxQsoLOR0XQyw3jD1GujBpRRYaGJYQUw1eOT1eUugWxdc7jomh1VQ1EKHcdwDLpLaCz/8y4KraU4T9A==
dependencies:
remove-accents "0.4.2"
"@tanstack/query-core@4.10.3":
version "4.10.3"
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.10.3.tgz#a6477bab9ed1ae4561ca0a59ae06f8c615c692b7"
integrity sha512-+ME02sUmBfx3Pjd+0XtEthK8/4rVMD2jcxvnW9DSgFONpKtpjzfRzjY4ykzpDw1QEo2eoPvl7NS8J5mNI199aA==
"@tanstack/query-core@4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.18.0.tgz#3e166ce319fd869c668536384ae94d174ea46a59"
integrity sha512-PP4mG8MD08sq64RZCqMfXMYfaj7+Oulwg7xZ/fJoEOdTZNcPIgaOkHajZvUBsNLbi/0ViMvJB4cFkL2Jg2WPbw==
"@tanstack/react-query-devtools@^4.11.0":
version "4.11.0"
resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-4.11.0.tgz#a2adf21fa644eae5b834ba4235b99818301ab702"
integrity sha512-g/414SruE0TEp4jeMYxVUSXGXCc+zMY9j0gB8Q6B91gmyPseNqs9WLkVrqxRXoQDyRvDcphVOaEANNygGx+zGg==
"@tanstack/react-query-devtools@^4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-4.18.0.tgz#3c2d2295d11940a2c3cca356d247c6dd8443419c"
integrity sha512-L51D0AUqLTs7J+W7RypJrUEKXsS0y0eEhXAgO+rhcZH6i8jVrKLhcNZStQSnE+UpZqPm73uxt2e8TJgYfzz0nw==
dependencies:
"@tanstack/match-sorter-utils" "^8.1.1"
"@tanstack/match-sorter-utils" "8.1.1"
superjson "^1.10.0"
use-sync-external-store "^1.2.0"
"@tanstack/react-query@^4.10.3":
version "4.10.3"
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-4.10.3.tgz#294deefa0fb6ada88bc4631d346ef5d5551c5443"
integrity sha512-4OEJjkcsCTmG3ui7RjsVzsXerWQvInTe95CBKFyOV/GchMUlNztoFnnYmlMhX7hLUqJMhbG9l7M507V7+xU8Hw==
"@tanstack/react-query@^4.18.0":
version "4.18.0"
resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-4.18.0.tgz#d9c661364b383fca79f5384cb97b445354068faa"
integrity sha512-s1kdbGMdVcfUIllzsHUqVUdktBT5uuIRgnvrqFNLjl9TSOXEoBSDrhjsGjao0INQZv8cMpQlgOh3YH9YtN6cKw==
dependencies:
"@tanstack/query-core" "4.10.3"
"@tanstack/query-core" "4.18.0"
use-sync-external-store "^1.2.0"
"@testing-library/dom@^8.5.0":
@@ -1969,6 +1974,14 @@ abab@^2.0.6:
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==
accepts@~1.3.8:
version "1.3.8"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
dependencies:
mime-types "~2.1.34"
negotiator "0.6.3"
acorn-globals@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45"
@@ -2078,6 +2091,11 @@ aria-query@^5.0.0:
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c"
integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
array-includes@^3.1.3:
version "3.1.4"
resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9"
@@ -2145,13 +2163,14 @@ autosize@^5.0.1:
resolved "https://registry.yarnpkg.com/autosize/-/autosize-5.0.1.tgz#ed269b0fa9b7eb47627048a1bb3299e99e003a0f"
integrity sha512-UIWUlE4TOVPNNj2jjrU39wI4hEYbneUypEqcyRmRFIx5CC2gNdg3rQr+Zh7/3h6egbBvm33TDQjNQKtj9Tk1HA==
axios@^0.27.2:
version "0.27.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
axios@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.0.tgz#1cb65bd75162c70e9f8d118a905126c4a201d383"
integrity sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw==
dependencies:
follow-redirects "^1.14.9"
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
babel-plugin-macros@^3.1.0:
version "3.1.0"
@@ -2177,6 +2196,24 @@ binary-extensions@^2.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
body-parser@1.20.1:
version "1.20.1"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668"
integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
dependencies:
bytes "3.1.2"
content-type "~1.0.4"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.11.0"
raw-body "2.5.1"
type-is "~1.6.18"
unpipe "1.0.0"
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
@@ -2207,6 +2244,11 @@ browserslist@^4.21.3:
node-releases "^2.0.6"
update-browserslist-db "^1.0.9"
bytes@3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
call-bind@^1.0.0, call-bind@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
@@ -2379,6 +2421,18 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
content-disposition@0.5.4:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
dependencies:
safe-buffer "5.2.1"
content-type@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
convert-source-map@^1.5.0, convert-source-map@^1.7.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
@@ -2386,6 +2440,16 @@ convert-source-map@^1.5.0, convert-source-map@^1.7.0:
dependencies:
safe-buffer "~5.1.1"
cookie-signature@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
cookie@0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
copy-anything@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-3.0.2.tgz#7189171ff5e1893b2287e8bf574b8cd448ed50b1"
@@ -2487,6 +2551,13 @@ data-urls@^3.0.2:
whatwg-mimetype "^3.0.0"
whatwg-url "^11.0.0"
debug@2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
@@ -2563,6 +2634,16 @@ delayed-stream@~1.0.0:
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
depd@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
destroy@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
detect-node-es@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493"
@@ -2606,6 +2687,11 @@ domexception@^4.0.0:
dependencies:
webidl-conversions "^7.0.0"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
electron-to-chromium@^1.4.251:
version "1.4.255"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.255.tgz#dc52d1095b876ed8acf25865db10265b02b1d6e1"
@@ -2616,10 +2702,15 @@ emoji-regex@^8.0.0:
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
engine.io-client@~6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.2.1.tgz#b6d0be614c3f194faf6ab5505eba7eb456f56b0d"
integrity sha512-5cu7xubVxEwoB6O9hJ6Zfu990yBVjXfyMlE1ZvfO5L8if3Kvc9bgDNEapV0C5pMp+5Om1UZFnljxoOuFm6dBKA==
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
engine.io-client@~6.2.3:
version "6.2.3"
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.2.3.tgz#a8cbdab003162529db85e9de31575097f6d29458"
integrity sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.1"
@@ -2850,6 +2941,11 @@ escalade@^3.1.1:
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@@ -3041,6 +3137,11 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
execall@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/execall/-/execall-2.0.0.tgz#16a06b5fe5099df7d00be5d9c06eecded1663b45"
@@ -3048,6 +3149,43 @@ execall@^2.0.0:
dependencies:
clone-regexp "^2.1.0"
express@^4.18.2:
version "4.18.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
body-parser "1.20.1"
content-disposition "0.5.4"
content-type "~1.0.4"
cookie "0.5.0"
cookie-signature "1.0.6"
debug "2.6.9"
depd "2.0.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "1.2.0"
fresh "0.5.2"
http-errors "2.0.0"
merge-descriptors "1.0.1"
methods "~1.1.2"
on-finished "2.4.1"
parseurl "~1.3.3"
path-to-regexp "0.1.7"
proxy-addr "~2.0.7"
qs "6.11.0"
range-parser "~1.2.1"
safe-buffer "5.2.1"
send "0.18.0"
serve-static "1.15.0"
setprototypeof "1.2.0"
statuses "2.0.1"
type-is "~1.6.18"
utils-merge "1.0.1"
vary "~1.1.2"
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -3100,6 +3238,19 @@ fill-range@^7.0.1:
dependencies:
to-regex-range "^5.0.1"
finalhandler@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==
dependencies:
debug "2.6.9"
encodeurl "~1.0.2"
escape-html "~1.0.3"
on-finished "2.4.1"
parseurl "~1.3.3"
statuses "2.0.1"
unpipe "~1.0.0"
find-root@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
@@ -3141,10 +3292,10 @@ focus-lock@^0.11.2:
dependencies:
tslib "^2.0.3"
follow-redirects@^1.14.9:
version "1.15.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5"
integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==
follow-redirects@^1.15.0:
version "1.15.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
form-data@^4.0.0:
version "4.0.0"
@@ -3155,6 +3306,11 @@ form-data@^4.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
framer-motion@^7.5.3:
version "7.6.12"
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-7.6.12.tgz#a1f228e00da03ceb78482ae4a7fba63be39d34e4"
@@ -3183,6 +3339,11 @@ framesync@6.1.2:
dependencies:
tslib "2.4.0"
fresh@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -3444,6 +3605,17 @@ html-tags@^3.2.0:
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.2.0.tgz#dbb3518d20b726524e4dd43de397eb0a95726961"
integrity sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==
http-errors@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
dependencies:
depd "2.0.0"
inherits "2.0.4"
setprototypeof "1.2.0"
statuses "2.0.1"
toidentifier "1.0.1"
http-proxy-agent@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
@@ -3461,6 +3633,13 @@ https-proxy-agent@^5.0.1:
agent-base "6"
debug "4"
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
dependencies:
safer-buffer ">= 2.1.2 < 3"
iconv-lite@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
@@ -3509,7 +3688,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@^2.0.4:
inherits@2, inherits@2.0.4, inherits@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -3535,6 +3714,11 @@ invariant@^2.2.4:
dependencies:
loose-envify "^1.0.0"
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
@@ -3720,10 +3904,10 @@ jest-get-type@^27.4.0:
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.4.0.tgz#7503d2663fffa431638337b3998d39c5e928e9b5"
integrity sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ==
jotai@^1.8.5:
version "1.8.5"
resolved "https://registry.yarnpkg.com/jotai/-/jotai-1.8.5.tgz#a1d64682cc7b12697782488d5b6c6cfb54051520"
integrity sha512-c6MGQ3S0X8VctRUEmRLEL3FMib3F7Ppsx3x86c5XL+Gm6JjH6qIt0lb0F4yE2V5/9Tg+KhGiagfsdA5lg0D7cQ==
jotai@^1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/jotai/-/jotai-1.10.0.tgz#50c5224789282eca1cdc907a51e316d8d94c5976"
integrity sha512-3Q8kQU3Ktds+80Ku4dVcvnwSXEcK0Fg0b6mC1+4wz3rmF64lOGNUySKXQ4njvYCWodR8bw2HygOKYSYkHxQQmA==
js-sdsl@^4.1.4:
version "4.1.4"
@@ -3904,10 +4088,10 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
luxon@^3.0.1:
version "3.0.3"
resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.0.3.tgz#573e65531efd3d92265feb640f02ba7a192e2388"
integrity sha512-+EfHWnF+UT7GgTnq5zXg3ldnTKL2zdv7QJgsU5bjjpbH17E3qi/puMhQyJVYuCq+FRkogvB5WB6iVvUr+E4a7w==
luxon@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.1.0.tgz#9ac33d7142b7ea18d4ec8583cdeb0b079abef60d"
integrity sha512-7w6hmKC0/aoWnEsmPCu5Br54BmbmUp5GfcqBxQngRcXJ+q5fdfjEzn7dxmJh2YdDhgW8PccYtlWKSv4tQkrTQg==
lz-string@^1.4.4:
version "1.4.4"
@@ -3936,6 +4120,11 @@ mathml-tag-names@^2.1.3:
resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3"
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
memoize-one@^5.1.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
@@ -3959,11 +4148,21 @@ meow@^9.0.0:
type-fest "^0.18.0"
yargs-parser "^20.2.3"
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
merge2@^1.3.0, merge2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
micromatch@^4.0.4, micromatch@^4.0.5:
version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
@@ -3977,13 +4176,18 @@ mime-db@1.52.0:
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12:
mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mime@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
min-indent@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
@@ -4010,11 +4214,21 @@ minimist@^1.2.6:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
ms@2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
nanoid@^3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
@@ -4025,6 +4239,11 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
negotiator@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
node-releases@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
@@ -4135,6 +4354,13 @@ object.values@^1.1.5:
define-properties "^1.1.3"
es-abstract "^1.19.1"
on-finished@2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
dependencies:
ee-first "1.1.1"
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@@ -4223,6 +4449,11 @@ parse5@^7.0.0:
dependencies:
entities "^4.4.0"
parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
@@ -4243,6 +4474,11 @@ path-parse@^1.0.6, path-parse@^1.0.7:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
@@ -4379,6 +4615,19 @@ prop-types@^15.8.1:
object-assign "^4.1.1"
react-is "^16.13.1"
proxy-addr@~2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
dependencies:
forwarded "0.2.0"
ipaddr.js "1.9.1"
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
psl@^1.1.33:
version "1.8.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
@@ -4394,6 +4643,13 @@ qr.js@0.0.0:
resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f"
integrity sha1-ys6GOG9ZoNuAUPqQ2baw6IoeNk8=
qs@6.11.0:
version "6.11.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
dependencies:
side-channel "^1.0.4"
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
@@ -4409,6 +4665,21 @@ raf-schd@^4.0.2:
resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a"
integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==
range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
raw-body@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"
iconv-lite "0.4.24"
unpipe "1.0.0"
react-beautiful-dnd@^13.1.1:
version "13.1.1"
resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz#b0f3087a5840920abf8bb2325f1ffa46d8c4d0a2"
@@ -4723,12 +4994,17 @@ run-parallel@^1.1.9:
dependencies:
queue-microtask "^1.2.2"
safe-buffer@5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
"safer-buffer@>= 2.1.2 < 3.0.0":
"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@@ -4787,6 +5063,40 @@ semver@^7.3.5:
dependencies:
lru-cache "^6.0.0"
send@0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==
dependencies:
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "2.0.0"
mime "1.6.0"
ms "2.1.3"
on-finished "2.4.1"
range-parser "~1.2.1"
statuses "2.0.1"
serve-static@1.15.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
dependencies:
encodeurl "~1.0.2"
escape-html "~1.0.3"
parseurl "~1.3.3"
send "0.18.0"
setprototypeof@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@@ -4834,20 +5144,20 @@ slice-ansi@^4.0.0:
astral-regex "^2.0.0"
is-fullwidth-code-point "^3.0.0"
socket.io-client@^4.5.2:
version "4.5.2"
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.5.2.tgz#9481518c560388c980c88b01e3cf62f367f04c96"
integrity sha512-naqYfFu7CLDiQ1B7AlLhRXKX3gdeaIMfgigwavDzgJoIUYulc1qHH5+2XflTsXTPY7BlPH5rppJyUjhjrKQKLg==
socket.io-client@^4.5.4:
version "4.5.4"
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.5.4.tgz#d3cde8a06a6250041ba7390f08d2468ccebc5ac9"
integrity sha512-ZpKteoA06RzkD32IbqILZ+Cnst4xewU7ZYK12aS1mzHftFFjpoMz69IuhP/nL25pJfao/amoPI527KnuhFm01g==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.2"
engine.io-client "~6.2.1"
socket.io-parser "~4.2.0"
engine.io-client "~6.2.3"
socket.io-parser "~4.2.1"
socket.io-parser@~4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.0.tgz#3f01e5bc525d94aa52a97ed5cbc12e229bbc4d6b"
integrity sha512-tLfmEwcEwnlQTxFB7jibL/q2+q8dlVQzj4JdRLJ/W/G1+Fu9VSxCx1Lo+n1HvXxKnM//dUuD0xgiA7tQf57Vng==
socket.io-parser@~4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.1.tgz#01c96efa11ded938dcb21cbe590c26af5eff65e5"
integrity sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==
dependencies:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.1"
@@ -4906,6 +5216,11 @@ spdx-license-ids@^3.0.0:
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95"
integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==
statuses@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
@@ -5205,6 +5520,11 @@ toggle-selection@^1.0.6:
resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI=
toidentifier@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
tough-cookie@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4"
@@ -5306,6 +5626,14 @@ type-fest@^0.8.1:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
type-is@~1.6.18:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
dependencies:
media-typer "0.3.0"
mime-types "~2.1.24"
typeface-open-sans@^1.1.13:
version "1.1.13"
resolved "https://registry.yarnpkg.com/typeface-open-sans/-/typeface-open-sans-1.1.13.tgz#32a09ebd7df59601e01ad81216f98ce641eeafd1"
@@ -5341,6 +5669,11 @@ universalify@^0.1.2:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
update-browserslist-db@^1.0.9:
version "1.0.9"
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz#2924d3927367a38d5c555413a7ce138fc95fcb18"
@@ -5386,6 +5719,11 @@ util-deprecate@^1.0.2:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
utils-merge@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
v8-compile-cache@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
@@ -5399,6 +5737,11 @@ validate-npm-package-license@^3.0.1:
spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0"
vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
vite-plugin-svgr@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/vite-plugin-svgr/-/vite-plugin-svgr-2.2.2.tgz#c5c9cb573bf455bb079550531847ddc5d2e122af"
+1
View File
@@ -2,3 +2,4 @@ node_modules/
/test-results/
/playwright-report/
/playwright/.cache/
/src/version.js
+6 -6
View File
@@ -28,6 +28,11 @@ const appIcon = path.join(__dirname, './assets/logo.png');
let loaded = 'Nothing loaded';
let isQuitting = false;
// initialise
let win;
let splash;
let tray = null;
(async () => {
try {
const loadDepPath = isProduction
@@ -56,7 +61,7 @@ let isQuitting = false;
*/
function showNotification(title, text) {
new Notification({
title: title,
title,
body: text,
silent: true,
}).show();
@@ -82,13 +87,8 @@ function askToQuit() {
win.send('user-request-shutdown');
}
let win;
let splash;
let tray = null;
// Ensure there isn't another instance of the app running already
const lock = app.requestSingleInstanceLock();
if (!lock) {
dialog.showErrorBox('Multiple instances', 'An instance if the App is already running.');
app.quit();
+3 -1
View File
@@ -1,6 +1,6 @@
{
"name": "ontime",
"version": "1.9.6",
"version": "2.0.0-alpha",
"author": "Carlos Valente",
"description": "Time keeping for live events",
"repository": "https://github.com/cpvalente/ontime",
@@ -26,6 +26,8 @@
"supertest": "^6.2.2"
},
"scripts": {
"setup": "yarn install && yarn setdb && yarn addversion",
"addversion": "node -p \"'export const ONTIME_VERSION = ' + JSON.stringify(require('./package.json').version) + ';'\" > src/version.js",
"nodestart": "NODE_ENV=development node src/app.js",
"setdb": "cp demo-db/db.json src/preloaded-db/db.json",
"clean": "rm -rf ../client/build/ && rm -rf ../client/node_modules && rm -rf src/node_modules && rm -rf ./node_modules && rm -rf ./dist",
+2 -1
View File
@@ -26,6 +26,7 @@ import { socketProvider } from './classes/socket/SocketController.js';
import { initiateOSC, shutdownOSCServer } from './controllers/OscController.js';
import { fileURLToPath } from 'url';
import { DataProvider } from './classes/data-provider/DataProvider.js';
import { ONTIME_VERSION } from './version.js';
// get environment
const env = process.env.NODE_ENV || 'production';
@@ -33,7 +34,7 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const isTest = process.env.IS_TEST;
console.log(`Starting ontime version ${process.env.npm_package_version}`);
console.log(`Starting ontime version ${ONTIME_VERSION}`);
// import socket provider
const socket = socketProvider;
@@ -2,7 +2,7 @@
* Class Event Provider is a mediator for handling the local db
* and adds logic specific to ontime data
*/
import { db, data } from '../../modules/loadDb.js';
import { data, db } from '../../modules/loadDb.js';
export class DataProvider {
static getData() {
@@ -42,7 +42,7 @@ export class DataProvider {
await this.persist();
}
static getNumEvents() {
static getRundownLenght() {
return data.rundown.length;
}
+10 -8
View File
@@ -99,17 +99,16 @@ export class EventLoader {
*/
findPrevious() {
const timedEvents = EventLoader.getPlayableEvents();
if (timedEvents === null || this.selectedEventIndex === 0) {
if (timedEvents === null || !timedEvents.length || this.selectedEventIndex === 0) {
return null;
}
// if there is no event running, go to first
if (this.selectedEventIndex === null) {
return { id: timedEvents[0].id };
} else {
const newIndex = this.selectedEventIndex - 1;
return { id: timedEvents?.[newIndex].id };
}
const newIndex = this.selectedEventIndex - 1;
return { id: timedEvents?.[newIndex].id };
}
/**
@@ -118,17 +117,20 @@ export class EventLoader {
*/
findNext() {
const timedEvents = EventLoader.getPlayableEvents();
if (timedEvents === null || this.selectedEventIndex === this.numEvents - 1) {
if (
timedEvents === null ||
!timedEvents.length ||
this.selectedEventIndex === this.numEvents - 1
) {
return null;
}
// if there is no event running, go to first
if (this.selectedEventIndex === null) {
return { id: timedEvents[0].id };
} else {
const newIndex = this.selectedEventIndex + 1;
return { id: timedEvents?.[newIndex].id };
}
const newIndex = this.selectedEventIndex + 1;
return { id: timedEvents?.[newIndex].id };
}
/**
+5 -6
View File
@@ -4,7 +4,6 @@ import { OSCIntegration } from './integrations/Osc.js';
import { HTTPIntegration } from './integrations/Http.js';
import { cleanURL } from '../../utils/url.js';
import { EventLoader, eventLoader } from '../event-loader/EventLoader.js';
import { DataProvider } from '../data-provider/DataProvider.js';
/*
* Class EventTimer adds functions specific to APP
@@ -143,7 +142,7 @@ export class EventTimer extends Timer {
* @private
*/
_broadcastFeaturePlaybackControl() {
const numEvents = DataProvider.getNumEvents();
const numEvents = EventLoader.getNumEvents();
const featureData = {
playback: this.state,
selectedEventId: this.selectedEventId,
@@ -157,7 +156,7 @@ export class EventTimer extends Timer {
* @private
*/
_broadcastFeatureInfo() {
const numEvents = DataProvider.getNumEvents();
const numEvents = EventLoader.getNumEvents();
const featureData = {
titles: this.titles,
playback: this.state,
@@ -169,7 +168,7 @@ export class EventTimer extends Timer {
}
_broadcastFeatureCuesheet() {
const numEvents = DataProvider.getNumEvents();
const numEvents = EventLoader.getNumEvents();
const featureData = {
playback: this.state,
selectedEventId: this.selectedEventId,
@@ -426,7 +425,7 @@ export class EventTimer extends Timer {
return;
}
const { loadedEvent, loadedEventIndex, selectedEventId, nextEventId, titles, titlesPublic } =
const { loadedEvent, selectedEventIndex, selectedEventId, nextEventId, titles, titlesPublic } =
loadedData;
const start = loadedEvent.timeStart || 0;
@@ -438,7 +437,7 @@ export class EventTimer extends Timer {
}
this.duration = end - start;
this.selectedEventIndex = loadedEventIndex;
this.selectedEventIndex = selectedEventIndex;
this.selectedEventId = selectedEventId;
this.nextEventId = nextEventId;
+1 -1
View File
@@ -31,7 +31,7 @@ export const dbDownload = async (req, res) => {
res.download(dbInDisk, `${fileTitle}.json`, (err) => {
if (err) {
res.status(500).send({
message: 'Could not download the file. ' + err,
message: `Could not download the file: ${err}`,
});
}
});
+2 -2
View File
@@ -5,8 +5,8 @@ import { fileURLToPath } from 'url';
import { ensureDirectory, getAppDataPath } from '../utils/fileManagement.js';
import { config } from '../config/config.js';
import { validateFile } from '../utils/parserUtils.js';
import { dbModel as dbModel } from '../models/dataModel.js';
import { parseJson as parseJson } from '../utils/parser.js';
import { dbModel } from '../models/dataModel.js';
import { parseJson } from '../utils/parser.js';
/**
* @description Decides which path the database is in
+2 -2
View File
@@ -10,12 +10,12 @@
"express-validator": "^6.14.2",
"lowdb": "3.0.0",
"multer": "^1.4.4",
"nanoid": "^3.3.3",
"nanoid": "^4.0.0",
"node-osc": "^8.0.6",
"node-xlsx": "^0.21.0",
"passport": "^0.6.0",
"passport-local": "~1.0.0",
"socket.io": "^4.5.2"
"socket.io": "^4.5.4"
},
"devDependencies": {
"eslint": "^8.25.0"
+3 -13
View File
@@ -6,7 +6,7 @@ import {
event as eventDef,
} from '../models/eventsDefinition.js';
import { MAX_EVENTS } from '../settings.js';
import { eventLoader } from '../classes/event-loader/EventLoader.js';
import { EventLoader, eventLoader } from '../classes/event-loader/EventLoader.js';
const affectedLoaded = (affectedIds) => {
const now = eventLoader.selectedEventId;
@@ -23,7 +23,7 @@ const affectedLoaded = (affectedIds) => {
};
const isNewNext = () => {
const timedEvents = getTimedEvents();
const timedEvents = EventLoader.getTimedEvents();
const now = eventLoader.selectedEventId;
const next = eventLoader.nextEventId;
@@ -88,23 +88,13 @@ export function updateTimer(affectedIds) {
return false;
}
/**
* @description returns all events of type event
* @return {unknown[]}
*/
export function getTimedEvents() {
// return data.events.filter((e) => e.type === 'event');
const rundown = DataProvider.getRundown();
return rundown.filter((e) => e.type === 'event');
}
/**
* @description creates a new event with given data
* @param {object} eventData
* @return {unknown[]}
*/
export async function addEvent(eventData) {
const numEvents = DataProvider.getNumEvents();
const numEvents = DataProvider.getRundownLenght();
if (numEvents > MAX_EVENTS) {
throw new Error(`ERROR: Reached limit number of ${MAX_EVENTS} events`);
}
+3 -4
View File
@@ -1,6 +1,6 @@
// test cleanURL()
import {cleanURL} from "../url";
import { cleanURL } from '../url';
describe('url is correctly formatted', () => {
it('has no leading spaces', () => {
@@ -22,7 +22,7 @@ describe('url is correctly formatted', () => {
});
it('only contains allowed characters', () => {
const test = 'http://<>[]{}|\^';
const test = 'http://<>[]{}|^';
const expected = 'http://';
expect(cleanURL(test)).toBe(expected);
});
@@ -32,5 +32,4 @@ describe('url is correctly formatted', () => {
const expected = 'http://ontime.com';
expect(cleanURL(test)).toBe(expected);
});
});
});
+40 -40
View File
@@ -2,6 +2,29 @@ const mts = 1000; // millis to seconds
const mtm = 1000 * 60; // millis to minutes
const mth = 1000 * 60 * 60; // millis to hours
export const timeFormat = 'HH:mm';
export const timeFormatSeconds = 'HH:mm:ss';
/**
* @description Validates a time string
* @param {string} string - time string "23:00:12"
* @returns {boolean} string represents time
*/
export const isTimeString = (string) => {
// ^ # Start of string
// (?: # Try to match...
// (?: # Try to match...
// ([01]?\d|2[0-3]): # HH:
// )? # (optionally).
// ([0-5]?\d): # MM: (required)
// )? # (entire group optional, so either HH:MM:, MM: or nothing)
// ([0-5]?\d) # SS (required)
// $ # End of string
const regex = /^(?:(?:([01]?\d|2[0-3])[:,.])?([0-5]?\d)[:,.])?([0-5]?\d)$/;
return regex.test(string);
};
/**
* @description Converts milliseconds to string representing time
* @param {number} ms - time in milliseconds
@@ -46,46 +69,6 @@ export const dateToMillis = (date) => {
return h * mth + m * mtm + s * mts;
};
/**
* @description Parses an excel date using the correct parser
* @param {string} excelDate
* @returns {number} - time in milliseconds
*/
export const parseExcelDate = (excelDate) => {
// attempt converting to date object
const date = new Date(excelDate);
if (date instanceof Date && !isNaN(date)) {
return dateToMillis(date);
} else if (isTimeString(excelDate)) {
return forgivingStringToMillis(excelDate);
}
return 0;
};
export const timeFormat = 'HH:mm';
export const timeFormatSeconds = 'HH:mm:ss';
/**
* @description Validates a time string
* @param {string} string - time string "23:00:12"
* @returns {boolean} string represents time
*/
export const isTimeString = (string) => {
// ^ # Start of string
// (?: # Try to match...
// (?: # Try to match...
// ([01]?\d|2[0-3]): # HH:
// )? # (optionally).
// ([0-5]?\d): # MM: (required)
// )? # (entire group optional, so either HH:MM:, MM: or nothing)
// ([0-5]?\d) # SS (required)
// $ # End of string
const regex = /^(?:(?:([01]?\d|2[0-3])[:,.])?([0-5]?\d)[:,.])?([0-5]?\d)$/;
return regex.test(string);
};
/**
* @description safe parse string to int, copied from client code
* @param valueAsString
@@ -146,3 +129,20 @@ export const forgivingStringToMillis = (value, fillLeft = true) => {
}
return millis;
};
/**
* @description Parses an excel date using the correct parser
* @param {string} excelDate
* @returns {number} - time in milliseconds
*/
export const parseExcelDate = (excelDate) => {
// attempt converting to date object
const date = new Date(excelDate);
if (date instanceof Date && !isNaN(date)) {
return dateToMillis(date);
} else if (isTimeString(excelDate)) {
return forgivingStringToMillis(excelDate);
}
return 0;
};
+15 -15
View File
@@ -409,10 +409,10 @@ engine.io-parser@~5.0.3:
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0"
integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==
engine.io@~6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.0.tgz#003bec48f6815926f2b1b17873e576acd54f41d0"
integrity sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==
engine.io@~6.2.1:
version "6.2.1"
resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.1.tgz#e3f7826ebc4140db9bbaa9021ad6b1efb175878f"
integrity sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==
dependencies:
"@types/cookie" "^0.4.1"
"@types/cors" "^2.8.12"
@@ -991,10 +991,10 @@ multer@^1.4.4:
type-is "^1.6.4"
xtend "^4.0.0"
nanoid@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25"
integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==
nanoid@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-4.0.0.tgz#6e144dee117609232c3f415c34b0e550e64999a5"
integrity sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg==
natural-compare@^1.4.0:
version "1.4.0"
@@ -1349,7 +1349,7 @@ socket.io-adapter@~2.4.0:
resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz#b50a4a9ecdd00c34d4c8c808224daa1a786152a6"
integrity sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==
socket.io-parser@~4.2.0:
socket.io-parser@~4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.1.tgz#01c96efa11ded938dcb21cbe590c26af5eff65e5"
integrity sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==
@@ -1357,17 +1357,17 @@ socket.io-parser@~4.2.0:
"@socket.io/component-emitter" "~3.1.0"
debug "~4.3.1"
socket.io@^4.5.2:
version "4.5.2"
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.2.tgz#1eb25fd380ab3d63470aa8279f8e48d922d443ac"
integrity sha512-6fCnk4ARMPZN448+SQcnn1u8OHUC72puJcNtSgg2xS34Cu7br1gQ09YKkO1PFfDn/wyUE9ZgMAwosJed003+NQ==
socket.io@^4.5.4:
version "4.5.4"
resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.4.tgz#a4513f06e87451c17013b8d13fdfaf8da5a86a90"
integrity sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ==
dependencies:
accepts "~1.3.4"
base64id "~2.0.0"
debug "~4.3.2"
engine.io "~6.2.0"
engine.io "~6.2.1"
socket.io-adapter "~2.4.0"
socket.io-parser "~4.2.0"
socket.io-parser "~4.2.1"
ssf@~0.11.2:
version "0.11.2"