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

manage isPageCreateModalShown with SWR

Yuki Takei 4 лет назад
Родитель
Сommit
64285fcbd3

+ 0 - 16
packages/app/src/client/services/NavigationContainer.js

@@ -34,12 +34,8 @@ export default class NavigationContainer extends Container {
       sidebarContentsId: localStorage.sidebarContentsId || 'recent',
 
       isScrollTop: true,
-
-      isPageCreateModalShown: false,
     };
 
-    this.openPageCreateModal = this.openPageCreateModal.bind(this);
-    this.closePageCreateModal = this.closePageCreateModal.bind(this);
     this.setEditorMode = this.setEditorMode.bind(this);
     this.initDeviceSize();
     this.initScrollEvent();
@@ -195,18 +191,6 @@ export default class NavigationContainer extends Container {
     this.setState({ sidebarContentsId: contentsId });
   }
 
-  openPageCreateModal() {
-    if (this.appContainer.currentUser == null) {
-      logger.warn('Please login or signup to create a new page.');
-      return;
-    }
-    this.setState({ isPageCreateModalShown: true });
-  }
-
-  closePageCreateModal() {
-    this.setState({ isPageCreateModalShown: false });
-  }
-
   /**
    * Function that implements the click event for realizing smooth scroll
    * @param {array} elements

+ 5 - 1
packages/app/src/components/Fab.jsx

@@ -6,6 +6,8 @@ import loggerFactory from '~/utils/logger';
 
 import AppContainer from '~/client/services/AppContainer';
 import NavigationContainer from '~/client/services/NavigationContainer';
+import { usePageCreateModalOpened } from '~/stores/ui';
+
 import { withUnstatedContainers } from './UnstatedUtils';
 import CreatePageIcon from './Icons/CreatePageIcon';
 import ReturnTopIcon from './Icons/ReturnTopIcon';
@@ -16,6 +18,8 @@ const Fab = (props) => {
   const { navigationContainer, appContainer } = props;
   const { currentUser } = appContainer;
 
+  const { mutate: mutatePageCreateModalOpened } = usePageCreateModalOpened();
+
   const [animateClasses, setAnimateClasses] = useState('invisible');
   const [buttonClasses, setButtonClasses] = useState('');
 
@@ -52,7 +56,7 @@ const Fab = (props) => {
           <button
             type="button"
             className={`btn btn-lg btn-create-page btn-primary rounded-circle p-0 waves-effect waves-light ${buttonClasses}`}
-            onClick={navigationContainer.openPageCreateModal}
+            onClick={() => mutatePageCreateModalOpened(true)}
           >
             <CreatePageIcon />
           </button>

+ 9 - 11
packages/app/src/components/Hotkeys/Subscribers/CreatePage.jsx

@@ -1,31 +1,29 @@
 import React, { useEffect } from 'react';
 import PropTypes from 'prop-types';
 
-import NavigationContainer from '~/client/services/NavigationContainer';
-import { withUnstatedContainers } from '../../UnstatedUtils';
+import { usePageCreateModalOpened } from '~/stores/ui';
 
-const CreatePage = (props) => {
+const CreatePage = React.memo((props) => {
+
+  const { mutate } = usePageCreateModalOpened();
 
   // setup effect
   useEffect(() => {
-    props.navigationContainer.openPageCreateModal();
+    mutate(true);
 
     // remove this
     props.onDeleteRender(this);
-  }, [props]);
+  }, [mutate, props]);
 
   return <></>;
-};
+});
 
 CreatePage.propTypes = {
-  navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
   onDeleteRender: PropTypes.func.isRequired,
 };
 
-const CreatePageWrapper = withUnstatedContainers(CreatePage, [NavigationContainer]);
-
-CreatePageWrapper.getHotkeyStrokes = () => {
+CreatePage.getHotkeyStrokes = () => {
   return [['c']];
 };
 
-export default CreatePageWrapper;
+export default CreatePage;

+ 85 - 81
packages/app/src/components/Navbar/GrowiNavbar.jsx

@@ -1,103 +1,109 @@
-import React from 'react';
+import React, { useMemo } from 'react';
 import PropTypes from 'prop-types';
 
-import { withTranslation } from 'react-i18next';
+import { useTranslation } from 'react-i18next';
 
 import { UncontrolledTooltip } from 'reactstrap';
-import { withUnstatedContainers } from '../UnstatedUtils';
+
 import NavigationContainer from '~/client/services/NavigationContainer';
 import AppContainer from '~/client/services/AppContainer';
+import { usePageCreateModalOpened } from '~/stores/ui';
 
-
+import { withUnstatedContainers } from '../UnstatedUtils';
 import GrowiLogo from '../Icons/GrowiLogo';
 
 import PersonalDropdown from './PersonalDropdown';
 import GlobalSearch from './GlobalSearch';
 
-class GrowiNavbar extends React.Component {
-
-  renderNavbarRight() {
-    const { t, appContainer, navigationContainer } = this.props;
-    const { currentUser } = appContainer;
-
-    // render login button
-    if (currentUser == null) {
-      return <li id="login-user" className="nav-item"><a className="nav-link" href="/login">Login</a></li>;
-    }
-
-    return (
-      <>
-        <li className="nav-item d-none d-md-block">
-          <button className="px-md-2 nav-link btn-create-page border-0 bg-transparent" type="button" onClick={navigationContainer.openPageCreateModal}>
-            <i className="icon-pencil mr-2"></i>
-            <span className="d-none d-lg-block">{ t('New') }</span>
-          </button>
-        </li>
-
-        <li className="grw-personal-dropdown nav-item dropdown dropdown-toggle dropdown-toggle-no-caret">
-          <PersonalDropdown />
-        </li>
-      </>
-    );
+const NavbarRight = React.memo(({ currentUser }) => {
+  const { t } = useTranslation();
+  const { mutate: mutatePageCreateModalOpened } = usePageCreateModalOpened();
+
+  // render login button
+  if (currentUser == null) {
+    return <li id="login-user" className="nav-item"><a className="nav-link" href="/login">Login</a></li>;
   }
 
-  renderConfidential() {
-    const { appContainer } = this.props;
-    const { crowi } = appContainer.config;
-
-    return (
-      <li className="nav-item confidential text-light">
-        <i id="confidentialTooltip" className="icon-info d-md-none" />
-        <span className="d-none d-md-inline">
-          {crowi.confidential}
-        </span>
-        <UncontrolledTooltip
-          placement="bottom"
-          target="confidentialTooltip"
-          className="d-md-none"
+  return (
+    <>
+      <li className="nav-item d-none d-md-block">
+        <button
+          className="px-md-2 nav-link btn-create-page border-0 bg-transparent"
+          type="button"
+          onClick={() => mutatePageCreateModalOpened(true)}
         >
-          {crowi.confidential}
-        </UncontrolledTooltip>
+          <i className="icon-pencil mr-2"></i>
+          <span className="d-none d-lg-block">{ t('New') }</span>
+        </button>
       </li>
-    );
-  }
 
-  render() {
-    const { appContainer, navigationContainer } = this.props;
-    const { crowi, isSearchServiceConfigured } = appContainer.config;
-    const { isDeviceSmallerThanMd } = navigationContainer.state;
+      <li className="grw-personal-dropdown nav-item dropdown dropdown-toggle dropdown-toggle-no-caret">
+        <PersonalDropdown />
+      </li>
+    </>
+  );
+});
 
-    return (
-      <>
 
-        {/* Brand Logo  */}
-        <div className="navbar-brand mr-0">
-          <a className="grw-logo d-block" href="/">
-            <GrowiLogo />
-          </a>
-        </div>
+const Confidential = React.memo(({ confidential }) => {
+  if (confidential == null) {
+    return null;
+  }
 
-        <div className="grw-app-title d-none d-md-block">
-          {crowi.title}
+  return (
+    <li className="nav-item confidential text-light">
+      <i id="confidentialTooltip" className="icon-info d-md-none" />
+      <span className="d-none d-md-inline">
+        {confidential}
+      </span>
+      <UncontrolledTooltip
+        placement="bottom"
+        target="confidentialTooltip"
+        className="d-md-none"
+      >
+        {confidential}
+      </UncontrolledTooltip>
+    </li>
+  );
+});
+
+
+const GrowiNavbar = (props) => {
+
+  const { appContainer, navigationContainer } = props;
+  const { currentUser } = appContainer;
+  const { crowi, isSearchServiceConfigured } = appContainer.config;
+  const { isDeviceSmallerThanMd } = navigationContainer.state;
+
+  return (
+    <>
+      {/* Brand Logo  */}
+      <div className="navbar-brand mr-0">
+        <a className="grw-logo d-block" href="/">
+          <GrowiLogo />
+        </a>
+      </div>
+
+      <div className="grw-app-title d-none d-md-block">
+        {crowi.title}
+      </div>
+
+
+      {/* Navbar Right  */}
+      <ul className="navbar-nav ml-auto">
+        <NavbarRight currentUser={currentUser}></NavbarRight>
+        <Confidential confidential={crowi.confidential}></Confidential>
+      </ul>
+
+      { isSearchServiceConfigured && !isDeviceSmallerThanMd && (
+        <div className="grw-global-search grw-global-search-top position-absolute">
+          <GlobalSearch />
         </div>
+      ) }
+    </>
+  );
 
-
-        {/* Navbar Right  */}
-        <ul className="navbar-nav ml-auto">
-          {this.renderNavbarRight()}
-          {crowi.confidential != null && this.renderConfidential()}
-        </ul>
-
-        { isSearchServiceConfigured && !isDeviceSmallerThanMd && (
-          <div className="grw-global-search grw-global-search-top position-absolute">
-            <GlobalSearch />
-          </div>
-        ) }
-      </>
-    );
-  }
-
-}
+};
 
 /**
  * Wrapper component for using unstated
@@ -106,10 +112,8 @@ const GrowiNavbarWrapper = withUnstatedContainers(GrowiNavbar, [AppContainer, Na
 
 
 GrowiNavbar.propTypes = {
-  t: PropTypes.func.isRequired, //  i18next
-
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
   navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
 };
 
-export default withTranslation()(GrowiNavbarWrapper);
+export default GrowiNavbarWrapper;

+ 5 - 2
packages/app/src/components/Navbar/GrowiNavbarBottom.jsx

@@ -2,8 +2,9 @@ import React from 'react';
 import PropTypes from 'prop-types';
 
 import NavigationContainer from '~/client/services/NavigationContainer';
-import { withUnstatedContainers } from '../UnstatedUtils';
+import { usePageCreateModalOpened } from '~/stores/ui';
 
+import { withUnstatedContainers } from '../UnstatedUtils';
 import GlobalSearch from './GlobalSearch';
 
 const GrowiNavbarBottom = (props) => {
@@ -13,6 +14,8 @@ const GrowiNavbarBottom = (props) => {
   } = props;
   const { isDrawerOpened, isDeviceSmallerThanMd } = navigationContainer.state;
 
+  const { mutate: mutatePageCreateModalOpened } = usePageCreateModalOpened();
+
   const additionalClasses = ['grw-navbar-bottom'];
   if (isDrawerOpened) {
     additionalClasses.push('grw-navbar-bottom-drawer-opened');
@@ -55,7 +58,7 @@ const GrowiNavbarBottom = (props) => {
             <a
               role="button"
               className="nav-link btn-lg"
-              onClick={() => navigationContainer.openPageCreateModal()}
+              onClick={() => mutatePageCreateModalOpened(true)}
             >
               <i className="icon-pencil"></i>
             </a>

+ 8 - 7
packages/app/src/components/PageCreateModal.jsx

@@ -11,9 +11,9 @@ import { pagePathUtils, pathUtils } from '@growi/core';
 
 
 import AppContainer from '~/client/services/AppContainer';
-import NavigationContainer from '~/client/services/NavigationContainer';
 import { withUnstatedContainers } from './UnstatedUtils';
 import { toastError } from '~/client/util/apiNotification';
+import { usePageCreateModalOpened } from '~/stores/ui';
 
 import PagePathAutoComplete from './PagePathAutoComplete';
 
@@ -22,7 +22,9 @@ const {
 } = pagePathUtils;
 
 const PageCreateModal = (props) => {
-  const { t, appContainer, navigationContainer } = props;
+  const { t, appContainer } = props;
+
+  const { data: isPageCreateModalOpened, mutate: mutatePageCreateModalOpened } = usePageCreateModalOpened();
 
   const config = appContainer.getConfig();
   const isReachable = config.isSearchServiceReachable;
@@ -264,12 +266,12 @@ const PageCreateModal = (props) => {
   return (
     <Modal
       size="lg"
-      isOpen={navigationContainer.state.isPageCreateModalShown}
-      toggle={navigationContainer.closePageCreateModal}
+      isOpen={isPageCreateModalOpened}
+      toggle={() => mutatePageCreateModalOpened(false)}
       className="grw-create-page"
       autoFocus={false}
     >
-      <ModalHeader tag="h4" toggle={navigationContainer.closePageCreateModal} className="bg-primary text-light">
+      <ModalHeader tag="h4" toggle={() => mutatePageCreateModalOpened(false)} className="bg-primary text-light">
         {t('New Page')}
       </ModalHeader>
       <ModalBody>
@@ -286,13 +288,12 @@ const PageCreateModal = (props) => {
 /**
  * Wrapper component for using unstated
  */
-const ModalControlWrapper = withUnstatedContainers(PageCreateModal, [AppContainer, NavigationContainer]);
+const ModalControlWrapper = withUnstatedContainers(PageCreateModal, [AppContainer]);
 
 
 PageCreateModal.propTypes = {
   t: PropTypes.func.isRequired, //  i18next
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-  navigationContainer: PropTypes.instanceOf(NavigationContainer).isRequired,
 };
 
 export default withTranslation()(ModalControlWrapper);

+ 5 - 0
packages/app/src/stores/ui.tsx

@@ -212,3 +212,8 @@ export const useSidebarResizeDisabled = (isDisabled?: boolean): SWRResponse<bool
   const initialData = false;
   return useStaticSWR('isSidebarResizeDisabled', isDisabled || null, { fallbackData: initialData });
 };
+
+export const usePageCreateModalOpened = (isOpened?: boolean): SWRResponse<boolean, Error> => {
+  const initialData = false;
+  return useStaticSWR('isPageCreateModalOpened', isOpened || null, { fallbackData: initialData });
+};