socket-io.ts 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  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) =>
  24. logger.error({ err: error }, '/admin'),
  25. );
  26. newSocket.on('error', (error) =>
  27. logger.error({ err: error }, '/admin'),
  28. );
  29. setSocket(newSocket);
  30. })
  31. .catch((error) =>
  32. logger.error({ err: error }, 'Failed to initialize admin WebSocket'),
  33. );
  34. return () => {
  35. cancelled = true;
  36. };
  37. }, [socket, setSocket]);
  38. };
  39. /** Returns the admin Socket.IO instance, or null before it is initialised. */
  40. export const useAdminSocket = (): Socket | null => {
  41. return useAtomValue(adminSocketAtom);
  42. };