Explorar o código

omit page container from GrowiSubNavigation

yohei0125 %!s(int64=4) %!d(string=hai) anos
pai
achega
8ec32ecbb7

+ 8 - 13
packages/app/src/components/Navbar/GrowiSubNavigation.jsx

@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
 
 import { withUnstatedContainers } from '../UnstatedUtils';
 import AppContainer from '~/client/services/AppContainer';
-import PageContainer from '~/client/services/PageContainer';
 import EditorContainer from '~/client/services/EditorContainer';
 import {
   EditorMode, useDrawerMode, useEditorMode, useIsDeviceSmallerThanMd,
@@ -13,6 +12,7 @@ import {
   useIsAbleToDeleteCompletely, useCreator, useRevisionAuthor, useIsPageExist, useIsTrashPage, useIsUserPage,
   useIsNotCreatable,
 } from '~/stores/context';
+import { useSWRTagsInfo } from '~/stores/page';
 
 import TagLabels from '../Page/TagLabels';
 import SubNavButtons from './SubNavButtons';
@@ -44,16 +44,12 @@ const GrowiSubNavigation = (props) => {
   const { data: isUserPage } = useIsUserPage();
   const { data: isNotCreatable } = useIsNotCreatable();
 
+  const { mutate: mutateSWRTagsInfo, data: TagsInfoData } = useSWRTagsInfo(pageId);
 
   const {
-    appContainer, pageContainer, editorContainer, isCompactMode,
+    appContainer, editorContainer, isCompactMode,
   } = props;
 
-  const {
-    tags,
-  } = pageContainer.state;
-
-
   const { isGuestUser, isSharedUser } = appContainer;
   const isEditorMode = editorMode !== EditorMode.View;
   // Tags cannot be edited while the new page and editorMode is view
@@ -75,10 +71,10 @@ const GrowiSubNavigation = (props) => {
     }
 
     try {
-      const { tags } = await apiPost('/tags.update', { pageId, tags: newTags });
+      const { tags } = await apiPost('/tags.update', { pageId, revisionId, tags: newTags });
 
-      // update pageContainer.state
-      pageContainer.setState({ tags });
+      // mutate SWRTagsInfo
+      mutateSWRTagsInfo();
       // update editorContainer.state
       editorContainer.setState({ tags });
 
@@ -104,7 +100,7 @@ const GrowiSubNavigation = (props) => {
         <div className="grw-path-nav-container">
           { isAbleToShowTagLabel && !isCompactMode && !isTagLabelHidden && (
             <div className="grw-taglabels-container">
-              <TagLabels tags={tags} tagsUpdateInvoked={tagsUpdatedHandler} />
+              <TagLabels tags={TagsInfoData?.tags || []} tagsUpdateInvoked={tagsUpdatedHandler} />
             </div>
           ) }
           <PagePathNav pageId={pageId} pagePath={path} isSingleLineMode={isEditorMode} isCompactMode={isCompactMode} />
@@ -155,12 +151,11 @@ const GrowiSubNavigation = (props) => {
 /**
  * Wrapper component for using unstated
  */
-const GrowiSubNavigationWrapper = withUnstatedContainers(GrowiSubNavigation, [AppContainer, PageContainer, EditorContainer]);
+const GrowiSubNavigationWrapper = withUnstatedContainers(GrowiSubNavigation, [AppContainer, EditorContainer]);
 
 
 GrowiSubNavigation.propTypes = {
   appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-  pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
   editorContainer: PropTypes.instanceOf(EditorContainer).isRequired,
 
   isCompactMode: PropTypes.bool,

+ 5 - 2
packages/app/src/components/Page/RenderTagLabels.jsx

@@ -22,7 +22,6 @@ const RenderTagLabels = React.memo((props) => {
   }
 
   const isTagsEmpty = tags.length === 0;
-
   const tagElements = tags.map((tag) => {
     return (
       <a key={tag} href={`/_search?q=tag:${tag}`} className="grw-tag-label badge badge-secondary mr-2">
@@ -62,4 +61,8 @@ RenderTagLabels.propTypes = {
   isGuestUser: PropTypes.bool.isRequired,
 };
 
-export default withTranslation()(RenderTagLabels);
+const RenderTagLabelsWrapper = (props) => {
+  return <RenderTagLabels {...props}></RenderTagLabels>;
+};
+
+export default withTranslation()(RenderTagLabelsWrapper);

+ 63 - 0
packages/app/src/components/Page/RenderTagLabels.tsx

@@ -0,0 +1,63 @@
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+
+import { UncontrolledTooltip } from 'reactstrap';
+
+type RenderTagLabelsProps = {
+  tags: string[],
+  isGuestUser: boolean,
+  openEditorModal?: () => void,
+}
+
+const RenderTagLabels = React.memo((props: RenderTagLabelsProps) => {
+  const {
+    tags, isGuestUser, openEditorModal,
+  } = props;
+  const { t } = useTranslation();
+
+  function openEditorHandler() {
+    if (openEditorModal == null) {
+      return;
+    }
+    openEditorModal();
+  }
+
+  // activate suspense
+  if (tags == null) {
+    throw new Promise(() => {});
+  }
+
+  const isTagsEmpty = tags.length === 0;
+  const tagElements = tags.map((tag) => {
+    return (
+      <a key={tag} href={`/_search?q=tag:${tag}`} className="grw-tag-label badge badge-secondary mr-2">
+        {tag}
+      </a>
+    );
+  });
+
+  return (
+    <>
+      {tagElements}
+
+      <div id="edit-tags-btn-wrapper-for-tooltip">
+        <a
+          className={`btn btn-link btn-edit-tags p-0 text-muted ${isTagsEmpty ? 'no-tags' : ''} ${isGuestUser ? 'disabled' : ''}`}
+          onClick={openEditorHandler}
+        >
+          { isTagsEmpty && <>{ t('Add tags for this page') }</>}
+          <i className="ml-1 icon-plus"></i>
+        </a>
+      </div>
+      {isGuestUser && (
+        <UncontrolledTooltip placement="top" target="edit-tags-btn-wrapper-for-tooltip" fade={false}>
+          {t('Not available for guest')}
+        </UncontrolledTooltip>
+      )}
+    </>
+  );
+
+});
+
+
+export default RenderTagLabels;

+ 0 - 113
packages/app/src/components/Page/TagLabels.jsx

@@ -1,113 +0,0 @@
-import React, { Suspense } from 'react';
-import PropTypes from 'prop-types';
-import { withTranslation } from 'react-i18next';
-
-
-import { withUnstatedContainers } from '../UnstatedUtils';
-import AppContainer from '~/client/services/AppContainer';
-import EditorContainer from '~/client/services/EditorContainer';
-import PageContainer from '~/client/services/PageContainer';
-import { EditorMode } from '~/stores/ui';
-import { toastError, toastSuccess } from '~/client/util/apiNotification';
-
-import RenderTagLabels from './RenderTagLabels';
-import TagEditModal from './TagEditModal';
-
-class TagLabels extends React.Component {
-
-  constructor(props) {
-    super(props);
-
-    this.state = {
-      isTagEditModalShown: false,
-    };
-
-    this.openEditorModal = this.openEditorModal.bind(this);
-    this.closeEditorModal = this.closeEditorModal.bind(this);
-  }
-
-
-  openEditorModal() {
-    this.setState({ isTagEditModalShown: true });
-  }
-
-  closeEditorModal() {
-    this.setState({ isTagEditModalShown: false });
-  }
-
-  async tagsUpdatedHandler(newTags) {
-    const {
-      appContainer, editorContainer, pageContainer, editorMode,
-    } = this.props;
-
-    const { pageId, revisionId } = pageContainer.state;
-    // It will not be reflected in the DB until the page is refreshed
-    if (editorMode === EditorMode.Editor) {
-      return editorContainer.setState({ tags: newTags });
-    }
-    try {
-      const { tags, savedPage } = await appContainer.apiPost('/tags.update', {
-        pageId, tags: newTags, revisionId,
-      });
-      editorContainer.setState({ tags });
-      pageContainer.updatePageMetaData(savedPage, savedPage.revision, tags);
-      toastSuccess('updated tags successfully');
-    }
-    catch (err) {
-      toastError(err, 'fail to update tags');
-    }
-  }
-
-
-  render() {
-    const { appContainer, tagsUpdateInvoked, tags } = this.props;
-
-    return (
-      <>
-
-        <form className="grw-tag-labels form-inline">
-          <i className="tag-icon icon-tag mr-2"></i>
-          <Suspense fallback={<span className="grw-tag-label badge badge-secondary">―</span>}>
-            <RenderTagLabels
-              tags={tags}
-              openEditorModal={this.openEditorModal}
-              isGuestUser={appContainer.isGuestUser}
-            />
-          </Suspense>
-        </form>
-
-        <TagEditModal
-          tags={tags}
-          isOpen={this.state.isTagEditModalShown}
-          onClose={this.closeEditorModal}
-          appContainer={this.props.appContainer}
-          onTagsUpdated={tagsUpdateInvoked}
-        />
-
-      </>
-    );
-  }
-
-}
-
-/**
- * Wrapper component for using unstated
- */
-const TagLabelsUnstatedWrapper = withUnstatedContainers(TagLabels, [AppContainer, EditorContainer, PageContainer]);
-
-TagLabels.propTypes = {
-  t: PropTypes.func.isRequired, // i18next
-
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-  editorContainer: PropTypes.instanceOf(EditorContainer).isRequired,
-  pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
-  editorMode: PropTypes.string.isRequired,
-  tags: PropTypes.arrayOf(String),
-  tagsUpdateInvoked: PropTypes.func,
-};
-
-// wrapping tsx component returned by withUnstatedContainers to avoid type error when this component used in other tsx components.
-const TagLabelsWrapper = (props) => {
-  return <TagLabelsUnstatedWrapper {...props}></TagLabelsUnstatedWrapper>;
-};
-export default withTranslation()(TagLabelsWrapper);

+ 60 - 0
packages/app/src/components/Page/TagLabels.tsx

@@ -0,0 +1,60 @@
+import React, { FC, Suspense, useState } from 'react';
+
+import { withUnstatedContainers } from '../UnstatedUtils';
+import AppContainer from '~/client/services/AppContainer';
+
+import RenderTagLabels from './RenderTagLabels';
+import TagEditModal from './TagEditModal';
+
+type TagLabels = {
+  appContainer: AppContainer,
+  tagsUpdateInvoked: any,
+  tags: string[],
+}
+
+
+const TagLabels:FC<TagLabels> = (props:TagLabels) => {
+  const {
+    appContainer, tagsUpdateInvoked,
+  } = props;
+  const tags = props.tags;
+  const [isTagEditModalShown, setIsTagEditModalShown] = useState(false);
+
+  const openEditorModal = () => {
+    setIsTagEditModalShown(true);
+  };
+
+  const closeEditorModal = () => {
+    setIsTagEditModalShown(false);
+  };
+
+  return (
+    <>
+      <form className="grw-tag-labels form-inline">
+        <i className="tag-icon icon-tag mr-2"></i>
+        <Suspense fallback={<span className="grw-tag-label badge badge-secondary">―</span>}>
+          <RenderTagLabels
+            tags={tags}
+            openEditorModal={openEditorModal}
+            isGuestUser={appContainer.isGuestUser}
+          />
+        </Suspense>
+      </form>
+
+      <TagEditModal
+        tags={tags}
+        isOpen={isTagEditModalShown}
+        onClose={closeEditorModal}
+        onTagsUpdated={tagsUpdateInvoked}
+      />
+
+    </>
+  );
+};
+
+/**
+ * Wrapper component for using unstated
+ */
+const TagLabelsUnstatedWrapper = withUnstatedContainers(TagLabels, [AppContainer]);
+
+export default TagLabelsUnstatedWrapper;

+ 4 - 0
packages/app/src/server/routes/tag.js

@@ -144,6 +144,10 @@ module.exports = function(crowi, app) {
     const tags = req.body.tags;
     const userId = req.user._id;
     const revisionId = req.body.revisionId;
+    console.log(req.body.pageId);
+    console.log(req.body.tags);
+    console.log(req.user._id);
+    console.log(req.body.revisionId);
 
     const result = {};
     try {