diff --git a/client/src/App.jsx b/client/src/App.jsx
index 1aa7cb1b1..20d7f2d82 100644
--- a/client/src/App.jsx
+++ b/client/src/App.jsx
@@ -19,18 +19,17 @@ const SStageManager = withSocket(StageManager);
function App() {
return (
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/client/src/features/viewers/ViewWrapper.jsx b/client/src/features/viewers/ViewWrapper.jsx
index ab98ada94..96034b1df 100644
--- a/client/src/features/viewers/ViewWrapper.jsx
+++ b/client/src/features/viewers/ViewWrapper.jsx
@@ -1,9 +1,25 @@
import { useEffect, useState } from 'react';
+import { useQuery } from 'react-query';
+import { eventsNamespace, fetchAllEvents } from '../../app/api/eventsApi';
+import { fetchSettings, settingsNamespace } from '../../app/api/settingsApi';
import { useSocket } from '../../app/context/socketContext';
import { stringFromMillis } from '../../common/dateConfig';
const withSocket = (Component) => {
const WrappedComponent = (props) => {
+ const {
+ data: eventsData,
+ status: eventsDataStatus,
+ isError: eventsDataIsError,
+ } = useQuery(eventsNamespace, fetchAllEvents);
+ const {
+ data: genData,
+ status: genDataStatus,
+ isError: genDataIsError,
+ } = useQuery(settingsNamespace, fetchSettings);
+
+ const [events, setEvents] = useState([]);
+
const socket = useSocket();
const [pres, setPres] = useState({
text: '',
@@ -31,6 +47,13 @@ const withSocket = (Component) => {
subtitleNext: '',
presenterNext: '',
});
+ const [selectedId, setSelectedId] = useState({});
+ const [general, setGeneral] = useState({
+ title: '',
+ url: '',
+ publicInfo: '',
+ backstageInfo: '',
+ });
// Ask for update on load
useEffect(() => {
@@ -56,11 +79,16 @@ const withSocket = (Component) => {
setTimer({ ...data });
});
- // Handle timer
+ // Handle titles
socket.on('titles', (data) => {
setTitles({ ...data });
});
+ // Handle selected event
+ socket.on('selected-id', (data) => {
+ setSelectedId(data);
+ });
+
// Ask for up to date data
socket.emit('get-messages');
@@ -70,6 +98,9 @@ const withSocket = (Component) => {
// Ask for up titles
socket.emit('get-titles');
+ // Ask for up selected
+ socket.emit('get-selected-id');
+
// Clear listeners
return () => {
socket.off('messages-public');
@@ -77,9 +108,23 @@ const withSocket = (Component) => {
socket.off('messages-lower');
socket.off('timer');
socket.off('titles');
+ socket.off('selected-id');
};
}, [socket]);
+ // Filter events only to pass down
+ useEffect(() => {
+ if (eventsData == null) return;
+ const e = eventsData.filter((d) => d.type === 'event');
+ setEvents(e);
+ }, [eventsData]);
+
+ // Set general data
+ useEffect(() => {
+ if (genData == null) return;
+ setGeneral(genData);
+ }, [genData]);
+
/********************************************/
/*** + titleManager ***/
/*** WRAP INFORMATION RELATED TO TITLES ***/
@@ -114,6 +159,9 @@ const withSocket = (Component) => {
lower={lower}
title={titleManager}
time={timeManager}
+ events={events}
+ selectedId={selectedId}
+ general={general}
/>
);
};
diff --git a/client/src/features/viewers/backstage/StageManager.jsx b/client/src/features/viewers/backstage/StageManager.jsx
index 4f4caab7e..ce36a83ea 100644
--- a/client/src/features/viewers/backstage/StageManager.jsx
+++ b/client/src/features/viewers/backstage/StageManager.jsx
@@ -1,19 +1,27 @@
import QRCode from 'react-qr-code';
import { formatDisplay } from '../../../common/dateConfig';
import style from './StageManager.module.css';
+import TodayItem from './TodayItem';
export default function StageManager(props) {
- const { pres, publ, lower, title, time } = props;
+ const { pres, publ, lower, title, time, events, selectedId, general } = props;
+ // Format messages
const showPubl = publ.text !== '' && publ.visible;
const stageTimer =
time.currentSeconds != null && !isNaN(time.currentSeconds)
? formatDisplay(time.currentSeconds, true)
: '';
+ // Keep track of order
+ // 0 - events before
+ // 1 - running event
+ // 2 - future event
+ let selectedYet = 0;
+
return (
-
A hamburger life
+
{general.title}
Now
{title.titleNow}
@@ -29,32 +37,20 @@ export default function StageManager(props) {
Today
-
-
10:30
-
10:45
-
A dress to the nation
-
-
-
10:30
-
10:45
-
A dress to the nation
-
-
-
10:30
-
10:45
-
A dress to the nation
-
-
-
10:30
-
10:45
-
Are burguers real
-
-
-
10:30
-
10:45
-
Up to midnight
-
+ {events.map((e) => {
+ if (e.id === selectedId) selectedYet = 1;
+ else if (selectedYet === 1) selectedYet = 2;
+ return (
+
+ );
+ })}
+
@@ -84,16 +80,16 @@ export default function StageManager(props) {
Info
-
Water in the public bathroom
-
Wifi Password: bemywifi
-
Have a nice day
+
{general.backstageInfo}
-
+ {general.url != null && general.url !== '' && (
+
+ )}
diff --git a/client/src/features/viewers/backstage/StageManager.module.css b/client/src/features/viewers/backstage/StageManager.module.css
index 4960ae05e..c12b1d2ff 100644
--- a/client/src/features/viewers/backstage/StageManager.module.css
+++ b/client/src/features/viewers/backstage/StageManager.module.css
@@ -1,3 +1,5 @@
+@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600;800&display=swap');
+
.container__gray,
.container__grayFinished {
margin: 0;
@@ -30,8 +32,9 @@
.eventTitle {
grid-area: titl;
- font-size: 4vw;
+ font-size: 3vw;
font-weight: 600;
+ text-decoration: underline #ff7597 0.5vh;
padding-top: 0.2vh;
padding-left: 1vw;
}
@@ -206,15 +209,18 @@
grid-area: info;
display: grid;
+ grid-template-rows: 3vh minmax(0, 1fr);
grid-template-columns: 3fr 1fr;
grid-template-areas:
- 'titl qr'
- 'info qr';
+ 'titl .'
+ 'binf qr';
+ gap: 0.5vw;
}
.infoMessages {
- grid-area: info;
+ grid-area: binf;
font-size: 1.5vw;
+ white-space: pre-line;
}
.qr {
@@ -236,6 +242,7 @@
}
.clock {
+ font-family: 'Open Sans', sans-serif;
font-size: 3vw;
line-height: 3vw;
text-align: center;
diff --git a/client/src/features/viewers/backstage/TodayItem.jsx b/client/src/features/viewers/backstage/TodayItem.jsx
new file mode 100644
index 000000000..891d11de6
--- /dev/null
+++ b/client/src/features/viewers/backstage/TodayItem.jsx
@@ -0,0 +1,21 @@
+import { stringFromMillis } from '../../../common/dateConfig';
+import style from './StageManager.module.css';
+export default function TodayItem(props) {
+ const { selected, timeStart, timeEnd, title } = props;
+
+ // Format timers
+ const start = timeStart ? stringFromMillis(timeStart, false) : '';
+ const end = timeEnd ? stringFromMillis(timeEnd, false) : '';
+
+ // select styling
+ let selectStyle = style.entryPast;
+ if (selected === 1) selectStyle = style.entryNow;
+ else if (selected === 2) selectStyle = style.entryFuture;
+ return (
+
+
{start}
+
{end}
+
{title}
+
+ );
+}
diff --git a/server/controllers/settingsController.js b/server/controllers/settingsController.js
new file mode 100644
index 000000000..066e3557d
--- /dev/null
+++ b/server/controllers/settingsController.js
@@ -0,0 +1,143 @@
+// get database
+const db = require('../app.js').db;
+const table = 'settings';
+
+function getSettings() {
+ return db.get(table).value();
+}
+
+// Create controller for GET request to 'settings'
+// Returns ACK message
+exports.getAll = async (req, res) => {
+ const settings = getSettings();
+ if (settings) res.json(settings);
+ else res.sendStatus(400);
+};
+
+// Create controller for POST request to 'settings'
+// Returns ACK message
+exports.post = async (req, res) => {
+ if (!req.body) {
+ res.status(400).send(`No object found in request`);
+ return;
+ }
+ // TODO: validate data
+ try {
+ db.get(table)
+ .assign({ ...req.body })
+ .write();
+ res.sendStatus(200);
+ } catch (error) {
+ res.status(400).send(error);
+ }
+};
+
+// Create controller for GET request to 'settings/title'
+// Returns ACK message
+exports.titleGet = async (req, res) => {
+ const settings = getSettings();
+
+ if (settings) res.json(settings.title);
+ else res.sendStatus(400);
+};
+
+// Create controller for POST request to 'settings/title'
+// Returns ACK message
+exports.titlePost = async (req, res) => {
+ if (!req.body) {
+ res.status(400).send(`No object found in request`);
+ return;
+ }
+
+ // TODO: validate data
+ try {
+ db.get(table).assign({ title: req.body.title }).write();
+ res.sendStatus(200);
+ } catch (error) {
+ res.status(400).send(error);
+ }
+};
+
+// Create controller for GET request to '/settings/url'
+// Returns ACK message
+exports.urlGet = async (req, res) => {
+ let event = getEventOptions();
+
+ if (event) res.json(event.url);
+ else res.sendStatus(400);
+};
+
+// Create controller for POST request to '/settings/url'
+// Returns ACK message
+exports.urlPost = async (req, res) => {
+ if (!req.body) {
+ res.status(400).send(`No object found in request`);
+ return;
+ }
+
+ // TODO: validate data
+ try {
+ db.get(table).assign({ url: req.body.url }).write();
+ res.sendStatus(200);
+ } catch (error) {
+ res.status(400).send(error);
+ }
+};
+
+// Create controller for GET request to 'settings/publicInfo'
+// Returns ACK message
+exports.publicInfoGet = async (req, res) => {
+ const settings = getSettings();
+
+ if (settings) res.json(settings.publicInfo);
+ else res.sendStatus(400);
+};
+
+// Create controller for POST request to '/settings/publicInfo'
+// Returns ACK message
+exports.publicInfoPost = async (req, res) => {
+ if (!req.body) {
+ res.status(400).send(`No object found in request`);
+ return;
+ }
+
+ // TODO: validate data
+ try {
+ db.get(table).assign({ publicInfo: req.body.publicInfo }).write();
+ res.sendStatus(200);
+ } catch (error) {
+ res.status(400).send(error);
+ }
+};
+
+// Create controller for GET request to 'settings/backstageInfo'
+// Returns ACK message
+exports.backstageInfoGet = async (req, res) => {
+ const settings = getSettings();
+
+ if (settings) res.json(settings.backstageInfo);
+ else res.sendStatus(400);
+};
+
+// Create controller for POST request to '/settings/info'
+// Returns ACK message
+exports.backstageInfoPost = async (req, res) => {
+ if (!req.body) {
+ res.status(400).send(`No object found in request`);
+ return;
+ }
+
+ // TODO: validate data
+ try {
+ db.get(table).assign({ backstageInfo: req.body.backstageInfo }).write();
+ res.sendStatus(200);
+ } catch (error) {
+ res.status(400).send(error);
+ }
+};
+
+// Create controller for GET request to 'settings/osc'
+// Returns ACK message
+exports.osc = async (req, res) => {
+ res.send('Not yet implemented').status(500);
+};