mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-05 15:33:59 +00:00
feat: eventsAPI
This commit is contained in:
@@ -10,11 +10,13 @@
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.1.10",
|
||||
"axios": "^0.21.1",
|
||||
"date-fns": "^2.19.0",
|
||||
"framer-motion": "^4",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-icons": "^4.2.0",
|
||||
"react-query": "^3.13.5",
|
||||
"react-router": "^5.2.0",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "4.0.3",
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { serverURL } from './apiConstants';
|
||||
export const eventsURL = serverURL + 'events/';
|
||||
|
||||
export const fetchAllEvents = async () => {
|
||||
const res = await fetch(eventsURL + 'all');
|
||||
// TODO: Safe json convert
|
||||
return res.json();
|
||||
};
|
||||
@@ -2,9 +2,9 @@ import { IconButton } from '@chakra-ui/button';
|
||||
import { SettingsIcon } from '@chakra-ui/icons';
|
||||
import { Grid, GridItem, Heading } from '@chakra-ui/layout';
|
||||
import { Box } from '@chakra-ui/layout';
|
||||
import { useContext } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { EventContext } from '../../app/context/eventContext';
|
||||
import { useContext, useEffect } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useState } from 'react';
|
||||
import { EventListContext } from '../../app/context/eventListContext';
|
||||
import NumberedText from '../../common/components/text/NumberedText';
|
||||
import PlaybackControl from '../control/PlaybackControl';
|
||||
@@ -12,26 +12,22 @@ import MessageForm from '../form/MessageForm';
|
||||
import PreviewContainer from '../viewers/PreviewContainer';
|
||||
import styles from './Editor.module.css';
|
||||
import EventList from './list/EventList';
|
||||
import EventListWrapper from './list/EventListWrapper';
|
||||
|
||||
export default function Editor() {
|
||||
const [formMode, setFormMode] = useState(null);
|
||||
const [events] = useContext(EventListContext);
|
||||
const [webEvents, setWebEvents] = useState(null);
|
||||
const [event, setEvent] = useContext(EventContext);
|
||||
const [playback, setPlayback] = useState({
|
||||
current: null,
|
||||
next: null,
|
||||
currentTimer: null,
|
||||
numEvents: 0,
|
||||
state: 'pause',
|
||||
prevState: 'pause',
|
||||
});
|
||||
const updatePlayback = (vals) => {
|
||||
setPlayback({ ...playback, ...vals });
|
||||
};
|
||||
|
||||
console.log('events here', webEvents);
|
||||
|
||||
return (
|
||||
<Grid
|
||||
templateRows='1fr 1fr 1fr'
|
||||
@@ -46,10 +42,7 @@ export default function Editor() {
|
||||
</Heading>
|
||||
<NumberedText number={1} text={'Manage and select event to run'} />
|
||||
<div className={styles.content}>
|
||||
<EventList
|
||||
events={webEvents}
|
||||
formMode={formMode}
|
||||
setFormMode={setFormMode}
|
||||
<EventListWrapper
|
||||
selected={playback.current}
|
||||
updatePlayback={updatePlayback}
|
||||
/>
|
||||
|
||||
@@ -1,160 +1,55 @@
|
||||
import { useContext, useEffect } from 'react';
|
||||
import { EventContext } from '../../../app/context/eventContext';
|
||||
import { EventListContext } from '../../../app/context/eventListContext';
|
||||
import EventListItem from './EventListItem';
|
||||
import style from './List.module.css';
|
||||
import DelayBlock from './DelayBlock';
|
||||
import BlockBlock from './BlockBlock';
|
||||
import EventListMenu from '../../menu/EventListMenu';
|
||||
|
||||
|
||||
export default function EventList(props) {
|
||||
const [events, setEvents] = useContext(EventListContext);
|
||||
const [event] = useContext(EventContext);
|
||||
const { events, selected, eventsHandler } = props;
|
||||
|
||||
// update number of events
|
||||
useEffect(() => {
|
||||
const f = events.filter((e) => e.type === 'event');
|
||||
props.updatePlayback({ numEvents: f.length });
|
||||
}, [events]);
|
||||
|
||||
const insertItemAt = (item, index) => {
|
||||
// handle insert at beggining of array
|
||||
if (index === -1) {
|
||||
// move all items one element down, starting from new position
|
||||
events.forEach((e) => {
|
||||
e.order = e.order + 1;
|
||||
});
|
||||
return [item, ...events];
|
||||
} else {
|
||||
let before = events.slice(0, index + 1);
|
||||
let after = events.slice(index + 1);
|
||||
|
||||
// move all items one element down, starting from new position
|
||||
after.forEach((e) => {
|
||||
e.order = e.order + 1;
|
||||
});
|
||||
return [...before, item, ...after];
|
||||
}
|
||||
};
|
||||
|
||||
const createEvent = (itemIndex = -1) => {
|
||||
// make an event
|
||||
// TODO: Replace this with global def somewhere
|
||||
// TODO: handle random ids better
|
||||
let newEvent = {
|
||||
id: Math.random(),
|
||||
order: itemIndex + 1,
|
||||
title: '',
|
||||
subtitle: '',
|
||||
presenter: '',
|
||||
timeStart: new Date(),
|
||||
timeEnd: new Date(),
|
||||
clockStarted: null,
|
||||
timerDuration: 0,
|
||||
type: 'event',
|
||||
};
|
||||
|
||||
// set to state
|
||||
setEvents(insertItemAt(newEvent, itemIndex));
|
||||
};
|
||||
|
||||
const createDelay = (itemIndex = 0) => {
|
||||
// make an event
|
||||
// TODO: Replace this with global def somewhere
|
||||
// TODO: handle random ids better
|
||||
let newEvent = {
|
||||
id: Math.random(),
|
||||
order: itemIndex + 1,
|
||||
timerDuration: 0,
|
||||
type: 'delay',
|
||||
};
|
||||
|
||||
// set to state
|
||||
setEvents(insertItemAt(newEvent, itemIndex));
|
||||
};
|
||||
|
||||
const createBlock = (itemIndex = 0) => {
|
||||
// make an event
|
||||
// TODO: Replace this with global def somewhere
|
||||
// TODO: handle random ids better
|
||||
let newEvent = {
|
||||
id: Math.random(),
|
||||
order: itemIndex + 1,
|
||||
type: 'block',
|
||||
};
|
||||
|
||||
// set to state
|
||||
setEvents(insertItemAt(newEvent, itemIndex));
|
||||
};
|
||||
|
||||
const deleteEvent = (index) => {
|
||||
// TODO: This feels weird?
|
||||
let e = events.splice(index, 1);
|
||||
setEvents([...events]);
|
||||
};
|
||||
|
||||
const replaceAt = (array, index, value) => {
|
||||
const ret = array.slice(0);
|
||||
ret[index] = value;
|
||||
return ret;
|
||||
};
|
||||
|
||||
const updateData = (itemIndex, data) => {
|
||||
const newEvents = replaceAt(events, itemIndex, data);
|
||||
setEvents(newEvents);
|
||||
};
|
||||
|
||||
console.log('events in event list', events);
|
||||
console.log('EventList: events in event list', events);
|
||||
let cumulativeDelay = 0;
|
||||
let eventCount = -1;
|
||||
|
||||
// Torbjorn: is this very dirty code?
|
||||
// -- map and cumulative delay thing
|
||||
// -- should i skip the order value and just use the array as order? (checkout dnd)
|
||||
return (
|
||||
<>
|
||||
<EventListMenu createEvent={createEvent} />
|
||||
<div className={style.eventContainer}>
|
||||
{events.map((e, index) => {
|
||||
if (e.type === 'event') {
|
||||
eventCount = eventCount + 1;
|
||||
return (
|
||||
<EventListItem
|
||||
key={e.id}
|
||||
index={index}
|
||||
data={e}
|
||||
selected={props.selected === eventCount}
|
||||
createEvent={createEvent}
|
||||
deleteEvent={deleteEvent}
|
||||
createDelay={createDelay}
|
||||
createBlock={createBlock}
|
||||
updateData={updateData}
|
||||
delay={cumulativeDelay}
|
||||
/>
|
||||
);
|
||||
} else if (e.type === 'block') {
|
||||
cumulativeDelay = 0;
|
||||
return (
|
||||
<BlockBlock
|
||||
key={e.id}
|
||||
index={index}
|
||||
createEvent={createEvent}
|
||||
deleteEvent={deleteEvent}
|
||||
/>
|
||||
);
|
||||
} else if (e.type === 'delay') {
|
||||
cumulativeDelay = cumulativeDelay + e.timerDuration;
|
||||
return (
|
||||
<DelayBlock
|
||||
key={e.id}
|
||||
index={index}
|
||||
data={e}
|
||||
createEvent={createEvent}
|
||||
deleteEvent={deleteEvent}
|
||||
updateData={updateData}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
<div className={style.eventContainer}>
|
||||
{events.map((e, index) => {
|
||||
if (e.type === 'event') {
|
||||
eventCount = eventCount + 1;
|
||||
return (
|
||||
<EventListItem
|
||||
key={e.id}
|
||||
index={index}
|
||||
data={e}
|
||||
selected={selected === eventCount}
|
||||
eventsHandler={eventsHandler}
|
||||
delay={cumulativeDelay}
|
||||
/>
|
||||
);
|
||||
} else if (e.type === 'block') {
|
||||
cumulativeDelay = 0;
|
||||
return (
|
||||
<BlockBlock
|
||||
key={e.id}
|
||||
index={index}
|
||||
data={e}
|
||||
eventsHandler={eventsHandler}
|
||||
/>
|
||||
);
|
||||
} else if (e.type === 'delay') {
|
||||
cumulativeDelay = cumulativeDelay + e.timerDuration;
|
||||
return (
|
||||
<DelayBlock
|
||||
key={e.id}
|
||||
index={index}
|
||||
data={e}
|
||||
eventsHandler={eventsHandler}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { useMutation, useQuery, useQueryClient } from 'react-query';
|
||||
import { useEffect } from 'react';
|
||||
import { fetchAllEvents } from '../../../app/api/eventsApi.js';
|
||||
import EventList from './EventList';
|
||||
import EventListMenu from '../../menu/EventListMenu.jsx';
|
||||
import axios from 'axios';
|
||||
import { showErrorToast } from '../../../common/helpers/toastManager';
|
||||
import { eventsURL } from '../../../app/api/eventsApi';
|
||||
import { Skeleton } from '@chakra-ui/skeleton';
|
||||
import style from './List.module.css';
|
||||
|
||||
export default function EventListWrapper() {
|
||||
const { data, status, isError } = useQuery('events', fetchAllEvents);
|
||||
const queryClient = useQueryClient();
|
||||
// TODO: Move to events API?
|
||||
const addEvent = useMutation((data) => axios.post(eventsURL, data));
|
||||
const updateEvent = useMutation((data) => axios.put(eventsURL, data));
|
||||
const deleteEvent = useMutation((eventId) =>
|
||||
axios.delete(eventsURL + eventId)
|
||||
);
|
||||
|
||||
// Show toasts on errors
|
||||
useEffect(() => {
|
||||
if (isError) {
|
||||
showErrorToast('Error fetching data');
|
||||
}
|
||||
}, [isError]);
|
||||
|
||||
// Events API
|
||||
const eventsHandler = async (action, payload) => {
|
||||
// Torbjorn: is this a good way to do it?
|
||||
// How do I handle the mutation thing
|
||||
// https://react-query.tanstack.com/guides/invalidations-from-mutations
|
||||
// https://react-query.tanstack.com/guides/updates-from-mutation-responses
|
||||
switch (action) {
|
||||
case 'add':
|
||||
try {
|
||||
const event = await addEvent
|
||||
.mutateAsync(payload)
|
||||
.then(queryClient.invalidateQueries('events'));
|
||||
} catch (error) {
|
||||
showErrorToast('Error creating event', error.message);
|
||||
}
|
||||
break;
|
||||
case 'update':
|
||||
try {
|
||||
const event = await updateEvent
|
||||
.mutateAsync(payload)
|
||||
.then(queryClient.invalidateQueries('events'));
|
||||
} catch (error) {
|
||||
showErrorToast('Error updating event', error.message);
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
try {
|
||||
const event = await deleteEvent
|
||||
.mutateAsync(payload)
|
||||
.then(queryClient.invalidateQueries('events'));
|
||||
} catch (error) {
|
||||
showErrorToast('Error deleting event', error.message);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
showErrorToast('Unrecognised request', action);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<EventListMenu eventsHandler={eventsHandler} />
|
||||
{status === 'success' ? (
|
||||
<EventList events={data} eventsHandler={eventsHandler} />
|
||||
) : (
|
||||
<div className={style.eventContainer}>
|
||||
<Skeleton height='50vh' />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
+81
-9
@@ -1120,7 +1120,7 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||
version "7.13.10"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d"
|
||||
integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==
|
||||
@@ -3219,6 +3219,13 @@ axe-core@^4.0.2:
|
||||
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.1.3.tgz#64a4c85509e0991f5168340edc4bedd1ceea6966"
|
||||
integrity sha512-vwPpH4Aj4122EW38mxO/fxhGKtwWTMLDIJfZ1He0Edbtjcfna/R3YB67yVhezUMzqc3Jr3+Ii50KRntlENL4xQ==
|
||||
|
||||
axios@^0.21.1:
|
||||
version "0.21.1"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
|
||||
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
|
||||
dependencies:
|
||||
follow-redirects "^1.10.0"
|
||||
|
||||
axobject-query@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
|
||||
@@ -3467,6 +3474,11 @@ bfj@^7.0.2:
|
||||
hoopy "^0.1.4"
|
||||
tryer "^1.0.1"
|
||||
|
||||
big-integer@^1.6.16:
|
||||
version "1.6.48"
|
||||
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
|
||||
integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==
|
||||
|
||||
big.js@^5.2.2:
|
||||
version "5.2.2"
|
||||
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
||||
@@ -3568,6 +3580,19 @@ braces@^3.0.1, braces@~3.0.2:
|
||||
dependencies:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
broadcast-channel@^3.4.1:
|
||||
version "3.5.3"
|
||||
resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.5.3.tgz#c75c39d923ae8af6284a893bfdc8bd3996d2dd2d"
|
||||
integrity sha512-OLOXfwReZa2AAAh9yOUyiALB3YxBe0QpThwwuyRHLgpl8bSznSDmV6Mz7LeBJg1VZsMcDcNMy7B53w12qHrIhQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.7.2"
|
||||
detect-node "^2.0.4"
|
||||
js-sha3 "0.8.0"
|
||||
microseconds "0.2.0"
|
||||
nano-time "1.0.0"
|
||||
rimraf "3.0.2"
|
||||
unload "2.2.0"
|
||||
|
||||
brorand@^1.0.1, brorand@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
|
||||
@@ -5804,7 +5829,7 @@ focus-lock@^0.8.1:
|
||||
dependencies:
|
||||
tslib "^1.9.3"
|
||||
|
||||
follow-redirects@^1.0.0:
|
||||
follow-redirects@^1.0.0, follow-redirects@^1.10.0:
|
||||
version "1.13.3"
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267"
|
||||
integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==
|
||||
@@ -7482,6 +7507,11 @@ jest@26.6.0:
|
||||
import-local "^3.0.2"
|
||||
jest-cli "^26.6.0"
|
||||
|
||||
js-sha3@0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
|
||||
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@@ -7919,6 +7949,14 @@ map-visit@^1.0.0:
|
||||
dependencies:
|
||||
object-visit "^1.0.0"
|
||||
|
||||
match-sorter@^6.0.2:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.0.tgz#454a1b31ed218cddbce6231a0ecb5fdc549fed01"
|
||||
integrity sha512-efYOf/wUpNb8FgNY+cOD2EIJI1S5I7YPKsw0LBp7wqPh5pmMS6i/wr3ZWwfwrAw1NvqTA2KUReVRWDX84lUcOQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
remove-accents "0.4.2"
|
||||
|
||||
md5.js@^1.3.4:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
|
||||
@@ -8011,6 +8049,11 @@ micromatch@^4.0.2:
|
||||
braces "^3.0.1"
|
||||
picomatch "^2.0.5"
|
||||
|
||||
microseconds@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39"
|
||||
integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==
|
||||
|
||||
miller-rabin@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
|
||||
@@ -8213,6 +8256,13 @@ nan@^2.12.1:
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
|
||||
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
|
||||
|
||||
nano-time@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef"
|
||||
integrity sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8=
|
||||
dependencies:
|
||||
big-integer "^1.6.16"
|
||||
|
||||
nanoid@^3.1.20:
|
||||
version "3.1.22"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844"
|
||||
@@ -9959,6 +10009,15 @@ react-is@^17.0.1:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
|
||||
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
|
||||
|
||||
react-query@^3.13.5:
|
||||
version "3.13.5"
|
||||
resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.13.5.tgz#b59e2094014dae0461ed7aab68ae030be41eda8a"
|
||||
integrity sha512-rL0NO2gVfaeMaVD0Z0yrxZT1oMGejGxGD5t+5d8eC9kpID6oa3r2bFBR3nJeuPhwdGvZtifbB1KJkKdPYaYqVg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
broadcast-channel "^3.4.1"
|
||||
match-sorter "^6.0.2"
|
||||
|
||||
react-refresh@^0.8.3:
|
||||
version "0.8.3"
|
||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
|
||||
@@ -10268,6 +10327,11 @@ relateurl@^0.2.7:
|
||||
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
|
||||
integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
|
||||
|
||||
remove-accents@0.4.2:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5"
|
||||
integrity sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U=
|
||||
|
||||
remove-trailing-separator@^1.0.1:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
|
||||
@@ -10473,6 +10537,13 @@ rgba-regex@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
|
||||
integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
|
||||
|
||||
rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
|
||||
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
rimraf@^2.5.4, rimraf@^2.6.3:
|
||||
version "2.7.1"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
|
||||
@@ -10480,13 +10551,6 @@ rimraf@^2.5.4, rimraf@^2.6.3:
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
rimraf@^3.0.0, rimraf@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
|
||||
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
|
||||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
ripemd160@^2.0.0, ripemd160@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
|
||||
@@ -11841,6 +11905,14 @@ universalify@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
|
||||
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
|
||||
|
||||
unload@2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7"
|
||||
integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.6.2"
|
||||
detect-node "^2.0.4"
|
||||
|
||||
unpipe@1.0.0, unpipe@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||
|
||||
+2
-1
@@ -28,7 +28,8 @@ const app = express();
|
||||
app.use(cors());
|
||||
|
||||
// Implement middleware
|
||||
// ---
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use(express.json());
|
||||
|
||||
// Implement route endpoints
|
||||
app.use('/events', eventsRouter);
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
// utils
|
||||
const { nanoid } = require('nanoid');
|
||||
|
||||
// import json with initial data
|
||||
const events = require('../data/eventsData.json');
|
||||
let events = require('../data/eventsData.json');
|
||||
const eventDefs = require('../data/eventsDefinition.js');
|
||||
|
||||
// aux
|
||||
const replaceAt = (array, index, value) => {
|
||||
const ret = array.slice(0);
|
||||
ret[index] = value;
|
||||
return ret;
|
||||
};
|
||||
|
||||
// Create controller for GET request to '/events'
|
||||
// Returns ACK message
|
||||
@@ -8,7 +19,94 @@ exports.eventsGet = async (req, res) => {
|
||||
};
|
||||
|
||||
// Create controller for GET request to '/events/all'
|
||||
// Returns playback state object
|
||||
// Returns -
|
||||
exports.eventsGetAll = async (req, res) => {
|
||||
res.json(events);
|
||||
};
|
||||
|
||||
// Create controller for GET request to '/events/:Id'
|
||||
// Returns -
|
||||
exports.eventsGetById = async (req, res) => {
|
||||
// TODO
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/events/'
|
||||
// Returns -
|
||||
exports.eventsPost = async (req, res) => {
|
||||
// TODO: Validate event
|
||||
if (req.body) {
|
||||
// ensure structure
|
||||
let newEvent = {};
|
||||
req.body.id = nanoid(10);
|
||||
|
||||
switch (req.body.type) {
|
||||
case 'event':
|
||||
newEvent = { ...eventDefs.event, ...req.body };
|
||||
break;
|
||||
case 'delay':
|
||||
newEvent = { ...eventDefs.delay, ...req.body };
|
||||
break;
|
||||
case 'block':
|
||||
newEvent = { ...eventDefs.block, ...req.body };
|
||||
break;
|
||||
|
||||
default:
|
||||
res.sendStatus(400);
|
||||
break;
|
||||
}
|
||||
|
||||
// Torbjorn: hmmmmmmm, can we look here?
|
||||
if (newEvent.order <= 0) {
|
||||
newEvent.order = 0;
|
||||
// insert at top
|
||||
events.forEach((e) => {
|
||||
e.order = e.order + 1;
|
||||
});
|
||||
events = [newEvent, ...events];
|
||||
} else if (newEvent.order >= events.length) {
|
||||
newEvent.order = events.length;
|
||||
events = [...events, newEvent];
|
||||
} else {
|
||||
let before = events.slice(0, newEvent.order);
|
||||
let after = events.slice(newEvent.order);
|
||||
// move all items one element down, starting from new position
|
||||
after.forEach((e) => {
|
||||
e.order = e.order + 1;
|
||||
});
|
||||
events = [...before, newEvent, ...after];
|
||||
}
|
||||
console.log('added', events);
|
||||
res.sendStatus(201);
|
||||
} else {
|
||||
console.log('nothing to add', req.body);
|
||||
res.sendStatus(400);
|
||||
}
|
||||
};
|
||||
|
||||
// Create controller for PUT request to '/events/:id'
|
||||
// Returns -
|
||||
exports.eventsPut = async (req, res) => {
|
||||
const itemIndex = events.findIndex((e) => e.id == req.body.id);
|
||||
|
||||
if (!itemIndex) res.sendStatus(400);
|
||||
const newEvents = replaceAt(events, itemIndex, req.body);
|
||||
events = [...newEvents];
|
||||
res.sendStatus(200);
|
||||
};
|
||||
|
||||
// Create controller for DELETE request to '/events/'
|
||||
// Returns -
|
||||
exports.eventsDelete = async (req, res) => {
|
||||
if (req.params.id) {
|
||||
const itemIndex = events.findIndex((e) => e.id == req.params.id);
|
||||
|
||||
if (!itemIndex) res.sendStatus(400);
|
||||
|
||||
// Torbjorn: this syntax is very bad
|
||||
const e = events.splice(itemIndex, 1);
|
||||
events = [...events];
|
||||
res.sendStatus(200);
|
||||
} else {
|
||||
res.sendStatus(400);
|
||||
}
|
||||
};
|
||||
|
||||
+19
-13
@@ -1,48 +1,54 @@
|
||||
[
|
||||
{
|
||||
"id": "1",
|
||||
"id":"5ce39a29-a70c-426a-9ce2-f97782049c71",
|
||||
"order": 0,
|
||||
"timerDuration": 5,
|
||||
"type": "delay"
|
||||
},
|
||||
{
|
||||
"id": "5ce39a29-a70c-426a-9ce2-f97782049c72",
|
||||
"order": 1,
|
||||
"title": "Is the internet a fad?",
|
||||
"subtitle": "It is",
|
||||
"presenter": "Carlos Valente",
|
||||
"timeStart": "",
|
||||
"timeEnd":"",
|
||||
"timeStart": "2020-04-09T11:25:43.511Z",
|
||||
"timeEnd": "2020-04-09T12:25:43.511Z",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"id": 0.4849093424577693,
|
||||
"id":"5ce39a29-a70c-426a-9ce2-f97782049c73",
|
||||
"order": 2,
|
||||
"timerDuration": 25,
|
||||
"type": "delay"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"id": "5ce39a29-a70c-426a-9ce2-f97782049c74",
|
||||
"order": 3,
|
||||
"title": "Is reddit a dictatorship?",
|
||||
"subtitle": "It is",
|
||||
"presenter": "Carlos Valente",
|
||||
"timeStart": "",
|
||||
"timeEnd":"",
|
||||
"timeStart": "2020-04-09T13:25:43.511Z",
|
||||
"timeEnd": "2020-04-09T14:25:43.511Z",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"id": "5ce39a29-a70c-426a-9ce2-f97782049c75",
|
||||
"order": 4,
|
||||
"title": "Out of words",
|
||||
"subtitle": "",
|
||||
"presenter": "Carlos Valente",
|
||||
"timeStart": "",
|
||||
"timeEnd":"",
|
||||
"timeStart": "2020-04-09T15:25:43.511Z",
|
||||
"timeEnd": "2020-04-09T16:25:43.511Z",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
"id": "5ce39a29-a70c-426a-9ce2-f97782049c76",
|
||||
"order": 5,
|
||||
"title": "Really",
|
||||
"subtitle": "",
|
||||
"presenter": "Carlos Valente",
|
||||
"timeStart": "",
|
||||
"timeEnd":"",
|
||||
"timeStart": "2020-04-09T17:25:43.511Z",
|
||||
"timeEnd": "2020-04-09T18:25:43.511Z",
|
||||
"type": "event"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
const event = {
|
||||
order: 0,
|
||||
title: '',
|
||||
subtitle: '',
|
||||
presenter: '',
|
||||
timeStart: new Date(),
|
||||
timeEnd: new Date(),
|
||||
clockStarted: null,
|
||||
type: 'event',
|
||||
};
|
||||
|
||||
const delay = {
|
||||
order: 0,
|
||||
timerDuration: 0,
|
||||
type: 'delay',
|
||||
};
|
||||
|
||||
const block = {
|
||||
order: 0,
|
||||
type: 'block',
|
||||
};
|
||||
|
||||
module.exports = { event, delay, block };
|
||||
+2
-1
@@ -6,6 +6,7 @@
|
||||
"body-parser": "~1.19.0",
|
||||
"express": "~4.17.1",
|
||||
"express-session": "~1.17.1",
|
||||
"nanoid": "^3.1.22",
|
||||
"passport": "~0.4.1",
|
||||
"passport-local": "~1.0.0",
|
||||
"socket.io": "^4.0.0"
|
||||
@@ -13,4 +14,4 @@
|
||||
"scripts": {
|
||||
"start": "node app.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const router = express.Router();
|
||||
|
||||
// Cross origin stuff
|
||||
const corsOptions = {
|
||||
origin: 'http://localhost',
|
||||
}
|
||||
|
||||
// import playback controller
|
||||
const eventsController = require('../controllers/eventsController');
|
||||
|
||||
// create route between controller and '/events' endpoint
|
||||
router.get('/', cors(corsOptions), eventsController.eventsGet);
|
||||
router.get('/', eventsController.eventsGet);
|
||||
|
||||
// create route between controller and '/events/all' endpoint
|
||||
router.get('/all', cors(corsOptions), eventsController.eventsGetAll);
|
||||
router.get('/all', eventsController.eventsGetAll);
|
||||
|
||||
// create route between controller and '/events/:id' endpoint
|
||||
router.get('/:id', eventsController.eventsGetById);
|
||||
|
||||
// create route between controller and '/events/' endpoint
|
||||
router.post('/', eventsController.eventsPost);
|
||||
|
||||
// create route between controller and '/events/:id' endpoint
|
||||
router.put('/', eventsController.eventsPut);
|
||||
|
||||
// create route between controller and '/events/:id' endpoint
|
||||
router.delete('/:id', eventsController.eventsDelete);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -339,6 +339,11 @@ ms@2.1.2:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
nanoid@^3.1.22:
|
||||
version "3.1.22"
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844"
|
||||
integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==
|
||||
|
||||
negotiator@0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
|
||||
|
||||
Reference in New Issue
Block a user