Parcourir la source

Merge pull request #6856 from weseek/feat/106973-enable-View-this-version

feat: 106973 Enable History feature on PageAccessoriesModal
Ryoji Shimizu il y a 3 ans
Parent
commit
0c1457829b

+ 1 - 0
packages/app/src/components/Comments.tsx

@@ -53,6 +53,7 @@ export const Comments = (props: CommentsProps): JSX.Element => {
                 pageId={pageId}
                 isForNewComment
                 onCommentButtonClicked={mutate}
+                revisionId={revision._id}
               />
             </div>
           )}

+ 1 - 1
packages/app/src/components/Navbar/PageEditorModeManager.jsx

@@ -27,7 +27,7 @@ const PageEditorModeButtonWrapper = React.memo(({
       className={classNames.join(' ')}
       onClick={() => { onClick(targetMode) }}
       id={id}
-      data-testId={`${targetMode}-button`}
+      data-testid={`${targetMode}-button`}
     >
       <span className="d-flex flex-column flex-md-row justify-content-center">
         <span className="grw-page-editor-mode-manager-icon mr-md-1">{icon}</span>

+ 0 - 1
packages/app/src/components/Page.tsx

@@ -203,7 +203,6 @@ export const Page = (props) => {
     return null;
   }
 
-
   return (
     <PageSubstance
       {...props}

+ 2 - 2
packages/app/src/components/PageAccessoriesModal.tsx

@@ -57,7 +57,7 @@ const PageAccessoriesModal = (): JSX.Element => {
           if (!isOpened) {
             return <></>;
           }
-          return <PageHistory />;
+          return <PageHistory onClose={close}/>;
         },
         i18n: t('History'),
         index: 0,
@@ -87,7 +87,7 @@ const PageAccessoriesModal = (): JSX.Element => {
         isLinkEnabled: () => !isGuestUser && !isSharedUser && !isLinkSharingDisabled,
       },
     };
-  }, [status, t, isGuestUser, isSharedUser, isLinkSharingDisabled]);
+  }, [status, t, close, isGuestUser, isSharedUser, isLinkSharingDisabled]);
 
   const buttons = useMemo(() => (
     <div className="d-flex flex-nowrap">

+ 1 - 0
packages/app/src/components/PageComment.tsx

@@ -200,6 +200,7 @@ export const PageComment: FC<PageCommentProps> = memo((props:PageCommentProps):
                         removeShowEditorId(comment._id);
                         mutate();
                       }}
+                      revisionId={revisionId}
                     />
                   )}
                 </div>

+ 1 - 0
packages/app/src/components/PageComment/Comment.tsx

@@ -138,6 +138,7 @@ export const Comment = (props: CommentProps): JSX.Element => {
             setIsReEdit(false);
             if (onComment != null) onComment();
           }}
+          revisionId={revisionId}
         />
       ) : (
         <div id={commentId} className={rootClassName}>

+ 3 - 3
packages/app/src/components/PageComment/CommentEditor.tsx

@@ -13,7 +13,7 @@ import { apiPostForm } from '~/client/util/apiv1-client';
 import { IEditorMethods } from '~/interfaces/editor-methods';
 import { useSWRxPageComment } from '~/stores/comment';
 import {
-  useCurrentUser, useRevisionId, useIsSlackConfigured,
+  useCurrentUser, useIsSlackConfigured,
   useIsUploadableFile, useIsUploadableImage,
 } from '~/stores/context';
 import { useSWRxSlackChannels, useIsSlackEnabled } from '~/stores/editor';
@@ -49,6 +49,7 @@ export type CommentEditorProps = {
   pageId: string,
   isForNewComment?: boolean,
   replyTo?: string,
+  revisionId: string,
   currentCommentId?: string,
   commentBody?: string,
   onCancelButtonClicked?: () => void,
@@ -59,14 +60,13 @@ export type CommentEditorProps = {
 export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
 
   const {
-    pageId, isForNewComment, replyTo,
+    pageId, isForNewComment, replyTo, revisionId,
     currentCommentId, commentBody, onCancelButtonClicked, onCommentButtonClicked,
   } = props;
 
   const { data: currentUser } = useCurrentUser();
   const { data: currentPagePath } = useCurrentPagePath();
   const { update: updateComment, post: postComment } = useSWRxPageComment(pageId);
-  const { data: revisionId } = useRevisionId();
   const { data: isSlackEnabled, mutate: mutateIsSlackEnabled } = useIsSlackEnabled();
   const { data: slackChannelsData } = useSWRxSlackChannels(currentPagePath);
   const { data: isSlackConfigured } = useIsSlackConfigured();

+ 1 - 1
packages/app/src/components/PageEditor/Editor.tsx

@@ -229,7 +229,7 @@ const Editor: ForwardRefRenderFunction<IEditorMethods, EditorPropsType> = (props
 
   const renderNavbar = useCallback(() => {
     return (
-      <div className="m-0 navbar navbar-default navbar-editor" data-testId="navbar-editor" style={{ minHeight: 'unset' }}>
+      <div className="m-0 navbar navbar-default navbar-editor" data-testid="navbar-editor" style={{ minHeight: 'unset' }}>
         <ul className="pl-2 nav nav-navbar">
           { (editorSubstance()?.getNavbarItems() ?? []).map((item, idx) => {
             // eslint-disable-next-line react/no-array-index-key

+ 8 - 2
packages/app/src/components/PageHistory.tsx

@@ -12,13 +12,13 @@ import { RevisionComparer } from './RevisionComparer/RevisionComparer';
 
 const logger = loggerFactory('growi:PageHistory');
 
-export const PageHistory = (): JSX.Element => {
+export const PageHistory: React.FC<{ onClose: () => void }> = ({ onClose }) => {
 
   const [activePage, setActivePage] = useState(1);
 
   const { data: currentPageId } = useCurrentPageId();
 
-  const { data: revisionsData } = useSWRxPageRevisions(activePage, 10, currentPageId);
+  const { data: revisionsData, mutate: mutatePageRevisions } = useSWRxPageRevisions(activePage, 10, currentPageId);
 
   const [sourceRevision, setSourceRevision] = useState<IRevisionHasPageId>();
   const [targetRevision, setTargetRevision] = useState<IRevisionHasPageId>();
@@ -30,6 +30,10 @@ export const PageHistory = (): JSX.Element => {
     }
   }, [revisionsData]);
 
+  useEffect(() => {
+    mutatePageRevisions();
+  });
+
   const pagingLimit = 10;
 
   if (revisionsData == null || sourceRevision == null || targetRevision == null || currentPageId == null) {
@@ -61,6 +65,7 @@ export const PageHistory = (): JSX.Element => {
         targetRevision={targetRevision}
         onChangeSourceInvoked={setSourceRevision}
         onChangeTargetInvoked={setTargetRevision}
+        onClose={onClose}
       />
       <div className="my-3">
         {pager()}
@@ -69,6 +74,7 @@ export const PageHistory = (): JSX.Element => {
         sourceRevision={sourceRevision}
         targetRevision={targetRevision}
         currentPageId={currentPageId}
+        onClose={onClose}
       />
     </div>
   );

+ 6 - 4
packages/app/src/components/PageHistory/PageRevisionTable.tsx

@@ -14,13 +14,14 @@ type PageRevisionTAble = {
   targetRevision: IRevisionHasId,
   onChangeSourceInvoked: React.Dispatch<React.SetStateAction<IRevisionHasId | undefined>>,
   onChangeTargetInvoked: React.Dispatch<React.SetStateAction<IRevisionHasId | undefined>>,
+  onClose: () => void,
 }
 
 export const PageRevisionTable = (props: PageRevisionTAble): JSX.Element => {
   const { t } = useTranslation();
 
   const {
-    revisions, pagingLimit, sourceRevision, targetRevision, onChangeSourceInvoked, onChangeTargetInvoked,
+    revisions, pagingLimit, sourceRevision, targetRevision, onChangeSourceInvoked, onChangeTargetInvoked, onClose,
   } = props;
 
   const revisionCount = revisions.length;
@@ -51,6 +52,7 @@ export const PageRevisionTable = (props: PageRevisionTAble): JSX.Element => {
               isLatestRevision={revision === latestRevision}
               hasDiff={hasDiff}
               key={`revision-history-rev-${revisionId}`}
+              onClose={onClose}
             />
             {hasDiff && (
               <div className="ml-md-3 mt-auto">
@@ -132,9 +134,9 @@ export const PageRevisionTable = (props: PageRevisionTAble): JSX.Element => {
     <table className={`${styles['revision-history-table']} table revision-history-table`}>
       <thead>
         <tr className="d-flex">
-          <th className="col">{ t('page_history.revision') }</th>
-          <th className="col-1">{ t('page_history.comparing_source') }</th>
-          <th className="col-2">{ t('page_history.comparing_target') }</th>
+          <th className="col">{t('page_history.revision')}</th>
+          <th className="col-1">{t('page_history.comparing_source')}</th>
+          <th className="col-2">{t('page_history.comparing_target')}</th>
         </tr>
       </thead>
       <tbody className="overflow-auto d-block">

+ 11 - 5
packages/app/src/components/PageHistory/Revision.tsx

@@ -3,6 +3,7 @@ import React from 'react';
 import { IRevisionHasId } from '@growi/core';
 import { UserPicture } from '@growi/ui';
 import { useTranslation } from 'next-i18next';
+import Link from 'next/link';
 
 import UserDate from '../User/UserDate';
 import { Username } from '../User/Username';
@@ -13,12 +14,15 @@ type RevisionProps = {
   revision: IRevisionHasId,
   isLatestRevision: boolean,
   hasDiff: boolean,
+  onClose: () => void,
 }
 
 export const Revision = (props: RevisionProps): JSX.Element => {
   const { t } = useTranslation();
 
-  const { revision, isLatestRevision, hasDiff } = props;
+  const {
+    revision, isLatestRevision, hasDiff, onClose,
+  } = props;
 
   const renderSimplifiedNodiff = (revision: IRevisionHasId) => {
 
@@ -34,7 +38,7 @@ export const Revision = (props: RevisionProps): JSX.Element => {
         </div>
         <div className="ml-3">
           <span className="text-muted small">
-            <UserDate dateTime={revision.createdAt} /> ({ t('No diff') })
+            <UserDate dateTime={revision.createdAt} /> {t('No diff')}
           </span>
         </div>
       </div>
@@ -60,9 +64,11 @@ export const Revision = (props: RevisionProps): JSX.Element => {
           <div className="mb-1">
             <UserDate dateTime={revision.createdAt} />
             <br className="d-xl-none d-block" />
-            <a className="ml-xl-3" href={`?revisionId=${revision._id}`}>
-              <i className="icon-login"></i> { t('Go to this version') }
-            </a>
+            <Link href={`?revisionId=${revision._id}`} prefetch={false}>
+              <a className="ml-xl-3" onClick={onClose}>
+                <i className="icon-login"></i> {t('Go to this version')}
+              </a>
+            </Link>
           </div>
         </div>
       </div>

+ 15 - 8
packages/app/src/components/PageHistory/RevisionDiff.tsx

@@ -4,6 +4,7 @@ import { IRevisionHasPageId } from '@growi/core';
 import { createPatch } from 'diff';
 import { html, Diff2HtmlConfig } from 'diff2html';
 import { useTranslation } from 'next-i18next';
+import Link from 'next/link';
 
 import UserDate from '../User/UserDate';
 
@@ -15,12 +16,15 @@ type RevisioinDiffProps = {
   currentRevision: IRevisionHasPageId,
   previousRevision: IRevisionHasPageId,
   revisionDiffOpened: boolean,
+  onClose: () => void,
 }
 
 export const RevisionDiff = (props: RevisioinDiffProps): JSX.Element => {
   const { t } = useTranslation();
 
-  const { currentRevision, previousRevision, revisionDiffOpened } = props;
+  const {
+    currentRevision, previousRevision, revisionDiffOpened, onClose,
+  } = props;
 
   const previousText = (currentRevision._id === previousRevision._id) ? '' : previousRevision.body;
 
@@ -46,16 +50,19 @@ export const RevisionDiff = (props: RevisioinDiffProps): JSX.Element => {
           <div className="row">
             <div className="col comparison-source-wrapper pt-1 px-0">
               <span className="comparison-source pr-3">{t('page_history.comparing_source')}</span><UserDate dateTime={previousRevision.createdAt} />
-              <a href={`?revisionId=${previousRevision._id}`} className="ml-3">
-                <i className="icon-login"></i>
-              </a>
-
+              <Link href={`?revisionId=${previousRevision._id}`}>
+                <a className="ml-3" onClick={onClose}>
+                  <i className="icon-login"></i>
+                </a>
+              </Link>
             </div>
             <div className="col comparison-target-wrapper pt-1">
               <span className="comparison-target pr-3">{t('page_history.comparing_target')}</span><UserDate dateTime={currentRevision.createdAt} />
-              <a href={`?revisionId=${currentRevision._id}`} className="ml-3">
-                <i className="icon-login"></i>
-              </a>
+              <Link href={`?revisionId=${currentRevision._id}`}>
+                <a className="ml-3" onClick={onClose}>
+                  <i className="icon-login"></i>
+                </a>
+              </Link>
             </div>
           </div>
         </div>

+ 3 - 1
packages/app/src/components/RevisionComparer/RevisionComparer.tsx

@@ -26,13 +26,14 @@ type RevisionComparerProps = {
   sourceRevision: IRevisionHasPageId
   targetRevision: IRevisionHasPageId
   currentPageId?: string
+  onClose: () => void
 }
 
 export const RevisionComparer = (props: RevisionComparerProps): JSX.Element => {
   const { t } = useTranslation(['translation', 'commons']);
 
   const {
-    sourceRevision, targetRevision, currentPageId,
+    sourceRevision, targetRevision, currentPageId, onClose,
   } = props;
 
   const { data: currentPagePath } = useCurrentPagePath();
@@ -104,6 +105,7 @@ export const RevisionComparer = (props: RevisionComparerProps): JSX.Element => {
               revisionDiffOpened
               previousRevision={sourceRevision}
               currentRevision={targetRevision}
+              onClose={onClose}
             />
           )
         }

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

@@ -57,7 +57,7 @@ import DisplaySwitcher from '../components/Page/DisplaySwitcher';
 // import PageStatusAlert from '../client/js/components/PageStatusAlert';
 import {
   useCurrentUser,
-  useIsLatestRevision,
+  useIsLatestRevision, useCurrentRevisionId,
   useIsForbidden, useIsNotFound, useIsSharedUser,
   useIsEnabledStaleNotification, useIsIdenticalPath,
   useIsSearchServiceConfigured, useIsSearchServiceReachable, useDisableLinkSharing,
@@ -135,7 +135,8 @@ type Props = CommonProps & {
   redirectFrom?: string;
 
   // shareLinkId?: string;
-  isLatestRevision?: boolean
+  isLatestRevision?: boolean,
+  currentRevisionId?: string,
 
   isIdenticalPathPage?: boolean,
   isForbidden: boolean,
@@ -242,6 +243,7 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
   useCurrentPageId(pageId ?? null);
   // useIsNotCreatable(props.isForbidden || !isCreatablePage(pagePath)); // TODO: need to include props.isIdentical
   useCurrentPathname(props.currentPathname);
+  useCurrentRevisionId(props.currentRevisionId);
 
   const { data: currentPage } = useSWRxCurrentPage(undefined, pageWithMeta?.data ?? null); // store initial data
   useEditingMarkdown(pageWithMeta?.data.revision?.body ?? '');
@@ -415,6 +417,10 @@ async function injectPageData(context: GetServerSidePropsContext, props: Props):
     props.isLatestRevision = page.isLatestRevision();
   }
 
+  if (typeof revisionId === 'string' || typeof revisionId === 'undefined') {
+    props.currentRevisionId = props.isLatestRevision && page.latestRevision != null ? page.latestRevision.toString() : revisionId;
+  }
+
   props.pageWithMeta = pageWithMeta;
 }
 

+ 4 - 2
packages/app/src/server/routes/apiv3/page.js

@@ -255,7 +255,9 @@ module.exports = (crowi) => {
    */
   router.get('/', certifySharedPage, accessTokenParser, loginRequired, validator.getPage, apiV3FormValidator, async(req, res) => {
     const { user } = req;
-    const { pageId, path, findAll } = req.query;
+    const {
+      pageId, path, findAll, revisionId,
+    } = req.query;
 
     if (pageId == null && path == null) {
       return res.apiv3Err(new ErrorV3('Either parameter of path or pageId is required.', 'invalid-request'));
@@ -285,7 +287,7 @@ module.exports = (crowi) => {
 
     if (page != null) {
       try {
-        page.initLatestRevisionField();
+        page.initLatestRevisionField(revisionId);
 
         // populate
         page = await page.populateDataToShowRevision();

+ 2 - 2
packages/app/src/stores/context.tsx

@@ -48,8 +48,8 @@ export const useCurrentUser = (initialData?: Nullable<IUser>): SWRResponse<Nulla
   return useContextSWR<Nullable<IUser>, Error>('currentUser', initialData);
 };
 
-export const useRevisionId = (initialData?: Nullable<any>): SWRResponse<Nullable<any>, Error> => {
-  return useContextSWR<Nullable<any>, Error>('revisionId', initialData);
+export const useCurrentRevisionId = (initialData?: string): SWRResponse<string, Error> => {
+  return useContextSWR('currentRevisionId', initialData);
 };
 
 export const useCurrentPathname = (initialData?: string): SWRResponse<string, Error> => {

+ 7 - 5
packages/app/src/stores/page.tsx

@@ -15,8 +15,8 @@ import { IRevisionsForPagination } from '~/interfaces/revision';
 
 import { IPageTagsInfo } from '../interfaces/tag';
 
-import { useCurrentPageId, useCurrentPathname } from './context';
-
+import { useCurrentPageId, useCurrentPathname, useCurrentRevisionId } from './context';
+import { useStaticSWR } from './use-static-swr';
 
 const { isPermalink: _isPermalink } = pagePathUtils;
 
@@ -24,11 +24,12 @@ const { isPermalink: _isPermalink } = pagePathUtils;
 export const useSWRxPage = (
     pageId?: string|null,
     shareLinkId?: string,
+    revisionId?: string,
     initialData?: IPagePopulatedToShowRevision|null,
 ): SWRResponse<IPagePopulatedToShowRevision|null, Error> => {
   return useSWR<IPagePopulatedToShowRevision|null, Error>(
-    pageId != null ? ['/page', pageId, shareLinkId] : null,
-    (endpoint, pageId, shareLinkId) => apiv3Get<{ page: IPagePopulatedToShowRevision }>(endpoint, { pageId, shareLinkId })
+    pageId != null ? ['/page', pageId, shareLinkId, revisionId] : null,
+    (endpoint, pageId, shareLinkId, revisionId) => apiv3Get<{ page: IPagePopulatedToShowRevision }>(endpoint, { pageId, shareLinkId, revisionId })
       .then(result => result.data.page)
       .catch((errs) => {
         if (!Array.isArray(errs)) { throw Error('error is not array') }
@@ -54,8 +55,9 @@ export const useSWRxCurrentPage = (
     shareLinkId?: string, initialData?: IPagePopulatedToShowRevision|null,
 ): SWRResponse<IPagePopulatedToShowRevision|null, Error> => {
   const { data: currentPageId } = useCurrentPageId();
+  const { data: currentRevisionId } = useCurrentRevisionId();
 
-  const swrResult = useSWRxPage(currentPageId, shareLinkId, initialData);
+  const swrResult = useSWRxPage(currentPageId, shareLinkId, currentRevisionId, initialData);
 
   return swrResult;
 };