Преглед изворни кода

Merge pull request #4521 from weseek/feat/77877-show-modal

Feat/77877 show modal
Yuki Takei пре 4 година
родитељ
комит
fb31a184a0

+ 2 - 1
packages/app/resource/locales/en_US/translation.json

@@ -461,7 +461,8 @@
     "dont_ask_again": "Don't ask again"
   },
   "modal_resolve_conflict": {
-    "title": "Resolve Conflict",
+    "file_conflicting_with_newer_remote": "This file is conflicting with newer remote file",
+    "resolve_conflict": "Resolve Conflict",
     "resolve_and_save" : "Resolve and save"
   },
   "link_edit": {

+ 2 - 1
packages/app/resource/locales/ja_JP/translation.json

@@ -461,7 +461,8 @@
     "dont_ask_again": "常に許可する"
   },
   "modal_resolve_conflict": {
-    "title": "衝突を解消する",
+    "file_conflicting_with_newer_remote": "サーバー側の新しいファイルと衝突します。",
+    "resolve_conflict": "衝突を解消する",
     "resolve_and_save" : "解消し保存する"
   },
   "link_edit": {

+ 2 - 1
packages/app/resource/locales/zh_CN/translation.json

@@ -439,7 +439,8 @@
     "dont_ask_again": "不要再问"
   },
   "modal_resolve_conflict": {
-    "title": "解决冲突",
+    "file_conflicting_with_newer_remote": "此文件与较新的远程文件冲突",
+    "resolve_conflict": "解决冲突",
     "resolve_and_save" : "解决冲突并保存"
   },
   "link_edit": {

+ 2 - 0
packages/app/src/client/services/PageContainer.js

@@ -91,6 +91,8 @@ export default class PageContainer extends Container {
       pageIdOnHackmd: mainContent.getAttribute('data-page-id-on-hackmd') || null,
       hasDraftOnHackmd: !!mainContent.getAttribute('data-page-has-draft-on-hackmd'),
       isHackmdDraftUpdatingInRealtime: false,
+      isConflictingOnSave: false,
+      isConflictDiffModalOpen: false,
     };
 
     // parse creator, lastUpdateUser and revisionAuthor

+ 1 - 1
packages/app/src/components/PageEditor/ConflictDiffModal.tsx

@@ -55,7 +55,7 @@ export const ConflictDiffModal: FC<ConflictDiffModalProps> = (props) => {
   return (
     <Modal isOpen={props.isOpen || false} toggle={onCancel} className="modal-gfm-cheatsheet">
       <ModalHeader tag="h4" toggle={onCancel} className="bg-primary text-light">
-        <i className="icon-fw icon-exclamation" />{t('modal_resolve_conflict.title')}
+        <i className="icon-fw icon-exclamation" />{t('modal_resolve_conflict.resolve_conflict')}
       </ModalHeader>
       <ModalBody>
         <div ref={(el) => { setCodeMirrorRef(el) }}></div>

+ 5 - 4
packages/app/src/components/PageEditor/Editor.jsx

@@ -10,6 +10,7 @@ import {
 import Dropzone from 'react-dropzone';
 
 import EditorContainer from '~/client/services/EditorContainer';
+import PageContainer from '~/client/services/PageContainer';
 import { withUnstatedContainers } from '../UnstatedUtils';
 
 import Cheatsheet from './Cheatsheet';
@@ -30,7 +31,6 @@ class Editor extends AbstractEditor {
       dropzoneActive: false,
       isUploading: false,
       isCheatsheetModalShown: false,
-      isConflictDiffModalOpen: false,
     };
 
     this.getEditorSubstance = this.getEditorSubstance.bind(this);
@@ -372,8 +372,8 @@ class Editor extends AbstractEditor {
 
         </div>
         <ConflictDiffModal
-          isOpen={this.state.isConflictDiffModalOpen}
-          onCancel={() => this.setState({ isConflictDiffModalOpen: false })}
+          isOpen={this.props.pageContainer.state.isConflictDiffModalOpen}
+          onCancel={() => this.props.pageContainer.setState({ isConflictDiffModalOpen: false })}
           onResolveConflict={() => {}}
         />
       </>
@@ -391,6 +391,7 @@ Editor.propTypes = Object.assign({
   onChange: PropTypes.func,
   onUpload: PropTypes.func,
   editorContainer: PropTypes.instanceOf(EditorContainer).isRequired,
+  pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
 }, AbstractEditor.propTypes);
 
-export default withUnstatedContainers(Editor, [EditorContainer]);
+export default withUnstatedContainers(Editor, [EditorContainer, PageContainer]);

+ 28 - 2
packages/app/src/components/PageStatusAlert.jsx

@@ -26,6 +26,7 @@ class PageStatusAlert extends React.Component {
     };
 
     this.getContentsForSomeoneEditingAlert = this.getContentsForSomeoneEditingAlert.bind(this);
+    this.getContentsForRevisionOutdated = this.getContentsForRevisionOutdated.bind(this);
     this.getContentsForDraftExistsAlert = this.getContentsForDraftExistsAlert.bind(this);
     this.getContentsForUpdatedAlert = this.getContentsForUpdatedAlert.bind(this);
   }
@@ -49,6 +50,27 @@ class PageStatusAlert extends React.Component {
     ];
   }
 
+  getContentsForRevisionOutdated() {
+    const { t, pageContainer } = this.props;
+    return [
+      ['bg-warning', 'd-hackmd-none'],
+      <>
+        <i className="icon-fw icon-pencil"></i>
+        {t('modal_resolve_conflict.file_conflicting_with_newer_remote')}
+      </>,
+      <>
+        <button type="button" onClick={() => { }} className="btn btn-outline-white mr-4">
+          <i className="icon-fw icon-reload mr-1"></i>
+          Reload
+        </button>
+        <button type="button" onClick={() => pageContainer.setState({ isConflictDiffModalOpen: true })} className="btn btn-outline-white">
+          <i className="fa fa-fw fa-file-text-o mr-1"></i>
+          {t('modal_resolve_conflict.resolve_conflict')}
+        </button>
+      </>,
+    ];
+  }
+
   getContentsForDraftExistsAlert(isRealtime) {
     const { t } = this.props;
     return [
@@ -84,7 +106,7 @@ class PageStatusAlert extends React.Component {
 
   render() {
     const {
-      revisionId, revisionIdHackmdSynced, remoteRevisionId, hasDraftOnHackmd, isHackmdDraftUpdatingInRealtime,
+      revisionId, revisionIdHackmdSynced, remoteRevisionId, hasDraftOnHackmd, isHackmdDraftUpdatingInRealtime, isConflictingOnSave,
     } = this.props.pageContainer.state;
 
     const isRevisionOutdated = revisionId !== remoteRevisionId;
@@ -92,8 +114,12 @@ class PageStatusAlert extends React.Component {
 
     let getContentsFunc = null;
 
+    // when conflicting on save
+    if (isConflictingOnSave) {
+      getContentsFunc = this.getContentsForRevisionOutdated;
+    }
     // when remote revision is newer than both
-    if (isHackmdDocumentOutdated && isRevisionOutdated) {
+    else if (isHackmdDocumentOutdated && isRevisionOutdated) {
       getContentsFunc = this.getContentsForUpdatedAlert;
     }
     // when someone editing with HackMD

+ 5 - 3
packages/app/src/components/SavePageControls.jsx

@@ -47,11 +47,13 @@ class SavePageControls extends React.Component {
       await pageContainer.saveAndReload(editorContainer.getCurrentOptionsToSave());
     }
     catch (error) {
-      // TODO: display resolve conflict button when operation to update page is conflicted
-      // ref: https://estoc.weseek.co.jp/redmine/issues/78784
-      console.log(error.data);
       logger.error('failed to save', error);
       pageContainer.showErrorToastr(error);
+      if (error.code === 'conflict') {
+        pageContainer.setState({
+          isConflictingOnSave: true,
+        });
+      }
     }
   }