This commit is contained in:
cv
2021-05-10 22:01:58 +02:00
parent d24fb406af
commit d71615fba4
7 changed files with 160 additions and 364 deletions
@@ -1,7 +1,6 @@
import EditableTimer from '../../input/EditableTimer';
import { showWarningToast } from '../../helpers/toastManager';
import { useEffect, useState } from 'react';
import { millisToMinutes, stringFromMillis } from '../../dateConfig';
import { stringFromMillis } from '../../dateConfig';
const label = {
fontSize: '0.75em',
@@ -50,7 +49,7 @@ const TimesDelayed = (props) => {
};
const Times = (props) => {
const { handleValidate, actionHandler, timeStart, timeEnd, duration } = props;
const { handleValidate, actionHandler, timeStart, timeEnd } = props;
return (
<>
@@ -84,13 +83,6 @@ const Times = (props) => {
export default function EventTimesVertical(props) {
const { delay, timeStart, timeEnd } = props;
const [duration, setDuration] = useState(
millisToMinutes(timeEnd - timeStart) || 0
);
useEffect(() => {
setDuration(millisToMinutes(timeEnd - timeStart));
}, [timeStart, timeEnd]);
const handleValidate = (entry, v) => {
// we dont enforce validation here
@@ -115,7 +107,6 @@ export default function EventTimesVertical(props) {
delay={delay}
timeStart={timeStart}
timeEnd={timeEnd}
duration={duration}
/>
) : (
<Times
@@ -123,7 +114,6 @@ export default function EventTimesVertical(props) {
actionHandler={props.actionHandler}
timeStart={timeStart}
timeEnd={timeEnd}
duration={duration}
/>
);
}
+51 -60
View File
@@ -8,6 +8,33 @@ const inputProps = {
size: 'sm',
};
const InputRow = (props) => {
const { label, placeholder, text, visible } = props;
return (
<>
<span className={style.label}>{label}</span>
<div className={style.inputItems}>
<Editable
onChange={(event) => props.changeHandler(event)}
value={text}
placeholder={placeholder}
className={style.inline}
color={text === '' ? '#666' : 'inherit'}
>
<EditablePreview className={style.padleft} />
<EditableInput className={style.padleft} />
</Editable>
<VisibleIconBtn
active={visible || undefined}
actionHandler={props.actionHandler}
{...inputProps}
/>
</div>
</>
);
};
export default function MessageControl() {
const socket = useSocket();
const [pres, setPres] = useState({
@@ -80,66 +107,30 @@ export default function MessageControl() {
return (
<div className={style.messageContainer}>
<div className={style.inputContainer}>
<span className={style.label}>Presenter screen message</span>
<div className={style.inputItems}>
<Editable
onChange={(event) => messageControl('pres-text', event)}
value={pres.text}
placeholder='only the presenter screens see this'
className={style.inline}
>
<EditablePreview className={style.padleft} />
<EditableInput className={style.padleft} />
</Editable>
<VisibleIconBtn
active={pres.visible || undefined}
actionHandler={() => messageControl('toggle-pres-visible')}
{...inputProps}
/>
</div>
</div>
<div className={style.inputContainerWGap}>
<span className={style.label}>Public screen message</span>
<div className={style.inputItems}>
<Editable
onChange={(event) => messageControl('publ-text', event)}
value={publ.text}
placeholder='public screens will render this'
className={style.inline}
>
<EditablePreview className={style.padleft} />
<EditableInput className={style.padleft} />
</Editable>
<VisibleIconBtn
active={publ.visible || undefined}
actionHandler={() => messageControl('toggle-publ-visible')}
{...inputProps}
/>
</div>
</div>
<div className={style.inputContainerWGap}>
<span className={style.label}>Lower third message</span>
<div className={style.inputItems}>
<Editable
onChange={(event) => messageControl('lower-text', event)}
value={lower.text}
placeholder='visible in lower third screen'
className={style.inline}
>
<EditablePreview className={style.padleft} />
<EditableInput className={style.padleft} />
</Editable>
<VisibleIconBtn
active={lower.visible || undefined}
actionHandler={() => messageControl('toggle-lower-visible')}
{...inputProps}
/>
</div>
</div>
<InputRow
label='Presenter screen message'
placeholder='only the presenter screens see this'
text={pres.text}
visible={pres.visible}
changeHandler={(event) => messageControl('pres-text', event)}
actionHandler={() => messageControl('toggle-pres-visible')}
/>
<InputRow
label='Public screen message'
placeholder='public screens will render this'
text={publ.text}
visible={publ.visible}
changeHandler={(event) => messageControl('publ-text', event)}
actionHandler={() => messageControl('toggle-publ-visible')}
/>
<InputRow
label='Lower third message'
placeholder='visible in lower third screen'
text={lower.text}
visible={lower.visible}
changeHandler={(event) => messageControl('lower-text', event)}
actionHandler={() => messageControl('toggle-publ-visible')}
/>
</div>
);
}
@@ -2,17 +2,12 @@
background-color: rgba(0, 0, 0, 0.05);
border: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 4px;
}
.inputContainer,
.inputContainerWGap {
display: flex;
flex-direction: column;
gap: 0.5em;
padding: 0.5em;
}
.inputContainerWGap {
padding-top: 0.5em;
}
.inputItems {
display: grid;
grid-template-columns: 1fr auto;
+10 -11
View File
@@ -3,7 +3,6 @@ import { Box } from '@chakra-ui/layout';
import NumberedText from '../../common/components/text/NumberedText';
import PlaybackControl from '../control/PlaybackControl';
import MessageControl from '../control/MessageControl';
import PreviewContainer from '../viewers/PreviewContainer';
import styles from './Editor.module.css';
import EventListWrapper from './list/EventListWrapper';
import { useDisclosure } from '@chakra-ui/hooks';
@@ -28,7 +27,7 @@ export default function Editor() {
<Heading size='lg' paddingBottom={'0.25em'}>
Event List
</Heading>
<NumberedText number={1} text={'Manage and select event to run'} />
<NumberedText number={1} text={'Manage events'} />
<div className={styles.content}>
<EventListWrapper />
</div>
@@ -39,8 +38,8 @@ export default function Editor() {
Display Messages
</Heading>
<NumberedText
number={2}
text={'Show realtime messages on separate screen types'}
number={3}
text={'Show realtime messages on different screens'}
/>
<div className={styles.content}>
<MessageControl />
@@ -51,18 +50,12 @@ export default function Editor() {
<Heading size='lg' paddingBottom={'0.25em'}>
Time Control
</Heading>
<NumberedText number={3} text={'Control Timer'} />
<NumberedText number={2} text={'Control timers'} />
<div className={styles.content}>
<PlaybackControl />
</div>
</Box>
<Box className={styles.settings}>
<div className={styles.content}>
<SettingsIconBtn size='md' clickhandler={onOpen} />
</div>
</Box>
<Box className={styles.info} borderRadius='0.5em' overflowX='auto'>
<Heading size='lg' paddingBottom={'0.25em'}>
Info
@@ -70,6 +63,12 @@ export default function Editor() {
<NumberedText number={4} text={'Running Info'} />
<div className={styles.content}></div>
</Box>
<Box className={styles.settings}>
<div className={styles.content}>
<SettingsIconBtn size='md' clickhandler={onOpen} />
</div>
</Box>
</div>
</>
);
@@ -1,188 +0,0 @@
import { FiChevronDown, FiChevronUp, FiMoreVertical } from 'react-icons/fi';
import { memo, useCallback, useEffect, useState } from 'react';
import { showErrorToast } from '../../../common/helpers/toastManager';
import { Draggable } from 'react-beautiful-dnd';
import EventTimes from '../../../common/components/eventTimes/EventTimes';
// import common from './Block.module.css';
import style from './EventBlock.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';
import DeleteIconBtn from '../../../common/components/buttons/DeleteIconBtn';
const areEqual = (prevProps, nextProps) => {
return (
prevProps.data.revision === nextProps.data.revision &&
prevProps.selected === nextProps.selected &&
prevProps.next === nextProps.next &&
prevProps.index === nextProps.index &&
prevProps.delay === nextProps.delay
);
};
const EventBlock = (props) => {
const { data, selected, next, delay, index, eventsHandler } = props;
const [more, setMore] = useState(true);
const [visible, setVisible] = useState(data.isPublic || false);
// Set visibility indicator
useEffect(() => {
setVisible(data.isPublic);
}, [data.isPublic]);
// Create / delete new events
const actionHandler = (action) => {
switch (action) {
case 'event':
eventsHandler('add', { type: 'event', order: index + 1 });
break;
case 'delay':
eventsHandler('add', { type: 'delay', order: index + 1 });
break;
case 'block':
eventsHandler('add', { type: 'block', order: index + 1 });
break;
case 'delete':
eventsHandler('delete', data.id);
break;
default:
break;
}
};
// Update data in fields
const updateValues = (field, value) => {
// validate field
if (field in data) {
// create object with new field
const newData = { id: data.id, [field]: value };
// request update in parent
eventsHandler('patch', newData);
} else {
showErrorToast('Field Error: ' + field);
}
};
const handleTitleSubmit = (v) => {
updateValues('title', v);
};
const handleSubtitleSubmit = (v) => {
updateValues('subtitle', v);
};
const handlePresenterSubmit = (v) => {
updateValues('presenter', v);
};
const handleNoteSubmit = (v) => {
updateValues('note', v);
};
const handleVisibleToggle = useCallback(() => {
const viz = !data.isPublic || !visible;
updateValues('isPublic', viz);
}, [data.isPublic, visible, updateValues]);
// TODO: inside useEffect()
const isSelected = selected ? style.active : '';
const isExpanded = more ? style.expanded : style.collapsed;
const classSelect = ` ${style.event} ${isExpanded} ${isSelected}`;
return (
<Draggable key={data.id} draggableId={data.id} index={index}>
{(provided) => (
<div
className={classSelect}
{...provided.draggableProps}
ref={provided.innerRef}
>
<span className={style.drag} {...provided.dragHandleProps}>
<FiMoreVertical />
</span>
<div className={style.indicators}>
<div className={next ? style.next : style.nextDisabled}>Next</div>
<DelayValue delay={delay} />
</div>
<EventTimes
updateValues={updateValues}
timeStart={data.timeStart}
timeEnd={data.timeEnd}
delay={delay}
className={style.time}
/>
{more && <div className={style.start}>Start Time</div>}
{more && <div className={style.end}>End Time</div>}
{more && <div className={style.duration}>Duration</div>}
<div className={style.rowDetailed}>
{more ? (
<div className={style.titleContainer}>
<EditableText
label='Title'
defaultValue={data.title}
placeholder='Add Title'
submitHandler={handleTitleSubmit}
underlined
/>
<EditableText
label='Presenter'
defaultValue={data.presenter}
placeholder='Add Presenter name'
submitHandler={handlePresenterSubmit}
underlined
/>
<EditableText
label='Subtitle'
defaultValue={data.subtitle}
placeholder='Add Subtitle'
submitHandler={handleSubtitleSubmit}
underlined
/>
<EditableText
label='note'
defaultValue={data.note}
placeholder='Add note'
submitHandler={handleNoteSubmit}
underlined
/>
</div>
) : (
<div className={style.titleContainer}>
<EditableText
label='Title'
defaultValue={data.title}
placeholder='Add Title'
submitHandler={handleTitleSubmit}
isTight
/>
</div>
)}
<div className={style.more} onClick={() => setMore(!more)}>
{more ? <FiChevronUp /> : <FiChevronDown />}
</div>
</div>
<div className={style.actionOverlay}>
<VisibleIconBtn
clickhandler={handleVisibleToggle}
active={visible}
/>
<DeleteIconBtn actionHandler={actionHandler} />
<ActionButtons
showAdd
showDelay
showBlock
actionHandler={actionHandler}
/>
</div>
</div>
)}
</Draggable>
);
};
export default memo(EventBlock, areEqual);
// export default EventBlock;
@@ -203,93 +203,104 @@ export default function EventListWrapper() {
}, [isError]);
// Events API
const eventsHandler = useCallback(async (action, payload) => {
switch (action) {
case 'add':
try {
let t = Date.now();
await addEvent.mutateAsync(payload);
console.log('debug m add', Date.now() - t);
} catch (error) {
showErrorToast('Error creating event', error.message);
}
break;
case 'update':
try {
let t = Date.now();
await updateEvent.mutateAsync(payload);
console.log('debug m update', Date.now() - t);
} catch (error) {
showErrorToast('Error updating event', error.message);
}
break;
case 'patch':
try {
let t = Date.now();
await patchEvent.mutateAsync(payload);
console.log('debug m patch', Date.now() - t);
} catch (error) {
showErrorToast('Error updating event', error.message);
}
break;
case 'delete':
try {
let t = Date.now();
await deleteEvent.mutateAsync(payload);
console.log('debug m delete', Date.now() - t);
} catch (error) {
showErrorToast('Error deleting event', error.message);
}
break;
case 'reorder':
try {
let t = Date.now();
await reorderEvent.mutateAsync(payload);
console.log('debug m reorder', Date.now() - t);
} catch (error) {
showErrorToast('Error reordering event', error.message);
}
break;
case 'applyDelay':
let t = Date.now();
// if delay <= 0 delete delay and next block
if (payload.duration <= 0) {
const eventsHandler = useCallback(
async (action, payload) => {
switch (action) {
case 'add':
try {
// look for block after
let afterId = false;
let blockAfter = null;
for (const d of data) {
if (d.id === payload.id) afterId = true;
if (afterId && d.type === 'block') {
blockAfter = d.id;
break;
let t = Date.now();
await addEvent.mutateAsync(payload);
console.log('debug m add', Date.now() - t);
} catch (error) {
showErrorToast('Error creating event', error.message);
}
break;
case 'update':
try {
let t = Date.now();
await updateEvent.mutateAsync(payload);
console.log('debug m update', Date.now() - t);
} catch (error) {
showErrorToast('Error updating event', error.message);
}
break;
case 'patch':
try {
let t = Date.now();
await patchEvent.mutateAsync(payload);
console.log('debug m patch', Date.now() - t);
} catch (error) {
showErrorToast('Error updating event', error.message);
}
break;
case 'delete':
try {
let t = Date.now();
await deleteEvent.mutateAsync(payload);
console.log('debug m delete', Date.now() - t);
} catch (error) {
showErrorToast('Error deleting event', error.message);
}
break;
case 'reorder':
try {
let t = Date.now();
await reorderEvent.mutateAsync(payload);
console.log('debug m reorder', Date.now() - t);
} catch (error) {
showErrorToast('Error reordering event', error.message);
}
break;
case 'applyDelay':
let t = Date.now();
// if delay <= 0 delete delay and next block
if (payload.duration <= 0) {
try {
// look for block after
let afterId = false;
let blockAfter = null;
for (const d of data) {
if (d.id === payload.id) afterId = true;
if (afterId && d.type === 'block') {
blockAfter = d.id;
break;
}
}
// delete delay
await deleteEvent.mutateAsync(payload.id);
// delete block after, if any
if (blockAfter) await deleteEvent.mutateAsync(blockAfter);
} catch (error) {
showErrorToast('Error applying delay', error.message);
}
} else {
console.log('debug applydelay', payload.id);
try {
await applyDelay.mutateAsync(payload.id);
} catch (error) {
showErrorToast('Error applying delay', error.message);
}
// delete delay
await deleteEvent.mutateAsync(payload.id);
// delete block after, if any
if (blockAfter) await deleteEvent.mutateAsync(blockAfter);
} catch (error) {
showErrorToast('Error applying delay', error.message);
}
} else {
console.log('debug applydelay', payload.id);
try {
await applyDelay.mutateAsync(payload.id);
} catch (error) {
showErrorToast('Error applying delay', error.message);
}
}
console.log('debug m apply', Date.now() - t);
console.log('debug m apply', Date.now() - t);
break;
default:
showErrorToast('Unrecognised request', action);
break;
}
}, []);
break;
default:
showErrorToast('Unrecognised request', action);
break;
}
},
[
addEvent,
applyDelay,
data,
deleteEvent,
patchEvent,
reorderEvent,
updateEvent,
]
);
return (
<>
+1 -3
View File
@@ -1,4 +1,4 @@
import { memo, useCallback } from 'react';
import { memo } from 'react';
import { FiChevronDown } from 'react-icons/fi';
import {
Button,
@@ -8,7 +8,6 @@ import {
MenuItem,
MenuList,
} from '@chakra-ui/react';
import AddIconBtn from '../../common/components/buttons/AddIconBtn';
import style from './EventListMenu.module.css';
import MenuActionButtons from '../editors/list/MenuActionButtons';
@@ -29,7 +28,6 @@ const EventListMenu = ({ eventsHandler }) => {
};
const actionHandler = (action) => {
console.log('debug action called', action);
switch (action) {
case 'event':
eventsHandler('add', { type: action, order: 0 });