refactor: improve performance on EventBlock

This commit is contained in:
cv
2023-04-06 14:42:34 +02:00
parent 4c4b83979d
commit ba62276951
5 changed files with 126 additions and 69 deletions
@@ -10,13 +10,7 @@ export default function TooltipActionBtn(props: TooltipActionBtnProps) {
const { clickHandler, icon, size = 'xs', tooltip, openDelay = 0, className, ...rest } = props; const { clickHandler, icon, size = 'xs', tooltip, openDelay = 0, className, ...rest } = props;
return ( return (
<Tooltip label={tooltip} openDelay={openDelay}> <Tooltip label={tooltip} openDelay={openDelay}>
<IconButton <IconButton {...rest} size={size} icon={icon} onClick={clickHandler} className={className} />
{...rest}
size={size}
icon={icon}
onClick={clickHandler}
className={className}
/>
</Tooltip> </Tooltip>
); );
} }
@@ -4,26 +4,21 @@ import { IoCaretDownCircle } from '@react-icons/all-files/io5/IoCaretDownCircle'
import { IoCaretUpCircle } from '@react-icons/all-files/io5/IoCaretUpCircle'; import { IoCaretUpCircle } from '@react-icons/all-files/io5/IoCaretUpCircle';
import { IoOptions } from '@react-icons/all-files/io5/IoOptions'; import { IoOptions } from '@react-icons/all-files/io5/IoOptions';
import { IoPeople } from '@react-icons/all-files/io5/IoPeople'; import { IoPeople } from '@react-icons/all-files/io5/IoPeople';
import { IoPlay } from '@react-icons/all-files/io5/IoPlay';
import { IoPlayCircle } from '@react-icons/all-files/io5/IoPlayCircle'; import { IoPlayCircle } from '@react-icons/all-files/io5/IoPlayCircle';
import { IoPlayForwardCircle } from '@react-icons/all-files/io5/IoPlayForwardCircle'; import { IoPlayForwardCircle } from '@react-icons/all-files/io5/IoPlayForwardCircle';
import { IoPlayOutline } from '@react-icons/all-files/io5/IoPlayOutline';
import { IoPlaySkipForwardCircle } from '@react-icons/all-files/io5/IoPlaySkipForwardCircle'; import { IoPlaySkipForwardCircle } from '@react-icons/all-files/io5/IoPlaySkipForwardCircle';
import { IoReload } from '@react-icons/all-files/io5/IoReload';
import { IoRemoveCircle } from '@react-icons/all-files/io5/IoRemoveCircle';
import { IoRemoveCircleOutline } from '@react-icons/all-files/io5/IoRemoveCircleOutline';
import { IoStopCircle } from '@react-icons/all-files/io5/IoStopCircle'; import { IoStopCircle } from '@react-icons/all-files/io5/IoStopCircle';
import { IoTime } from '@react-icons/all-files/io5/IoTime'; import { IoTime } from '@react-icons/all-files/io5/IoTime';
import { EndAction, Playback, TimerType } from 'ontime-types'; import { EndAction, Playback, TimerType } from 'ontime-types';
import TooltipActionBtn from '../../../common/components/buttons/TooltipActionBtn'; import TooltipActionBtn from '../../../common/components/buttons/TooltipActionBtn';
import { setEventPlayback } from '../../../common/hooks/useSocket';
import { useEventEditorStore } from '../../../common/stores/eventEditor'; import { useEventEditorStore } from '../../../common/stores/eventEditor';
import { tooltipDelayMid } from '../../../ontimeConfig'; import { tooltipDelayMid } from '../../../ontimeConfig';
import EditableBlockTitle from '../common/EditableBlockTitle'; import EditableBlockTitle from '../common/EditableBlockTitle';
import { EventItemActions } from '../RundownEntry'; import { EventItemActions } from '../RundownEntry';
import BlockActionMenu from './composite/BlockActionMenu'; import BlockActionMenu from './composite/BlockActionMenu';
import EventBlockPlayback from './composite/EventBlockPlayback';
import EventBlockProgressBar from './composite/EventBlockProgressBar'; import EventBlockProgressBar from './composite/EventBlockProgressBar';
import EventBlockTimers from './composite/EventBlockTimers'; import EventBlockTimers from './composite/EventBlockTimers';
@@ -104,49 +99,13 @@ const EventBlockInner = (props: EventBlockInnerProps) => {
return !renderInner ? null : ( return !renderInner ? null : (
<> <>
<div className={style.playbackActions}> <EventBlockPlayback eventId={eventId} skip={skip} isPlaying={eventIsPlaying} selected={selected} />
<TooltipActionBtn
variant='ontime-subtle-white'
aria-label='Skip event'
tooltip='Skip event'
icon={skip ? <IoRemoveCircle /> : <IoRemoveCircleOutline />}
{...tooltipProps}
{...blockBtnStyle}
clickHandler={() => actionHandler('update', { field: 'skip', value: !skip })}
tabIndex={-1}
disabled={selected}
/>
<TooltipActionBtn
variant='ontime-subtle-white'
aria-label='Load event'
tooltip='Load event'
icon={<IoReload className={style.flip} />}
disabled={skip}
{...tooltipProps}
{...blockBtnStyle}
clickHandler={() => setEventPlayback.loadEvent(eventId)}
tabIndex={-1}
/>
<TooltipActionBtn
variant='ontime-subtle-white'
aria-label='Start event'
tooltip='Start event'
icon={eventIsPlaying ? <IoPlay /> : <IoPlayOutline />}
disabled={skip}
{...tooltipProps}
{...blockBtnStyle}
clickHandler={() => setEventPlayback.startEvent(eventId)}
backgroundColor={eventIsPlaying ? '#58A151' : undefined}
_hover={{ backgroundColor: eventIsPlaying ? '#58A151' : undefined }}
tabIndex={-1}
/>
</div>
<EventBlockTimers <EventBlockTimers
eventId={eventId}
timeStart={timeStart} timeStart={timeStart}
timeEnd={timeEnd} timeEnd={timeEnd}
duration={duration} duration={duration}
delay={delay} delay={delay}
actionHandler={actionHandler}
previousEnd={previousEnd} previousEnd={previousEnd}
/> />
<EditableBlockTitle title={title} eventId={eventId} placeholder='Event title' className={style.eventTitle} /> <EditableBlockTitle title={title} eventId={eventId} placeholder='Event title' className={style.eventTitle} />
@@ -0,0 +1,79 @@
import { memo } from 'react';
import { IoPlay } from '@react-icons/all-files/io5/IoPlay';
import { IoPlayOutline } from '@react-icons/all-files/io5/IoPlayOutline';
import { IoReload } from '@react-icons/all-files/io5/IoReload';
import { IoRemoveCircle } from '@react-icons/all-files/io5/IoRemoveCircle';
import { IoRemoveCircleOutline } from '@react-icons/all-files/io5/IoRemoveCircleOutline';
import TooltipActionBtn from '../../../../common/components/buttons/TooltipActionBtn';
import { useEventAction } from '../../../../common/hooks/useEventAction';
import { setEventPlayback } from '../../../../common/hooks/useSocket';
import { tooltipDelayMid } from '../../../../ontimeConfig';
import style from '../EventBlock.module.scss';
const blockBtnStyle = {
size: 'sm',
};
const tooltipProps = {
openDelay: tooltipDelayMid,
};
interface EventBlockPlaybackProps {
eventId: string;
skip: boolean;
isPlaying: boolean;
selected: boolean;
}
const EventBlockPlayback = (props: EventBlockPlaybackProps) => {
const { eventId, skip, isPlaying, selected } = props;
const { updateEvent } = useEventAction();
const toggleSkip = () => {
updateEvent({ id: eventId, skip: !skip });
};
return (
<div className={style.playbackActions}>
<TooltipActionBtn
variant='ontime-subtle-white'
aria-label='Skip event'
tooltip='Skip event'
icon={skip ? <IoRemoveCircle /> : <IoRemoveCircleOutline />}
{...tooltipProps}
{...blockBtnStyle}
clickHandler={toggleSkip}
tabIndex={-1}
isDisabled={selected}
/>
<TooltipActionBtn
variant='ontime-subtle-white'
aria-label='Load event'
tooltip='Load event'
icon={<IoReload className={style.flip} />}
isDisabled={skip}
{...tooltipProps}
{...blockBtnStyle}
clickHandler={() => setEventPlayback.loadEvent(eventId)}
tabIndex={-1}
/>
<TooltipActionBtn
variant='ontime-subtle-white'
aria-label='Start event'
tooltip='Start event'
icon={isPlaying ? <IoPlay /> : <IoPlayOutline />}
isDisabled={skip}
{...tooltipProps}
{...blockBtnStyle}
clickHandler={() => setEventPlayback.startEvent(eventId)}
backgroundColor={isPlaying ? '#58A151' : undefined}
_hover={{ backgroundColor: isPlaying ? '#58A151' : undefined }}
tabIndex={-1}
/>
</div>
);
};
export default memo(EventBlockPlayback);
@@ -1,28 +1,53 @@
import { useCallback, useState } from 'react'; import { memo, useCallback, useState } from 'react';
import { OntimeEvent } from 'ontime-types';
import { millisToString } from 'ontime-utils'; import { millisToString } from 'ontime-utils';
import TimeInput from '../../../../common/components/input/time-input/TimeInput'; import TimeInput from '../../../../common/components/input/time-input/TimeInput';
import { useEventAction } from '../../../../common/hooks/useEventAction';
import { millisToMinutes } from '../../../../common/utils/dateConfig'; import { millisToMinutes } from '../../../../common/utils/dateConfig';
import { TimeEntryField, validateEntry } from '../../../../common/utils/timesManager'; import { calculateDuration, TimeEntryField, validateEntry } from '../../../../common/utils/timesManager';
import { EventItemActions } from '../../RundownEntry';
import style from '../EventBlock.module.scss'; import style from '../EventBlock.module.scss';
interface EventBlockTimerProps { interface EventBlockTimerProps {
eventId: string;
timeStart: number; timeStart: number;
timeEnd: number; timeEnd: number;
duration: number; duration: number;
delay: number; delay: number;
actionHandler: (action: EventItemActions, payload?: any) => void;
previousEnd: number; previousEnd: number;
} }
export default function EventBlockTimers(props: EventBlockTimerProps) { type TimeActions = 'timeStart' | 'timeEnd' | 'durationOverride';
const { timeStart, timeEnd, duration, delay, actionHandler, previousEnd } = props;
const EventBlockTimers = (props: EventBlockTimerProps) => {
const { eventId, timeStart, timeEnd, duration, delay, previousEnd } = props;
const { updateEvent } = useEventAction();
const [warning, setWarnings] = useState({ start: '', end: '', duration: '' }); const [warning, setWarnings] = useState({ start: '', end: '', duration: '' });
const delayTime = `${delay >= 0 ? '+' : '-'} ${millisToMinutes(Math.abs(delay))}`; const handleSubmit = (field: TimeActions, value: number) => {
const newTime = millisToString(timeStart + delay); const newEventData: Partial<OntimeEvent> = { id: eventId };
switch (field) {
case 'durationOverride': {
// duration defines timeEnd
newEventData.duration = value;
newEventData.timeEnd = timeStart + value;
break;
}
case 'timeStart': {
newEventData.duration = calculateDuration(value, timeEnd);
newEventData.timeStart = value;
break;
}
case 'timeEnd': {
newEventData.duration = calculateDuration(timeStart, value);
newEventData.timeEnd = value;
break;
}
}
updateEvent(newEventData);
};
/** /**
* @description Validates a time input against its pair * @description Validates a time input against its pair
@@ -39,12 +64,8 @@ export default function EventBlockTimers(props: EventBlockTimerProps) {
[timeEnd, timeStart], [timeEnd, timeStart],
); );
const handleSubmit = useCallback( const delayTime = `${delay >= 0 ? '+' : '-'} ${millisToMinutes(Math.abs(delay))}`;
(field: TimeEntryField, value: number) => { const newTime = millisToString(timeStart + delay);
actionHandler('update', { field, value });
},
[actionHandler],
);
return ( return (
<div className={style.eventTimers}> <div className={style.eventTimers}>
@@ -86,4 +107,6 @@ export default function EventBlockTimers(props: EventBlockTimerProps) {
)} )}
</div> </div>
); );
} };
export default memo(EventBlockTimers);
@@ -1,4 +1,4 @@
import { useCallback, useRef } from 'react'; import { memo, useCallback, useRef } from 'react';
import { Button, Checkbox, Tooltip } from '@chakra-ui/react'; import { Button, Checkbox, Tooltip } from '@chakra-ui/react';
import { SupportedEvent } from 'ontime-types'; import { SupportedEvent } from 'ontime-types';
@@ -17,7 +17,7 @@ interface QuickAddBlockProps {
disableAddBlock: boolean; disableAddBlock: boolean;
} }
export default function QuickAddBlock(props: QuickAddBlockProps) { const QuickAddBlock = (props: QuickAddBlockProps) => {
const { showKbd, eventId, previousEventId, disableAddDelay = true, disableAddBlock } = props; const { showKbd, eventId, previousEventId, disableAddDelay = true, disableAddBlock } = props;
const { addEvent } = useEventAction(); const { addEvent } = useEventAction();
const { emitError } = useEmitLog(); const { emitError } = useEmitLog();
@@ -117,4 +117,6 @@ export default function QuickAddBlock(props: QuickAddBlockProps) {
</div> </div>
</div> </div>
); );
} };
export default memo(QuickAddBlock);