mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-07 08:23:55 +00:00
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:
+16
-19
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,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;
|
||||
@@ -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,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 {
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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,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';
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user