Procházet zdrojové kódy

Merge branch 'master' into feat/detect-browser-language

Ryoji Shimizu před 3 roky
rodič
revize
2c398dfadf

+ 22 - 40
packages/app/src/components/Page/RevisionLoader.tsx

@@ -1,10 +1,10 @@
-import React, { useState, useCallback, useEffect } from 'react';
+import React, { useState, useEffect } from 'react';
 
 
 import { Ref, IRevision, IRevisionHasId } from '@growi/core';
 import { Ref, IRevision, IRevisionHasId } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
 
 
-import { apiv3Get } from '~/client/util/apiv3-client';
 import { RendererOptions } from '~/services/renderer/renderer';
 import { RendererOptions } from '~/services/renderer/renderer';
+import { useSWRxPageRevision } from '~/stores/page';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 import RevisionRenderer from './RevisionRenderer';
 import RevisionRenderer from './RevisionRenderer';
@@ -35,42 +35,36 @@ export const RevisionLoader = (props: RevisionLoaderProps): JSX.Element => {
     rendererOptions, pageId, revisionId, onRevisionLoaded,
     rendererOptions, pageId, revisionId, onRevisionLoaded,
   } = props;
   } = props;
 
 
-  const [isLoading, setIsLoading] = useState<boolean>();
-  const [isLoaded, setIsLoaded] = useState<boolean>();
-  const [markdown, setMarkdown] = useState<string>('');
-  const [errors, setErrors] = useState<any | null>();
-
-  const loadData = useCallback(async() => {
-    if (!isLoaded && !isLoading) {
-      setIsLoading(true);
-    }
+  const { data: pageRevision, isLoading, error } = useSWRxPageRevision(pageId, revisionId);
 
 
-    // load data with REST API
-    try {
-      const res = await apiv3Get(`/revisions/${revisionId}`, { pageId });
+  const [markdown, setMarkdown] = useState<string>('');
 
 
-      setMarkdown(res.data?.revision?.body);
-      setErrors(null);
+  useEffect(() => {
+    if (pageRevision != null) {
+      setMarkdown(pageRevision?.body ?? '');
 
 
       if (onRevisionLoaded != null) {
       if (onRevisionLoaded != null) {
-        onRevisionLoaded(res.data.revision);
+        onRevisionLoaded(pageRevision);
       }
       }
     }
     }
-    catch (errors) {
-      setErrors(errors);
-    }
-    finally {
-      setIsLoaded(true);
-      setIsLoading(false);
-    }
 
 
-  }, [isLoaded, isLoading, onRevisionLoaded, pageId, revisionId]);
+  }, [onRevisionLoaded, pageRevision]);
 
 
   useEffect(() => {
   useEffect(() => {
-    loadData();
-  }, [loadData]);
+    if (error != null) {
+      const isForbidden = error != null && error[0].code === 'forbidden-page';
+      if (isForbidden) {
+        setMarkdown(`<i class="icon-exclamation p-1"></i>${t('not_allowed_to_see_this_page')}`);
+      }
+      else {
+        const errorMessages = error.map((error) => {
+          return `<i class="icon-exclamation p-1"></i><span class="text-muted"><em>${error.message}</em></span>`;
+        });
+        setMarkdown(errorMessages.join('\n'));
+      }
+    }
+  }, [error, t]);
 
 
-  /* ----- loading ----- */
   if (isLoading) {
   if (isLoading) {
     return (
     return (
       <div className="wiki">
       <div className="wiki">
@@ -81,18 +75,6 @@ export const RevisionLoader = (props: RevisionLoaderProps): JSX.Element => {
     );
     );
   }
   }
 
 
-  /* ----- after load ----- */
-  const isForbidden = errors != null && errors[0].code === 'forbidden-page';
-  if (isForbidden) {
-    setMarkdown(`<i class="icon-exclamation p-1"></i>${t('not_allowed_to_see_this_page')}`);
-  }
-  else if (errors != null) {
-    const errorMessages = errors.map((error) => {
-      return `<i class="icon-exclamation p-1"></i><span class="text-muted"><em>${error.message}</em></span>`;
-    });
-    setMarkdown(errorMessages.join('\n'));
-  }
-
   return (
   return (
     <RevisionLoaderRoot>
     <RevisionLoaderRoot>
       <RevisionRenderer
       <RevisionRenderer

+ 1 - 1
packages/app/src/interfaces/revision.ts

@@ -1,3 +1,3 @@
 export type {
 export type {
-  IRevision, IRevisionsForPagination, IRevisionOnConflict, HasRevisionShortbody,
+  IRevision, IRevisionHasId, IRevisionsForPagination, IRevisionOnConflict, HasRevisionShortbody,
 } from '@growi/core';
 } from '@growi/core';

+ 5 - 1
packages/app/src/server/crowi/express-init.js

@@ -1,6 +1,7 @@
 import { manifestPath as presetThemesManifestPath } from '@growi/preset-themes';
 import { manifestPath as presetThemesManifestPath } from '@growi/preset-themes';
 import csrf from 'csurf';
 import csrf from 'csurf';
 import mongoose from 'mongoose';
 import mongoose from 'mongoose';
+import qs from 'qs';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 import { resolveFromRoot } from '~/utils/project-dir-utils';
 import { resolveFromRoot } from '~/utils/project-dir-utils';
@@ -30,6 +31,10 @@ module.exports = function(crowi, app) {
 
 
   const env = crowi.node_env;
   const env = crowi.node_env;
 
 
+  // see: https://qiita.com/nazomikan/items/9458d591a4831480098d
+  // Cannot set a custom query parser after app.use() has been called: https://github.com/expressjs/express/issues/3454
+  app.set('query parser', str => qs.parse(str, { arrayLimit: Infinity }));
+
   app.use(compression());
   app.use(compression());
 
 
 
 
@@ -55,7 +60,6 @@ module.exports = function(crowi, app) {
     logger.error(err);
     logger.error(err);
   }
   }
 
 
-
   app.use(helmet({
   app.use(helmet({
     contentSecurityPolicy: false,
     contentSecurityPolicy: false,
     expectCt: false,
     expectCt: false,

+ 11 - 3
packages/app/src/stores/page.tsx

@@ -1,9 +1,9 @@
-import { useEffect, useMemo } from 'react';
+import { useEffect, useMemo, useCallback } from 'react';
 
 
 import type {
 import type {
   IPageInfoForEntity, IPagePopulatedToShowRevision, Nullable,
   IPageInfoForEntity, IPagePopulatedToShowRevision, Nullable,
 } from '@growi/core';
 } from '@growi/core';
-import { isClient, pagePathUtils } from '@growi/core';
+import { Ref, isClient, pagePathUtils } from '@growi/core';
 import useSWR, { mutate, SWRResponse } from 'swr';
 import useSWR, { mutate, SWRResponse } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 import useSWRImmutable from 'swr/immutable';
 import useSWRMutation, { SWRMutationResponse } from 'swr/mutation';
 import useSWRMutation, { SWRMutationResponse } from 'swr/mutation';
@@ -14,7 +14,7 @@ import {
   IPageInfo, IPageInfoForOperation,
   IPageInfo, IPageInfoForOperation,
 } from '~/interfaces/page';
 } from '~/interfaces/page';
 import { IRecordApplicableGrant, IResIsGrantNormalized } from '~/interfaces/page-grant';
 import { IRecordApplicableGrant, IResIsGrantNormalized } from '~/interfaces/page-grant';
-import { IRevisionsForPagination } from '~/interfaces/revision';
+import { IRevision, IRevisionHasId, IRevisionsForPagination } from '~/interfaces/revision';
 
 
 import { IPageTagsInfo } from '../interfaces/tag';
 import { IPageTagsInfo } from '../interfaces/tag';
 
 
@@ -135,6 +135,14 @@ export const useSWRxPageInfo = (
   return swrResult;
   return swrResult;
 };
 };
 
 
+export const useSWRxPageRevision = (pageId: string, revisionId: Ref<IRevision>): SWRResponse<IRevisionHasId> => {
+  const key = [`/revisions/${revisionId}`, pageId, revisionId];
+  return useSWRImmutable(
+    key,
+    () => apiv3Get<{ revision: IRevisionHasId }>(`/revisions/${revisionId}`, { pageId }).then(response => response.data.revision),
+  );
+};
+
 export const useSWRxPageRevisions = (
 export const useSWRxPageRevisions = (
     page: number, // page number of pagination
     page: number, // page number of pagination
     limit: number, // max number of pages in one paginate
     limit: number, // max number of pages in one paginate