Просмотр исходного кода

refactor socket.io states management

Yuki Takei 7 месяцев назад
Родитель
Сommit
0b76be834b

+ 1 - 0
apps/app/.eslintrc.js

@@ -33,6 +33,7 @@ module.exports = {
     'src/features/callout/**',
     'src/features/comment/**',
     'src/features/templates/**',
+    'src/states',
   ],
   settings: {
     // resolve path aliases by eslint-import-resolver-typescript

+ 3 - 3
apps/app/src/pages/[[...path]].page.tsx

@@ -35,8 +35,8 @@ import {
 } from '~/states/server-configurations';
 import { useHydrateServerConfigurationAtoms } from '~/states/server-configurations/hydrate';
 import { useHydrateSidebarAtoms } from '~/states/sidebar/hydrate';
+import { useSetupGlobalSocket, useSetupGlobalSocketForPage } from '~/states/socket-io';
 import { useEditingMarkdown } from '~/states/ui/editor';
-import { useSetupGlobalSocket, useSetupGlobalSocketForPage } from '~/stores/websocket';
 import { useSWRMUTxCurrentPageYjsData } from '~/stores/yjs';
 
 import { NEXT_JS_ROUTING_PAGE } from './[[...path]]/common-helpers';
@@ -142,9 +142,9 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
 
   const { trigger: mutateCurrentPageYjsDataFromApi } = useSWRMUTxCurrentPageYjsData();
 
-
+  // setup socket.io
   useSetupGlobalSocket();
-  useSetupGlobalSocketForPage(pageId);
+  useSetupGlobalSocketForPage();
 
   // Use custom hooks for navigation and routing
   useSameRouteNavigation();

+ 1 - 0
apps/app/src/states/socket-io/index.ts

@@ -0,0 +1 @@
+export * from './socket-io';

+ 79 - 0
apps/app/src/states/socket-io/socket-io.ts

@@ -0,0 +1,79 @@
+import { GLOBAL_SOCKET_NS } from '@growi/core/dist/swr';
+import { atom, useAtomValue, useSetAtom } from 'jotai';
+import { useCallback, useEffect } from 'react';
+import type { Socket } from 'socket.io-client';
+
+import { SocketEventName } from '~/interfaces/websocket';
+import { useCurrentPageId } from '~/states/page';
+import loggerFactory from '~/utils/logger';
+
+const logger = loggerFactory('growi:states:websocket');
+
+// WebSocket connection atom
+const globalSocketAtom = atom<Socket | null>(null);
+
+/**
+ * Hook to get WebSocket connection
+ */
+export const useGlobalSocket = (): Socket | null => {
+  return useAtomValue(globalSocketAtom);
+};
+
+/**
+ * Hook to initialize WebSocket connection
+ * Alternative to useSetupGlobalSocket
+ */
+export const useSetupGlobalSocket = (): void => {
+  const setSocket = useSetAtom(globalSocketAtom);
+  const socket = useAtomValue(globalSocketAtom);
+
+  const initializeSocket = useCallback(async () => {
+    try {
+      // Dynamic import of socket.io-client
+      const { io } = await import('socket.io-client');
+      const newSocket = io(GLOBAL_SOCKET_NS, {
+        transports: ['websocket'],
+      });
+
+      // Error handling
+      newSocket.on('error', (err) => {
+        logger.error(err);
+      });
+      newSocket.on('connect_error', (err) => {
+        logger.error('Failed to connect with websocket.', err);
+      });
+
+      // Store connection in atom
+      setSocket(newSocket);
+    } catch (error) {
+      logger.error('Failed to initialize WebSocket:', error);
+    }
+  }, [setSocket]);
+
+  useEffect(() => {
+    if (socket == null) {
+      initializeSocket();
+    }
+  }, [socket, initializeSocket]);
+};
+
+/**
+ * Hook for page-specific WebSocket room management
+ * Alternative to useSetupGlobalSocketForPage
+ */
+export const useSetupGlobalSocketForPage = (): void => {
+  const socket = useAtomValue(globalSocketAtom);
+  const [pageId] = useCurrentPageId();
+
+  useEffect(() => {
+    if (socket == null || pageId == null) {
+      return;
+    }
+
+    socket.emit(SocketEventName.JoinPage, { pageId });
+
+    return () => {
+      socket.emit(SocketEventName.LeavePage, { pageId });
+    };
+  }, [pageId, socket]);
+};

+ 1 - 70
apps/app/src/stores/websocket.tsx

@@ -1,78 +1,9 @@
-import { useEffect } from 'react';
-
-import {
-  useGlobalSocket, GLOBAL_SOCKET_KEY, GLOBAL_SOCKET_NS, useSWRStatic,
-} from '@growi/core/dist/swr';
+import { useSWRStatic } from '@growi/core/dist/swr';
 import type { Socket } from 'socket.io-client';
 import type { SWRResponse } from 'swr';
 
-import { SocketEventName } from '~/interfaces/websocket';
-import loggerFactory from '~/utils/logger';
-
-const logger = loggerFactory('growi:stores:ui');
-
-export const GLOBAL_ADMIN_SOCKET_NS = '/admin';
 export const GLOBAL_ADMIN_SOCKET_KEY = 'globalAdminSocket';
 
-/*
- * Global Socket
- */
-export const useSetupGlobalSocket = (): void => {
-
-  const { data, mutate } = useSWRStatic(GLOBAL_SOCKET_KEY);
-
-  useEffect(() => {
-    if (data != null) {
-      return;
-    }
-
-    mutate(async() => {
-      const { io } = await import('socket.io-client');
-      const socket = io(GLOBAL_SOCKET_NS, {
-        transports: ['websocket'],
-      });
-
-      socket.on('error', (err) => { logger.error(err) });
-      socket.on('connect_error', (err) => { logger.error('Failed to connect with websocket.', err) });
-
-      return socket;
-    });
-  }, [data, mutate]);
-};
-
-// comment out for porduction build error: https://github.com/weseek/growi/pull/7131
-/*
- * Global Admin Socket
- */
-// export const useSetupGlobalAdminSocket = (shouldInit: boolean): SWRResponse<Socket, Error> => {
-//   let socket: Socket | undefined;
-
-//   if (shouldInit) {
-//     socket = io(GLOBAL_ADMIN_SOCKET_NS, {
-//       transports: ['websocket'],
-//     });
-
-//     socket.on('error', (err) => { logger.error(err) });
-//     socket.on('connect_error', (err) => { logger.error('Failed to connect with websocket.', err) });
-//   }
-
-//   return useStaticSWR(shouldInit ? GLOBAL_ADMIN_SOCKET_KEY : null, socket);
-// };
-
 export const useGlobalAdminSocket = (): SWRResponse<Socket, Error> => {
   return useSWRStatic(GLOBAL_ADMIN_SOCKET_KEY);
 };
-
-export const useSetupGlobalSocketForPage = (pageId: string | undefined): void => {
-  const { data: socket } = useGlobalSocket();
-
-  useEffect(() => {
-    if (socket == null || pageId == null) { return }
-
-    socket.emit(SocketEventName.JoinPage, { pageId });
-
-    return () => {
-      socket.emit(SocketEventName.LeavePage, { pageId });
-    };
-  }, [pageId, socket]);
-};