2019-04-13 11:54:14 +08:00
|
|
|
import React, { Fragment } from 'react';
|
2019-03-14 18:14:11 +08:00
|
|
|
import io from 'socket.io-client';
|
2022-05-20 16:26:53 +08:00
|
|
|
import { serialize, deserialize } from '@seafile/seafile-editor';
|
2022-05-13 11:19:16 +08:00
|
|
|
import { Utils } from '../../utils/utils';
|
|
|
|
import { seafileAPI } from '../../utils/seafile-api';
|
|
|
|
import { gettext, isDocs, mediaUrl } from '../../utils/constants';
|
|
|
|
import toaster from '../../components/toast';
|
|
|
|
import ShareDialog from '../../components/dialog/share-dialog';
|
|
|
|
import InsertFileDialog from '../../components/dialog/insert-file-dialog';
|
|
|
|
import LocalDraftDialog from '../../components/dialog/local-draft-dialog';
|
2022-05-13 13:23:54 +08:00
|
|
|
import HeaderToolbar from './header-toolbar';
|
2022-05-20 16:26:53 +08:00
|
|
|
import SeafileEditor from './seafile-editor';
|
2022-05-13 11:19:16 +08:00
|
|
|
import editorApi from './editor-api';
|
|
|
|
|
|
|
|
import '../../css/markdown-viewer/markdown-editor.css';
|
2019-03-11 21:08:25 +08:00
|
|
|
|
|
|
|
const CryptoJS = require('crypto-js');
|
2019-03-15 12:11:32 +08:00
|
|
|
const URL = require('url-parse');
|
2022-05-13 11:19:16 +08:00
|
|
|
const { repoID, filePath, fileName, draftID, isDraft, hasDraft, isLocked, lockedByMe } = window.app.pageOptions;
|
2019-02-25 18:17:04 +08:00
|
|
|
const { siteRoot, serviceUrl, seafileCollabServer } = window.app.config;
|
2018-07-27 11:48:21 +08:00
|
|
|
const userInfo = window.app.userInfo;
|
2022-01-18 21:29:45 +08:00
|
|
|
const IMAGE_SUFFIXES = ['png', 'PNG', 'jpg', 'JPG', 'jpeg', 'JPEG', 'gif', 'GIF'];
|
2018-05-02 14:09:58 +08:00
|
|
|
|
2018-09-21 14:16:15 +08:00
|
|
|
class MarkdownEditor extends React.Component {
|
2022-05-19 15:39:16 +08:00
|
|
|
|
2018-05-02 14:09:58 +08:00
|
|
|
constructor(props) {
|
2018-09-29 18:32:53 +08:00
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
markdownContent: '',
|
|
|
|
loading: true,
|
|
|
|
mode: 'editor',
|
|
|
|
fileInfo: {
|
|
|
|
repoID: repoID,
|
|
|
|
name: fileName,
|
|
|
|
path: filePath,
|
|
|
|
mtime: null,
|
|
|
|
size: 0,
|
|
|
|
starred: false,
|
|
|
|
permission: '',
|
|
|
|
lastModifier: '',
|
2018-12-29 16:28:04 +08:00
|
|
|
id: '',
|
2018-09-29 18:32:53 +08:00
|
|
|
},
|
2019-04-13 11:54:14 +08:00
|
|
|
editorMode: 'rich',
|
2018-09-29 18:32:53 +08:00
|
|
|
collabServer: seafileCollabServer ? seafileCollabServer : null,
|
2019-03-11 21:08:25 +08:00
|
|
|
localDraftDialog: false,
|
2019-01-16 11:49:00 +08:00
|
|
|
showMarkdownEditorDialog: false,
|
2019-02-25 18:17:04 +08:00
|
|
|
showShareLinkDialog: false,
|
2019-03-26 11:18:41 +08:00
|
|
|
showInsertFileDialog: false,
|
2019-03-11 21:08:25 +08:00
|
|
|
showDraftSaved: false,
|
|
|
|
collabUsers: userInfo ?
|
|
|
|
[{user: userInfo, is_editing: false}] : [],
|
2019-03-14 10:15:25 +08:00
|
|
|
value: null,
|
2019-03-15 12:11:32 +08:00
|
|
|
isShowHistory: false,
|
2019-04-13 11:54:14 +08:00
|
|
|
readOnly: true,
|
2019-04-13 14:51:41 +08:00
|
|
|
contentChanged: false,
|
|
|
|
saving: false,
|
2019-05-05 12:05:29 +08:00
|
|
|
isLocked: isLocked,
|
|
|
|
lockedByMe: lockedByMe,
|
2019-06-19 13:36:57 +08:00
|
|
|
fileTagList: [],
|
2019-06-26 14:56:03 +08:00
|
|
|
participants: [],
|
2018-09-29 18:32:53 +08:00
|
|
|
};
|
2019-03-11 21:08:25 +08:00
|
|
|
|
2022-05-19 15:39:16 +08:00
|
|
|
this.timer = null;
|
|
|
|
this.localDraft = '';
|
|
|
|
this.autoSave = false;
|
|
|
|
this.draftRichValue = '';
|
|
|
|
this.draftPlainValue = '';
|
|
|
|
|
2019-03-11 21:08:25 +08:00
|
|
|
if (this.state.collabServer) {
|
|
|
|
const socket = io(this.state.collabServer);
|
|
|
|
this.socket = socket;
|
|
|
|
socket.on('presence', (data) => this.receivePresenceData(data));
|
|
|
|
socket.on('repo_update', (data) => this.receiveUpdateData(data));
|
|
|
|
socket.on('connect', () => {
|
|
|
|
this.socket_id = socket.id;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-05 12:05:29 +08:00
|
|
|
toggleLockFile = () => {
|
|
|
|
const { repoID, path } = this.state.fileInfo;
|
|
|
|
if (this.state.isLocked) {
|
|
|
|
seafileAPI.unlockfile(repoID, path).then((res) => {
|
|
|
|
this.setState({ isLocked: false, lockedByMe: false });
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
seafileAPI.lockfile(repoID, path).then((res) => {
|
|
|
|
this.setState({ isLocked: true, lockedByMe: true });
|
|
|
|
});
|
|
|
|
}
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-05-05 12:05:29 +08:00
|
|
|
|
2019-03-11 21:08:25 +08:00
|
|
|
emitSwitchEditor = (is_editing=false) => {
|
|
|
|
if (userInfo && this.state.collabServer) {
|
|
|
|
const { repoID, path } = this.state.fileInfo;
|
|
|
|
this.socket.emit('presence', {
|
|
|
|
request: 'editing',
|
2019-03-14 18:14:11 +08:00
|
|
|
doc_id: CryptoJS.MD5(repoID+path).toString(),
|
|
|
|
user: userInfo,
|
|
|
|
is_editing,
|
2019-03-11 21:08:25 +08:00
|
|
|
});
|
|
|
|
}
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-03-11 21:08:25 +08:00
|
|
|
|
|
|
|
receiveUpdateData (data) {
|
|
|
|
let currentTime = new Date();
|
|
|
|
if ((parseFloat(currentTime - this.lastModifyTime)/1000) <= 5) {
|
|
|
|
return;
|
|
|
|
}
|
2020-06-30 21:10:47 +08:00
|
|
|
editorApi.fileMetaData().then((res) => {
|
2019-03-11 21:08:25 +08:00
|
|
|
if (res.data.id !== this.state.fileInfo.id) {
|
|
|
|
toaster.notify(
|
|
|
|
<span>
|
2019-03-14 10:15:25 +08:00
|
|
|
{gettext('This file has been updated.')}
|
|
|
|
<a href='' >{' '}{gettext('Refresh')}</a>
|
2019-03-11 21:08:25 +08:00
|
|
|
</span>,
|
|
|
|
{id: 'repo_updated', duration: 3600});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
receivePresenceData(data) {
|
|
|
|
switch(data.response) {
|
|
|
|
case 'user_join':
|
|
|
|
toaster.notify(`user ${data.user.name} joined`, {
|
|
|
|
duration: 3
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 'user_left':
|
|
|
|
toaster.notify(`user ${data.user.name} left`, {
|
|
|
|
duration: 3
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
case 'update_users':
|
|
|
|
for (var prop in data.users) {
|
2020-12-15 18:00:39 +08:00
|
|
|
if (Object.prototype.hasOwnProperty.call(data.users, prop)) {
|
2019-03-11 21:08:25 +08:00
|
|
|
if (prop === this.socket_id) {
|
|
|
|
data.users[prop]['myself'] = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.setState({collabUsers: Object.values(data.users)});
|
|
|
|
return;
|
|
|
|
case 'user_editing':
|
|
|
|
toaster.danger(`user ${data.user.name} is editing this file!`, {
|
|
|
|
duration: 3
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
default:
|
2023-09-13 08:40:50 +08:00
|
|
|
// eslint-disable-next-line
|
2019-03-11 21:08:25 +08:00
|
|
|
console.log('unknown response type: ' + data.response);
|
|
|
|
return;
|
|
|
|
}
|
2018-09-29 18:32:53 +08:00
|
|
|
}
|
2018-05-02 14:09:58 +08:00
|
|
|
|
2019-01-16 11:49:00 +08:00
|
|
|
toggleCancel = () => {
|
|
|
|
this.setState({
|
|
|
|
showMarkdownEditorDialog: false,
|
2019-02-25 18:17:04 +08:00
|
|
|
showShareLinkDialog: false,
|
2019-03-26 11:18:41 +08:00
|
|
|
showInsertFileDialog: false,
|
2019-01-16 11:49:00 +08:00
|
|
|
});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-01-16 11:49:00 +08:00
|
|
|
|
2022-05-19 15:39:16 +08:00
|
|
|
setEditorMode = (editorMode) => { // rich | plain
|
|
|
|
this.setState({editorMode: editorMode});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-03-11 21:08:25 +08:00
|
|
|
|
|
|
|
setDraftValue = (type, value) => {
|
|
|
|
if (type === 'rich') {
|
2019-03-14 18:14:11 +08:00
|
|
|
this.draftRichValue = value;
|
2019-03-11 21:08:25 +08:00
|
|
|
} else {
|
|
|
|
this.draftPlainValue = value;
|
|
|
|
}
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-03-14 10:15:25 +08:00
|
|
|
|
2019-03-11 21:08:25 +08:00
|
|
|
setContent = (str) => {
|
2019-03-14 10:15:25 +08:00
|
|
|
let value = deserialize(str);
|
2019-03-11 21:08:25 +08:00
|
|
|
this.setState({
|
2019-03-14 10:15:25 +08:00
|
|
|
markdownContent: str,
|
|
|
|
value: value,
|
2019-03-11 21:08:25 +08:00
|
|
|
});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-03-11 21:08:25 +08:00
|
|
|
|
|
|
|
checkDraft = () => {
|
2020-06-30 21:10:47 +08:00
|
|
|
let draftKey = editorApi.getDraftKey();
|
2019-03-11 21:08:25 +08:00
|
|
|
let draft = localStorage.getItem(draftKey);
|
|
|
|
let that = this;
|
|
|
|
if (draft) {
|
2022-05-19 15:39:16 +08:00
|
|
|
that.setState({localDraftDialog: true});
|
2019-03-11 21:08:25 +08:00
|
|
|
that.localDraft = draft;
|
|
|
|
localStorage.removeItem(draftKey);
|
|
|
|
}
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-03-11 21:08:25 +08:00
|
|
|
|
|
|
|
useDraft = () => {
|
|
|
|
this.setState({
|
|
|
|
localDraftDialog: false,
|
|
|
|
loading: false,
|
|
|
|
markdownContent: this.localDraft,
|
|
|
|
editorMode: 'rich',
|
|
|
|
});
|
|
|
|
this.emitSwitchEditor(true);
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-03-11 21:08:25 +08:00
|
|
|
|
|
|
|
deleteDraft = () => {
|
|
|
|
if (this.state.localDraftDialog) {
|
|
|
|
this.setState({
|
|
|
|
localDraftDialog: false,
|
|
|
|
loading: false,
|
|
|
|
});
|
|
|
|
}
|
2020-06-30 21:10:47 +08:00
|
|
|
let draftKey = editorApi.getDraftKey();
|
2019-04-15 12:09:58 +08:00
|
|
|
localStorage.removeItem(draftKey);
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-03-11 21:08:25 +08:00
|
|
|
|
2019-03-18 08:40:31 +08:00
|
|
|
closeDraftDialog = () => {
|
2022-05-19 15:39:16 +08:00
|
|
|
this.setState({localDraftDialog: false});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-03-18 08:40:31 +08:00
|
|
|
|
2019-03-11 21:08:25 +08:00
|
|
|
clearTimer = () => {
|
|
|
|
clearTimeout(this.timer);
|
|
|
|
this.timer = null;
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-03-11 21:08:25 +08:00
|
|
|
|
2019-01-16 11:49:00 +08:00
|
|
|
openDialogs = (option) => {
|
2022-05-19 15:39:16 +08:00
|
|
|
switch (option) {
|
2019-04-13 11:54:14 +08:00
|
|
|
case 'help':
|
|
|
|
window.richMarkdownEditor.showHelpDialog();
|
2019-01-16 11:49:00 +08:00
|
|
|
break;
|
2019-02-25 18:17:04 +08:00
|
|
|
case 'share_link':
|
|
|
|
this.setState({
|
|
|
|
showMarkdownEditorDialog: true,
|
|
|
|
showShareLinkDialog: true,
|
|
|
|
});
|
|
|
|
break;
|
2019-03-26 11:18:41 +08:00
|
|
|
case 'insert_file':
|
|
|
|
this.setState({
|
|
|
|
showMarkdownEditorDialog: true,
|
|
|
|
showInsertFileDialog: true,
|
|
|
|
});
|
|
|
|
break;
|
2019-01-16 11:49:00 +08:00
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-01-16 11:49:00 +08:00
|
|
|
|
2019-03-11 21:08:25 +08:00
|
|
|
componentWillUnmount() {
|
|
|
|
this.socket.emit('repo_update', {
|
|
|
|
request: 'unwatch_update',
|
2020-06-30 21:10:47 +08:00
|
|
|
repo_id: editorApi.repoID,
|
2019-03-14 18:14:11 +08:00
|
|
|
user: {
|
2020-06-30 21:10:47 +08:00
|
|
|
name: editorApi.name,
|
|
|
|
username: editorApi.username,
|
|
|
|
contact_email: editorApi.contact_email,
|
2019-03-14 18:14:11 +08:00
|
|
|
},
|
2019-03-11 21:08:25 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-09-13 10:37:07 +08:00
|
|
|
async componentDidMount() {
|
|
|
|
|
2023-11-22 11:29:01 +08:00
|
|
|
const fileIcon = Utils.getFileIconUrl(fileName, 192);
|
|
|
|
document.getElementById('favicon').href = fileIcon;
|
|
|
|
|
2021-09-13 10:37:07 +08:00
|
|
|
// get file info
|
|
|
|
const fileInfoRes = await seafileAPI.getFileInfo(repoID, filePath);
|
|
|
|
const { mtime, size, starred, permission, last_modifier_name, id } = fileInfoRes.data;
|
|
|
|
const lastModifier = last_modifier_name;
|
|
|
|
|
|
|
|
// get file download url
|
|
|
|
const fileDownloadUrlRes = await seafileAPI.getFileDownloadLink(repoID, filePath);
|
|
|
|
const downloadUrl = fileDownloadUrlRes.data;
|
|
|
|
|
2022-03-11 18:38:57 +08:00
|
|
|
// get file content
|
2021-09-13 10:37:07 +08:00
|
|
|
const fileContentRes = await seafileAPI.getFileContent(downloadUrl);
|
|
|
|
const markdownContent = fileContentRes.data;
|
|
|
|
const value = deserialize(markdownContent);
|
|
|
|
|
|
|
|
// init permission
|
|
|
|
let hasPermission = permission === 'rw' || permission === 'cloud-edit';
|
|
|
|
|
|
|
|
// get custom permission
|
|
|
|
if (permission.startsWith('custom-')) {
|
|
|
|
const permissionID = permission.split('-')[1];
|
|
|
|
const customPermissionRes = await seafileAPI.getCustomPermission(repoID, permissionID);
|
|
|
|
const customPermission = customPermissionRes.data.permission;
|
|
|
|
const { modify: canModify } = customPermission.permission;
|
|
|
|
hasPermission = canModify ? true : hasPermission;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Goto rich edit page
|
|
|
|
// First, the user has the relevant permissions, otherwise he can only enter the viewer interface or cannot access
|
|
|
|
// case1: If file is draft file
|
|
|
|
// case2: If mode == 'edit' and the file has no draft
|
|
|
|
// case3: The length of markDownContent is 1 when clear all content in editor and the file has no draft
|
|
|
|
const { fileInfo } = this.state;
|
|
|
|
this.setState({
|
|
|
|
loading: false,
|
|
|
|
fileInfo: {...fileInfo, mtime, size, starred, permission, lastModifier, id},
|
|
|
|
markdownContent,
|
|
|
|
value,
|
|
|
|
readOnly: !hasPermission || hasDraft,
|
2018-09-29 18:32:53 +08:00
|
|
|
});
|
2021-09-13 10:37:07 +08:00
|
|
|
|
2019-03-11 21:08:25 +08:00
|
|
|
if (userInfo && this.socket) {
|
|
|
|
const { repoID, path } = this.state.fileInfo;
|
|
|
|
this.socket.emit('presence', {
|
|
|
|
request: 'join_room',
|
2019-03-14 18:14:11 +08:00
|
|
|
doc_id: CryptoJS.MD5(repoID+path).toString(),
|
|
|
|
user: userInfo
|
2019-03-11 21:08:25 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
this.socket.emit('repo_update', {
|
|
|
|
request: 'watch_update',
|
2020-06-30 21:10:47 +08:00
|
|
|
repo_id: editorApi.repoID,
|
2019-03-14 18:14:11 +08:00
|
|
|
user: {
|
2020-06-30 21:10:47 +08:00
|
|
|
name: editorApi.name,
|
|
|
|
username: editorApi.username,
|
|
|
|
contact_email: editorApi.contact_email,
|
2019-03-14 18:14:11 +08:00
|
|
|
},
|
2019-03-11 21:08:25 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
this.checkDraft();
|
2019-06-19 13:36:57 +08:00
|
|
|
this.listFileTags();
|
2019-03-14 10:15:25 +08:00
|
|
|
|
2019-06-26 14:56:03 +08:00
|
|
|
this.listFileParticipants();
|
2019-06-29 11:24:18 +08:00
|
|
|
window.showParticipants = true;
|
2019-03-14 10:15:25 +08:00
|
|
|
setTimeout(() => {
|
|
|
|
let url = new URL(window.location.href);
|
|
|
|
if (url.hash) {
|
2020-12-15 18:00:39 +08:00
|
|
|
window.location.href = url;
|
2019-03-14 10:15:25 +08:00
|
|
|
}
|
|
|
|
}, 100);
|
2019-01-16 11:49:00 +08:00
|
|
|
}
|
2018-12-28 14:25:25 +08:00
|
|
|
|
2019-06-19 13:36:57 +08:00
|
|
|
listFileTags = () => {
|
|
|
|
seafileAPI.listFileTags(repoID, filePath).then(res => {
|
|
|
|
let fileTagList = res.data.file_tags;
|
|
|
|
for (let i = 0; i < fileTagList.length; i++) {
|
|
|
|
fileTagList[i].id = fileTagList[i].file_tag_id;
|
|
|
|
}
|
|
|
|
this.setState({ fileTagList: fileTagList });
|
|
|
|
});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-06-19 13:36:57 +08:00
|
|
|
|
|
|
|
onFileTagChanged = () => {
|
|
|
|
this.listFileTags();
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-06-19 13:36:57 +08:00
|
|
|
|
2019-06-26 14:56:03 +08:00
|
|
|
listFileParticipants = () => {
|
2020-06-30 21:10:47 +08:00
|
|
|
editorApi.listFileParticipant().then((res) => {
|
2019-06-26 14:56:03 +08:00
|
|
|
this.setState({ participants: res.data.participant_list });
|
|
|
|
});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-06-26 14:56:03 +08:00
|
|
|
|
|
|
|
onParticipantsChange = () => {
|
|
|
|
this.listFileParticipants();
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-06-26 14:56:03 +08:00
|
|
|
|
2019-03-11 21:08:25 +08:00
|
|
|
setFileInfoMtime = (fileInfo) => {
|
2022-05-19 15:39:16 +08:00
|
|
|
const { fileInfo: oldFileInfo } = this.state;
|
|
|
|
const newFileInfo = Object.assign({}, oldFileInfo, { mtime: fileInfo.mtime, id: fileInfo.id, lastModifier: fileInfo.last_modifier_name });
|
|
|
|
this.setState({fileInfo: newFileInfo});
|
2019-03-11 21:08:25 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
toggleStar = () => {
|
2022-05-19 15:39:16 +08:00
|
|
|
const { fileInfo } = this.state;
|
|
|
|
const { starred } = fileInfo;
|
|
|
|
const newFileInfo = Object.assign({}, fileInfo, {starred: !starred});
|
|
|
|
if (starred) {
|
2020-06-30 21:10:47 +08:00
|
|
|
editorApi.unstarItem().then((response) => {
|
2022-05-19 15:39:16 +08:00
|
|
|
this.setState({fileInfo: newFileInfo});
|
2019-03-11 21:08:25 +08:00
|
|
|
});
|
2022-05-19 15:39:16 +08:00
|
|
|
return;
|
2019-03-11 21:08:25 +08:00
|
|
|
}
|
2022-05-19 15:39:16 +08:00
|
|
|
|
|
|
|
editorApi.starItem().then((response) => {
|
|
|
|
this.setState({fileInfo: newFileInfo});
|
|
|
|
});
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-03-11 21:08:25 +08:00
|
|
|
|
|
|
|
autoSaveDraft = () => {
|
|
|
|
let that = this;
|
|
|
|
if (that.timer) {
|
|
|
|
return;
|
2019-03-14 18:14:11 +08:00
|
|
|
}
|
2022-05-19 15:39:16 +08:00
|
|
|
that.timer = setTimeout(() => {
|
|
|
|
let str = '';
|
|
|
|
if (this.state.editorMode == 'rich') {
|
|
|
|
let value = this.draftRichValue;
|
|
|
|
str = serialize(value);
|
|
|
|
}
|
|
|
|
else if (this.state.editorMode == 'plain') {
|
|
|
|
str = this.draftPlainValue;
|
|
|
|
}
|
|
|
|
let draftKey = editorApi.getDraftKey();
|
|
|
|
localStorage.setItem(draftKey, str);
|
|
|
|
that.setState({
|
|
|
|
showDraftSaved: true
|
|
|
|
});
|
|
|
|
setTimeout(() => {
|
2019-03-11 21:08:25 +08:00
|
|
|
that.setState({
|
2022-05-19 15:39:16 +08:00
|
|
|
showDraftSaved: false
|
2019-03-11 21:08:25 +08:00
|
|
|
});
|
2022-05-19 15:39:16 +08:00
|
|
|
}, 3000);
|
|
|
|
that.timer = null;
|
|
|
|
}, 60000);
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-03-11 21:08:25 +08:00
|
|
|
|
2019-08-13 10:42:21 +08:00
|
|
|
openParentDirectory = () => {
|
2020-06-30 21:10:47 +08:00
|
|
|
window.location.href = editorApi.getParentDectionaryUrl();
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-03-11 21:08:25 +08:00
|
|
|
|
2022-05-19 15:39:16 +08:00
|
|
|
onEdit = (editorMode) => {
|
|
|
|
if (editorMode === 'rich') {
|
2019-04-13 11:54:14 +08:00
|
|
|
window.seafileEditor.switchToRichTextEditor();
|
2023-09-13 08:40:50 +08:00
|
|
|
return;
|
2022-05-19 15:39:16 +08:00
|
|
|
}
|
|
|
|
if (editorMode === 'plain') {
|
2020-11-02 13:56:35 +08:00
|
|
|
window.seafileEditor.switchToPlainTextEditor();
|
2019-04-13 11:54:14 +08:00
|
|
|
}
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-03-11 21:08:25 +08:00
|
|
|
|
|
|
|
toggleShareLinkDialog = () => {
|
|
|
|
this.openDialogs('share_link');
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-03-11 21:08:25 +08:00
|
|
|
|
2019-03-15 12:11:32 +08:00
|
|
|
toggleHistory = () => {
|
2019-04-13 11:54:14 +08:00
|
|
|
window.location.href = siteRoot + 'repo/file_revisions/' + repoID + '/?p=' + Utils.encodePath(filePath);
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-03-15 12:11:32 +08:00
|
|
|
|
2019-03-26 11:18:41 +08:00
|
|
|
getInsertLink = (repoID, filePath) => {
|
2019-04-24 16:37:42 +08:00
|
|
|
const fileName = Utils.getFileName(filePath);
|
2021-01-12 11:25:46 +08:00
|
|
|
const suffix = fileName.slice(fileName.indexOf('.') + 1);
|
|
|
|
if (IMAGE_SUFFIXES.includes(suffix)) {
|
2022-01-05 15:49:27 +08:00
|
|
|
let innerURL = serviceUrl + '/lib/' + repoID + '/file' + Utils.encodePath(filePath) + '?raw=1';
|
|
|
|
window.richMarkdownEditor.addLink(fileName, innerURL, true);
|
2021-01-12 11:25:46 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
let innerURL = serviceUrl + '/lib/' + repoID + '/file' + Utils.encodePath(filePath);
|
2019-04-24 16:37:42 +08:00
|
|
|
window.richMarkdownEditor.addLink(fileName, innerURL);
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-03-26 11:18:41 +08:00
|
|
|
|
2019-04-13 14:51:41 +08:00
|
|
|
onContentChanged = (value) => {
|
|
|
|
this.setState({ contentChanged: value });
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-04-13 14:51:41 +08:00
|
|
|
|
|
|
|
onSaving = (value) => {
|
|
|
|
this.setState({ saving: value });
|
2023-09-13 08:40:50 +08:00
|
|
|
};
|
2019-04-13 14:51:41 +08:00
|
|
|
|
2018-05-02 14:09:58 +08:00
|
|
|
render() {
|
|
|
|
if (this.state.loading) {
|
|
|
|
return (
|
|
|
|
<div className="empty-loading-page">
|
|
|
|
<div className="lds-ripple page-centered"><div></div><div></div></div>
|
|
|
|
</div>
|
2018-09-29 18:32:53 +08:00
|
|
|
);
|
2022-05-19 15:39:16 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Fragment>
|
|
|
|
<HeaderToolbar
|
|
|
|
isDocs={isDocs}
|
|
|
|
hasDraft={hasDraft}
|
|
|
|
isDraft={isDraft}
|
|
|
|
editorApi={editorApi}
|
|
|
|
collabUsers={this.state.collabUsers}
|
|
|
|
fileInfo={this.state.fileInfo}
|
|
|
|
toggleStar={this.toggleStar}
|
|
|
|
openParentDirectory={this.openParentDirectory}
|
|
|
|
openDialogs={this.openDialogs}
|
|
|
|
toggleShareLinkDialog={this.toggleShareLinkDialog}
|
|
|
|
onEdit={this.onEdit}
|
|
|
|
toggleNewDraft={editorApi.createDraftFile}
|
|
|
|
showFileHistory={this.state.isShowHistory ? false : true }
|
|
|
|
toggleHistory={this.toggleHistory}
|
|
|
|
readOnly={this.state.readOnly}
|
|
|
|
editorMode={this.state.editorMode}
|
|
|
|
contentChanged={this.state.contentChanged}
|
|
|
|
saving={this.state.saving}
|
|
|
|
showDraftSaved={this.state.showDraftSaved}
|
|
|
|
isLocked={this.state.isLocked}
|
|
|
|
lockedByMe={this.state.lockedByMe}
|
|
|
|
toggleLockFile={this.toggleLockFile}
|
|
|
|
/>
|
|
|
|
<SeafileEditor
|
|
|
|
scriptSource={mediaUrl + 'js/mathjax/tex-svg.js'}
|
|
|
|
fileInfo={this.state.fileInfo}
|
|
|
|
markdownContent={this.state.markdownContent}
|
|
|
|
editorApi={editorApi}
|
|
|
|
collabUsers={this.state.collabUsers}
|
|
|
|
setFileInfoMtime={this.setFileInfoMtime}
|
|
|
|
setEditorMode={this.setEditorMode}
|
|
|
|
setContent={this.setContent}
|
|
|
|
draftID={draftID}
|
|
|
|
isDraft={isDraft}
|
|
|
|
mode={this.state.mode}
|
|
|
|
emitSwitchEditor={this.emitSwitchEditor}
|
|
|
|
hasDraft={hasDraft}
|
|
|
|
editorMode={this.state.editorMode}
|
|
|
|
siteRoot={siteRoot}
|
|
|
|
autoSaveDraft={this.autoSaveDraft}
|
|
|
|
setDraftValue={this.setDraftValue}
|
|
|
|
clearTimer={this.clearTimer}
|
|
|
|
openDialogs={this.openDialogs}
|
|
|
|
deleteDraft={this.deleteDraft}
|
|
|
|
readOnly={this.state.readOnly}
|
|
|
|
onContentChanged={this.onContentChanged}
|
|
|
|
onSaving={this.onSaving}
|
|
|
|
contentChanged={this.state.contentChanged}
|
|
|
|
fileTagList={this.state.fileTagList}
|
2022-05-20 16:26:53 +08:00
|
|
|
onFileTagChanged={this.onFileTagChanged}
|
2022-05-19 15:39:16 +08:00
|
|
|
participants={this.state.participants}
|
|
|
|
onParticipantsChange={this.onParticipantsChange}
|
|
|
|
markdownLint={fileName.toLowerCase() !== 'index.md'}
|
|
|
|
/>
|
|
|
|
{this.state.localDraftDialog &&
|
|
|
|
<LocalDraftDialog
|
|
|
|
localDraftDialog={this.state.localDraftDialog}
|
2019-04-13 11:54:14 +08:00
|
|
|
deleteDraft={this.deleteDraft}
|
2022-05-19 15:39:16 +08:00
|
|
|
closeDraftDialog={this.closeDraftDialog}
|
|
|
|
useDraft={this.useDraft}
|
2019-04-13 11:54:14 +08:00
|
|
|
/>
|
2022-05-19 15:39:16 +08:00
|
|
|
}
|
|
|
|
{this.state.showMarkdownEditorDialog && (
|
|
|
|
<React.Fragment>
|
|
|
|
{this.state.showInsertFileDialog &&
|
|
|
|
<InsertFileDialog
|
|
|
|
repoID={repoID}
|
|
|
|
filePath={filePath}
|
|
|
|
toggleCancel={this.toggleCancel}
|
|
|
|
getInsertLink={this.getInsertLink}
|
2019-03-18 08:40:31 +08:00
|
|
|
/>
|
2022-05-19 15:39:16 +08:00
|
|
|
}
|
|
|
|
{this.state.showShareLinkDialog &&
|
|
|
|
<ShareDialog
|
|
|
|
itemType="file"
|
|
|
|
itemName={this.state.fileInfo.name}
|
|
|
|
itemPath={filePath}
|
|
|
|
repoID={repoID}
|
|
|
|
toggleDialog={this.toggleCancel}
|
|
|
|
isGroupOwnedRepo={false}
|
|
|
|
repoEncrypted={false}
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
</React.Fragment>
|
|
|
|
)}
|
|
|
|
</Fragment>
|
|
|
|
);
|
2018-05-02 14:09:58 +08:00
|
|
|
}
|
|
|
|
}
|
2018-09-29 18:32:53 +08:00
|
|
|
|
2019-03-11 21:08:25 +08:00
|
|
|
export default MarkdownEditor;
|