Quellcode durchsuchen

Merge pull request #6721 from weseek/fix/search-page-scroll

fix: RevisionLoader to ts/fc
Yuki Takei vor 3 Jahren
Ursprung
Commit
1e998bf29e

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

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

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

@@ -8,7 +8,7 @@ import { IPageHasId } from '~/interfaces/page';
 import { useCurrentPagePath } from '~/stores/context';
 import { useTimelineOptions } from '~/stores/renderer';
 
-import RevisionLoader from './Page/RevisionLoader';
+import { RevisionLoader } from './Page/RevisionLoader';
 import PaginationWrapper from './PaginationWrapper';
 
 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 { GrowiSubNavigationProps } from '../Navbar/GrowiSubNavigation';
 import { SubNavButtonsProps } from '../Navbar/SubNavButtons';
+import { RevisionLoaderProps } from '../Page/RevisionLoader';
 import { PageCommentProps } from '../PageComment';
 import { PageContentFooterProps } from '../PageContentFooter';
 
+
 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 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 PageContentFooter = dynamic<PageContentFooterProps>(() => import('../PageContentFooter').then(mod => mod.PageContentFooter), { ssr: false });