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) => {