فهرست منبع

Merge pull request #6451 from weseek/fix/102294-show-toc-correctly

fix: Show toc correctly
Yuki Takei 3 سال پیش
والد
کامیت
4764cbc994

+ 41 - 19
packages/app/src/components/Page.jsx → packages/app/src/components/Page.tsx

@@ -1,15 +1,15 @@
 import React, {
+  useCallback,
   useEffect, useRef, useState,
 } from 'react';
 
 import dynamic from 'next/dynamic';
-import PropTypes from 'prop-types';
 // import { debounce } from 'throttle-debounce';
 
 import { blinkSectionHeaderAtBoot } from '~/client/util/blink-section-header';
 // import { getOptionsToSave } from '~/client/util/editor';
 import {
-  useIsGuestUser, useIsBlinkedHeaderAtBoot,
+  useIsGuestUser, useIsBlinkedHeaderAtBoot, useCurrentPageTocNode,
 } from '~/stores/context';
 import {
   useSWRxSlackChannels, useIsSlackEnabled, usePageTagsForEditors, useIsEnabledUnsavedWarning,
@@ -22,6 +22,7 @@ import {
 import loggerFactory from '~/utils/logger';
 
 import RevisionRenderer from './Page/RevisionRenderer';
+import { HtmlElementNode } from 'rehype-toc';
 
 // TODO: import dynamically
 // import MarkdownTable from '~/client/models/MarkdownTable';
@@ -30,9 +31,28 @@ import RevisionRenderer from './Page/RevisionRenderer';
 
 const logger = loggerFactory('growi:Page');
 
-class PageSubstance extends React.Component {
+type PageSubstanceProps = {
+  rendererOptions: any,
+  page: any,
+  pageTags?: string[],
+  editorMode: string,
+  isGuestUser: boolean,
+  isMobile?: boolean,
+  isSlackEnabled: boolean,
+  slackChannels: string,
+};
+
+class PageSubstance extends React.Component<PageSubstanceProps> {
+
+  gridEditModal: any;
+
+  linkEditModal: any;
+
+  handsontableModal: any;
+
+  drawioModal: any;
 
-  constructor(props) {
+  constructor(props: PageSubstanceProps) {
     super(props);
 
     this.state = {
@@ -138,7 +158,7 @@ class PageSubstance extends React.Component {
     // }
   }
 
-  render() {
+  override render() {
     const {
       rendererOptions, page, isMobile, isGuestUser,
     } = this.props;
@@ -171,29 +191,26 @@ class PageSubstance extends React.Component {
 
 }
 
-PageSubstance.propTypes = {
-  rendererOptions: PropTypes.object.isRequired,
+export const Page = (props) => {
+  // Pass tocRef to generateViewOptions (=> rehypePlugin => customizeTOC) to call mutateCurrentPageTocNode when tocRef.current changes.
+  // The toc node passed by customizeTOC is assigned to tocRef.current.
+  const tocRef = useRef<HtmlElementNode>();
 
-  page: PropTypes.any.isRequired,
-  pageTags:  PropTypes.arrayOf(PropTypes.string),
-  editorMode: PropTypes.string.isRequired,
-  isGuestUser: PropTypes.bool.isRequired,
-  isMobile: PropTypes.bool,
-  isSlackEnabled: PropTypes.bool.isRequired,
-  slackChannels: PropTypes.string.isRequired,
-};
+  const storeTocNodeHandler = useCallback((toc: HtmlElementNode) => {
+    tocRef.current = toc;
+  }, []);
 
-export const Page = (props) => {
   const { data: currentPage } = useSWRxCurrentPage();
   const { data: editorMode } = useEditorMode();
   const { data: isGuestUser } = useIsGuestUser();
   const { data: isMobile } = useIsMobile();
   const { data: slackChannelsData } = useSWRxSlackChannels(currentPage?.path);
   const { data: isSlackEnabled } = useIsSlackEnabled();
-  const { data: pageTags } = usePageTagsForEditors();
-  const { data: rendererOptions } = useViewOptions();
+  const { data: pageTags } = usePageTagsForEditors(null); // TODO: pass pageId
+  const { data: rendererOptions } = useViewOptions(storeTocNodeHandler);
   const { mutate: mutateIsEnabledUnsavedWarning } = useIsEnabledUnsavedWarning();
   const { data: isBlinkedAtBoot, mutate: mutateBlinkedAtBoot } = useIsBlinkedHeaderAtBoot();
+  const { mutate: mutateCurrentPageTocNode } = useCurrentPageTocNode();
 
   const pageRef = useRef(null);
 
@@ -206,6 +223,11 @@ export const Page = (props) => {
     mutateBlinkedAtBoot(true);
   }, [isBlinkedAtBoot, mutateBlinkedAtBoot]);
 
+  useEffect(() => {
+    mutateCurrentPageTocNode(tocRef.current);
+  // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [mutateCurrentPageTocNode, tocRef.current]); // include tocRef.current to call mutateCurrentPageTocNode when tocRef.current changes
+
   // // set handler to open DrawioModal
   // useEffect(() => {
   //   const handler = (beginLineNumber, endLineNumber) => {
@@ -253,7 +275,7 @@ export const Page = (props) => {
       isMobile={isMobile}
       isSlackEnabled={isSlackEnabled}
       pageTags={pageTags}
-      slackChannels={slackChannelsData.toString()}
+      slackChannels={slackChannelsData?.toString()}
       mutateIsEnabledUnsavedWarning={mutateIsEnabledUnsavedWarning}
     />
   );

+ 0 - 1
packages/app/src/components/Page/RevisionRenderer.tsx

@@ -8,7 +8,6 @@ import { CustomWindow } from '~/interfaces/global';
 import { RendererOptions } from '~/services/renderer/renderer';
 import { useCurrentPathname, useInterceptorManager } from '~/stores/context';
 import { useEditorSettings } from '~/stores/editor';
-import { useViewOptions } from '~/stores/renderer';
 import loggerFactory from '~/utils/logger';
 
 // import RevisionBody from './RevisionBody';

+ 6 - 2
packages/app/src/services/renderer/renderer.ts

@@ -251,7 +251,7 @@ const generateCommonOptions = (pagePath: string|undefined, config: RendererConfi
 export const generateViewOptions = (
     pagePath: string,
     config: RendererConfig,
-    storeTocNode: (node: HtmlElementNode) => void,
+    storeTocNode: (toc: HtmlElementNode) => void,
 ): RendererOptions => {
 
   const options = generateCommonOptions(pagePath, config);
@@ -286,7 +286,11 @@ export const generateViewOptions = (
           });
         };
         replacer([toc]); // replace <ol> to <ul>
-        storeTocNode(toc); // store tocNode to global state with swr
+
+        // For storing tocNode to global state with swr
+        // search: tocRef.current
+        storeTocNode(toc);
+
         return false; // not show toc in body
       },
     }]);

+ 6 - 6
packages/app/src/stores/context.tsx

@@ -1,5 +1,3 @@
-import EventEmitter from 'events';
-
 import { HtmlElementNode } from 'rehype-toc';
 import { Key, SWRResponse } from 'swr';
 import useSWRImmutable from 'swr/immutable';
@@ -232,10 +230,6 @@ export const useRendererConfig = (initialData?: RendererConfig): SWRResponse<Ren
   return useStaticSWR('growiRendererConfig', initialData);
 };
 
-export const useCurrentPageTocNode = (): SWRResponse<HtmlElementNode, any> => {
-  return useStaticSWR('currentPageTocNode');
-};
-
 export const useIsBlinkedHeaderAtBoot = (initialData?: boolean): SWRResponse<boolean, Error> => {
   return useStaticSWR('isBlinkedAtBoot', initialData);
 };
@@ -278,3 +272,9 @@ export const useIsEditable = (): SWRResponse<boolean, Error> => {
     },
   );
 };
+
+export const useCurrentPageTocNode = (): SWRResponse<HtmlElementNode, any> => {
+  const { data: currentPagePath } = useCurrentPagePath();
+
+  return useStaticSWR(['currentPageTocNode', currentPagePath]);
+};

+ 18 - 7
packages/app/src/stores/renderer.tsx

@@ -1,3 +1,4 @@
+import { HtmlElementNode } from 'rehype-toc';
 import { Key, SWRResponse } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 
@@ -9,7 +10,9 @@ import {
 } from '~/services/renderer/renderer';
 
 
-import { useCurrentPagePath, useCurrentPageTocNode, useRendererConfig } from './context';
+import {
+  useCurrentPagePath, useCurrentPageTocNode, useRendererConfig,
+} from './context';
 
 interface ReactMarkdownOptionsGenerator {
   (config: RendererConfig): RendererOptions
@@ -37,10 +40,9 @@ const _useOptionsBase = (
   return useSWRImmutable<RendererOptions, Error>(key);
 };
 
-export const useViewOptions = (): SWRResponse<RendererOptions, Error> => {
+export const useViewOptions = (storeTocNodeHandler: (toc: HtmlElementNode) => void): SWRResponse<RendererOptions, Error> => {
   const { data: currentPagePath } = useCurrentPagePath();
   const { data: rendererConfig } = useRendererConfig();
-  const { mutate: storeTocNode } = useCurrentPageTocNode();
 
   const isAllDataValid = currentPagePath != null && rendererConfig != null;
 
@@ -50,16 +52,25 @@ export const useViewOptions = (): SWRResponse<RendererOptions, Error> => {
 
   return useSWRImmutable<RendererOptions, Error>(
     key,
-    (rendererId, currentPagePath, rendererConfig) => generateViewOptions(currentPagePath, rendererConfig, storeTocNode),
+    (rendererId, currentPagePath, rendererConfig) => generateViewOptions(currentPagePath, rendererConfig, storeTocNodeHandler),
   );
 };
 
 export const useTocOptions = (): SWRResponse<RendererOptions, Error> => {
-  const key = 'tocOptions';
-
+  const { data: currentPagePath } = useCurrentPagePath();
+  const { data: rendererConfig } = useRendererConfig();
   const { data: tocNode } = useCurrentPageTocNode();
 
-  return _useOptionsBase(key, config => generateTocOptions(config, tocNode));
+  const isAllDataValid = rendererConfig != null;
+
+  const key = isAllDataValid
+    ? ['tocOptions', currentPagePath, tocNode, rendererConfig]
+    : null;
+
+  return useSWRImmutable<RendererOptions, Error>(
+    key,
+    (rendererId, path, tocNode, rendererConfig) => generateTocOptions(rendererConfig, tocNode),
+  );
 };
 
 export const usePreviewOptions = (): SWRResponse<RendererOptions, Error> => {