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

Merge branch 'master' into fix/104900-fix-convert-to-v5

Yuken Tezuka 3 лет назад
Родитель
Сommit
e335ad835b

+ 1 - 1
packages/app/src/components/Layout/SearchResultLayout.tsx

@@ -20,7 +20,7 @@ const SearchResultLayout = ({
   }
   }
 
 
   return (
   return (
-    <div className={`${commonStyles['on-search']}`}>
+    <div className={`on-search ${commonStyles['on-search']}`}>
       <BasicLayout title={title} className={classNames.join(' ')}>
       <BasicLayout title={title} className={classNames.join(' ')}>
         <div id="grw-fav-sticky-trigger" className="sticky-top"></div>
         <div id="grw-fav-sticky-trigger" className="sticky-top"></div>
         <div id="main" className="main search-page mt-0">
         <div id="main" className="main search-page mt-0">

+ 0 - 138
packages/app/src/components/Page/RevisionLoader.jsx

@@ -1,138 +0,0 @@
-import React from 'react';
-
-import { useTranslation } from 'next-i18next';
-import PropTypes from 'prop-types';
-import { Waypoint } from 'react-waypoint';
-
-import { apiv3Get } from '~/client/util/apiv3-client';
-import { RendererOptions } from '~/services/renderer/renderer';
-import loggerFactory from '~/utils/logger';
-
-import RevisionRenderer from './RevisionRenderer';
-
-
-/**
- * Load data from server and render RevisionBody component
- */
-class RevisionLoader extends React.Component {
-
-  constructor(props) {
-    super(props);
-    this.logger = loggerFactory('growi:Page:RevisionLoader');
-
-    this.state = {
-      markdown: null,
-      isLoading: false,
-      isLoaded: false,
-      errors: null,
-    };
-
-    this.loadData = this.loadData.bind(this);
-    this.onWaypointChange = this.onWaypointChange.bind(this);
-  }
-
-  UNSAFE_componentWillMount() {
-    if (!this.props.lazy) {
-      this.loadData();
-    }
-  }
-
-  async loadData() {
-    if (!this.state.isLoaded && !this.state.isLoading) {
-      this.setState({ isLoading: true });
-    }
-
-    const { pageId, revisionId } = this.props;
-
-
-    // load data with REST API
-    try {
-      const res = await apiv3Get(`/revisions/${revisionId}`, { pageId });
-
-      this.setState({
-        markdown: res.data?.revision?.body,
-        errors: null,
-      });
-
-      if (this.props.onRevisionLoaded != null) {
-        this.props.onRevisionLoaded(res.data.revision);
-      }
-    }
-    catch (errors) {
-      this.setState({ errors });
-    }
-    finally {
-      this.setState({ isLoaded: true, isLoading: false });
-    }
-
-  }
-
-  onWaypointChange(event) {
-    if (event.currentPosition === Waypoint.above || event.currentPosition === Waypoint.inside) {
-      this.loadData();
-    }
-  }
-
-  render() {
-    // ----- before load -----
-    if (this.props.lazy && !this.state.isLoaded) {
-      return (
-        <Waypoint onPositionChange={this.onWaypointChange} bottomOffset="-100px">
-          <div className="wiki"></div>
-        </Waypoint>
-      );
-    }
-
-    // ----- loading -----
-    if (this.state.isLoading) {
-      return (
-        <div className="wiki">
-          <div className="text-muted text-center">
-            <i className="fa fa-2x fa-spinner fa-pulse mr-1"></i>
-          </div>
-        </div>
-      );
-    }
-
-    // ----- after load -----
-    const isForbidden = this.state.errors != null && this.state.errors[0].code === 'forbidden-page';
-    let markdown = this.state.markdown;
-    if (isForbidden) {
-      markdown = `<i class="icon-exclamation p-1"></i>${this.props.t('not_allowed_to_see_this_page')}`;
-    }
-    else if (this.state.errors != null) {
-      const errorMessages = this.state.errors.map((error) => {
-        return `<i class="icon-exclamation p-1"></i><span class="text-muted"><em>${error.message}</em></span>`;
-      });
-      markdown = errorMessages.join('\n');
-    }
-
-    return (
-      <RevisionRenderer
-        rendererOptions={this.props.rendererOptions}
-        markdown={markdown}
-      />
-    );
-  }
-
-}
-
-
-RevisionLoader.propTypes = {
-  t: PropTypes.func.isRequired,
-
-  rendererOptions: PropTypes.instanceOf(RendererOptions).isRequired,
-  pageId: PropTypes.string.isRequired,
-  pagePath: PropTypes.string.isRequired,
-  revisionId: PropTypes.string.isRequired,
-  lazy: PropTypes.bool,
-  onRevisionLoaded: PropTypes.func,
-  highlightKeywords: PropTypes.arrayOf(PropTypes.string),
-};
-
-const RevisionLoaderWrapperFC = (props) => {
-  const { t } = useTranslation();
-  return <RevisionLoader t={t} {...props} />;
-};
-
-export default RevisionLoaderWrapperFC;

+ 118 - 0
packages/app/src/components/Page/RevisionLoader.tsx

@@ -0,0 +1,118 @@
+import React, { useEffect, useState, useCallback } from 'react';
+
+import { Ref, IRevision, IRevisionHasId } from '@growi/core';
+import { useTranslation } from 'next-i18next';
+import { Waypoint } from 'react-waypoint';
+
+import { apiv3Get } from '~/client/util/apiv3-client';
+import { RendererOptions } from '~/services/renderer/renderer';
+import loggerFactory from '~/utils/logger';
+
+import RevisionRenderer from './RevisionRenderer';
+
+export type RevisionLoaderProps = {
+  rendererOptions: RendererOptions,
+  pageId: string,
+  revisionId: Ref<IRevision>,
+  lazy?: boolean,
+  onRevisionLoaded?: (revision: IRevisionHasId) => void,
+
+  pagePath: string,
+  highlightKeywords?: string[],
+}
+
+const logger = loggerFactory('growi:Page:RevisionLoader');
+
+/**
+ * Load data from server and render RevisionBody component
+ */
+export const RevisionLoader = (props: RevisionLoaderProps): JSX.Element => {
+  const { t } = useTranslation();
+
+  const {
+    rendererOptions, pageId, revisionId, lazy, onRevisionLoaded,
+  } = 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);
+    }
+
+    // load data with REST API
+    try {
+      const res = await apiv3Get(`/revisions/${revisionId}`, { pageId });
+
+      setMarkdown(res.data?.revision?.body);
+      setErrors(null);
+
+      if (onRevisionLoaded != null) {
+        onRevisionLoaded(res.data.revision);
+      }
+    }
+    catch (errors) {
+      setErrors(errors);
+    }
+    finally {
+      setIsLoaded(true);
+      setIsLoading(false);
+    }
+
+  }, [isLoaded, isLoading, onRevisionLoaded, pageId, revisionId]);
+
+  useEffect(() => {
+    if (!lazy) {
+      loadData();
+    }
+  }, [lazy, loadData]);
+
+  const onWaypointChange = (event) => {
+    if (event.currentPosition === Waypoint.above || event.currentPosition === Waypoint.inside) {
+      loadData();
+    }
+    return;
+  };
+
+  /* ----- before load ----- */
+  if (lazy && !isLoaded) {
+    return (
+      <Waypoint onPositionChange={onWaypointChange} bottomOffset="-100px">
+        <div className="wiki"></div>
+      </Waypoint>
+    );
+  }
+
+  /* ----- loading ----- */
+  if (isLoading) {
+    return (
+      <div className="wiki">
+        <div className="text-muted text-center">
+          <i className="fa fa-2x fa-spinner fa-pulse mr-1"></i>
+        </div>
+      </div>
+    );
+  }
+
+  /* ----- 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 (
+    <RevisionRenderer
+      rendererOptions={rendererOptions}
+      markdown={markdown}
+    />
+  );
+};

+ 0 - 6
packages/app/src/components/PageComment/CommentEditor.module.scss

@@ -30,10 +30,4 @@
       padding-top: 0.5em;
       padding-top: 0.5em;
     }
     }
   }
   }
-
-  .page-comment-editor-skelton {
-    height: comment-inheritance.$codemirror-default-height;
-    margin-top: page-editor-inheritance.$navbar-editor-height;
-    margin-bottom: bs.$line-height-base + bs.$btn-padding-y;
-  }
 }
 }

+ 1 - 6
packages/app/src/components/PageComment/CommentEditor.tsx

@@ -20,7 +20,7 @@ import { useSWRxSlackChannels, useIsSlackEnabled } from '~/stores/editor';
 
 
 import { CustomNavTab } from '../CustomNavigation/CustomNav';
 import { CustomNavTab } from '../CustomNavigation/CustomNav';
 import NotAvailableForGuest from '../NotAvailableForGuest';
 import NotAvailableForGuest from '../NotAvailableForGuest';
-import { Skelton } from '../Skelton';
+import Editor from '../PageEditor/Editor';
 
 
 
 
 import { CommentPreview } from './CommentPreview';
 import { CommentPreview } from './CommentPreview';
@@ -29,11 +29,6 @@ import styles from './CommentEditor.module.scss';
 
 
 
 
 const SlackNotification = dynamic(() => import('../SlackNotification').then(mod => mod.SlackNotification), { ssr: false });
 const SlackNotification = dynamic(() => import('../SlackNotification').then(mod => mod.SlackNotification), { ssr: false });
-const Editor = dynamic(() => import('../PageEditor/Editor'),
-  {
-    ssr: false,
-    loading: () => <Skelton additionalClass="grw-skelton page-comment-editor-skelton" />,
-  });
 
 
 
 
 const navTabMapping = {
 const navTabMapping = {

+ 1 - 1
packages/app/src/components/PageTimeline.tsx

@@ -8,7 +8,7 @@ import { IPageHasId } from '~/interfaces/page';
 import { useCurrentPagePath } from '~/stores/context';
 import { useCurrentPagePath } from '~/stores/context';
 import { useTimelineOptions } from '~/stores/renderer';
 import { useTimelineOptions } from '~/stores/renderer';
 
 
-import RevisionLoader from './Page/RevisionLoader';
+import { RevisionLoader } from './Page/RevisionLoader';
 import PaginationWrapper from './PaginationWrapper';
 import PaginationWrapper from './PaginationWrapper';
 
 
 import styles from './PageTimeline.module.scss';
 import styles from './PageTimeline.module.scss';

+ 3 - 1
packages/app/src/components/SearchPage/SearchResultContent.tsx

@@ -24,12 +24,14 @@ import { useFullTextSearchTermManager } from '~/stores/search';
 import { AdditionalMenuItemsRendererProps, ForceHideMenuItems } from '../Common/Dropdown/PageItemControl';
 import { AdditionalMenuItemsRendererProps, ForceHideMenuItems } from '../Common/Dropdown/PageItemControl';
 import { GrowiSubNavigationProps } from '../Navbar/GrowiSubNavigation';
 import { GrowiSubNavigationProps } from '../Navbar/GrowiSubNavigation';
 import { SubNavButtonsProps } from '../Navbar/SubNavButtons';
 import { SubNavButtonsProps } from '../Navbar/SubNavButtons';
+import { RevisionLoaderProps } from '../Page/RevisionLoader';
 import { PageCommentProps } from '../PageComment';
 import { PageCommentProps } from '../PageComment';
 import { PageContentFooterProps } from '../PageContentFooter';
 import { PageContentFooterProps } from '../PageContentFooter';
 
 
+
 const GrowiSubNavigation = dynamic<GrowiSubNavigationProps>(() => import('../Navbar/GrowiSubNavigation').then(mod => mod.GrowiSubNavigation), { ssr: false });
 const GrowiSubNavigation = dynamic<GrowiSubNavigationProps>(() => import('../Navbar/GrowiSubNavigation').then(mod => mod.GrowiSubNavigation), { ssr: false });
 const SubNavButtons = dynamic<SubNavButtonsProps>(() => import('../Navbar/SubNavButtons').then(mod => mod.SubNavButtons), { ssr: false });
 const SubNavButtons = dynamic<SubNavButtonsProps>(() => import('../Navbar/SubNavButtons').then(mod => mod.SubNavButtons), { ssr: false });
-const RevisionLoader = dynamic(() => import('../Page/RevisionLoader'), { ssr: false });
+const RevisionLoader = dynamic<RevisionLoaderProps>(() => import('../Page/RevisionLoader').then(mod => mod.RevisionLoader), { ssr: false });
 const PageComment = dynamic<PageCommentProps>(() => import('../PageComment').then(mod => mod.PageComment), { ssr: false });
 const PageComment = dynamic<PageCommentProps>(() => import('../PageComment').then(mod => mod.PageComment), { ssr: false });
 const PageContentFooter = dynamic<PageContentFooterProps>(() => import('../PageContentFooter').then(mod => mod.PageContentFooter), { ssr: false });
 const PageContentFooter = dynamic<PageContentFooterProps>(() => import('../PageContentFooter').then(mod => mod.PageContentFooter), { ssr: false });
 
 

+ 2 - 1
packages/app/src/pages/[[...path]].page.tsx

@@ -59,7 +59,7 @@ import {
   useIsEnabledStaleNotification, useIsIdenticalPath,
   useIsEnabledStaleNotification, useIsIdenticalPath,
   useIsSearchServiceConfigured, useIsSearchServiceReachable, useDisableLinkSharing,
   useIsSearchServiceConfigured, useIsSearchServiceReachable, useDisableLinkSharing,
   useDrawioUri, useHackmdUri,
   useDrawioUri, useHackmdUri,
-  useIsAclEnabled, useIsUserPage,
+  useIsAclEnabled, useIsUserPage, useIsSearchPage,
   useCsrfToken, useIsSearchScopeChildrenAsDefault, useCurrentPageId, useCurrentPathname,
   useCsrfToken, useIsSearchScopeChildrenAsDefault, useCurrentPageId, useCurrentPathname,
   useIsSlackConfigured, useRendererConfig, useEditingMarkdown,
   useIsSlackConfigured, useRendererConfig, useEditingMarkdown,
   useEditorConfig, useIsAllReplyShown, useIsUploadableFile, useIsUploadableImage,
   useEditorConfig, useIsAllReplyShown, useIsUploadableFile, useIsUploadableImage,
@@ -204,6 +204,7 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
   useIsIdenticalPath(false); // TODO: need to initialize from props
   useIsIdenticalPath(false); // TODO: need to initialize from props
   // useIsAbleToDeleteCompletely(props.isAbleToDeleteCompletely);
   // useIsAbleToDeleteCompletely(props.isAbleToDeleteCompletely);
   useIsEnabledStaleNotification(props.isEnabledStaleNotification);
   useIsEnabledStaleNotification(props.isEnabledStaleNotification);
+  useIsSearchPage(false);
 
 
   useIsSearchServiceConfigured(props.isSearchServiceConfigured);
   useIsSearchServiceConfigured(props.isSearchServiceConfigured);
   useIsSearchServiceReachable(props.isSearchServiceReachable);
   useIsSearchServiceReachable(props.isSearchServiceReachable);

+ 2 - 1
packages/app/src/pages/admin/[[...path]].page.tsx

@@ -34,7 +34,7 @@ import PluginUtils from '~/server/plugins/plugin-utils';
 import ConfigLoader from '~/server/service/config-loader';
 import ConfigLoader from '~/server/service/config-loader';
 import {
 import {
   useCurrentUser, /* useSearchServiceConfigured, */ useIsAclEnabled, useIsMailerSetup, useIsSearchServiceReachable, useSiteUrl,
   useCurrentUser, /* useSearchServiceConfigured, */ useIsAclEnabled, useIsMailerSetup, useIsSearchServiceReachable, useSiteUrl,
-  useAuditLogEnabled, useAuditLogAvailableActions,
+  useAuditLogEnabled, useAuditLogAvailableActions, useIsSearchPage,
 } from '~/stores/context';
 } from '~/stores/context';
 import { useIsMaintenanceMode } from '~/stores/maintenanceMode';
 import { useIsMaintenanceMode } from '~/stores/maintenanceMode';
 
 
@@ -191,6 +191,7 @@ const AdminMarkdownSettingsPage: NextPage<Props> = (props: Props) => {
 
 
   const targetPage = getTargetPageToRender(adminPagesMap, pagePathKeys);
   const targetPage = getTargetPageToRender(adminPagesMap, pagePathKeys);
 
 
+  useIsSearchPage(false);
   useCurrentUser(props.currentUser != null ? JSON.parse(props.currentUser) : null);
   useCurrentUser(props.currentUser != null ? JSON.parse(props.currentUser) : null);
   useIsMailerSetup(props.isMailerSetup);
   useIsMailerSetup(props.isMailerSetup);
   useIsMaintenanceMode(props.isMaintenanceMode);
   useIsMaintenanceMode(props.isMaintenanceMode);

+ 3 - 1
packages/app/src/pages/me/[[...path]].page.tsx

@@ -18,7 +18,7 @@ import { ISidebarConfig } from '~/interfaces/sidebar-config';
 import { IUserUISettings } from '~/interfaces/user-ui-settings';
 import { IUserUISettings } from '~/interfaces/user-ui-settings';
 import { UserUISettingsModel } from '~/server/models/user-ui-settings';
 import { UserUISettingsModel } from '~/server/models/user-ui-settings';
 import {
 import {
-  useCurrentUser,
+  useCurrentUser, useIsSearchPage,
   useIsSearchServiceConfigured, useIsSearchServiceReachable,
   useIsSearchServiceConfigured, useIsSearchServiceReachable,
   useCsrfToken, useIsSearchScopeChildrenAsDefault,
   useCsrfToken, useIsSearchScopeChildrenAsDefault,
   useRegistrationWhiteList, useShowPageLimitationXL,
   useRegistrationWhiteList, useShowPageLimitationXL,
@@ -85,6 +85,8 @@ const MePage: NextPage<Props> = (props: Props) => {
 
 
   const targetPage = getTargetPageToRender(mePagesMap, pagePathKeys);
   const targetPage = getTargetPageToRender(mePagesMap, pagePathKeys);
 
 
+  useIsSearchPage(false);
+
   useCurrentUser(props.currentUser ?? null);
   useCurrentUser(props.currentUser ?? null);
 
 
   useRegistrationWhiteList(props.registrationWhiteList);
   useRegistrationWhiteList(props.registrationWhiteList);

+ 2 - 1
packages/app/src/pages/share/[[...path]].page.tsx

@@ -15,7 +15,7 @@ import { CrowiRequest } from '~/interfaces/crowi-request';
 import { RendererConfig } from '~/interfaces/services/renderer';
 import { RendererConfig } from '~/interfaces/services/renderer';
 import { IShareLinkHasId } from '~/interfaces/share-link';
 import { IShareLinkHasId } from '~/interfaces/share-link';
 import {
 import {
-  useCurrentUser, useCurrentPagePath, useCurrentPathname, useCurrentPageId, useRendererConfig,
+  useCurrentUser, useCurrentPagePath, useCurrentPathname, useCurrentPageId, useRendererConfig, useIsSearchPage,
   useShareLinkId, useIsSearchServiceConfigured, useIsSearchServiceReachable, useIsSearchScopeChildrenAsDefault,
   useShareLinkId, useIsSearchServiceConfigured, useIsSearchServiceReachable, useIsSearchScopeChildrenAsDefault,
 } from '~/stores/context';
 } from '~/stores/context';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
@@ -41,6 +41,7 @@ type Props = CommonProps & {
 };
 };
 
 
 const SharedPage: NextPage<Props> = (props: Props) => {
 const SharedPage: NextPage<Props> = (props: Props) => {
+  useIsSearchPage(false);
   useShareLinkId(props.shareLink?._id);
   useShareLinkId(props.shareLink?._id);
   useCurrentPageId(props.shareLink?.relatedPage._id);
   useCurrentPageId(props.shareLink?.relatedPage._id);
   useCurrentPagePath(props.shareLink?.relatedPage.path);
   useCurrentPagePath(props.shareLink?.relatedPage.path);

+ 2 - 1
packages/app/src/pages/tags.page.tsx

@@ -16,7 +16,7 @@ import { useSWRxTagsList } from '~/stores/tag';
 
 
 import { BasicLayout } from '../components/Layout/BasicLayout';
 import { BasicLayout } from '../components/Layout/BasicLayout';
 import {
 import {
-  useCurrentUser,
+  useCurrentUser, useIsSearchPage,
   useIsSearchServiceConfigured, useIsSearchServiceReachable,
   useIsSearchServiceConfigured, useIsSearchServiceReachable,
   useIsSearchScopeChildrenAsDefault,
   useIsSearchScopeChildrenAsDefault,
 } from '../stores/context';
 } from '../stores/context';
@@ -55,6 +55,7 @@ const TagPage: NextPage<CommonProps> = (props: Props) => {
   const isLoading = tagDataList === undefined && error == null;
   const isLoading = tagDataList === undefined && error == null;
   const classNames: string[] = [];
   const classNames: string[] = [];
 
 
+  useIsSearchPage(false);
   useIsSearchServiceConfigured(props.isSearchServiceConfigured);
   useIsSearchServiceConfigured(props.isSearchServiceConfigured);
   useIsSearchServiceReachable(props.isSearchServiceReachable);
   useIsSearchServiceReachable(props.isSearchServiceReachable);
   useIsSearchScopeChildrenAsDefault(props.isSearchScopeChildrenAsDefault);
   useIsSearchScopeChildrenAsDefault(props.isSearchScopeChildrenAsDefault);

+ 2 - 1
packages/app/src/pages/trash.page.tsx

@@ -15,7 +15,7 @@ import GrowiContextualSubNavigation from '../components/Navbar/GrowiContextualSu
 import {
 import {
   useCurrentUser, useCurrentPageId, useCurrentPagePath, useCurrentPathname,
   useCurrentUser, useCurrentPageId, useCurrentPagePath, useCurrentPathname,
   useIsSearchServiceConfigured, useIsSearchServiceReachable,
   useIsSearchServiceConfigured, useIsSearchServiceReachable,
-  useIsSearchScopeChildrenAsDefault,
+  useIsSearchScopeChildrenAsDefault, useIsSearchPage,
 } from '../stores/context';
 } from '../stores/context';
 
 
 import {
 import {
@@ -41,6 +41,7 @@ const TrashPage: NextPage<CommonProps> = (props: Props) => {
   useIsSearchServiceReachable(props.isSearchServiceReachable);
   useIsSearchServiceReachable(props.isSearchServiceReachable);
   useIsSearchScopeChildrenAsDefault(props.isSearchScopeChildrenAsDefault);
   useIsSearchScopeChildrenAsDefault(props.isSearchScopeChildrenAsDefault);
 
 
+  useIsSearchPage(false);
   useCurrentPageId(null);
   useCurrentPageId(null);
   useCurrentPathname('/trash');
   useCurrentPathname('/trash');
   useCurrentPagePath('/trash');
   useCurrentPagePath('/trash');