refractor + style tweaks

- bugfix on lower
- delay between 0-60 minutes
- action buttons in dropdown
- added button for toggling element visibility
- several css and code cleanups
- renamed settingsAPI
This commit is contained in:
cv
2021-04-24 10:32:19 +02:00
parent 11fca1ab6d
commit ea92fc7de7
44 changed files with 835 additions and 572 deletions
+9 -9
View File
@@ -1,4 +1,4 @@
import { Route, Switch } from 'react-router-dom';
import { Route } from 'react-router-dom';
import './App.css';
import Editor from './features/editors/Editor';
import DefaultPresenter from './features/viewers/DefaultPresenter';
@@ -22,14 +22,14 @@ function App() {
return (
<SocketProvider>
<QueryClientProvider client={queryClient}>
<div className='App'>
<Route exact path='/' component={SDefault} />
<Route exact path='/sm' component={SStageManager} />
<Route exact path='/speaker' component={SSpeaker} />
<Route exact path='/speakersimple' component={SSpeakerSimple} />
<Route exact path='/editor' component={Editor} />
<Route path='/lower' component={SLowerThird} />
</div>
<div className='App'>
<Route exact path='/' component={PresenterView} />
<Route exact path='/sm' component={SStageManager} />
<Route exact path='/speaker' component={SSpeaker} />
<Route exact path='/speakersimple' component={SSpeakerSimple} />
<Route exact path='/editor' component={Editor} />
<Route path='/lower' component={SLowerThird} />
</div>
</QueryClientProvider>
</SocketProvider>
);
+21
View File
@@ -0,0 +1,21 @@
import axios from 'axios';
import { NODE_PORT } from './apiConstants';
// get origin from URL
const serverURL = window.location.origin.replace(
window.location.port,
`${NODE_PORT}/`
);
export const eventNamespace = 'event';
export const eventURL = serverURL + eventNamespace;
export const fetchEvent = async () => {
const res = await axios.get(eventURL);
return res.data;
};
export const postEvent = async (data) => {
const res = await axios.post(eventURL, data);
return res;
};
-23
View File
@@ -1,23 +0,0 @@
import axios from 'axios';
import { NODE_PORT } from '../api/apiConstants';
// get origin from URL
const serverURL = window.location.origin.replace(
window.location.port,
`${NODE_PORT}/`
);
export const settingsNamespace = 'settings';
export const settingsURL = serverURL + settingsNamespace;
export const fetchSettings = async () => {
const res = await axios.get(settingsURL);
return res.data;
};
export const postSettings = async (data) => {
console.log('debug sending', data)
console.log('debug await', await axios.post(settingsURL, data))
const res = await axios.post(settingsURL, data);
return res;
};
@@ -8,6 +8,7 @@ export default function AddIconBtn(props) {
icon={<FiPlus />}
colorScheme='blue'
onClick={props.clickHandler}
_focus={{ boxShadow: 'none' }}
/>
);
}
@@ -8,6 +8,7 @@ export default function BlockIconBtn(props) {
icon={<FiMinusCircle />}
colorScheme='purple'
onClick={props.clickHandler}
_focus={{ boxShadow: 'none' }}
/>
);
}
@@ -9,6 +9,7 @@ export default function CollapseIconBtn(props) {
colorScheme='orange'
variant={props.active ? 'solid' : 'outline'}
onClick={props.clickHandler}
_focus={{ boxShadow: 'none' }}
/>
);
}
@@ -8,6 +8,7 @@ export default function DelayIconBtn(props) {
icon={<FiClock />}
colorScheme='yellow'
onClick={props.clickHandler}
_focus={{ boxShadow: 'none' }}
/>
);
}
@@ -8,6 +8,7 @@ export default function DeleteIconBtn(props) {
icon={<FiMinus />}
colorScheme='red'
onClick={props.clickHandler}
_focus={{ boxShadow: 'none' }}
/>
);
}
@@ -5,11 +5,12 @@ export default function NextIconBtn(props) {
return (
<IconButton
icon={<FiSkipForward />}
colorScheme= 'whiteAlpha'
colorScheme='whiteAlpha'
backgroundColor='#ffffff05'
variant= 'outline'
variant='outline'
onClick={props.clickHandler}
width={90}
_focus={{ boxShadow: 'none' }}
/>
);
}
@@ -8,7 +8,8 @@ export default function PauseIconBtn(props) {
colorScheme='orange'
variant={props.active ? 'solid' : 'outline'}
onClick={props.clickHandler}
width={90}
width={120}
_focus={{ boxShadow: 'none' }}
/>
);
}
@@ -5,11 +5,12 @@ export default function PrevIconBtn(props) {
return (
<IconButton
icon={<FiSkipBack />}
colorScheme= 'whiteAlpha'
colorScheme='whiteAlpha'
backgroundColor='#ffffff05'
variant= 'outline'
variant='outline'
onClick={props.clickHandler}
width={90}
_focus={{ boxShadow: 'none' }}
/>
);
}
@@ -0,0 +1,16 @@
import { IconButton } from '@chakra-ui/button';
import { FiRefreshCcw } from 'react-icons/fi';
export default function ReloadIconButton(props) {
return (
<IconButton
icon={<FiRefreshCcw />}
colorScheme='whiteAlpha'
backgroundColor='#ffffff05'
variant='outline'
onClick={props.clickHandler}
width={90}
_focus={{ boxShadow: 'none' }}
/>
);
}
@@ -8,7 +8,9 @@ export default function RollIconBtn(props) {
colorScheme='blue'
variant={props.active ? 'solid' : 'outline'}
onClick={props.clickHandler}
width={90}
width={120}
disabled
_focus={{ boxShadow: 'none' }}
/>
);
}
@@ -9,6 +9,7 @@ export default function SettingsIconBtn(props) {
isRound
variant='outline'
onClick={props.clickHandler}
_focus={{ boxShadow: 'none' }}
/>
);
}
@@ -8,7 +8,8 @@ export default function StartIconBtn(props) {
colorScheme='green'
variant={props.active ? 'solid' : 'outline'}
onClick={props.clickHandler}
width={90}
width={120}
_focus={{ boxShadow: 'none' }}
/>
);
}
@@ -0,0 +1,16 @@
import { IconButton } from '@chakra-ui/button';
import { FiCornerLeftUp } from 'react-icons/fi';
export default function UnloadIconBtn(props) {
return (
<IconButton
icon={<FiCornerLeftUp />}
colorScheme='whiteAlpha'
backgroundColor='#ffffff05'
variant='outline'
onClick={props.clickHandler}
width={90}
_focus={{ boxShadow: 'none' }}
/>
);
}
@@ -9,6 +9,7 @@ export default function VisibleIconBtn(props) {
colorScheme='blue'
variant={props.active ? 'solid' : 'outline'}
onClick={props.clickHandler}
_focus={{ boxShadow: 'none' }}
/>
);
}
@@ -1,5 +1,4 @@
import EditableTimer from '../../input/EditableTimer';
import DelayValue from '../../input/DelayValue';
import { showWarningToast } from '../../helpers/toastManager';
export default function EventTimes(props) {
@@ -24,7 +23,6 @@ export default function EventTimes(props) {
return (
<>
<DelayValue delay={delay} />
<EditableTimer
name='timeStart'
validate={handleValidate}
+2 -2
View File
@@ -1,11 +1,11 @@
import { millisToMinutes } from '../dateConfig';
import style from './EditableTimer.module.css';
import style from '../../features/editors/list/Block.module.css';
export default function DelayValue(props) {
const { delay } = props;
const delayed = delay > 0;
return (
<div className={delayed ? style.delayValue : undefined}>
<div className={style.delayValue}>
{delayed && <span>{`+ ${millisToMinutes(delay)}`}</span>}
</div>
);
+10 -2
View File
@@ -1,8 +1,10 @@
import { Editable, EditableInput, EditablePreview } from '@chakra-ui/editable';
import { useState } from 'react';
import style from './EditableText.module.css';
export default function EditableText(props) {
const { label, defaultValue, placeholder, submitHandler } = props;
const [text, setText] = useState(props.defaultValue || '');
const handleSubmit = (submitedVal) => {
// No need to update if it hasnt changed
@@ -11,19 +13,25 @@ export default function EditableText(props) {
submitHandler(submitedVal);
};
const handleChange = (val) => {
if (val.length < 40) setText(val);
console.log(val.length);
};
return (
<div className={style.block}>
<span className={props.underlined ? style.titleUnderlined : style.title}>
{label}
</span>
<Editable
onChange={(v) => handleChange(v)}
onSubmit={(v) => handleSubmit(v)}
defaultValue={defaultValue}
value={text}
placeholder={placeholder}
className={style.inline}
>
<EditablePreview />
<EditableInput className={style.editable13} />
<EditableInput />
</Editable>
</div>
);
@@ -14,13 +14,11 @@
.block {
display: 'block';
overflow: hidden;
max-width: 100%;
}
.inline {
display: inline;
}
.editable13 {
width: '13em';
min-width: '13em';
width: max-content;
}
@@ -1,6 +1,5 @@
.time {
background-color: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 4px;
width: 5em;
height: fit-content;
@@ -10,17 +9,9 @@
.delayedEditable {
text-align: center;
border-radius: 4px;
border: 1px solid rgba(255, 255, 255, 0.05);
}
.delayedEditable {
border: 1px solid #d69e2e55;
}
.delayValue {
color: #fff;
border-radius: 4px;
padding: 0.3em 0;
text-align: center;
align-self: flex-start;
background-color: #d69e2e55;
}
+4 -2
View File
@@ -22,14 +22,16 @@ export default function TimeInput(props) {
}, [props.value]);
const handleChange = (val) => {
if (val <= 999) setValue(val);
if (val <= 666) setValue(val);
};
const handleSubmit = (val) => {
if (val === props.value) return;
if (val === '') setValue(0);
if (val > 0 && val < 60) {
if (val >= 0 && val <= 60) {
props.submitHandler(val);
} else {
setValue(60);
}
};
@@ -8,6 +8,8 @@ import PauseIconBtn from '../../common/components/buttons/PauseIconBtn';
import PrevIconBtn from '../../common/components/buttons/PrevIconBtn';
import NextIconBtn from '../../common/components/buttons/NextIconBtn';
import RollIconBtn from '../../common/components/buttons/RollIconBtn';
import UnloadIconBtn from '../../common/components/buttons/UnloadIconBtn';
import ReloadIconButton from '../../common/components/buttons/ReloadIconBtn';
export default function PlaybackControl() {
const socket = useSocket();
@@ -19,6 +21,14 @@ export default function PlaybackControl() {
expectedFinish: null,
});
const resetTimer = () => {
setTimer({
currentSeconds: null,
startedAt: null,
expectedFinish: null,
});
};
// handle incoming messages
useEffect(() => {
if (socket == null) return;
@@ -56,9 +66,11 @@ export default function PlaybackControl() {
break;
case 'previous':
socket.emit('set-playstate', 'previous');
resetTimer();
break;
case 'next':
socket.emit('set-playstate', 'next');
resetTimer();
break;
default:
break;
@@ -93,13 +105,17 @@ export default function PlaybackControl() {
active={playback === 'pause'}
clickHandler={() => playbackControl('pause')}
/>
<PrevIconBtn clickHandler={() => playbackControl('previous')} />
<NextIconBtn clickHandler={() => playbackControl('next')} />
<RollIconBtn
active={playback === 'roll'}
clickHandler={() => playbackControl('roll')}
/>
</div>
<div className={style.playbackContainer}>
<PrevIconBtn clickHandler={() => playbackControl('previous')} />
<NextIconBtn clickHandler={() => playbackControl('next')} />
<UnloadIconBtn clickHandler={() => playbackControl('unload')} />
<ReloadIconButton clickHandler={() => playbackControl('reload')} />
</div>
</div>
);
}
@@ -7,7 +7,7 @@
padding: 2vh;
display: grid;
grid-template-rows: 1fr 1fr 1fr;
grid-template-rows: 38vh 1fr 1fr;
grid-template-columns: 1fr 25vw 25vw 5vw;
grid-template-areas:
'even mess play sett'
@@ -67,7 +67,6 @@
.mainContainer > div {
border-radius: 0.5em;
height: 100%;
margin-top: 1.5vh;
background-color: rgba(255, 255, 255, 0.13);
padding: 0.8em 1.5em;
@@ -0,0 +1,49 @@
import { Menu, MenuButton, MenuItem, MenuList } from '@chakra-ui/menu';
import { IconButton } from '@chakra-ui/button';
import { FiZap, FiPlus, FiMinus, FiMinusCircle, FiClock } from 'react-icons/fi';
export default function ActionButtons(props) {
const { showDel, showAdd, showDelay, showBlock } = props;
const menuStyle = {
color: '#000000',
backgroundColor: 'rgba(255,255,255,0.67)',
};
return (
<Menu>
<MenuButton
as={IconButton}
aria-label='Options'
size='xs'
icon={<FiZap />}
_hover={{ bg: 'pink.500' }}
_expanded={{ bg: 'pink.300' }}
_focus={{ boxShadow: 'none' }}
colorScheme='pink'
/>
<MenuList style={menuStyle}>
{showDel && (
<MenuItem icon={<FiMinus />} onClick={props.deleteHandler}>
Delete
</MenuItem>
)}
{showAdd && (
<MenuItem icon={<FiPlus />} onClick={props.addHandler}>
Event next
</MenuItem>
)}
{showDelay && (
<MenuItem icon={<FiClock />} onClick={props.delayHandler}>
Delay next
</MenuItem>
)}
{showBlock && (
<MenuItem icon={<FiMinusCircle />} onClick={props.blockHandler}>
Block next
</MenuItem>
)}
</MenuList>
</Menu>
);
}
@@ -27,8 +27,7 @@
gap: 0.5em;
align-content: center;
align-self: flex-start;
opacity: 0.4;
opacity: 0.8;
transition: linear 0.1s;
}
@@ -52,7 +51,8 @@
padding-left: 1em;
display: grid;
grid-template-columns: 3em auto auto 1fr auto;
grid-template-columns: auto 2em auto auto 1fr auto;
justify-content: center;
align-content: center;
align-items: baseline;
@@ -74,6 +74,45 @@
);
}
.drag {
color: rgba(255, 255, 255, 0.37);
align-self: center;
/* cursor: grab; Later */
}
.indicators {
display: flex;
flex-direction: column;
align-self: flex-start;
font-size: 0.75em;
overflow: hidden;
min-width: 0;
justify-content: center;
}
.next {
color: #4bffabcc;
width: max-content;
padding: 0 0.2em;
}
.nextDisabled {
color: rgba(255, 255, 255, 0.17);
}
.delayValue {
color: #d69e2eaa;
padding: 0 0.2em;
text-align: center;
align-self: flex-start;
width: max-content;
}
.next:after,
.delayValue:after {
content: '\200b';
}
.titleContainer,
.detailedContainer {
background-color: rgba(255, 255, 255, 0.05);
@@ -124,4 +163,4 @@
);
justify-content: space-between;
}
}
@@ -1,6 +1,4 @@
import AddIconBtn from '../../../common/components/buttons/AddIconBtn';
import DelayIconBtn from '../../../common/components/buttons/DelayIconBtn';
import DeleteIconBtn from '../../../common/components/buttons/DeleteIconBtn';
import ActionButtons from './ActionButtons';
import style from './Block.module.css';
export default function BlockBlock(props) {
@@ -19,9 +17,15 @@ export default function BlockBlock(props) {
return (
<div className={style.block}>
<div className={style.actionOverlay}>
<DeleteIconBtn clickHandler={deleteHandler} />
<AddIconBtn clickHandler={addHandler} />
<DelayIconBtn clickHandler={delayHandler} />
<ActionButtons
showDel
deleteHandler={deleteHandler}
showAdd
addHandler={addHandler}
showDelay
delayHandler={delayHandler}
showBlock
/>
</div>
</div>
);
@@ -1,8 +1,7 @@
import AddIconBtn from '../../../common/components/buttons/AddIconBtn';
import DeleteIconBtn from '../../../common/components/buttons/DeleteIconBtn';
import { millisToMinutes } from '../../../common/dateConfig';
import TimeInput from '../../../common/input/TimeInput';
import style from './Block.module.css';
import ActionButtons from './ActionButtons';
export default function DelayBlock(props) {
const { data, eventsHandler, index } = props;
@@ -13,6 +12,7 @@ export default function DelayBlock(props) {
const submitHandler = (value) => {
// convert to ms and patch
console.log('debug adding', { id: data.id, duration: value * 1000 * 60 });
eventsHandler('patch', { id: data.id, duration: value * 1000 * 60 });
};
@@ -27,8 +27,12 @@ export default function DelayBlock(props) {
submitHandler={submitHandler}
/>
<div className={style.actionOverlay}>
<DeleteIconBtn clickHandler={deleteHandler} />
<AddIconBtn clickHandler={addHandler} />
<ActionButtons
showDel
deleteHandler={deleteHandler}
showAdd
addHandler={addHandler}
/>
</div>
</div>
);
+35 -13
View File
@@ -1,16 +1,16 @@
import { FiChevronDown, FiChevronUp } from 'react-icons/fi';
import { FiChevronDown, FiChevronUp, FiMoreVertical } from 'react-icons/fi';
import { useState } from 'react';
import AddIconBtn from '../../../common/components/buttons/AddIconBtn';
import BlockIconBtn from '../../../common/components/buttons/BlockIconBtn';
import DelayIconBtn from '../../../common/components/buttons/DelayIconBtn';
import DeleteIconBtn from '../../../common/components/buttons/DeleteIconBtn';
import EventTimes from '../../../common/components/eventTimes/EventTimes';
import { showErrorToast } from '../../../common/helpers/toastManager';
import style from './Block.module.css';
import EditableText from '../../../common/input/EditableText';
import DelayValue from '../../../common/input/DelayValue';
import ActionButtons from './ActionButtons';
import VisibleIconBtn from '../../../common/components/buttons/VisibleIconBtn';
export default function EventBlock(props) {
const { data, selected, delay, index, eventsHandler } = props;
const [visible, setVisible] = useState(false);
const [more, setMore] = useState(false);
@@ -52,8 +52,18 @@ export default function EventBlock(props) {
updateValues('presenter', v);
};
// TODO: implement functionality to select next
let isNext = false;
return (
<div className={selected ? style.eventRowActive : style.eventRow}>
<span className={style.drag}>
<FiMoreVertical />
</span>
<div className={style.indicators}>
<div className={isNext ? style.next : style.nextDisabled}>Next</div>
<DelayValue delay={delay} />
</div>
<EventTimes
updateValues={updateValues}
timeStart={data.timeStart}
@@ -67,22 +77,22 @@ export default function EventBlock(props) {
label='Title'
defaultValue={data.title}
placeholder='Add Title'
underlined
submitHandler={handleTitleSubmit}
underlined
/>
<EditableText
label='Subtitle'
defaultValue={data.subtitle}
placeholder='Add Subtitle'
underlined
submitHandler={handleSubtitleSubmit}
underlined
/>
<EditableText
label='Presenter'
defaultValue={data.subtitle}
defaultValue={data.presenter}
placeholder='Add Presenter name'
underlined
submitHandler={handlePresenterSubmit}
underlined
/>
</div>
) : (
@@ -92,6 +102,7 @@ export default function EventBlock(props) {
defaultValue={data.title}
placeholder='Add Title'
submitHandler={handleTitleSubmit}
isTight
/>
</div>
)}
@@ -100,10 +111,21 @@ export default function EventBlock(props) {
</div>
</div>
<div className={style.actionOverlay}>
<DeleteIconBtn clickHandler={deleteHandler} />
<AddIconBtn clickHandler={addHandler} />
<DelayIconBtn clickHandler={delayHandler} />
<BlockIconBtn clickHandler={blockHandler} />
<VisibleIconBtn
clickHandler={() => setVisible(!visible)}
active={visible}
/>
<ActionButtons
showDel
deleteHandler={deleteHandler}
showAdd
addHandler={addHandler}
showDelay
delayHandler={delayHandler}
showBlock
blockHandler={blockHandler}
showPublic
/>
</div>
</div>
);
@@ -24,11 +24,14 @@ export default function EventListWrapper() {
// we optimistically update here
onMutate: async (newEvent) => {
// cancel ongoing queries
queryClient.cancelQueries(eventsNamespace);
queryClient.cancelQueries(eventsNamespace, { exact: true });
// Snapshot the previous value
const previousEvents = queryClient.getQueryData(eventsNamespace);
let previousEvents = queryClient.getQueryData(eventsNamespace);
if (previousEvents == null) {
await queryClient.refetchQueries();
previousEvents = queryClient.getQueryData(eventsNamespace);
}
// optimistically update object, temp ID until refetch
let optimistic = [...previousEvents];
optimistic.splice(newEvent.order, 0, {
@@ -8,7 +8,7 @@
border-radius: 4px;
padding: 8px;
overflow-y: scroll;
height: 71vh;
height: 73vh;
}
.empty {
@@ -17,6 +17,9 @@ export default function EventListMenu(props) {
variant: 'outline',
colorScheme: 'whiteAlpha',
backgroundColor: '#ffffff05',
_hover: { bg: 'blue.800' },
_expanded: { bg: 'blue.400' },
_focus: { boxShadow: 'none' },
};
const menuStyle = {
+10 -10
View File
@@ -15,15 +15,15 @@ import {
Textarea,
} from '@chakra-ui/react';
import {
fetchSettings,
postSettings,
settingsNamespace,
} from '../../app/api/settingsApi';
fetchEvent,
postEvent,
eventNamespace,
} from '../../app/api/eventApi';
import { useQuery } from 'react-query';
import { useEffect, useState } from 'react';
export default function SettingsModal(props) {
const { data, status, isError } = useQuery(settingsNamespace, fetchSettings);
const { data, status, isError } = useQuery(eventNamespace, fetchEvent);
const [formData, setFormData] = useState({
title: '',
url: '',
@@ -48,7 +48,7 @@ export default function SettingsModal(props) {
event.preventDefault();
setSubmitting(true);
await postSettings(formData).then(setSubmitting(false)).then(onClose);
await postEvent(formData).then(setSubmitting(false)).then(onClose);
};
return (
@@ -77,7 +77,7 @@ export default function SettingsModal(props) {
size='sm'
name='title'
placeholder='Event Title'
autocomplete='off'
autoComplete='off'
value={formData.title}
onChange={(event) =>
setFormData({ ...formData, title: event.target.value })
@@ -97,7 +97,7 @@ export default function SettingsModal(props) {
size='sm'
name='url'
placeholder='www.onsite.no'
autocomplete='off'
autoComplete='off'
value={formData.url}
onChange={(event) =>
setFormData({ ...formData, url: event.target.value })
@@ -117,7 +117,7 @@ export default function SettingsModal(props) {
size='sm'
name='pubInfo'
placeholder='Information to be shown on public screens'
autocomplete='off'
autoComplete='off'
value={formData.publicInfo}
onChange={(event) =>
setFormData({
@@ -140,7 +140,7 @@ export default function SettingsModal(props) {
size='sm'
name='backstageInfo'
placeholder='Information to be shown on backstage screens'
autocomplete='off'
autoComplete='off'
value={formData.backstageInfo}
onChange={(event) =>
setFormData({
+35 -8
View File
@@ -1,7 +1,7 @@
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 { fetchEvent, eventNamespace } from '../../app/api/eventApi';
import { useSocket } from '../../app/context/socketContext';
import { stringFromMillis } from '../../common/dateConfig';
@@ -16,9 +16,10 @@ const withSocket = (Component) => {
data: genData,
status: genDataStatus,
isError: genDataIsError,
} = useQuery(settingsNamespace, fetchSettings);
} = useQuery(eventNamespace, fetchEvent);
const [events, setEvents] = useState([]);
const [publicEvents, setPublicEvents] = useState([]);
const [backstageEvents, setBackstageEvents] = useState([]);
const socket = useSocket();
const [pres, setPres] = useState({
@@ -54,6 +55,7 @@ const withSocket = (Component) => {
publicInfo: '',
backstageInfo: '',
});
const [playback, setPlayback] = useState(null);
// Ask for update on load
useEffect(() => {
@@ -79,6 +81,11 @@ const withSocket = (Component) => {
setTimer({ ...data });
});
// Handle playstate
socket.on('playstate', (data) => {
setPlayback(data);
});
// Handle titles
socket.on('titles', (data) => {
setTitles({ ...data });
@@ -95,6 +102,12 @@ const withSocket = (Component) => {
// Ask for up to data
socket.emit('get-presenter');
// ask for timer
socket.emit('get-timer');
// ask for playstate
socket.emit('get-playstate');
// Ask for up titles
socket.emit('get-titles');
@@ -107,6 +120,7 @@ const withSocket = (Component) => {
socket.off('messages-presenter');
socket.off('messages-lower');
socket.off('timer');
socket.off('playstate');
socket.off('titles');
socket.off('selected-id');
};
@@ -117,9 +131,11 @@ const withSocket = (Component) => {
if (eventsData == null) return;
// filter just events with title
const e = eventsData.filter((d) => d.type === 'event' && d.title !== '');
const pe = eventsData.filter((d) => d.type === 'event' && d.title !== '');
setPublicEvents(pe);
setEvents(e);
// everything goes backstage
setBackstageEvents(eventsData);
}, [eventsData]);
// Set general data
@@ -133,12 +149,17 @@ const withSocket = (Component) => {
/*** WRAP INFORMATION RELATED TO TITLES ***/
/*** ---------------------------------- ***/
/********************************************/
// is there a now field?
let showNow = true;
if (!titles.titleNow && !titles.subtitleNow && !titles.presenterNow)
showNow = false;
// is there a next field?
let showNext = true;
if (!titles.titleNext && !titles.subtitleNext && !titles.presenterNext)
showNext = false;
const titleManager = { ...titles, showNext: showNext };
const titleManager = { ...titles, showNow: showNow, showNext: showNext };
/******************************************/
/*** + timeManager ***/
@@ -152,7 +173,12 @@ const withSocket = (Component) => {
// get clock
let clock = stringFromMillis(timer.clock);
const timeManager = { ...timer, finished: finished, clock: clock };
const timeManager = {
...timer,
finished: finished,
clock: clock,
playstate: playback,
};
return (
<Component
@@ -162,7 +188,8 @@ const withSocket = (Component) => {
lower={lower}
title={titleManager}
time={timeManager}
events={events}
events={publicEvents}
backstageEvents={backstageEvents}
selectedId={selectedId}
general={general}
/>
@@ -5,7 +5,7 @@ import Paginator from './Paginator';
import NavLogo from '../../../common/components/nav/NavLogo';
export default function StageManager(props) {
const { pres, publ, lower, title, time, events, selectedId, general } = props;
const { publ, title, time, events, selectedId, general } = props;
// Format messages
const showPubl = publ.text !== '' && publ.visible;
@@ -19,18 +19,24 @@ export default function StageManager(props) {
<NavLogo />
<div className={style.eventTitle}>{general.title}</div>
<div className={style.nowContainer}>
<div className={style.label}>Now</div>
<div className={style.nowTitle}>{title.titleNow}</div>
<div className={style.nowSubtitle}>{title.subtitleNow}</div>
<div className={style.nowPresenter}>{title.presenterNow}</div>
</div>
<div className={style.nextContainer}>
<div className={style.label}>Next</div>
<div className={style.nextTitle}>{title.titleNext}</div>
<div className={style.nextSubtitle}>{title.subtitleNext}</div>
<div className={style.nextPresenter}>{title.presenterNext}</div>
</div>
{title.showNow && (
<div className={style.nowContainer}>
<div className={style.label}>Now</div>
<div className={style.nowTitle}>{title.titleNow}</div>
<div className={style.nowSubtitle}>{title.subtitleNow}</div>
<div className={style.nowPresenter}>{title.presenterNow}</div>
</div>
)}
{title.showNext && (
<div className={style.nextContainer}>
<div className={style.label}>Next</div>
<div className={style.nextTitle}>{title.titleNext}</div>
<div className={style.nextSubtitle}>{title.subtitleNext}</div>
<div className={style.nextPresenter}>{title.presenterNext}</div>
</div>
)}
<div className={style.todayContainer}>
<div className={style.label}>Today</div>
<div className={style.entriesContainer}>
+7 -5
View File
@@ -2,16 +2,18 @@ import { AnimatePresence, motion } from 'framer-motion';
import style from './Lower.module.css';
export default function Lower(props) {
const { pres, publ, lower, title, time, events, selectedId, general } = props;
const { lower, title, time, general } = props;
// getting config from URL
// like http://localhost:3000/lower?bg=ff2&text=f00&size=0.6&transition=5
// getting config from URL: preset, size, transition, bg, text, key
// eg. http://localhost:3000/lower?bg=ff2&text=f00&size=0.6&transition=5
let params = new URLSearchParams(props.location.search);
// Preset: choose from an animation preset, not yet
const preset = params.get('preset') ? params.get('preset') : 1;
const size = params.get('size') ? params.get('size') : 1;
const transitionIn = params.get('transition') ? params.get('transition') : 3;
const textColour = params.get('bg') ? `#${params.get('text')}` : '#fffffa';
const textColour = params.get('text') ? `#${params.get('text')}` : '#fffffa';
const bgColour = params.get('bg') ? `#${params.get('bg')}` : '#00000033';
const key = params.get('key') ? `#${params.get('key')}` : 'null';
@@ -87,7 +89,7 @@ export default function Lower(props) {
},
},
};
return (
<div
className={style.lowerThird}
@@ -11,7 +11,7 @@
.lowerContainer {
position: absolute;
top: 70vh;
top: 75vh;
background-color: #00000033;
padding: 1vw 1vw 1vw 0;
@@ -4,9 +4,10 @@ import NavLogo from '../../../common/components/nav/NavLogo';
import style from './PresenterView.module.css';
export default function PresenterView(props) {
const { pres, publ, lower, title, time } = props;
const { pres, title, time } = props;
let showOverlay = pres.text !== '' && pres.visible;
const showOverlay = pres.text !== '' && pres.visible;
const isPlaying = time.playstate === 'start';
return (
<div
@@ -33,14 +34,18 @@ export default function PresenterView(props) {
{time.finished ? (
<div className={style.finished}>TIME UP</div>
) : (
<div className={style.countdown}>
<div className={isPlaying ? style.countdown : style.countdownPaused}>
<Countdown time={time.currentSeconds} hideZeroHours />
</div>
)}
</div>
{!time.finished && (
<div className={style.progressContainer}>
<div
className={
isPlaying ? style.progressContainer : style.progressContainerPaused
}
>
<MyProgressBar
now={time.currentSeconds}
complete={time.durationSeconds}
@@ -49,12 +54,14 @@ export default function PresenterView(props) {
</div>
)}
<div className={style.nowContainer}>
<div className={style.label}>Now</div>
<div className={style.title}>{title.titleNow}</div>
<div className={style.subtitle}>{title.subtitleNow}</div>
<div className={style.presenter}>{title.presenterNow}</div>
</div>
{title.showNow && (
<div className={style.nowContainer}>
<div className={style.label}>Now</div>
<div className={style.title}>{title.titleNow}</div>
<div className={style.subtitle}>{title.subtitleNow}</div>
<div className={style.presenter}>{title.presenterNow}</div>
</div>
)}
{title.showNext && (
<div className={style.nextContainer}>
@@ -101,10 +101,21 @@
padding: 0;
}
.progressContainer {
.countdown {
opacity: 1;
}
.progressContainer,
.progressContainerPaused {
grid-area: prog;
width: 80%;
margin: 0 auto;
opacity: 1;
}
.countdownPaused,
.progressContainerPaused {
opacity: 0.6;
}
.container__grayFinished {