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

Merge pull request #2661 from weseek/imprv/paginate-history

Imprv/paginate history
itizawa 5 лет назад
Родитель
Сommit
b2a07c4bc4

+ 40 - 7
src/client/js/components/PageHistory.jsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useCallback } from 'react';
 import PropTypes from 'prop-types';
 import loggerFactory from '@alias/logger';
 
@@ -9,6 +9,8 @@ import { withLoadingSppiner } from './SuspenseUtils';
 import PageRevisionList from './PageHistory/PageRevisionList';
 
 import PageHistroyContainer from '../services/PageHistoryContainer';
+import PaginationWrapper from './PaginationWrapper';
+
 
 const logger = loggerFactory('growi:PageHistory');
 
@@ -16,10 +18,29 @@ const logger = loggerFactory('growi:PageHistory');
 function PageHistory(props) {
   const { pageHistoryContainer } = props;
 
+  const handlePage = useCallback(async(selectedPage) => {
+    try {
+      await props.pageHistoryContainer.retrieveRevisions(selectedPage);
+    }
+    catch (err) {
+      toastError(err);
+      props.pageHistoryContainer.setState({ errorMessage: err.message });
+      logger.error(err);
+    }
+  }, [props.pageHistoryContainer]);
+
+  if (pageHistoryContainer.state.errorMessage != null) {
+    return (
+      <div className="my-5">
+        <div className="text-danger">{pageHistoryContainer.state.errorMessage}</div>
+      </div>
+    );
+  }
+
   if (pageHistoryContainer.state.revisions === pageHistoryContainer.dummyRevisions) {
     throw new Promise(async() => {
       try {
-        await props.pageHistoryContainer.retrieveRevisions();
+        await props.pageHistoryContainer.retrieveRevisions(1);
       }
       catch (err) {
         toastError(err);
@@ -29,19 +50,31 @@ function PageHistory(props) {
     });
   }
 
+
+  function pager() {
+    return (
+      <div className="my-3">
+        <PaginationWrapper
+          activePage={pageHistoryContainer.state.activePage}
+          changePage={handlePage}
+          totalItemsCount={pageHistoryContainer.state.totalPages}
+          pagingLimit={pageHistoryContainer.state.pagingLimit}
+        />
+      </div>
+    );
+  }
+
+
   return (
     <div className="mt-4">
-      {pageHistoryContainer.state.errorMessage && (
-      <div className="my-5">
-        <div className="text-danger">{pageHistoryContainer.state.errorMessage}</div>
-      </div>
-        ) }
+      {pager()}
       <PageRevisionList
         revisions={pageHistoryContainer.state.revisions}
         diffOpened={pageHistoryContainer.state.diffOpened}
         getPreviousRevision={pageHistoryContainer.getPreviousRevision}
         onDiffOpenClicked={pageHistoryContainer.onDiffOpenClicked}
       />
+      {pager()}
     </div>
   );
 

+ 1 - 1
src/client/js/components/SuspenseUtils.jsx

@@ -15,7 +15,7 @@ export function withLoadingSppiner(Component) {
         </div>
       )}
     >
-      <Component {...props} />;
+      <Component {...props} />
     </Suspense>
   ));
 }

+ 21 - 6
src/client/js/services/PageHistoryContainer.js

@@ -26,6 +26,10 @@ export default class PageHistoryContainer extends Container {
       // set dummy rivisions for using suspense
       revisions: this.dummyRevisions,
       diffOpened: {},
+
+      totalPages: 0,
+      activePage: 1,
+      pagingLimit: Infinity,
     };
 
     this.retrieveRevisions = this.retrieveRevisions.bind(this);
@@ -41,19 +45,30 @@ export default class PageHistoryContainer extends Container {
     return 'PageHistoryContainer';
   }
 
-  async retrieveRevisions() {
+  /**
+   * syncRevisions of selectedPage
+   * @param {number} selectedPage
+   */
+  async retrieveRevisions(selectedPage) {
     const { pageId, shareLinkId } = this.pageContainer.state;
-
     if (!pageId) {
       return;
     }
 
-    const res = await this.appContainer.apiv3Get('/revisions/list', { pageId, share_link_id: shareLinkId });
-    const rev = res.data.revisions;
+    const res = await this.appContainer.apiv3Get('/revisions/list', { page_id: pageId, share_link_id: shareLinkId, selectedPage });
+    const rev = res.data.docs;
+
+    // set Pagination state
+    this.setState({
+      activePage: selectedPage,
+      totalPages: res.data.totalDocs,
+      pagingLimit: res.data.limit,
+    });
+
     const diffOpened = {};
     const lastId = rev.length - 1;
 
-    res.data.revisions.forEach((revision, i) => {
+    res.data.docs.forEach((revision, i) => {
       const user = revision.author;
       if (user) {
         rev[i].author = user;
@@ -123,7 +138,7 @@ export default class PageHistoryContainer extends Container {
     }
 
     try {
-      const res = await this.appContainer.apiv3Get(`/revisions/${revision._id}`, { pageId, share_link_id: shareLinkId });
+      const res = await this.appContainer.apiv3Get(`/revisions/${revision._id}`, { page_id: pageId, share_link_id: shareLinkId });
       this.setState({
         revisions: this.state.revisions.map((rev) => {
           // comparing ObjectId

+ 2 - 0
src/server/models/revision.js

@@ -6,6 +6,7 @@ module.exports = function(crowi) {
   const logger = require('@alias/logger')('growi:models:revision');
 
   const mongoose = require('mongoose');
+  const mongoosePaginate = require('mongoose-paginate-v2');
 
   const ObjectId = mongoose.Schema.Types.ObjectId;
   const revisionSchema = new mongoose.Schema({
@@ -24,6 +25,7 @@ module.exports = function(crowi) {
     createdAt: { type: Date, default: Date.now },
     hasDiffToPrev: { type: Boolean },
   });
+  revisionSchema.plugin(mongoosePaginate);
 
   /*
    * preparation for https://github.com/weseek/growi/issues/216

+ 26 - 8
src/server/routes/apiv3/revisions.js

@@ -9,15 +9,17 @@ const ErrorV3 = require('../../models/vo/error-apiv3');
 
 const router = express.Router();
 
+const PAGE_ITEMS = 30;
+
 /**
  * @swagger
  *  tags:
  *    name: Revisions
  */
 module.exports = (crowi) => {
-  const certifySharedPage = require('../../middlewares/certify-shared-file')(crowi);
+  const certifySharedPage = require('../../middlewares/certify-shared-page')(crowi);
   const accessTokenParser = require('../../middlewares/access-token-parser')(crowi);
-  const loginRequired = require('../../middlewares/login-required')(crowi);
+  const loginRequired = require('../../middlewares/login-required')(crowi, true);
   const apiV3FormValidator = require('../../middlewares/apiv3-form-validator')(crowi);
 
   const {
@@ -28,10 +30,11 @@ module.exports = (crowi) => {
 
   const validator = {
     retrieveRevisions: [
-      query('pageId').isMongoId().withMessage('pageId is required'),
+      query('page_id').isMongoId().withMessage('pageId is required'),
+      query('selectedPage').isInt({ min: 0 }).withMessage('selectedPage must be int'),
     ],
     retrieveRevisionById: [
-      query('pageId').isMongoId().withMessage('pageId is required'),
+      query('page_id').isMongoId().withMessage('pageId is required'),
       param('id').isMongoId().withMessage('id is required'),
     ],
   };
@@ -55,9 +58,11 @@ module.exports = (crowi) => {
    *
    */
   router.get('/list', certifySharedPage, accessTokenParser, loginRequired, validator.retrieveRevisions, apiV3FormValidator, async(req, res) => {
-    const { pageId } = req.query;
+    const pageId = req.query.page_id;
     const { isSharedPage } = req;
 
+    const selectedPage = parseInt(req.query.selectedPage) || 1;
+
     // check whether accessible
     if (!isSharedPage && !(await Page.isAccessiblePageByViewer(pageId, req.user))) {
       return res.apiv3Err(new ErrorV3('Current user is not accessible to this page.', 'forbidden-page'), 403);
@@ -65,8 +70,21 @@ module.exports = (crowi) => {
 
     try {
       const page = await Page.findOne({ _id: pageId });
-      const revisions = await Revision.findRevisionIdList(page.path);
-      return res.apiv3({ revisions });
+
+      const paginateResult = await Revision.paginate(
+        { path: page.path },
+        {
+          page: selectedPage,
+          limit: PAGE_ITEMS,
+          sort: { createdAt: -1 },
+          populate: {
+            path: 'author',
+            select: User.USER_PUBLIC_FIELDS,
+          },
+        },
+      );
+
+      return res.apiv3(paginateResult);
     }
     catch (err) {
       const msg = 'Error occurred in getting revisions by poge id';
@@ -103,7 +121,7 @@ module.exports = (crowi) => {
    */
   router.get('/:id', certifySharedPage, accessTokenParser, loginRequired, validator.retrieveRevisionById, apiV3FormValidator, async(req, res) => {
     const revisionId = req.params.id;
-    const { pageId } = req.query;
+    const pageId = req.query.page_id;
     const { isSharedPage } = req;
 
     // check whether accessible