V2 socket store (#254)

* refactor: create socket utility
* refactor: ensure a single subscription to topics
* refactor: replace usages of socket object with subscription store handler
This commit is contained in:
Carlos Valente
2022-11-20 18:08:49 +01:00
committed by GitHub
parent edd983dfd6
commit c1f87736eb
24 changed files with 219 additions and 387 deletions
+16 -19
View File
@@ -6,7 +6,6 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import ErrorBoundary from 'common/components/errorBoundary/ErrorBoundary';
import { AppContextProvider } from 'common/context/AppContext';
import { LoggingProvider } from 'common/context/LoggingContext';
import SocketProvider from 'common/context/socketContext';
import { ontimeQueryClient } from './common/queryClient';
import theme from './theme/theme';
@@ -45,24 +44,22 @@ function App() {
return (
<ChakraProvider resetCSS theme={theme}>
<SocketProvider>
<LoggingProvider>
<QueryClientProvider client={ontimeQueryClient}>
<AppContextProvider>
<BrowserRouter>
<div className='App'>
<ErrorBoundary>
<Suspense fallback={null}>
<AppRouter />
</Suspense>
</ErrorBoundary>
<ReactQueryDevtools initialIsOpen={false} />
</div>
</BrowserRouter>
</AppContextProvider>
</QueryClientProvider>
</LoggingProvider>
</SocketProvider>
<LoggingProvider>
<QueryClientProvider client={ontimeQueryClient}>
<AppContextProvider>
<BrowserRouter>
<div className='App'>
<ErrorBoundary>
<Suspense fallback={null}>
<AppRouter />
</Suspense>
</ErrorBoundary>
<ReactQueryDevtools initialIsOpen={false} />
</div>
</BrowserRouter>
</AppContextProvider>
</QueryClientProvider>
</LoggingProvider>
</ChakraProvider>
);
}
-2
View File
@@ -1,7 +1,6 @@
import { lazy, useEffect } from 'react';
import { Route, Routes, useLocation, useNavigate } from 'react-router-dom';
import { useSocketProvider } from './common/hooks/useSocketProvider';
import useAliases from './common/hooks-query/useAliases';
import withSocket from './features/viewers/ViewWrapper';
@@ -36,7 +35,6 @@ const MessageControl = lazy(() => import('features/control/message/MessageContro
const Info = lazy(() => import('features/info/InfoExport'));
export default function AppRouter() {
useSocketProvider();
const { data } = useAliases();
const location = useLocation();
const navigate = useNavigate();
+9 -6
View File
@@ -1,4 +1,6 @@
export const STATIC_PORT = 4001;
// REST stuff
export const EVENT_TABLE = ['event'];
export const ALIASES = ['aliases'];
export const USERFIELDS = ['userFields'];
@@ -9,12 +11,13 @@ export const OSC_SETTINGS = ['oscSettings'];
export const APP_SETTINGS = ['appSettings'];
export const VIEW_SETTINGS = ['viewSettings'];
export const FEAT_RUNDOWN = ['feat-rundown'];
export const FEAT_MESSAGECONTROL = ['feat-messagecontrol'];
export const FEAT_PLAYBACKCONTROL = ['feat-playbackcontrol'];
export const FEAT_INFO = ['feat-info'];
export const FEAT_CUESHEET = ['feat-cuesheet'];
export const TIMER = ['ontime-timer'];
// websocket stuff
export const FEAT_CUESHEET = 'feat-cuesheet';
export const FEAT_INFO = 'feat-info';
export const FEAT_MESSAGECONTROL = 'feat-messagecontrol';
export const FEAT_PLAYBACKCONTROL = 'feat-playbackcontrol';
export const FEAT_RUNDOWN = 'feat-rundown';
export const TIMER = 'ontime-timer';
/**
* @description finds server path given the current location, it
+3 -6
View File
@@ -3,8 +3,7 @@ import { createContext, ReactNode, useCallback, useEffect, useState } from 'reac
import { generateId } from '../utils/generate_id';
import { nowInMillis, stringFromMillis } from '../utils/time';
import { useSocket } from './socketContext';
import socket from '../utils/socket';
type LOG_LEVEL = 'INFO' | 'WARN' | 'ERROR';
type Log = {
id: string;
@@ -40,15 +39,13 @@ export const LoggingContext = createContext<LoggingProviderState>({
export const LoggingProvider = ({ children }: LoggingProviderProps) => {
const MAX_MESSAGES = 100;
const socket = useSocket();
const [logData, setLogData] = useState<Log[]>([]);
const origin = 'USER';
// todo: use react-query store
// todo: useSubscription or feature
// handle incoming messages
useEffect(() => {
if (socket == null) return;
// Ask for log data
socket.emit('get-logger');
socket.on('logger', (data: Log) => {
@@ -1,43 +0,0 @@
import { createContext, ReactNode, useContext, useEffect, useState } from 'react';
import { serverURL } from 'common/api/apiConstants';
import io, { Socket } from 'socket.io-client';
interface SocketProviderState {
socket: Socket | null;
emit: <T>(topic: string, payload?: T) => void;
on: <T>(topic: string, callback: (data: T) => void) => void;
off: (topic: string) => void;
}
type SocketProviderProps = {
children: ReactNode;
};
const SocketContext = createContext<SocketProviderState>({
socket: null,
emit: () => {},
on: () => {},
off: () => {}
});
export const useSocket = () => {
return useContext(SocketContext);
};
function SocketProvider({ children }: SocketProviderProps) {
const [socket, setSocket] = useState<Socket | null>(null);
useEffect(() => {
const socketInstance = io(serverURL, { transports: ["websocket"] });
setSocket(socketInstance);
return () => {
socketInstance.disconnect();
};
}, []);
return (
<SocketContext.Provider value={socket}>{children}</SocketContext.Provider>
);
}
export default SocketProvider;
+140
View File
@@ -0,0 +1,140 @@
import { useQuery } from '@tanstack/react-query';
import { ontimeQueryClient as queryClient } from 'common/queryClient';
import socket, { subscribeOnce } from 'common/utils/socket';
import {
FEAT_CUESHEET,
FEAT_INFO,
FEAT_MESSAGECONTROL,
FEAT_PLAYBACKCONTROL,
FEAT_RUNDOWN,
TIMER,
} from '../api/apiConstants';
function createSocketHook<T>(key: string, defaultValue: T | null = null) {
subscribeOnce<T>(key, (data) => queryClient.setQueryData([key], data));
// retrieves data from the cache or null if non-existent
// we need the null because useQuery can't receive undefined
const fetcher = () => (queryClient.getQueryData([key]) ?? defaultValue) as T | null;
return () => useQuery([key], fetcher, { placeholderData: defaultValue });
}
const emptyRundown = {
selectEventId: null,
nextEventId: null,
playback: null,
};
export const useRundownEditor = createSocketHook(FEAT_RUNDOWN, emptyRundown);
const emptyMessageControl = {
presenter: {
text: '',
visible: false,
},
public: {
text: '',
visible: false,
},
lower: {
text: '',
visible: false,
},
onAir: false,
};
export const useMessageControl = createSocketHook(FEAT_MESSAGECONTROL, emptyMessageControl);
export const setMessage = () => ({
presenterText: (payload: string) => socket.emit('set-timer-message-text', payload),
presenterVisible: (payload: boolean) => socket.emit('set-timer-message-visible', payload),
publicText: (payload: string) => socket.emit('set-public-message-text', payload),
publicVisible: (payload: boolean) => socket.emit('set-public-message-visible', payload),
lowerText: (payload: string) => socket.emit('set-lower-message-text', payload),
lowerVisible: (payload: boolean) => socket.emit('set-lower-message-visible', payload),
onAir: (payload: boolean) => socket.emit('set-onAir', payload),
});
export const emptyPlaybackControl = {
playback: 'stop',
selectedEventId: null,
numEvents: 0,
};
export const usePlaybackControl = createSocketHook(FEAT_PLAYBACKCONTROL, emptyPlaybackControl);
export const resetPlayback = () => {
const cacheData = queryClient.getQueryData([FEAT_PLAYBACKCONTROL]) as Record<string, unknown>;
queryClient.setQueryData([FEAT_PLAYBACKCONTROL], {
...cacheData,
playback: 'stop',
selectedEventId: null,
});
};
export const setPlayback = {
start: () => socket.emit('set-start'),
pause: () => socket.emit('set-pause'),
roll: () => socket.emit('set-roll'),
previous: () => {
socket.emit('set-previous');
resetPlayback();
},
next: () => {
socket.emit('set-next');
resetPlayback();
},
stop: () => {
socket.emit('set-stop');
resetPlayback();
},
reload: () => {
socket.emit('set-reload');
resetPlayback();
},
delay: (amount: number) => {
socket.emit('set-delay', amount);
},
};
export const emptyInfo = {
titles: {
titleNow: '',
subtitleNow: '',
presenterNow: '',
noteNow: '',
titleNext: '',
subtitleNext: '',
presenterNext: '',
noteNext: '',
},
playback: 'stop',
selectedEventId: null,
selectedEventIndex: null,
numEvents: 0,
};
export const useInfoPanel = createSocketHook(FEAT_INFO, emptyInfo);
export const emptyCuesheet = {
selectedEventId: null,
titleNow: '',
};
export const useCuesheet = createSocketHook(FEAT_CUESHEET, emptyCuesheet);
export const setEventPlayback = {
loadEvent: (eventId: string) => socket.emit('set-loadid', eventId),
startEvent: (eventId: string) => socket.emit('set-startid', eventId),
pause: () => socket.emit('set-pause'),
};
const emptyTimer = {
clock: 0,
running: 0,
isNegative: false,
startedAt: null,
expectedFinish: null,
};
export const useTimer = createSocketHook(TIMER, emptyTimer);
@@ -1,252 +0,0 @@
import { useCallback, useEffect, useMemo } from 'react';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import {
FEAT_CUESHEET,
FEAT_INFO,
FEAT_MESSAGECONTROL,
FEAT_PLAYBACKCONTROL,
FEAT_RUNDOWN,
TIMER,
} from '../api/apiConstants';
import { useSocket } from '../context/socketContext';
export const useRundownProvider = () => {
const { data } = useQuery(FEAT_RUNDOWN, () => undefined, {
cacheTime: Infinity,
staleTime: Infinity,
});
const placeholder = useMemo(
() => ({
selectedEventId: null,
nextEventId: null,
playback: null,
}),
[],
);
return data ?? placeholder;
};
export const useMessageControlProvider = () => {
const socket = useSocket();
const { data } = useQuery(FEAT_MESSAGECONTROL, () => undefined, {
cacheTime: Infinity,
staleTime: Infinity,
});
const placeholder = useMemo(
() => ({
presenter: {
text: '',
visible: false,
},
public: {
text: '',
visible: false,
},
lower: {
text: '',
visible: false,
},
onAir: false,
}),
[],
);
const returnData = data ?? placeholder;
const setMessage = useMemo(
() => ({
presenterText: (payload) => socket.emit('set-timer-message-text', payload),
presenterVisible: (payload) => socket.emit('set-timer-message-visible', payload),
publicText: (payload) => socket.emit('set-public-message-text', payload),
publicVisible: (payload) => socket.emit('set-public-message-visible', payload),
lowerText: (payload) => socket.emit('set-lower-message-text', payload),
lowerVisible: (payload) => socket.emit('set-lower-message-visible', payload),
onAir: (payload) => socket.emit('set-onAir', payload),
}),
[socket],
);
return { data: returnData, setMessage };
};
export const usePlaybackControlProvider = () => {
const socket = useSocket();
const queryClient = useQueryClient();
const { data } = useQuery(FEAT_PLAYBACKCONTROL, () => undefined, {
cacheTime: Infinity,
staleTime: Infinity,
});
const placeholder = useMemo(
() => ({
playback: 'stop',
selectedEventId: null,
numEvents: 0,
}),
[],
);
const resetData = useCallback(() => {
queryClient.setQueryData(FEAT_PLAYBACKCONTROL, (data) => ({
...data,
playback: 'stop',
selectedEventId: null,
}));
}, [queryClient]);
const setPlayback = useMemo(
() => ({
start: () => socket.emit('set-start'),
pause: () => socket.emit('set-pause'),
roll: () => socket.emit('set-roll'),
previous: () => {
socket.emit('set-previous');
resetData();
},
next: () => {
socket.emit('set-next');
resetData();
},
stop: () => {
socket.emit('set-stop');
resetData();
},
reload: () => {
socket.emit('set-reload');
resetData();
},
delay: (amount) => {
socket.emit('set-delay', amount);
},
}),
[resetData, socket],
);
const returnData = data ?? placeholder;
return { data: returnData, resetData, setPlayback };
};
export const useInfoProvider = () => {
const { data } = useQuery(FEAT_INFO, () => undefined, {
cacheTime: Infinity,
staleTime: Infinity,
});
const placeholder = useMemo(
() => ({
titles: {
titleNow: '',
subtitleNow: '',
presenterNow: '',
noteNow: '',
titleNext: '',
subtitleNext: '',
presenterNext: '',
noteNext: '',
},
playback: 'stop',
selectedEventId: null,
selectedEventIndex: null,
numEvents: 0,
}),
[],
);
return data ?? placeholder;
};
export const useCuesheetProvider = () => {
const { data } = useQuery(FEAT_CUESHEET, () => undefined, {
cacheTime: Infinity,
staleTime: Infinity,
});
const placeholder = useMemo(
() => ({
selectedEventId: null,
titleNow: '',
}),
[],
);
return data ?? placeholder;
};
export const useTimerProvider = () => {
const { data } = useQuery(TIMER, () => undefined, {
cacheTime: Infinity,
staleTime: Infinity,
});
const placeholder = useMemo(
() => ({
clock: 0,
current: null,
secondaryTimer: null,
duration: null,
expectedFinish: null,
startedAt: null,
}),
[],
);
return data ?? placeholder;
};
export const useEventProvider = (eventId) => {
const socket = useSocket();
const setPlayback = useMemo(() => ({
loadEvent: () => socket.emit('set-loadid', eventId),
startEvent: () => socket.emit('set-startid', eventId),
pause: () => socket.emit('set-pause'),
}), [socket]);
return { setPlayback };
};
export const useSocketProvider = () => {
const queryClient = useQueryClient();
const socket = useSocket();
useEffect(() => {
if (!socket) {
return;
}
socket.emit('get-ontime-feat-eventlist');
socket.on('ontime-feat-rundown', (featureData) => {
queryClient.setQueryData(FEAT_RUNDOWN, () => featureData);
});
socket.emit('get-ontime-feat-messagecontrol');
socket.on('ontime-feat-messagecontrol', (featureData) => {
queryClient.setQueryData(FEAT_MESSAGECONTROL, () => featureData);
});
socket.emit('get-ontime-feat-playbackcontrol');
socket.on('ontime-feat-playbackcontrol', (featureData) => {
queryClient.setQueryData(FEAT_PLAYBACKCONTROL, () => featureData);
});
socket.emit('get-ontime-feat-info');
socket.on('ontime-feat-info', (featureData) => {
queryClient.setQueryData(FEAT_INFO, () => featureData);
});
socket.emit('get-ontime-feat-cuesheet');
socket.on('ontime-feat-cuesheet', (featureData) => {
queryClient.setQueryData(FEAT_CUESHEET, () => featureData);
});
socket.emit('get-ontime-timer');
socket.on('ontime-timer', (featureData) => {
queryClient.setQueryData(TIMER, () => featureData);
});
return () => {
socket.off('ontime-feat-rundown');
socket.off('ontime-feat-messagecontrol');
socket.off('ontime-feat-playbackcontrol');
socket.off('ontime-feat-info');
socket.off('ontime-feat-cuesheet');
socket.off('ontime-timer');
};
}, [queryClient, socket]);
};
+1 -6
View File
@@ -1,16 +1,11 @@
import { useEffect, useState } from 'react';
import { useSocket } from '../context/socketContext';
import socket from '../utils/socket';
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 {
+17
View File
@@ -0,0 +1,17 @@
import { serverURL } from 'common/api/apiConstants';
import { io } from 'socket.io-client';
const socket = io(serverURL, { transports: ['websocket'] });
const subscriptions = new Set();
export function subscribeOnce<T>(key: string, callback: (data: T) => void, requestString?: string) {
if (subscriptions.has(key)) {
return;
}
subscriptions.add(key);
requestString ? socket.emit(requestString) : socket.emit(`get-${key}`);
socket.on(key, callback);
}
export default socket;
@@ -1,6 +1,5 @@
import { QueryClientProvider } from '@tanstack/react-query';
import { render, screen } from '@testing-library/react';
import SocketProvider from 'common/context/socketContext';
import { queryClientMock } from '../../../__mocks__/QueryClient.mock';
import MessageControl from '../message/MessageControl';
@@ -11,9 +10,7 @@ import MessageControl from '../message/MessageControl';
const MockMessageControl = () => {
return (
<QueryClientProvider client={queryClientMock}>
<SocketProvider>
<MessageControl />
</SocketProvider>
<MessageControl />
</QueryClientProvider>
);
};
@@ -1,6 +1,5 @@
import { QueryClientProvider } from '@tanstack/react-query';
import { render, screen } from '@testing-library/react';
import SocketProvider from 'common/context/socketContext';
import { queryClientMock } from '../../../__mocks__/QueryClient.mock';
import PlaybackControl from '../playback/PlaybackControl';
@@ -11,10 +10,8 @@ test('check that playback control renders', async () => {
// render without failing
render(
<QueryClientProvider client={queryClientMock}>
<SocketProvider>
<PlaybackControl />
</SocketProvider>
</QueryClientProvider>
<PlaybackControl />
</QueryClientProvider>,
);
// Text labels for times
@@ -3,7 +3,7 @@ import { IoMicOffOutline } from '@react-icons/all-files/io5/IoMicOffOutline';
import { IoMicSharp } from '@react-icons/all-files/io5/IoMicSharp';
import CopyTag from '../../../common/components/osc-tag/CopyTag';
import { useMessageControlProvider } from '../../../common/hooks/useSocketProvider';
import { setMessage, useMessageControl } from '../../../common/hooks/useSocket';
import { tooltipDelayMid } from '../../../ontimeConfig';
import InputRow from './InputRow';
@@ -11,7 +11,7 @@ import InputRow from './InputRow';
import style from './MessageControl.module.scss';
export default function MessageControl() {
const { data, setMessage } = useMessageControlProvider();
const { data } = useMessageControl();
return (
<>
@@ -3,7 +3,7 @@ import { IoPause } from '@react-icons/all-files/io5/IoPause';
import { IoPlay } from '@react-icons/all-files/io5/IoPlay';
import { IoTimeOutline } from '@react-icons/all-files/io5/IoTimeOutline';
import { usePlaybackControlProvider } from '../../../common/hooks/useSocketProvider';
import { setPlayback } from '../../../common/hooks/useSocket';
import { Playstate } from '../../../common/models/OntimeTypes';
import { tooltipDelayMid } from '../../../ontimeConfig';
@@ -20,7 +20,6 @@ interface PlaybackProps {
export default function Playback(props: PlaybackProps) {
const { playback, selectedId, noEvents } = props;
const isRolling = playback === 'roll';
const { setPlayback } = usePlaybackControlProvider();
return (
<div className={style.playbackContainer}>
@@ -1,4 +1,4 @@
import { usePlaybackControlProvider } from '../../../common/hooks/useSocketProvider';
import { usePlaybackControl } from '../../../common/hooks/useSocket';
import { Playstate } from '../../../common/models/OntimeTypes';
import PlaybackButtons from './PlaybackButtons';
@@ -7,7 +7,7 @@ import PlaybackTimer from './PlaybackTimer';
import style from './PlaybackControl.module.scss';
export default function PlaybackControl() {
const { data } = usePlaybackControlProvider();
const { data } = usePlaybackControl();
return (
<div className={style.mainContainer}>
@@ -1,10 +1,7 @@
import { Tooltip } from '@chakra-ui/react';
import TimerDisplay from 'common/components/countdown/TimerDisplay';
import {
usePlaybackControlProvider,
useTimerProvider,
} from '../../../common/hooks/useSocketProvider';
import { setPlayback, useTimer } from '../../../common/hooks/useSocket';
import { Playstate } from '../../../common/models/OntimeTypes';
import { millisToSeconds } from '../../../common/utils/dateConfig';
import { stringFromMillis } from '../../../common/utils/time';
@@ -21,8 +18,7 @@ interface PlaybackTimerProps {
export default function PlaybackTimer(props: PlaybackTimerProps) {
const { playback, selectedId } = props;
const { setPlayback } = usePlaybackControlProvider();
const timerData = useTimerProvider();
const { data: timerData } = useTimer();
const started = stringFromMillis(timerData.startedAt, true);
const finish = stringFromMillis(timerData.expectedFinish, true);
const isRolling = playback === 'roll';
@@ -4,7 +4,7 @@ import { IoPlaySkipBack } from '@react-icons/all-files/io5/IoPlaySkipBack';
import { IoPlaySkipForward } from '@react-icons/all-files/io5/IoPlaySkipForward';
import { IoStop } from '@react-icons/all-files/io5/IoStop';
import { usePlaybackControlProvider } from '../../../common/hooks/useSocketProvider';
import { setPlayback } from '../../../common/hooks/useSocket';
import { Playstate } from '../../../common/models/OntimeTypes';
import { tooltipDelayMid } from '../../../ontimeConfig';
@@ -20,7 +20,6 @@ interface TransportProps {
export default function Transport(props: TransportProps) {
const { playback, selectedId, noEvents } = props;
const { setPlayback } = usePlaybackControlProvider();
const isRolling = playback === 'roll';
return (
+2 -2
View File
@@ -1,4 +1,4 @@
import { useInfoProvider } from '../../common/hooks/useSocketProvider';
import { useInfoPanel } from '../../common/hooks/useSocket';
import InfoLogger from './InfoLogger';
import InfoNif from './InfoNif';
@@ -7,7 +7,7 @@ import InfoTitle from './InfoTitle';
import style from './Info.module.scss';
export default function Info() {
const data = useInfoProvider();
const { data } = useInfoPanel();
const titlesNow = {
title: data.titles.titleNow,
@@ -1,6 +1,5 @@
import { QueryClientProvider } from '@tanstack/react-query';
import { render, screen } from '@testing-library/react';
import SocketProvider from 'common/context/socketContext';
import { queryClientMock } from '../../../__mocks__/QueryClient.mock';
import Info from '../Info';
@@ -10,10 +9,8 @@ test('check static info render', async () => {
// render without failing
render(
<QueryClientProvider client={queryClientMock}>
<SocketProvider>
<Info />
</SocketProvider>
</QueryClientProvider>
<Info />
</QueryClientProvider>,
);
// Info titles
+3 -2
View File
@@ -9,7 +9,7 @@ import {
import Empty from 'common/components/state/Empty';
import { CursorContext } from 'common/context/CursorContext';
import { useEventAction } from 'common/hooks/useEventAction';
import { useRundownProvider } from 'common/hooks/useSocketProvider';
import { useRundownEditor } from 'common/hooks/useSocket';
import { duplicateEvent } from 'common/utils/eventsManager';
import { useAtomValue } from 'jotai';
import PropTypes from 'prop-types';
@@ -23,6 +23,8 @@ import style from './Rundown.module.scss';
export default function Rundown(props) {
const { entries } = props;
// Todo: add selectedId and nextId to rundown editor hook
const { data } = useRundownEditor();
const { cursor, moveCursorUp, moveCursorDown, moveCursorTo, isCursorLocked } =
useContext(CursorContext);
const startTimeIsLastEnd = useAtomValue(startTimeIsLastEndAtom);
@@ -30,7 +32,6 @@ export default function Rundown(props) {
const { addEvent, reorderEvent } = useEventAction();
const cursorRef = createRef();
const showQuickEntry = useAtomValue(showQuickEntryAtom);
const data = useRundownProvider();
const [selectedId] = useSubscription('selected-id', null);
const [nextId] = useSubscription('next-id', null);
@@ -17,7 +17,7 @@ import TooltipActionBtn from 'common/components/buttons/TooltipActionBtn';
import { getAccessibleColour } from 'common/utils/styleUtils';
import { useAtom } from 'jotai';
import { useEventProvider } from '../../../common/hooks/useSocketProvider';
import { setEventPlayback } from '../../../common/hooks/useSocket';
import { Playstate } from '../../../common/models/OntimeTypes';
import { tooltipDelayMid } from '../../../ontimeConfig';
import { EventItemActions } from '../RundownEntry';
@@ -80,7 +80,6 @@ export default function EventBlock(props: EventBlockProps) {
} = props;
const [openId, setOpenId] = useAtom(editorEventId);
const { setPlayback } = useEventProvider(eventId);
const [blockTitle, setBlockTitle] = useState<string>(title || '');
const binderColours = colour && getAccessibleColour(colour);
@@ -157,7 +156,7 @@ export default function EventBlock(props: EventBlockProps) {
icon={selected ? <IoPlayBackOutline /> : <IoReload />}
disabled={skip}
{...blockBtnStyle}
clickHandler={() => setPlayback.loadEvent()}
clickHandler={() => setEventPlayback.loadEvent(eventId)}
tabIndex={-1}
/>
<TooltipActionBtn
@@ -168,7 +167,7 @@ export default function EventBlock(props: EventBlockProps) {
disabled={skip}
{...blockBtnStyle}
variant={eventIsPlaying ? 'solid' : 'ghost'}
clickHandler={() => setPlayback.startEvent()}
clickHandler={() => setEventPlayback.startEvent(eventId)}
backgroundColor={eventIsPlaying ? '#58A151' : undefined}
tabIndex={-1}
/>
+2 -2
View File
@@ -9,7 +9,7 @@ import PropTypes from 'prop-types';
import { TableSettingsContext } from '../../common/context/TableSettingsContext';
import useFullscreen from '../../common/hooks/useFullscreen';
import { useTimerProvider } from '../../common/hooks/useSocketProvider';
import { useTimer } from '../../common/hooks/useSocket';
import useEvent from '../../common/hooks-query/useEvent';
import { formatDisplay, millisToSeconds } from '../../common/utils/dateConfig';
import { formatTime } from '../../common/utils/time';
@@ -22,7 +22,7 @@ import style from './Table.module.scss';
export default function TableHeader({ handleCSVExport, featureData }) {
const { followSelected, showSettings, toggleTheme, toggleSettings, toggleFollow } =
useContext(TableSettingsContext);
const timer = useTimerProvider();
const { data: timer } = useTimer();
const { isFullScreen, toggleFullScreen } = useFullscreen();
const { data: event } = useEvent();
+3 -3
View File
@@ -3,7 +3,7 @@ import { useCallback, useContext, useEffect } from 'react';
import { requestPatchEvent } from '../../common/api/eventsApi';
import { TableSettingsContext } from '../../common/context/TableSettingsContext';
import useMutateEvents from '../../common/hooks/useMutateEvents';
import { useCuesheetProvider } from '../../common/hooks/useSocketProvider';
import { useCuesheet } from '../../common/hooks/useSocket';
import useRundown from '../../common/hooks-query/useRundown';
import useUserFields from '../../common/hooks-query/useUserFields';
@@ -16,10 +16,10 @@ import style from './Table.module.scss';
export default function TableWrapper() {
const { data: events } = useRundown();
const { data: userFields } = useUserFields();
const { data: featureData } = useCuesheet();
const mutation = useMutateEvents(requestPatchEvent);
const { theme } = useContext(TableSettingsContext);
const featureData = useCuesheetProvider();
// Set window title
useEffect(() => {
document.title = 'ontime - Cuesheet';
+3 -8
View File
@@ -1,21 +1,20 @@
/* eslint-disable react/display-name */
import { useEffect, useMemo, useState } from 'react';
import { useSocket } from '../../common/context/socketContext';
import { useMessageControlProvider } from '../../common/hooks/useSocketProvider';
import { useMessageControl } from '../../common/hooks/useSocket';
import useSubscription from '../../common/hooks/useSubscription';
import useEvent from '../../common/hooks-query/useEvent';
import useRundown from '../../common/hooks-query/useRundown';
import useViewSettings from '../../common/hooks-query/useViewSettings';
import socket from '../../common/utils/socket';
const withSocket = (Component) => {
return (props) => {
const { data: eventsData } = useRundown();
const { data: genData } = useEvent();
const { data: viewSettings } = useViewSettings();
const { data: messages } = useMessageControlProvider();
const { data: messages } = useMessageControl();
const socket = useSocket();
const [publicSelectedId, setPublicSelectedId] = useState(null);
const [timer] = useSubscription('timer', {
@@ -44,13 +43,9 @@ const withSocket = (Component) => {
const [selectedId] = useSubscription('selected-id', null);
const [nextId] = useSubscription('next-id', null);
const [playback] = useSubscription('playstate', null);
const [onAir] = useSubscription('onAir', false);
// Ask for update on load
useEffect(() => {
if (!socket) {
return;
}
// todo: remove
socket.on('publicselected-id', (data) => {
setPublicSelectedId(data);
+4 -4
View File
@@ -136,7 +136,7 @@ export class EventTimer extends Timer {
nextEventId: this.nextEventId,
playback: this.state,
};
this.socket.send('ontime-feat-rundown', featureData);
this.socket.send('feat-rundown', featureData);
}
/**
@@ -149,7 +149,7 @@ export class EventTimer extends Timer {
selectedEventId: this.selectedEventId,
numEvents: this.rundown.length,
};
this.socket.send('ontime-feat-playbackcontrol', featureData);
this.socket.send('feat-playbackcontrol', featureData);
}
/**
@@ -164,7 +164,7 @@ export class EventTimer extends Timer {
selectedEventIndex: this.selectedEventIndex,
numEvents: this.rundown.length,
};
this.socket.send('ontime-feat-info', featureData);
this.socket.send('feat-info', featureData);
}
_broadcastFeatureCuesheet() {
@@ -175,7 +175,7 @@ export class EventTimer extends Timer {
numEvents: this.rundown.length,
titleNow: this.titles.titleNow,
};
this.socket.send('ontime-feat-cuesheet', featureData);
this.socket.send('feat-cuesheet', featureData);
}
/**