Yuki Takei 3 лет назад
Родитель
Сommit
4dbf8ac333

+ 81 - 0
packages/app/src/client/services/side-effects/drawio-modal-launcher-for-view.ts

@@ -0,0 +1,81 @@
+import { useCallback, useEffect } from 'react';
+
+import { DrawioEditByViewerProps } from '@growi/remark-drawio';
+
+import { useSaveOrUpdate } from '~/client/services/page-operation';
+import mdu from '~/components/PageEditor/MarkdownDrawioUtil';
+import type { OptionsToSave } from '~/interfaces/page-operation';
+import { useShareLinkId } from '~/stores/context';
+import { useDrawioModal } from '~/stores/modal';
+import { useSWRxCurrentPage, useSWRxTagsInfo } from '~/stores/page';
+import loggerFactory from '~/utils/logger';
+
+
+const logger = loggerFactory('growi:cli:side-effects:useHandsontableModalLauncherForView');
+
+
+export const useDrawioModalLauncherForView = (opts?: {
+  onSaveSuccess?: (newMarkdown: string) => void,
+  onSaveError?: (error: any) => void,
+}): void => {
+
+  const { data: shareLinkId } = useShareLinkId();
+
+  const { data: currentPage } = useSWRxCurrentPage();
+  const { data: tagsInfo } = useSWRxTagsInfo(currentPage?._id);
+
+  const { open: openDrawioModal } = useDrawioModal();
+
+  const saveOrUpdate = useSaveOrUpdate();
+
+  const saveByDrawioModal = useCallback(async(drawioMxFile: string, bol: number, eol: number) => {
+    if (currentPage == null || tagsInfo == null || shareLinkId != null) {
+      return;
+    }
+
+    const currentMarkdown = currentPage.revision.body;
+    const newMarkdown = mdu.replaceDrawioInMarkdown(drawioMxFile, currentMarkdown, bol, eol);
+
+    const optionsToSave: OptionsToSave = {
+      isSlackEnabled: false,
+      slackChannels: '',
+      grant: currentPage.grant,
+      grantUserGroupId: currentPage.grantedGroup?._id,
+      grantUserGroupName: currentPage.grantedGroup?.name,
+      pageTags: tagsInfo.tags,
+    };
+
+    try {
+      const currentRevisionId = currentPage.revision._id;
+      await saveOrUpdate(
+        newMarkdown,
+        { pageId: currentPage._id, path: currentPage.path, revisionId: currentRevisionId },
+        optionsToSave,
+      );
+
+      opts?.onSaveSuccess?.(newMarkdown);
+    }
+    catch (error) {
+      logger.error('failed to save', error);
+      opts?.onSaveError?.(error);
+    }
+  }, [currentPage, opts, saveOrUpdate, shareLinkId, tagsInfo]);
+
+
+  // set handler to open DrawioModal
+  useEffect(() => {
+    // disable if share link
+    if (shareLinkId != null) {
+      return;
+    }
+
+    const handler = (data: DrawioEditByViewerProps) => {
+      openDrawioModal(data.drawioMxFile, drawioMxFile => saveByDrawioModal(drawioMxFile, data.bol, data.eol));
+    };
+    globalEmitter.on('launchDrawioModal', handler);
+
+    return function cleanup() {
+      globalEmitter.removeListener('launchDrawioModal', handler);
+    };
+  }, [openDrawioModal, saveByDrawioModal, shareLinkId]);
+};

+ 81 - 0
packages/app/src/client/services/side-effects/handsontable-modal-launcher-for-view.ts

@@ -0,0 +1,81 @@
+import { useCallback, useEffect } from 'react';
+
+import MarkdownTable from '~/client/models/MarkdownTable';
+import { useSaveOrUpdate } from '~/client/services/page-operation';
+import mtu from '~/components/PageEditor/MarkdownTableUtil';
+import type { OptionsToSave } from '~/interfaces/page-operation';
+import { useShareLinkId } from '~/stores/context';
+import { useHandsontableModal } from '~/stores/modal';
+import { useSWRxCurrentPage, useSWRxTagsInfo } from '~/stores/page';
+import loggerFactory from '~/utils/logger';
+
+
+const logger = loggerFactory('growi:cli:side-effects:useHandsontableModalLauncherForView');
+
+
+export const useHandsontableModalLauncherForView = (opts?: {
+  onSaveSuccess?: (newMarkdown: string) => void,
+  onSaveError?: (error: any) => void,
+}): void => {
+
+  const { data: shareLinkId } = useShareLinkId();
+
+  const { data: currentPage } = useSWRxCurrentPage();
+  const { data: tagsInfo } = useSWRxTagsInfo(currentPage?._id);
+
+  const { open: openHandsontableModal } = useHandsontableModal();
+
+  const saveOrUpdate = useSaveOrUpdate();
+
+  const saveByHandsontableModal = useCallback(async(table: MarkdownTable, bol: number, eol: number) => {
+    if (currentPage == null || tagsInfo == null || shareLinkId != null) {
+      return;
+    }
+
+    const currentMarkdown = currentPage.revision.body;
+    const newMarkdown = mtu.replaceMarkdownTableInMarkdown(table, currentMarkdown, bol, eol);
+
+    const optionsToSave: OptionsToSave = {
+      isSlackEnabled: false,
+      slackChannels: '',
+      grant: currentPage.grant,
+      grantUserGroupId: currentPage.grantedGroup?._id,
+      grantUserGroupName: currentPage.grantedGroup?.name,
+      pageTags: tagsInfo.tags,
+    };
+
+    try {
+      const currentRevisionId = currentPage.revision._id;
+      await saveOrUpdate(
+        newMarkdown,
+        { pageId: currentPage._id, path: currentPage.path, revisionId: currentRevisionId },
+        optionsToSave,
+      );
+
+      opts?.onSaveSuccess?.(newMarkdown);
+    }
+    catch (error) {
+      logger.error('failed to save', error);
+      opts?.onSaveError?.(error);
+    }
+  }, [currentPage, opts, saveOrUpdate, shareLinkId, tagsInfo]);
+
+
+  // set handler to open HandsonTableModal
+  useEffect(() => {
+    if (currentPage == null || shareLinkId != null) {
+      return;
+    }
+
+    const handler = (bol: number, eol: number) => {
+      const markdown = currentPage.revision.body;
+      const currentMarkdownTable = mtu.getMarkdownTableFromLine(markdown, bol, eol);
+      openHandsontableModal(currentMarkdownTable, undefined, false, table => saveByHandsontableModal(table, bol, eol));
+    };
+    globalEmitter.on('launchHandsonTableModal', handler);
+
+    return function cleanup() {
+      globalEmitter.removeListener('launchHandsonTableModal', handler);
+    };
+  }, [currentPage, openHandsontableModal, saveByHandsontableModal, shareLinkId]);
+};

+ 26 - 118
packages/app/src/components/Page.tsx

@@ -1,23 +1,22 @@
-import React, { useCallback, useEffect, useRef } from 'react';
+import React, {
+  useCallback, useEffect, useRef,
+} from 'react';
 
 import EventEmitter from 'events';
 
 import { pagePathUtils, IPagePopulatedToShowRevision } from '@growi/core';
-import { DrawioEditByViewerProps } from '@growi/remark-drawio';
 import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
 import { HtmlElementNode } from 'rehype-toc';
 
-import MarkdownTable from '~/client/models/MarkdownTable';
-import { useSaveOrUpdate } from '~/client/services/page-operation';
-import { toastSuccess, toastError } from '~/client/util/apiNotification';
-import { OptionsToSave } from '~/interfaces/page-operation';
+import { useDrawioModalLauncherForView } from '~/client/services/side-effects/drawio-modal-launcher-for-view';
+import { useHandsontableModalLauncherForView } from '~/client/services/side-effects/handsontable-modal-launcher-for-view';
+import { toastSuccess, toastError } from '~/client/util/toastr';
 import {
-  useIsGuestUser, useShareLinkId, useCurrentPathname, useIsNotFound,
+  useIsGuestUser, useCurrentPathname, useIsNotFound,
 } from '~/stores/context';
 import { useEditingMarkdown } from '~/stores/editor';
-import { useDrawioModal, useHandsontableModal } from '~/stores/modal';
-import { useCurrentPagePath, useSWRxCurrentPage, useSWRxTagsInfo } from '~/stores/page';
+import { useCurrentPagePath, useSWRxCurrentPage } from '~/stores/page';
 import { useViewOptions } from '~/stores/renderer';
 import {
   useCurrentPageTocNode,
@@ -27,8 +26,6 @@ import { registerGrowiFacade } from '~/utils/growi-facade';
 import loggerFactory from '~/utils/logger';
 
 import RevisionRenderer from './Page/RevisionRenderer';
-import mdu from './PageEditor/MarkdownDrawioUtil';
-import mtu from './PageEditor/MarkdownTableUtil';
 import { UserInfo } from './User/UserInfo';
 
 import styles from './Page.module.scss';
@@ -68,18 +65,12 @@ const PageSubstance = (props: PageSubstanceProps): JSX.Element => {
   const { data: currentPathname } = useCurrentPathname();
   const isSharedPage = pagePathUtils.isSharedPage(currentPathname ?? '');
 
-  const { data: shareLinkId } = useShareLinkId();
   const { mutate: mutateCurrentPage } = useSWRxCurrentPage();
   const { mutate: mutateEditingMarkdown } = useEditingMarkdown();
-  const { data: tagsInfo } = useSWRxTagsInfo(currentPage?._id);
   const { data: isGuestUser } = useIsGuestUser();
   const { data: isMobile } = useIsMobile();
   const { data: rendererOptions, mutate: mutateRendererOptions } = useViewOptions(storeTocNodeHandler);
   const { mutate: mutateCurrentPageTocNode } = useCurrentPageTocNode();
-  const { open: openDrawioModal } = useDrawioModal();
-  const { open: openHandsontableModal } = useHandsontableModal();
-
-  const saveOrUpdate = useSaveOrUpdate();
 
 
   // register to facade
@@ -99,37 +90,8 @@ const PageSubstance = (props: PageSubstanceProps): JSX.Element => {
   }, [mutateCurrentPageTocNode, tocRef.current]); // include tocRef.current to call mutateCurrentPageTocNode when tocRef.current changes
 
 
-  // TODO: refactor commonize saveByDrawioModal and saveByHandsontableModal
-  const saveByDrawioModal = useCallback(async(drawioMxFile: string, bol: number, eol: number) => {
-    if (currentPage == null || tagsInfo == null) {
-      return;
-    }
-
-    // disable if share link
-    if (shareLinkId != null) {
-      return;
-    }
-
-    const currentMarkdown = currentPage.revision.body;
-    const optionsToSave: OptionsToSave = {
-      isSlackEnabled: false,
-      slackChannels: '',
-      grant: currentPage.grant,
-      grantUserGroupId: currentPage.grantedGroup?._id,
-      grantUserGroupName: currentPage.grantedGroup?.name,
-      pageTags: tagsInfo.tags,
-    };
-
-    const newMarkdown = mdu.replaceDrawioInMarkdown(drawioMxFile, currentMarkdown, bol, eol);
-
-    try {
-      const currentRevisionId = currentPage.revision._id;
-      await saveOrUpdate(
-        newMarkdown,
-        { pageId: currentPage._id, path: currentPage.path, revisionId: currentRevisionId },
-        optionsToSave,
-      );
-
+  useHandsontableModalLauncherForView({
+    onSaveSuccess: (newMarkdown) => {
       toastSuccess(t('toaster.save_succeeded'));
 
       // rerender
@@ -137,55 +99,14 @@ const PageSubstance = (props: PageSubstanceProps): JSX.Element => {
         mutateCurrentPage();
       }
       mutateEditingMarkdown(newMarkdown);
-    }
-    catch (error) {
-      logger.error('failed to save', error);
+    },
+    onSaveError: (error) => {
       toastError(error);
-    }
-  }, [currentPage, isSharedPage, mutateCurrentPage, mutateEditingMarkdown, saveOrUpdate, shareLinkId, t, tagsInfo]);
-
-  // set handler to open DrawioModal
-  useEffect(() => {
-    // disable if share link
-    if (shareLinkId != null) {
-      return;
-    }
-
-    const handler = (data: DrawioEditByViewerProps) => {
-      openDrawioModal(data.drawioMxFile, drawioMxFile => saveByDrawioModal(drawioMxFile, data.bol, data.eol));
-    };
-    globalEmitter.on('launchDrawioModal', handler);
-
-    return function cleanup() {
-      globalEmitter.removeListener('launchDrawioModal', handler);
-    };
-  }, [openDrawioModal, saveByDrawioModal, shareLinkId]);
-
-  const saveByHandsontableModal = useCallback(async(table: MarkdownTable, bol: number, eol: number) => {
-    if (currentPage == null || tagsInfo == null || shareLinkId != null) {
-      return;
-    }
-
-    const currentMarkdown = currentPage.revision.body;
-    const optionsToSave: OptionsToSave = {
-      isSlackEnabled: false,
-      slackChannels: '',
-      grant: currentPage.grant,
-      grantUserGroupId: currentPage.grantedGroup?._id,
-      grantUserGroupName: currentPage.grantedGroup?.name,
-      pageTags: tagsInfo.tags,
-    };
-
-    const newMarkdown = mtu.replaceMarkdownTableInMarkdown(table, currentMarkdown, bol, eol);
-
-    try {
-      const currentRevisionId = currentPage.revision._id;
-      await saveOrUpdate(
-        newMarkdown,
-        { pageId: currentPage._id, path: currentPage.path, revisionId: currentRevisionId },
-        optionsToSave,
-      );
+    },
+  });
 
+  useDrawioModalLauncherForView({
+    onSaveSuccess: (newMarkdown) => {
       toastSuccess(t('toaster.save_succeeded'));
 
       // rerender
@@ -193,30 +114,12 @@ const PageSubstance = (props: PageSubstanceProps): JSX.Element => {
         mutateCurrentPage();
       }
       mutateEditingMarkdown(newMarkdown);
-    }
-    catch (error) {
-      logger.error('failed to save', error);
+    },
+    onSaveError: (error) => {
       toastError(error);
-    }
-  }, [currentPage, isSharedPage, mutateCurrentPage, mutateEditingMarkdown, saveOrUpdate, shareLinkId, t, tagsInfo]);
+    },
+  });
 
-  // set handler to open HandsonTableModal
-  useEffect(() => {
-    if (currentPage == null || shareLinkId != null) {
-      return;
-    }
-
-    const handler = (bol: number, eol: number) => {
-      const markdown = currentPage.revision.body;
-      const currentMarkdownTable = mtu.getMarkdownTableFromLine(markdown, bol, eol);
-      openHandsontableModal(currentMarkdownTable, undefined, false, table => saveByHandsontableModal(table, bol, eol));
-    };
-    globalEmitter.on('launchHandsonTableModal', handler);
-
-    return function cleanup() {
-      globalEmitter.removeListener('launchHandsonTableModal', handler);
-    };
-  }, [currentPage, openHandsontableModal, saveByHandsontableModal, shareLinkId]);
 
   if (currentPage == null || isGuestUser == null || rendererOptions == null) {
     const entries = Object.entries({
@@ -226,6 +129,7 @@ const PageSubstance = (props: PageSubstanceProps): JSX.Element => {
       .filter(([, value]) => value != null);
 
     logger.warn('Some of materials are missing.', Object.fromEntries(entries));
+
     return <></>;
   }
 
@@ -260,7 +164,11 @@ export const Page = React.memo((): JSX.Element => {
   return (
     <>
       { isUsersHomePagePath && <UserInfo author={currentPage?.creator} /> }
-      { !isNotFound && <PageSubstance currentPage={currentPage ?? undefined} /> }
+
+      { !isNotFound && (
+        <PageSubstance currentPage={currentPage ?? undefined} />
+      ) }
+
       { isNotFound && <NotFoundPage /> }
     </>
   );