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

fix: slack notification not working, SWRize slackChannels (#4888)

* impl

* cleanup

* removed console.log

* wip fb fix

* change ordering

* wip

* revert

* revert

* optional props

* err fix

* Use context extractor

* impl

* impl

* delete

* whitespace

* wip fb fix

* fb fix

* fix

* impl mediator

* page.jsx migrate to mediator

* impl

* fix props

* move util

* fb fix

* fix deleted page

* fix

* tweak rendering

Co-authored-by: Yuki Takei <yuki@weseek.co.jp>
stevenfukase 4 лет назад
Родитель
Сommit
ef6ecf8ebf

+ 5 - 1
packages/app/src/client/services/ContextExtractor.tsx

@@ -6,6 +6,7 @@ import {
   useIsDeletable, useIsDeleted, useIsNotCreatable, useIsPageExist, useIsTrashPage, useIsUserPage, useLastUpdateUsername,
   usePageId, usePageIdOnHackmd, usePageUser, useCurrentPagePath, useRevisionCreatedAt, useRevisionId, useRevisionIdHackmdSynced,
   useShareLinkId, useShareLinksNumber, useTemplateTagData, useUpdatedAt, useCreator, useRevisionAuthor, useCurrentUser,
+  useSlackChannels,
 } from '../../stores/context';
 import {
   useIsDeviceSmallerThanMd,
@@ -60,7 +61,7 @@ const ContextExtractorOnce: FC = () => {
   const hasDraftOnHackmd = !!mainContent?.getAttribute('data-page-has-draft-on-hackmd');
   const creator = JSON.parse(mainContent?.getAttribute('data-page-creator') || jsonNull);
   const revisionAuthor = JSON.parse(mainContent?.getAttribute('data-page-revision-author') || jsonNull);
-
+  const slackChannels = mainContent?.getAttribute('data-slack-channels') || '';
   /*
    * use static swr
    */
@@ -107,6 +108,9 @@ const ContextExtractorOnce: FC = () => {
   usePreferDrawerModeOnEditByUser();
   useIsDeviceSmallerThanMd();
 
+  // Editor
+  useSlackChannels(slackChannels);
+
   return null;
 };
 

+ 3 - 4
packages/app/src/client/services/EditorContainer.js

@@ -27,8 +27,6 @@ export default class EditorContainer extends Container {
     this.state = {
       tags: null,
 
-      slackChannels: mainContent.getAttribute('data-slack-channels') || '',
-
       grant: 1, // default: public
       grantGroupId: null,
       grantGroupName: null,
@@ -142,10 +140,11 @@ export default class EditorContainer extends Container {
     }
   }
 
+  // TODO: Remove when SWR is complete
   getCurrentOptionsToSave() {
     const opt = {
-      isSlackEnabled: this.state.isSlackEnabled,
-      slackChannels: this.state.slackChannels,
+      // isSlackEnabled: this.state.isSlackEnabled,
+      // slackChannels: this.state.slackChannels,
       grant: this.state.grant,
       pageTags: this.state.tags,
     };

+ 15 - 0
packages/app/src/client/util/editor.ts

@@ -0,0 +1,15 @@
+import EditorContainer from '~/client/services/EditorContainer';
+
+type OptionsToSave = {
+  isSlackEnabled: boolean;
+  slackChannels: string;
+  grant: number;
+  pageTags: string[];
+  grantUserGroupId?: string;
+};
+
+// TODO: Remove editorContainer upon migration to SWR
+export const getOptionsToSave = (isSlackEnabled: boolean, slackChannels: string, editorContainer: EditorContainer): OptionsToSave => {
+  const optionsToSave = editorContainer.getCurrentOptionsToSave();
+  return { ...optionsToSave, isSlackEnabled, slackChannels };
+};

+ 26 - 7
packages/app/src/components/Page.jsx

@@ -17,8 +17,12 @@ import DrawioModal from './PageEditor/DrawioModal';
 import mtu from './PageEditor/MarkdownTableUtil';
 import mdu from './PageEditor/MarkdownDrawioUtil';
 
+import { getOptionsToSave } from '~/client/util/editor';
+
 // TODO: remove this when omitting unstated is completed
 import { useEditorMode } from '~/stores/ui';
+import { useIsSlackEnabled } from '~/stores/editor';
+import { useSlackChannels } from '~/stores/context';
 
 const logger = loggerFactory('growi:Page');
 
@@ -73,8 +77,10 @@ class Page extends React.Component {
   }
 
   async saveHandlerForHandsontableModal(markdownTable) {
-    const { pageContainer, editorContainer } = this.props;
-    const optionsToSave = editorContainer.getCurrentOptionsToSave();
+    const {
+      isSlackEnabled, slackChannels, pageContainer, editorContainer,
+    } = this.props;
+    const optionsToSave = getOptionsToSave(isSlackEnabled, slackChannels, editorContainer);
 
     const newMarkdown = mtu.replaceMarkdownTableInMarkdown(
       markdownTable,
@@ -103,8 +109,10 @@ class Page extends React.Component {
   }
 
   async saveHandlerForDrawioModal(drawioData) {
-    const { pageContainer, editorContainer } = this.props;
-    const optionsToSave = editorContainer.getCurrentOptionsToSave();
+    const {
+      isSlackEnabled, slackChannels, pageContainer, editorContainer,
+    } = this.props;
+    const optionsToSave = getOptionsToSave(isSlackEnabled, slackChannels, editorContainer);
 
     const newMarkdown = mdu.replaceDrawioInMarkdown(
       drawioData,
@@ -163,16 +171,27 @@ Page.propTypes = {
 
   // TODO: remove this when omitting unstated is completed
   editorMode: PropTypes.string.isRequired,
+  isSlackEnabled: PropTypes.bool.isRequired,
+  slackChannels: PropTypes.string.isRequired,
 };
 
 const PageWrapper = (props) => {
-  const { data } = useEditorMode();
+  const { data: editorMode } = useEditorMode();
+  const { data: isSlackEnabled } = useIsSlackEnabled();
+  const { data: slackChannels } = useSlackChannels();
 
-  if (data == null) {
+  if (editorMode == null) {
     return null;
   }
 
-  return <Page {...props} editorMode={data} />;
+  return (
+    <Page
+      {...props}
+      editorMode={editorMode}
+      isSlackEnabled={isSlackEnabled}
+      slackChannels={slackChannels}
+    />
+  );
 };
 
 export default withUnstatedContainers(PageWrapper, [AppContainer, PageContainer, EditorContainer]);

+ 22 - 4
packages/app/src/components/PageEditor.jsx

@@ -15,9 +15,12 @@ import Preview from './PageEditor/Preview';
 import scrollSyncHelper from './PageEditor/ScrollSyncHelper';
 import EditorContainer from '~/client/services/EditorContainer';
 
+import { getOptionsToSave } from '~/client/util/editor';
+
 // TODO: remove this when omitting unstated is completed
 import { useEditorMode } from '~/stores/ui';
-import { useIsEditable } from '~/stores/context';
+import { useIsEditable, useSlackChannels } from '~/stores/context';
+import { useIsSlackEnabled } from '~/stores/editor';
 
 const logger = loggerFactory('growi:PageEditor');
 
@@ -128,8 +131,11 @@ class PageEditor extends React.Component {
    * save and update state of containers
    */
   async onSaveWithShortcut() {
-    const { pageContainer, editorContainer } = this.props;
-    const optionsToSave = editorContainer.getCurrentOptionsToSave();
+    const {
+      isSlackEnabled, slackChannels, editorContainer, pageContainer,
+    } = this.props;
+
+    const optionsToSave = getOptionsToSave(isSlackEnabled, slackChannels, editorContainer);
 
     try {
       // disable unsaved warning
@@ -360,12 +366,22 @@ const PageEditorHOCWrapper = withUnstatedContainers(PageEditor, [AppContainer, P
 const PageEditorWrapper = (props) => {
   const { data: isEditable } = useIsEditable();
   const { data: editorMode } = useEditorMode();
+  const { data: isSlackEnabled } = useIsSlackEnabled();
+  const { data: slackChannels } = useSlackChannels();
 
   if (isEditable == null || editorMode == null) {
     return null;
   }
 
-  return <PageEditorHOCWrapper {...props} isEditable={isEditable} editorMode={editorMode} />;
+  return (
+    <PageEditorHOCWrapper
+      {...props}
+      isEditable={isEditable}
+      editorMode={editorMode}
+      isSlackEnabled={isSlackEnabled}
+      slackChannels={slackChannels}
+    />
+  );
 };
 
 PageEditor.propTypes = {
@@ -377,6 +393,8 @@ PageEditor.propTypes = {
 
   // TODO: remove this when omitting unstated is completed
   editorMode: PropTypes.string.isRequired,
+  isSlackEnabled: PropTypes.bool.isRequired,
+  slackChannels: PropTypes.string.isRequired,
 };
 
 export default PageEditorWrapper;

+ 9 - 8
packages/app/src/components/PageEditor/EditorNavbarBottom.tsx

@@ -17,6 +17,7 @@ import SavePageControls from '../SavePageControls';
 
 import OptionsSelector from './OptionsSelector';
 import { useIsSlackEnabled } from '~/stores/editor';
+import { useSlackChannels } from '~/stores/context';
 
 const EditorNavbarBottom = (props) => {
 
@@ -30,10 +31,15 @@ const EditorNavbarBottom = (props) => {
   const { mutate: mutateDrawerOpened } = useDrawerOpened();
   const { data: isDeviceSmallerThanMd } = useIsDeviceSmallerThanMd();
   const { data: isSlackEnabled, mutate: mutateIsSlackEnabled } = useIsSlackEnabled();
+  const { data: slackChannels, mutate: mutateSlackChannels } = useSlackChannels();
   const additionalClasses = ['grw-editor-navbar-bottom'];
 
   const isSlackEnabledToggleHandler = useCallback(
-    bool => mutateIsSlackEnabled(bool), [mutateIsSlackEnabled],
+    (bool: boolean) => mutateIsSlackEnabled(bool), [mutateIsSlackEnabled],
+  );
+
+  const slackChannelsChangedHandler = useCallback(
+    (slackChannels: string) => mutateSlackChannels(slackChannels), [mutateSlackChannels],
   );
 
   const renderDrawerButton = () => (
@@ -46,11 +52,6 @@ const EditorNavbarBottom = (props) => {
     </button>
   );
 
-  const slackChannelsChangedHandler = (slackChannels) => {
-    props.editorContainer.setState({ slackChannels });
-  };
-
-  // eslint-disable-next-line react/prop-types
   const renderExpandButton = () => (
     <div className="d-md-none ml-2">
       <button
@@ -74,7 +75,7 @@ const EditorNavbarBottom = (props) => {
           <nav className={`navbar navbar-expand-lg border-top ${additionalClasses.join(' ')}`}>
             <SlackNotification
               isSlackEnabled={isSlackEnabled ?? false}
-              slackChannels={props.editorContainer.state.slackChannels}
+              slackChannels={slackChannels}
               onEnabledFlagChange={isSlackEnabledToggleHandler}
               onChannelChange={slackChannelsChangedHandler}
               id="idForEditorNavbarBottomForMobile"
@@ -105,7 +106,7 @@ const EditorNavbarBottom = (props) => {
             <div className="mr-2">
               <SlackNotification
                 isSlackEnabled={isSlackEnabled ?? false}
-                slackChannels={props.editorContainer.state.slackChannels}
+                slackChannels={slackChannels}
                 onEnabledFlagChange={isSlackEnabledToggleHandler}
                 onChannelChange={slackChannelsChangedHandler}
                 id="idForEditorNavbarBottom"

+ 22 - 5
packages/app/src/components/PageEditorByHackmd.jsx

@@ -11,8 +11,12 @@ import EditorContainer from '~/client/services/EditorContainer';
 import { withUnstatedContainers } from './UnstatedUtils';
 import HackmdEditor from './PageEditorByHackmd/HackmdEditor';
 
+import { getOptionsToSave } from '~/client/util/editor';
+
 // TODO: remove this when omitting unstated is completed
 import { useEditorMode } from '~/stores/ui';
+import { useSlackChannels } from '~/stores/context';
+import { useIsSlackEnabled } from '~/stores/editor';
 
 const logger = loggerFactory('growi:PageEditorByHackmd');
 
@@ -166,8 +170,10 @@ class PageEditorByHackmd extends React.Component {
    * @param {string} markdown
    */
   async onSaveWithShortcut(markdown) {
-    const { pageContainer, editorContainer } = this.props;
-    const optionsToSave = editorContainer.getCurrentOptionsToSave();
+    const {
+      isSlackEnabled, slackChannels, pageContainer, editorContainer,
+    } = this.props;
+    const optionsToSave = getOptionsToSave(isSlackEnabled, slackChannels, editorContainer);
 
     try {
       // disable unsaved warning
@@ -423,13 +429,22 @@ class PageEditorByHackmd extends React.Component {
 const PageEditorByHackmdHOCWrapper = withUnstatedContainers(PageEditorByHackmd, [AppContainer, PageContainer, EditorContainer]);
 
 const PageEditorByHackmdWrapper = (props) => {
-  const { data } = useEditorMode();
+  const { data: editorMode } = useEditorMode();
+  const { data: isSlackEnabled } = useIsSlackEnabled();
+  const { data: slackChannels } = useSlackChannels();
 
-  if (data == null) {
+  if (editorMode == null) {
     return null;
   }
 
-  return <PageEditorByHackmdHOCWrapper {...props} editorMode={data} />;
+  return (
+    <PageEditorByHackmdHOCWrapper
+      {...props}
+      editorMode={editorMode}
+      isSlackEnabled={isSlackEnabled}
+      slackChannels={slackChannels}
+    />
+  );
 };
 
 PageEditorByHackmd.propTypes = {
@@ -441,6 +456,8 @@ PageEditorByHackmd.propTypes = {
 
   // TODO: remove this when omitting unstated is completed
   editorMode: PropTypes.string.isRequired,
+  isSlackEnabled: PropTypes.bool.isRequired,
+  slackChannels: PropTypes.string.isRequired,
 };
 
 export default withTranslation()(PageEditorByHackmdWrapper);

+ 26 - 6
packages/app/src/components/SavePageControls.jsx

@@ -17,9 +17,12 @@ import EditorContainer from '~/client/services/EditorContainer';
 import { withUnstatedContainers } from './UnstatedUtils';
 import GrantSelector from './SavePageControls/GrantSelector';
 
+import { getOptionsToSave } from '~/client/util/editor';
+
 // TODO: remove this when omitting unstated is completed
 import { useEditorMode } from '~/stores/ui';
-import { useIsEditable } from '~/stores/context';
+import { useIsEditable, useSlackChannels } from '~/stores/context';
+import { useIsSlackEnabled } from '~/stores/editor';
 
 const logger = loggerFactory('growi:SavePageControls');
 
@@ -43,13 +46,16 @@ class SavePageControls extends React.Component {
   }
 
   async save() {
-    const { pageContainer, editorContainer } = this.props;
+    const {
+      isSlackEnabled, slackChannels, pageContainer, editorContainer,
+    } = this.props;
     // disable unsaved warning
     editorContainer.disableUnsavedWarning();
 
     try {
       // save
-      await pageContainer.saveAndReload(editorContainer.getCurrentOptionsToSave(), this.props.editorMode);
+      const optionsToSave = getOptionsToSave(isSlackEnabled, slackChannels, editorContainer);
+      await pageContainer.saveAndReload(optionsToSave, this.props.editorMode);
     }
     catch (error) {
       logger.error('failed to save', error);
@@ -58,11 +64,14 @@ class SavePageControls extends React.Component {
   }
 
   saveAndOverwriteScopesOfDescendants() {
-    const { pageContainer, editorContainer } = this.props;
+    const {
+      isSlackEnabled, slackChannels, pageContainer, editorContainer,
+    } = this.props;
     // disable unsaved warning
     editorContainer.disableUnsavedWarning();
     // save
-    const optionsToSave = Object.assign(editorContainer.getCurrentOptionsToSave(), {
+    const currentOptionsToSave = getOptionsToSave(isSlackEnabled, slackChannels, editorContainer);
+    const optionsToSave = Object.assign(currentOptionsToSave, {
       overwriteScopesOfDescendants: true,
     });
     pageContainer.saveAndReload(optionsToSave, this.props.editorMode);
@@ -117,6 +126,8 @@ const SavePageControlsHOCWrapper = withUnstatedContainers(SavePageControls, [App
 const SavePageControlsWrapper = (props) => {
   const { data: isEditable } = useIsEditable();
   const { data: editorMode } = useEditorMode();
+  const { data: isSlackEnabled } = useIsSlackEnabled();
+  const { data: slackChannels } = useSlackChannels();
 
   if (isEditable == null || editorMode == null) {
     return null;
@@ -126,7 +137,14 @@ const SavePageControlsWrapper = (props) => {
     return null;
   }
 
-  return <SavePageControlsHOCWrapper {...props} editorMode={editorMode} />;
+  return (
+    <SavePageControlsHOCWrapper
+      {...props}
+      editorMode={editorMode}
+      isSlackEnabled={isSlackEnabled}
+      slackChannels={slackChannels}
+    />
+  );
 };
 
 SavePageControls.propTypes = {
@@ -138,6 +156,8 @@ SavePageControls.propTypes = {
 
   // TODO: remove this when omitting unstated is completed
   editorMode: PropTypes.string.isRequired,
+  isSlackEnabled: PropTypes.bool.isRequired,
+  slackChannels: PropTypes.string.isRequired,
 };
 
 export default withTranslation()(SavePageControlsWrapper);

+ 3 - 0
packages/app/src/stores/context.tsx

@@ -118,6 +118,9 @@ export const useRevisionAuthor = (initialData?: Nullable<any>): SWRResponse<Null
   return useStaticSWR<Nullable<any>, Error>('revisionAuthor', initialData ?? null);
 };
 
+export const useSlackChannels = (initialData?: Nullable<any>): SWRResponse<Nullable<any>, Error> => {
+  return useStaticSWR<Nullable<any>, Error>('slackChannels', initialData ?? null);
+};
 
 /** **********************************************************
  *                     Computed contexts