mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-06 14:59:09 +00:00
Refactor/ts sockets (#210)
* refactor(ts): create socket subscription service * refactor(ts-sockets): useSubscription hook * refactor(ts-sockets): extract colour selection to utility
This commit is contained in:
@@ -54,6 +54,7 @@
|
|||||||
"@testing-library/react": "^13.1.1",
|
"@testing-library/react": "^13.1.1",
|
||||||
"@testing-library/react-hooks": "^8.0.0",
|
"@testing-library/react-hooks": "^8.0.0",
|
||||||
"@testing-library/user-event": "^14.1.1",
|
"@testing-library/user-event": "^14.1.1",
|
||||||
|
"@types/color": "^3.0.3",
|
||||||
"@types/node": "^18.7.16",
|
"@types/node": "^18.7.16",
|
||||||
"@types/react": "^18.0.19",
|
"@types/react": "^18.0.19",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
export interface OntimeBaseEvent {
|
||||||
|
type: 'block' | 'event' | 'delay';
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OntimeDelay extends OntimeBaseEvent {
|
||||||
|
type: 'delay';
|
||||||
|
duration: number;
|
||||||
|
revision: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OntimeBlock extends OntimeBaseEvent {
|
||||||
|
type: 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OntimeEvent extends OntimeBaseEvent {
|
||||||
|
type: 'event';
|
||||||
|
title: string,
|
||||||
|
subtitle: string,
|
||||||
|
presenter: string,
|
||||||
|
note: string,
|
||||||
|
timeStart: number,
|
||||||
|
timeEnd: number,
|
||||||
|
timeType?: string,
|
||||||
|
duration: number,
|
||||||
|
isPublic: boolean,
|
||||||
|
skip: boolean,
|
||||||
|
colour: string,
|
||||||
|
user0: string,
|
||||||
|
user1: string,
|
||||||
|
user2: string,
|
||||||
|
user3: string,
|
||||||
|
user4: string,
|
||||||
|
user5: string,
|
||||||
|
user6: string,
|
||||||
|
user7: string,
|
||||||
|
user8: string,
|
||||||
|
user9: string,
|
||||||
|
revision: number,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OntimeEventEntry = OntimeDelay | OntimeBlock | OntimeEvent;
|
||||||
+9
-10
@@ -4,11 +4,17 @@ import { Tooltip } from '@chakra-ui/tooltip';
|
|||||||
import { FiClock } from '@react-icons/all-files/fi/FiClock';
|
import { FiClock } from '@react-icons/all-files/fi/FiClock';
|
||||||
import { FiMinusCircle } from '@react-icons/all-files/fi/FiMinusCircle';
|
import { FiMinusCircle } from '@react-icons/all-files/fi/FiMinusCircle';
|
||||||
import { FiPlus } from '@react-icons/all-files/fi/FiPlus';
|
import { FiPlus } from '@react-icons/all-files/fi/FiPlus';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||||
|
|
||||||
export default function ActionButtons(props) {
|
interface ActionButtonProps {
|
||||||
|
showAdd?: boolean;
|
||||||
|
showDelay?: boolean;
|
||||||
|
showBlock?: boolean;
|
||||||
|
actionHandler: (action: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ActionButtons(props: ActionButtonProps) {
|
||||||
const { showAdd, showDelay, showBlock, actionHandler } = props;
|
const { showAdd, showDelay, showBlock, actionHandler } = props;
|
||||||
|
|
||||||
const menuStyle = {
|
const menuStyle = {
|
||||||
@@ -18,7 +24,7 @@ export default function ActionButtons(props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu isLazy lazyBehavior='unmount'>
|
<Menu isLazy lazyBehavior='unmount'>
|
||||||
<Tooltip label='Add ...' delay={tooltipDelayMid}>
|
<Tooltip label='Add ...' openDelay={tooltipDelayMid}>
|
||||||
<MenuButton
|
<MenuButton
|
||||||
as={IconButton}
|
as={IconButton}
|
||||||
aria-label='Options'
|
aria-label='Options'
|
||||||
@@ -49,10 +55,3 @@ export default function ActionButtons(props) {
|
|||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionButtons.propTypes = {
|
|
||||||
showAdd: PropTypes.bool,
|
|
||||||
showDelay: PropTypes.bool,
|
|
||||||
showBlock: PropTypes.bool,
|
|
||||||
actionHandler: PropTypes.func,
|
|
||||||
}
|
|
||||||
+8
-9
@@ -1,30 +1,29 @@
|
|||||||
import { IconButton } from '@chakra-ui/button';
|
import { IconButton } from '@chakra-ui/button';
|
||||||
import { Tooltip } from '@chakra-ui/tooltip';
|
import { Tooltip } from '@chakra-ui/tooltip';
|
||||||
import { IoPause } from '@react-icons/all-files/io5/IoPause';
|
import { IoPause } from '@react-icons/all-files/io5/IoPause';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { tooltipDelayMid } from '../../../ontimeConfig';
|
import { tooltipDelayMid } from '../../../ontimeConfig';
|
||||||
|
|
||||||
export default function PauseIconBtn(props) {
|
interface PauseIconBtnProps {
|
||||||
|
clickhandler: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||||
|
active: boolean;
|
||||||
|
disabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PauseIconBtn(props: PauseIconBtnProps) {
|
||||||
const { clickhandler, active, disabled, ...rest } = props;
|
const { clickhandler, active, disabled, ...rest } = props;
|
||||||
return (
|
return (
|
||||||
<Tooltip label='Pause timer' openDelay={tooltipDelayMid} shouldWrapChildren={disabled}>
|
<Tooltip label='Pause timer' openDelay={tooltipDelayMid} shouldWrapChildren={disabled}>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={<IoPause size='24px' />}
|
icon={<IoPause size='24px' />}
|
||||||
colorScheme='orange'
|
colorScheme='orange'
|
||||||
_hover={!disabled && { bg: 'orange.400' }}
|
|
||||||
variant={active ? 'solid' : 'outline'}
|
variant={active ? 'solid' : 'outline'}
|
||||||
onClick={clickhandler}
|
onClick={clickhandler}
|
||||||
width={120}
|
width={120}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
aria-label='Pause playback'
|
||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
PauseIconBtn.propTypes = {
|
|
||||||
clickhandler: PropTypes.func,
|
|
||||||
active: PropTypes.bool,
|
|
||||||
disabled: PropTypes.bool
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import { formatTime } from '../../utils/time';
|
|
||||||
|
|
||||||
import './Paginator.scss';
|
|
||||||
|
|
||||||
export default function TodayItem(props) {
|
|
||||||
const { selected, timeStart, timeEnd, title, backstageEvent, colour } = props;
|
|
||||||
|
|
||||||
// Format timers
|
|
||||||
const start = formatTime(timeStart, { format: 'hh:mm' });
|
|
||||||
const end = formatTime(timeEnd, { format: 'hh:mm' });
|
|
||||||
|
|
||||||
// user colours
|
|
||||||
const userColour = colour !== '' ? colour : 'transparent';
|
|
||||||
|
|
||||||
// select styling
|
|
||||||
let selectStyle = 'entry--past';
|
|
||||||
if (selected === 1) selectStyle = 'entry--now';
|
|
||||||
else if (selected === 2) selectStyle = 'entry--future';
|
|
||||||
return (
|
|
||||||
<div className={`entry ${selectStyle}`} style={{ borderLeft: `4px solid ${userColour}` }}>
|
|
||||||
<div className='entry-times'>
|
|
||||||
{`${start} · ${end}`}
|
|
||||||
</div>
|
|
||||||
<div className='entry-title'>{title}</div>
|
|
||||||
{backstageEvent && <div className='backstage-indicator' />}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TodayItem.propTypes = {
|
|
||||||
selected: PropTypes.number,
|
|
||||||
timeStart: PropTypes.number,
|
|
||||||
timeEnd: PropTypes.number,
|
|
||||||
title: PropTypes.string,
|
|
||||||
backstageEvent: PropTypes.bool,
|
|
||||||
colour: PropTypes.string,
|
|
||||||
};
|
|
||||||
+18
-17
@@ -1,14 +1,24 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useInterval } from 'common/hooks/useInterval';
|
import { useInterval } from 'common/hooks/useInterval';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
|
import { OntimeEvent } from '../../application-types/event';
|
||||||
import Empty from '../state/Empty';
|
import Empty from '../state/Empty';
|
||||||
|
|
||||||
import TodayItem from './TodayItem';
|
import TodayItem from './TodayItem';
|
||||||
|
|
||||||
import './Paginator.scss';
|
import style from './Paginator.module.scss';
|
||||||
|
|
||||||
export default function Paginator(props) {
|
interface PaginatorProps {
|
||||||
|
events: OntimeEvent[];
|
||||||
|
selectedId: string;
|
||||||
|
limit?: number;
|
||||||
|
time?: number;
|
||||||
|
isBackstage: boolean;
|
||||||
|
setPageNumber: (page: number) => void;
|
||||||
|
setCurrentPage: (selectedPage: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Paginator(props: PaginatorProps) {
|
||||||
const {
|
const {
|
||||||
events,
|
events,
|
||||||
selectedId,
|
selectedId,
|
||||||
@@ -21,9 +31,9 @@ export default function Paginator(props) {
|
|||||||
const LIMIT_PER_PAGE = limit;
|
const LIMIT_PER_PAGE = limit;
|
||||||
const SCROLL_TIME = time * 1000;
|
const SCROLL_TIME = time * 1000;
|
||||||
const [numEvents, setNumEvents] = useState(0);
|
const [numEvents, setNumEvents] = useState(0);
|
||||||
const [page, setPage] = useState([]);
|
const [page, setPage] = useState<OntimeEvent[]>([]);
|
||||||
const [pages, setPages] = useState(0);
|
const [pages, setPages] = useState<number>(0);
|
||||||
const [selPage, setSelPage] = useState(0);
|
const [selPage, setSelPage] = useState<number>(0);
|
||||||
|
|
||||||
// keep parent up to date
|
// keep parent up to date
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -70,7 +80,7 @@ export default function Paginator(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='paginator entries'>
|
<div className={style.entries}>
|
||||||
{page.map((e) => {
|
{page.map((e) => {
|
||||||
if (e.id === selectedId) selectedState = 1;
|
if (e.id === selectedId) selectedState = 1;
|
||||||
else if (selectedState === 1) selectedState = 2;
|
else if (selectedState === 1) selectedState = 2;
|
||||||
@@ -83,19 +93,10 @@ export default function Paginator(props) {
|
|||||||
title={e.title}
|
title={e.title}
|
||||||
colour={isBackstage ? e.colour : ''}
|
colour={isBackstage ? e.colour : ''}
|
||||||
backstageEvent={!e.isPublic}
|
backstageEvent={!e.isPublic}
|
||||||
|
skip={e.skip}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Paginator.propTypes = {
|
|
||||||
events: PropTypes.array,
|
|
||||||
selectedId: PropTypes.string,
|
|
||||||
limit: PropTypes.number,
|
|
||||||
time: PropTypes.number,
|
|
||||||
isBackstage: PropTypes.bool,
|
|
||||||
setPageNumber: PropTypes.func,
|
|
||||||
setCurrentPage: PropTypes.func,
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import { formatTime } from '../../utils/time';
|
||||||
|
|
||||||
|
import style from './Paginator.module.scss';
|
||||||
|
|
||||||
|
interface TodayItemProps {
|
||||||
|
selected: number;
|
||||||
|
timeStart: number;
|
||||||
|
timeEnd: number;
|
||||||
|
title: string;
|
||||||
|
backstageEvent: boolean;
|
||||||
|
colour: string;
|
||||||
|
skip: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Todo: apply skip CSS and selector
|
||||||
|
export default function TodayItem(props: TodayItemProps) {
|
||||||
|
// @ts-ignore
|
||||||
|
const { selected, timeStart, timeEnd, title, backstageEvent, colour, skip } = props;
|
||||||
|
|
||||||
|
// Format timers
|
||||||
|
const start = formatTime(timeStart, { format: 'hh:mm' });
|
||||||
|
const end = formatTime(timeEnd, { format: 'hh:mm' });
|
||||||
|
|
||||||
|
// user colours
|
||||||
|
const userColour = colour !== '' ? colour : 'transparent';
|
||||||
|
|
||||||
|
// select styling
|
||||||
|
let selectStyle = style.entryPast;
|
||||||
|
if (selected === 1) selectStyle = style.entryNow;
|
||||||
|
else if (selected === 2) selectStyle = style.entryFuture;
|
||||||
|
return (
|
||||||
|
<div className={selectStyle} style={{ borderLeft: `4px solid ${userColour}` }}>
|
||||||
|
<div className={`${style.entryTimes} ${backstageEvent ? style.backstage : ''}`}>
|
||||||
|
{`${start} · ${end}`}
|
||||||
|
</div>
|
||||||
|
<div className={style.entryTitle}>{title}</div>
|
||||||
|
{backstageEvent && <div className={style.backstageInd} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TodayItem.propTypes = {
|
||||||
|
selected: PropTypes.number,
|
||||||
|
timeStart: PropTypes.number,
|
||||||
|
timeEnd: PropTypes.number,
|
||||||
|
title: PropTypes.string,
|
||||||
|
backstageEvent: PropTypes.bool,
|
||||||
|
colour: PropTypes.string,
|
||||||
|
};
|
||||||
+26
-5
@@ -1,17 +1,32 @@
|
|||||||
import { createContext, useCallback, useMemo, useState } from 'react';
|
import { createContext, ReactNode, useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { useLocalStorage } from '../hooks/useLocalStorage';
|
import { useLocalStorage } from '../hooks/useLocalStorage';
|
||||||
|
|
||||||
export const CursorContext = createContext({
|
interface CursorContextState {
|
||||||
|
cursor: number;
|
||||||
|
isCursorLocked: boolean;
|
||||||
|
toggleCursorLocked: (newValue?: boolean) => void;
|
||||||
|
setCursor: (index: number) => void;
|
||||||
|
moveCursorUp: () => void;
|
||||||
|
moveCursorDown: () => void;
|
||||||
|
moveCursorTo: (index: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CursorContext = createContext<CursorContextState>({
|
||||||
cursor: 0,
|
cursor: 0,
|
||||||
isCursorLocked: false,
|
isCursorLocked: false,
|
||||||
|
toggleCursorLocked: () => undefined,
|
||||||
setCursor: () => undefined,
|
setCursor: () => undefined,
|
||||||
moveCursorUp: () => undefined,
|
moveCursorUp: () => undefined,
|
||||||
moveCursorDown: () => undefined,
|
moveCursorDown: () => undefined,
|
||||||
|
moveCursorTo: () => undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const CursorProvider = ({ children }) => {
|
interface CursorProviderProps {
|
||||||
|
children: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CursorProvider = ({ children }: CursorProviderProps) => {
|
||||||
const [cursor, setCursor] = useState(0);
|
const [cursor, setCursor] = useState(0);
|
||||||
const [_cursorLocked, _setCursorLocked] = useLocalStorage('isCursorLocked', 'locked');
|
const [_cursorLocked, _setCursorLocked] = useLocalStorage('isCursorLocked', 'locked');
|
||||||
const isCursorLocked = useMemo(() => _cursorLocked === 'locked', [_cursorLocked]);
|
const isCursorLocked = useMemo(() => _cursorLocked === 'locked', [_cursorLocked]);
|
||||||
@@ -31,7 +46,7 @@ export const CursorProvider = ({ children }) => {
|
|||||||
* @param {boolean | undefined} newValue
|
* @param {boolean | undefined} newValue
|
||||||
*/
|
*/
|
||||||
const toggleCursorLocked = useCallback(
|
const toggleCursorLocked = useCallback(
|
||||||
(newValue = undefined) => {
|
(newValue?: boolean) => {
|
||||||
if (typeof newValue === 'undefined') {
|
if (typeof newValue === 'undefined') {
|
||||||
if (isCursorLocked) {
|
if (isCursorLocked) {
|
||||||
cursorLockedOff();
|
cursorLockedOff();
|
||||||
@@ -47,6 +62,11 @@ export const CursorProvider = ({ children }) => {
|
|||||||
[cursorLockedOff, cursorLockedOn, isCursorLocked]
|
[cursorLockedOff, cursorLockedOn, isCursorLocked]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// moves cursor to given index
|
||||||
|
const moveCursorTo = useCallback((index: number) => {
|
||||||
|
setCursor(index);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CursorContext.Provider
|
<CursorContext.Provider
|
||||||
value={{
|
value={{
|
||||||
@@ -56,6 +76,7 @@ export const CursorProvider = ({ children }) => {
|
|||||||
setCursor,
|
setCursor,
|
||||||
moveCursorUp,
|
moveCursorUp,
|
||||||
moveCursorDown,
|
moveCursorDown,
|
||||||
|
moveCursorTo,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@@ -27,7 +27,7 @@ type LoggingProviderProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const notInitialised = () => {
|
const notInitialised = () => {
|
||||||
throw new Error("Not initialised");
|
throw new Error('Not initialised');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LoggingContext = createContext<LoggingProviderState>({
|
export const LoggingContext = createContext<LoggingProviderState>({
|
||||||
@@ -35,7 +35,7 @@ export const LoggingContext = createContext<LoggingProviderState>({
|
|||||||
emitInfo: notInitialised,
|
emitInfo: notInitialised,
|
||||||
emitWarning: notInitialised,
|
emitWarning: notInitialised,
|
||||||
emitError: notInitialised,
|
emitError: notInitialised,
|
||||||
clearLog: notInitialised
|
clearLog: notInitialised,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const LoggingProvider = ({ children }: LoggingProviderProps) => {
|
export const LoggingProvider = ({ children }: LoggingProviderProps) => {
|
||||||
@@ -52,7 +52,7 @@ export const LoggingProvider = ({ children }: LoggingProviderProps) => {
|
|||||||
socket.emit('get-logger');
|
socket.emit('get-logger');
|
||||||
|
|
||||||
socket.on('logger', (data: Log) => {
|
socket.on('logger', (data: Log) => {
|
||||||
setLogData((l) => [data, ...l]);
|
setLogData((currentLog) => [data, ...currentLog]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Clear listener
|
// Clear listener
|
||||||
@@ -70,21 +70,21 @@ export const LoggingProvider = ({ children }: LoggingProviderProps) => {
|
|||||||
const _send = useCallback(
|
const _send = useCallback(
|
||||||
(text: string, level: LOG_LEVEL) => {
|
(text: string, level: LOG_LEVEL) => {
|
||||||
if (socket != null) {
|
if (socket != null) {
|
||||||
const m: Log = {
|
const newLogMessage: Log = {
|
||||||
id: generateId(),
|
id: generateId(),
|
||||||
origin,
|
origin,
|
||||||
time: stringFromMillis(nowInMillis()),
|
time: stringFromMillis(nowInMillis()),
|
||||||
level,
|
level,
|
||||||
text,
|
text,
|
||||||
};
|
};
|
||||||
setLogData((l) => [m, ...l]);
|
setLogData((currentLog) => [newLogMessage, ...currentLog]);
|
||||||
socket.emit('logger', m);
|
socket.emit('logger', newLogMessage);
|
||||||
}
|
}
|
||||||
if (logData.length > MAX_MESSAGES) {
|
if (logData.length > MAX_MESSAGES) {
|
||||||
setLogData((l) => l.slice(1));
|
setLogData((currentLog) => currentLog.slice(1));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[logData, socket]
|
[logData.length, setLogData, socket],
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,7 +95,7 @@ export const LoggingProvider = ({ children }: LoggingProviderProps) => {
|
|||||||
(text: string) => {
|
(text: string) => {
|
||||||
_send(text, 'INFO');
|
_send(text, 'INFO');
|
||||||
},
|
},
|
||||||
[_send]
|
[_send],
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -106,7 +106,7 @@ export const LoggingProvider = ({ children }: LoggingProviderProps) => {
|
|||||||
(text: string) => {
|
(text: string) => {
|
||||||
_send(text, 'WARN');
|
_send(text, 'WARN');
|
||||||
},
|
},
|
||||||
[_send]
|
[_send],
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,7 +117,7 @@ export const LoggingProvider = ({ children }: LoggingProviderProps) => {
|
|||||||
(text: string) => {
|
(text: string) => {
|
||||||
_send(text, 'ERROR');
|
_send(text, 'ERROR');
|
||||||
},
|
},
|
||||||
[_send]
|
[_send],
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -125,7 +125,7 @@ export const LoggingProvider = ({ children }: LoggingProviderProps) => {
|
|||||||
*/
|
*/
|
||||||
const clearLog = useCallback(() => {
|
const clearLog = useCallback(() => {
|
||||||
setLogData([]);
|
setLogData([]);
|
||||||
}, []);
|
}, [setLogData]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoggingContext.Provider value={{ emitInfo, logData, emitWarning, emitError, clearLog }}>
|
<LoggingContext.Provider value={{ emitInfo, logData, emitWarning, emitError, clearLog }}>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import { createContext, ReactNode, useContext, useEffect, useState } from 'react';
|
import { createContext, ReactNode, useContext, useEffect, useState } from 'react';
|
||||||
import { serverURL } from 'common/api/apiConstants';
|
import { serverURL } from 'common/api/apiConstants';
|
||||||
import io, { Socket } from 'socket.io-client';
|
import io, { Socket } from 'socket.io-client';
|
||||||
@@ -26,7 +25,7 @@ export const useSocket = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function SocketProvider({ children }: SocketProviderProps) {
|
function SocketProvider({ children }: SocketProviderProps) {
|
||||||
const [socket, setSocket] = useState(null);
|
const [socket, setSocket] = useState<Socket | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const socketInstance = io(serverURL, { transports: ["websocket"] });
|
const socketInstance = io(serverURL, { transports: ["websocket"] });
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { useSocket } from './socketContext';
|
||||||
|
|
||||||
|
export default function useSubscription<T>(topic: string, initialState: T, requestString?: string) {
|
||||||
|
const socket = useSocket();
|
||||||
|
const [state, setState] = useState<T>(initialState);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!socket) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requestString) {
|
||||||
|
socket.emit(requestString);
|
||||||
|
} else {
|
||||||
|
socket.emit(`get-${topic}`);
|
||||||
|
}
|
||||||
|
socket.on(topic, setState);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.off(topic);
|
||||||
|
};
|
||||||
|
}, [requestString, socket, topic]);
|
||||||
|
|
||||||
|
return [state, setState] as const;
|
||||||
|
};
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
// @ts-nocheck
|
|
||||||
export default function useElectronEvent() {
|
export default function useElectronEvent() {
|
||||||
const isElectron = window?.process?.type === 'renderer';
|
const isElectron = window?.process?.type === 'renderer';
|
||||||
|
|
||||||
const sendToElectron = (channel: string, args: any) => {
|
const sendToElectron = (channel: string, args?: string | Record<string, any>) => {
|
||||||
if (isElectron) {
|
if (isElectron) {
|
||||||
window?.ipcRenderer.send(channel, args);
|
window?.ipcRenderer.send(channel, args);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
|
|
||||||
const refetchIntervalMs = 10000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @description utility hook to simplify query config
|
|
||||||
* @param namespace
|
|
||||||
* @param fn
|
|
||||||
*/
|
|
||||||
export const useFetch = (namespace, fn) => {
|
|
||||||
const { data, status, isError, refetch } = useQuery(namespace, fn, {
|
|
||||||
refetchInterval: refetchIntervalMs,
|
|
||||||
cacheTime: Infinity,
|
|
||||||
});
|
|
||||||
|
|
||||||
return { data, status, isError, refetch };
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { QueryFunction, QueryKey, useQuery, UseQueryOptions } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
interface UseFetchState {
|
||||||
|
data: unknown;
|
||||||
|
status: "loading" | "error" | "success";
|
||||||
|
isError: boolean;
|
||||||
|
refetch: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useFetch = ( key: QueryKey, fn: QueryFunction, options?: UseQueryOptions): UseFetchState => {
|
||||||
|
const { data, status, isError, refetch } = useQuery(key, fn, {
|
||||||
|
refetchInterval: 10000,
|
||||||
|
cacheTime: Infinity,
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, status, isError, refetch };
|
||||||
|
};
|
||||||
+29
-4
@@ -1,3 +1,5 @@
|
|||||||
|
import { OntimeEvent, OntimeEventEntry } from '../application-types/event';
|
||||||
|
|
||||||
import { formatTime } from './time';
|
import { formatTime } from './time';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -6,7 +8,7 @@ import { formatTime } from './time';
|
|||||||
* @returns {Object[]} Filtered events with calculated delays
|
* @returns {Object[]} Filtered events with calculated delays
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const getEventsWithDelay = (events) => {
|
export const getEventsWithDelay = (events: OntimeEventEntry[]) => {
|
||||||
if (events == null) return [];
|
if (events == null) return [];
|
||||||
|
|
||||||
const unfilteredEvents = [...events];
|
const unfilteredEvents = [...events];
|
||||||
@@ -33,7 +35,7 @@ export const getEventsWithDelay = (events) => {
|
|||||||
* @param {number} limit - max number of events to return
|
* @param {number} limit - max number of events to return
|
||||||
* @returns {Object[]} Event list with maximum <limit> objects
|
* @returns {Object[]} Event list with maximum <limit> objects
|
||||||
*/
|
*/
|
||||||
export const trimEventlist = (events, selectedId, limit) => {
|
export const trimEventlist = (events: OntimeEventEntry[], selectedId: string, limit: number) => {
|
||||||
if (events == null) return [];
|
if (events == null) return [];
|
||||||
|
|
||||||
const BEFORE = 2;
|
const BEFORE = 2;
|
||||||
@@ -53,6 +55,9 @@ export const trimEventlist = (events, selectedId, limit) => {
|
|||||||
return trimmedEvents;
|
return trimmedEvents;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type FormatEventListOptionsProp = {
|
||||||
|
showEnd?: boolean;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @description Returns list of events formatted to be displayed
|
* @description Returns list of events formatted to be displayed
|
||||||
* @param {Object[]} events - given events
|
* @param {Object[]} events - given events
|
||||||
@@ -62,7 +67,7 @@ export const trimEventlist = (events, selectedId, limit) => {
|
|||||||
* @param {boolean} [options.showEnd] - whether to show the end time
|
* @param {boolean} [options.showEnd] - whether to show the end time
|
||||||
* @returns {Object[]} Formatted list of events [{time: -, title: -, isNow, isNext}]
|
* @returns {Object[]} Formatted list of events [{time: -, title: -, isNow, isNext}]
|
||||||
*/
|
*/
|
||||||
export const formatEventList = (events, selectedId, nextId, options) => {
|
export const formatEventList = (events: OntimeEvent[], selectedId: string, nextId: string, options: FormatEventListOptionsProp) => {
|
||||||
if (events == null) return [];
|
if (events == null) return [];
|
||||||
const { showEnd = false } = options;
|
const { showEnd = false } = options;
|
||||||
|
|
||||||
@@ -71,7 +76,7 @@ export const formatEventList = (events, selectedId, nextId, options) => {
|
|||||||
// format list
|
// format list
|
||||||
const formattedEvents = [];
|
const formattedEvents = [];
|
||||||
for (const event of givenEvents) {
|
for (const event of givenEvents) {
|
||||||
const start = formatTime(event.timeStart)
|
const start = formatTime(event.timeStart);
|
||||||
const end = formatTime(event.timeEnd);
|
const end = formatTime(event.timeEnd);
|
||||||
|
|
||||||
formattedEvents.push({
|
formattedEvents.push({
|
||||||
@@ -86,3 +91,23 @@ export const formatEventList = (events, selectedId, nextId, options) => {
|
|||||||
|
|
||||||
return formattedEvents;
|
return formattedEvents;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Creates a safe duplicate of an event
|
||||||
|
* @param {object} event
|
||||||
|
* @return {object} clean event
|
||||||
|
*/
|
||||||
|
export const duplicateEvent = (event: OntimeEvent) => {
|
||||||
|
return {
|
||||||
|
type: 'event',
|
||||||
|
title: event.title,
|
||||||
|
subtitle: event.subtitle,
|
||||||
|
presenter: event.presenter,
|
||||||
|
note: event.note,
|
||||||
|
timeStart: event.timeStart,
|
||||||
|
timeEnd: event.timeEnd,
|
||||||
|
isPublic: event.isPublic,
|
||||||
|
skip: event.skip,
|
||||||
|
colour: event.colour,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import Color from 'color';
|
||||||
|
|
||||||
|
type ColourCombination = {
|
||||||
|
backgroundColor: string;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Selects text colour to maintain accessible contrast
|
||||||
|
* @param bgColour
|
||||||
|
* @return {{backgroundColor, color: string}}
|
||||||
|
*/
|
||||||
|
export const getAccessibleColour = (bgColour: string): ColourCombination => {
|
||||||
|
if (bgColour) {
|
||||||
|
try {
|
||||||
|
const textColor = Color(bgColour).isLight() ? 'black' : '#fffffa';
|
||||||
|
return { backgroundColor: bgColour, color: textColor };
|
||||||
|
} catch (error) {
|
||||||
|
console.log(`Unable to parse colour: ${bgColour}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { backgroundColor: '#000', color: "#fffffa" };
|
||||||
|
};
|
||||||
+11
-3
@@ -3,8 +3,16 @@ declare module '*.scss' {
|
|||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare namespace NodeJS {
|
declare global {
|
||||||
export interface ProcessEnv {
|
interface Window {
|
||||||
type: string
|
ipcRenderer: {
|
||||||
|
send: (channel: string, args?: string | object) => void;
|
||||||
|
};
|
||||||
|
process: {
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-anonymous-default-export
|
||||||
|
export default {}
|
||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
requestReorder,
|
requestReorder,
|
||||||
} from 'common/api/eventsApi.js';
|
} from 'common/api/eventsApi.js';
|
||||||
import Empty from 'common/components/state/Empty';
|
import Empty from 'common/components/state/Empty';
|
||||||
import { useFetch } from 'common/hooks/useFetch.js';
|
import { useFetch } from 'common/hooks/useFetch.ts';
|
||||||
import EventListMenu from 'features/menu/EventListMenu.jsx';
|
import EventListMenu from 'features/menu/EventListMenu.jsx';
|
||||||
|
|
||||||
import { CollapseContext } from '../../../common/context/CollapseContext';
|
import { CollapseContext } from '../../../common/context/CollapseContext';
|
||||||
|
|||||||
@@ -5,18 +5,22 @@ import { vi } from 'vitest';
|
|||||||
import { queryClientMock } from '../../../__mocks__/QueryClient.mock';
|
import { queryClientMock } from '../../../__mocks__/QueryClient.mock';
|
||||||
import MenuBar from '../MenuBar';
|
import MenuBar from '../MenuBar';
|
||||||
|
|
||||||
const onOpenHandler = vi.fn();
|
const onSettingOpenHandler = vi.fn();
|
||||||
const onCloseHandler = vi.fn();
|
const onSettingsCloseHandler = vi.fn();
|
||||||
const isOpen = false;
|
|
||||||
const onUploadOpenHandler = vi.fn();
|
const onUploadOpenHandler = vi.fn();
|
||||||
|
const isOpen = false;
|
||||||
|
|
||||||
const renderInMock = () => {
|
const renderInMock = () => {
|
||||||
render(
|
render(
|
||||||
<QueryClientProvider client={queryClientMock}>
|
<QueryClientProvider client={queryClientMock}>
|
||||||
<MenuBar isSettingsOpen={isOpen} onSettingsOpen={onOpenHandler}
|
<MenuBar
|
||||||
onSettingsClose={onCloseHandler} isUploadOpen={isOpen}
|
isSettingsOpen={isOpen}
|
||||||
onUploadOpen={onUploadOpenHandler} />
|
onSettingsOpen={onSettingOpenHandler}
|
||||||
</QueryClientProvider>,
|
onSettingsClose={onSettingsCloseHandler}
|
||||||
|
isUploadOpen={isOpen}
|
||||||
|
onUploadOpen={onUploadOpenHandler}
|
||||||
|
/>
|
||||||
|
</QueryClientProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,13 @@
|
|||||||
import Color from 'color';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import style from '../Table.module.scss';
|
import { getAccessibleColour } from '../../../common/utils/styleUtils';
|
||||||
|
|
||||||
/**
|
import style from '../Table.module.scss';
|
||||||
* Selects text colour to maintain accessible contrast
|
|
||||||
* @param bgColour
|
|
||||||
* @return {{backgroundColor, color: string}}
|
|
||||||
*/
|
|
||||||
const selCol = (bgColour) => {
|
|
||||||
if (bgColour != null && bgColour !== '') {
|
|
||||||
try {
|
|
||||||
const textColor = Color(bgColour).isLight() ? 'black' : 'white';
|
|
||||||
return { backgroundColor: bgColour, color: textColor };
|
|
||||||
} catch (error) {
|
|
||||||
console.log(`Unable to parse colour: ${bgColour}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function EventRow(props) {
|
export default function EventRow(props) {
|
||||||
const { row, index, selectedId, delay } = props;
|
const { row, index, selectedId, delay } = props;
|
||||||
const selected = row.original.id === selectedId;
|
const selected = row.original.id === selectedId;
|
||||||
const colours = selCol(row.original.colour);
|
const colours = getAccessibleColour(row.original.colour);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr {...row.getRowProps()} className={selected ? style.selected : ''} id={row.original.id}>
|
<tr {...row.getRowProps()} className={selected ? style.selected : ''} id={row.original.id}>
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
/* eslint-disable react/display-name */
|
/* eslint-disable react/display-name */
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { EVENT_TABLE, EVENTS_TABLE, VIEW_SETTINGS } from 'common/api/apiConstants';
|
|
||||||
import { fetchEvent } from 'common/api/eventApi';
|
|
||||||
import { fetchAllEvents } from 'common/api/eventsApi';
|
|
||||||
import { useSocket } from 'common/context/socketContext';
|
|
||||||
import { useFetch } from 'common/hooks/useFetch';
|
|
||||||
|
|
||||||
|
import { EVENT_TABLE, EVENTS_TABLE } from '../../common/api/apiConstants';
|
||||||
|
import { fetchEvent } from '../../common/api/eventApi';
|
||||||
|
import { fetchAllEvents } from '../../common/api/eventsApi';
|
||||||
|
import { useSocket } from '../../common/context/socketContext';
|
||||||
|
import { useFetch } from '../../common/hooks/useFetch';
|
||||||
import { getView } from '../../common/api/ontimeApi';
|
import { getView } from '../../common/api/ontimeApi';
|
||||||
|
import useSubscription from '../../common/context/useSubscription';
|
||||||
|
import { eventPlaceholderSettings } from '../../common/api/ontimeApi';
|
||||||
|
|
||||||
const withSocket = (Component) => {
|
const withSocket = (Component) => {
|
||||||
return (props) => {
|
return (props) => {
|
||||||
const { data: eventsData } = useFetch(EVENTS_TABLE, fetchAllEvents);
|
const { data: eventsData } = useFetch(EVENTS_TABLE, fetchAllEvents, {
|
||||||
const { data: genData } = useFetch(EVENT_TABLE, fetchEvent);
|
placeholderData: [],
|
||||||
|
});
|
||||||
|
const { data: genData } = useFetch(EVENT_TABLE, fetchEvent, {
|
||||||
|
placeholderData: eventPlaceholderSettings,
|
||||||
|
});
|
||||||
const { data: viewSettings } = useFetch(VIEW_SETTINGS, getView);
|
const { data: viewSettings } = useFetch(VIEW_SETTINGS, getView);
|
||||||
|
|
||||||
const [publicEvents, setPublicEvents] = useState([]);
|
|
||||||
const [backstageEvents, setBackstageEvents] = useState([]);
|
|
||||||
|
|
||||||
const socket = useSocket();
|
const socket = useSocket();
|
||||||
const [pres, setPres] = useState({
|
const [pres, setPres] = useState({
|
||||||
text: '',
|
text: '',
|
||||||
@@ -30,14 +33,16 @@ const withSocket = (Component) => {
|
|||||||
text: '',
|
text: '',
|
||||||
visible: false,
|
visible: false,
|
||||||
});
|
});
|
||||||
const [timer, setTimer] = useState({
|
const [publicSelectedId, setPublicSelectedId] = useState(null);
|
||||||
|
|
||||||
|
const [timer] = useSubscription('timer', {
|
||||||
clock: 0,
|
clock: 0,
|
||||||
running: 0,
|
running: 0,
|
||||||
isNegative: false,
|
isNegative: false,
|
||||||
startedAt: null,
|
startedAt: null,
|
||||||
expectedFinish: null,
|
expectedFinish: null,
|
||||||
});
|
});
|
||||||
const [titles, setTitles] = useState({
|
const [titles] = useSubscription('titles', {
|
||||||
titleNow: '',
|
titleNow: '',
|
||||||
subtitleNow: '',
|
subtitleNow: '',
|
||||||
presenterNow: '',
|
presenterNow: '',
|
||||||
@@ -45,7 +50,7 @@ const withSocket = (Component) => {
|
|||||||
subtitleNext: '',
|
subtitleNext: '',
|
||||||
presenterNext: '',
|
presenterNext: '',
|
||||||
});
|
});
|
||||||
const [publicTitles, setPublicTitles] = useState({
|
const [publicTitles] = useSubscription('publictitles', {
|
||||||
titleNow: '',
|
titleNow: '',
|
||||||
subtitleNow: '',
|
subtitleNow: '',
|
||||||
presenterNow: '',
|
presenterNow: '',
|
||||||
@@ -53,18 +58,10 @@ const withSocket = (Component) => {
|
|||||||
subtitleNext: '',
|
subtitleNext: '',
|
||||||
presenterNext: '',
|
presenterNext: '',
|
||||||
});
|
});
|
||||||
const [selectedId, setSelectedId] = useState(null);
|
const [selectedId] = useSubscription('selected-id', null);
|
||||||
const [nextId, setNextId] = useState(null);
|
const [nextId] = useSubscription('next-id', null);
|
||||||
const [publicSelectedId, setPublicSelectedId] = useState(null);
|
const [playback] = useSubscription('playstate', null);
|
||||||
const [general, setGeneral] = useState({
|
const [onAir] = useSubscription('onAir', false);
|
||||||
title: '',
|
|
||||||
url: '',
|
|
||||||
publicInfo: '',
|
|
||||||
backstageInfo: '',
|
|
||||||
endMessage: '',
|
|
||||||
});
|
|
||||||
const [playback, setPlayback] = useState(null);
|
|
||||||
const [onAir, setOnAir] = useState(false);
|
|
||||||
|
|
||||||
// Ask for update on load
|
// Ask for update on load
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -87,96 +84,29 @@ const withSocket = (Component) => {
|
|||||||
setLower({ ...data });
|
setLower({ ...data });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle timer
|
|
||||||
socket.on('timer', (data) => {
|
|
||||||
setTimer({ ...data });
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle playstate
|
|
||||||
socket.on('playstate', (data) => {
|
|
||||||
setPlayback(data);
|
|
||||||
});
|
|
||||||
socket.on('onAir', (data) => {
|
|
||||||
setOnAir(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle titles
|
|
||||||
socket.on('titles', (data) => {
|
|
||||||
setTitles({ ...data });
|
|
||||||
});
|
|
||||||
socket.on('publictitles', (data) => {
|
|
||||||
setPublicTitles({ ...data });
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle selected event
|
|
||||||
socket.on('selected-id', (data) => {
|
|
||||||
setSelectedId(data);
|
|
||||||
});
|
|
||||||
socket.on('publicselected-id', (data) => {
|
socket.on('publicselected-id', (data) => {
|
||||||
setPublicSelectedId(data);
|
setPublicSelectedId(data);
|
||||||
});
|
});
|
||||||
socket.on('next-id', (data) => {
|
|
||||||
setNextId(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ask for up to date data
|
// Ask for up to date data
|
||||||
socket.emit('get-messages');
|
socket.emit('get-messages');
|
||||||
|
|
||||||
// Ask for up to data
|
|
||||||
socket.emit('get-timer');
|
|
||||||
|
|
||||||
// ask for timer
|
|
||||||
socket.emit('get-timer');
|
|
||||||
|
|
||||||
// ask for playstate
|
|
||||||
socket.emit('get-playstate');
|
|
||||||
socket.emit('get-onAir');
|
|
||||||
|
|
||||||
// Ask for up titles
|
|
||||||
socket.emit('get-titles');
|
|
||||||
socket.emit('get-publictitles');
|
|
||||||
|
|
||||||
// Ask for up selected
|
|
||||||
socket.emit('get-selected-id');
|
|
||||||
socket.emit('get-next-id');
|
|
||||||
|
|
||||||
// Clear listeners
|
// Clear listeners
|
||||||
return () => {
|
return () => {
|
||||||
socket.off('messages-public');
|
socket.off('messages-public');
|
||||||
socket.off('messages-timer');
|
socket.off('messages-timer');
|
||||||
socket.off('messages-lower');
|
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]);
|
}, [socket]);
|
||||||
|
|
||||||
// Filter events only to pass down
|
|
||||||
useEffect(() => {
|
const publicEvents = useMemo(() => {
|
||||||
if (!eventsData) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// filter just events with title
|
|
||||||
if (Array.isArray(eventsData)) {
|
if (Array.isArray(eventsData)) {
|
||||||
const pe = eventsData.filter((d) => d.type === 'event' && d.title !== '' && d.isPublic);
|
return eventsData.filter((d) => d.type === 'event' && d.title !== '' && d.isPublic);
|
||||||
setPublicEvents(pe);
|
} else {
|
||||||
|
return [];
|
||||||
// everything goes backstage
|
|
||||||
setBackstageEvents(eventsData);
|
|
||||||
}
|
}
|
||||||
}, [eventsData]);
|
},[eventsData])
|
||||||
|
|
||||||
// Set general data
|
|
||||||
useEffect(() => {
|
|
||||||
if (!genData) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setGeneral(genData);
|
|
||||||
}, [genData]);
|
|
||||||
|
|
||||||
/********************************************/
|
/********************************************/
|
||||||
/*** + titleManager ***/
|
/*** + titleManager ***/
|
||||||
@@ -245,12 +175,12 @@ const withSocket = (Component) => {
|
|||||||
publicTitle={publicTitleManager}
|
publicTitle={publicTitleManager}
|
||||||
time={timeManager}
|
time={timeManager}
|
||||||
events={publicEvents}
|
events={publicEvents}
|
||||||
backstageEvents={backstageEvents}
|
backstageEvents={eventsData}
|
||||||
selectedId={selectedId}
|
selectedId={selectedId}
|
||||||
publicSelectedId={publicSelectedId}
|
publicSelectedId={publicSelectedId}
|
||||||
viewSettings={viewSettings}
|
viewSettings={viewSettings}
|
||||||
nextId={nextId}
|
nextId={nextId}
|
||||||
general={general}
|
general={genData}
|
||||||
onAir={onAir}
|
onAir={onAir}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import QRCode from 'react-qr-code';
|
import QRCode from 'react-qr-code';
|
||||||
import NavLogo from 'common/components/nav/NavLogo';
|
import NavLogo from 'common/components/nav/NavLogo';
|
||||||
import Paginator from 'common/components/paginator/Paginator';
|
|
||||||
import TitleSide from 'common/components/title-side/TitleSide';
|
import TitleSide from 'common/components/title-side/TitleSide';
|
||||||
import { formatDisplay } from 'common/utils/dateConfig';
|
import { formatDisplay } from 'common/utils/dateConfig';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { overrideStylesURL } from '../../../common/api/apiConstants';
|
import { overrideStylesURL } from '../../../common/api/apiConstants';
|
||||||
|
import Paginator from '../../../common/components/views/Paginator';
|
||||||
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
||||||
import { getEventsWithDelay } from '../../../common/utils/eventsManager';
|
import { getEventsWithDelay } from '../../../common/utils/eventsManager';
|
||||||
import { formatTime } from '../../../common/utils/time';
|
import { formatTime } from '../../../common/utils/time';
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import { useEffect, useRef, useState } from 'react';
|
|||||||
import QRCode from 'react-qr-code';
|
import QRCode from 'react-qr-code';
|
||||||
import { ReactComponent as Emptyimage } from 'assets/images/empty.svg';
|
import { ReactComponent as Emptyimage } from 'assets/images/empty.svg';
|
||||||
import NavLogo from 'common/components/nav/NavLogo';
|
import NavLogo from 'common/components/nav/NavLogo';
|
||||||
import Paginator from 'common/components/paginator/Paginator';
|
|
||||||
import { formatDisplay } from 'common/utils/dateConfig';
|
import { formatDisplay } from 'common/utils/dateConfig';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { overrideStylesURL } from '../../../common/api/apiConstants';
|
import { overrideStylesURL } from '../../../common/api/apiConstants';
|
||||||
|
import Paginator from '../../../common/components/views/Paginator';
|
||||||
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
||||||
import { formatTime } from '../../../common/utils/time';
|
import { formatTime } from '../../../common/utils/time';
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import QRCode from 'react-qr-code';
|
import QRCode from 'react-qr-code';
|
||||||
import NavLogo from 'common/components/nav/NavLogo';
|
import NavLogo from 'common/components/nav/NavLogo';
|
||||||
import Paginator from 'common/components/paginator/Paginator';
|
|
||||||
import TitleSide from 'common/components/title-side/TitleSide';
|
import TitleSide from 'common/components/title-side/TitleSide';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { overrideStylesURL } from '../../../common/api/apiConstants';
|
import { overrideStylesURL } from '../../../common/api/apiConstants';
|
||||||
|
import Paginator from '../../../common/components/views/Paginator';
|
||||||
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
||||||
import { formatTime } from '../../../common/utils/time';
|
import { formatTime } from '../../../common/utils/time';
|
||||||
import { titleVariants } from '../common/animation';
|
import { titleVariants } from '../common/animation';
|
||||||
@@ -24,7 +24,6 @@ export default function Public(props) {
|
|||||||
const [pageNumber, setPageNumber] = useState(0);
|
const [pageNumber, setPageNumber] = useState(0);
|
||||||
const [currentPage, setCurrentPage] = useState(0);
|
const [currentPage, setCurrentPage] = useState(0);
|
||||||
|
|
||||||
// Set window title
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = 'ontime - Public Screen';
|
document.title = 'ontime - Public Screen';
|
||||||
}, []);
|
}, []);
|
||||||
@@ -36,7 +35,6 @@ export default function Public(props) {
|
|||||||
|
|
||||||
// Format messages
|
// Format messages
|
||||||
const showPubl = publ.text !== '' && publ.visible;
|
const showPubl = publ.text !== '' && publ.visible;
|
||||||
|
|
||||||
const clock = formatTime(time.clock, formatOptions);
|
const clock = formatTime(time.clock, formatOptions);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -136,7 +134,7 @@ Public.propTypes = {
|
|||||||
publ: PropTypes.object,
|
publ: PropTypes.object,
|
||||||
publicTitle: PropTypes.object,
|
publicTitle: PropTypes.object,
|
||||||
time: PropTypes.object,
|
time: PropTypes.object,
|
||||||
events: PropTypes.object,
|
events: PropTypes.array,
|
||||||
publicSelectedId: PropTypes.string,
|
publicSelectedId: PropTypes.string,
|
||||||
general: PropTypes.object,
|
general: PropTypes.object,
|
||||||
viewSettings: PropTypes.object,
|
viewSettings: PropTypes.object,
|
||||||
|
|||||||
@@ -1583,6 +1583,25 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.3.tgz#3c90752792660c4b562ad73b3fbd68bf3bc7ae07"
|
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.3.tgz#3c90752792660c4b562ad73b3fbd68bf3bc7ae07"
|
||||||
integrity sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==
|
integrity sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==
|
||||||
|
|
||||||
|
"@types/color-convert@*":
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/color-convert/-/color-convert-2.0.0.tgz#8f5ee6b9e863dcbee5703f5a517ffb13d3ea4e22"
|
||||||
|
integrity sha512-m7GG7IKKGuJUXvkZ1qqG3ChccdIM/qBBo913z+Xft0nKCX4hAU/IxKwZBU4cpRZ7GS5kV4vOblUkILtSShCPXQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/color-name" "*"
|
||||||
|
|
||||||
|
"@types/color-name@*":
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
||||||
|
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
|
||||||
|
|
||||||
|
"@types/color@^3.0.3":
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/color/-/color-3.0.3.tgz#e6d8d72b7aaef4bb9fe80847c26c7c786191016d"
|
||||||
|
integrity sha512-X//qzJ3d3Zj82J9sC/C18ZY5f43utPbAJ6PhYt/M7uG6etcF6MRpKdN880KBy43B0BMzSfeT96MzrsNjFI3GbA==
|
||||||
|
dependencies:
|
||||||
|
"@types/color-convert" "*"
|
||||||
|
|
||||||
"@types/hoist-non-react-statics@^3.3.0":
|
"@types/hoist-non-react-statics@^3.3.0":
|
||||||
version "3.3.1"
|
version "3.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
|
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
|
||||||
|
|||||||
Reference in New Issue
Block a user