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

WIP: impl SavePageControls component

* add createPage/updatePage method to util/Crowi.js
Yuki Takei 7 лет назад
Родитель
Сommit
046b2e8b22

+ 15 - 22
lib/views/_form.html

@@ -14,29 +14,22 @@
 </div>
 {% endif %}
 
-<!-- <form action="/_/edit" id="page-form" method="post" class="{% if isUploadable() %}uploadable{% endif %} page-form"> -->
-  <!--
-  <input type="hidden" id="form-body" name="pageForm[body]" value="{% if pageForm.body %}{{ pageForm.body }}{% endif %}">
-  <input type="hidden" name="pageForm[path]" value="{{ path | preventXss }}">
-  <input type="hidden" name="pageForm[currentRevision]" value="{{ pageForm.currentRevision|default(page.revision._id.toString()) }}">
-   -->
-  <div class="page-editor-footer d-flex flex-row align-items-center justify-content-between">
-    <div>
-      <div id="page-editor-options-selector" class="hidden-xs"></div>
-    </div>
+<div class="page-editor-footer d-flex flex-row align-items-center justify-content-between">
+  <div>
+    <div id="page-editor-options-selector" class="hidden-xs"></div>
+  </div>
 
-    <div id="save-page-controls"
-      data-page-grant="{{ page.grant }}"
-      data-grant-group="{{ pageRelatedGroup._id.toString() }}"
-      data-grant-group-name="{{ pageRelatedGroup.name }}">
-      <!--
-      <input type="hidden" id="page-grant" name="pageForm[grant]" value="{{ page.grant }}">
-      <input type="hidden" id="grant-group" name="pageForm[grantUserGroupId]" value="{{ pageRelatedGroup._id.toString() }}">
-      <input type="hidden" id="edit-form-csrf" name="_csrf" value="{{ csrf() }}">
-       -->
-    </div>
+  <div id="save-page-controls"
+    data-page-grant="{{ page.grant }}"
+    data-grant-group="{{ pageRelatedGroup._id.toString() }}"
+    data-grant-group-name="{{ pageRelatedGroup.name }}">
+    <!--
+    <input type="hidden" id="page-grant" name="pageForm[grant]" value="{{ page.grant }}">
+    <input type="hidden" id="grant-group" name="pageForm[grantUserGroupId]" value="{{ pageRelatedGroup._id.toString() }}">
+    <input type="hidden" id="edit-form-csrf" name="_csrf" value="{{ csrf() }}">
+      -->
   </div>
-<!-- </form> -->
-<!-- <input type="hidden" id="grant-group-name" value="{{ pageRelatedGroup.name }}">{# for storing group name #} -->
+</div>
+
 <div class="file-module hidden">
 </div>

+ 84 - 42
resource/js/app.js

@@ -1,6 +1,7 @@
 import React from 'react';
 import ReactDOM from 'react-dom';
 import { I18nextProvider } from 'react-i18next';
+import * as toastr from 'toastr';
 
 import i18nFactory from './i18n';
 
@@ -148,6 +149,86 @@ Object.keys(componentMappings).forEach((key) => {
   }
 });
 
+
+/**
+ * save success handler when reloading is not needed
+ * @param {object} page Page instance
+ */
+const saveWithShortcutSuccessHandler = function(page) {
+  // modify the revision id value to pass checking id when updating
+  // crowi.getCrowiForJquery().updatePageForm(page);
+
+  // show toastr
+  toastr.success(undefined, 'Saved successfully', {
+    closeButton: true,
+    progressBar: true,
+    newestOnTop: false,
+    showDuration: '100',
+    hideDuration: '100',
+    timeOut: '1200',
+    extendedTimeOut: '150',
+  });
+
+  pageId = page._id;
+  pageRevisionId = page.revision._id;
+
+  // set page id to SavePageControls
+  componentInstances.savePageControls.setPageId(pageId);  // TODO fix this line failed because of i18next
+
+  // re-render Page component
+  if (componentInstances.page != null) {
+    componentInstances.page.setMarkdown(page.revision.body);
+  }
+  // set revision id to PageEditorByHackmd
+  if (componentInstances.pageEditorByHackmd != null) {
+    componentInstances.pageEditorByHackmd.setRevisionId(pageRevisionId);
+  }
+};
+
+const errorHandler = function(error) {
+  toastr.error(error.message, 'Error occured', {
+    closeButton: true,
+    progressBar: true,
+    newestOnTop: false,
+    showDuration: '100',
+    hideDuration: '100',
+    timeOut: '3000',
+  });
+};
+
+const saveWithShortcut = function(markdown) {
+  let promise = undefined;
+  if (pageId == null) {
+    promise = crowi.createPage(pagePath, markdown, {});
+  }
+  else {
+    promise = crowi.updatePage(pageId, pageRevisionId, markdown, {});
+  }
+
+  promise
+    .then(saveWithShortcutSuccessHandler)
+    .catch(errorHandler);
+};
+
+// render SavePageControls
+let savePageControls = null;
+const savePageControlsElem = document.getElementById('save-page-controls');
+if (savePageControlsElem) {
+  const pageGrant = +savePageControlsElem.dataset.pageGrant;
+  const pageGrantGroupId = savePageControlsElem.dataset.grantGroup;
+  const pageGrantGroupName = savePageControlsElem.dataset.grantGroupName;
+  savePageControls = (
+    <SavePageControls crowi={crowi}
+      pageId={pageId} pagePath={pagePath} slackChannels={slackChannels}
+      pageGrant={pageGrant} pageGrantGroupId={pageGrantGroupId} pageGrantGroupName={pageGrantGroupName} />
+  );
+  ReactDOM.render(
+    <I18nextProvider i18n={i18n}>{savePageControls}</I18nextProvider>,
+    savePageControlsElem
+  );
+  componentInstances.savePageControls = savePageControls;
+}
+
 /*
  * HackMD Editor
  */
@@ -155,22 +236,12 @@ Object.keys(componentMappings).forEach((key) => {
 let pageEditorByHackmd = null;
 const pageEditorWithHackmdElem = document.getElementById('page-editor-with-hackmd');
 if (pageEditorWithHackmdElem) {
-  // create onSave event handler
-  const onSaveSuccess = function(page) {
-    // modify the revision id value to pass checking id when updating
-    crowi.getCrowiForJquery().updatePageForm(page);
-    // re-render Page component if exists
-    if (componentInstances.page != null) {
-      componentInstances.page.setMarkdown(page.revision.body);
-    }
-  };
-
   pageEditorByHackmd = ReactDOM.render(
     <PageEditorByHackmd crowi={crowi}
         pageId={pageId} revisionId={pageRevisionId}
         pageIdOnHackmd={pageIdOnHackmd} revisionIdHackmdSynced={pageRevisionIdHackmdSynced} hasDraftOnHackmd={hasDraftOnHackmd}
         markdown={markdown}
-        onSaveSuccess={onSaveSuccess} />,
+        onSaveWithShortcut={saveWithShortcut} />,
     pageEditorWithHackmdElem
   );
   componentInstances.pageEditorByHackmd = pageEditorByHackmd;
@@ -186,27 +257,12 @@ const previewOptions = new PreviewOptions(crowi.previewOptions);
 // render PageEditor
 const pageEditorElem = document.getElementById('page-editor');
 if (pageEditorElem) {
-  // create onSave event handler
-  const onSaveSuccess = function(page) {
-    // modify the revision id value to pass checking id when updating
-    crowi.getCrowiForJquery().updatePageForm(page);
-
-    if (componentInstances.page != null) {
-      // re-render Page component
-      componentInstances.page.setMarkdown(page.revision.body);
-      // set revision id to PageEditorByHackmd
-      if (componentInstances.pageEditorByHackmd != null) {
-        componentInstances.pageEditorByHackmd.setRevisionId(page.revision._id);
-      }
-    }
-  };
-
   pageEditor = ReactDOM.render(
     <PageEditor crowi={crowi} crowiRenderer={crowiRenderer}
         pageId={pageId} revisionId={pageRevisionId} pagePath={pagePath}
         markdown={markdown}
         editorOptions={editorOptions} previewOptions={previewOptions}
-        onSaveSuccess={onSaveSuccess} />,
+        onSaveWithShortcut={saveWithShortcut} />,
     pageEditorElem
   );
   // set refs for pageEditor
@@ -285,21 +341,7 @@ if (pageEditorOptionsSelectorElem) {
 //     pageEditorGrantSelectorElem
 //   );
 // }
-// render SavePageControls
-const savePageControlsElem = document.getElementById('save-page-controls');
-if (savePageControlsElem) {
-  const pageGrant = +savePageControlsElem.dataset.pageGrant;
-  const pageGrantGroupId = savePageControlsElem.dataset.grantGroup;
-  const pageGrantGroupName = savePageControlsElem.dataset.grantGroupName;
-  ReactDOM.render(
-    <I18nextProvider i18n={i18n}>
-      <SavePageControls crowi={crowi}
-        pageId={pageId} pagePath={pagePath} slackChannels={slackChannels}
-        pageGrant={pageGrant} pageGrantGroupId={pageGrantGroupId} pageGrantGroupName={pageGrantGroupName} />
-    </I18nextProvider>,
-    savePageControlsElem
-  );
-}
+
 
 // render for admin
 const customCssEditorElem = document.getElementById('custom-css-editor');

+ 4 - 77
resource/js/components/PageEditor.js

@@ -1,7 +1,6 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
-import * as toastr from 'toastr';
 import { throttle, debounce } from 'throttle-debounce';
 
 import GrowiRenderer from '../util/GrowiRenderer';
@@ -37,15 +36,12 @@ export default class PageEditor extends React.Component {
     this.setCaretLine = this.setCaretLine.bind(this);
     this.focusToEditor = this.focusToEditor.bind(this);
     this.onMarkdownChanged = this.onMarkdownChanged.bind(this);
-    this.onSave = this.onSave.bind(this);
     this.onUpload = this.onUpload.bind(this);
     this.onEditorScroll = this.onEditorScroll.bind(this);
     this.onEditorScrollCursorIntoView = this.onEditorScrollCursorIntoView.bind(this);
     this.onPreviewScroll = this.onPreviewScroll.bind(this);
     this.saveDraft = this.saveDraft.bind(this);
     this.clearDraft = this.clearDraft.bind(this);
-    this.pageSavedHandler = this.pageSavedHandler.bind(this);
-    this.apiErrorHandler = this.apiErrorHandler.bind(this);
 
     // for scrolling
     this.lastScrolledDateWithCursor = null;
@@ -103,49 +99,6 @@ export default class PageEditor extends React.Component {
     this.saveDraftWithDebounce();
   }
 
-  /**
-   * the save event handler
-   */
-  onSave() {
-    let endpoint;
-    let data;
-
-    // update
-    if (this.state.pageId != null) {
-      endpoint = '/pages.update';
-      data = {
-        page_id: this.state.pageId,
-        revision_id: this.state.revisionId,
-        body: this.state.markdown,
-      };
-    }
-    // create
-    else {
-      endpoint = '/pages.create';
-      data = {
-        path: this.props.pagePath,
-        body: this.state.markdown,
-      };
-    }
-
-    this.props.crowi.apiPost(endpoint, data)
-      .then((res) => {
-        // show toastr
-        toastr.success(undefined, 'Saved successfully', {
-          closeButton: true,
-          progressBar: true,
-          newestOnTop: false,
-          showDuration: '100',
-          hideDuration: '100',
-          timeOut: '1200',
-          extendedTimeOut: '150',
-        });
-
-        this.pageSavedHandler(res.page);
-      })
-      .catch(this.apiErrorHandler);
-  }
-
   /**
    * the upload event handler
    * @param {any} files
@@ -293,34 +246,6 @@ export default class PageEditor extends React.Component {
     this.props.crowi.clearDraft(this.props.pagePath);
   }
 
-  pageSavedHandler(page) {
-    // update states
-    this.setState({
-      pageId: page.id,
-      revisionId: page.revision._id,
-      markdown: page.revision.body
-    });
-
-    // clear draft
-    this.clearDraft();
-
-    // dispatch onSaveSuccess event
-    if (this.props.onSaveSuccess != null) {
-      this.props.onSaveSuccess(page);
-    }
-  }
-
-  apiErrorHandler(error) {
-    toastr.error(error.message, 'Error occured', {
-      closeButton: true,
-      progressBar: true,
-      newestOnTop: false,
-      showDuration: '100',
-      hideDuration: '100',
-      timeOut: '3000',
-    });
-  }
-
   renderPreview(value) {
     this.setState({ markdown: value });
 
@@ -374,8 +299,10 @@ export default class PageEditor extends React.Component {
             onScroll={this.onEditorScroll}
             onScrollCursorIntoView={this.onEditorScrollCursorIntoView}
             onChange={this.onMarkdownChanged}
-            onSave={this.onSave}
             onUpload={this.onUpload}
+            onSave={() => {
+              this.props.onSaveWithShortcut(this.state.markdown);
+            }}
           />
         </div>
         <div className="col-md-6 hidden-sm hidden-xs page-editor-preview-container">
@@ -395,11 +322,11 @@ export default class PageEditor extends React.Component {
 PageEditor.propTypes = {
   crowi: PropTypes.object.isRequired,
   crowiRenderer: PropTypes.object.isRequired,
+  onSaveWithShortcut: PropTypes.func.isRequired,
   markdown: PropTypes.string.isRequired,
   pageId: PropTypes.string,
   revisionId: PropTypes.string,
   pagePath: PropTypes.string,
-  onSaveSuccess: PropTypes.func,
   editorOptions: PropTypes.instanceOf(EditorOptions),
   previewOptions: PropTypes.instanceOf(PreviewOptions),
 };

+ 11 - 30
resource/js/components/SavePageControls.jsx

@@ -2,8 +2,6 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { translate } from 'react-i18next';
 
-import * as toastr from 'toastr';
-
 import SlackNotification from './SlackNotification';
 import GrantSelector from './SavePageControls/GrantSelector';
 
@@ -13,43 +11,26 @@ class SavePageControls extends React.PureComponent {
     super(props);
 
     this.state = {
+      pageId: this.props.pageId,
     };
-
-    this.apiErrorHandler = this.apiErrorHandler.bind(this);
   }
 
   componentWillMount() {
   }
 
-  save() {
-    const params = {
-    };
-    this.props.crowi.apiPost('/page.save', params)
-      .then(res => {
-        if (!res.ok) {
-          throw new Error(res.error);
-        }
-
-      })
-      .catch(this.apiErrorHandler)
-      .then(() => {
-      });
-  }
-
-  apiErrorHandler(error) {
-    toastr.error(error.message, 'Error occured', {
-      closeButton: true,
-      progressBar: true,
-      newestOnTop: false,
-      showDuration: '100',
-      hideDuration: '100',
-      timeOut: '3000',
-    });
+  /**
+   * update pageId of state
+   * @param {string} pageId
+   */
+  setPageId(pageId) {
+    this.setState({pageId});
   }
 
   render() {
     const { t } = this.props;
 
+    const label = this.state.pageId == null ? t('Create') : t('Update');
+
     return (
       <div className="d-flex align-items-center form-inline">
         <div className="mr-2">
@@ -69,7 +50,7 @@ class SavePageControls extends React.PureComponent {
             pageGrantGroupName={this.props.pageGrantGroupName} />
         </div>
 
-        <button className="btn btn-primary btn-submit">{t('Save')}</button>
+        <button className="btn btn-primary btn-submit">{label}</button>
       </div>
     );
   }
@@ -78,8 +59,8 @@ class SavePageControls extends React.PureComponent {
 SavePageControls.propTypes = {
   t: PropTypes.func.isRequired,               // i18next
   crowi: PropTypes.object.isRequired,
-  // for SlackNotification
   pageId: PropTypes.string,
+  // for SlackNotification
   pagePath: PropTypes.string,
   slackChannels: PropTypes.string,
   // for GrantSelector

+ 29 - 0
resource/js/util/Crowi.js

@@ -196,6 +196,35 @@ export default class Crowi {
     return null;
   }
 
+  createPage(pagePath, markdown, additionalParams = {}) {
+    const params = Object.assign(additionalParams, {
+      path: pagePath,
+      body: markdown,
+    });
+    return this.apiPost('/pages.create', params)
+      .then(res => {
+        if (!res.ok) {
+          throw new Error(res.error);
+        }
+        return res.page;
+      });
+  }
+
+  updatePage(pageId, revisionId, markdown, additionalParams = {}) {
+    const params = Object.assign(additionalParams, {
+      page_id: pageId,
+      revision_id: revisionId,
+      body: markdown,
+    });
+    return this.apiPost('/pages.update', params)
+      .then(res => {
+        if (!res.ok) {
+          throw new Error(res.error);
+        }
+        return res.page;
+      });
+  }
+
   apiGet(path, params) {
     return this.apiRequest('get', path, {params: params});
   }