mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-17 07:41:26 +00:00
Viewer add (#3084)
This commit is contained in:
6
frontend/package-lock.json
generated
6
frontend/package-lock.json
generated
@@ -531,9 +531,9 @@
|
|||||||
"integrity": "sha512-8KtjFl4D0vJBTl1H64ciXHz5oyUtqnnJI65wAa1IBKwA+xmF/++DWeV1i+O9/DK135ZVhrERfgW2EGvu50ZMNQ=="
|
"integrity": "sha512-8KtjFl4D0vJBTl1H64ciXHz5oyUtqnnJI65wAa1IBKwA+xmF/++DWeV1i+O9/DK135ZVhrERfgW2EGvu50ZMNQ=="
|
||||||
},
|
},
|
||||||
"@seafile/seafile-editor": {
|
"@seafile/seafile-editor": {
|
||||||
"version": "0.1.84",
|
"version": "0.1.85",
|
||||||
"resolved": "https://registry.npmjs.org/@seafile/seafile-editor/-/seafile-editor-0.1.84.tgz",
|
"resolved": "https://registry.npmjs.org/@seafile/seafile-editor/-/seafile-editor-0.1.85.tgz",
|
||||||
"integrity": "sha512-S/Ybh3NxPRQEogpnCEvA+/Ld9g+M9dTo1evt88zVIo0K27PqL9EEoTYFD5nsuth9aQaE3Bp8nfsaz/wUjg0JAQ==",
|
"integrity": "sha512-NcksE2epv51X0+u6CpMSKxaKx4HVZz3hgsJOVz4onfPRDnuGmxVItalvDmCVu3T0UWfJd3Sq6Ub1oOIXCvg+lQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@seafile/slate-react": "0.1.4",
|
"@seafile/slate-react": "0.1.4",
|
||||||
"autoprefixer": "7.1.6",
|
"autoprefixer": "7.1.6",
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
"@babel/runtime": "^7.3.1",
|
"@babel/runtime": "^7.3.1",
|
||||||
"@reach/router": "^1.2.0",
|
"@reach/router": "^1.2.0",
|
||||||
"@seafile/resumablejs": "^1.1.8",
|
"@seafile/resumablejs": "^1.1.8",
|
||||||
"@seafile/seafile-editor": "^0.1.84",
|
"@seafile/seafile-editor": "^0.1.85",
|
||||||
"MD5": "^1.3.0",
|
"MD5": "^1.3.0",
|
||||||
"antd": "^3.13.6",
|
"antd": "^3.13.6",
|
||||||
"autoprefixer": "7.1.6",
|
"autoprefixer": "7.1.6",
|
||||||
|
139
frontend/src/components/toolbar/markdown-viewer-toolbar.js
Normal file
139
frontend/src/components/toolbar/markdown-viewer-toolbar.js
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { gettext } from '../../utils/constants';
|
||||||
|
import { IconButton, ButtonGroup, CollabUsersButton } from '@seafile/seafile-editor/dist/components/topbarcomponent/editorToolBar';
|
||||||
|
import FileInfo from '@seafile/seafile-editor/dist/components/topbarcomponent/file-info';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
hasDraft: PropTypes.bool.isRequired,
|
||||||
|
isDraft: PropTypes.bool.isRequired,
|
||||||
|
showFileHistory: PropTypes.bool.isRequired,
|
||||||
|
editorUtilities: PropTypes.object.isRequired,
|
||||||
|
collabUsers: PropTypes.array.isRequired,
|
||||||
|
fileInfo: PropTypes.object.isRequired,
|
||||||
|
fileTagList: PropTypes.array.isRequired,
|
||||||
|
relatedFiles: PropTypes.array.isRequired,
|
||||||
|
commentsNumber: PropTypes.number.isRequired,
|
||||||
|
toggleCommentList: PropTypes.func.isRequired,
|
||||||
|
toggleShareLinkDialog: PropTypes.func.isRequired,
|
||||||
|
onEdit: PropTypes.func.isRequired,
|
||||||
|
toggleHistory: PropTypes.func.isRequired,
|
||||||
|
toggleNewDraft: PropTypes.func.isRequired,
|
||||||
|
toggleStar: PropTypes.func.isRequired,
|
||||||
|
backToParentDirectory: PropTypes.func.isRequired,
|
||||||
|
openDialogs: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
class MarkdownViewerToolbar extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderFirstToolbar() {
|
||||||
|
return (
|
||||||
|
<div className="sf-md-viewer-topbar-first d-flex justify-content-between">
|
||||||
|
<FileInfo toggleStar={this.props.toggleStar} editorUtilities={this.props.editorUtilities}
|
||||||
|
fileInfo={this.props.fileInfo}/>
|
||||||
|
{(this.props.hasDraft && !this.props.isDraft) &&
|
||||||
|
<div className='seafile-btn-view-review'>
|
||||||
|
<div className='tag tag-green'>{gettext('This file is in draft stage.')}
|
||||||
|
<a className="ml-2" onMouseDown={this.props.editorUtilities.goDraftPage}>{gettext('View draft')}</a></div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div className="topbar-btn-container">
|
||||||
|
{ (!this.props.hasDraft && !this.props.isDraft) &&
|
||||||
|
<button onMouseDown={this.props.toggleNewDraft} className="btn btn-success btn-new-draft">
|
||||||
|
{gettext('New draft')}</button>
|
||||||
|
}
|
||||||
|
{this.props.collabUsers.length > 0 && <CollabUsersButton className={'collab-users-dropdown'}
|
||||||
|
users={this.props.collabUsers} id={'usersButton'} />}
|
||||||
|
<ButtonGroup>
|
||||||
|
<IconButton id={'shareBtn'} text={gettext('Share')} icon={'fa fa-share-alt'}
|
||||||
|
onMouseDown={this.props.toggleShareLinkDialog}/>
|
||||||
|
{
|
||||||
|
this.props.commentsNumber > 0 ?
|
||||||
|
<button className="btn btn-icon btn-secondary btn-active" id="commentsNumber"
|
||||||
|
type="button" data-active="false" onMouseDown={this.props.toggleCommentList}>
|
||||||
|
<i className="fa fa-comments"></i>{' '}<span>{this.props.commentsNumber}</span>
|
||||||
|
</button>
|
||||||
|
:
|
||||||
|
<IconButton id={'commentsNumber'} text={gettext('Comments')} icon={'fa fa-comments'}
|
||||||
|
onMouseDown={this.props.toggleCommentList}/>
|
||||||
|
}
|
||||||
|
<IconButton text={gettext('Back to parent directory')} id={'parentDirectory'}
|
||||||
|
icon={'fa fa-folder-open'} onMouseDown={this.props.backToParentDirectory}/>
|
||||||
|
{
|
||||||
|
(!this.props.hasDraft && this.props.fileInfo.permission === 'rw')? <IconButton text={gettext('Edit')}
|
||||||
|
id={'editButton'} icon={'fa fa-edit'} onMouseDown={this.props.onEdit}/>: null
|
||||||
|
}
|
||||||
|
{
|
||||||
|
(this.props.showFileHistory) && (!this.props.isShowHistory && <IconButton id={'historyButton'}
|
||||||
|
text={gettext('File history')} onMouseDown={this.props.toggleHistory} icon={'fa fa-history'}/>)
|
||||||
|
}
|
||||||
|
</ButtonGroup>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSecondToolbar() {
|
||||||
|
const { relatedFiles, fileTagList } = this.props;
|
||||||
|
const openDialogs = this.props.openDialogs;
|
||||||
|
let relatedFileString = '';
|
||||||
|
if (relatedFiles) {
|
||||||
|
const length = relatedFiles.length;
|
||||||
|
if (length === 1) relatedFileString = 'Related file';
|
||||||
|
else if (length > 1) relatedFileString = 'Related files';
|
||||||
|
}
|
||||||
|
return(
|
||||||
|
<div className="sf-md-viewer-topbar-second d-flex justify-content-center">
|
||||||
|
{(fileTagList) && (fileTagList.length > 0 ?
|
||||||
|
<ul className="sf-files-tags">
|
||||||
|
{
|
||||||
|
fileTagList.map((item, index=0) => {
|
||||||
|
return (
|
||||||
|
<li key={index} className='sf-files-tag'>
|
||||||
|
<span className="file-tag-icon" style={{backgroundColor: item.tag_color}}></span>
|
||||||
|
<span className="file-tag-name" title={item.tag_name}>{item.tag_name}</span>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
<li className='sf-files-tag'><span className="file-tag-name"
|
||||||
|
onClick={openDialogs.bind(this, 'tags')}>{gettext('Edit')}</span></li>
|
||||||
|
</ul>
|
||||||
|
:
|
||||||
|
<span className="no-file-tag edit-related-file"
|
||||||
|
onClick={openDialogs.bind(this, 'tags')}>{gettext('No tags')}</span>
|
||||||
|
)}
|
||||||
|
{relatedFiles &&
|
||||||
|
<div className="sf-related-files-bar">
|
||||||
|
{relatedFiles.length === 0 ?
|
||||||
|
<span className="edit-related-file no-related-file"
|
||||||
|
onClick={openDialogs.bind(this, 'related_files')}>{gettext('No related files')}</span>:
|
||||||
|
<React.Fragment>
|
||||||
|
<a href="#sf-releted-files">{relatedFiles.length}{' '}{gettext(relatedFileString)}</a>
|
||||||
|
<span className="edit-related-file" onClick={openDialogs.bind(this, 'related_files')}>
|
||||||
|
{gettext('Edit')}</span>
|
||||||
|
</React.Fragment>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="sf-md-viewer-topbar">
|
||||||
|
{this.renderFirstToolbar()}
|
||||||
|
{this.renderSecondToolbar()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MarkdownViewerToolbar.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default MarkdownViewerToolbar;
|
@@ -8,7 +8,14 @@ import EditFileTagDialog from './components/dialog/edit-filetag-dialog';
|
|||||||
import ListRelatedFileDialog from './components/dialog/list-related-file-dialog';
|
import ListRelatedFileDialog from './components/dialog/list-related-file-dialog';
|
||||||
import AddRelatedFileDialog from './components/dialog/add-related-file-dialog';
|
import AddRelatedFileDialog from './components/dialog/add-related-file-dialog';
|
||||||
import ShareDialog from './components/dialog/share-dialog';
|
import ShareDialog from './components/dialog/share-dialog';
|
||||||
|
import MarkdownViewerSlate from '@seafile/seafile-editor/dist/viewer/markdown-viewer-slate';
|
||||||
|
import io from "socket.io-client";
|
||||||
|
import toaster from "./components/toast";
|
||||||
|
import { serialize } from "@seafile/seafile-editor/dist/utils/slate2markdown";
|
||||||
|
import LocalDraftDialog from "@seafile/seafile-editor/dist/components/local-draft-dialog";
|
||||||
|
import MarkdownViewerToolbar from './components/toolbar/markdown-viewer-toolbar';
|
||||||
|
|
||||||
|
const CryptoJS = require('crypto-js');
|
||||||
const { repoID, repoName, filePath, fileName, mode, draftID, draftFilePath, draftOriginFilePath, isDraft, hasDraft, shareLinkExpireDaysMin, shareLinkExpireDaysMax } = window.app.pageOptions;
|
const { repoID, repoName, filePath, fileName, mode, draftID, draftFilePath, draftOriginFilePath, isDraft, hasDraft, shareLinkExpireDaysMin, shareLinkExpireDaysMax } = window.app.pageOptions;
|
||||||
const { siteRoot, serviceUrl, seafileCollabServer } = window.app.config;
|
const { siteRoot, serviceUrl, seafileCollabServer } = window.app.config;
|
||||||
const userInfo = window.app.userInfo;
|
const userInfo = window.app.userInfo;
|
||||||
@@ -256,6 +263,11 @@ const editorUtilities = new EditorUtilities();
|
|||||||
class MarkdownEditor extends React.Component {
|
class MarkdownEditor extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
this.timer = null;
|
||||||
|
this.localDraft = '';
|
||||||
|
this.autoSave = false;
|
||||||
|
this.draftRichValue = '';
|
||||||
|
this.draftPlainValue = '';
|
||||||
this.state = {
|
this.state = {
|
||||||
markdownContent: '',
|
markdownContent: '',
|
||||||
loading: true,
|
loading: true,
|
||||||
@@ -271,15 +283,98 @@ class MarkdownEditor extends React.Component {
|
|||||||
lastModifier: '',
|
lastModifier: '',
|
||||||
id: '',
|
id: '',
|
||||||
},
|
},
|
||||||
|
editorMode: 'viewer',
|
||||||
collabServer: seafileCollabServer ? seafileCollabServer : null,
|
collabServer: seafileCollabServer ? seafileCollabServer : null,
|
||||||
relatedFiles: [],
|
relatedFiles: [],
|
||||||
fileTagList: [],
|
fileTagList: [],
|
||||||
|
localDraftDialog: false,
|
||||||
showRelatedFileDialog: false,
|
showRelatedFileDialog: false,
|
||||||
showEditFileTagDialog: false,
|
showEditFileTagDialog: false,
|
||||||
showAddRelatedFileDialog: false,
|
showAddRelatedFileDialog: false,
|
||||||
showMarkdownEditorDialog: false,
|
showMarkdownEditorDialog: false,
|
||||||
showShareLinkDialog: false,
|
showShareLinkDialog: false,
|
||||||
|
showDraftSaved: false,
|
||||||
|
collabUsers: userInfo ?
|
||||||
|
[{user: userInfo, is_editing: false}] : [],
|
||||||
|
isShowHistory: false,
|
||||||
|
isShowComments: false,
|
||||||
|
commentsNumber: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emitSwitchEditor = (is_editing=false) => {
|
||||||
|
if (userInfo && this.state.collabServer) {
|
||||||
|
const { repoID, path } = this.state.fileInfo;
|
||||||
|
this.socket.emit('presence', {
|
||||||
|
request: 'editing',
|
||||||
|
doc_id: CryptoJS.MD5(repoID+path).toString(),
|
||||||
|
user: userInfo,
|
||||||
|
is_editing,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
receiveUpdateData (data) {
|
||||||
|
let currentTime = new Date();
|
||||||
|
if ((parseFloat(currentTime - this.lastModifyTime)/1000) <= 5) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
editorUtilities.fileMetaData().then((res) => {
|
||||||
|
if (res.data.id !== this.state.fileInfo.id) {
|
||||||
|
toaster.notify(
|
||||||
|
<span>
|
||||||
|
{this.props.t('this_file_has_been_updated')}
|
||||||
|
<a href='' >{' '}{this.props.t('refresh')}</a>
|
||||||
|
</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) {
|
||||||
|
if (data.users.hasOwnProperty(prop)) {
|
||||||
|
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:
|
||||||
|
console.log('unknown response type: ' + data.response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleCancel = () => {
|
toggleCancel = () => {
|
||||||
@@ -292,6 +387,65 @@ class MarkdownEditor extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setEditorMode = (type) => {
|
||||||
|
this.setState({
|
||||||
|
editorMode: type
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setDraftValue = (type, value) => {
|
||||||
|
if (type === 'rich') {
|
||||||
|
this.draftRichValue = value
|
||||||
|
} else {
|
||||||
|
this.draftPlainValue = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setContent = (str) => {
|
||||||
|
this.setState({
|
||||||
|
markdownContent: str
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkDraft = () => {
|
||||||
|
let draftKey = editorUtilities.getDraftKey();
|
||||||
|
let draft = localStorage.getItem(draftKey);
|
||||||
|
let that = this;
|
||||||
|
if (draft) {
|
||||||
|
that.setState({
|
||||||
|
localDraftDialog: true,
|
||||||
|
});
|
||||||
|
that.localDraft = draft;
|
||||||
|
localStorage.removeItem(draftKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useDraft = () => {
|
||||||
|
this.setState({
|
||||||
|
localDraftDialog: false,
|
||||||
|
loading: false,
|
||||||
|
markdownContent: this.localDraft,
|
||||||
|
editorMode: 'rich',
|
||||||
|
});
|
||||||
|
this.emitSwitchEditor(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteDraft = () => {
|
||||||
|
if (this.state.localDraftDialog) {
|
||||||
|
this.setState({
|
||||||
|
localDraftDialog: false,
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let draftKey = editorUtilities.getDraftKey();
|
||||||
|
localStorage.removeItem(draftKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clearTimer = () => {
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
this.timer = null;
|
||||||
|
}
|
||||||
|
|
||||||
closeAddRelatedFileDialog = () => {
|
closeAddRelatedFileDialog = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
showAddRelatedFileDialog: false,
|
showAddRelatedFileDialog: false,
|
||||||
@@ -340,6 +494,18 @@ class MarkdownEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.socket.emit('repo_update', {
|
||||||
|
request: 'unwatch_update',
|
||||||
|
repo_id: this.props.editorUtilities.repoID,
|
||||||
|
user: {
|
||||||
|
name: this.props.editorUtilities.name,
|
||||||
|
username: this.props.editorUtilities.username,
|
||||||
|
contact_email: this.props.editorUtilities.contact_email,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
|
||||||
seafileAPI.getFileInfo(repoID, filePath).then((res) => {
|
seafileAPI.getFileInfo(repoID, filePath).then((res) => {
|
||||||
@@ -361,15 +527,46 @@ class MarkdownEditor extends React.Component {
|
|||||||
seafileAPI.getFileDownloadLink(repoID, filePath).then((res) => {
|
seafileAPI.getFileDownloadLink(repoID, filePath).then((res) => {
|
||||||
const downLoadUrl = res.data;
|
const downLoadUrl = res.data;
|
||||||
seafileAPI.getFileContent(downLoadUrl).then((res) => {
|
seafileAPI.getFileContent(downLoadUrl).then((res) => {
|
||||||
|
const contentLength = res.data.length;
|
||||||
|
let isBlankFile = (contentLength === 0 || contentLength === 1);
|
||||||
|
let hasPermission = (this.state.fileInfo.permission === 'rw');
|
||||||
|
let isEditMode = this.state.mode;
|
||||||
this.setState({
|
this.setState({
|
||||||
markdownContent: res.data,
|
markdownContent: res.data,
|
||||||
loading: false
|
loading: false,
|
||||||
|
// 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
|
||||||
|
editorMode: (hasPermission && (isDraft || (isEditMode && !hasDraft) || (isBlankFile && !hasDraft))) ? 'rich' : 'viewer',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
if (userInfo && this.socket) {
|
||||||
|
const { repoID, path } = this.state.fileInfo;
|
||||||
|
this.socket.emit('presence', {
|
||||||
|
request: 'join_room',
|
||||||
|
doc_id: CryptoJS.MD5(repoID+path).toString(),
|
||||||
|
user: userInfo
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.emit('repo_update', {
|
||||||
|
request: 'watch_update',
|
||||||
|
repo_id: editorUtilities.repoID,
|
||||||
|
user: {
|
||||||
|
name: editorUtilities.name,
|
||||||
|
username: editorUtilities.username,
|
||||||
|
contact_email: editorUtilities.contact_email,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checkDraft();
|
||||||
this.listRelatedFiles();
|
this.listRelatedFiles();
|
||||||
this.listFileTags();
|
this.listFileTags();
|
||||||
|
this.getCommentsNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
listRelatedFiles = () => {
|
listRelatedFiles = () => {
|
||||||
@@ -400,7 +597,99 @@ class MarkdownEditor extends React.Component {
|
|||||||
this.listFileTags();
|
this.listFileTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setFileInfoMtime = (fileInfo) => {
|
||||||
|
this.setState({
|
||||||
|
fileInfo: Object.assign({}, this.state.fileInfo, { mtime: fileInfo.mtime, id: fileInfo.id, lastModifier: fileInfo.last_modifier_name })
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
toggleStar = () => {
|
||||||
|
let starrd = this.state.fileInfo.starred;
|
||||||
|
if (starrd) {
|
||||||
|
editorUtilities.unStarItem().then((response) => {
|
||||||
|
this.setState({
|
||||||
|
fileInfo: Object.assign({}, this.state.fileInfo, {starred: !starrd})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else if (!starrd) {
|
||||||
|
editorUtilities.starItem().then((response) => {
|
||||||
|
this.setState({
|
||||||
|
fileInfo: Object.assign({}, this.state.fileInfo, {starred: !starrd})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
autoSaveDraft = () => {
|
||||||
|
let that = this;
|
||||||
|
if (that.timer) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
that.timer = setTimeout(() => {
|
||||||
|
let str = '';
|
||||||
|
if (this.state.editorMode == 'rich') {
|
||||||
|
let value = this.draftRichValue;
|
||||||
|
str = serialize(value.toJSON());
|
||||||
|
}
|
||||||
|
else if (this.state.editorMode == 'plain') {
|
||||||
|
str = this.draftPlainValue;
|
||||||
|
}
|
||||||
|
let draftKey = editorUtilities.getDraftKey();
|
||||||
|
localStorage.setItem(draftKey, str);
|
||||||
|
that.setState({
|
||||||
|
showDraftSaved: true
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
that.setState({
|
||||||
|
showDraftSaved: false
|
||||||
|
});
|
||||||
|
}, 3000);
|
||||||
|
that.timer = null;
|
||||||
|
}, 60000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backToParentDirectory = () => {
|
||||||
|
window.location.href = editorUtilities.getParentDectionaryUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
onEdit = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.setEditorMode('rich');
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleShareLinkDialog = () => {
|
||||||
|
this.openDialogs('share_link');
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleHistory = () => {
|
||||||
|
this.setState({ isShowHistory: !this.state.isShowHistory });
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleCommentList = () => {
|
||||||
|
if (this.state.isShowHistory) {
|
||||||
|
this.setState({ isShowHistory: false, isShowComments: true });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.setState({ isShowComments: !this.state.isShowComments });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getCommentsNumber = () => {
|
||||||
|
editorUtilities.getCommentsNumber().then((res) => {
|
||||||
|
let commentsNumber = res.data[Object.getOwnPropertyNames(res.data)[0]];
|
||||||
|
this.setState({
|
||||||
|
commentsNumber: commentsNumber
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onCommentAdded = () => {
|
||||||
|
this.getCommentsNumber();
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let component;
|
||||||
if (this.state.loading) {
|
if (this.state.loading) {
|
||||||
return (
|
return (
|
||||||
<div className="empty-loading-page">
|
<div className="empty-loading-page">
|
||||||
@@ -408,26 +697,96 @@ class MarkdownEditor extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (this.state.mode === 'editor') {
|
} else if (this.state.mode === 'editor') {
|
||||||
return (
|
if (this.state.editorMode === 'viewer') {
|
||||||
<React.Fragment>
|
component = (
|
||||||
<SeafileEditor
|
<div className="seafile-md-viewer d-flex flex-column">
|
||||||
|
<MarkdownViewerToolbar
|
||||||
|
hasDraft={hasDraft}
|
||||||
|
isDraft={isDraft}
|
||||||
|
editorUtilities={editorUtilities}
|
||||||
|
collabUsers={this.state.collabUsers}
|
||||||
|
fileInfo={this.state.fileInfo}
|
||||||
|
toggleStar={this.toggleStar}
|
||||||
|
backToParentDirectory={this.backToParentDirectory}
|
||||||
|
openDialogs={this.openDialogs}
|
||||||
|
fileTagList={this.state.fileTagList}
|
||||||
|
relatedFiles={this.state.relatedFiles}
|
||||||
|
commentsNumber={this.state.commentsNumber}
|
||||||
|
toggleCommentList={this.toggleCommentList}
|
||||||
|
toggleShareLinkDialog={this.toggleShareLinkDialog}
|
||||||
|
onEdit={this.onEdit}
|
||||||
|
showFileHistory={true}
|
||||||
|
toggleHistory={this.toggleHistory}
|
||||||
|
toggleNewDraft={editorUtilities.createDraftFile}
|
||||||
|
/>
|
||||||
|
<MarkdownViewerSlate
|
||||||
|
fileInfo={this.state.fileInfo}
|
||||||
|
markdownContent={this.state.markdownContent}
|
||||||
|
editorUtilities={editorUtilities}
|
||||||
|
collabUsers={this.state.collabUsers}
|
||||||
|
showFileHistory={true}
|
||||||
|
setFileInfoMtime={this.setFileInfoMtime}
|
||||||
|
toggleStar={this.toggleStar}
|
||||||
|
setEditorMode={this.setEditorMode}
|
||||||
|
draftID={draftID}
|
||||||
|
isDraft={isDraft}
|
||||||
|
emitSwitchEditor={this.emitSwitchEditor}
|
||||||
|
hasDraft={hasDraft}
|
||||||
|
shareLinkExpireDaysMin={shareLinkExpireDaysMin}
|
||||||
|
shareLinkExpireDaysMax={shareLinkExpireDaysMax}
|
||||||
|
relatedFiles={this.state.relatedFiles}
|
||||||
|
siteRoot={siteRoot}
|
||||||
|
openDialogs={this.openDialogs}
|
||||||
|
fileTagList={this.state.fileTagList}
|
||||||
|
showDraftSaved={this.state.showDraftSaved}
|
||||||
|
isShowHistory={this.state.isShowHistory}
|
||||||
|
isShowComments={this.state.isShowComments}
|
||||||
|
onCommentAdded={this.onCommentAdded}
|
||||||
|
commentsNumber={this.state.commentsNumber}
|
||||||
|
getCommentsNumber={this.getCommentsNumber}
|
||||||
|
toggleHistory={this.toggleHistory}
|
||||||
|
toggleCommentList={this.toggleCommentList}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
component = <SeafileEditor
|
||||||
fileInfo={this.state.fileInfo}
|
fileInfo={this.state.fileInfo}
|
||||||
markdownContent={this.state.markdownContent}
|
markdownContent={this.state.markdownContent}
|
||||||
editorUtilities={editorUtilities}
|
editorUtilities={editorUtilities}
|
||||||
userInfo={this.state.collabServer ? userInfo : null}
|
collabUsers={this.state.collabUsers}
|
||||||
collabServer={this.state.collabServer}
|
setFileInfoMtime={this.setFileInfoMtime}
|
||||||
|
toggleStar={this.toggleStar}
|
||||||
showFileHistory={true}
|
showFileHistory={true}
|
||||||
mode={mode}
|
setEditorMode={this.setEditorMode}
|
||||||
|
setContent={this.setContent}
|
||||||
draftID={draftID}
|
draftID={draftID}
|
||||||
isDraft={isDraft}
|
isDraft={isDraft}
|
||||||
|
mode={this.state.mode}
|
||||||
|
emitSwitchEditor={this.emitSwitchEditor}
|
||||||
hasDraft={hasDraft}
|
hasDraft={hasDraft}
|
||||||
shareLinkExpireDaysMin={shareLinkExpireDaysMin}
|
editorMode={this.state.editorMode}
|
||||||
shareLinkExpireDaysMax={shareLinkExpireDaysMax}
|
|
||||||
relatedFiles={this.state.relatedFiles}
|
relatedFiles={this.state.relatedFiles}
|
||||||
siteRoot={siteRoot}
|
siteRoot={siteRoot}
|
||||||
|
autoSaveDraft={this.autoSaveDraft}
|
||||||
|
setDraftValue={this.setDraftValue}
|
||||||
|
clearTimer={this.clearTimer}
|
||||||
openDialogs={this.openDialogs}
|
openDialogs={this.openDialogs}
|
||||||
fileTagList={this.state.fileTagList}
|
fileTagList={this.state.fileTagList}
|
||||||
|
deleteDraft={this.deleteDraft}
|
||||||
|
showDraftSaved={this.state.showDraftSaved}
|
||||||
/>
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{this.state.localDraftDialog?
|
||||||
|
<LocalDraftDialog
|
||||||
|
localDraftDialog={this.state.localDraftDialog}
|
||||||
|
deleteDraft={this.deleteDraft}
|
||||||
|
useDraft={this.useDraft}/>:
|
||||||
|
null}
|
||||||
|
{component}
|
||||||
{this.state.showMarkdownEditorDialog && (
|
{this.state.showMarkdownEditorDialog && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{this.state.showRelatedFileDialog &&
|
{this.state.showRelatedFileDialog &&
|
||||||
@@ -485,4 +844,4 @@ class MarkdownEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MarkdownEditor;
|
export default MarkdownEditor;
|
||||||
|
Reference in New Issue
Block a user