{
if (socket == null) return;
// Handle presenter messages
socket.on('messages-presenter', (data) => {
- setPres({ ...data });
+ setPres({...data});
});
// Handle public messages
socket.on('messages-public', (data) => {
- setPubl({ ...data });
+ setPubl({...data});
});
// Handle lower third messages
socket.on('messages-lower', (data) => {
- setLower({ ...data });
+ setLower({...data});
+ });
+
+ // Handle lower third messages
+ socket.on('onAir', (data) => {
+ setonAir(data);
});
// Ask for up to date data
socket.emit('get-messages');
+ // Ask for onAir state
+ socket.emit('get-onAir');
// Clear listeners
return () => {
socket.off('messages-public');
socket.off('messages-presenter');
socket.off('messages-lower');
+ socket.off('onAir');
};
}, [socket]);
@@ -99,38 +109,54 @@ export default function MessageControl() {
case 'toggle-lower-visible':
socket.emit('set-lower-visible', !lower.visible);
break;
-
+ case 'toggle-onAir':
+ socket.emit('set-onAir', !onAir);
+ break;
default:
break;
}
};
return (
-
- messageControl('pres-text', event)}
- actionHandler={() => messageControl('toggle-pres-visible')}
- />
- messageControl('publ-text', event)}
- actionHandler={() => messageControl('toggle-publ-visible')}
- />
- messageControl('lower-text', event)}
- actionHandler={() => messageControl('toggle-lower-visible')}
- />
-
+ <>
+
+ messageControl('pres-text', event)}
+ actionHandler={() => messageControl('toggle-pres-visible')}
+ />
+ messageControl('publ-text', event)}
+ actionHandler={() => messageControl('toggle-publ-visible')}
+ />
+ messageControl('lower-text', event)}
+ actionHandler={() => messageControl('toggle-lower-visible')}
+ />
+
+
+ messageControl('toggle-onAir')}>
+ On Air?
+
+
+ {`/ontime/offAir << OSC >> /ontime/onAir`}
+
+
+ >
);
}
diff --git a/client/src/features/control/MessageControl.module.css b/client/src/features/control/MessageControl.module.css
deleted file mode 100644
index 6c67eeb37..000000000
--- a/client/src/features/control/MessageControl.module.css
+++ /dev/null
@@ -1,32 +0,0 @@
-.messageContainer {
- background-color: rgba(0, 0, 0, 0.05);
- border: 1px solid rgba(0, 0, 0, 0.05);
- border-radius: 4px;
- display: flex;
- flex-direction: column;
- gap: 0.5em;
- padding: 0.5em;
-}
-
-.inputItems {
- display: grid;
- grid-template-columns: 1fr auto;
- gap: 1em;
-}
-
-.label {
- padding: 0;
- margin: 0;
- font-size: 0.9em;
- color: #ccc;
-}
-
-.inline {
- border-radius: 4px;
- background-color: rgba(255, 255, 255, 0.05);
- border: 1px solid rgba(255, 255, 255, 0.05);
-}
-
-.padleft {
- padding-left: 0.5em;
-}
diff --git a/client/src/features/control/MessageControl.module.scss b/client/src/features/control/MessageControl.module.scss
new file mode 100644
index 000000000..52889d3fb
--- /dev/null
+++ b/client/src/features/control/MessageControl.module.scss
@@ -0,0 +1,54 @@
+.messageContainer,
+.onAirToggle {
+ background-color: rgba(0, 0, 0, 0.05);
+ border: 1px solid rgba(0, 0, 0, 0.05);
+ border-radius: 4px;
+ display: flex;
+ gap: 0.5em;
+ padding: 0.5em;
+
+}
+
+.messageContainer {
+ flex-direction: column;
+
+ .inputItems {
+ display: grid;
+ grid-template-columns: 1fr auto;
+ gap: 1em;
+ }
+ .label {
+ padding: 0;
+ margin: 0;
+ font-size: 0.9em;
+ color: #ccc;
+ }
+ .inline {
+ border-radius: 4px;
+ background-color: rgba(255, 255, 255, 0.05);
+ border: 1px solid rgba(255, 255, 255, 0.05);
+ }
+ .padleft {
+ padding-left: 0.5em;
+ }
+}
+
+.onAirToggle {
+ margin-top: 1em;
+ display: flex;
+ gap: 1em;
+ align-items: center;
+ line-height: 3em;
+
+ .onAirLabel {
+ font-size: 1.2em;
+ }
+ .oscLabel {
+ color: #4bffabcc;
+ font-size: 0.8em;
+ float: right;
+ padding-right: 1em;
+ -webkit-user-select: text;
+ user-select: text;
+ }
+}
diff --git a/client/src/features/viewers/ViewWrapper.jsx b/client/src/features/viewers/ViewWrapper.jsx
index 6d90c0a7e..7f58dea19 100644
--- a/client/src/features/viewers/ViewWrapper.jsx
+++ b/client/src/features/viewers/ViewWrapper.jsx
@@ -10,13 +10,9 @@ const withSocket = (Component) => {
const WrappedComponent = (props) => {
const {
data: eventsData,
- status: eventsDataStatus,
- isError: eventsDataIsError,
} = useFetch(EVENTS_TABLE, fetchAllEvents);
const {
data: genData,
- status: genDataStatus,
- isError: genDataIsError,
} = useFetch(EVENT_TABLE, fetchEvent);
const [publicEvents, setPublicEvents] = useState([]);
@@ -58,6 +54,7 @@ const withSocket = (Component) => {
presenterNext: '',
});
const [selectedId, setSelectedId] = useState(null);
+ const [nextId, setNextId] = useState(null);
const [publicSelectedId, setPublicSelectedId] = useState(null);
const [general, setGeneral] = useState({
title: '',
@@ -67,6 +64,7 @@ const withSocket = (Component) => {
endMessage: '',
});
const [playback, setPlayback] = useState(null);
+ const [onAir, setOnAir] = useState(false);
// Ask for update on load
useEffect(() => {
@@ -96,6 +94,9 @@ const withSocket = (Component) => {
socket.on('playstate', (data) => {
setPlayback(data);
});
+ socket.on('onAir', (data) => {
+ setOnAir(data);
+ });
// Handle titles
socket.on('titles', (data) => {
@@ -112,6 +113,9 @@ const withSocket = (Component) => {
socket.on('publicselected-id', (data) => {
setPublicSelectedId(data);
});
+ socket.on('next-id', (data) => {
+ setNextId(data);
+ });
// Ask for up to date data
socket.emit('get-messages');
@@ -124,6 +128,7 @@ const withSocket = (Component) => {
// ask for playstate
socket.emit('get-playstate');
+ socket.emit('get-onAir')
// Ask for up titles
socket.emit('get-titles');
@@ -131,6 +136,7 @@ const withSocket = (Component) => {
// Ask for up selected
socket.emit('get-selected-id');
+ socket.emit('get-next-id');
// Clear listeners
return () => {
@@ -139,9 +145,11 @@ const withSocket = (Component) => {
socket.off('messages-lower');
socket.off('timer');
socket.off('playstate');
+ socket.off('onAir');
socket.off('titles');
socket.off('publictitles');
socket.off('selected-id');
+ socket.emit('next-id');
};
}, [socket]);
@@ -224,6 +232,7 @@ const withSocket = (Component) => {
...timer,
finished: playback === 'start' && timer.running <= 0 && timer.startedAt,
clock: stringFromMillis(timer.clock),
+ clockNoSeconds: stringFromMillis(timer.clock, false),
playstate: playback,
};
@@ -240,7 +249,9 @@ const withSocket = (Component) => {
backstageEvents={backstageEvents}
selectedId={selectedId}
publicSelectedId={publicSelectedId}
+ nextId={nextId}
general={general}
+ onAir={onAir}
/>
);
};
diff --git a/client/src/features/viewers/backstage/StageManager.jsx b/client/src/features/viewers/backstage/StageManager.jsx
index 3e843b2a1..39d507a83 100644
--- a/client/src/features/viewers/backstage/StageManager.jsx
+++ b/client/src/features/viewers/backstage/StageManager.jsx
@@ -6,6 +6,7 @@ import NavLogo from 'common/components/nav/NavLogo';
import { useEffect, useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import TitleSide from 'common/components/views/TitleSide';
+import {getEventsWithDelay} from "../../../common/utils/eventsManager";
export default function StageManager(props) {
const { publ, title, time, backstageEvents, selectedId, general } = props;
@@ -20,23 +21,9 @@ export default function StageManager(props) {
useEffect(() => {
if (backstageEvents == null) return;
- let events = [...backstageEvents];
+ setFilteredEvents(getEventsWithDelay(backstageEvents));
- // Add running delay
- let delay = 0;
- for (const e of events) {
- if (e.type === 'block') delay = 0;
- else if (e.type === 'delay') delay = delay + e.duration;
- else if (e.type === 'event' && delay > 0) {
- e.timeStart += delay;
- e.timeEnd += delay;
- }
- }
-
- // filter just events
- let filtered = events.filter((e) => e.type === 'event');
-
- setFilteredEvents(filtered);
+ console.log('hhh', getEventsWithDelay(backstageEvents))
}, [backstageEvents]);
// Format messages
diff --git a/client/src/features/viewers/presenter/PresenterView.jsx b/client/src/features/viewers/presenter/PresenterView.jsx
index 59957f1f1..7e185b1e1 100644
--- a/client/src/features/viewers/presenter/PresenterView.jsx
+++ b/client/src/features/viewers/presenter/PresenterView.jsx
@@ -11,7 +11,7 @@ export default function PresenterView(props) {
// Set window title
useEffect(() => {
- document.title = 'ontime - Speaker Screen';
+ document.title = 'ontime - Presenter Screen';
}, []);
const showOverlay = pres.text !== '' && pres.visible;
diff --git a/client/src/features/viewers/production/lower/LowerClean.jsx b/client/src/features/viewers/production/lower/LowerClean.jsx
index fb9fc8c40..b26dec608 100644
--- a/client/src/features/viewers/production/lower/LowerClean.jsx
+++ b/client/src/features/viewers/production/lower/LowerClean.jsx
@@ -1,6 +1,7 @@
import { AnimatePresence, motion } from 'framer-motion';
import { useEffect, useState } from 'react';
import style from './LowerClean.module.css';
+import NavLogo from "../../../../common/components/nav/NavLogo";
export default function LowerClean(props) {
const { lower, title, options } = props;
@@ -86,6 +87,9 @@ export default function LowerClean(props) {
fontSize: `${sizeMultiplier}vh`,
}}
>
+
+
+
{showLower && (
+
+
+
{showLower && (
{
+ document.title = 'ontime - Studio Clock';
+ }, []);
+
+ // Prepare event list
+ // Todo: useMemo()
+ useEffect(() => {
+ if (backstageEvents == null) return;
+
+ const events = backstageEvents.filter((e) => e.type === 'event');
+
+ let e = trimEventlist(events, selectedId, MAX_TITLES);
+ e = formatEventList(e, selectedId, nextId);
+ setSchedule(e);
+
+ }, [backstageEvents, selectedId, nextId]);
+
+ return (
+
+
+
+
{time.clockNoSeconds}
+
+ {title.titleNext}
+
+
0 ? style.nextCountdown: style.nextCountdown__overtime}>
+ {selectedId != null && formatDisplay(time.running)}
+
+
+ {hoursIndicators.map(i => (
+
+ )
+ )}
+ {minutesIndicators.map(i => (
+
+ )
+ )}
+
+
+
+
ON AIR
+
+
+ {schedule.map((s) => (
+ -
+ {`${s.time} ${s.title}`}
+
+ ))
+ }
+
+
+
+
+ );
+}
diff --git a/client/src/features/viewers/studio/StudioClock.module.scss b/client/src/features/viewers/studio/StudioClock.module.scss
new file mode 100644
index 000000000..ebf1f7e53
--- /dev/null
+++ b/client/src/features/viewers/studio/StudioClock.module.scss
@@ -0,0 +1,190 @@
+@font-face {
+ font-family: "digital-clock";
+ src: local('digital-7'), url('./../../../assets/fonts/digital-7.mono.ttf') format('truetype') ;
+}
+
+.container {
+ margin: 0;
+ box-sizing: border-box; /* reset */
+ overflow: hidden;
+ width: 100%; /* restrict the page width to viewport */
+ height: 100vh;
+ padding: 1vw;
+
+ background: #111;
+
+ display: grid;
+ grid-template-columns: 1000px 1fr;
+ gap: 50px;
+ grid-template-areas: "clck schd";
+
+ /* =============== CLOCK STUFF ==================*/
+
+ $clock-size: 900px;
+ $size-hours: 20px;
+ $half-hours: 10px;
+ $size-min: 18px;
+ $half-min: 9px;
+ $red-active: #c53030;
+ $red-idle: darken($red-active, 30%);
+ $cyan-active: #0ff;
+ $cyan-idle: #0aa;
+
+ .clockContainer {
+ display: grid;
+ place-content: center;
+ grid-area: clck;
+ width: $clock-size;
+ height: $clock-size;
+ text-align: center;
+ position: relative;
+ margin: auto;
+ font-family: digital-clock, monospace;
+ text-transform: uppercase;
+
+
+ .time {
+ margin-top: 175px;
+ color: $red-active;
+ font-size: 275px;
+ line-height: 0.8em;
+ text-shadow: rgb(180,0,0) 0 0 20px;
+ }
+ .nextTitle:after,
+ .nextCountdown:after,
+ .nextCountdown__overtime:after {
+ content: '\200b';
+ }
+ .nextTitle {
+ color:$cyan-idle;
+ line-height: 100px;
+ }
+ .nextCountdown,
+ .nextCountdown__overtime {
+ font-size: 100px;
+ line-height: 1em;
+ }
+ .nextCountdown {
+ color: $cyan-active;
+ text-shadow: rgb(0,100,100) 0 0 20px;
+ }
+ .nextCountdown::before {
+ content: '-';
+ }
+ .nextCountdown__overtime {
+ color: darken($red-active, 10%);
+ }
+ .indicators {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+
+ .min,
+ .min__active,
+ .hours,
+ .hours__active {
+ border-radius: 50%;
+ position: absolute;
+ background: $red-idle;
+ }
+ .min,
+ .min__active {
+ min-height: $size-min;
+ width: $size-min;
+ top: calc(50% - #{$half_min});
+ left: calc(50% - #{$half_min});
+ }
+ .hours,
+ .hours__active{
+ min-height:$size-hours;
+ width: $size-hours;
+ top: calc(50% - #{$half_hours});
+ left: calc(50% - #{$half_hours});
+ }
+ .min__active,
+ .hours__active {
+ background: $red-active;
+ box-shadow: 0 0 10px 2px rgba(255,0,0,0.25);
+ }
+ }
+ }
+
+ /* ============= SCHEDULE STUFF =================*/
+
+ .scheduleContainer {
+ grid-area: schd;
+ margin: 50px 0;
+ padding-right: 50px;
+ font-family: digital-clock, monospace;
+ text-transform: uppercase;
+
+
+ .onAir,
+ .onAir__idle{
+ padding-bottom: 50px;
+ font-size: 190px;
+ line-height: 0.9em;
+ letter-spacing: 0.05em;
+ }
+ .onAir {
+ color: $red-active;
+ text-shadow: rgb(150,0,0) 0 0 20px;
+ }
+ .onAir__idle {
+ color: $red-idle;
+ }
+ .schedule {
+
+ ul {
+ color: $cyan-idle;
+ line-height: 1em;
+ list-style: none;
+ font-size: 40px;
+ }
+ li {
+ padding-bottom: 0.5em;
+ display: flex;
+ align-items: center;
+ gap: 20px;
+ }
+ .now {
+ color: $cyan-active;
+ text-shadow: rgb(0,100,100) 0 0 20px;
+ }
+ .next {
+ color: $red-active;
+ text-shadow: rgb(100,0,0) 0 0 20px;
+ }
+ .decorator,
+ .decorator__active,
+ .decorator__next {
+ aspect-ratio: 1;
+ border-radius: 50%;
+ background: $red-idle;
+ min-height: $size-hours;
+ max-height: $size-hours;
+ width: $size-hours;
+ }
+ .decorator__active {
+ background: $cyan-active;
+ box-shadow: 0 0 10px 2px rgba(0,255,255,0.25);
+ }
+ .decorator__next{
+ background: $red-active;
+ box-shadow: 0 0 10px 2px rgba(255,0,0,0.25);
+ }
+ }
+ }
+}
+
+@media only screen and (max-width: 1600px) {
+ .container {
+ display: grid;
+ grid-template-areas: "clck";
+ grid-template-columns: 100%;
+ place-content: center;
+ }
+ .scheduleContainer{
+ display: none;
+ }
+}
diff --git a/client/yarn.lock b/client/yarn.lock
index a15a4edc8..967a81dfc 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -3073,6 +3073,14 @@ anymatch@^3.0.3, anymatch@~3.1.1:
normalize-path "^3.0.0"
picomatch "^2.0.4"
+anymatch@~3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
+ integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
+ dependencies:
+ normalize-path "^3.0.0"
+ picomatch "^2.0.4"
+
aproba@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
@@ -3990,6 +3998,21 @@ check-types@^11.1.1:
resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.1.2.tgz#86a7c12bf5539f6324eb0e70ca8896c0e38f3e2f"
integrity sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ==
+"chokidar@>=3.0.0 <4.0.0":
+ version "3.5.2"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
+ integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==
+ dependencies:
+ anymatch "~3.1.2"
+ braces "~3.0.2"
+ glob-parent "~5.1.2"
+ is-binary-path "~2.1.0"
+ is-glob "~4.0.1"
+ normalize-path "~3.0.0"
+ readdirp "~3.6.0"
+ optionalDependencies:
+ fsevents "~2.3.2"
+
chokidar@^2.1.8:
version "2.1.8"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
@@ -6054,7 +6077,7 @@ fsevents@^1.2.7:
bindings "^1.5.0"
nan "^2.12.1"
-fsevents@^2.1.2, fsevents@^2.1.3, fsevents@~2.3.1:
+fsevents@^2.1.2, fsevents@^2.1.3, fsevents@~2.3.1, fsevents@~2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
@@ -6137,7 +6160,7 @@ glob-parent@^3.1.0:
is-glob "^3.1.0"
path-dirname "^1.0.0"
-glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0:
+glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0, glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
@@ -6608,6 +6631,11 @@ immer@8.0.1:
resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656"
integrity sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==
+immutable@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23"
+ integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==
+
import-cwd@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
@@ -10366,6 +10394,13 @@ readdirp@~3.5.0:
dependencies:
picomatch "^2.2.1"
+readdirp@~3.6.0:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
+ integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
+ dependencies:
+ picomatch "^2.2.1"
+
recursive-readdir@2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f"
@@ -10565,6 +10600,11 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
+resize-observer-polyfill@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
+ integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
+
resolve-cwd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
@@ -10811,6 +10851,14 @@ sass-loader@^10.0.5:
schema-utils "^3.0.0"
semver "^7.3.2"
+sass@^1.44.0:
+ version "1.44.0"
+ resolved "https://registry.yarnpkg.com/sass/-/sass-1.44.0.tgz#619aa0a2275c097f9af5e6b8fe8a95e3056430fb"
+ integrity sha512-0hLREbHFXGQqls/K8X+koeP+ogFRPF4ZqetVB19b7Cst9Er8cOR0rc6RU7MaI4W1JmUShd1BPgPoeqmmgMMYFw==
+ dependencies:
+ chokidar ">=3.0.0 <4.0.0"
+ immutable "^4.0.0"
+
sax@~1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
@@ -12131,6 +12179,13 @@ use-callback-ref@^1.2.3, use-callback-ref@^1.2.5:
resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.2.5.tgz#6115ed242cfbaed5915499c0a9842ca2912f38a5"
integrity sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==
+use-fit-text@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/use-fit-text/-/use-fit-text-2.4.0.tgz#d3d1cd72f6d29cfb2233ec0e0b38c6f25d319f67"
+ integrity sha512-Iy4LMrXcdxWlyZ5phntMpJMgyXGB1p3tV73y2r0QrZ6f/thPh+/QU3ie6RCXmjF8tHMs20FKMPskXeDYIla/Ww==
+ dependencies:
+ resize-observer-polyfill "^1.5.1"
+
use-memo-one@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.2.tgz#0c8203a329f76e040047a35a1197defe342fab20"
diff --git a/server/package.json b/server/package.json
index dca7b404e..3a1099646 100644
--- a/server/package.json
+++ b/server/package.json
@@ -1,6 +1,6 @@
{
"name": "ontime",
- "version": "0.4.1",
+ "version": "0.4.2",
"author": "Carlos Valente",
"description": "Time keeping for live events",
"repository": "https://github.com/cpvalente/ontime",
diff --git a/server/src/classes/EventTimer.js b/server/src/classes/EventTimer.js
index 2224b2d72..c7fc10943 100644
--- a/server/src/classes/EventTimer.js
+++ b/server/src/classes/EventTimer.js
@@ -63,6 +63,7 @@ export class EventTimer extends Timer {
nextPublicEventId = null;
numEvents = null;
_eventlist = null;
+ onAir = false;
constructor(httpServer, oscClient, config) {
// call super constructor
@@ -199,7 +200,7 @@ export class EventTimer extends Timer {
// send current timer
broadcastTimer() {
// through websockets
- this.io.emit('timer', this.getObject());
+ this.io.emit('timer', this.getTimes());
// through OSC, only if running
if (this.state === 'start' || this.state === 'roll') {
@@ -211,7 +212,7 @@ export class EventTimer extends Timer {
// broadcast state
broadcastState(update = true) {
- this.io.emit('timer', this.getObject(update));
+ this.io.emit('timer', this.getTimes(update));
this.io.emit('playstate', this.state);
this.io.emit('selected', {
id: this.selectedEventId,
@@ -224,6 +225,7 @@ export class EventTimer extends Timer {
this.io.emit('publicnext-id', this.nextPublicEventId);
this.io.emit('titles', this.titles);
this.io.emit('publictitles', this.titlesPublic);
+ this.io.emit('onAir', this.onAir);
}
// broadcast message
@@ -359,7 +361,7 @@ export class EventTimer extends Timer {
);
// send state
- socket.emit('timer', this.getObject());
+ socket.emit('timer', this.getTimes());
socket.emit('playstate', this.state);
socket.emit('selected-id', this.selectedEventId);
socket.emit('next-id', this.nextEventId);
@@ -385,7 +387,7 @@ export class EventTimer extends Timer {
/*******************************************/
// general playback state
socket.on('get-state', () => {
- socket.emit('timer', this.getObject());
+ socket.emit('timer', this.getTimes());
socket.emit('playstate', this.state);
socket.emit('selected-id', this.selectedEventId);
socket.emit('next-id', this.nextEventId);
@@ -400,7 +402,7 @@ export class EventTimer extends Timer {
});
socket.on('get-timer', () => {
- socket.emit('timer', this.getObject());
+ socket.emit('timer', this.getTimes());
});
socket.on('increment-timer', (data) => {
@@ -419,6 +421,15 @@ export class EventTimer extends Timer {
socket.emit('playstate', this.state);
});
+ socket.on('set-onAir', (data) => {
+ this.onAir = data;
+ this.broadcastThis('onAir', this.onAir);
+ });
+
+ socket.on('get-onAir', () => {
+ socket.emit('onAir', this.onAir);
+ });
+
/*******************************************/
// selection data
socket.on('get-selected', () => {
@@ -949,6 +960,14 @@ export class EventTimer extends Timer {
`;
}
+ /**
+ * @description Set onAir property of timer
+ * @param {boolean} onAir - whether flag is active
+ */
+ set onAir(onAir) {
+ this.onAir = onAir;
+ }
+
start() {
// if there is nothing selected, no nothing
if (this.selectedEventId == null) return;
diff --git a/server/src/classes/Timer.js b/server/src/classes/Timer.js
index 5ed019a14..8cf6eea38 100644
--- a/server/src/classes/Timer.js
+++ b/server/src/classes/Timer.js
@@ -139,8 +139,8 @@ export class Timer {
return this.duration - this.current;
}
- // getObject
- getObject(update = true) {
+ // get time object
+ getTimes(update = true) {
// update timer
if (update) this.update();
diff --git a/server/src/controllers/OscController.js b/server/src/controllers/OscController.js
index a9a59e1b4..5eff71e31 100644
--- a/server/src/controllers/OscController.js
+++ b/server/src/controllers/OscController.js
@@ -38,6 +38,12 @@ export const initiateOSC = (config) => {
// get second part (command)
switch (path.toLowerCase()) {
+ case 'onAir':
+ global.timer.onAir = true;
+ break;
+ case 'offAir':
+ global.timer.onAir = false;
+ break;
case 'start':
case 'play':
console.log('calling play');
diff --git a/server/src/controllers/playbackController.js b/server/src/controllers/playbackController.js
index 16b3d7f78..0e2477878 100644
--- a/server/src/controllers/playbackController.js
+++ b/server/src/controllers/playbackController.js
@@ -4,6 +4,22 @@ export const pbGet = async (req, res) => {
res.send(global.timer.playState);
};
+// Create controller for GET request to '/playback/onAir'
+// Turns onAir flag to true
+export const onAir = async (req, res) => {
+ console.log('Setting onAir to true');
+ global.timer.onAir = true;
+ res.sendStatus(200);
+};
+
+// Create controller for GET request to '/playback/onAir'
+// Turns onAir flag to true
+export const offAir = async (req, res) => {
+ console.log('Setting onAir to false');
+ global.timer.onAir = false;
+ res.sendStatus(200);
+};
+
// Create controller for GET request to '/playback/start'
// Starts timer object
export const pbStart = async (req, res) => {
diff --git a/server/src/routes/playbackRouter.js b/server/src/routes/playbackRouter.js
index 3b71b9c86..d31d1bc99 100644
--- a/server/src/routes/playbackRouter.js
+++ b/server/src/routes/playbackRouter.js
@@ -7,6 +7,12 @@ const playbackController = require('../controllers/playbackController');
// create route between controller and '/playback/' endpoint
router.get('/', playbackController.pbGet);
+// create route between controller and '/playback/onAir' endpoint
+router.get('/onAir', playbackController.onAir);
+
+// create route between controller and '/playback/offAir' endpoint
+router.get('/offAir', playbackController.offAir);
+
// create route between controller and '/playback/start' endpoint
router.get('/start', playbackController.pbStart);