Yuki Takei 3 лет назад
Родитель
Сommit
af9412d183

+ 0 - 58
packages/app/src/components/ShareLink/ShareLinkPageContents.tsx

@@ -1,58 +0,0 @@
-import React, { useEffect } from 'react';
-
-import type { IPagePopulatedToShowRevision } from '@growi/core';
-
-import { useViewOptions } from '~/stores/renderer';
-import { registerGrowiFacade } from '~/utils/growi-facade';
-import loggerFactory from '~/utils/logger';
-
-import RevisionRenderer from '../Page/RevisionRenderer';
-
-
-const logger = loggerFactory('growi:Page');
-
-
-export type ShareLinkPageContentsProps = {
-  page?: IPagePopulatedToShowRevision,
-}
-
-export const ShareLinkPageContents = (props: ShareLinkPageContentsProps): JSX.Element => {
-  const { page } = props;
-
-  const { data: rendererOptions, mutate: mutateRendererOptions } = useViewOptions();
-
-  // register to facade
-  useEffect(() => {
-    registerGrowiFacade({
-      markdownRenderer: {
-        optionsMutators: {
-          viewOptionsMutator: mutateRendererOptions,
-        },
-      },
-    });
-  }, [mutateRendererOptions]);
-
-
-  if (page == null || rendererOptions == null) {
-    const entries = Object.entries({
-      page, rendererOptions,
-    })
-      .map(([key, value]) => [key, value == null ? 'null' : undefined])
-      .filter(([, value]) => value != null);
-
-    logger.warn('Some of materials are missing.', Object.fromEntries(entries));
-
-    return <></>;
-  }
-
-  const { _id: revisionId, body: markdown } = page.revision;
-
-  return (
-    <>
-      { revisionId != null && (
-        <RevisionRenderer rendererOptions={rendererOptions} markdown={markdown} />
-      )}
-    </>
-  );
-
-};

+ 124 - 0
packages/app/src/components/ShareLink/ShareLinkPageView.tsx

@@ -0,0 +1,124 @@
+import React, { useEffect, useMemo } from 'react';
+
+import type { IPagePopulatedToShowRevision } from '@growi/core';
+import dynamic from 'next/dynamic';
+
+import type { RendererConfig } from '~/interfaces/services/renderer';
+import { IShareLinkHasId } from '~/interfaces/share-link';
+import { generateSSRViewOptions } from '~/services/renderer/renderer';
+import { useIsNotFound } from '~/stores/context';
+import { useViewOptions } from '~/stores/renderer';
+import { registerGrowiFacade } from '~/utils/growi-facade';
+import loggerFactory from '~/utils/logger';
+
+import { MainPane } from '../Layout/MainPane';
+import RevisionRenderer from '../Page/RevisionRenderer';
+import ShareLinkAlert from '../Page/ShareLinkAlert';
+import { PageSideContentsProps } from '../PageSideContents';
+
+
+const logger = loggerFactory('growi:Page');
+
+
+const PageSideContents = dynamic<PageSideContentsProps>(() => import('../PageSideContents').then(mod => mod.PageSideContents), { ssr: false });
+const ForbiddenPage = dynamic(() => import('../ForbiddenPage'), { ssr: false });
+
+
+type Props = {
+  pagePath: string,
+  rendererConfig: RendererConfig,
+  page?: IPagePopulatedToShowRevision,
+  shareLink?: IShareLinkHasId,
+  isExpired: boolean,
+  disableLinkSharing: boolean,
+}
+
+export const ShareLinkPageView = (props: Props): JSX.Element => {
+  const {
+    pagePath, rendererConfig,
+    page, shareLink,
+    isExpired, disableLinkSharing,
+  } = props;
+
+  const { data: isNotFoundMeta } = useIsNotFound();
+
+  const { data: viewOptions, mutate: mutateRendererOptions } = useViewOptions();
+
+  // register to facade
+  useEffect(() => {
+    registerGrowiFacade({
+      markdownRenderer: {
+        optionsMutators: {
+          viewOptionsMutator: mutateRendererOptions,
+        },
+      },
+    });
+  }, [mutateRendererOptions]);
+
+  const isNotFound = isNotFoundMeta || page == null || shareLink == null;
+
+  const specialContents = useMemo(() => {
+    if (disableLinkSharing) {
+      return <ForbiddenPage isLinkSharingDisabled={props.disableLinkSharing} />;
+    }
+  }, [disableLinkSharing, props.disableLinkSharing]);
+
+  const sideContents = !isNotFound
+    ? (
+      <PageSideContents page={page} />
+    )
+    : null;
+
+
+  const Contents = () => {
+    if (isNotFound) {
+      return <></>;
+    }
+
+    if (isExpired) {
+      return (
+        <>
+          <h2 className="text-muted mt-4">
+            <i className="icon-ban" aria-hidden="true" />
+            <span> Page is expired</span>
+          </h2>
+        </>
+      );
+    }
+
+    const rendererOptions = viewOptions ?? generateSSRViewOptions(rendererConfig, pagePath);
+    const markdown = page.revision.body;
+
+    return (
+      <>
+        <RevisionRenderer rendererOptions={rendererOptions} markdown={markdown} />
+      </>
+    );
+  };
+
+  return (
+    <MainPane
+      sideContents={sideContents}
+    >
+      { specialContents }
+      { specialContents == null && (
+        <>
+          { isNotFound && (
+            <h2 className="text-muted mt-4">
+              <i className="icon-ban" aria-hidden="true" />
+              <span> Page is not found</span>
+            </h2>
+          ) }
+          { !isNotFound && (
+            <>
+              <ShareLinkAlert expiredAt={shareLink.expiredAt} createdAt={shareLink.createdAt} />
+              <div className="mb-5">
+                <Contents />
+              </div>
+            </>
+          ) }
+        </>
+      ) }
+    </MainPane>
+  );
+};

+ 23 - 91
packages/app/src/pages/share/[[...path]].page.tsx

@@ -5,27 +5,21 @@ import {
   GetServerSideProps, GetServerSidePropsContext,
   GetServerSideProps, GetServerSidePropsContext,
 } from 'next';
 } from 'next';
 import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
 import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
-import dynamic from 'next/dynamic';
 import Head from 'next/head';
 import Head from 'next/head';
 import superjson from 'superjson';
 import superjson from 'superjson';
 
 
 import { useCurrentGrowiLayoutFluidClassName } from '~/client/services/layout';
 import { useCurrentGrowiLayoutFluidClassName } from '~/client/services/layout';
-import { MainPane } from '~/components/Layout/MainPane';
 import { ShareLinkLayout } from '~/components/Layout/ShareLinkLayout';
 import { ShareLinkLayout } from '~/components/Layout/ShareLinkLayout';
 import GrowiContextualSubNavigationSubstance from '~/components/Navbar/GrowiContextualSubNavigation';
 import GrowiContextualSubNavigationSubstance from '~/components/Navbar/GrowiContextualSubNavigation';
-import RevisionRenderer from '~/components/Page/RevisionRenderer';
-import ShareLinkAlert from '~/components/Page/ShareLinkAlert';
-import type { PageSideContentsProps } from '~/components/PageSideContents';
 import { DrawioViewerScript } from '~/components/Script/DrawioViewerScript';
 import { DrawioViewerScript } from '~/components/Script/DrawioViewerScript';
-import type { ShareLinkPageContentsProps } from '~/components/ShareLink/ShareLinkPageContents';
+import { ShareLinkPageView } from '~/components/ShareLink/ShareLinkPageView';
 import { SupportedAction, SupportedActionType } from '~/interfaces/activity';
 import { SupportedAction, SupportedActionType } from '~/interfaces/activity';
 import { CrowiRequest } from '~/interfaces/crowi-request';
 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 type { PageDocument } from '~/server/models/page';
 import type { PageDocument } from '~/server/models/page';
-import { generateSSRViewOptions } from '~/services/renderer/renderer';
 import {
 import {
-  useCurrentUser, useCurrentPageId, useRendererConfig, useIsSearchPage, useCurrentPathname,
+  useCurrentUser, useCurrentPageId, useRendererConfig, useIsSearchPage, useCurrentPathname, useIsNotFound,
   useShareLinkId, useIsSearchServiceConfigured, useIsSearchServiceReachable, useIsSearchScopeChildrenAsDefault, useDrawioUri, useIsContainerFluid,
   useShareLinkId, useIsSearchServiceConfigured, useIsSearchServiceReachable, useIsSearchScopeChildrenAsDefault, useDrawioUri, useIsContainerFluid,
 } from '~/stores/context';
 } from '~/stores/context';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
@@ -37,13 +31,10 @@ import {
 
 
 const logger = loggerFactory('growi:next-page:share');
 const logger = loggerFactory('growi:next-page:share');
 
 
-const PageSideContents = dynamic<PageSideContentsProps>(() => import('~/components/PageSideContents').then(mod => mod.PageSideContents), { ssr: false });
-// const Comments = dynamic(() => import('~/components/Comments').then(mod => mod.Comments), { ssr: false });
-const ForbiddenPage = dynamic(() => import('~/components/ForbiddenPage'), { ssr: false });
-
 type Props = CommonProps & {
 type Props = CommonProps & {
   shareLinkRelatedPage?: IShareLinkRelatedPage,
   shareLinkRelatedPage?: IShareLinkRelatedPage,
   shareLink?: IShareLinkHasId,
   shareLink?: IShareLinkHasId,
+  isNotFound: boolean,
   isExpired: boolean,
   isExpired: boolean,
   disableLinkSharing: boolean,
   disableLinkSharing: boolean,
   isSearchServiceConfigured: boolean,
   isSearchServiceConfigured: boolean,
@@ -73,16 +64,16 @@ superjson.registerCustom<IShareLinkRelatedPage, string>(
 // GrowiContextualSubNavigation for shared page
 // GrowiContextualSubNavigation for shared page
 // get page info from props not to send request 'GET /page' from client
 // get page info from props not to send request 'GET /page' from client
 type GrowiContextualSubNavigationForSharedPageProps = {
 type GrowiContextualSubNavigationForSharedPageProps = {
-  currentPage?: IPagePopulatedToShowRevision,
+  page?: IPagePopulatedToShowRevision,
   isLinkSharingDisabled: boolean,
   isLinkSharingDisabled: boolean,
 }
 }
 
 
 const GrowiContextualSubNavigationForSharedPage = (props: GrowiContextualSubNavigationForSharedPageProps): JSX.Element => {
 const GrowiContextualSubNavigationForSharedPage = (props: GrowiContextualSubNavigationForSharedPageProps): JSX.Element => {
-  const { currentPage, isLinkSharingDisabled } = props;
-  if (currentPage == null) { return <></> }
+  const { page, isLinkSharingDisabled } = props;
+
   return (
   return (
     <div data-testid="grw-contextual-sub-nav">
     <div data-testid="grw-contextual-sub-nav">
-      <GrowiContextualSubNavigationSubstance currentPage={currentPage} isLinkSharingDisabled={isLinkSharingDisabled}/>
+      <GrowiContextualSubNavigationSubstance currentPage={page} isLinkSharingDisabled={isLinkSharingDisabled}/>
     </div>
     </div>
   );
   );
 };
 };
@@ -90,6 +81,7 @@ const GrowiContextualSubNavigationForSharedPage = (props: GrowiContextualSubNavi
 const SharedPage: NextPageWithLayout<Props> = (props: Props) => {
 const SharedPage: NextPageWithLayout<Props> = (props: Props) => {
   useCurrentPathname(props.shareLink?.relatedPage.path);
   useCurrentPathname(props.shareLink?.relatedPage.path);
   useIsSearchPage(false);
   useIsSearchPage(false);
+  useIsNotFound(props.isNotFound);
   useShareLinkId(props.shareLink?._id);
   useShareLinkId(props.shareLink?._id);
   useCurrentPageId(props.shareLink?.relatedPage._id);
   useCurrentPageId(props.shareLink?.relatedPage._id);
   useCurrentUser(props.currentUser);
   useCurrentUser(props.currentUser);
@@ -103,43 +95,10 @@ const SharedPage: NextPageWithLayout<Props> = (props: Props) => {
 
 
   const growiLayoutFluidClass = useCurrentGrowiLayoutFluidClassName();
   const growiLayoutFluidClass = useCurrentGrowiLayoutFluidClassName();
 
 
-  const isNotFound = props.shareLink == null || props.shareLink.relatedPage == null || props.shareLink.relatedPage.isEmpty;
-  const isShowSharedPage = !props.disableLinkSharing && !isNotFound && !props.isExpired;
-  const shareLink = props.shareLink;
-
   const pagePath = props.shareLinkRelatedPage?.path ?? '';
   const pagePath = props.shareLinkRelatedPage?.path ?? '';
-  const revisionBody = props.shareLinkRelatedPage?.revision.body;
 
 
   const title = generateCustomTitleForPage(props, pagePath);
   const title = generateCustomTitleForPage(props, pagePath);
 
 
-  // TODO: show SSR body
-  // const rendererOptions = generateSSRViewOptions(props.rendererConfig, pagePath);
-  // const ssrBody = <RevisionRenderer rendererOptions={rendererOptions} markdown={revisionBody ?? ''} />;
-
-  const sideContents = shareLink != null
-    ? <PageSideContents page={shareLink.relatedPage} />
-    : <></>;
-
-  // const footerContents = shareLink != null && isPopulated(shareLink.relatedPage.revision)
-  //   ? (
-  //     <>
-  //       <Comments pageId={shareLink._id} pagePath={shareLink.relatedPage.path} revision={shareLink.relatedPage.revision} />
-  //     </>
-  //   )
-  //   : <></>;
-
-  const contents = (() => {
-    const ShareLinkPageContents = dynamic<ShareLinkPageContentsProps>(
-      () => import('~/components/ShareLink/ShareLinkPageContents').then(mod => mod.ShareLinkPageContents),
-      {
-        ssr: false,
-        // TODO: show SSR body
-        // loading: () => ssrBody,
-      },
-    );
-    return <ShareLinkPageContents page={props.shareLinkRelatedPage} />;
-  })();
-
   return (
   return (
     <>
     <>
       <Head>
       <Head>
@@ -148,50 +107,19 @@ const SharedPage: NextPageWithLayout<Props> = (props: Props) => {
 
 
       <div className={`dynamic-layout-root ${growiLayoutFluidClass} h-100 d-flex flex-column justify-content-between`}>
       <div className={`dynamic-layout-root ${growiLayoutFluidClass} h-100 d-flex flex-column justify-content-between`}>
         <header className="py-0 position-relative">
         <header className="py-0 position-relative">
-          {isShowSharedPage
-          && <GrowiContextualSubNavigationForSharedPage currentPage={props.shareLinkRelatedPage} isLinkSharingDisabled={props.disableLinkSharing} />}
+          <GrowiContextualSubNavigationForSharedPage page={props.shareLinkRelatedPage} isLinkSharingDisabled={props.disableLinkSharing} />
         </header>
         </header>
 
 
         <div id="grw-fav-sticky-trigger" className="sticky-top"></div>
         <div id="grw-fav-sticky-trigger" className="sticky-top"></div>
 
 
-        <MainPane
-          sideContents={sideContents}
-          // footerContents={footerContents}
-        >
-          { props.disableLinkSharing && (
-            <div className="mt-4">
-              <ForbiddenPage isLinkSharingDisabled={props.disableLinkSharing} />
-            </div>
-          )}
-
-          { (isNotFound && !props.disableLinkSharing) && (
-            <div className="container-lg">
-              <h2 className="text-muted mt-4">
-                <i className="icon-ban" aria-hidden="true" />
-                <span> Page is not found</span>
-              </h2>
-            </div>
-          )}
-
-          { (props.isExpired && !props.disableLinkSharing && shareLink != null) && (
-            <div className="container-lg">
-              <ShareLinkAlert expiredAt={shareLink.expiredAt} createdAt={shareLink.createdAt} />
-              <h2 className="text-muted mt-4">
-                <i className="icon-ban" aria-hidden="true" />
-                <span> Page is expired</span>
-              </h2>
-            </div>
-          )}
-
-          {(isShowSharedPage && shareLink != null) && (
-            <>
-              <ShareLinkAlert expiredAt={shareLink.expiredAt} createdAt={shareLink.createdAt} />
-              <div className="mb-5">
-                { contents }
-              </div>
-            </>
-          )}
-        </MainPane>
+        <ShareLinkPageView
+          pagePath={pagePath}
+          rendererConfig={props.rendererConfig}
+          page={props.shareLinkRelatedPage}
+          shareLink={props.shareLink}
+          isExpired={props.isExpired}
+          disableLinkSharing={props.disableLinkSharing}
+        />
 
 
       </div>
       </div>
     </>
     </>
@@ -210,7 +138,7 @@ SharedPage.getLayout = function getLayout(page) {
 function injectServerConfigurations(context: GetServerSidePropsContext, props: Props): void {
 function injectServerConfigurations(context: GetServerSidePropsContext, props: Props): void {
   const req: CrowiRequest = context.req as CrowiRequest;
   const req: CrowiRequest = context.req as CrowiRequest;
   const { crowi } = req;
   const { crowi } = req;
-  const { configManager, searchService, xssService } = crowi;
+  const { configManager, searchService } = crowi;
 
 
   props.disableLinkSharing = configManager.getConfig('crowi', 'security:disableLinkSharing');
   props.disableLinkSharing = configManager.getConfig('crowi', 'security:disableLinkSharing');
 
 
@@ -288,7 +216,11 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
   try {
   try {
     const ShareLinkModel = crowi.model('ShareLink');
     const ShareLinkModel = crowi.model('ShareLink');
     const shareLink = await ShareLinkModel.findOne({ _id: params.linkId }).populate('relatedPage');
     const shareLink = await ShareLinkModel.findOne({ _id: params.linkId }).populate('relatedPage');
-    if (shareLink != null) {
+    if (shareLink == null) {
+      props.isNotFound = true;
+    }
+    else {
+      props.isNotFound = false;
       props.shareLinkRelatedPage = await shareLink.relatedPage.populateDataToShowRevision();
       props.shareLinkRelatedPage = await shareLink.relatedPage.populateDataToShowRevision();
       props.isExpired = shareLink.isExpired();
       props.isExpired = shareLink.isExpired();
       props.shareLink = shareLink.toObject();
       props.shareLink = shareLink.toObject();