ソースを参照

Merge pull request #8975 from weseek/fix/hide-sync-menu-item

fix: Hide the sync to the latest revision menu item when not editing
Yuki Takei 1 年間 前
コミット
413b64747d

+ 17 - 9
apps/app/src/client/components/Navbar/GrowiContextualSubNavigation.tsx

@@ -17,7 +17,7 @@ import Sticky from 'react-stickynode';
 import { DropdownItem } from 'reactstrap';
 
 import { exportAsMarkdown, updateContentWidth, syncLatestRevisionBody } from '~/client/services/page-operation';
-import { toastSuccess, toastError } from '~/client/util/toastr';
+import { toastSuccess, toastError, toastWarning } from '~/client/util/toastr';
 import { GroundGlassBar } from '~/components/Navbar/GroundGlassBar';
 import type { OnDuplicatedFunction, OnRenamedFunction, OnDeletedFunction } from '~/interfaces/ui';
 import { useShouldExpandContent } from '~/services/layout/use-should-expand-content';
@@ -25,7 +25,7 @@ import {
   useCurrentPathname,
   useCurrentUser, useIsGuestUser, useIsReadOnlyUser, useIsSharedUser, useShareLinkId,
 } from '~/stores-universal/context';
-import { useEditorMode } from '~/stores-universal/ui';
+import { EditorMode, useEditorMode } from '~/stores-universal/ui';
 import {
   usePageAccessoriesModal, PageAccessoriesModalContents, type IPageForPageDuplicateModal,
   usePageDuplicateModal, usePageRenameModal, usePageDeleteModal, usePagePresentationModal,
@@ -72,6 +72,7 @@ const PageOperationMenuItems = (props: PageOperationMenuItemsProps): JSX.Element
     pageId, revisionId, isLinkSharingDisabled,
   } = props;
 
+  const { data: editorMode } = useEditorMode();
   const { data: isGuestUser } = useIsGuestUser();
   const { data: isReadOnlyUser } = useIsReadOnlyUser();
   const { data: isSharedUser } = useIsSharedUser();
@@ -89,6 +90,11 @@ const PageOperationMenuItems = (props: PageOperationMenuItemsProps): JSX.Element
         const editingMarkdownLength = codeMirrorEditor?.getDoc().length;
         const res = await syncLatestRevisionBody(pageId, editingMarkdownLength);
 
+        if (!res.synced) {
+          toastWarning('Skipped synchronizing since the page is not being edited.');
+          return;
+        }
+
         if (res?.isYjsDataBroken) {
           // eslint-disable-next-line no-alert
           window.alert(t('sync-latest-reevision-body.alert'));
@@ -105,13 +111,15 @@ const PageOperationMenuItems = (props: PageOperationMenuItemsProps): JSX.Element
 
   return (
     <>
-      <DropdownItem
-        onClick={() => syncLatestRevisionBodyHandler()}
-        className="grw-page-control-dropdown-item"
-      >
-        <span className="material-symbols-outlined me-1 grw-page-control-dropdown-icon">sync</span>
-        {t('SyncLatestRevisionBody')}
-      </DropdownItem>
+      { editorMode === EditorMode.Editor && (
+        <DropdownItem
+          onClick={() => syncLatestRevisionBodyHandler()}
+          className="grw-page-control-dropdown-item"
+        >
+          <span className="material-symbols-outlined me-1 grw-page-control-dropdown-icon">sync</span>
+          {t('SyncLatestRevisionBody')}
+        </DropdownItem>
+      ) }
 
       {/* Presentation */}
       <DropdownItem

+ 2 - 1
apps/app/src/client/services/page-operation.ts

@@ -4,6 +4,7 @@ import type { IPageHasId } from '@growi/core';
 import { SubscriptionStatusType } from '@growi/core';
 import urljoin from 'url-join';
 
+import type { SyncLatestRevisionBody } from '~/interfaces/yjs';
 import { useEditingMarkdown, usePageTagsForEditors } from '~/stores/editor';
 import {
   useCurrentPageId, useSWRMUTxCurrentPage, useSWRxApplicableGrant, useSWRxTagsInfo,
@@ -175,7 +176,7 @@ export const unpublish = async(pageId: string): Promise<IPageHasId> => {
   return res.data;
 };
 
-export const syncLatestRevisionBody = async(pageId: string, editingMarkdownLength?: number): Promise<{ isYjsDataBroken?: boolean } | void> => {
+export const syncLatestRevisionBody = async(pageId: string, editingMarkdownLength?: number): Promise<SyncLatestRevisionBody> => {
   const res = await apiv3Put(`/page/${pageId}/sync-latest-revision-body-to-yjs-draft`, { editingMarkdownLength });
   return res.data;
 };

+ 5 - 0
apps/app/src/interfaces/yjs.ts

@@ -2,3 +2,8 @@ export type CurrentPageYjsData = {
   hasYdocsNewerThanLatestRevision?: boolean,
   awarenessStateSize?: number,
 }
+
+export type SyncLatestRevisionBody = {
+  synced: boolean,
+  isYjsDataBroken?: boolean,
+}

+ 10 - 6
apps/app/src/server/service/yjs/yjs.ts

@@ -8,6 +8,7 @@ import type { Document } from 'y-socket.io/dist/server';
 import { YSocketIO, type Document as Ydoc } from 'y-socket.io/dist/server';
 
 import { SocketEventName } from '~/interfaces/websocket';
+import type { SyncLatestRevisionBody } from '~/interfaces/yjs';
 import { RoomPrefix, getRoomNameWithId } from '~/server/util/socket-io-helpers';
 import loggerFactory from '~/utils/logger';
 
@@ -32,7 +33,7 @@ type RequestWithUser = IncomingMessage & { user: IUserHasId };
 
 export interface IYjsService {
   getYDocStatus(pageId: string): Promise<YDocStatus>;
-  syncWithTheLatestRevisionForce(pageId: string, editingMarkdownLength?: number): Promise<{ isYjsDataBroken?: boolean } | void>
+  syncWithTheLatestRevisionForce(pageId: string, editingMarkdownLength?: number): Promise<SyncLatestRevisionBody>
   getCurrentYdoc(pageId: string): Ydoc | undefined;
 }
 
@@ -181,19 +182,22 @@ class YjsService implements IYjsService {
     return YDocStatus.OUTDATED;
   }
 
-  public async syncWithTheLatestRevisionForce(pageId: string, editingMarkdownLength?: number): Promise<{ isYjsDataBroken?: boolean } | void> {
+  public async syncWithTheLatestRevisionForce(pageId: string, editingMarkdownLength?: number): Promise<SyncLatestRevisionBody> {
     const doc = this.ysocketio.documents.get(pageId);
 
     if (doc == null) {
-      return;
+      return { synced: false };
     }
 
     const ytextLength = doc?.getText('codemirror').length;
     syncYDoc(this.mdb, doc, true);
 
-    if (editingMarkdownLength != null) {
-      return { isYjsDataBroken: editingMarkdownLength !== ytextLength };
-    }
+    return {
+      synced: true,
+      isYjsDataBroken: editingMarkdownLength != null
+        ? editingMarkdownLength !== ytextLength
+        : undefined,
+    };
   }
 
   public getCurrentYdoc(pageId: string): Ydoc | undefined {