Răsfoiți Sursa

move conflict revisions from pagestatusalert to conflictdiffmodal and change user field from remote

yuto-oweseek 4 ani în urmă
părinte
comite
6eca4f5039

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

@@ -91,7 +91,6 @@ export default class PageContainer extends Container {
       remoteRevisionUpdateAt: null,
       remoteRevisionUpdateAt: null,
       revisionIdHackmdSynced: mainContent.getAttribute('data-page-revision-id-hackmd-synced') || null,
       revisionIdHackmdSynced: mainContent.getAttribute('data-page-revision-id-hackmd-synced') || null,
       lastUpdateUsername: mainContent.getAttribute('data-page-last-update-username') || null,
       lastUpdateUsername: mainContent.getAttribute('data-page-last-update-username') || null,
-      lastUpdateUserImagePath:  mainContent.getAttribute('data-page-updated-at') || null,
       deleteUsername: mainContent.getAttribute('data-page-delete-username') || null,
       deleteUsername: mainContent.getAttribute('data-page-delete-username') || null,
       pageIdOnHackmd: mainContent.getAttribute('data-page-id-on-hackmd') || null,
       pageIdOnHackmd: mainContent.getAttribute('data-page-id-on-hackmd') || null,
       hasDraftOnHackmd: !!mainContent.getAttribute('data-page-has-draft-on-hackmd'),
       hasDraftOnHackmd: !!mainContent.getAttribute('data-page-has-draft-on-hackmd'),
@@ -110,6 +109,7 @@ export default class PageContainer extends Container {
     }
     }
     try {
     try {
       this.state.revisionAuthor = JSON.parse(mainContent.getAttribute('data-page-revision-author'));
       this.state.revisionAuthor = JSON.parse(mainContent.getAttribute('data-page-revision-author'));
+      this.state.lastUpdateUser = JSON.parse(mainContent.getAttribute('data-page-revision-author'));
     }
     }
     catch (e) {
     catch (e) {
       logger.warn('The data of \'data-page-revision-author\' is invalid', e);
       logger.warn('The data of \'data-page-revision-author\' is invalid', e);
@@ -368,8 +368,9 @@ export default class PageContainer extends Container {
       remoteRevisionBody: s2cMessagePageUpdated.revisionBody,
       remoteRevisionBody: s2cMessagePageUpdated.revisionBody,
       remoteRevisionUpdateAt: s2cMessagePageUpdated.revisionUpdateAt,
       remoteRevisionUpdateAt: s2cMessagePageUpdated.revisionUpdateAt,
       revisionIdHackmdSynced: s2cMessagePageUpdated.revisionIdHackmdSynced,
       revisionIdHackmdSynced: s2cMessagePageUpdated.revisionIdHackmdSynced,
+      // TODO // TODO remove lastUpdateUsername and refactor parts that lastUpdateUsername is used
       lastUpdateUsername: s2cMessagePageUpdated.lastUpdateUsername,
       lastUpdateUsername: s2cMessagePageUpdated.lastUpdateUsername,
-      lastUpdateUserImagePath: s2cMessagePageUpdated.lastUpdateUserImagePath,
+      lastUpdateUser: s2cMessagePageUpdated.remoteLastUpdateUser,
     };
     };
 
 
     if (s2cMessagePageUpdated.hasDraftOnHackmd != null) {
     if (s2cMessagePageUpdated.hasDraftOnHackmd != null) {

+ 140 - 109
packages/app/src/components/PageEditor/ConflictDiffModal.tsx

@@ -4,10 +4,14 @@ import {
   Modal, ModalHeader, ModalBody, ModalFooter,
   Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
 } from 'reactstrap';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
+import { format } from 'date-fns';
 // TODO: consider whether to use codemirrorEditor
 // TODO: consider whether to use codemirrorEditor
 import { UnControlled as CodeMirrorAny } from 'react-codemirror2';
 import { UnControlled as CodeMirrorAny } from 'react-codemirror2';
+import { UserPicture } from '@growi/ui';
 import PageContainer from '../../client/services/PageContainer';
 import PageContainer from '../../client/services/PageContainer';
 import EditorContainer from '../../client/services/EditorContainer';
 import EditorContainer from '../../client/services/EditorContainer';
+import AppContainer from '../../client/services/AppContainer';
+import { IRevisionOnConflict } from '../../interfaces/revision';
 
 
 require('codemirror/mode/htmlmixed/htmlmixed');
 require('codemirror/mode/htmlmixed/htmlmixed');
 const DMP = require('diff_match_patch');
 const DMP = require('diff_match_patch');
@@ -22,15 +26,43 @@ type ConflictDiffModalProps = {
   onCancel: (() => void) | null;
   onCancel: (() => void) | null;
   pageContainer: PageContainer;
   pageContainer: PageContainer;
   editorContainer: EditorContainer;
   editorContainer: EditorContainer;
+  appContainer: AppContainer;
 };
 };
 
 
+type IRevisionOnConflictWithStringDate = Omit<IRevisionOnConflict, 'createdAt'> & {
+  createdAt: string
+}
+
 export const ConflictDiffModal: FC<ConflictDiffModalProps> = (props) => {
 export const ConflictDiffModal: FC<ConflictDiffModalProps> = (props) => {
   const { t } = useTranslation('');
   const { t } = useTranslation('');
   const resolvedRevision = useRef<string>('');
   const resolvedRevision = useRef<string>('');
   const [isRevisionselected, setIsRevisionSelected] = useState<boolean>(false);
   const [isRevisionselected, setIsRevisionSelected] = useState<boolean>(false);
 
 
-  const { pageContainer, editorContainer } = props;
-  const { request, origin, latest } = pageContainer.state.revisionsOnConflict || { request: {}, origin: {}, latest: {} };
+  const { pageContainer, editorContainer, appContainer } = props;
+
+  const pageEditor = appContainer.getComponentInstance('PageEditor');
+  const markdownOnEdit: string = pageEditor.getMarkdown();
+
+  const currentTime: Date = new Date();
+
+  const request: IRevisionOnConflictWithStringDate = {
+    revisionId: '',
+    revisionBody: markdownOnEdit,
+    createdAt: format(currentTime, 'yyyy/MM/dd HH:mm:ss'),
+    user: appContainer.currentUser,
+  };
+  const origin: IRevisionOnConflictWithStringDate = {
+    revisionId: pageContainer.state.revisionId || '',
+    revisionBody: pageContainer.state.markdown || '',
+    createdAt: pageContainer.state.updatedAt || '',
+    user: pageContainer.state.revisionAuthor,
+  };
+  const latest: IRevisionOnConflictWithStringDate = {
+    revisionId: pageContainer.state.remoteRevisionId || '',
+    revisionBody: pageContainer.state.remoteRevisionBody || '',
+    createdAt: format(new Date(pageContainer.state.remoteRevisionUpdateAt || currentTime.toString()), 'yyyy/MM/dd HH:mm:ss'),
+    user: pageContainer.state.lastUpdateUser,
+  };
 
 
   const codeMirrorRevisionOption = {
   const codeMirrorRevisionOption = {
     mode: 'htmlmixed',
     mode: 'htmlmixed',
@@ -69,119 +101,117 @@ export const ConflictDiffModal: FC<ConflictDiffModalProps> = (props) => {
         <i className="icon-fw icon-exclamation" />{t('modal_resolve_conflict.resolve_conflict')}
         <i className="icon-fw icon-exclamation" />{t('modal_resolve_conflict.resolve_conflict')}
       </ModalHeader>
       </ModalHeader>
       <ModalBody>
       <ModalBody>
-        {Object.keys(pageContainer.state.revisionsOnConflict || {}).length > 0
-          && (
-            <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 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 />
               </div>
               </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>
-                    <img height="40px" className="rounded-circle" src={request.userImgPath} />
-                  </div>
-                  <div className="ml-3 text-muted">
-                    <p className="my-0">updated by {request.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 className="ml-3 text-muted">
+                <p className="my-0">updated by {request.user.username}</p>
+                <p className="my-0">{request.createdAt}</p>
               </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>
-                    <img height="40px" className="rounded-circle" src={origin.userImgPath} />
-                  </div>
-                  <div className="ml-3 text-muted">
-                    <p className="my-0">updated by {origin.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);
-                      resolvedRevision.current = origin.revisionBody;
-                    }}
-                  >
-                    <i className="icon-fw icon-arrow-down-circle"></i>
-                    {t('modal_resolve_conflict.select_revision', { revision: 'origin' })}
-                  </button>
-                </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>
+              <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>
-              <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>
-                    <img height="40px" className="rounded-circle" src={latest.userImgPath} />
-                  </div>
-                  <div className="ml-3 text-muted">
-                    <p className="my-0">updated by {latest.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>
+            <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>
-              <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 className="ml-3 text-muted">
+                <p className="my-0">updated by {latest.user.username}</p>
+                <p className="my-0">{latest.createdAt}</p>
               </div>
               </div>
             </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>
       </ModalBody>
       <ModalFooter>
       <ModalFooter>
         <button
         <button
@@ -209,6 +239,7 @@ ConflictDiffModal.propTypes = {
   onCancel: PropTypes.func,
   onCancel: PropTypes.func,
   pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
   pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
   editorContainer:  PropTypes.instanceOf(EditorContainer).isRequired,
   editorContainer:  PropTypes.instanceOf(EditorContainer).isRequired,
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
 };
 };
 
 
 ConflictDiffModal.defaultProps = {
 ConflictDiffModal.defaultProps = {

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

@@ -11,6 +11,7 @@ import Dropzone from 'react-dropzone';
 
 
 import EditorContainer from '~/client/services/EditorContainer';
 import EditorContainer from '~/client/services/EditorContainer';
 import PageContainer from '~/client/services/PageContainer';
 import PageContainer from '~/client/services/PageContainer';
+import AppContainer from '~/client/services/AppContainer';
 import { withUnstatedContainers } from '../UnstatedUtils';
 import { withUnstatedContainers } from '../UnstatedUtils';
 
 
 import Cheatsheet from './Cheatsheet';
 import Cheatsheet from './Cheatsheet';
@@ -374,6 +375,7 @@ class Editor extends AbstractEditor {
         <ConflictDiffModal
         <ConflictDiffModal
           isOpen={this.props.pageContainer.state.isConflictDiffModalOpen}
           isOpen={this.props.pageContainer.state.isConflictDiffModalOpen}
           onCancel={() => this.props.pageContainer.setState({ isConflictDiffModalOpen: false })}
           onCancel={() => this.props.pageContainer.setState({ isConflictDiffModalOpen: false })}
+          appContainer={this.props.appContainer}
           pageContainer={this.props.pageContainer}
           pageContainer={this.props.pageContainer}
           editorContainer={this.props.editorContainer}
           editorContainer={this.props.editorContainer}
         />
         />
@@ -393,6 +395,7 @@ Editor.propTypes = Object.assign({
   onUpload: PropTypes.func,
   onUpload: PropTypes.func,
   editorContainer: PropTypes.instanceOf(EditorContainer).isRequired,
   editorContainer: PropTypes.instanceOf(EditorContainer).isRequired,
   pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
   pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
 }, AbstractEditor.propTypes);
 }, AbstractEditor.propTypes);
 
 
-export default withUnstatedContainers(Editor, [EditorContainer, PageContainer]);
+export default withUnstatedContainers(Editor, [EditorContainer, PageContainer, AppContainer]);

+ 1 - 31
packages/app/src/components/PageStatusAlert.jsx

@@ -38,39 +38,9 @@ class PageStatusAlert extends React.Component {
   }
   }
 
 
   onClickResolveConflict() {
   onClickResolveConflict() {
-
-    const { pageContainer, appContainer } = this.props;
-
-    const pageEditor = appContainer.getComponentInstance('PageEditor');
-    const markdownOnEdit = pageEditor.getMarkdown();
-
-    pageContainer.setState({
+    this.props.pageContainer.setState({
       isConflictDiffModalOpen: true,
       isConflictDiffModalOpen: true,
-      revisionsOnConflict: {
-        request: {
-          revisionId: '',
-          revisionBody: markdownOnEdit,
-          createdAt: format(new Date(), 'yyyy/MM/dd HH:mm:ss'),
-          userName: this.props.appContainer.currentUser.username,
-          userImgPath: this.props.appContainer.currentUser.imageUrlCached,
-        },
-        origin: {
-          revisionId: pageContainer.state.revisionId,
-          revisionBody: pageContainer.state.markdown,
-          createdAt: pageContainer.state.updatedAt,
-          userName: pageContainer.state.creator.username,
-          userImgPath: pageContainer.state.creator.imageUrlCached,
-        },
-        latest: {
-          revisionId: pageContainer.state.remoteRevisionId,
-          revisionBody: pageContainer.state.remoteRevisionBody,
-          createdAt: format(new Date(pageContainer.state.remoteRevisionUpdateAt), 'yyyy/MM/dd HH:mm:ss'),
-          userName: pageContainer.state.lastUpdateUsername,
-          userImgPath: pageContainer.state.lastUpdateUserImagePath,
-        },
-      },
     });
     });
-
   }
   }
 
 
   getContentsForSomeoneEditingAlert() {
   getContentsForSomeoneEditingAlert() {

+ 7 - 0
packages/app/src/interfaces/revision.ts

@@ -7,3 +7,10 @@ export type IRevision = {
   createdAt: Date,
   createdAt: Date,
   updatedAt: Date,
   updatedAt: Date,
 }
 }
+
+export type IRevisionOnConflict = {
+  revisionId: string,
+  revisionBody: string,
+  createdAt: Date,
+  user: IUser
+}

+ 2 - 1
packages/app/src/server/models/vo/s2c-message.js

@@ -21,8 +21,9 @@ class S2cMessagePageUpdated {
     this.hasDraftOnHackmd = hasDraftOnHackmd;
     this.hasDraftOnHackmd = hasDraftOnHackmd;
 
 
     if (user != null) {
     if (user != null) {
+      this.remoteLastUpdateUser = user;
+      // TODO remove lastUpdateUsername and refactor parts that lastUpdateUsername is used
       this.lastUpdateUsername = user.name;
       this.lastUpdateUsername = user.name;
-      this.lastUpdateUserImagePath = user.imageUrlCached;
     }
     }
   }
   }