1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-07-14 07:24:58 +00:00
seahub/frontend/src/pages/markdown-editor/seafile-editor/index.js
2022-05-20 16:26:53 +08:00

263 lines
8.1 KiB
JavaScript

import React from 'react';
import PropTypes from 'prop-types';
import { deserialize, serialize, PlainMarkdownEditor } from '@seafile/seafile-editor';
import toaster from '../../../components/toast';
import { gettext } from '../../../utils/constants';
import RichEditor from '../rich-editor';
const propTypes = {
mode: PropTypes.string,
editorMode: PropTypes.string,
readOnly: PropTypes.bool,
isDraft: PropTypes.bool,
scriptSource: PropTypes.string,
markdownContent: PropTypes.string,
editorApi: PropTypes.object.isRequired,
collaUsers: PropTypes.array,
onContentChanged: PropTypes.func.isRequired,
onSaving: PropTypes.func.isRequired,
saving: PropTypes.bool,
fileTagList: PropTypes.array,
onFileTagChanged: PropTypes.func.isRequired,
participants: PropTypes.array.isRequired,
onParticipantsChange: PropTypes.func.isRequired,
markdownLint: PropTypes.bool,
setFileInfoMtime: PropTypes.func.isRequired,
setEditorMode: PropTypes.func,
autoSaveDraft: PropTypes.func,
setDraftValue: PropTypes.func,
clearTimer: PropTypes.func,
deleteDraft: PropTypes.func,
contentChanged: PropTypes.bool,
openDialogs: PropTypes.func,
};
class SeafileEditor extends React.Component {
constructor(props) {
super(props);
const { mode, markdownContent, isDraft } = this.props;
const isEditMode = mode === 'editor' || isDraft;
const richValue = isEditMode ? deserialize(markdownContent) : deserialize('');
this.state = {
initialPlainValue: '',
currentContent: markdownContent,
richValue: richValue,
issues: { issue_list: []}
};
this.lastModifyTime = null;
this.autoSave = false;
this.isParticipant = false;
window.seafileEditor = this;
}
componentWillMount() {
if (this.props.editorMode === 'rich') {
const document = this.state.richValue;
const firstNode = document[0];
/**
* if the markdown content is empty, the rich value contains
* only a paragraph which contains a empty text node
*
*/
if (document.length === 1 &&
firstNode.type === 'paragraph' &&
firstNode.children.length === 1 &&
Text.isText(firstNode.children[0]) &&
firstNode.children[0].text.length === 0) {
let headerContent = this.props.fileInfo.name.slice(0, this.props.fileInfo.name.lastIndexOf('.'));
const header = {
type: 'header_one',
children: [{text: headerContent, marks: []}]
};
document.push(header);
document.shift();
this.setState({richValue: document});
}
}
}
componentDidMount() {
window.addEventListener('beforeunload', this.onUnload);
// notify current user if others are also editing this file
const { collabUsers } = this.props;
const editingUsers = collabUsers.filter(ele => ele.is_editing === true && ele.myself === undefined);
if (editingUsers.length > 0) {
const message = gettext('Another user is editing this file!');
toaster.danger(message, {duration: 3});
}
}
componentWillUnmount() {
window.removeEventListener('beforeunload', this.onUnload);
}
onUnload = (event) => {
if (!this.props.contentChanged) return;
const confirmationMessage = 'Leave this page? The system may not save your changes.';
this.props.clearTimer();
this.props.deleteDraft && this.props.deleteDraft();
event.returnValue = confirmationMessage;
return confirmationMessage;
};
switchToPlainTextEditor = () => {
// TODO: performance, change to do serialize in async way
if (this.props.editorMode === 'rich') {
const value = this.state.richValue;
const str = serialize(value);
this.props.setEditorMode('plain');
this.setState({
initialPlainValue: str,
currentContent: str
});
}
if (this.props.collabServer) {
this.props.emitSwitchEditor(false);
}
};
switchToRichTextEditor = () => {
// TODO: performance, change to do deserialize in async way
this.setState({richValue: deserialize(this.state.currentContent)});
this.props.setEditorMode('rich');
if (this.props.collabServer) {
this.props.emitSwitchEditor(false);
}
};
saveContent = (str) => {
this.props.onSaving(true);
this.props.editorApi.saveContent(str).then(() => {
this.props.onSaving(false);
this.props.onContentChanged(false);
// remove markdown lint temporarily
// if (this.props.markdownLint) {
// const slateValue = this.state.richValue;
// this.props.editorApi.markdownLint(JSON.stringify(slateValue)).then((res) => {
// this.setState({
// issues: res.data
// });
// });
// }
this.lastModifyTime = new Date();
const message = gettext('File Saved');
toaster.success(message, {duration: 2,});
this.props.editorApi.getFileInfo().then((res) => {
this.props.setFileInfoMtime(res.data);
});
this.addParticipants();
}, () => {
this.props.onSaving(false);
const message = gettext('File failed to save');
toaster.danger(message, {duration: 2});
});
};
onRichEditorSave = () => {
if (this.props.isSaving) return;
const value = this.state.richValue;
const str = serialize(value);
this.saveContent(str);
this.props.clearTimer();
this.props.deleteDraft && this.props.deleteDraft();
}
onPlainEditorSave = () => {
if (this.props.isSaving) return;
const str = this.state.currentContent;
this.saveContent(str);
this.props.clearTimer();
this.props.deleteDraft && this.props.deleteDraft();
}
resetRichValue = () => {
const value = this.state.richValue;
this.setState({ richValue: value });
}
onChange = (value, operations) => {
if (this.props.editorMode === 'rich') {
this.setState({richValue: value,});
this.props.setDraftValue('rich', this.state.richValue);
const ops = operations.filter(o => {
return o.type !== 'set_selection' && o.type !== 'set_value';
});
if (ops.length !== 0) {
this.props.onContentChanged(true);
if (this.autoSave) this.props.autoSaveDraft();
}
} else {
this.setState({currentContent: value});
this.props.onContentChanged(true);
this.props.setDraftValue('rich', this.state.richValue);
this.props.autoSaveDraft();
}
};
addParticipants = () => {
if (this.isParticipant || !window.showParticipants) return;
const { userName, addFileParticipants } = this.props.editorApi;
const { participants } = this.props;
if (participants && participants.length !== 0) {
this.isParticipant = participants.every((participant) => {
return participant.email === userName;
});
if (this.isParticipant) return;
}
let emails = [userName];
addFileParticipants(emails).then((res) => {
this.isParticipant = true;
this.props.onParticipantsChange();
});
}
render() {
if (this.props.editorMode === 'rich') {
return (
<RichEditor
scriptSource={this.props.scriptSource}
readOnly={this.props.readOnly}
value={this.state.richValue}
editorApi={this.props.editorApi}
fileInfo={this.props.fileInfo}
collaUsers={this.props.collaUsers}
onChange={this.onChange}
onSave={this.onRichEditorSave}
resetRichValue={this.resetRichValue}
fileTagList={this.props.fileTagList}
onFileTagChanged={this.props.onFileTagChanged}
participants={this.props.participants}
onParticipantsChange={this.props.onParticipantsChange}
openDialogs={this.props.openDialogs}
/>
);
}
return (
<PlainMarkdownEditor
scriptSource={this.props.scriptSource}
editorApi={this.props.editorApi}
initialValue={this.state.initialPlainValue}
currentContent={this.state.currentContent}
contentChanged={this.props.contentChanged}
fileInfo={this.props.fileInfo}
collabUsers={this.props.collabUsers}
onSave={this.onPlainEditorSave}
onChange={this.onChange}
/>
);
}
}
SeafileEditor.propTypes = propTypes;
export default SeafileEditor;