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

retrieve data from server side on conflict

yuto-oweseek 4 лет назад
Родитель
Сommit
6420ae4382

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

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

+ 110 - 106
packages/app/src/components/PageEditor/ConflictDiffModal.tsx

@@ -41,7 +41,6 @@ export const ConflictDiffModal: FC<ConflictDiffModalProps> = (props) => {
 
   const { pageContainer, editorContainer, appContainer } = props;
 
-
   const currentTime: Date = new Date();
 
   const request: IRevisionOnConflictWithStringDate = {
@@ -100,117 +99,122 @@ export const ConflictDiffModal: FC<ConflictDiffModalProps> = (props) => {
         <i className="icon-fw icon-exclamation" />{t('modal_resolve_conflict.resolve_conflict')}
       </ModalHeader>
       <ModalBody>
-        <div className="row mx-2">
-          <div className="col-12 text-center mt-2 mb-4">
-            <h2 className="font-weight-bold">{t('modal_resolve_conflict.resolve_conflict_message')}</h2>
-          </div>
-          <div className="col-12 col-md-4 border border-dark">
-            <h3 className="font-weight-bold my-2">{t('modal_resolve_conflict.requested_revision')}</h3>
-            <div className="d-flex align-items-center my-3">
-              <div>
-                <UserPicture user={request.user} size="lg" noLink noTooltip />
+        {
+          pageContainer.state.isConflictDiffModalOpen
+          && (
+            <div className="row mx-2">
+              <div className="col-12 text-center mt-2 mb-4">
+                <h2 className="font-weight-bold">{t('modal_resolve_conflict.resolve_conflict_message')}</h2>
               </div>
-              <div className="ml-3 text-muted">
-                <p className="my-0">updated by {request.user.username}</p>
-                <p className="my-0">{request.createdAt}</p>
+              <div className="col-12 col-md-4 border border-dark">
+                <h3 className="font-weight-bold my-2">{t('modal_resolve_conflict.requested_revision')}</h3>
+                <div className="d-flex align-items-center my-3">
+                  <div>
+                    <UserPicture user={request.user} size="lg" noLink noTooltip />
+                  </div>
+                  <div className="ml-3 text-muted">
+                    <p className="my-0">updated by {request.user.username}</p>
+                    <p className="my-0">{request.createdAt}</p>
+                  </div>
+                </div>
+                <CodeMirror
+                  value={request.revisionBody}
+                  options={codeMirrorRevisionOption}
+                />
+                <div className="text-center my-4">
+                  <button
+                    type="button"
+                    className="btn btn-primary"
+                    onClick={() => {
+                      setIsRevisionSelected(true);
+                      resolvedRevision.current = request.revisionBody;
+                    }}
+                  >
+                    <i className="icon-fw icon-arrow-down-circle"></i>
+                    {t('modal_resolve_conflict.select_revision', { revision: 'request' })}
+                  </button>
+                </div>
               </div>
-            </div>
-            <CodeMirror
-              value={request.revisionBody}
-              options={codeMirrorRevisionOption}
-            />
-            <div className="text-center my-4">
-              <button
-                type="button"
-                className="btn btn-primary"
-                onClick={() => {
-                  setIsRevisionSelected(true);
-                  resolvedRevision.current = request.revisionBody;
-                }}
-              >
-                <i className="icon-fw icon-arrow-down-circle"></i>
-                {t('modal_resolve_conflict.select_revision', { revision: 'request' })}
-              </button>
-            </div>
-          </div>
-          <div className="col-12 col-md-4 border border-dark">
-            <h3 className="font-weight-bold my-2">{t('origin_revision')}</h3>
-            <div className="d-flex align-items-center my-3">
-              <div>
-                <UserPicture user={origin.user} size="lg" noLink noTooltip />
+              <div className="col-12 col-md-4 border border-dark">
+                <h3 className="font-weight-bold my-2">{t('origin_revision')}</h3>
+                <div className="d-flex align-items-center my-3">
+                  <div>
+                    <UserPicture user={origin.user} size="lg" noLink noTooltip />
+                  </div>
+                  <div className="ml-3 text-muted">
+                    <p className="my-0">updated by {origin.user.username}</p>
+                    <p className="my-0">{origin.createdAt}</p>
+                  </div>
+                </div>
+                <CodeMirror
+                  value={origin.revisionBody}
+                  options={codeMirrorRevisionOption}
+                />
+                <div className="text-center my-4">
+                  <button
+                    type="button"
+                    className="btn btn-primary"
+                    onClick={() => {
+                      setIsRevisionSelected(true);
+                      if (resolvedRevision != null) {
+                        resolvedRevision.current = origin.revisionBody;
+                      }
+                    }}
+                  >
+                    <i className="icon-fw icon-arrow-down-circle"></i>
+                    {t('modal_resolve_conflict.select_revision', { revision: 'origin' })}
+                  </button>
+                </div>
               </div>
-              <div className="ml-3 text-muted">
-                <p className="my-0">updated by {origin.user.username}</p>
-                <p className="my-0">{origin.createdAt}</p>
+              <div className="col-12 col-md-4 border border-dark">
+                <h3 className="font-weight-bold my-2">{t('modal_resolve_conflict.latest_revision')}</h3>
+                <div className="d-flex align-items-center my-3">
+                  <div>
+                    <UserPicture user={latest.user} size="lg" noLink noTooltip />
+                  </div>
+                  <div className="ml-3 text-muted">
+                    <p className="my-0">updated by {latest.user.username}</p>
+                    <p className="my-0">{latest.createdAt}</p>
+                  </div>
+                </div>
+                <CodeMirror
+                  value={latest.revisionBody}
+                  options={codeMirrorRevisionOption}
+                />
+                <div className="text-center my-4">
+                  <button
+                    type="button"
+                    className="btn btn-primary"
+                    onClick={() => {
+                      setIsRevisionSelected(true);
+                      resolvedRevision.current = latest.revisionBody;
+                    }}
+                  >
+                    <i className="icon-fw icon-arrow-down-circle"></i>
+                    {t('modal_resolve_conflict.select_revision', { revision: 'latest' })}
+                  </button>
+                </div>
               </div>
-            </div>
-            <CodeMirror
-              value={origin.revisionBody}
-              options={codeMirrorRevisionOption}
-            />
-            <div className="text-center my-4">
-              <button
-                type="button"
-                className="btn btn-primary"
-                onClick={() => {
-                  setIsRevisionSelected(true);
-                  if (resolvedRevision != null) {
-                    resolvedRevision.current = origin.revisionBody;
-                  }
-                }}
-              >
-                <i className="icon-fw icon-arrow-down-circle"></i>
-                {t('modal_resolve_conflict.select_revision', { revision: 'origin' })}
-              </button>
-            </div>
-          </div>
-          <div className="col-12 col-md-4 border border-dark">
-            <h3 className="font-weight-bold my-2">{t('modal_resolve_conflict.latest_revision')}</h3>
-            <div className="d-flex align-items-center my-3">
-              <div>
-                <UserPicture user={latest.user} size="lg" noLink noTooltip />
-              </div>
-              <div className="ml-3 text-muted">
-                <p className="my-0">updated by {latest.user.username}</p>
-                <p className="my-0">{latest.createdAt}</p>
+              <div className="col-12 border border-dark">
+                <h3 className="font-weight-bold my-2">{t('modal_resolve_conflict.selected_editable_revision')}</h3>
+                <CodeMirror
+                  value={resolvedRevision.current}
+                  options={{
+                    mode: 'htmlmixed',
+                    lineNumbers: true,
+                    tabSize: 2,
+                    indentUnit: 2,
+                    placeholder: t('modal_resolve_conflict.resolve_conflict_message'),
+                  }}
+                  onChange={(editor, data, pageBody) => {
+                    if (pageBody === '') setIsRevisionSelected(false);
+                    resolvedRevision.current = pageBody;
+                  }}
+                />
               </div>
             </div>
-            <CodeMirror
-              value={latest.revisionBody}
-              options={codeMirrorRevisionOption}
-            />
-            <div className="text-center my-4">
-              <button
-                type="button"
-                className="btn btn-primary"
-                onClick={() => {
-                  setIsRevisionSelected(true);
-                  resolvedRevision.current = latest.revisionBody;
-                }}
-              >
-                <i className="icon-fw icon-arrow-down-circle"></i>
-                {t('modal_resolve_conflict.select_revision', { revision: 'latest' })}
-              </button>
-            </div>
-          </div>
-          <div className="col-12 border border-dark">
-            <h3 className="font-weight-bold my-2">{t('modal_resolve_conflict.selected_editable_revision')}</h3>
-            <CodeMirror
-              value={resolvedRevision.current}
-              options={{
-                mode: 'htmlmixed',
-                lineNumbers: true,
-                tabSize: 2,
-                indentUnit: 2,
-                placeholder: t('modal_resolve_conflict.resolve_conflict_message'),
-              }}
-              onChange={(editor, data, pageBody) => {
-                if (pageBody === '') setIsRevisionSelected(false);
-                resolvedRevision.current = pageBody;
-              }}
-            />
-          </div>
-        </div>
+          )
+        }
       </ModalBody>
       <ModalFooter>
         <button

+ 8 - 0
packages/app/src/components/SavePageControls.jsx

@@ -49,6 +49,14 @@ class SavePageControls extends React.Component {
     catch (error) {
       logger.error('failed to save', error);
       pageContainer.showErrorToastr(error);
+      if (error.code === 'conflict') {
+        pageContainer.setState({
+          remoteRevisionId: error.data.revisionId,
+          remoteRevisionBody: error.data.revisionBody,
+          remoteRevisionUpdateAt: error.data.createdAt,
+          lastUpdateUser: error.data.user,
+        });
+      }
     }
   }
 

+ 8 - 1
packages/app/src/server/routes/page.js

@@ -831,7 +831,14 @@ module.exports = function(crowi, app) {
     const Revision = crowi.model('Revision');
     let page = await Page.findByIdAndViewer(pageId, req.user);
     if (page != null && revisionId != null && !page.isUpdatable(revisionId)) {
-      return res.json(ApiResponse.error('Posted param "revisionId" is outdated.', 'conflict'));
+      const latestRevision = await Revision.findById(page.revision).populate('author');
+      const returnLatestRevision = {
+        revisionId: latestRevision._id.toString(),
+        revisionBody:  xss.process(latestRevision.body),
+        createdAt: latestRevision.createdAt,
+        user: serializeUserSecurely(latestRevision.author),
+      };
+      return res.json(ApiResponse.error('Posted param "revisionId" is outdated.', 'conflict', returnLatestRevision));
     }
 
     const options = { isSyncRevisionToHackmd };