Przeglądaj źródła

Merge branch 'master' into fix/req-params

jam411 3 lat temu
rodzic
commit
7a68293be8

+ 5 - 5
packages/app/src/components/Comments.tsx

@@ -3,7 +3,7 @@ import React from 'react';
 import { IRevisionHasId } from '@growi/core';
 import dynamic from 'next/dynamic';
 
-import { PageComment } from '~/components/PageComment';
+import { PageCommentProps } from '~/components/PageComment';
 import { useSWRxPageComment } from '~/stores/comment';
 import { useIsTrashPage } from '~/stores/page';
 
@@ -11,18 +11,18 @@ import { useCurrentUser } from '../stores/context';
 
 import { CommentEditorProps } from './PageComment/CommentEditor';
 
-
+const PageComment = dynamic<PageCommentProps>(() => import('~/components/PageComment').then(mod => mod.PageComment), { ssr: false });
 const CommentEditor = dynamic<CommentEditorProps>(() => import('./PageComment/CommentEditor').then(mod => mod.CommentEditor), { ssr: false });
 
-
 type CommentsProps = {
   pageId: string,
+  pagePath: string,
   revision: IRevisionHasId,
 }
 
 export const Comments = (props: CommentsProps): JSX.Element => {
 
-  const { pageId, revision } = props;
+  const { pageId, pagePath, revision } = props;
 
   const { mutate } = useSWRxPageComment(pageId);
   const { data: isDeleted } = useIsTrashPage();
@@ -33,13 +33,13 @@ export const Comments = (props: CommentsProps): JSX.Element => {
   }
 
   return (
-    // TODO: Check and refactor CSS import
     <div className="page-comments-row mt-5 py-4 d-edit-none d-print-none">
       <div className="container-lg">
         <div className="page-comments">
           <div id="page-comments-list" className="page-comments-list">
             <PageComment
               pageId={pageId}
+              pagePath={pagePath}
               revision={revision}
               currentUser={currentUser}
               isReadOnly={false}

+ 9 - 11
packages/app/src/components/PageComment.tsx

@@ -3,7 +3,6 @@ import React, {
 } from 'react';
 
 import { IRevisionHasId, isPopulated, getIdForRef } from '@growi/core';
-import dynamic from 'next/dynamic';
 import { Button } from 'reactstrap';
 
 import { toastError } from '~/client/util/apiNotification';
@@ -15,18 +14,12 @@ import { ICommentHasId, ICommentHasIdList } from '../interfaces/comment';
 import { useSWRxPageComment } from '../stores/comment';
 
 import { Comment } from './PageComment/Comment';
-import { CommentEditorProps } from './PageComment/CommentEditor';
-import { DeleteCommentModalProps } from './PageComment/DeleteCommentModal';
+import { CommentEditor } from './PageComment/CommentEditor';
+import { DeleteCommentModal } from './PageComment/DeleteCommentModal';
 import { ReplyComments } from './PageComment/ReplyComments';
-import { PageCommentSkeleton } from './PageCommentSkeleton';
 
 import styles from './PageComment.module.scss';
 
-const CommentEditor = dynamic<CommentEditorProps>(() => import('./PageComment/CommentEditor').then(mod => mod.CommentEditor), { ssr: false });
-const DeleteCommentModal = dynamic<DeleteCommentModalProps>(
-  () => import('./PageComment/DeleteCommentModal').then(mod => mod.DeleteCommentModal), { ssr: false },
-);
-
 export const ROOT_ELEM_ID = 'page-comments' as const;
 
 // Always render '#page-comments' for MutationObserver of SearchResultContent
@@ -38,6 +31,7 @@ const PageCommentRoot = (props: React.HTMLAttributes<HTMLDivElement>): JSX.Eleme
 export type PageCommentProps = {
   rendererOptions?: RendererOptions,
   pageId: string,
+  pagePath: string,
   revision: string | IRevisionHasId,
   currentUser: any,
   isReadOnly: boolean,
@@ -49,7 +43,7 @@ export const PageComment: FC<PageCommentProps> = memo((props:PageCommentProps):
 
   const {
     rendererOptions: rendererOptionsByProps,
-    pageId, revision, currentUser, isReadOnly, titleAlign, hideIfEmpty,
+    pageId, pagePath, revision, currentUser, isReadOnly, titleAlign, hideIfEmpty,
   } = props;
 
   const { data: comments, mutate } = useSWRxPageComment(pageId);
@@ -123,7 +117,7 @@ export const PageComment: FC<PageCommentProps> = memo((props:PageCommentProps):
       return <PageCommentRoot />;
     }
     return (
-      <PageCommentSkeleton commentTitleClasses={commentTitleClasses}/>
+      <></>
     );
   }
 
@@ -138,6 +132,8 @@ export const PageComment: FC<PageCommentProps> = memo((props:PageCommentProps):
       revisionCreatedAt={revisionCreatedAt as Date}
       currentUser={currentUser}
       isReadOnly={isReadOnly}
+      pageId={pageId}
+      pagePath={pagePath}
       deleteBtnClicked={onClickDeleteButton}
       onComment={mutate}
     />
@@ -151,6 +147,8 @@ export const PageComment: FC<PageCommentProps> = memo((props:PageCommentProps):
       revisionCreatedAt={revisionCreatedAt as Date}
       currentUser={currentUser}
       replyList={replyComments}
+      pageId={pageId}
+      pagePath={pagePath}
       deleteBtnClicked={onClickDeleteButton}
       onComment={mutate}
     />

+ 19 - 12
packages/app/src/components/PageComment/Comment.tsx

@@ -1,11 +1,12 @@
 import React, { useEffect, useMemo, useState } from 'react';
 
-import { IUser } from '@growi/core';
+import { IUser, pathUtils } from '@growi/core';
 import { UserPicture } from '@growi/ui';
 import { format } from 'date-fns';
 import { useTranslation } from 'next-i18next';
-import dynamic from 'next/dynamic';
+import Link from 'next/link';
 import { UncontrolledTooltip } from 'reactstrap';
+import urljoin from 'url-join';
 
 import { RendererOptions } from '~/services/renderer/renderer';
 
@@ -16,12 +17,10 @@ import RevisionRenderer from '../Page/RevisionRenderer';
 import { Username } from '../User/Username';
 
 import { CommentControl } from './CommentControl';
-import { CommentEditorProps } from './CommentEditor';
+import { CommentEditor } from './CommentEditor';
 
 import styles from './Comment.module.scss';
 
-const CommentEditor = dynamic<CommentEditorProps>(() => import('./CommentEditor').then(mod => mod.CommentEditor), { ssr: false });
-
 type CommentProps = {
   comment: ICommentHasId,
   rendererOptions: RendererOptions,
@@ -29,6 +28,8 @@ type CommentProps = {
   revisionCreatedAt: Date,
   currentUser: IUser,
   isReadOnly: boolean,
+  pageId: string,
+  pagePath: string,
   deleteBtnClicked: (comment: ICommentHasId) => void,
   onComment: () => void,
 }
@@ -37,9 +38,11 @@ export const Comment = (props: CommentProps): JSX.Element => {
 
   const {
     comment, rendererOptions, revisionId, revisionCreatedAt, currentUser, isReadOnly,
-    deleteBtnClicked, onComment,
+    pageId, pagePath, deleteBtnClicked, onComment,
   } = props;
 
+  const { returnPathForURL } = pathUtils;
+
   const { t } = useTranslation();
 
   const [markdown, setMarkdown] = useState('');
@@ -151,9 +154,11 @@ export const Comment = (props: CommentProps): JSX.Element => {
             </div>
             <div className="page-comment-body">{commentBody}</div>
             <div className="page-comment-meta">
-              <a href={`#${commentId}`}>
-                <FormattedDistanceDate id={commentId} date={comment.createdAt} />
-              </a>
+              <Link href={`#${commentId}`} prefetch={false}>
+                <a>
+                  <FormattedDistanceDate id={commentId} date={comment.createdAt} />
+                </a>
+              </Link>
               { isEdited && (
                 <>
                   <span id={editedDateId}>&nbsp;(edited)</span>
@@ -161,9 +166,11 @@ export const Comment = (props: CommentProps): JSX.Element => {
                 </>
               ) }
               <span className="ml-2">
-                <a id={`page-comment-revision-${commentId}`} className="page-comment-revision" href={revHref}>
-                  <HistoryIcon />
-                </a>
+                <Link href={urljoin(returnPathForURL(pagePath, pageId), revHref)} prefetch={false}>
+                  <a id={`page-comment-revision-${commentId}`} className="page-comment-revision">
+                    <HistoryIcon />
+                  </a>
+                </Link>
                 <UncontrolledTooltip placement="bottom" fade={false} target={`page-comment-revision-${commentId}`}>
                   {t('page_comment.display_the_page_when_posting_this_comment')}
                 </UncontrolledTooltip>

+ 5 - 1
packages/app/src/components/PageComment/ReplyComments.tsx

@@ -21,6 +21,8 @@ type ReplycommentsProps = {
   revisionCreatedAt: Date,
   currentUser: IUser,
   replyList: ICommentHasIdList,
+  pageId: string,
+  pagePath: string,
   deleteBtnClicked: (comment: ICommentHasId) => void,
   onComment: () => void,
 }
@@ -29,7 +31,7 @@ export const ReplyComments = (props: ReplycommentsProps): JSX.Element => {
 
   const {
     rendererOptions, isReadOnly, revisionId, revisionCreatedAt, currentUser, replyList,
-    deleteBtnClicked, onComment,
+    pageId, pagePath, deleteBtnClicked, onComment,
   } = props;
 
   const { data: isAllReplyShown } = useIsAllReplyShown();
@@ -46,6 +48,8 @@ export const ReplyComments = (props: ReplycommentsProps): JSX.Element => {
           revisionCreatedAt={revisionCreatedAt}
           currentUser={currentUser}
           isReadOnly={isReadOnly}
+          pageId={pageId}
+          pagePath={pagePath}
           deleteBtnClicked={deleteBtnClicked}
           onComment={onComment}
         />

+ 0 - 58
packages/app/src/components/PageCommentSkeleton.tsx

@@ -1,58 +0,0 @@
-import React from 'react';
-
-import { Skeleton } from './Skeleton';
-
-import styles from './PageComment.module.scss';
-import CommentStyles from './PageComment/Comment.module.scss';
-import CommentEditorStyles from './PageComment/CommentEditor.module.scss';
-
-type PageCommentSkeletonProps = {
-  commentTitleClasses?: string,
-  roundedPill?: boolean,
-}
-
-export const PageCommentSkeleton = (props: PageCommentSkeletonProps): JSX.Element => {
-  const {
-    commentTitleClasses,
-  } = props;
-
-  return (
-    <>
-      {/* TODO: Check the comment.html CSS */}
-      <div className={`${styles['page-comment-styles']} page-comments-row comment-list`}>
-        <div className="container-lg">
-          <div className="page-comments">
-            <h2 className={commentTitleClasses}><i className="icon-fw icon-bubbles"></i>Comments</h2>
-            <div className="page-comments-list" id="page-comments-list">
-              <div className={`${CommentStyles['comment-styles']} page-comment-thread pb-5  page-comment-thread-no-replies`}>
-                <div className='page-comment flex-column'>
-                  <div className='page-commnet-writer'>
-                    <Skeleton additionalClass='rounded-circle picture' roundedPill />
-                  </div>
-                  <Skeleton additionalClass="page-comment-comment-body-skeleton grw-skeleton" />
-                </div>
-                <div className='page-comment flex-column ml-4 ml-sm-5 mr-3'>
-                  <div className='page-commnet-writer mt-3'>
-                    <Skeleton additionalClass='rounded-circle picture' roundedPill />
-                  </div>
-                  <Skeleton additionalClass="page-comment-comment-body-skeleton grw-skeleton mt-3" />
-                </div>
-                <div className="text-right">
-                  <Skeleton additionalClass="page-comment-button-skeleton btn btn-outline-secondary btn-sm grw-skeleton" />
-                </div>
-              </div>
-            </div>
-            <div className={`${CommentEditorStyles['comment-editor-styles']} form page-comment-form`}>
-              <div className='comment-form'>
-                <div className='comment-form-user'>
-                  <Skeleton additionalClass='rounded-circle picture' roundedPill />
-                </div>
-                <Skeleton additionalClass="page-comment-commenteditorlazyrenderer-body-skeleton grw-skeleton" />
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-    </>
-  );
-};

+ 1 - 5
packages/app/src/components/PageContentFooter.tsx

@@ -6,14 +6,10 @@ import dynamic from 'next/dynamic';
 import { useSWRxCurrentPage } from '~/stores/page';
 
 import type { AuthorInfoProps } from './Navbar/AuthorInfo';
-import { Skeleton } from './Skeleton';
 
 import styles from './PageContentFooter.module.scss';
 
-const AuthorInfo = dynamic<AuthorInfoProps>(() => import('./Navbar/AuthorInfo').then(mod => mod.AuthorInfo), {
-  ssr: false,
-  loading: () => <Skeleton additionalClass={`${styles['page-content-footer-skeleton']} mb-3`} />,
-});
+const AuthorInfo = dynamic<AuthorInfoProps>(() => import('./Navbar/AuthorInfo').then(mod => mod.AuthorInfo), { ssr: false });
 
 export type PageContentFooterProps = {
   page: IPage,

+ 1 - 1
packages/app/src/components/PageHistory/Revision.tsx

@@ -69,7 +69,7 @@ export const Revision = (props: RevisionProps): JSX.Element => {
           <div className="mb-1">
             <UserDate dateTime={revision.createdAt} />
             <br className="d-xl-none d-block" />
-            <Link href={urljoin(returnPathForURL(currentPageId, currentPagePath), `?revisionId=${revision._id}`)} prefetch={false}>
+            <Link href={urljoin(returnPathForURL(currentPagePath, currentPageId), `?revisionId=${revision._id}`)} prefetch={false}>
               <a className="ml-xl-3" onClick={onClose}>
                 <i className="icon-login"></i> {t('Go to this version')}
               </a>

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

@@ -262,6 +262,7 @@ export const SearchResultContent: FC<Props> = (props: Props) => {
         <PageComment
           rendererOptions={rendererOptions}
           pageId={page._id}
+          pagePath={page.path}
           revision={page.revision}
           currentUser={currentUser}
           isReadOnly

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

@@ -337,7 +337,9 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
           </div>
           { !props.isIdenticalPathPage && !props.isNotFound && (
             <footer className="footer d-edit-none">
-              { pageWithMeta != null && !isTopPagePath && (<Comments pageId={pageId} revision={pageWithMeta.data.revision} />) }
+              { pageWithMeta != null && pagePath != null && !isTopPagePath && (
+                <Comments pageId={pageId} pagePath={pagePath} revision={pageWithMeta.data.revision} />
+              ) }
               { pageWithMeta != null && isUsersHomePage(pageWithMeta.data.path) && (
                 <UsersHomePageFooter creatorId={pageWithMeta.data.creator._id}/>
               ) }

+ 7 - 2
packages/app/src/server/routes/apiv3/pages.js

@@ -1,4 +1,3 @@
-import { ErrorV3 } from '@growi/core';
 
 import { SupportedTargetModel, SupportedAction } from '~/interfaces/activity';
 import { subscribeRuleNames } from '~/interfaces/in-app-notification';
@@ -8,6 +7,8 @@ import { generateAddActivityMiddleware } from '../../middlewares/add-activity';
 import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
 import { isV5ConversionError } from '../../models/vo/v5-conversion-error';
 
+import { ErrorV3 } from '@growi/core';
+
 const logger = loggerFactory('growi:routes:apiv3:pages'); // eslint-disable-line no-unused-vars
 const { pathUtils, pagePathUtils } = require('@growi/core');
 const express = require('express');
@@ -627,7 +628,11 @@ module.exports = (crowi) => {
     // when all pages are deletable
     else {
       try {
-        const pages = await crowi.pageService.emptyTrashPage(req.user, options);
+        const activityParameters = {
+          ip: req.ip,
+          endpoint: req.originalUrl,
+        };
+        const pages = await crowi.pageService.emptyTrashPage(req.user, options, activityParameters);
 
         activityEvent.emit('update', res.locals.activity._id, parameters);
 

+ 22 - 2
packages/app/src/server/service/page.ts

@@ -1857,8 +1857,28 @@ class PageService {
     return;
   }
 
-  async emptyTrashPage(user, options = {}) {
-    return this.deleteCompletelyDescendantsWithStream({ path: '/trash' }, user, options);
+  async emptyTrashPage(user, options = {}, activityParameters) {
+    const page = { path: '/trash' };
+
+    const parameters = {
+      ...activityParameters,
+      action: SupportedAction.ACTION_PAGE_RECURSIVELY_DELETE_COMPLETELY,
+      user,
+      targetModel: 'Page',
+      snapshot: {
+        username: user.username,
+      },
+    };
+
+    const activity = await this.crowi.activityService.createActivity(parameters);
+
+    const descendantsSubscribedSets = new Set();
+    const pages = await this.deleteCompletelyDescendantsWithStream(page, user, options, true, descendantsSubscribedSets);
+    const descendantsSubscribedUsers = Array.from(descendantsSubscribedSets);
+
+    this.activityEvent.emit('updated', activity, page, descendantsSubscribedUsers);
+
+    return pages;
   }
 
   /**

+ 4 - 4
packages/app/src/stores/ui.tsx

@@ -97,17 +97,17 @@ const getClassNamesByEditorMode = (editorMode: EditorMode | undefined, isSidebar
 };
 
 const updateHashByEditorMode = (newEditorMode: EditorMode) => {
-  const { pathname } = window.location;
+  const { pathname, search } = window.location;
 
   switch (newEditorMode) {
     case EditorMode.View:
-      window.history.replaceState(null, '', pathname);
+      window.history.replaceState(null, '', `${pathname}${search}`);
       break;
     case EditorMode.Editor:
-      window.history.replaceState(null, '', `${pathname}#edit`);
+      window.history.replaceState(null, '', `${pathname}${search}#edit`);
       break;
     case EditorMode.HackMD:
-      window.history.replaceState(null, '', `${pathname}#hackmd`);
+      window.history.replaceState(null, '', `${pathname}${search}#hackmd`);
       break;
   }
 };