socket-io.ts 1.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. import { useEffect } from 'react';
  2. import { atom, useAtomValue, useSetAtom } from 'jotai';
  3. import type { Socket } from 'socket.io-client';
  4. import loggerFactory from '~/utils/logger';
  5. const logger = loggerFactory('growi:cli:states:socket');
  6. // Socket.IO client is imported dynamically so that socket.io-client stays out
  7. // of the SSR bundle (.next/node_modules/) and can be listed in devDependencies.
  8. const adminSocketAtom = atom<Socket | null>(null);
  9. /**
  10. * Hook to initialise the admin Socket.IO connection.
  11. * Call this once from AdminLayout so every admin page shares the connection.
  12. */
  13. export const useSetupAdminSocket = (): void => {
  14. const setSocket = useSetAtom(adminSocketAtom);
  15. const socket = useAtomValue(adminSocketAtom);
  16. useEffect(() => {
  17. if (socket != null) return;
  18. let cancelled = false;
  19. import('socket.io-client')
  20. .then(({ default: io }) => {
  21. if (cancelled) return;
  22. const newSocket = io('/admin', { transports: ['websocket'] });
  23. newSocket.on('connect_error', (error) => logger.error('/admin', error));
  24. newSocket.on('error', (error) => logger.error('/admin', error));
  25. setSocket(newSocket);
  26. })
  27. .catch((error) =>
  28. logger.error('Failed to initialize admin WebSocket:', error),
  29. );
  30. return () => {
  31. cancelled = true;
  32. };
  33. }, [socket, setSocket]);
  34. };
  35. /** Returns the admin Socket.IO instance, or null before it is initialised. */
  36. export const useAdminSocket = (): Socket | null => {
  37. return useAtomValue(adminSocketAtom);
  38. };