mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-08 08:53:51 +00:00
@@ -30,7 +30,7 @@ const SPip = withSocket(Pip);
|
||||
const SStudio = withSocket(StudioClock);
|
||||
|
||||
const FeatureWrapper = lazy(() => import('features/FeatureWrapper'));
|
||||
const EventList = lazy(() => import('features/editors/list/EventListExport'));
|
||||
const RundownPanel = lazy(() => import('features/rundown/RundownExport'));
|
||||
const TimerControl = lazy(() => import('features/control/playback/TimerControlExport'));
|
||||
const MessageControl = lazy(() => import('features/control/message/MessageControlExport'));
|
||||
const Info = lazy(() => import('features/info/InfoExport'));
|
||||
@@ -85,10 +85,10 @@ export default function AppRouter() {
|
||||
|
||||
{/*/!* Protected Routes - Elements *!/*/}
|
||||
<Route
|
||||
path='/eventlist'
|
||||
path='/rundown'
|
||||
element={
|
||||
<FeatureWrapper>
|
||||
<EventList />
|
||||
<RundownPanel />
|
||||
</FeatureWrapper>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -2,14 +2,14 @@ export const STATIC_PORT = 4001;
|
||||
export const EVENT_TABLE = ['event'];
|
||||
export const ALIASES = ['aliases'];
|
||||
export const USERFIELDS = ['userFields'];
|
||||
export const EVENTS_TABLE_KEY = 'events';
|
||||
export const EVENTS_TABLE = [EVENTS_TABLE_KEY];
|
||||
export const RUNDOWN_TABLE_KEY = 'rundown';
|
||||
export const RUNDOWN_TABLE = [RUNDOWN_TABLE_KEY];
|
||||
export const APP_INFO = ['appinfo'];
|
||||
export const OSC_SETTINGS = ['oscSettings'];
|
||||
export const APP_SETTINGS = ['appSettings'];
|
||||
export const VIEW_SETTINGS = ['viewSettings'];
|
||||
|
||||
export const FEAT_EVENTLIST = ['feat-eventList'];
|
||||
export const FEAT_RUNDOWN = ['feat-rundown'];
|
||||
export const FEAT_MESSAGECONTROL = ['feat-messagecontrol'];
|
||||
export const FEAT_PLAYBACKCONTROL = ['feat-playbackcontrol'];
|
||||
export const FEAT_INFO = ['feat-info'];
|
||||
@@ -24,8 +24,8 @@ export const calculateServer = () =>
|
||||
import.meta.env.DEV ? `http://localhost:${STATIC_PORT}` : window.location.origin;
|
||||
|
||||
export const serverURL = calculateServer();
|
||||
export const eventURL = `${serverURL}/${EVENT_TABLE}`;
|
||||
export const eventsURL = `${serverURL}/${EVENTS_TABLE}`;
|
||||
export const eventURL = `${serverURL}/event`;
|
||||
export const rundownURL = `${serverURL}/eventlist`;
|
||||
export const ontimeURL = `${serverURL}/ontime`;
|
||||
|
||||
export const stylesPath = 'external/styles/override.css';
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import axios from 'axios';
|
||||
|
||||
import { OntimeEventEntry } from '../models/EventTypes';
|
||||
import { OntimeRundown, OntimeRundownEntry } from '../models/EventTypes';
|
||||
|
||||
import { eventsURL } from './apiConstants';
|
||||
import { rundownURL } from './apiConstants';
|
||||
|
||||
/**
|
||||
* @description HTTP request to fetch all events
|
||||
* @return {Promise}
|
||||
*/
|
||||
export async function fetchAllEvents(): Promise<OntimeEventEntry[]> {
|
||||
const res = await axios.get(eventsURL);
|
||||
export async function fetchRundown(): Promise<OntimeRundown> {
|
||||
const res = await axios.get(rundownURL);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
@@ -17,32 +17,32 @@ export async function fetchAllEvents(): Promise<OntimeEventEntry[]> {
|
||||
* @description HTTP request to post new event
|
||||
* @return {Promise}
|
||||
*/
|
||||
export async function requestPostEvent(data: OntimeEventEntry) {
|
||||
return axios.post(eventsURL, data);
|
||||
export async function requestPostEvent(data: OntimeRundownEntry) {
|
||||
return axios.post(rundownURL, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description HTTP request to put new event
|
||||
* @return {Promise}
|
||||
*/
|
||||
export async function requestPutEvent(data: OntimeEventEntry) {
|
||||
return axios.put(eventsURL, data);
|
||||
export async function requestPutEvent(data: OntimeRundownEntry) {
|
||||
return axios.put(rundownURL, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description HTTP request to modify event
|
||||
* @return {Promise}
|
||||
*/
|
||||
export async function requestPatchEvent(data: OntimeEventEntry) {
|
||||
return axios.patch(eventsURL, data);
|
||||
export async function requestPatchEvent(data: OntimeRundownEntry) {
|
||||
return axios.patch(rundownURL, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description HTTP request to reorder events
|
||||
* @return {Promise}
|
||||
*/
|
||||
export async function requestReorderEvent(data: OntimeEventEntry) {
|
||||
return axios.patch(`${eventsURL}/reorder`, data);
|
||||
export async function requestReorderEvent(data: OntimeRundownEntry) {
|
||||
return axios.patch(`${rundownURL}/reorder`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,7 +50,7 @@ export async function requestReorderEvent(data: OntimeEventEntry) {
|
||||
* @return {Promise}
|
||||
*/
|
||||
export async function requestApplyDelay(eventId: string) {
|
||||
return axios.patch(`${eventsURL}/applydelay/${eventId}`);
|
||||
return axios.patch(`${rundownURL}/applydelay/${eventId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,7 +58,7 @@ export async function requestApplyDelay(eventId: string) {
|
||||
* @return {Promise}
|
||||
*/
|
||||
export async function requestDelete(eventId: string) {
|
||||
return axios.delete(`${eventsURL}/${eventId}`);
|
||||
return axios.delete(`${rundownURL}/${eventId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,5 +66,5 @@ export async function requestDelete(eventId: string) {
|
||||
* @return {Promise}
|
||||
*/
|
||||
export async function requestDeleteAll() {
|
||||
return axios.delete(`${eventsURL}/all`);
|
||||
return axios.delete(`${rundownURL}/all`);
|
||||
}
|
||||
|
||||
@@ -107,14 +107,14 @@ export async function postOSC(data: OscSettingsType) {
|
||||
* @description HTTP request to download db
|
||||
* @return {Promise}
|
||||
*/
|
||||
export const downloadEvents = async () => {
|
||||
export const downloadRundown = async () => {
|
||||
await axios({
|
||||
url: `${ontimeURL}/db`,
|
||||
method: 'GET',
|
||||
responseType: 'blob', // important
|
||||
}).then((response) => {
|
||||
const headerLine = response.headers['Content-Disposition'];
|
||||
let filename = 'events.json';
|
||||
let filename = 'rundown.json';
|
||||
|
||||
// try and get the filename from the response
|
||||
if (headerLine != null) {
|
||||
@@ -136,15 +136,15 @@ export const downloadEvents = async () => {
|
||||
* @description HTTP request to upload events db
|
||||
* @return {Promise}
|
||||
*/
|
||||
type UploadEventsOptions = {
|
||||
onlyEvents?: boolean;
|
||||
type UploadDataOptions = {
|
||||
onlyRundown?: boolean;
|
||||
}
|
||||
export const uploadEvents = async (file: string, setProgress: (value: number) => void, options?: UploadEventsOptions) => {
|
||||
export const uploadData = async (file: string, setProgress: (value: number) => void, options?: UploadDataOptions) => {
|
||||
const formData = new FormData();
|
||||
formData.append('userFile', file);
|
||||
const onlyEvents = options?.onlyEvents;
|
||||
const onlyRundown = options?.onlyRundown;
|
||||
await axios
|
||||
.post(`${ontimeURL}/db?onlyEvents=${onlyEvents}`, formData, {
|
||||
.post(`${ontimeURL}/db?onlyRundown=${onlyRundown}`, formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
|
||||
@@ -19,8 +19,8 @@ import {
|
||||
import { IoCloseSharp } from '@react-icons/all-files/io5/IoCloseSharp';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { EVENTS_TABLE } from '../../api/apiConstants';
|
||||
import { uploadEvents } from '../../api/ontimeApi';
|
||||
import { RUNDOWN_TABLE } from '../../api/apiConstants';
|
||||
import { uploadData } from '../../api/ontimeApi';
|
||||
import { LoggingContext } from '../../context/LoggingContext';
|
||||
import TooltipActionBtn from '../buttons/TooltipActionBtn';
|
||||
|
||||
@@ -58,11 +58,11 @@ export default function UploadModal({ onClose, isOpen }: UploadModalProps) {
|
||||
const handleUpload = useCallback(async () => {
|
||||
if (file) {
|
||||
try {
|
||||
await uploadEvents(file, setProgress, { onlyEvents: overrideOptionRef?.current?.checked });
|
||||
await uploadData(file, setProgress, { onlyEvents: overrideOptionRef?.current?.checked });
|
||||
} catch (error) {
|
||||
emitError(`Failed uploading file: ${error}`);
|
||||
} finally {
|
||||
await queryClient.invalidateQueries(EVENTS_TABLE);
|
||||
await queryClient.invalidateQueries(RUNDOWN_TABLE);
|
||||
setFile(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { EVENTS_TABLE } from '../api/apiConstants';
|
||||
import { fetchAllEvents } from '../api/eventsApi';
|
||||
|
||||
export default function useEventsList() {
|
||||
const {
|
||||
data,
|
||||
status,
|
||||
isError,
|
||||
refetch,
|
||||
} = useQuery(EVENTS_TABLE, fetchAllEvents, { placeholderData: [] });
|
||||
|
||||
return { data, status, isError, refetch };
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { RUNDOWN_TABLE } from '../api/apiConstants';
|
||||
import { fetchRundown } from '../api/eventsApi';
|
||||
|
||||
export default function useRundown() {
|
||||
const {
|
||||
data,
|
||||
status,
|
||||
isError,
|
||||
refetch,
|
||||
} = useQuery(RUNDOWN_TABLE, fetchRundown, { placeholderData: [] });
|
||||
|
||||
return { data, status, isError, refetch };
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useContext } from 'react';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { EVENTS_TABLE, EVENTS_TABLE_KEY } from '../api/apiConstants';
|
||||
import { RUNDOWN_TABLE_KEY,RUNDOWN_TABLE } from '../api/apiConstants';
|
||||
import {
|
||||
requestApplyDelay,
|
||||
requestDelete,
|
||||
@@ -27,7 +27,7 @@ export const useEventAction = () => {
|
||||
// Mutation finished, failed or successful
|
||||
// Fetch anyway, just to be sure
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries(EVENTS_TABLE);
|
||||
queryClient.invalidateQueries(RUNDOWN_TABLE);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ export const useEventAction = () => {
|
||||
// ************* CHECK OPTIONS
|
||||
// there is an option to pass an index of an array to use as start time
|
||||
if (typeof options?.startIsLastEnd !== 'undefined') {
|
||||
const events = queryClient.getQueryData(EVENTS_TABLE);
|
||||
const events = queryClient.getQueryData(RUNDOWN_TABLE);
|
||||
const previousEvent = events.find((event) => event.id === options.startIsLastEnd);
|
||||
newEvent.timeStart = previousEvent.timeEnd || 0;
|
||||
}
|
||||
@@ -71,13 +71,13 @@ export const useEventAction = () => {
|
||||
// we optimistically update here
|
||||
onMutate: async (newEvent) => {
|
||||
// cancel ongoing queries
|
||||
await queryClient.cancelQueries([EVENTS_TABLE_KEY, newEvent.id]);
|
||||
await queryClient.cancelQueries([RUNDOWN_TABLE_KEY, newEvent.id]);
|
||||
|
||||
// Snapshot the previous value
|
||||
const previousEvent = queryClient.getQueryData([EVENTS_TABLE_KEY, newEvent.id]);
|
||||
const previousEvent = queryClient.getQueryData([RUNDOWN_TABLE_KEY, newEvent.id]);
|
||||
|
||||
// optimistically update object
|
||||
queryClient.setQueryData([EVENTS_TABLE_KEY, newEvent.id], newEvent);
|
||||
queryClient.setQueryData([RUNDOWN_TABLE_KEY, newEvent.id], newEvent);
|
||||
|
||||
// Return a context with the previous and new events
|
||||
return { previousEvent, newEvent };
|
||||
@@ -85,12 +85,12 @@ export const useEventAction = () => {
|
||||
|
||||
// Mutation fails, rollback undoes optimist update
|
||||
onError: (error, newEvent, context) => {
|
||||
queryClient.setQueryData([EVENTS_TABLE_KEY, context.newEvent.id], context.previousEvent);
|
||||
queryClient.setQueryData([RUNDOWN_TABLE_KEY, context.newEvent.id], context.previousEvent);
|
||||
},
|
||||
// Mutation finished, failed or successful
|
||||
// Fetch anyway, just to be sure
|
||||
onSettled: async (newEvent) => {
|
||||
await queryClient.invalidateQueries([EVENTS_TABLE_KEY, newEvent.id]);
|
||||
await queryClient.invalidateQueries([RUNDOWN_TABLE_KEY, newEvent.id]);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -117,15 +117,15 @@ export const useEventAction = () => {
|
||||
// we optimistically update here
|
||||
onMutate: async (eventId) => {
|
||||
// cancel ongoing queries
|
||||
await queryClient.cancelQueries([EVENTS_TABLE_KEY, eventId]);
|
||||
await queryClient.cancelQueries([RUNDOWN_TABLE_KEY, eventId]);
|
||||
|
||||
// Snapshot the previous value
|
||||
const previousEvents = queryClient.getQueryData(EVENTS_TABLE);
|
||||
const previousEvents = queryClient.getQueryData(RUNDOWN_TABLE);
|
||||
|
||||
const filtered = [...previousEvents].filter((e) => e.id !== eventId);
|
||||
|
||||
// optimistically update object
|
||||
queryClient.setQueryData(EVENTS_TABLE, filtered);
|
||||
queryClient.setQueryData(RUNDOWN_TABLE, filtered);
|
||||
|
||||
// Return a context with the previous and new events
|
||||
return { previousEvents };
|
||||
@@ -133,12 +133,12 @@ export const useEventAction = () => {
|
||||
|
||||
// Mutation fails, rollback undoes optimist update
|
||||
onError: (error, eventId, context) => {
|
||||
queryClient.setQueryData(EVENTS_TABLE, context.previousEvents);
|
||||
queryClient.setQueryData(RUNDOWN_TABLE, context.previousEvents);
|
||||
},
|
||||
// Mutation finished, failed or successful
|
||||
// Fetch anyway, just to be sure
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries(EVENTS_TABLE);
|
||||
queryClient.invalidateQueries(RUNDOWN_TABLE);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -165,15 +165,15 @@ export const useEventAction = () => {
|
||||
// we optimistically update here
|
||||
onMutate: async () => {
|
||||
// cancel ongoing queries
|
||||
await queryClient.cancelQueries(EVENTS_TABLE, { exact: true });
|
||||
await queryClient.cancelQueries(RUNDOWN_TABLE, { exact: true });
|
||||
|
||||
// Snapshot the previous value
|
||||
const previousEvents = queryClient.getQueryData(EVENTS_TABLE);
|
||||
const previousEvents = queryClient.getQueryData(RUNDOWN_TABLE);
|
||||
|
||||
const clear = [];
|
||||
|
||||
// optimistically update object
|
||||
queryClient.setQueryData(EVENTS_TABLE, clear);
|
||||
queryClient.setQueryData(RUNDOWN_TABLE, clear);
|
||||
|
||||
// Return a context with the previous and new events
|
||||
return { previousEvents };
|
||||
@@ -181,12 +181,12 @@ export const useEventAction = () => {
|
||||
|
||||
// Mutation fails, rollback undos optimist update
|
||||
onError: (error, eventId, context) => {
|
||||
queryClient.setQueryData(EVENTS_TABLE, context.previousEvents);
|
||||
queryClient.setQueryData(RUNDOWN_TABLE, context.previousEvents);
|
||||
},
|
||||
// Mutation finished, failed or successful
|
||||
// Fetch anyway, just to be sure
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries(EVENTS_TABLE);
|
||||
queryClient.invalidateQueries(RUNDOWN_TABLE);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -208,7 +208,7 @@ export const useEventAction = () => {
|
||||
const _applyDelayMutation = useMutation(requestApplyDelay, {
|
||||
// Mutation finished, failed or successful
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries(EVENTS_TABLE);
|
||||
queryClient.invalidateQueries(RUNDOWN_TABLE);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -235,17 +235,17 @@ export const useEventAction = () => {
|
||||
// we optimistically update here
|
||||
onMutate: async (data) => {
|
||||
// cancel ongoing queries
|
||||
await queryClient.cancelQueries(EVENTS_TABLE, { exact: true });
|
||||
await queryClient.cancelQueries(RUNDOWN_TABLE, { exact: true });
|
||||
|
||||
// Snapshot the previous value
|
||||
const previousEvents = queryClient.getQueryData(EVENTS_TABLE);
|
||||
const previousEvents = queryClient.getQueryData(RUNDOWN_TABLE);
|
||||
|
||||
const e = [...previousEvents];
|
||||
const [reorderedItem] = e.splice(data.from, 1);
|
||||
e.splice(data.to, 0, reorderedItem);
|
||||
|
||||
// optimistically update object
|
||||
queryClient.setQueryData(EVENTS_TABLE, e);
|
||||
queryClient.setQueryData(RUNDOWN_TABLE, e);
|
||||
|
||||
// Return a context with the previous and new events
|
||||
return { previousEvents };
|
||||
@@ -253,12 +253,12 @@ export const useEventAction = () => {
|
||||
|
||||
// Mutation fails, rollback undoes optimist update
|
||||
onError: (error, eventId, context) => {
|
||||
queryClient.setQueryData(EVENTS_TABLE, context.previousEvents);
|
||||
queryClient.setQueryData(RUNDOWN_TABLE, context.previousEvents);
|
||||
},
|
||||
// Mutation finished, failed or successful
|
||||
// Fetch anyway, just to be sure
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries(EVENTS_TABLE);
|
||||
queryClient.invalidateQueries(RUNDOWN_TABLE);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { EVENTS_TABLE } from '../api/apiConstants';
|
||||
import { RUNDOWN_TABLE } from '../api/apiConstants';
|
||||
|
||||
/**
|
||||
* @description utility hook to handle mutations in events
|
||||
@@ -11,13 +11,13 @@ export default function useMutateEvents(mutation){
|
||||
return useMutation(mutation, {
|
||||
onMutate: async (newEvent) => {
|
||||
// cancel ongoing queries
|
||||
queryClient.cancelQueries(EVENTS_TABLE, { exact: true });
|
||||
queryClient.cancelQueries(RUNDOWN_TABLE, { exact: true });
|
||||
|
||||
// Snapshot the previous value
|
||||
const previousEvent = queryClient.getQueryData([EVENTS_TABLE, newEvent.id]);
|
||||
const previousEvent = queryClient.getQueryData([RUNDOWN_TABLE, newEvent.id]);
|
||||
|
||||
// optimistically update object
|
||||
queryClient.setQueryData([EVENTS_TABLE, newEvent.id], newEvent);
|
||||
queryClient.setQueryData([RUNDOWN_TABLE, newEvent.id], newEvent);
|
||||
|
||||
// Return a context with the previous and new event
|
||||
return { previousEvent, newEvent };
|
||||
@@ -25,13 +25,13 @@ export default function useMutateEvents(mutation){
|
||||
|
||||
// Mutation fails, rollback undoes optimist update
|
||||
onError: (error, newEvent, context) => {
|
||||
queryClient.setQueryData([EVENTS_TABLE, context.newEvent.id], context.previousEvent);
|
||||
queryClient.setQueryData([RUNDOWN_TABLE, context.newEvent.id], context.previousEvent);
|
||||
},
|
||||
|
||||
// Mutation finished, failed or successful
|
||||
// Fetch anyway, just to be sure
|
||||
onSettled: (newEvent) => {
|
||||
queryClient.invalidateQueries([EVENTS_TABLE, newEvent.id]);
|
||||
queryClient.invalidateQueries([RUNDOWN_TABLE, newEvent.id]);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -3,16 +3,16 @@ import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import {
|
||||
FEAT_CUESHEET,
|
||||
FEAT_EVENTLIST,
|
||||
FEAT_INFO,
|
||||
FEAT_MESSAGECONTROL,
|
||||
FEAT_PLAYBACKCONTROL,
|
||||
FEAT_RUNDOWN,
|
||||
TIMER,
|
||||
} from '../api/apiConstants';
|
||||
import { useSocket } from '../context/socketContext';
|
||||
|
||||
export const useEventListProvider = () => {
|
||||
const { data } = useQuery(FEAT_EVENTLIST, () => undefined, {
|
||||
export const useRundownProvider = () => {
|
||||
const { data } = useQuery(FEAT_RUNDOWN, () => undefined, {
|
||||
cacheTime: Infinity,
|
||||
staleTime: Infinity,
|
||||
});
|
||||
@@ -211,8 +211,8 @@ export const useSocketProvider = () => {
|
||||
}
|
||||
|
||||
socket.emit('get-ontime-feat-eventlist');
|
||||
socket.on('ontime-feat-eventlist', (featureData) => {
|
||||
queryClient.setQueryData(FEAT_EVENTLIST, () => featureData);
|
||||
socket.on('ontime-feat-rundown', (featureData) => {
|
||||
queryClient.setQueryData(FEAT_RUNDOWN, () => featureData);
|
||||
});
|
||||
|
||||
socket.emit('get-ontime-feat-messagecontrol');
|
||||
@@ -241,7 +241,7 @@ export const useSocketProvider = () => {
|
||||
});
|
||||
|
||||
return () => {
|
||||
socket.off('ontime-feat-eventlist');
|
||||
socket.off('ontime-feat-rundown');
|
||||
socket.off('ontime-feat-messagecontrol');
|
||||
socket.off('ontime-feat-playbackcontrol');
|
||||
socket.off('ontime-feat-info');
|
||||
|
||||
@@ -41,4 +41,5 @@ export type OntimeEvent = OntimeBaseEvent & {
|
||||
revision: number,
|
||||
}
|
||||
|
||||
export type OntimeEventEntry = OntimeDelay | OntimeBlock | OntimeEvent;
|
||||
export type OntimeRundownEntry = OntimeDelay | OntimeBlock | OntimeEvent;
|
||||
export type OntimeRundown = OntimeRundownEntry[]
|
||||
@@ -1,4 +1,4 @@
|
||||
import { OntimeEvent, OntimeEventEntry } from '../models/EventTypes';
|
||||
import { OntimeEvent, OntimeRundownEntry } from '../models/EventTypes';
|
||||
|
||||
import { formatTime } from './time';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { formatTime } from './time';
|
||||
* @returns {Object[]} Filtered events with calculated delays
|
||||
*/
|
||||
|
||||
export const getEventsWithDelay = (events: OntimeEventEntry[]) => {
|
||||
export const getEventsWithDelay = (events: OntimeRundownEntry[]) => {
|
||||
if (events == null) return [];
|
||||
|
||||
const unfilteredEvents = [...events];
|
||||
@@ -35,7 +35,7 @@ export const getEventsWithDelay = (events: OntimeEventEntry[]) => {
|
||||
* @param {number} limit - max number of events to return
|
||||
* @returns {Object[]} Event list with maximum <limit> objects
|
||||
*/
|
||||
export const trimEventlist = (events: OntimeEventEntry[], selectedId: string, limit: number) => {
|
||||
export const trimEventlist = (events: OntimeRundownEntry[], selectedId: string, limit: number) => {
|
||||
if (events == null) return [];
|
||||
|
||||
const BEFORE = 2;
|
||||
|
||||
@@ -27,7 +27,7 @@ export default function PlaybackTimer(props: PlaybackTimerProps) {
|
||||
const finish = stringFromMillis(timerData.expectedFinish, true);
|
||||
const isRolling = playback === 'roll';
|
||||
const isWaiting = timerData.secondaryTimer !== null && timerData.secondaryTimer > 0 && timerData.current === null;
|
||||
const disableButtons = selectedId == null || isRolling;
|
||||
const disableButtons = selectedId === null || isRolling;
|
||||
const isOvertime = timerData.current !== null && timerData.current < 0;
|
||||
|
||||
return (
|
||||
|
||||
@@ -9,7 +9,7 @@ import MenuBar from '../menu/MenuBar';
|
||||
|
||||
import styles from './Editor.module.scss';
|
||||
|
||||
const EventList = lazy(() => import('features/editors/list/EventListExport'));
|
||||
const Rundown = lazy(() => import('features/rundown/RundownExport'));
|
||||
const TimerControl = lazy(() => import('features/control/playback/TimerControlExport'));
|
||||
const MessageControl = lazy(() => import('features/control/message/MessageControlExport'));
|
||||
const Info = lazy(() => import('features/info/InfoExport'));
|
||||
@@ -51,7 +51,7 @@ export default function Editor() {
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</Box>
|
||||
<EventList />
|
||||
<Rundown />
|
||||
<MessageControl />
|
||||
<TimerControl />
|
||||
<Info />
|
||||
|
||||
@@ -14,14 +14,14 @@ import { stringFromMillis } from 'common/utils/time';
|
||||
import { calculateDuration, validateEntry } from 'common/utils/timesManager';
|
||||
import { useAtom } from 'jotai';
|
||||
|
||||
import useEventsList from '../../common/hooks-query/useEventsList';
|
||||
import useRundown from '../../common/hooks-query/useRundown';
|
||||
|
||||
import style from './EventEditor.module.scss';
|
||||
import CopyTag from '../../common/components/osc-tag/CopyTag';
|
||||
|
||||
export default function EventEditor() {
|
||||
const [openId] = useAtom(editorEventId);
|
||||
const { data } = useEventsList();
|
||||
const { data } = useRundown();
|
||||
const { emitWarning, emitError } = useContext(LoggingContext);
|
||||
const { updateEvent } = useEventAction();
|
||||
const [event, setEvent] = useState(null);
|
||||
|
||||
@@ -6,7 +6,7 @@ import { FiMinimize } from '@react-icons/all-files/fi/FiMinimize';
|
||||
import { FiSave } from '@react-icons/all-files/fi/FiSave';
|
||||
import { FiUpload } from '@react-icons/all-files/fi/FiUpload';
|
||||
import { IoSettingsOutline } from '@react-icons/all-files/io5/IoSettingsOutline';
|
||||
import { downloadEvents } from 'common/api/ontimeApi';
|
||||
import { downloadRundown } from 'common/api/ontimeApi';
|
||||
|
||||
import QuitIconBtn from '../../common/components/buttons/QuitIconBtn';
|
||||
import TooltipActionBtn from '../../common/components/buttons/TooltipActionBtn';
|
||||
@@ -136,7 +136,7 @@ export default function MenuBar(props: MenuBarProps) {
|
||||
<TooltipActionBtn
|
||||
{...buttonStyle}
|
||||
icon={<FiSave />}
|
||||
clickHandler={downloadEvents}
|
||||
clickHandler={downloadRundown}
|
||||
tooltip='Export event list'
|
||||
aria-label=''
|
||||
/>
|
||||
|
||||
+3
-3
@@ -18,14 +18,14 @@ import { CursorContext } from 'common/context/CursorContext';
|
||||
|
||||
import { useEventAction } from '../../common/hooks/useEventAction';
|
||||
|
||||
import style from './EventListMenu.module.scss';
|
||||
import style from './RundownMenu.module.scss';
|
||||
|
||||
const menuStyle = {
|
||||
color: '#000000',
|
||||
backgroundColor: 'rgba(255,255,255,1)',
|
||||
};
|
||||
|
||||
const EventListMenu = () => {
|
||||
const RundownMenu = () => {
|
||||
const { isCursorLocked, toggleCursorLocked } = useContext(CursorContext);
|
||||
const { addEvent, deleteAllEvents } = useEventAction();
|
||||
|
||||
@@ -91,4 +91,4 @@ const EventListMenu = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(EventListMenu);
|
||||
export default memo(RundownMenu);
|
||||
+24
-24
@@ -9,20 +9,20 @@ import {
|
||||
import Empty from 'common/components/state/Empty';
|
||||
import { CursorContext } from 'common/context/CursorContext';
|
||||
import { useEventAction } from 'common/hooks/useEventAction';
|
||||
import { useEventListProvider } from 'common/hooks/useSocketProvider';
|
||||
import { useRundownProvider } from 'common/hooks/useSocketProvider';
|
||||
import { duplicateEvent } from 'common/utils/eventsManager';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import useSubscription from '../../../common/hooks/useSubscription';
|
||||
import QuickAddBlock from '../quick-add-block/QuickAddBlock';
|
||||
import useSubscription from '../../common/hooks/useSubscription';
|
||||
|
||||
import EventListItem from './EventListItem';
|
||||
import QuickAddBlock from './quick-add-block/QuickAddBlock';
|
||||
import RundownEntry from './RundownEntry';
|
||||
|
||||
import style from './List.module.scss';
|
||||
import style from './Rundown.module.scss';
|
||||
|
||||
export default function EventList(props) {
|
||||
const { events } = props;
|
||||
export default function Rundown(props) {
|
||||
const { entries } = props;
|
||||
const { cursor, moveCursorUp, moveCursorDown, moveCursorTo, isCursorLocked } =
|
||||
useContext(CursorContext);
|
||||
const startTimeIsLastEnd = useAtomValue(startTimeIsLastEndAtom);
|
||||
@@ -30,7 +30,7 @@ export default function EventList(props) {
|
||||
const { addEvent, reorderEvent } = useEventAction();
|
||||
const cursorRef = createRef();
|
||||
const showQuickEntry = useAtomValue(showQuickEntryAtom);
|
||||
const data = useEventListProvider();
|
||||
const data = useRundownProvider();
|
||||
const [selectedId] = useSubscription('selected-id', null);
|
||||
const [nextId] = useSubscription('next-id', null);
|
||||
|
||||
@@ -39,8 +39,8 @@ export default function EventList(props) {
|
||||
if (cursor === -1) {
|
||||
addEvent({ type: type });
|
||||
} else {
|
||||
const previousEvent = events?.[cursor];
|
||||
const nextEvent = events?.[cursor + 1];
|
||||
const previousEvent = entries?.[cursor];
|
||||
const nextEvent = entries?.[cursor + 1];
|
||||
|
||||
// prevent adding two non-event blocks consecutively
|
||||
const isPreviousDifferent = previousEvent?.type !== type;
|
||||
@@ -64,7 +64,7 @@ export default function EventList(props) {
|
||||
}
|
||||
}
|
||||
},
|
||||
[addEvent, defaultPublic, events, startTimeIsLastEnd],
|
||||
[addEvent, defaultPublic, entries, startTimeIsLastEnd],
|
||||
);
|
||||
|
||||
// Handle keyboard shortcuts
|
||||
@@ -77,7 +77,7 @@ export default function EventList(props) {
|
||||
if (e.altKey && (!e.ctrlKey || !e.shiftKey)) {
|
||||
// Arrow down
|
||||
if (e.keyCode === 40) {
|
||||
if (cursor < events.length - 1) moveCursorDown();
|
||||
if (cursor < entries.length - 1) moveCursorDown();
|
||||
}
|
||||
// Arrow up
|
||||
if (e.keyCode === 38) {
|
||||
@@ -103,21 +103,21 @@ export default function EventList(props) {
|
||||
}
|
||||
}
|
||||
},
|
||||
[cursor, events.length, insertAtCursor, moveCursorDown, moveCursorUp],
|
||||
[cursor, entries.length, insertAtCursor, moveCursorDown, moveCursorUp],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// attach the event listener
|
||||
document.addEventListener('keydown', handleKeyPress);
|
||||
|
||||
if (cursor > events.length - 1) moveCursorTo(events.length - 1);
|
||||
if (events.length > 0 && cursor === -1) moveCursorTo(0);
|
||||
if (cursor > entries.length - 1) moveCursorTo(entries.length - 1);
|
||||
if (entries.length > 0 && cursor === -1) moveCursorTo(0);
|
||||
|
||||
// remove the event listener
|
||||
return () => {
|
||||
document.removeEventListener('keydown', handleKeyPress);
|
||||
};
|
||||
}, [handleKeyPress, cursor, events, moveCursorTo]);
|
||||
}, [handleKeyPress, cursor, entries, moveCursorTo]);
|
||||
|
||||
// when cursor moves, view should follow
|
||||
useEffect(() => {
|
||||
@@ -138,7 +138,7 @@ export default function EventList(props) {
|
||||
// move cursor
|
||||
let gotoIndex = -1;
|
||||
let found = false;
|
||||
for (const e of events) {
|
||||
for (const e of entries) {
|
||||
gotoIndex++;
|
||||
if (e.id === data.selectedEventId) {
|
||||
found = true;
|
||||
@@ -166,10 +166,10 @@ export default function EventList(props) {
|
||||
[reorderEvent],
|
||||
);
|
||||
|
||||
if (events.length < 1) {
|
||||
if (entries.length < 1) {
|
||||
return (
|
||||
<div className={style.alignCenter}>
|
||||
<Empty text='No Events' style={{ marginTop: '7vh' }} />
|
||||
<Empty text='No data yet' style={{ marginTop: '7vh' }} />
|
||||
<Button
|
||||
onClick={() => insertAtCursor('event', cursor)}
|
||||
variant='solid'
|
||||
@@ -192,7 +192,7 @@ export default function EventList(props) {
|
||||
<Droppable droppableId='eventlist'>
|
||||
{(provided) => (
|
||||
<div className={style.list} {...provided.droppableProps} ref={provided.innerRef}>
|
||||
{events.map((e, index) => {
|
||||
{entries.map((e, index) => {
|
||||
if (index === 0) {
|
||||
cumulativeDelay = 0;
|
||||
eventIndex = -1;
|
||||
@@ -207,7 +207,7 @@ export default function EventList(props) {
|
||||
thisEnd = e.timeEnd;
|
||||
previousEventId = e.id;
|
||||
}
|
||||
const isLast = index === events.length - 1;
|
||||
const isLast = index === entries.length - 1;
|
||||
return (
|
||||
<div
|
||||
key={e.id}
|
||||
@@ -218,7 +218,7 @@ export default function EventList(props) {
|
||||
ref={cursor === index ? cursorRef : undefined}
|
||||
className={cursor === index ? style.cursor : ''}
|
||||
>
|
||||
<EventListItem
|
||||
<RundownEntry
|
||||
type={e.type}
|
||||
index={index}
|
||||
eventIndex={eventIndex}
|
||||
@@ -251,6 +251,6 @@ export default function EventList(props) {
|
||||
);
|
||||
}
|
||||
|
||||
EventList.propTypes = {
|
||||
events: PropTypes.array,
|
||||
Rundown.propTypes = {
|
||||
entries: PropTypes.array,
|
||||
};
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
@use '../../../theme/main' as *;
|
||||
@use '../../../theme/mixins' as *;
|
||||
@use '../../theme/main' as *;
|
||||
@use '../../theme/mixins' as *;
|
||||
|
||||
.eventContainer {
|
||||
margin-top: 1em;
|
||||
+9
-8
@@ -6,16 +6,17 @@ import {
|
||||
} from 'common/atoms/LocalEventSettings';
|
||||
import { LoggingContext } from 'common/context/LoggingContext';
|
||||
import { useEventAction } from 'common/hooks/useEventAction';
|
||||
import { OntimeEvent, OntimeEventEntry } from 'common/models/EventTypes';
|
||||
import { OntimeEvent, OntimeRundownEntry } from 'common/models/EventTypes';
|
||||
import { Playstate } from 'common/models/OntimeTypes';
|
||||
import { duplicateEvent } from 'common/utils/eventsManager';
|
||||
import { calculateDuration } from 'common/utils/timesManager';
|
||||
import { useAtom, useAtomValue } from 'jotai';
|
||||
|
||||
import { CursorContext } from '../../../common/context/CursorContext';
|
||||
import BlockBlock from '../block-block/BlockBlock';
|
||||
import DelayBlock from '../delay-block/DelayBlock';
|
||||
import EventBlock from '../event-block/EventBlock';
|
||||
import { CursorContext } from '../../common/context/CursorContext';
|
||||
|
||||
import BlockBlock from './block-block/BlockBlock';
|
||||
import DelayBlock from './delay-block/DelayBlock';
|
||||
import EventBlock from './event-block/EventBlock';
|
||||
|
||||
export type EventItemActions =
|
||||
'set-cursor'
|
||||
@@ -26,10 +27,10 @@ export type EventItemActions =
|
||||
| 'clone'
|
||||
| 'update'
|
||||
|
||||
interface EventListItemProps {
|
||||
interface RundownEntryProps {
|
||||
index: number;
|
||||
eventIndex: number;
|
||||
data: OntimeEventEntry;
|
||||
data: OntimeRundownEntry;
|
||||
selected: boolean;
|
||||
next: boolean;
|
||||
delay: number;
|
||||
@@ -37,7 +38,7 @@ interface EventListItemProps {
|
||||
playback: Playstate;
|
||||
}
|
||||
|
||||
export default function EventListItem(props: EventListItemProps) {
|
||||
export default function RundownEntry(props: RundownEntryProps) {
|
||||
const { index, eventIndex, data, selected, next, delay, previousEnd, playback } = props;
|
||||
const { emitError } = useContext(LoggingContext);
|
||||
const startTimeIsLastEnd = useAtomValue(startTimeIsLastEndAtom);
|
||||
+6
-6
@@ -4,20 +4,20 @@ import ErrorBoundary from 'common/components/errorBoundary/ErrorBoundary';
|
||||
import { CursorProvider } from 'common/context/CursorContext';
|
||||
import { handleLinks } from 'common/utils/linkUtils';
|
||||
|
||||
import EventListWrapper from './EventListWrapper';
|
||||
import RundownWrapper from './RundownWrapper';
|
||||
|
||||
import style from '../Editor.module.scss';
|
||||
import style from '../editors/Editor.module.scss';
|
||||
|
||||
export default function EventListExport() {
|
||||
export default function RundownExport() {
|
||||
return (
|
||||
<CursorProvider>
|
||||
<Box className={style.editor} data-testid="panel-event-list">
|
||||
<Box className={style.editor} data-testid='panel-rundown'>
|
||||
<FiArrowUpRight
|
||||
className={style.corner}
|
||||
onClick={(event) => handleLinks(event, 'eventlist')}
|
||||
onClick={(event) => handleLinks(event, 'rundown')}
|
||||
/>
|
||||
<ErrorBoundary>
|
||||
<EventListWrapper />
|
||||
<RundownWrapper />
|
||||
</ErrorBoundary>
|
||||
</Box>
|
||||
</CursorProvider>
|
||||
+8
-8
@@ -1,17 +1,17 @@
|
||||
import { useContext, useEffect } from 'react';
|
||||
import Empty from 'common/components/state/Empty';
|
||||
import { LoggingContext } from 'common/context/LoggingContext';
|
||||
import EventListMenu from 'features/menu/EventListMenu';
|
||||
import RundownMenu from 'features/menu/RundownMenu';
|
||||
|
||||
import useEventsList from '../../../common/hooks-query/useEventsList';
|
||||
import useRundown from '../../common/hooks-query/useRundown';
|
||||
|
||||
import EventList from './EventList';
|
||||
import Rundown from './Rundown';
|
||||
|
||||
import styles from '../Editor.module.scss';
|
||||
import styles from '../editors/Editor.module.scss';
|
||||
|
||||
export default function EventListWrapper() {
|
||||
export default function RundownWrapper() {
|
||||
const { emitError } = useContext(LoggingContext);
|
||||
const { data, status, isError } = useEventsList();
|
||||
const { data, status, isError } = useRundown();
|
||||
|
||||
useEffect(() => {
|
||||
if (isError) {
|
||||
@@ -21,10 +21,10 @@ export default function EventListWrapper() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<EventListMenu />
|
||||
<RundownMenu />
|
||||
<div className={styles.content}>
|
||||
{status === 'success' && data ? (
|
||||
<EventList events={data} />
|
||||
<Rundown entries={data} />
|
||||
) : (
|
||||
<Empty text='Connecting to server' />
|
||||
)}
|
||||
+1
-1
@@ -20,7 +20,7 @@ import { useAtom } from 'jotai';
|
||||
import { useEventProvider } from '../../../common/hooks/useSocketProvider';
|
||||
import { Playstate } from '../../../common/models/OntimeTypes';
|
||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||
import { EventItemActions } from '../list/EventListItem';
|
||||
import { EventItemActions } from '../RundownEntry';
|
||||
|
||||
import EventBlockActionMenu from './composite/EventBlockActionMenu';
|
||||
import EventBlockTimers from './composite/EventBlockTimers';
|
||||
@@ -4,7 +4,7 @@ import { requestPatchEvent } from '../../common/api/eventsApi';
|
||||
import { TableSettingsContext } from '../../common/context/TableSettingsContext';
|
||||
import useMutateEvents from '../../common/hooks/useMutateEvents';
|
||||
import { useCuesheetProvider } from '../../common/hooks/useSocketProvider';
|
||||
import useEventsList from '../../common/hooks-query/useEventsList';
|
||||
import useRundown from '../../common/hooks-query/useRundown';
|
||||
import useUserFields from '../../common/hooks-query/useUserFields';
|
||||
|
||||
import OntimeTable from './OntimeTable';
|
||||
@@ -14,7 +14,7 @@ import { makeCSV, makeTable } from './utils';
|
||||
import style from './Table.module.scss';
|
||||
|
||||
export default function TableWrapper() {
|
||||
const { data: events } = useEventsList();
|
||||
const { data: events } = useRundown();
|
||||
const { data: userFields } = useUserFields();
|
||||
const mutation = useMutateEvents(requestPatchEvent);
|
||||
const { theme } = useContext(TableSettingsContext);
|
||||
|
||||
@@ -5,12 +5,12 @@ import { useSocket } from '../../common/context/socketContext';
|
||||
import { useMessageControlProvider } from '../../common/hooks/useSocketProvider';
|
||||
import useSubscription from '../../common/hooks/useSubscription';
|
||||
import useEvent from '../../common/hooks-query/useEvent';
|
||||
import useEventsList from '../../common/hooks-query/useEventsList';
|
||||
import useRundown from '../../common/hooks-query/useRundown';
|
||||
import useViewSettings from '../../common/hooks-query/useViewSettings';
|
||||
|
||||
const withSocket = (Component) => {
|
||||
return (props) => {
|
||||
const { data: eventsData } = useEventsList();
|
||||
const { data: eventsData } = useRundown();
|
||||
const { data: genData } = useEvent();
|
||||
const { data: viewSettings } = useViewSettings();
|
||||
const { data: messages } = useMessageControlProvider();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"events": [
|
||||
"rundown": [
|
||||
{
|
||||
"title": "Welcome to Ontime",
|
||||
"subtitle": "Subtitles are useful",
|
||||
|
||||
+4
-4
@@ -14,7 +14,7 @@ import http from 'http';
|
||||
import cors from 'cors';
|
||||
|
||||
// Import Routes
|
||||
import { router as eventsRouter } from './routes/eventsRouter.js';
|
||||
import { router as rundownRouter } from './routes/rundownRouter.js';
|
||||
import { router as eventRouter } from './routes/eventRouter.js';
|
||||
import { router as ontimeRouter } from './routes/ontimeRouter.js';
|
||||
import { router as playbackRouter } from './routes/playbackRouter.js';
|
||||
@@ -54,7 +54,7 @@ app.use(express.urlencoded({ extended: true }));
|
||||
app.use(express.json({ limit: '1mb' }));
|
||||
|
||||
// Implement route endpoints
|
||||
app.use('/events', eventsRouter);
|
||||
app.use('/eventlist', rundownRouter);
|
||||
app.use('/event', eventRouter);
|
||||
app.use('/ontime', ontimeRouter);
|
||||
app.use('/playback', playbackRouter);
|
||||
@@ -133,7 +133,7 @@ const server = http.createServer(app);
|
||||
*/
|
||||
export const startServer = async (overrideConfig = null) => {
|
||||
const port = 4001; // port hardcoded
|
||||
const { events, http } = DataProvider.getData();
|
||||
const { rundown, http } = DataProvider.getData();
|
||||
|
||||
// Start server
|
||||
const returnMessage = `Ontime is listening on port ${port}`;
|
||||
@@ -151,7 +151,7 @@ export const startServer = async (overrideConfig = null) => {
|
||||
|
||||
// init timer
|
||||
global.timer = new EventTimer(socket, config.timer, oscConfig, http);
|
||||
global.timer.setupWithEventList(events.filter((entry) => entry.type === 'event'));
|
||||
global.timer.setupWithEventList(rundown.filter((entry) => entry.type === 'event'));
|
||||
|
||||
socket.info('SERVER', returnMessage);
|
||||
socket.startListener();
|
||||
|
||||
@@ -19,35 +19,35 @@ export class DataProvider {
|
||||
return data.event;
|
||||
}
|
||||
|
||||
static async setEvents(newData) {
|
||||
data.events = [...newData];
|
||||
static async setRundown(newData) {
|
||||
data.rundown = [...newData];
|
||||
await this.persist();
|
||||
}
|
||||
|
||||
static getEventById(eventId) {
|
||||
return data.events.find((e) => e.id === eventId);
|
||||
return data.rundown.find((e) => e.id === eventId);
|
||||
}
|
||||
|
||||
static async updateEventById(eventId, newData) {
|
||||
const eventIndex = data.events.findIndex((e) => e.id === eventId);
|
||||
const e = data.events[eventIndex];
|
||||
data.events[eventIndex] = { ...e, ...newData };
|
||||
data.events[eventIndex].revision++;
|
||||
const eventIndex = data.rundown.findIndex((e) => e.id === eventId);
|
||||
const e = data.rundown[eventIndex];
|
||||
data.rundown[eventIndex] = { ...e, ...newData };
|
||||
data.rundown[eventIndex].revision++;
|
||||
await this.persist();
|
||||
return data.events[eventIndex];
|
||||
return data.rundown[eventIndex];
|
||||
}
|
||||
|
||||
static async deleteEvent(eventId) {
|
||||
data.events = Array.from(data.events).filter((e) => e.id !== eventId);
|
||||
data.rundown = Array.from(data.rundown).filter((e) => e.id !== eventId);
|
||||
await this.persist();
|
||||
}
|
||||
|
||||
static getNumEvents() {
|
||||
return data.events.length;
|
||||
return data.rundown.length;
|
||||
}
|
||||
|
||||
static async deleteAllEvents() {
|
||||
data.events = [];
|
||||
static async clearRundown() {
|
||||
data.rundown = [];
|
||||
await db.write();
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ export class DataProvider {
|
||||
*/
|
||||
static async insertEventAt(entry, index) {
|
||||
// get events
|
||||
const events = DataProvider.getEvents();
|
||||
const events = DataProvider.getRundown();
|
||||
const count = events.length;
|
||||
const order = entry.order;
|
||||
|
||||
@@ -83,7 +83,7 @@ export class DataProvider {
|
||||
}
|
||||
|
||||
// save events
|
||||
await DataProvider.setEvents(events);
|
||||
await DataProvider.setRundown(events);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,7 +94,7 @@ export class DataProvider {
|
||||
* @private
|
||||
*/
|
||||
static async insertEventAfterId(entry, id) {
|
||||
const index = [...data.events].findIndex((event) => event.id === id);
|
||||
const index = [...data.rundown].findIndex((event) => event.id === id);
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { _after, ...sanitisedEvent } = entry;
|
||||
await DataProvider.insertEventAt(sanitisedEvent, index + 1);
|
||||
@@ -145,8 +145,8 @@ export class DataProvider {
|
||||
await this.persist();
|
||||
}
|
||||
|
||||
static getEvents() {
|
||||
return [...data.events];
|
||||
static getRundown() {
|
||||
return [...data.rundown];
|
||||
}
|
||||
|
||||
static async persist() {
|
||||
@@ -161,7 +161,7 @@ export class DataProvider {
|
||||
data.http = mergedData.http;
|
||||
data.aliases = mergedData.aliases;
|
||||
data.userFields = mergedData.userFields;
|
||||
data.events = mergedData.events;
|
||||
data.rundown = mergedData.rundown;
|
||||
await this.persist();
|
||||
}
|
||||
|
||||
@@ -173,8 +173,8 @@ export class DataProvider {
|
||||
static safeMerge(existing, newData) {
|
||||
const mergedData = { ...existing };
|
||||
|
||||
if (typeof newData?.events !== 'undefined') {
|
||||
mergedData.events = newData.events;
|
||||
if (typeof newData?.rundown !== 'undefined') {
|
||||
mergedData.rundown = newData.rundown;
|
||||
}
|
||||
if (typeof newData?.event !== 'undefined') {
|
||||
mergedData.event = { ...newData.event };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
let instance;
|
||||
|
||||
export class MessageManager {
|
||||
class MessageManager {
|
||||
constructor() {
|
||||
if (instance) {
|
||||
throw new Error('There can be only one');
|
||||
|
||||
@@ -320,8 +320,8 @@ class SocketController {
|
||||
* */
|
||||
|
||||
// 1. EVENT LIST
|
||||
socket.on('get-ontime-feat-eventlist', () => {
|
||||
global.timer._broadcastFeatureEventList();
|
||||
socket.on('get-ontime-feat-rundown', () => {
|
||||
global.timer._broadcastFeatureRundown();
|
||||
});
|
||||
|
||||
// 2. MESSAGE CONTROL
|
||||
|
||||
@@ -50,7 +50,7 @@ export class EventTimer extends Timer {
|
||||
// call general title reset
|
||||
this._resetSelection();
|
||||
|
||||
this._eventlist = [];
|
||||
this.rundown = [];
|
||||
|
||||
// set recurrent emits
|
||||
this._interval = setInterval(() => this.runCycle(), timerConfig?.refresh || 1000);
|
||||
@@ -130,13 +130,13 @@ export class EventTimer extends Timer {
|
||||
* @description Broadcast data for Event List feature
|
||||
* @private
|
||||
*/
|
||||
_broadcastFeatureEventList() {
|
||||
_broadcastFeatureRundown() {
|
||||
const featureData = {
|
||||
selectedEventId: this.selectedEventId,
|
||||
nextEventId: this.nextEventId,
|
||||
playback: this.state,
|
||||
};
|
||||
this.socket.send('ontime-feat-eventlist', featureData);
|
||||
this.socket.send('ontime-feat-rundown', featureData);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,7 +147,7 @@ export class EventTimer extends Timer {
|
||||
const featureData = {
|
||||
playback: this.state,
|
||||
selectedEventId: this.selectedEventId,
|
||||
numEvents: this._eventlist.length,
|
||||
numEvents: this.rundown.length,
|
||||
};
|
||||
this.socket.send('ontime-feat-playbackcontrol', featureData);
|
||||
}
|
||||
@@ -162,7 +162,7 @@ export class EventTimer extends Timer {
|
||||
playback: this.state,
|
||||
selectedEventId: this.selectedEventId,
|
||||
selectedEventIndex: this.selectedEventIndex,
|
||||
numEvents: this._eventlist.length,
|
||||
numEvents: this.rundown.length,
|
||||
};
|
||||
this.socket.send('ontime-feat-info', featureData);
|
||||
}
|
||||
@@ -172,7 +172,7 @@ export class EventTimer extends Timer {
|
||||
playback: this.state,
|
||||
selectedEventId: this.selectedEventId,
|
||||
selectedEventIndex: this.selectedEventIndex,
|
||||
numEvents: this._eventlist.length,
|
||||
numEvents: this.rundown.length,
|
||||
titleNow: this.titles.titleNow,
|
||||
};
|
||||
this.socket.send('ontime-feat-cuesheet', featureData);
|
||||
@@ -183,13 +183,13 @@ export class EventTimer extends Timer {
|
||||
*/
|
||||
broadcastState() {
|
||||
// feature sync
|
||||
this._broadcastFeatureEventList();
|
||||
this._broadcastFeatureRundown();
|
||||
this._broadcastFeaturePlaybackControl();
|
||||
this._broadcastFeatureInfo();
|
||||
this._broadcastFeatureCuesheet();
|
||||
this._broadcastFeatureTimer();
|
||||
|
||||
const numEvents = this._eventlist.length;
|
||||
const numEvents = this.rundown.length;
|
||||
this.broadcastTimer();
|
||||
this.socket.send('playstate', this.state);
|
||||
this.socket.send('selected', {
|
||||
@@ -214,7 +214,7 @@ export class EventTimer extends Timer {
|
||||
*/
|
||||
trigger(action, payload) {
|
||||
let success = true;
|
||||
const numEvents = this._eventlist.length;
|
||||
const numEvents = this.rundown.length;
|
||||
switch (action) {
|
||||
case 'start': {
|
||||
if (!numEvents) return false;
|
||||
@@ -537,13 +537,13 @@ export class EventTimer extends Timer {
|
||||
this.unload();
|
||||
|
||||
// set general
|
||||
this._eventlist = [];
|
||||
this.rundown = [];
|
||||
|
||||
// update lifecycle: onStop
|
||||
this.ontimeCycle = this.cycleState.onStop;
|
||||
|
||||
// update clients
|
||||
this.socket.send('numevents', this._eventlist.length);
|
||||
this.socket.send('numevents', this.rundown.length);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -558,7 +558,7 @@ export class EventTimer extends Timer {
|
||||
const numEvents = events.length;
|
||||
|
||||
// set general
|
||||
this._eventlist = events;
|
||||
this.rundown = events;
|
||||
|
||||
// list may contain no events
|
||||
if (numEvents < 1) return;
|
||||
@@ -585,7 +585,7 @@ export class EventTimer extends Timer {
|
||||
const numEvents = events.length;
|
||||
|
||||
// set general
|
||||
this._eventlist = events;
|
||||
this.rundown = events;
|
||||
|
||||
// list may be empty
|
||||
if (numEvents < 1) {
|
||||
@@ -594,12 +594,12 @@ export class EventTimer extends Timer {
|
||||
}
|
||||
|
||||
// auto load if is the there was nothing before
|
||||
if (!this._eventlist.length) {
|
||||
if (!this.rundown.length) {
|
||||
this.loadEvent(0);
|
||||
} else if (this.selectedEventId != null) {
|
||||
// handle reload selected
|
||||
// Look for event (order might have changed)
|
||||
const eventIndex = this._eventlist.findIndex((e) => e.id === this.selectedEventId);
|
||||
const eventIndex = this.rundown.findIndex((e) => e.id === this.selectedEventId);
|
||||
|
||||
// Maybe is missing
|
||||
if (eventIndex === -1) {
|
||||
@@ -627,7 +627,7 @@ export class EventTimer extends Timer {
|
||||
*/
|
||||
updateSingleEvent(id, entry) {
|
||||
// find object in events
|
||||
const eventIndex = this._eventlist.findIndex((e) => e.id === id);
|
||||
const eventIndex = this.rundown.findIndex((e) => e.id === id);
|
||||
if (eventIndex === -1) {
|
||||
throw new Error('Event not found');
|
||||
}
|
||||
@@ -644,8 +644,8 @@ export class EventTimer extends Timer {
|
||||
}
|
||||
|
||||
// update event in memory
|
||||
const e = this._eventlist[eventIndex];
|
||||
this._eventlist[eventIndex] = { ...e, ...entry };
|
||||
const e = this.rundown[eventIndex];
|
||||
this.rundown[eventIndex] = { ...e, ...entry };
|
||||
|
||||
try {
|
||||
// check if entry is running
|
||||
@@ -685,18 +685,18 @@ export class EventTimer extends Timer {
|
||||
insertEventAfterId(event, previousId) {
|
||||
if (typeof previousId === 'undefined') {
|
||||
// Insert at beginning
|
||||
this._eventlist.unshift(event);
|
||||
this.rundown.unshift(event);
|
||||
} else {
|
||||
// find object in events
|
||||
const previousIndex = this._eventlist.findIndex((e) => e.id === previousId);
|
||||
const previousIndex = this.rundown.findIndex((e) => e.id === previousId);
|
||||
if (previousIndex === -1) {
|
||||
throw 'Event not found';
|
||||
}
|
||||
|
||||
if (previousIndex + 1 >= this._eventlist.length) {
|
||||
this._eventlist.push(event);
|
||||
if (previousIndex + 1 >= this.rundown.length) {
|
||||
this.rundown.push(event);
|
||||
} else {
|
||||
this._eventlist.splice(previousIndex + 1, 0, event);
|
||||
this.rundown.splice(previousIndex + 1, 0, event);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -737,11 +737,11 @@ export class EventTimer extends Timer {
|
||||
*/
|
||||
deleteId(eventId) {
|
||||
// find object in events
|
||||
const eventIndex = this._eventlist.findIndex((e) => e.id === eventId);
|
||||
const eventIndex = this.rundown.findIndex((e) => e.id === eventId);
|
||||
if (eventIndex === -1) return;
|
||||
|
||||
// delete event and update count
|
||||
this._eventlist.splice(eventIndex, 1);
|
||||
this.rundown.splice(eventIndex, 1);
|
||||
|
||||
// reload data if necessary
|
||||
if (eventId === this.selectedEventId) {
|
||||
@@ -750,7 +750,7 @@ export class EventTimer extends Timer {
|
||||
}
|
||||
|
||||
// update selected event index
|
||||
this.selectedEventIndex = this._eventlist.findIndex((e) => e.id === this.selectedEventId);
|
||||
this.selectedEventIndex = this.rundown.findIndex((e) => e.id === this.selectedEventId);
|
||||
|
||||
// reload titles if necessary
|
||||
if (eventId === this.nextEventId || eventId === this.nextPublicEventId) {
|
||||
@@ -771,7 +771,7 @@ export class EventTimer extends Timer {
|
||||
* @param {string} eventId - ID of event in eventlist
|
||||
*/
|
||||
loadEventById(eventId) {
|
||||
const eventIndex = this._eventlist.findIndex((e) => e.id === eventId);
|
||||
const eventIndex = this.rundown.findIndex((e) => e.id === eventId);
|
||||
|
||||
if (eventIndex === -1) return false;
|
||||
this.pause();
|
||||
@@ -786,7 +786,7 @@ export class EventTimer extends Timer {
|
||||
* @param {number} eventIndex - Index of event in eventlist
|
||||
*/
|
||||
loadEventByIndex(eventIndex) {
|
||||
if (eventIndex === -1 || eventIndex > this._eventlist.length) return false;
|
||||
if (eventIndex === -1 || eventIndex > this.rundown.length) return false;
|
||||
this.pause();
|
||||
this.loadEvent(eventIndex, 'load');
|
||||
// run cycle
|
||||
@@ -800,7 +800,7 @@ export class EventTimer extends Timer {
|
||||
* @param {string} [type='load'] - 'load' or 'reload', whether we are keeping running time
|
||||
*/
|
||||
loadEvent(eventIndex, type = 'load') {
|
||||
const e = this._eventlist?.[eventIndex];
|
||||
const e = this.rundown?.[eventIndex];
|
||||
if (e == null) return;
|
||||
|
||||
const start = e.timeStart == null || e.timeStart === '' ? 0 : e.timeStart;
|
||||
@@ -840,7 +840,7 @@ export class EventTimer extends Timer {
|
||||
* @private
|
||||
*/
|
||||
_loadTitlesNow() {
|
||||
const e = this._eventlist[this.selectedEventIndex];
|
||||
const e = this.rundown[this.selectedEventIndex];
|
||||
if (e == null) return;
|
||||
|
||||
// private title is always current
|
||||
@@ -861,8 +861,8 @@ export class EventTimer extends Timer {
|
||||
|
||||
// iterate backwards to find it
|
||||
for (let i = this.selectedEventIndex; i >= 0; i--) {
|
||||
if (this._eventlist[i].type === 'event' && this._eventlist[i].isPublic) {
|
||||
this._loadThisTitles(this._eventlist[i], 'now-public');
|
||||
if (this.rundown[i].type === 'event' && this.rundown[i].isPublic) {
|
||||
this._loadThisTitles(this.rundown[i], 'now-public');
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -963,7 +963,7 @@ export class EventTimer extends Timer {
|
||||
this.titlesPublic.presenterNext = null;
|
||||
this.nextPublicEventId = null;
|
||||
|
||||
const numEvents = this._eventlist.length;
|
||||
const numEvents = this.rundown.length;
|
||||
|
||||
if (this.selectedEventIndex < numEvents - 1) {
|
||||
let nextPublic = false;
|
||||
@@ -971,16 +971,16 @@ export class EventTimer extends Timer {
|
||||
|
||||
for (let i = this.selectedEventIndex + 1; i < numEvents; i++) {
|
||||
// check that is the right type
|
||||
if (this._eventlist[i].type === 'event') {
|
||||
if (this.rundown[i].type === 'event') {
|
||||
// if we have not set private
|
||||
if (!nextPrivate) {
|
||||
this._loadThisTitles(this._eventlist[i], 'next-private');
|
||||
this._loadThisTitles(this.rundown[i], 'next-private');
|
||||
nextPrivate = true;
|
||||
}
|
||||
|
||||
// if event is public
|
||||
if (this._eventlist[i].isPublic) {
|
||||
this._loadThisTitles(this._eventlist[i], 'next-public');
|
||||
if (this.rundown[i].isPublic) {
|
||||
this._loadThisTitles(this.rundown[i], 'next-public');
|
||||
nextPublic = true;
|
||||
}
|
||||
}
|
||||
@@ -1098,7 +1098,7 @@ export class EventTimer extends Timer {
|
||||
}
|
||||
|
||||
const { nowIndex, nowId, publicIndex, nextIndex, publicNextIndex, timers, timeToNext } =
|
||||
getSelectionByRoll(this._eventlist, now);
|
||||
getSelectionByRoll(this.rundown, now);
|
||||
|
||||
// nothing to play, unload
|
||||
if (nowIndex === null && nextIndex === null) {
|
||||
@@ -1139,25 +1139,25 @@ export class EventTimer extends Timer {
|
||||
|
||||
// timer counts to next event
|
||||
this.secondaryTimer = timeToNext;
|
||||
this._secondaryTarget = this._eventlist[nextIndex].timeStart;
|
||||
this._secondaryTarget = this.rundown[nextIndex].timeStart;
|
||||
}
|
||||
|
||||
// TITLES: Load next private
|
||||
this._loadThisTitles(this._eventlist[nextIndex], 'next-private');
|
||||
this._loadThisTitles(this.rundown[nextIndex], 'next-private');
|
||||
}
|
||||
|
||||
// TITLES: Load next public
|
||||
if (publicNextIndex !== null) {
|
||||
this._loadThisTitles(this._eventlist[publicNextIndex], 'next-public');
|
||||
this._loadThisTitles(this.rundown[publicNextIndex], 'next-public');
|
||||
}
|
||||
|
||||
// TITLES: Load now private
|
||||
if (nowIndex !== null) {
|
||||
this._loadThisTitles(this._eventlist[nowIndex], 'now-private');
|
||||
this._loadThisTitles(this.rundown[nowIndex], 'now-private');
|
||||
}
|
||||
// TITLES: Load now public
|
||||
if (publicIndex !== null) {
|
||||
this._loadThisTitles(this._eventlist[publicIndex], 'now-public');
|
||||
this._loadThisTitles(this.rundown[publicIndex], 'now-public');
|
||||
}
|
||||
|
||||
if (prevLoaded !== this.selectedEventId) {
|
||||
@@ -1172,7 +1172,7 @@ export class EventTimer extends Timer {
|
||||
// do we need to change
|
||||
if (this.state === 'roll') return;
|
||||
|
||||
if (!this._eventlist.length) return;
|
||||
if (!this.rundown.length) return;
|
||||
|
||||
// set state
|
||||
this.state = 'roll';
|
||||
@@ -1186,7 +1186,7 @@ export class EventTimer extends Timer {
|
||||
|
||||
previous() {
|
||||
// check that we have events to run
|
||||
if (!this._eventlist.length) return;
|
||||
if (!this.rundown.length) return;
|
||||
|
||||
// maybe this is the first event?
|
||||
if (this.selectedEventIndex === 0) return;
|
||||
@@ -1208,7 +1208,7 @@ export class EventTimer extends Timer {
|
||||
}
|
||||
|
||||
next() {
|
||||
const numEvents = this._eventlist.length;
|
||||
const numEvents = this.rundown.length;
|
||||
// check that we have events to run
|
||||
if (!numEvents) return;
|
||||
|
||||
@@ -1250,7 +1250,7 @@ export class EventTimer extends Timer {
|
||||
* @description reloads current event
|
||||
*/
|
||||
reload() {
|
||||
if (!this._eventlist.length) return;
|
||||
if (!this.rundown.length) return;
|
||||
|
||||
// change playstate
|
||||
this.pause();
|
||||
|
||||
@@ -60,8 +60,8 @@ test('object instantiates correctly', async () => {
|
||||
expect(t.nextEventId).toBeNull();
|
||||
expect(t.selectedPublicEventId).toBeNull();
|
||||
expect(t.nextPublicEventId).toBeNull();
|
||||
expect(t._eventlist.length).toBe(0);
|
||||
expect(t._eventlist).toStrictEqual([]);
|
||||
expect(t.rundown.length).toBe(0);
|
||||
expect(t.rundown).toStrictEqual([]);
|
||||
expect(t.onAir).toBeFalsy();
|
||||
|
||||
t.shutdown();
|
||||
@@ -77,7 +77,7 @@ describe('test triggers behaviour', () => {
|
||||
});
|
||||
|
||||
test('does not allow triggering events with an empty list', (done) => {
|
||||
expect(t._eventlist.length).toBe(0);
|
||||
expect(t.rundown.length).toBe(0);
|
||||
|
||||
expect(t.trigger('start')).toBeFalsy();
|
||||
expect(t.trigger('pause')).toBeFalsy();
|
||||
@@ -90,7 +90,7 @@ describe('test triggers behaviour', () => {
|
||||
});
|
||||
|
||||
test('...and is consistent by calling the class methods', (done) => {
|
||||
expect(t._eventlist.length).toBe(0);
|
||||
expect(t.rundown.length).toBe(0);
|
||||
expect(t.state).toBe('stop');
|
||||
|
||||
t.start();
|
||||
|
||||
@@ -24,7 +24,7 @@ export const poll = async (req, res) => {
|
||||
// Returns -
|
||||
export const dbDownload = async (req, res) => {
|
||||
const { title } = DataProvider.getEventData();
|
||||
const fileTitle = title || 'ontime events';
|
||||
const fileTitle = title || 'ontime data';
|
||||
const dbInDisk = resolveDbPath();
|
||||
|
||||
res.download(dbInDisk, `${fileTitle}.json`, (err) => {
|
||||
@@ -50,13 +50,13 @@ const uploadAndParse = async (file, req, res, options) => {
|
||||
} else if (result.message === 'success') {
|
||||
// explicitly write objects
|
||||
if (typeof result !== 'undefined') {
|
||||
const newEvents = result.data.events || [];
|
||||
if (options?.onlyEvents === 'true') {
|
||||
await DataProvider.setEvents(newEvents);
|
||||
const newRundown = result.data.rundown || [];
|
||||
if (options?.onlyRundown === 'true') {
|
||||
await DataProvider.setRundown(newRundown);
|
||||
} else {
|
||||
await DataProvider.mergeIntoData(result.data);
|
||||
}
|
||||
global.timer.setupWithEventList(newEvents.filter((entry) => entry.type === 'event'));
|
||||
global.timer.setupWithEventList(newRundown.filter((entry) => entry.type === 'event'));
|
||||
}
|
||||
res.sendStatus(200);
|
||||
} else {
|
||||
|
||||
+37
-45
@@ -24,8 +24,8 @@ async function _insertAndSync(newEvent) {
|
||||
delete newEvent.after;
|
||||
await DataProvider.insertEventAfterId(newEvent, afterId);
|
||||
if (newEvent.type === 'event') {
|
||||
const events = DataProvider.getEvents();
|
||||
const { id } = getPreviousPlayable(events, newEvent.id);
|
||||
const rundown = DataProvider.getRundown();
|
||||
const { id } = getPreviousPlayable(rundown, newEvent.id);
|
||||
_insertEventInTimerAfterId(newEvent, id);
|
||||
}
|
||||
}
|
||||
@@ -37,8 +37,8 @@ async function _insertAndSync(newEvent) {
|
||||
*/
|
||||
function getEventEvents() {
|
||||
// return data.events.filter((e) => e.type === 'event');
|
||||
const events = DataProvider.getEvents();
|
||||
return Array.from(events).filter((e) => e.type === 'event');
|
||||
const rundown = DataProvider.getRundown();
|
||||
return Array.from(rundown).filter((e) => e.type === 'event');
|
||||
}
|
||||
|
||||
// Updates timer object
|
||||
@@ -76,15 +76,15 @@ function _deleteTimerId(entryId) {
|
||||
global.timer.deleteId(entryId);
|
||||
}
|
||||
|
||||
// Create controller for GET request to '/events'
|
||||
// Create controller for GET request to '/eventlist'
|
||||
// Returns -
|
||||
export const eventsGetAll = async (req, res) => {
|
||||
res.json(DataProvider.getEvents());
|
||||
export const rundownGetAll = async (req, res) => {
|
||||
res.json(DataProvider.getRundown());
|
||||
};
|
||||
|
||||
// Create controller for GET request to '/events/:eventId'
|
||||
// Create controller for GET request to '/eventlist/:eventId'
|
||||
// Returns -
|
||||
export const eventsGetById = async (req, res) => {
|
||||
export const getEventById = async (req, res) => {
|
||||
const id = req.params?.eventId;
|
||||
|
||||
if (id == null) {
|
||||
@@ -95,9 +95,9 @@ export const eventsGetById = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/events/'
|
||||
// Create controller for POST request to '/eventlist/'
|
||||
// Returns -
|
||||
export const eventsPost = async (req, res) => {
|
||||
export const rundownPost = async (req, res) => {
|
||||
if (failEmptyObjects(req.body, res)) {
|
||||
return;
|
||||
}
|
||||
@@ -137,9 +137,9 @@ export const eventsPost = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Create controller for PUT request to '/events/'
|
||||
// Create controller for PUT request to '/eventlist/'
|
||||
// Returns -
|
||||
export const eventsPut = async (req, res) => {
|
||||
export const rundownPut = async (req, res) => {
|
||||
if (failEmptyObjects(req.body, res)) {
|
||||
return;
|
||||
}
|
||||
@@ -163,8 +163,8 @@ export const eventsPut = async (req, res) => {
|
||||
} else {
|
||||
if (eventInMemory.skip) {
|
||||
// if it was skipped before we add it to the timer
|
||||
const events = DataProvider.getEvents();
|
||||
const { id } = getPreviousPlayable(events, patchedObject.id);
|
||||
const rundown = DataProvider.getRundown();
|
||||
const { id } = getPreviousPlayable(rundown, patchedObject.id);
|
||||
_insertEventInTimerAfterId(patchedObject, id);
|
||||
} else {
|
||||
// otherwise update as normal
|
||||
@@ -178,24 +178,16 @@ export const eventsPut = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Create controller for PATCH request to '/events/'
|
||||
// Returns -
|
||||
// DEPRECATED
|
||||
export const eventsPatch = async (req, res) => {
|
||||
// Code is the same as put, call that
|
||||
await eventsPut(req, res);
|
||||
};
|
||||
|
||||
export const eventsReorder = async (req, res) => {
|
||||
export const rundownReorder = async (req, res) => {
|
||||
if (failEmptyObjects(req.body, res)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { index, from, to } = req.body;
|
||||
|
||||
// get events
|
||||
const events = DataProvider.getEvents();
|
||||
const idx = events.findIndex((e) => e.id === index, from);
|
||||
// get rundown
|
||||
const rundown = DataProvider.getRundown();
|
||||
const idx = rundown.findIndex((e) => e.id === index, from);
|
||||
|
||||
// Check if item is at given index
|
||||
if (idx !== from) {
|
||||
@@ -205,13 +197,13 @@ export const eventsReorder = async (req, res) => {
|
||||
|
||||
try {
|
||||
// remove item at from
|
||||
const [reorderedItem] = events.splice(from, 1);
|
||||
const [reorderedItem] = rundown.splice(from, 1);
|
||||
|
||||
// reinsert item at to
|
||||
events.splice(to, 0, reorderedItem);
|
||||
rundown.splice(to, 0, reorderedItem);
|
||||
|
||||
// save events
|
||||
await DataProvider.setEventData(events);
|
||||
// save rundown
|
||||
await DataProvider.setEventData(rundown);
|
||||
|
||||
// update timer
|
||||
_updateTimers();
|
||||
@@ -222,19 +214,19 @@ export const eventsReorder = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Create controller for PATCH request to '/events/applydelay/:eventId'
|
||||
// Create controller for PATCH request to '/eventlist/applydelay/:eventId'
|
||||
// Returns -
|
||||
export const eventsApplyDelay = async (req, res) => {
|
||||
export const rundownApplyDelay = async (req, res) => {
|
||||
try {
|
||||
// get events
|
||||
const events = DataProvider.getEvents();
|
||||
// get rundown
|
||||
const rundown = DataProvider.getRundown();
|
||||
|
||||
// AUX
|
||||
let delayIndex = null;
|
||||
let blockIndex = null;
|
||||
let delayValue = 0;
|
||||
|
||||
for (const [index, e] of events.entries()) {
|
||||
for (const [index, e] of rundown.entries()) {
|
||||
if (delayIndex == null) {
|
||||
// look for delay
|
||||
if (e.id === req.params.eventId && e.type === 'delay') {
|
||||
@@ -261,14 +253,14 @@ export const eventsApplyDelay = async (req, res) => {
|
||||
}
|
||||
|
||||
// delete delay
|
||||
events.splice(delayIndex, 1);
|
||||
rundown.splice(delayIndex, 1);
|
||||
|
||||
// delete block
|
||||
// index would have moved down since we deleted delay
|
||||
if (blockIndex) events.splice(blockIndex - 1, 1);
|
||||
if (blockIndex) rundown.splice(blockIndex - 1, 1);
|
||||
|
||||
// update events
|
||||
await DataProvider.setEvents(events);
|
||||
// update rundown
|
||||
await DataProvider.setRundown(rundown);
|
||||
|
||||
// update timer
|
||||
_updateTimers();
|
||||
@@ -279,9 +271,9 @@ export const eventsApplyDelay = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Create controller for DELETE request to '/events/:eventId'
|
||||
// Create controller for DELETE request to '/eventlist/:eventId'
|
||||
// Returns -
|
||||
export const eventsDelete = async (req, res) => {
|
||||
export const deleteEventById = async (req, res) => {
|
||||
try {
|
||||
const eventId = req.params.eventId;
|
||||
|
||||
@@ -296,11 +288,11 @@ export const eventsDelete = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Create controller for DELETE request to '/events/:eventId'
|
||||
// Create controller for DELETE request to '/eventlist/:eventId'
|
||||
// Returns -
|
||||
export const eventsDeleteAll = async (req, res) => {
|
||||
export const rundownDelete = async (req, res) => {
|
||||
try {
|
||||
await DataProvider.deleteAllEvents();
|
||||
await DataProvider.clearRundown();
|
||||
global.timer.clearEventList();
|
||||
res.sendStatus(204);
|
||||
} catch (error) {
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { body, param, validationResult } from 'express-validator';
|
||||
|
||||
export const eventsPostValidator = [
|
||||
export const rundownPostValidator = [
|
||||
body('type').isString().exists().isIn(['event', 'delay', 'block']),
|
||||
(req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
@@ -9,7 +9,7 @@ export const eventsPostValidator = [
|
||||
},
|
||||
];
|
||||
|
||||
export const eventsPutValidator = [
|
||||
export const rundownPutValidator = [
|
||||
body('id').isString().exists(),
|
||||
(req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
@@ -1,5 +1,5 @@
|
||||
export const dbModelv1 = {
|
||||
events: [],
|
||||
export const dbModel = {
|
||||
rundown: [],
|
||||
event: {
|
||||
title: '',
|
||||
url: '',
|
||||
@@ -9,7 +9,7 @@ export const dbModelv1 = {
|
||||
},
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
version: 2,
|
||||
serverPort: 4001,
|
||||
lock: null,
|
||||
pinCode: null,
|
||||
|
||||
@@ -4,8 +4,8 @@ import { copyFileSync, existsSync } from 'fs';
|
||||
import { ensureDirectory, getAppDataPath } from '../utils/fileManagement.js';
|
||||
import { config } from '../config/config.js';
|
||||
import { validateFile } from '../utils/parserUtils.js';
|
||||
import { dbModelv1 as dbModel } from '../models/dataModel.js';
|
||||
import { parseJson_v1 as parseJson } from '../utils/parser.js';
|
||||
import { dbModel as dbModel } from '../models/dataModel.js';
|
||||
import { parseJson as parseJson } from '../utils/parser.js';
|
||||
|
||||
/**
|
||||
* @description Decides which path the database is in
|
||||
|
||||
+6
-6
@@ -30,15 +30,15 @@ const eventFromDb = {
|
||||
};
|
||||
|
||||
describe('When a POST request is sent', () => {
|
||||
test('POST /event should return a 201', async () => {
|
||||
await supertest(server).post('/events').send(testEvent).expect(201);
|
||||
test('POST /eventlist should return a 201', async () => {
|
||||
await supertest(server).post('/eventlist').send(testEvent).expect(201);
|
||||
});
|
||||
});
|
||||
|
||||
describe('When a GET request request is sent', () => {
|
||||
test('GET /events returns a valid object', async () => {
|
||||
test('GET /eventlist returns a valid object', async () => {
|
||||
await supertest(server)
|
||||
.get('/events')
|
||||
.get('/eventlist')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
@@ -46,9 +46,9 @@ describe('When a GET request request is sent', () => {
|
||||
expect(typeof response.body).toBe('object');
|
||||
});
|
||||
});
|
||||
test('GET /events/:eventId returns a valid object', async () => {
|
||||
test('GET /eventlist/:eventId returns a valid object', async () => {
|
||||
await supertest(server)
|
||||
.get(`/events/${eventFromDb.id}`)
|
||||
.get(`/eventlist/${eventFromDb.id}`)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
@@ -1,48 +0,0 @@
|
||||
import express from 'express';
|
||||
// import events controller
|
||||
import {
|
||||
eventsApplyDelay,
|
||||
eventsDelete,
|
||||
eventsDeleteAll,
|
||||
eventsGetAll,
|
||||
eventsGetById,
|
||||
eventsPatch,
|
||||
eventsPost,
|
||||
eventsPut,
|
||||
eventsReorder,
|
||||
} from '../controllers/eventsController.js';
|
||||
import {
|
||||
eventsPostValidator,
|
||||
eventsPutValidator,
|
||||
paramsMustHaveEventId,
|
||||
} from '../controllers/eventsController.validate.js';
|
||||
|
||||
export const router = express.Router();
|
||||
|
||||
// create route between controller and '/events/' endpoint
|
||||
router.get('/', eventsGetAll);
|
||||
|
||||
// create route between controller and '/events/:eventId' endpoint
|
||||
router.get('/:eventId', eventsGetById);
|
||||
|
||||
// create route between controller and '/events/' endpoint
|
||||
router.post('/', eventsPostValidator, eventsPost);
|
||||
|
||||
// create route between controller and '/events/' endpoint
|
||||
router.put('/', eventsPutValidator, eventsPut);
|
||||
|
||||
// create route between controller and '/events/' endpoint
|
||||
// DEPRECATED
|
||||
router.patch('/', eventsPatch);
|
||||
|
||||
// create route between controller and '/events/reorder' endpoint
|
||||
router.patch('/reorder/', eventsReorder);
|
||||
|
||||
// create route between controller and '/events/applydelay/:eventId' endpoint
|
||||
router.patch('/applydelay/:eventId', paramsMustHaveEventId, eventsApplyDelay);
|
||||
|
||||
// create route between controller and '/events/all' endpoint
|
||||
router.delete('/all', eventsDeleteAll);
|
||||
|
||||
// create route between controller and '/events/:eventId' endpoint
|
||||
router.delete('/:eventId', paramsMustHaveEventId, eventsDelete);
|
||||
@@ -0,0 +1,42 @@
|
||||
import express from 'express';
|
||||
import {
|
||||
deleteEventById,
|
||||
getEventById,
|
||||
rundownApplyDelay,
|
||||
rundownDelete,
|
||||
rundownGetAll,
|
||||
rundownPost,
|
||||
rundownPut,
|
||||
rundownReorder,
|
||||
} from '../controllers/rundownController.js';
|
||||
import {
|
||||
paramsMustHaveEventId,
|
||||
rundownPostValidator,
|
||||
rundownPutValidator,
|
||||
} from '../controllers/rundownController.validate.js';
|
||||
|
||||
export const router = express.Router();
|
||||
|
||||
// create route between controller and '/eventlist/' endpoint
|
||||
router.get('/', rundownGetAll);
|
||||
|
||||
// create route between controller and '/eventlist/:eventId' endpoint
|
||||
router.get('/:eventId', getEventById);
|
||||
|
||||
// create route between controller and '/eventlist/' endpoint
|
||||
router.post('/', rundownPostValidator, rundownPost);
|
||||
|
||||
// create route between controller and '/eventlist/' endpoint
|
||||
router.put('/', rundownPutValidator, rundownPut);
|
||||
|
||||
// create route between controller and '/eventlist/reorder' endpoint
|
||||
router.patch('/reorder/', rundownReorder);
|
||||
|
||||
// create route between controller and '/eventlist/applydelay/:eventId' endpoint
|
||||
router.patch('/applydelay/:eventId', paramsMustHaveEventId, rundownApplyDelay);
|
||||
|
||||
// create route between controller and '/eventlist/all' endpoint
|
||||
router.delete('/all', rundownDelete);
|
||||
|
||||
// create route between controller and '/eventlist/:eventId' endpoint
|
||||
router.delete('/:eventId', paramsMustHaveEventId, deleteEventById);
|
||||
@@ -1,12 +1,22 @@
|
||||
import jest from 'jest-mock';
|
||||
import { dbModelv1, dbModelv1 as dbModel } from '../../models/dataModel.js';
|
||||
import { isStringEmpty, parseExcel_v1, parseJson_v1, validateEvent_v1 } from '../parser.js';
|
||||
import { dbModel } from '../../models/dataModel.js';
|
||||
import { isStringEmpty, parseExcel, parseJson, validateEvent } from '../parser.js';
|
||||
import { makeString, validateDuration } from '../parserUtils.js';
|
||||
import { parseAliases_v1, parseUserFields_v1, parseViews_v1 } from '../parserUtils_v1.js';
|
||||
import { parseAliases, parseUserFields, parseViews } from '../parserFunctions.js';
|
||||
|
||||
describe('refuses import of old / unknown versions', () => {
|
||||
test('a v1 file', () => {
|
||||
const testFile = {
|
||||
settings: {
|
||||
version: 1,
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
describe('test json parser with valid def', () => {
|
||||
const testData = {
|
||||
events: [
|
||||
rundown: [
|
||||
{
|
||||
title: 'Guest Welcoming',
|
||||
subtitle: '',
|
||||
@@ -183,7 +193,7 @@ describe('test json parser with valid def', () => {
|
||||
},
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
version: 2,
|
||||
timeFormat: '24',
|
||||
},
|
||||
};
|
||||
@@ -191,16 +201,16 @@ describe('test json parser with valid def', () => {
|
||||
let parseResponse;
|
||||
|
||||
beforeEach(async () => {
|
||||
parseResponse = await parseJson_v1(testData);
|
||||
parseResponse = await parseJson(testData);
|
||||
});
|
||||
|
||||
it('has 7 events', () => {
|
||||
const length = parseResponse?.events.length;
|
||||
const length = parseResponse?.rundown.length;
|
||||
expect(length).toBe(7);
|
||||
});
|
||||
|
||||
it('first event is as a match', () => {
|
||||
const first = parseResponse?.events[0];
|
||||
const first = parseResponse?.rundown[0];
|
||||
const expected = {
|
||||
title: 'Guest Welcoming',
|
||||
subtitle: '',
|
||||
@@ -231,7 +241,7 @@ describe('test json parser with valid def', () => {
|
||||
});
|
||||
|
||||
it('second event is as a match', () => {
|
||||
const second = parseResponse?.events[1];
|
||||
const second = parseResponse?.rundown[1];
|
||||
const expected = {
|
||||
title: 'Good Morning',
|
||||
subtitle: 'Days schedule',
|
||||
@@ -275,7 +285,7 @@ describe('test json parser with valid def', () => {
|
||||
it('settings are for right app and version', () => {
|
||||
const settings = parseResponse?.settings;
|
||||
expect(settings.app).toBe('ontime');
|
||||
expect(settings.version).toBe(1);
|
||||
expect(settings.version).toBe(2);
|
||||
});
|
||||
|
||||
it('missing settings', () => {
|
||||
@@ -287,7 +297,7 @@ describe('test json parser with valid def', () => {
|
||||
describe('test parser edge cases', () => {
|
||||
it('generates missing ids', async () => {
|
||||
const testData = {
|
||||
events: [
|
||||
rundown: [
|
||||
{
|
||||
title: 'Test Event',
|
||||
type: 'event',
|
||||
@@ -295,14 +305,14 @@ describe('test parser edge cases', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const parseResponse = await parseJson_v1(testData);
|
||||
expect(parseResponse.events[0].id).toBeDefined();
|
||||
const parseResponse = await parseJson(testData);
|
||||
expect(parseResponse.rundown[0].id).toBeDefined();
|
||||
});
|
||||
|
||||
it('detects duplicate Ids', async () => {
|
||||
console.log = jest.fn();
|
||||
const testData = {
|
||||
events: [
|
||||
rundown: [
|
||||
{
|
||||
title: 'Test Event 1',
|
||||
type: 'event',
|
||||
@@ -316,15 +326,15 @@ describe('test parser edge cases', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const parseResponse = await parseJson_v1(testData);
|
||||
const parseResponse = await parseJson(testData);
|
||||
expect(console.log).toHaveBeenCalledWith('ERROR: ID collision on import, skipping');
|
||||
expect(parseResponse?.events.length).toBe(1);
|
||||
expect(parseResponse?.rundown.length).toBe(1);
|
||||
});
|
||||
|
||||
it('handles incomplete datasets', async () => {
|
||||
console.log = jest.fn();
|
||||
const testData = {
|
||||
events: [
|
||||
rundown: [
|
||||
{
|
||||
title: 'Test Event 1',
|
||||
id: '1',
|
||||
@@ -336,9 +346,9 @@ describe('test parser edge cases', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const parseResponse = await parseJson_v1(testData);
|
||||
const parseResponse = await parseJson(testData);
|
||||
expect(console.log).toHaveBeenCalledWith('ERROR: undefined event type, skipping');
|
||||
expect(parseResponse?.events.length).toBe(0);
|
||||
expect(parseResponse?.rundown.length).toBe(0);
|
||||
});
|
||||
|
||||
it('skips unknown app and version settings', async () => {
|
||||
@@ -349,7 +359,7 @@ describe('test parser edge cases', () => {
|
||||
},
|
||||
};
|
||||
|
||||
await parseJson_v1(testData);
|
||||
await parseJson(testData);
|
||||
expect(console.log).toHaveBeenCalledWith('ERROR: unknown app version, skipping');
|
||||
});
|
||||
});
|
||||
@@ -357,7 +367,7 @@ describe('test parser edge cases', () => {
|
||||
describe('test corrupt data', () => {
|
||||
it('handles some empty events', async () => {
|
||||
const emptyEvents = {
|
||||
events: [
|
||||
rundown: [
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
@@ -384,20 +394,20 @@ describe('test corrupt data', () => {
|
||||
},
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
version: 2,
|
||||
serverPort: 4001,
|
||||
lock: null,
|
||||
timeFormat: '24',
|
||||
},
|
||||
};
|
||||
|
||||
const parsedDef = await parseJson_v1(emptyEvents);
|
||||
expect(parsedDef.events.length).toBe(2);
|
||||
const parsedDef = await parseJson(emptyEvents);
|
||||
expect(parsedDef.rundown.length).toBe(2);
|
||||
});
|
||||
|
||||
it('handles all empty events', async () => {
|
||||
const emptyEvents = {
|
||||
events: [{}, {}, {}, {}, {}, {}, {}, {}],
|
||||
rundown: [{}, {}, {}, {}, {}, {}, {}, {}],
|
||||
event: {
|
||||
title: 'All about Carlos demo event',
|
||||
url: 'www.carlosvalente.com',
|
||||
@@ -407,52 +417,52 @@ describe('test corrupt data', () => {
|
||||
},
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
version: 2,
|
||||
serverPort: 4001,
|
||||
lock: null,
|
||||
timeFormat: '24',
|
||||
},
|
||||
};
|
||||
|
||||
const parsedDef = await parseJson_v1(emptyEvents);
|
||||
expect(parsedDef.events.length).toBe(0);
|
||||
const parsedDef = await parseJson(emptyEvents);
|
||||
expect(parsedDef.rundown.length).toBe(0);
|
||||
});
|
||||
|
||||
it('handles missing event data', async () => {
|
||||
const emptyEventData = {
|
||||
events: [{}, {}, {}, {}, {}, {}, {}, {}],
|
||||
rundown: [{}, {}, {}, {}, {}, {}, {}, {}],
|
||||
event: {},
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
version: 2,
|
||||
serverPort: 4001,
|
||||
lock: null,
|
||||
timeFormat: '24',
|
||||
},
|
||||
};
|
||||
|
||||
const parsedDef = await parseJson_v1(emptyEventData);
|
||||
const parsedDef = await parseJson(emptyEventData);
|
||||
expect(parsedDef.event).toStrictEqual(dbModel.event);
|
||||
});
|
||||
|
||||
it('handles missing settings', async () => {
|
||||
const missingSettings = {
|
||||
events: [{}, {}, {}, {}, {}, {}, {}, {}],
|
||||
rundown: [{}, {}, {}, {}, {}, {}, {}, {}],
|
||||
event: {},
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
version: 2,
|
||||
},
|
||||
};
|
||||
|
||||
const parsedDef = await parseJson_v1(missingSettings);
|
||||
const parsedDef = await parseJson(missingSettings);
|
||||
expect(parsedDef.settings).toStrictEqual(dbModel.settings);
|
||||
});
|
||||
|
||||
it('fails with invalid JSON', async () => {
|
||||
console.log = jest.fn();
|
||||
const invalidJSON = 'some random dataset';
|
||||
const parsedDef = await parseJson_v1(invalidJSON);
|
||||
const parsedDef = await parseJson(invalidJSON);
|
||||
expect(console.log).toHaveBeenCalledWith('ERROR: Invalid JSON format');
|
||||
expect(parsedDef).toBe(-1);
|
||||
});
|
||||
@@ -463,7 +473,7 @@ describe('test event validator', () => {
|
||||
const event = {
|
||||
title: 'test',
|
||||
};
|
||||
const validated = validateEvent_v1(event);
|
||||
const validated = validateEvent(event);
|
||||
|
||||
expect(validated).toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -489,13 +499,13 @@ describe('test event validator', () => {
|
||||
user7: expect.any(String),
|
||||
user8: expect.any(String),
|
||||
user9: expect.any(String),
|
||||
})
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('fails an empty object', () => {
|
||||
const event = {};
|
||||
const validated = validateEvent_v1(event);
|
||||
const validated = validateEvent(event);
|
||||
expect(validated).toEqual(null);
|
||||
});
|
||||
|
||||
@@ -506,7 +516,7 @@ describe('test event validator', () => {
|
||||
presenter: 3.2,
|
||||
note: '1899-12-30T08:00:10.000Z',
|
||||
};
|
||||
const validated = validateEvent_v1(event);
|
||||
const validated = validateEvent(event);
|
||||
expect(typeof validated.title).toEqual('string');
|
||||
expect(typeof validated.subtitle).toEqual('string');
|
||||
expect(typeof validated.presenter).toEqual('string');
|
||||
@@ -518,7 +528,7 @@ describe('test event validator', () => {
|
||||
timeStart: false,
|
||||
timeEnd: '2',
|
||||
};
|
||||
const validated = validateEvent_v1(event);
|
||||
const validated = validateEvent(event);
|
||||
expect(typeof validated.timeStart).toEqual('number');
|
||||
expect(validated.timeStart).toEqual(0);
|
||||
expect(typeof validated.timeEnd).toEqual('number');
|
||||
@@ -529,7 +539,7 @@ describe('test event validator', () => {
|
||||
const event = {
|
||||
title: {},
|
||||
};
|
||||
const validated = validateEvent_v1(event);
|
||||
const validated = validateEvent(event);
|
||||
expect(typeof validated.title).toEqual('string');
|
||||
});
|
||||
});
|
||||
@@ -651,7 +661,7 @@ describe('test parseExcel function', () => {
|
||||
endMessage: 'test end message',
|
||||
};
|
||||
|
||||
const expectedParsedEvents = [
|
||||
const expectedParsedRundown = [
|
||||
{
|
||||
timeStart: 25200000,
|
||||
timeEnd: 28810000,
|
||||
@@ -690,27 +700,27 @@ describe('test parseExcel function', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const parsedData = await parseExcel_v1(testdata);
|
||||
const parsedData = await parseExcel(testdata);
|
||||
|
||||
expect(parsedData.event).toStrictEqual(expectedParsedEvent);
|
||||
expect(parsedData.events).toBeDefined();
|
||||
expect(parsedData.events.title).toBe(expectedParsedEvents.title);
|
||||
expect(parsedData.events.presenter).toBe(expectedParsedEvents.presenter);
|
||||
expect(parsedData.events.subtitle).toBe(expectedParsedEvents.subtitle);
|
||||
expect(parsedData.events.isPublic).toBe(expectedParsedEvents.isPublic);
|
||||
expect(parsedData.events.skip).toBe(expectedParsedEvents.skip);
|
||||
expect(parsedData.events.note).toBe(expectedParsedEvents.note);
|
||||
expect(parsedData.events.type).toBe(expectedParsedEvents.type);
|
||||
expect(parsedData.rundown).toBeDefined();
|
||||
expect(parsedData.rundown.title).toBe(expectedParsedRundown.title);
|
||||
expect(parsedData.rundown.presenter).toBe(expectedParsedRundown.presenter);
|
||||
expect(parsedData.rundown.subtitle).toBe(expectedParsedRundown.subtitle);
|
||||
expect(parsedData.rundown.isPublic).toBe(expectedParsedRundown.isPublic);
|
||||
expect(parsedData.rundown.skip).toBe(expectedParsedRundown.skip);
|
||||
expect(parsedData.rundown.note).toBe(expectedParsedRundown.note);
|
||||
expect(parsedData.rundown.type).toBe(expectedParsedRundown.type);
|
||||
});
|
||||
});
|
||||
|
||||
describe('test aliases import', () => {
|
||||
it('imports a well defined alias', () => {
|
||||
const testData = {
|
||||
events: [],
|
||||
rundown: [],
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
version: 2,
|
||||
},
|
||||
aliases: [
|
||||
{
|
||||
@@ -721,7 +731,7 @@ describe('test aliases import', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const parsed = parseAliases_v1(testData);
|
||||
const parsed = parseAliases(testData);
|
||||
expect(parsed.length).toBe(1);
|
||||
|
||||
// generates missing id
|
||||
@@ -730,7 +740,7 @@ describe('test aliases import', () => {
|
||||
});
|
||||
|
||||
describe('test userFields import', () => {
|
||||
const model = dbModelv1.userFields;
|
||||
const model = dbModel.userFields;
|
||||
it('imports a fully defined user fields', () => {
|
||||
const testUserFields = {
|
||||
user0: 'test0',
|
||||
@@ -746,15 +756,15 @@ describe('test userFields import', () => {
|
||||
};
|
||||
|
||||
const testData = {
|
||||
events: [],
|
||||
rundown: [],
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
version: 2,
|
||||
},
|
||||
userFields: testUserFields,
|
||||
};
|
||||
|
||||
const parsed = parseUserFields_v1(testData);
|
||||
const parsed = parseUserFields(testData);
|
||||
expect(parsed).toStrictEqual(testUserFields);
|
||||
});
|
||||
|
||||
@@ -773,38 +783,38 @@ describe('test userFields import', () => {
|
||||
};
|
||||
|
||||
const testData = {
|
||||
events: [],
|
||||
rundown: [],
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
version: 2,
|
||||
},
|
||||
userFields: testUserFields,
|
||||
};
|
||||
|
||||
const parsed = parseUserFields_v1(testData);
|
||||
const parsed = parseUserFields(testData);
|
||||
expect(parsed).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
it('handles missing user fields', () => {
|
||||
const testData = {
|
||||
events: [],
|
||||
rundown: [],
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
version: 2,
|
||||
},
|
||||
};
|
||||
|
||||
const parsed = parseUserFields_v1(testData);
|
||||
const parsed = parseUserFields(testData);
|
||||
expect(parsed).toStrictEqual(model);
|
||||
expect(parsed).toStrictEqual(model);
|
||||
});
|
||||
|
||||
it('ignores badly defined fields', () => {
|
||||
const testData = {
|
||||
events: [],
|
||||
rundown: [],
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
version: 2,
|
||||
},
|
||||
userFields: {
|
||||
notThis: 'this shouldng be accepted',
|
||||
@@ -812,7 +822,7 @@ describe('test userFields import', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const parsed = parseUserFields_v1(testData);
|
||||
const parsed = parseUserFields(testData);
|
||||
expect(parsed).toStrictEqual(model);
|
||||
});
|
||||
});
|
||||
@@ -820,29 +830,29 @@ describe('test userFields import', () => {
|
||||
describe('test views import', () => {
|
||||
it('imports data from file', () => {
|
||||
const testData = {
|
||||
events: [],
|
||||
rundown: [],
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
version: 2,
|
||||
},
|
||||
views: {
|
||||
overrideStyles: true,
|
||||
},
|
||||
};
|
||||
const parsed = parseViews_v1(testData);
|
||||
const parsed = parseViews(testData);
|
||||
expect(parsed).toStrictEqual(testData.views);
|
||||
});
|
||||
|
||||
it('imports defaults to model', () => {
|
||||
const testData = {
|
||||
events: [],
|
||||
rundown: [],
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
version: 2,
|
||||
},
|
||||
};
|
||||
const parsed = parseViews_v1(testData, true);
|
||||
expect(parsed).toStrictEqual(dbModelv1.views);
|
||||
const parsed = parseViews(testData, true);
|
||||
expect(parsed).toStrictEqual(dbModel.views);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+31
-31
@@ -1,18 +1,18 @@
|
||||
import fs from 'fs';
|
||||
import xlsx from 'node-xlsx';
|
||||
import { event as eventDef } from '../models/eventsDefinition.js';
|
||||
import { dbModelv1 } from '../models/dataModel.js';
|
||||
import { dbModel } from '../models/dataModel.js';
|
||||
import { deleteFile, makeString, validateDuration } from './parserUtils.js';
|
||||
import {
|
||||
parseAliases_v1,
|
||||
parseEvent_v1,
|
||||
parseEvents_v1,
|
||||
parseHttp_v1,
|
||||
parseOsc_v1,
|
||||
parseSettings_v1,
|
||||
parseUserFields_v1,
|
||||
parseViews_v1,
|
||||
} from './parserUtils_v1.js';
|
||||
parseAliases,
|
||||
parseEvent,
|
||||
parseHttp,
|
||||
parseOsc,
|
||||
parseRundown,
|
||||
parseSettings,
|
||||
parseUserFields,
|
||||
parseViews,
|
||||
} from './parserFunctions.js';
|
||||
import { parseExcelDate } from './time.js';
|
||||
import { generateId } from './generate_id.js';
|
||||
|
||||
@@ -37,13 +37,13 @@ export const isStringEmpty = (value) => {
|
||||
* @param {array} excelData - array with excel sheet
|
||||
* @returns {object} - parsed object
|
||||
*/
|
||||
export const parseExcel_v1 = async (excelData) => {
|
||||
export const parseExcel = async (excelData) => {
|
||||
const eventData = {
|
||||
title: '',
|
||||
url: '',
|
||||
};
|
||||
const customUserFields = {};
|
||||
const events = [];
|
||||
const rundown = [];
|
||||
let timeStartIndex = null;
|
||||
let timeEndIndex = null;
|
||||
let titleIndex = null;
|
||||
@@ -238,17 +238,17 @@ export const parseExcel_v1 = async (excelData) => {
|
||||
if (Object.keys(event).length > 0) {
|
||||
// if any data was found, push to array
|
||||
// take care of it in the next step
|
||||
events.push({ ...event, type: 'event' });
|
||||
rundown.push({ ...event, type: 'event' });
|
||||
}
|
||||
});
|
||||
return {
|
||||
events,
|
||||
rundown,
|
||||
event: eventData,
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
version: 1,
|
||||
version: 2,
|
||||
},
|
||||
userFields: { ...dbModelv1.userFields, ...customUserFields },
|
||||
userFields: { ...dbModel.userFields, ...customUserFields },
|
||||
};
|
||||
};
|
||||
|
||||
@@ -258,7 +258,7 @@ export const parseExcel_v1 = async (excelData) => {
|
||||
* @param {boolean} [enforce=false] - flag, tells to create an object anyway
|
||||
* @returns {object} - parsed object
|
||||
*/
|
||||
export const parseJson_v1 = async (jsonData, enforce = false) => {
|
||||
export const parseJson = async (jsonData, enforce = false) => {
|
||||
if (!jsonData || typeof jsonData !== 'object') {
|
||||
console.log('ERROR: Invalid JSON format');
|
||||
return -1;
|
||||
@@ -268,21 +268,21 @@ export const parseJson_v1 = async (jsonData, enforce = false) => {
|
||||
const returnData = {};
|
||||
|
||||
// parse Events
|
||||
returnData.events = parseEvents_v1(jsonData);
|
||||
returnData.rundown = parseRundown(jsonData);
|
||||
// parse Event
|
||||
returnData.event = parseEvent_v1(jsonData, enforce);
|
||||
returnData.event = parseEvent(jsonData, enforce);
|
||||
// Settings handled partially
|
||||
returnData.settings = parseSettings_v1(jsonData, enforce);
|
||||
returnData.settings = parseSettings(jsonData, enforce);
|
||||
// View settings handled partially
|
||||
returnData.views = parseViews_v1(jsonData, enforce);
|
||||
returnData.views = parseViews(jsonData, enforce);
|
||||
// Import OSC settings if any
|
||||
returnData.osc = parseOsc_v1(jsonData, enforce);
|
||||
returnData.osc = parseOsc(jsonData, enforce);
|
||||
// Import HTTP settings if any
|
||||
returnData.http = parseHttp_v1(jsonData, enforce);
|
||||
returnData.http = parseHttp(jsonData, enforce);
|
||||
// Import Aliases if any
|
||||
returnData.aliases = parseAliases_v1(jsonData);
|
||||
returnData.aliases = parseAliases(jsonData);
|
||||
// Import user fields if any
|
||||
returnData.userFields = parseUserFields_v1(jsonData);
|
||||
returnData.userFields = parseUserFields(jsonData);
|
||||
|
||||
return returnData;
|
||||
};
|
||||
@@ -293,7 +293,7 @@ export const parseJson_v1 = async (jsonData, enforce = false) => {
|
||||
* @returns {object|null} - formatted object or null in case is invalid
|
||||
*/
|
||||
|
||||
export const validateEvent_v1 = (eventArgs) => {
|
||||
export const validateEvent = (eventArgs) => {
|
||||
// ensure id is defined and unique
|
||||
const id = eventArgs.id || generateId();
|
||||
let event = null;
|
||||
@@ -363,11 +363,11 @@ export const fileHandler = async (file) => {
|
||||
|
||||
// we only look at worksheets called ontime or event schedule
|
||||
if (excelData?.data) {
|
||||
const dataFromExcel = await parseExcel_v1(excelData.data);
|
||||
const dataFromExcel = await parseExcel(excelData.data);
|
||||
res.data = {};
|
||||
res.data.events = parseEvents_v1(dataFromExcel);
|
||||
res.data.event = parseEvent_v1(dataFromExcel, true);
|
||||
res.data.userFields = parseUserFields_v1(dataFromExcel);
|
||||
res.data.events = parseRundown(dataFromExcel);
|
||||
res.data.event = parseEvent(dataFromExcel, true);
|
||||
res.data.userFields = parseUserFields(dataFromExcel);
|
||||
res.message = 'success';
|
||||
} else {
|
||||
console.log('Error: No sheets found named ontime or event schedule');
|
||||
@@ -394,7 +394,7 @@ export const fileHandler = async (file) => {
|
||||
|
||||
if (uploadedJson.settings.version === 1) {
|
||||
try {
|
||||
res.data = await parseJson_v1(uploadedJson);
|
||||
res.data = await parseJson(uploadedJson);
|
||||
res.message = 'success';
|
||||
} catch (error) {
|
||||
res = { error: true, message: `Error parsing file: ${error}` };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { block as blockDef, delay as delayDef } from '../models/eventsDefinition.js';
|
||||
import { dbModelv1 } from '../models/dataModel.js';
|
||||
import { validateEvent_v1 } from './parser.js';
|
||||
import { dbModel } from '../models/dataModel.js';
|
||||
import { validateEvent } from './parser.js';
|
||||
import { generateId } from './generate_id.js';
|
||||
import { MAX_EVENTS } from '../settings.js';
|
||||
|
||||
@@ -9,16 +9,16 @@ import { MAX_EVENTS } from '../settings.js';
|
||||
* @param {object} data - data object
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseEvents_v1 = (data) => {
|
||||
let newEvents = [];
|
||||
if ('events' in data) {
|
||||
console.log('Found events definition, importing...');
|
||||
const events = [];
|
||||
export const parseRundown = (data) => {
|
||||
let newRundown = [];
|
||||
if ('rundown' in data) {
|
||||
console.log('Found rundown definition, importing...');
|
||||
const rundown = [];
|
||||
try {
|
||||
const ids = [];
|
||||
for (const e of data.events) {
|
||||
for (const e of data.rundown) {
|
||||
// cap number of events
|
||||
if (events.length >= MAX_EVENTS) {
|
||||
if (rundown.length >= MAX_EVENTS) {
|
||||
console.log(`ERROR: Reached limit number of ${MAX_EVENTS} events`);
|
||||
break;
|
||||
}
|
||||
@@ -30,19 +30,19 @@ export const parseEvents_v1 = (data) => {
|
||||
}
|
||||
|
||||
if (e.type === 'event') {
|
||||
const event = validateEvent_v1(e);
|
||||
const event = validateEvent(e);
|
||||
if (event != null) {
|
||||
events.push(event);
|
||||
rundown.push(event);
|
||||
ids.push(event.id);
|
||||
}
|
||||
} else if (e.type === 'delay') {
|
||||
events.push({
|
||||
rundown.push({
|
||||
...delayDef,
|
||||
duration: e.duration,
|
||||
id: e.id || generateId(),
|
||||
});
|
||||
} else if (e.type === 'block') {
|
||||
events.push({ ...blockDef, id: e.id || generateId() });
|
||||
rundown.push({ ...blockDef, id: e.id || generateId() });
|
||||
} else {
|
||||
console.log('ERROR: undefined event type, skipping');
|
||||
}
|
||||
@@ -51,10 +51,10 @@ export const parseEvents_v1 = (data) => {
|
||||
console.log(`Error ${error}`);
|
||||
}
|
||||
// write to db
|
||||
newEvents = events;
|
||||
console.log(`Uploaded file with ${events.length} entries`);
|
||||
newRundown = rundown;
|
||||
console.log(`Uploaded file with ${newRundown.length} entries`);
|
||||
}
|
||||
return newEvents;
|
||||
return newRundown;
|
||||
};
|
||||
/**
|
||||
* Parse event portion of an entry
|
||||
@@ -62,22 +62,22 @@ export const parseEvents_v1 = (data) => {
|
||||
* @param {boolean} enforce - whether to create a definition if one is missing
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseEvent_v1 = (data, enforce) => {
|
||||
export const parseEvent = (data, enforce) => {
|
||||
let newEvent = {};
|
||||
if ('event' in data) {
|
||||
console.log('Found event data, importing...');
|
||||
const e = data.event;
|
||||
// filter known properties and write to db
|
||||
newEvent = {
|
||||
...dbModelv1.event,
|
||||
title: e.title || dbModelv1.event.title,
|
||||
url: e.url || dbModelv1.event.url,
|
||||
publicInfo: e.publicInfo || dbModelv1.event.publicInfo,
|
||||
backstageInfo: e.backstageInfo || dbModelv1.event.backstageInfo,
|
||||
endMessage: e.endMessage || dbModelv1.event.endMessage,
|
||||
...dbModel.event,
|
||||
title: e.title || dbModel.event.title,
|
||||
url: e.url || dbModel.event.url,
|
||||
publicInfo: e.publicInfo || dbModel.event.publicInfo,
|
||||
backstageInfo: e.backstageInfo || dbModel.event.backstageInfo,
|
||||
endMessage: e.endMessage || dbModel.event.endMessage,
|
||||
};
|
||||
} else if (enforce) {
|
||||
newEvent = { ...dbModelv1.event };
|
||||
newEvent = { ...dbModel.event };
|
||||
console.log(`Created event object in db`);
|
||||
}
|
||||
return newEvent;
|
||||
@@ -89,7 +89,7 @@ export const parseEvent_v1 = (data, enforce) => {
|
||||
* @param {boolean} enforce - whether to create a definition if one is missing
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseSettings_v1 = (data, enforce) => {
|
||||
export const parseSettings = (data, enforce) => {
|
||||
let newSettings = {};
|
||||
if ('settings' in data) {
|
||||
console.log('Found settings definition, importing...');
|
||||
@@ -107,12 +107,12 @@ export const parseSettings_v1 = (data, enforce) => {
|
||||
|
||||
// write to db
|
||||
newSettings = {
|
||||
...dbModelv1.settings,
|
||||
...dbModel.settings,
|
||||
...settings,
|
||||
};
|
||||
}
|
||||
} else if (enforce) {
|
||||
newSettings = dbModelv1.settings;
|
||||
newSettings = dbModel.settings;
|
||||
console.log(`Created settings object in db`);
|
||||
}
|
||||
return newSettings;
|
||||
@@ -124,14 +124,14 @@ export const parseSettings_v1 = (data, enforce) => {
|
||||
* @param {boolean} enforce - whether to create a definition if one is missing
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseViews_v1 = (data, enforce) => {
|
||||
export const parseViews = (data, enforce) => {
|
||||
let newViews = {};
|
||||
if ('views' in data) {
|
||||
console.log('Found view definition, importing...');
|
||||
const v = data.views;
|
||||
|
||||
const viewSettings = {
|
||||
overrideStyles: v.overrideStyles ?? dbModelv1.views.overrideStyles,
|
||||
overrideStyles: v.overrideStyles ?? dbModel.views.overrideStyles,
|
||||
};
|
||||
|
||||
// write to db
|
||||
@@ -139,7 +139,7 @@ export const parseViews_v1 = (data, enforce) => {
|
||||
...viewSettings,
|
||||
};
|
||||
} else if (enforce) {
|
||||
newViews = dbModelv1.views;
|
||||
newViews = dbModel.views;
|
||||
console.log(`Created view object in db`);
|
||||
}
|
||||
return newViews;
|
||||
@@ -151,7 +151,7 @@ export const parseViews_v1 = (data, enforce) => {
|
||||
* @param {boolean} enforce - whether to create a definition if one is missing
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseOsc_v1 = (data, enforce) => {
|
||||
export const parseOsc = (data, enforce) => {
|
||||
let newOsc = {};
|
||||
if ('osc' in data) {
|
||||
console.log('Found OSC definition, importing...');
|
||||
@@ -164,11 +164,11 @@ export const parseOsc_v1 = (data, enforce) => {
|
||||
if (typeof s.enabled !== 'undefined') osc.enabled = s.enabled;
|
||||
// write to db
|
||||
newOsc = {
|
||||
...dbModelv1.osc,
|
||||
...dbModel.osc,
|
||||
...osc,
|
||||
};
|
||||
} else if (enforce) {
|
||||
newOsc = { ...dbModelv1.osc };
|
||||
newOsc = { ...dbModel.osc };
|
||||
console.log(`Created OSC object in db`);
|
||||
}
|
||||
return newOsc;
|
||||
@@ -180,7 +180,7 @@ export const parseOsc_v1 = (data, enforce) => {
|
||||
* @param {boolean} enforce - whether to create a definition if one is missing
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseHttp_v1 = (data, enforce) => {
|
||||
export const parseHttp = (data, enforce) => {
|
||||
const newHttp = {};
|
||||
if ('http' in data) {
|
||||
console.log('Found HTTP definition, importing...');
|
||||
@@ -192,11 +192,11 @@ export const parseHttp_v1 = (data, enforce) => {
|
||||
|
||||
// write to db
|
||||
newHttp.http = {
|
||||
...dbModelv1.http,
|
||||
...dbModel.http,
|
||||
...http,
|
||||
};
|
||||
} else if (enforce) {
|
||||
newHttp.http = { ...dbModelv1.http };
|
||||
newHttp.http = { ...dbModel.http };
|
||||
console.log(`Created http object in db`);
|
||||
}
|
||||
return newHttp;
|
||||
@@ -207,7 +207,7 @@ export const parseHttp_v1 = (data, enforce) => {
|
||||
* @param {object} data - data object
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseAliases_v1 = (data) => {
|
||||
export const parseAliases = (data) => {
|
||||
const newAliases = [];
|
||||
if ('aliases' in data) {
|
||||
console.log('Found Aliases definition, importing...');
|
||||
@@ -242,8 +242,8 @@ export const parseAliases_v1 = (data) => {
|
||||
* @param {object} data - data object
|
||||
* @returns {object} - event object data
|
||||
*/
|
||||
export const parseUserFields_v1 = (data) => {
|
||||
const newUserFields = { ...dbModelv1.userFields };
|
||||
export const parseUserFields = (data) => {
|
||||
const newUserFields = { ...dbModel.userFields };
|
||||
|
||||
if ('userFields' in data) {
|
||||
console.log('Found User Fields definition, importing...');
|
||||
@@ -8,7 +8,7 @@ test.describe('pages routes are available', () => {
|
||||
|
||||
await expect(page).toHaveTitle(/ontime/);
|
||||
await page.getByTestId('event-editor').click();
|
||||
await page.getByTestId('panel-event-list').click();
|
||||
await page.getByTestId('panel-rundown').click();
|
||||
await page.getByTestId('panel-timer-control').click();
|
||||
await page.getByTestId('panel-messages-control').click();
|
||||
await page.getByTestId('panel-info').click();
|
||||
@@ -23,8 +23,8 @@ test.describe('pages routes are available', () => {
|
||||
|
||||
test.describe('detached views', () => {
|
||||
test('rundown', async ({ page }) => {
|
||||
await page.goto('http://localhost:4001/eventlist');
|
||||
await page.getByTestId('panel-event-list').click();
|
||||
await page.goto('http://localhost:4001/rundown');
|
||||
await page.getByTestId('panel-rundown').click();
|
||||
});
|
||||
test('timer control', async ({ page }) => {
|
||||
await page.goto('http://localhost:4001/timercontrol');
|
||||
|
||||
Reference in New Issue
Block a user