From ac0d5832b6f943cce59e27175655f30792b4f73d Mon Sep 17 00:00:00 2001
From: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
Date: Wed, 22 Dec 2021 18:17:48 +0100
Subject: [PATCH] event cucle (#76)
* install sass
* refact: integration settings
- OSC in its own HTTP endpoint
- OSC settings have own object in db
* refact: simplify event cycle
* refact: restructure external triggers http
* refact: restructure external triggers osc+socket
* refact: restructure data updates
* feat: osc integration class
* IO improvements
- timer uses osc integration
- create trigger handler to manage external triggers
* refact: refract state machine update
* Integration: simple HTTP Client
* Integration: http options in datamodel
* Integration: call http send on life cycle
* feat/62-logging: fix issue #71
---
.gitignore | 12 +-
.idea/.gitignore | 5 +
client/src/App.jsx | 2 +-
client/src/{App.css => App.scss} | 0
client/src/app/api/apiConstants.js | 1 +
client/src/app/api/ontimeApi.js | 84 +-
.../src/features/control/PlaybackButtons.jsx | 46 +-
.../src/features/control/PlaybackControl.jsx | 9 +
client/src/features/control/PlaybackTimer.jsx | 30 +-
client/src/features/info/Info.jsx | 2 +-
.../{Info.module.css => Info.module.scss} | 11 +-
client/src/features/info/InfoLogger.jsx | 2 +-
client/src/features/info/InfoNif.jsx | 2 +-
client/src/features/info/InfoTitle.jsx | 2 +-
client/src/features/modals/AliasesModal.jsx | 20 +-
.../src/features/modals/AppSettingsModal.jsx | 75 +-
.../features/modals/EventSettingsModal.jsx | 21 +-
.../modals/IntegrationSettingsModal.jsx | 335 ++++
client/src/features/modals/ModalManager.jsx | 12 +-
client/src/features/modals/Modals.module.css | 60 -
client/src/features/modals/Modals.module.scss | 99 ++
.../viewers/backstage/StageManager.jsx | 6 +-
.../features/viewers/studio/StudioClock.jsx | 16 +-
client/src/index.js | 15 +-
client/src/{index.css => index.scss} | 2 +
client/src/styles/_main.scss | 11 +
client/src/styles/_variables.scss | 4 +
server/main.js | 9 +-
server/package.json | 15 +-
server/src/app.js | 43 +-
server/src/classes/EventTimer.js | 641 +++++---
server/src/classes/Timer.js | 57 +-
.../src/classes/__tests__/classUtils.test.js | 215 ++-
.../src/classes/__tests__/eventtimer.test.js | 137 ++
server/src/classes/classUtils.js | 87 +-
server/src/classes/integrations/Http.js | 52 +
server/src/classes/integrations/Osc.js | 107 ++
server/src/config/config.js | 8 +-
server/src/controllers/OscController.js | 14 +-
server/src/controllers/eventsController.js | 26 +-
server/src/controllers/ontimeController.js | 39 +-
server/src/controllers/playbackController.js | 31 +-
server/src/models/dataModel.js | 43 +-
server/src/package.json | 10 +-
server/src/routes/ontimeRouter.js | 10 +-
server/src/utils/__tests__/parser.tests.js | 31 +-
server/src/utils/__tests__/time.tests.js | 4 +-
server/src/utils/__tests__/url.test.js | 36 +
server/src/utils/parser.js | 85 +-
server/src/utils/time.js | 3 +-
server/src/utils/url.js | 21 +
server/src/yarn.lock | 1376 +----------------
server/yarn.lock | 5 +
53 files changed, 2041 insertions(+), 1948 deletions(-)
create mode 100644 .idea/.gitignore
rename client/src/{App.css => App.scss} (100%)
rename client/src/features/info/{Info.module.css => Info.module.scss} (96%)
create mode 100644 client/src/features/modals/IntegrationSettingsModal.jsx
delete mode 100644 client/src/features/modals/Modals.module.css
create mode 100644 client/src/features/modals/Modals.module.scss
rename client/src/{index.css => index.scss} (94%)
create mode 100644 client/src/styles/_main.scss
create mode 100644 client/src/styles/_variables.scss
create mode 100644 server/src/classes/__tests__/eventtimer.test.js
create mode 100644 server/src/classes/integrations/Http.js
create mode 100644 server/src/classes/integrations/Osc.js
create mode 100644 server/src/utils/__tests__/url.test.js
create mode 100644 server/src/utils/url.js
diff --git a/.gitignore b/.gitignore
index 7a597a038..6819aafb4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,13 +23,17 @@ dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
-ontime.code-workspace
-TODO.md
# working stuff
_SS/
-.vscode/launch.json
-.eslintrc.json
db backup.json
server/src/data/db.json
server/src/models/db.json
+TODO.md
+
+# vscode stuff
+.vscode/*
+ontime.code-workspace
+
+# webstorm stuff
+.idea/*
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 000000000..b58b603fe
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,5 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/client/src/App.jsx b/client/src/App.jsx
index a56c126de..fef3e7a31 100644
--- a/client/src/App.jsx
+++ b/client/src/App.jsx
@@ -1,6 +1,6 @@
import { lazy, Suspense, useCallback, useEffect } from 'react';
import { Route, Switch } from 'react-router-dom';
-import './App.css';
+import './App.scss';
import { QueryClient, QueryClientProvider } from 'react-query';
import SocketProvider from 'app/context/socketContext';
import withSocket from 'features/viewers/ViewWrapper';
diff --git a/client/src/App.css b/client/src/App.scss
similarity index 100%
rename from client/src/App.css
rename to client/src/App.scss
diff --git a/client/src/app/api/apiConstants.js b/client/src/app/api/apiConstants.js
index d6a0f2d2f..2fbdadb91 100644
--- a/client/src/app/api/apiConstants.js
+++ b/client/src/app/api/apiConstants.js
@@ -2,6 +2,7 @@ export const NODE_PORT = 4001;
export const EVENT_TABLE = 'event';
export const EVENTS_TABLE = 'events';
export const APP_TABLE = 'appinfo';
+export const OSC_SETTINGS = 'oscSettings';
const calculateServer = () => {
return window.location.origin.replace(window.location.port, `${NODE_PORT}/`);
diff --git a/client/src/app/api/ontimeApi.js b/client/src/app/api/ontimeApi.js
index 9d850259c..1d736dfb8 100644
--- a/client/src/app/api/ontimeApi.js
+++ b/client/src/app/api/ontimeApi.js
@@ -3,13 +3,77 @@ import { ontimeURL } from './apiConstants';
export const ontimePlaceholderInfo = {
networkInterfaces: [],
- version: '',
- serverPort: 4001,
- oscInPort: '',
- oscOutPort: '',
- oscOutIP: '',
+ settings: {
+ version: '',
+ serverPort: 4001,
+ },
};
+export const oscPlaceholderSettings = {
+ port: '',
+ portOut: '',
+ targetIP: '',
+ enabled: true,
+};
+
+export const httpPlaceholder = {
+ onLoad: {
+ url: '',
+ enabled: false,
+ },
+ onStart: {
+ url: '',
+ enabled: false,
+ },
+ onUpdate: {
+ url: '',
+ enabled: false,
+ },
+ onPause: {
+ url: '',
+ enabled: false,
+ },
+ onStop: {
+ url: '',
+ enabled: false,
+ },
+ onFinish: {
+ url: '',
+ enabled: false,
+ },
+};
+
+export const ontimeVars = [
+ {
+ name: '$timer',
+ description: 'Current running timer',
+ },
+ {
+ name: '$title',
+ description: 'Current title',
+ },
+ {
+ name: '$presenter',
+ description: 'Current presenter',
+ },
+ {
+ name: '$subtitle',
+ description: 'Current subtitle',
+ },
+ {
+ name: '$next-title',
+ description: 'Next title',
+ },
+ {
+ name: '$next-presenter',
+ description: 'Next presenter',
+ },
+ {
+ name: '$next-subtitle',
+ description: 'Next subtitle',
+ },
+];
+
export const getInfo = async () => {
const res = await axios.get(ontimeURL + '/info');
return res.data;
@@ -20,6 +84,16 @@ export const postInfo = async (data) => {
return res;
};
+export const getOSC = async () => {
+ const res = await axios.get(ontimeURL + '/osc');
+ return res.data;
+};
+
+export const postOSC = async (data) => {
+ const res = await axios.post(ontimeURL + '/osc', data);
+ return res;
+};
+
export const downloadEvents = async () => {
await axios({
url: ontimeURL + '/db',
diff --git a/client/src/features/control/PlaybackButtons.jsx b/client/src/features/control/PlaybackButtons.jsx
index 3a6452b48..c5b25f92d 100644
--- a/client/src/features/control/PlaybackButtons.jsx
+++ b/client/src/features/control/PlaybackButtons.jsx
@@ -1,4 +1,5 @@
import { memo } from 'react';
+import PropTypes from 'prop-types';
import style from './PlaybackControl.module.scss';
import StartIconBtn from 'common/components/buttons/StartIconBtn';
import PauseIconBtn from 'common/components/buttons/PauseIconBtn';
@@ -10,70 +11,77 @@ import ReloadIconButton from 'common/components/buttons/ReloadIconBtn';
const areEqual = (prevProps, nextProps) => {
return (
- prevProps.playback === nextProps.playback &&
- prevProps.selectedId === nextProps.selectedId
+ prevProps.playback === nextProps.playback
+ && prevProps.selectedId === nextProps.selectedId
+ && prevProps.noEvents === nextProps.noEvents
);
};
-const Playback = ({ playback, selectedId, playbackControl }) => {
+const Playback = (props) => {
+ const { playback, selectedId, playbackControl, noEvents } = props;
const isRolling = playback === 'roll';
+
return (
playbackControl('start')}
- disabled={!selectedId || isRolling}
+ disabled={!selectedId || isRolling || noEvents}
/>
playbackControl('pause')}
- disabled={!selectedId || isRolling}
+ disabled={!selectedId || isRolling || noEvents || playback !== 'start'}
/>
playbackControl('roll')}
/>
);
};
-const Transport = ({ playback, selectedId, playbackControl }) => {
+const Transport = (props) => {
+ const { playback, selectedId, playbackControl, noEvents } = props;
const isRolling = playback === 'roll';
+
return (
playbackControl('previous')}
- disabled={playback === 'roll'}
+ disabled={isRolling || noEvents}
/>
playbackControl('next')}
- disabled={playback === 'roll'}
+ disabled={isRolling || noEvents}
/>
playbackControl('reload')}
- disabled={!selectedId || isRolling}
+ disabled={selectedId == null || isRolling || noEvents}
/>
playbackControl('unload')}
- disabled={!selectedId && !isRolling}
+ disabled={(selectedId == null && !isRolling) || noEvents}
/>
);
};
const PlaybackButtons = (props) => {
- const { playback, selectedId } = props;
+ const { playback, selectedId, noEvents } = props;
return (
<>
>
@@ -81,3 +89,17 @@ const PlaybackButtons = (props) => {
};
export default memo(PlaybackButtons, areEqual);
+
+PlaybackButtons.propTypes = {
+ playback: PropTypes.string,
+ selectedId: PropTypes.string,
+ playbackControl: PropTypes.func.isRequired,
+ noEvents: PropTypes.bool.isRequired,
+};
+
+Transport.propTypes = {
+ playback: PropTypes.string,
+ selectedId: PropTypes.string,
+ playbackControl: PropTypes.func.isRequired,
+ noEvents: PropTypes.bool.isRequired,
+};
diff --git a/client/src/features/control/PlaybackControl.jsx b/client/src/features/control/PlaybackControl.jsx
index 366f3505f..64e9dd95a 100644
--- a/client/src/features/control/PlaybackControl.jsx
+++ b/client/src/features/control/PlaybackControl.jsx
@@ -15,6 +15,7 @@ export default function PlaybackControl() {
secondary: null,
});
const [selectedId, setSelectedId] = useState(null);
+ const [numEvents, setNumEvents] = useState(0);
const resetTimer = () => {
setTimer({
@@ -32,6 +33,7 @@ export default function PlaybackControl() {
socket.emit('get-timer');
socket.emit('get-playstate');
socket.emit('get-selected-id');
+ socket.emit('get-numevents');
// Handle playstate
socket.on('playstate', (data) => {
@@ -48,11 +50,16 @@ export default function PlaybackControl() {
setSelectedId(data);
});
+ socket.on('numevents', (data) => {
+ setNumEvents(data);
+ });
+
// Clear listener
return () => {
socket.off('playstate');
socket.off('timer');
socket.off('selected-id');
+ socket.off('numevents');
};
}, [socket]);
@@ -93,11 +100,13 @@ export default function PlaybackControl() {
socket.emit('increment-timer', amount)}
/>
diff --git a/client/src/features/control/PlaybackTimer.jsx b/client/src/features/control/PlaybackTimer.jsx
index 5e739c759..5aad78e3d 100644
--- a/client/src/features/control/PlaybackTimer.jsx
+++ b/client/src/features/control/PlaybackTimer.jsx
@@ -4,24 +4,27 @@ import {stringFromMillis} from 'common/utils/dateConfig';
import {Tooltip} from '@chakra-ui/react';
import {Button} from '@chakra-ui/button';
import {memo} from 'react';
+import PropTypes from "prop-types";
const areEqual = (prevProps, nextProps) => {
return (
- prevProps.timer.running === nextProps.timer.running &&
- prevProps.timer.expectedFinish === nextProps.timer.expectedFinish &&
- prevProps.timer.startedAt === nextProps.timer.startedAt &&
- prevProps.playback === nextProps.playback &&
- prevProps.timer.secondary === nextProps.timer.secondary
+ prevProps.timer.running === nextProps.timer.running
+ && prevProps.timer.expectedFinish === nextProps.timer.expectedFinish
+ && prevProps.timer.startedAt === nextProps.timer.startedAt
+ && prevProps.playback === nextProps.playback
+ && prevProps.timer.secondary === nextProps.timer.secondary
+ && prevProps.selectedId === nextProps.selectedId
);
};
const PlaybackTimer = (props) => {
- const {timer, playback, handleIncrement} = props;
+ const {timer, playback, handleIncrement, selectedId} = props;
const started = stringFromMillis(timer.startedAt, true);
const finish = stringFromMillis(timer.expectedFinish, true);
const isNegative = timer.running < 0;
const isRolling = playback === 'roll';
const isWaiting = timer.secondary > 0 && timer.running == null;
+ const disableButtons = (selectedId == null || isRolling);
const incrementProps = {
size: 'sm',
@@ -70,28 +73,28 @@ const PlaybackTimer = (props) => {