Przeglądaj źródła

activate NotFoundPage

Yuki Takei 3 lat temu
rodzic
commit
728210c680

+ 6 - 28
packages/app/src/components/NotFoundPage.tsx

@@ -1,40 +1,17 @@
-import React, { useMemo, useEffect } from 'react';
+import React, { useMemo } from 'react';
 
 import { useTranslation } from 'next-i18next';
-import urljoin from 'url-join';
+import dynamic from 'next/dynamic';
 
-import { useIsEmptyPage, useNotFoundTargetPathOrId, useCurrentPathname } from '~/stores/context';
-
-import CustomNavAndContents from './CustomNavigation/CustomNavAndContents';
 import { DescendantsPageListForCurrentPath } from './DescendantsPageList';
 import PageListIcon from './Icons/PageListIcon';
 import TimeLineIcon from './Icons/TimeLineIcon';
-import PageTimeline from './PageTimeline';
-
-/**
- * Replace url in address bar with new path and query parameters
- */
-const replaceURLHistory = (path: string) => {
-  const queryParameters = window.location.search;
-  window.history.replaceState(null, '', urljoin(path, queryParameters));
-};
+// import PageTimeline from './PageTimeline';
 
 const NotFoundPage = (): JSX.Element => {
   const { t } = useTranslation();
-  const { data: isEmptyPage } = useIsEmptyPage();
-  const { data: path } = useCurrentPathname();
-  const { data: notFoundTargetPathOrId } = useNotFoundTargetPathOrId();
 
-  // replace url in address bar with path when accessing empty page by permalink
-  useEffect(() => {
-    if (path == null) {
-      return;
-    }
-    const isPermalink = !notFoundTargetPathOrId?.includes('/');
-    if (isEmptyPage && isPermalink) {
-      replaceURLHistory(path);
-    }
-  }, [path, isEmptyPage, notFoundTargetPathOrId]);
+  const CustomNavAndContents = dynamic(import('./CustomNavigation/CustomNavAndContents'), { ssr: false });
 
   const navTabMapping = useMemo(() => {
     return {
@@ -46,7 +23,8 @@ const NotFoundPage = (): JSX.Element => {
       },
       timeLine: {
         Icon: TimeLineIcon,
-        Content: PageTimeline,
+        // Content: PageTimeline,
+        Content: () => <></>,
         i18n: t('Timeline View'),
         index: 1,
       },

+ 24 - 18
packages/app/src/components/Page/DisplaySwitcher.tsx

@@ -2,12 +2,13 @@ import React, { useMemo } from 'react';
 
 import { pagePathUtils } from '@growi/core';
 import { useTranslation } from 'next-i18next';
+import dynamic from 'next/dynamic';
 import { TabContent, TabPane } from 'reactstrap';
 
-
 import { smoothScrollIntoView } from '~/client/util/smooth-scroll';
+import { isPopulated } from '~/interfaces/common';
 import {
-  useCurrentPagePath, useIsSharedUser, useIsEditable, useCurrentPageId, useIsUserPage, usePageUser, useShareLinkId, useIsEmptyPage,
+  useCurrentPagePath, useIsSharedUser, useIsEditable, useIsUserPage, usePageUser, useShareLinkId, useIsNotFound,
 } from '~/stores/context';
 import { useDescendantsPageListModal } from '~/stores/modal';
 import { useSWRxCurrentPage } from '~/stores/page';
@@ -15,12 +16,11 @@ import { EditorMode, useEditorMode } from '~/stores/ui';
 
 import CountBadge from '../Common/CountBadge';
 import ContentLinkButtons from '../ContentLinkButtons';
-import HashChanged from '../EventListeneres/HashChanged';
 import PageListIcon from '../Icons/PageListIcon';
-import Page from '../Page';
-import PageEditor from '../PageEditor';
-import EditorNavbarBottom from '../PageEditor/EditorNavbarBottom';
-import PageEditorByHackmd from '../PageEditorByHackmd';
+import NotFoundPage from '../NotFoundPage';
+// import Page from '../Page';
+// import PageEditor from '../PageEditor';
+// import PageEditorByHackmd from '../PageEditorByHackmd';
 import TableOfContents from '../TableOfContents';
 import UserInfo from '../User/UserInfo';
 
@@ -33,34 +33,37 @@ const { isTopPage } = pagePathUtils;
 const DisplaySwitcher = (): JSX.Element => {
   const { t } = useTranslation();
 
+  const EditorNavbarBottom = dynamic(import('../PageEditor/EditorNavbarBottom'), { ssr: false });
+  const HashChanged = dynamic(import('../EventListeneres/HashChanged'), { ssr: false });
+
   // get element for smoothScroll
-  const getCommentListDom = useMemo(() => { return document.getElementById('page-comments-list') }, []);
+  // const getCommentListDom = useMemo(() => { return document.getElementById('page-comments-list') }, []);
 
-  const { data: isEmptyPage } = useIsEmptyPage();
-  const { data: currentPageId } = useCurrentPageId();
   const { data: currentPagePath } = useCurrentPagePath();
   const { data: isSharedUser } = useIsSharedUser();
   const { data: shareLinkId } = useShareLinkId();
   const { data: isUserPage } = useIsUserPage();
   const { data: isEditable } = useIsEditable();
   const { data: pageUser } = usePageUser();
+  const { data: isNotFound } = useIsNotFound();
   const { data: currentPage } = useSWRxCurrentPage(shareLinkId ?? undefined);
 
   const { data: editorMode } = useEditorMode();
 
   const { open: openDescendantPageListModal } = useDescendantsPageListModal();
 
-  const isPageExist = currentPageId != null;
   const isViewMode = editorMode === EditorMode.View;
   const isTopPagePath = isTopPage(currentPagePath ?? '');
 
+  const revision = currentPage?.revision;
+
   return (
     <>
       <TabContent activeTab={editorMode}>
         <TabPane tabId={EditorMode.View}>
           <div className="d-flex flex-column flex-lg-row-reverse">
 
-            { isPageExist && !isEmptyPage && (
+            { !isNotFound && !currentPage?.isEmpty && (
               <div className="grw-side-contents-container">
                 <div className="grw-side-contents-sticky-container">
 
@@ -82,12 +85,13 @@ const DisplaySwitcher = (): JSX.Element => {
                   </div>
 
                   {/* Comments */}
-                  { getCommentListDom != null && !isTopPagePath && (
+                  {/* { getCommentListDom != null && !isTopPagePath && ( */}
+                  { !isTopPagePath && (
                     <div className="grw-page-accessories-control mt-2">
                       <button
                         type="button"
                         className="btn btn-block btn-outline-secondary grw-btn-page-accessories rounded-pill d-flex justify-content-between align-items-center"
-                        onClick={() => smoothScrollIntoView(getCommentListDom, WIKI_HEADER_LINK)}
+                        // onClick={() => smoothScrollIntoView(getCommentListDom, WIKI_HEADER_LINK)}
                       >
                         <i className="icon-fw icon-bubbles grw-page-accessories-control-icon"></i>
                         <span>Comments</span>
@@ -98,7 +102,7 @@ const DisplaySwitcher = (): JSX.Element => {
 
                   <div className="d-none d-lg-block">
                     <div id="revision-toc" className="revision-toc">
-                      <TableOfContents />
+                      {/* <TableOfContents /> */}
                     </div>
                     <ContentLinkButtons />
                   </div>
@@ -109,7 +113,9 @@ const DisplaySwitcher = (): JSX.Element => {
 
             <div className="flex-grow-1 flex-basis-0 mw-0">
               { isUserPage && <UserInfo pageUser={pageUser} />}
-              <Page />
+              {/* { !isNotFound && <Page /> } */}
+              { !isNotFound && revision != null && isPopulated(revision) && revision.body }
+              { isNotFound && <NotFoundPage /> }
             </div>
 
           </div>
@@ -117,14 +123,14 @@ const DisplaySwitcher = (): JSX.Element => {
         { isEditable && (
           <TabPane tabId={EditorMode.Editor}>
             <div data-testid="page-editor" id="page-editor">
-              <PageEditor />
+              {/* <PageEditor /> */}
             </div>
           </TabPane>
         ) }
         { isEditable && (
           <TabPane tabId={EditorMode.HackMD}>
             <div id="page-editor-with-hackmd">
-              <PageEditorByHackmd />
+              {/* <PageEditorByHackmd /> */}
             </div>
           </TabPane>
         ) }

+ 4 - 5
packages/app/src/components/PageEditor/EditorNavbarBottom.tsx

@@ -4,9 +4,8 @@ import PropTypes from 'prop-types';
 import { Collapse, Button } from 'reactstrap';
 
 
-import AppContainer from '~/client/services/AppContainer';
 import EditorContainer from '~/client/services/EditorContainer';
-import { useCurrentPagePath } from '~/stores/context';
+import { useCurrentPagePath, useIsSlackConfigured } from '~/stores/context';
 import { useSWRxSlackChannels, useIsSlackEnabled } from '~/stores/editor';
 import {
   EditorMode, useDrawerOpened, useEditorMode, useIsDeviceSmallerThanMd,
@@ -27,13 +26,14 @@ const EditorNavbarBottom = (props) => {
   const [isExpanded, setExpanded] = useState(false);
 
   const [isSlackExpanded, setSlackExpanded] = useState(false);
-  const isSlackConfigured = props.appContainer.getConfig().isSlackConfigured;
 
+  const { data: isSlackConfigured } = useIsSlackConfigured();
   const { mutate: mutateDrawerOpened } = useDrawerOpened();
   const { data: isDeviceSmallerThanMd } = useIsDeviceSmallerThanMd();
   const additionalClasses = ['grw-editor-navbar-bottom'];
   const { data: currentPagePath } = useCurrentPagePath();
   const { data: slackChannelsData } = useSWRxSlackChannels(currentPagePath);
+
   const { data: isSlackEnabled, mutate: mutateIsSlackEnabled } = useIsSlackEnabled();
 
   const [slackChannelsStr, setSlackChannelsStr] = useState<string>('');
@@ -153,8 +153,7 @@ const EditorNavbarBottom = (props) => {
 };
 
 EditorNavbarBottom.propTypes = {
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   editorContainer: PropTypes.instanceOf(EditorContainer).isRequired,
 };
 
-export default withUnstatedContainers(EditorNavbarBottom, [EditorContainer, AppContainer]);
+export default withUnstatedContainers(EditorNavbarBottom, [EditorContainer]);

+ 4 - 20
packages/app/src/components/PageEditor/OptionsSelector.tsx

@@ -7,12 +7,10 @@ import {
   Dropdown, DropdownToggle, DropdownMenu, DropdownItem,
 } from 'reactstrap';
 
-import AppContainer from '~/client/services/AppContainer';
 import { useIsIndentSizeForced } from '~/stores/context';
 import { useEditorSettings, useIsTextlintEnabled, useCurrentIndentSize } from '~/stores/editor';
 
 import { DEFAULT_THEME, KeyMapMode } from '../../interfaces/editor-settings';
-import { withUnstatedContainers } from '../UnstatedUtils';
 
 import { DownloadDictModal } from './DownloadDictModal';
 
@@ -165,11 +163,10 @@ IndentSizeSelector.displayName = 'IndentSizeSelector';
 
 
 type ConfigurationDropdownProps = {
-  isMathJaxEnabled: boolean,
   onConfirmEnableTextlint?: () => void,
 }
 
-const ConfigurationDropdown = memo(({ isMathJaxEnabled, onConfirmEnableTextlint }: ConfigurationDropdownProps): JSX.Element => {
+const ConfigurationDropdown = memo(({ onConfirmEnableTextlint }: ConfigurationDropdownProps): JSX.Element => {
   const { t } = useTranslation();
 
   const [isCddMenuOpened, setCddMenuOpened] = useState(false);
@@ -207,10 +204,6 @@ const ConfigurationDropdown = memo(({ isMathJaxEnabled, onConfirmEnableTextlint
       return <></>;
     }
 
-    if (!isMathJaxEnabled) {
-      return <></>;
-    }
-
     const isActive = editorSettings.renderMathJaxInRealtime;
 
     const iconClasses = ['text-info'];
@@ -228,7 +221,7 @@ const ConfigurationDropdown = memo(({ isMathJaxEnabled, onConfirmEnableTextlint
         </div>
       </DropdownItem>
     );
-  }, [editorSettings, isMathJaxEnabled, update]);
+  }, [editorSettings, update]);
 
   const renderRealtimeDrawioMenuItem = useCallback(() => {
     if (editorSettings == null) {
@@ -347,14 +340,7 @@ const ConfigurationDropdown = memo(({ isMathJaxEnabled, onConfirmEnableTextlint
 ConfigurationDropdown.displayName = 'ConfigurationDropdown';
 
 
-type Props = {
-  appContainer: AppContainer
-};
-
-const OptionsSelector = (props: Props): JSX.Element => {
-  const { appContainer } = props;
-  const config = appContainer.config;
-
+const OptionsSelector = (): JSX.Element => {
   const [isDownloadDictModalShown, setDownloadDictModalShown] = useState(false);
 
   const { data: editorSettings, turnOffAskingBeforeDownloadLargeFiles } = useEditorSettings();
@@ -384,7 +370,6 @@ const OptionsSelector = (props: Props): JSX.Element => {
         </span>
         <span className="ml-2 ml-sm-4">
           <ConfigurationDropdown
-            isMathJaxEnabled={!!config.env.MATHJAX}
             onConfirmEnableTextlint={() => setDownloadDictModalShown(true)}
           />
         </span>
@@ -411,5 +396,4 @@ const OptionsSelector = (props: Props): JSX.Element => {
 };
 
 
-const OptionsSelectorWrapper = withUnstatedContainers(OptionsSelector, [AppContainer]);
-export default OptionsSelectorWrapper;
+export default OptionsSelector;

+ 8 - 11
packages/app/src/components/SavePageControls.jsx

@@ -7,14 +7,12 @@ import {
   DropdownToggle, DropdownMenu, DropdownItem,
 } from 'reactstrap';
 
-
-import AppContainer from '~/client/services/AppContainer';
 import EditorContainer from '~/client/services/EditorContainer';
 import PageContainer from '~/client/services/PageContainer';
 import { getOptionsToSave } from '~/client/util/editor';
 
 // TODO: remove this when omitting unstated is completed
-import { useIsEditable, useCurrentPageId } from '~/stores/context';
+import { useIsEditable, useCurrentPageId, useIsAclEnabled } from '~/stores/context';
 import { usePageTagsForEditors } from '~/stores/editor';
 import {
   useEditorMode, useSelectedGrant, useSelectedGrantGroupId, useSelectedGrantGroupName,
@@ -31,9 +29,6 @@ class SavePageControls extends React.Component {
   constructor(props) {
     super(props);
 
-    const config = this.props.appContainer.getConfig();
-    this.isAclEnabled = config.isAclEnabled;
-
     this.updateGrantHandler = this.updateGrantHandler.bind(this);
 
     this.save = this.save.bind(this);
@@ -92,7 +87,7 @@ class SavePageControls extends React.Component {
   render() {
 
     const {
-      t, pageContainer, grant, grantGroupId, grantGroupName,
+      t, pageContainer, isAclEnabled, grant, grantGroupId, grantGroupName,
     } = this.props;
 
     const isRootPage = pageContainer.state.path === '/';
@@ -102,7 +97,7 @@ class SavePageControls extends React.Component {
     return (
       <div className="d-flex align-items-center form-inline flex-nowrap">
 
-        {this.isAclEnabled
+        {isAclEnabled
           && (
             <div className="mr-2">
               <GrantSelector
@@ -135,12 +130,13 @@ class SavePageControls extends React.Component {
 /**
  * Wrapper component for using unstated
  */
-const SavePageControlsHOCWrapper = withUnstatedContainers(SavePageControls, [AppContainer, PageContainer, EditorContainer]);
+const SavePageControlsHOCWrapper = withUnstatedContainers(SavePageControls, [PageContainer, EditorContainer]);
 
 const SavePageControlsWrapper = (props) => {
   const { t } = useTranslation();
   const { data: isEditable } = useIsEditable();
   const { data: editorMode } = useEditorMode();
+  const { data: isAclEnabled } = useIsAclEnabled();
   const { data: grant, mutate: mutateGrant } = useSelectedGrant();
   const { data: grantGroupId, mutate: mutateGrantGroupId } = useSelectedGrantGroupId();
   const { data: grantGroupName, mutate: mutateGrantGroupName } = useSelectedGrantGroupName();
@@ -148,7 +144,7 @@ const SavePageControlsWrapper = (props) => {
   const { data: pageTags } = usePageTagsForEditors(pageId);
 
 
-  if (isEditable == null || editorMode == null) {
+  if (isEditable == null || editorMode == null || isAclEnabled == null) {
     return null;
   }
 
@@ -161,6 +157,7 @@ const SavePageControlsWrapper = (props) => {
       t={t}
       {...props}
       editorMode={editorMode}
+      isAclEnabled={isAclEnabled}
       grant={grant}
       grantGroupId={grantGroupId}
       grantGroupName={grantGroupName}
@@ -175,7 +172,6 @@ const SavePageControlsWrapper = (props) => {
 SavePageControls.propTypes = {
   t: PropTypes.func.isRequired, // i18next
 
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
   editorContainer: PropTypes.instanceOf(EditorContainer).isRequired,
 
@@ -184,6 +180,7 @@ SavePageControls.propTypes = {
   isSlackEnabled: PropTypes.bool.isRequired,
   slackChannels: PropTypes.string.isRequired,
   pageTags: PropTypes.arrayOf(PropTypes.string),
+  isAclEnabled: PropTypes.bool.isRequired,
   grant: PropTypes.number.isRequired,
   grantGroupId: PropTypes.string,
   grantGroupName: PropTypes.string,

+ 22 - 20
packages/app/src/pages/[[...path]].page.tsx

@@ -1,7 +1,6 @@
 import React, { useEffect } from 'react';
 
-import { isClient, pagePathUtils } from '@growi/core';
-import { isValidObjectId } from 'mongoose';
+import { isClient, pagePathUtils, pathUtils } from '@growi/core';
 import {
   NextPage, GetServerSideProps, GetServerSidePropsContext,
 } from 'next';
@@ -27,8 +26,8 @@ import loggerFactory from '~/utils/logger';
 
 // import GrowiSubNavigation from '../client/js/components/Navbar/GrowiSubNavigation';
 // import GrowiSubNavigationSwitcher from '../client/js/components/Navbar/GrowiSubNavigationSwitcher';
-// import DisplaySwitcher from '../client/js/components/Page/DisplaySwitcher';
 import { BasicLayout } from '../components/BasicLayout';
+import DisplaySwitcher from '../components/Page/DisplaySwitcher';
 
 // import { serializeUserSecurely } from '../server/models/serializers/user-serializer';
 // import PageStatusAlert from '../client/js/components/PageStatusAlert';
@@ -40,7 +39,7 @@ import {
   useIsForbidden, useIsNotFound, useIsTrashPage, useShared, useShareLinkId, useIsSharedUser, useIsAbleToDeleteCompletely,
   useAppTitle, useSiteUrl, useConfidential, useIsEnabledStaleNotification,
   useIsSearchServiceConfigured, useIsSearchServiceReachable, useIsMailerSetup,
-  useAclEnabled, useHasSlackConfig, useDrawioUri, useHackmdUri, useMathJax, useNoCdn, useEditorConfig, useCsrfToken, useIsSearchScopeChildrenAsDefault, useCurrentPageId, useCurrentPathname,
+  useIsAclEnabled, useHasSlackConfig, useDrawioUri, useHackmdUri, useMathJax, useNoCdn, useEditorConfig, useCsrfToken, useIsSearchScopeChildrenAsDefault, useCurrentPageId, useCurrentPathname, useIsSlackConfigured,
 } from '../stores/context';
 
 import { CommonProps, getServerSideCommonProps, useCustomTitle } from './commons';
@@ -48,7 +47,8 @@ import { CommonProps, getServerSideCommonProps, useCustomTitle } from './commons
 
 
 const logger = loggerFactory('growi:pages:all');
-const { isUsersHomePage, isTrashPage: _isTrashPage } = pagePathUtils;
+const { isPermalink: _isPermalink, isUsersHomePage, isTrashPage: _isTrashPage } = pagePathUtils;
+const { removeHeadingSlash } = pathUtils;
 
 type Props = CommonProps & {
   currentUser: string,
@@ -63,11 +63,14 @@ type Props = CommonProps & {
   isForbidden: boolean,
   isNotFound: boolean,
   // isAbleToDeleteCompletely: boolean,
+
   isSearchServiceConfigured: boolean,
   isSearchServiceReachable: boolean,
   isSearchScopeChildrenAsDefault: boolean,
+
+  isSlackConfigured: boolean,
   // isMailerSetup: boolean,
-  // isAclEnabled: boolean,
+  isAclEnabled: boolean,
   // hasSlackConfig: boolean,
   // drawioUri: string,
   // hackmdUri: string,
@@ -100,8 +103,8 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
   // page
   useCurrentPagePath(props.currentPathname);
   // useOwnerOfCurrentPage(props.pageUser != null ? JSON.parse(props.pageUser) : null);
-  // useIsForbidden(props.isForbidden);
-  // useNotFound(props.isNotFound);
+  useIsForbidden(props.isForbidden);
+  useIsNotFound(props.isNotFound);
   // useIsTrashPage(_isTrashPage(props.currentPagePath));
   // useShared();
   // useShareLinkId(props.shareLinkId);
@@ -112,8 +115,10 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
   useIsSearchServiceConfigured(props.isSearchServiceConfigured);
   useIsSearchServiceReachable(props.isSearchServiceReachable);
   useIsSearchScopeChildrenAsDefault(props.isSearchScopeChildrenAsDefault);
+
+  useIsSlackConfigured(props.isSlackConfigured);
   // useIsMailerSetup(props.isMailerSetup);
-  // useAclEnabled(props.isAclEnabled);
+  useIsAclEnabled(props.isAclEnabled);
   // useHasSlackConfig(props.hasSlackConfig);
   // useDrawioUri(props.drawioUri);
   // useHackmdUri(props.hackmdUri);
@@ -194,8 +199,7 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
               <div id="content-main" className="content-main grw-container-convertible">
                 {/* <PageAlerts /> */}
                 PageAlerts<br />
-                {/* <DisplaySwitcher /> */}
-                DisplaySwitcher<br />
+                <DisplaySwitcher />
                 <div id="page-editor-navbar-bottom-container" className="d-none d-edit-block"></div>
                 {/* <PageStatusAlert /> */}
                 PageStatusAlert
@@ -213,9 +217,6 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
         <footer>
           {/* <PageComments /> */}
           PageComments
-          <p>
-            { pageWithMeta != null && isPopulated(pageWithMeta.data.revision) && pageWithMeta.data.revision.body }
-          </p>
         </footer>
 
       </BasicLayout>
@@ -225,8 +226,7 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
 
 
 function getPageIdFromPathname(currentPathname: string): string | null {
-  const pageIdStr = currentPathname.substring(1);
-  return isValidObjectId(pageIdStr) ? pageIdStr : null;
+  return _isPermalink(currentPathname) ? removeHeadingSlash(currentPathname) : null;
 }
 
 async function getPageData(context: GetServerSidePropsContext, props: Props): Promise<IPageWithMeta|null> {
@@ -259,16 +259,16 @@ async function injectRoutingInformation(context: GetServerSidePropsContext, prop
 
   const { currentPathname } = props;
   const pageId = getPageIdFromPathname(currentPathname);
-  const isPermalink = pageId != null;
+  const isPermalink = _isPermalink(currentPathname);
 
   const page = pageWithMeta?.data;
 
   if (page == null) {
+    props.isNotFound = true;
+
     // check the page is forbidden or just does not exist.
     const count = isPermalink ? await Page.count({ _id: pageId }) : await Page.count({ path: currentPathname });
     props.isForbidden = count > 0;
-    props.isNotFound = true;
-    logger.warn(`Page is ${props.isForbidden ? 'forbidden' : 'not found'}`, currentPathname);
   }
 
   if (page != null) {
@@ -332,8 +332,10 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
   props.isSearchServiceConfigured = searchService.isConfigured;
   props.isSearchServiceReachable = searchService.isReachable;
   props.isSearchScopeChildrenAsDefault = configManager.getConfig('crowi', 'customize:isSearchScopeChildrenAsDefault');
+
+  props.isSlackConfigured = crowi.slackIntegrationService.isSlackConfigured;
   // props.isMailerSetup = mailService.isMailerSetup;
-  // props.isAclEnabled = aclService.isAclEnabled();
+  props.isAclEnabled = aclService.isAclEnabled();
   // props.hasSlackConfig = slackNotificationService.hasSlackConfig();
   // props.drawioUri = configManager.getConfig('crowi', 'app:drawioUri');
   // props.hackmdUri = configManager.getConfig('crowi', 'app:hackmdUri');

+ 8 - 7
packages/app/src/stores/context.tsx

@@ -88,6 +88,10 @@ export const useIsForbidden = (initialData?: boolean): SWRResponse<boolean, Erro
   return useStaticSWR<boolean, Error>('isForbidden', initialData, { fallbackData: false });
 };
 
+export const useIsNotFound = (initialData?: boolean): SWRResponse<boolean, Error> => {
+  return useStaticSWR<boolean, Error>('isNotFound', initialData, { fallbackData: false });
+};
+
 export const usePageUser = (initialData?: Nullable<any>): SWRResponse<Nullable<any>, Error> => {
   return useStaticSWR<Nullable<any>, Error>('pageUser', initialData);
 };
@@ -148,10 +152,6 @@ export const useTargetAndAncestors = (initialData?: TargetAndAncestors): SWRResp
   return useStaticSWR<TargetAndAncestors, Error>('targetAndAncestors', initialData);
 };
 
-export const useNotFoundTargetPathOrId = (initialData?: string): SWRResponse<string, Error> => {
-  return useStaticSWR<string, Error>('notFoundTargetPathOrId', initialData);
-};
-
 export const useIsAclEnabled = (initialData?: boolean) : SWRResponse<boolean, Error> => {
   return useStaticSWR<boolean, Error>('isAclEnabled', initialData);
 };
@@ -168,13 +168,14 @@ export const useIsSearchScopeChildrenAsDefault = (initialData?: boolean) : SWRRe
   return useStaticSWR<boolean, Error>('isSearchScopeChildrenAsDefault', initialData);
 };
 
+export const useIsSlackConfigured = (initialData?: boolean) : SWRResponse<boolean, Error> => {
+  return useStaticSWR<boolean, Error>('isSlackConfigured', initialData);
+};
+
 export const useIsEnabledAttachTitleHeader = (initialData?: boolean) : SWRResponse<boolean, Error> => {
   return useStaticSWR<boolean, Error>('isEnabledAttachTitleHeader', initialData);
 };
 
-export const useIsEmptyPage = (initialData?: boolean) : SWRResponse<boolean, Error> => {
-  return useStaticSWR<boolean, Error>('isEmptyPage', initialData);
-};
 export const useHasParent = (initialData?: boolean) : SWRResponse<boolean, Error> => {
   return useStaticSWR<boolean, Error>('hasParent', initialData);
 };

+ 9 - 7
packages/app/src/stores/ui.tsx

@@ -2,7 +2,7 @@ import { RefObject } from 'react';
 
 import { constants } from 'zlib';
 
-import { isClient, pagePathUtils } from '@growi/core';
+import { isClient, isServer, pagePathUtils } from '@growi/core';
 import { Breakpoint, addBreakpointListener } from '@growi/ui';
 import SimpleBar from 'simplebar-react';
 import {
@@ -21,7 +21,7 @@ import loggerFactory from '~/utils/logger';
 
 import {
   useCurrentPageId, useCurrentPagePath, useIsEditable, useIsTrashPage, useIsUserPage, useIsGuestUser, useEmptyPageId,
-  useIsNotCreatable, useIsSharedUser, useNotFoundTargetPathOrId, useIsForbidden, useIsIdenticalPath, useCurrentUser,
+  useIsNotCreatable, useIsSharedUser, useIsForbidden, useIsIdenticalPath, useCurrentUser, useIsNotFound,
 } from './context';
 import { localStorageMiddleware } from './middlewares/sync-to-storage';
 import { useStaticSWR } from './use-static-swr';
@@ -113,6 +113,10 @@ const updateHashByEditorMode = (newEditorMode: EditorMode) => {
 };
 
 export const determineEditorModeByHash = (): EditorMode => {
+  if (isServer()) {
+    return EditorMode.View;
+  }
+
   const { hash } = window.location;
 
   switch (hash) {
@@ -424,18 +428,16 @@ export const useIsAbleToShowTagLabel = (): SWRResponse<boolean, Error> => {
   const { data: isUserPage } = useIsUserPage();
   const { data: isSharedUser } = useIsSharedUser();
   const { data: isIdenticalPath } = useIsIdenticalPath();
-  const { data: notFoundTargetPathOrId } = useNotFoundTargetPathOrId();
+  const { data: isNotFound } = useIsNotFound();
   const { data: editorMode } = useEditorMode();
 
-  const includesUndefined = [isUserPage, isSharedUser, isIdenticalPath, notFoundTargetPathOrId, editorMode].some(v => v === undefined);
+  const includesUndefined = [isUserPage, isSharedUser, isIdenticalPath, isNotFound, editorMode].some(v => v === undefined);
 
   const isViewMode = editorMode === EditorMode.View;
-  const isNotFoundPage = notFoundTargetPathOrId != null;
 
   return useSWRImmutable(
     includesUndefined ? null : [key, editorMode],
-    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
-    () => !isUserPage && !isSharedUser && !isIdenticalPath && !(isViewMode && isNotFoundPage),
+    () => !isUserPage && !isSharedUser && !isIdenticalPath && !(isViewMode && isNotFound),
   );
 };