import React, { useState, useRef, useEffect, FC, } from 'react'; import PropTypes from 'prop-types'; import { UserPicture } from '@growi/ui'; import { Modal, ModalHeader, ModalBody, ModalFooter, } from 'reactstrap'; import { useTranslation } from 'react-i18next'; import { format } from 'date-fns'; import CodeMirror from 'codemirror/lib/codemirror'; import PageContainer from '../../client/services/PageContainer'; import EditorContainer from '../../client/services/EditorContainer'; import AppContainer from '../../client/services/AppContainer'; import { IRevisionOnConflict } from '../../interfaces/revision'; import { UncontrolledCodeMirror } from '../UncontrolledCodeMirror'; require('codemirror/lib/codemirror.css'); require('codemirror/addon/merge/merge'); require('codemirror/addon/merge/merge.css'); const DMP = require('diff_match_patch'); Object.keys(DMP).forEach((key) => { window[key] = DMP[key] }); type ConflictDiffModalProps = { isOpen: boolean | null; onCancel: (() => void) | null; pageContainer: PageContainer; editorContainer: EditorContainer; appContainer: AppContainer; markdownOnEdit: string; }; type IRevisionOnConflictWithStringDate = Omit & { createdAt: string } export const ConflictDiffModal: FC = (props) => { const { t } = useTranslation(''); const resolvedRevision = useRef(''); const [isRevisionselected, setIsRevisionSelected] = useState(false); const [codeMirrorRef, setCodeMirrorRef] = useState(null); const { pageContainer, editorContainer, appContainer } = props; const currentTime: Date = new Date(); const request: IRevisionOnConflictWithStringDate = { revisionId: '', revisionBody: props.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, }; useEffect(() => { if (codeMirrorRef != null) { CodeMirror.MergeView(codeMirrorRef, { value: origin.revisionBody, origLeft: request.revisionBody, origRight: latest.revisionBody, lineNumbers: true, collapseIdentical: true, showDifferences: true, highlightDifferences: true, connect: 'connect', readOnly: true, revertButtons: false, }); } }, [codeMirrorRef, origin.revisionBody, request.revisionBody, latest.revisionBody]); const onCancel = () => { if (props.onCancel != null) { props.onCancel(); } }; const onResolveConflict = async() : Promise => { // disable button after clicked setIsRevisionSelected(false); editorContainer.disableUnsavedWarning(); try { await pageContainer.resolveConflictAndReload( pageContainer.state.pageId, latest.revisionId, resolvedRevision.current, editorContainer.getCurrentOptionsToSave(), ); } catch (error) { pageContainer.showErrorToastr(error); } }; return ( {t('modal_resolve_conflict.resolve_conflict')} { pageContainer.state.isConflictDiffModalOpen && (

{t('modal_resolve_conflict.resolve_conflict_message')}

{t('modal_resolve_conflict.requested_revision')}

updated by {request.user.username}

{request.createdAt}

{t('modal_resolve_conflict.origin_revision')}

updated by {origin.user.username}

{origin.createdAt}

{t('modal_resolve_conflict.latest_revision')}

updated by {latest.user.username}

{latest.createdAt}

{ setCodeMirrorRef(el) }}>

{t('modal_resolve_conflict.selected_editable_revision')}

{ if (pageBody === '') setIsRevisionSelected(false); resolvedRevision.current = pageBody; }} />
)}
); }; ConflictDiffModal.propTypes = { isOpen: PropTypes.bool, onCancel: PropTypes.func, pageContainer: PropTypes.instanceOf(PageContainer).isRequired, editorContainer: PropTypes.instanceOf(EditorContainer).isRequired, appContainer: PropTypes.instanceOf(AppContainer).isRequired, markdownOnEdit: PropTypes.string.isRequired, }; ConflictDiffModal.defaultProps = { isOpen: false, };