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

Add real-time seen users. count update via Socket io

Ryotaro Nagahara 2 месяцев назад
Родитель
Сommit
11975d7e72

+ 2 - 0
apps/app/src/client/components/Page/EditablePageEffects.tsx

@@ -1,5 +1,6 @@
 import type { JSX } from 'react';
 
+import { usePageSeenUsersUpdatedEffect } from '~/client/services/side-effects/page-seen-users-updated';
 import { usePageUpdatedEffect } from '~/client/services/side-effects/page-updated';
 import {
   useAwarenessSyncingEffect,
@@ -9,6 +10,7 @@ import {
 
 export const EditablePageEffects = (): JSX.Element => {
   usePageUpdatedEffect();
+  usePageSeenUsersUpdatedEffect();
 
   useCurrentPageYjsDataAutoLoadEffect();
   useNewlyYjsDataSyncingEffect();

+ 54 - 0
apps/app/src/client/services/side-effects/page-seen-users-updated.ts

@@ -0,0 +1,54 @@
+import { useCallback, useEffect } from 'react';
+
+import { SocketEventName } from '~/interfaces/websocket';
+import { useCurrentPageId } from '~/states/page';
+import { useGlobalSocket } from '~/states/socket-io';
+import { useSWRxPageInfo } from '~/stores/page';
+
+export const usePageSeenUsersUpdatedEffect = (): void => {
+  const socket = useGlobalSocket();
+  const currentPageId = useCurrentPageId();
+  const { mutate: mutatePageInfo } = useSWRxPageInfo(currentPageId);
+
+  const seenUsersUpdatedHandler = useCallback(
+    (data: {
+      s2cMessagePageSeenUsersUpdated: {
+        pageId: string;
+        seenUserIds: string[];
+        seenUsersCount: number;
+      };
+    }) => {
+      const { s2cMessagePageSeenUsersUpdated } = data;
+
+      if (
+        currentPageId != null &&
+        currentPageId === s2cMessagePageSeenUsersUpdated.pageId
+      ) {
+        mutatePageInfo(
+          (currentData) => {
+            if (currentData == null) return currentData;
+            return {
+              ...currentData,
+              seenUserIds: s2cMessagePageSeenUsersUpdated.seenUserIds,
+              sumOfSeenUsers: s2cMessagePageSeenUsersUpdated.seenUsersCount,
+            };
+          },
+          { revalidate: false },
+        );
+      }
+    },
+    [currentPageId, mutatePageInfo],
+  );
+
+  useEffect(() => {
+    if (socket == null) {
+      return;
+    }
+
+    socket.on(SocketEventName.PageSeenUsersUpdated, seenUsersUpdatedHandler);
+
+    return () => {
+      socket.off(SocketEventName.PageSeenUsersUpdated, seenUsersUpdatedHandler);
+    };
+  }, [seenUsersUpdatedHandler, socket]);
+};

+ 1 - 0
apps/app/src/interfaces/websocket.ts

@@ -48,6 +48,7 @@ export const SocketEventName = {
   PageCreated: 'page:create',
   PageUpdated: 'page:update',
   PageDeleted: 'page:delete',
+  PageSeenUsersUpdated: 'page:seenUsersUpdated',
 
   // Yjs
   YjsAwarenessStateSizeUpdated: 'yjs:awareness-state-size-update',

+ 21 - 0
apps/app/src/server/models/vo/s2c-message.ts

@@ -42,3 +42,24 @@ export class S2cMessagePageUpdated {
     }
   }
 }
+
+/**
+ * Server-to-client message VO for page seenUsers update
+ */
+export class S2cMessagePageSeenUsersUpdated {
+  pageId: string;
+
+  seenUserIds: string[];
+
+  seenUsersCount: number;
+
+  constructor(page: IPage) {
+    const serializedPage = serializePageSecurely(page);
+
+    this.pageId = serializedPage._id;
+    this.seenUserIds = serializedPage.seenUsers
+      .slice(0, 15)
+      .map((id: any) => (typeof id === 'string' ? id : id.toString()));
+    this.seenUsersCount = serializedPage.seenUsers.length;
+  }
+}

+ 22 - 1
apps/app/src/server/service/system-events/sync-page-status.ts

@@ -1,7 +1,10 @@
 import type Crowi from '~/server/crowi';
 import loggerFactory from '~/utils/logger';
 
-import { S2cMessagePageUpdated } from '../../models/vo/s2c-message';
+import {
+  S2cMessagePageSeenUsersUpdated,
+  S2cMessagePageUpdated,
+} from '../../models/vo/s2c-message';
 import S2sMessage from '../../models/vo/s2s-message';
 import type { S2sMessagingService } from '../s2s-messaging/base';
 import type { S2sMessageHandlable } from '../s2s-messaging/handlable';
@@ -136,6 +139,24 @@ class SyncPageStatusService implements S2sMessageHandlable {
 
       this.publishToOtherServers('page:delete', { s2cMessagePageUpdated });
     });
+
+    this.emitter.on('addSeenUsers', (page) => {
+      logger.debug("'addSeenUsers' event emitted.");
+
+      const s2cMessagePageSeenUsersUpdated = new S2cMessagePageSeenUsersUpdated(
+        page,
+      );
+
+      // emit to the room for each page
+      socketIoService
+        .getDefaultSocket()
+        .in(getRoomNameWithId(RoomPrefix.PAGE, page._id))
+        .emit('page:seenUsersUpdated', { s2cMessagePageSeenUsersUpdated });
+
+      this.publishToOtherServers('page:seenUsersUpdated', {
+        s2cMessagePageSeenUsersUpdated,
+      });
+    });
   }
 }