1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-06 17:33:18 +00:00

Delete markdown viewer (#3263)

* delete md viewer

* add
This commit is contained in:
Michael An
2019-04-13 11:54:14 +08:00
committed by Daniel Pan
parent 4415b3a873
commit c42bf25a1d
4 changed files with 207 additions and 549 deletions

View File

@@ -1,28 +1,18 @@
import React from 'react';
import { SeafileEditor } from '@seafile/seafile-editor';
import React, { Fragment } from 'react';
import { SeafileEditor } from '@seafile/seafile-editor/dist/editor/editor.js';
import 'whatwg-fetch';
import { Value, Document, Block } from 'slate';
import { seafileAPI } from './utils/seafile-api';
import { Utils } from './utils/utils';
import { gettext, isDocs } from './utils/constants';
import io from 'socket.io-client';
import toaster from './components/toast';
import ModalPortal from './components/modal-portal';
import EditFileTagDialog from './components/dialog/edit-filetag-dialog';
import RelatedFileDialogs from './components/dialog/related-file-dialogs';
import ShareDialog from './components/dialog/share-dialog';
import CommentDialog from './components/markdown-view/comment-dialog';
import InsertFileDialog from './components/dialog/insert-file-dialog';
import MarkdownViewerSlate from '@seafile/seafile-editor/dist/viewer/markdown-viewer-slate';
import { serialize, deserialize } from '@seafile/seafile-editor/dist/utils/slate2markdown';
import LocalDraftDialog from './components/dialog/local-draft-dialog';
import DiffViewer from '@seafile/seafile-editor/dist/viewer/diff-viewer';
import MarkdownViewerToolbar from './components/toolbar/markdown-viewer-toolbar';
import HistoryList from './components/markdown-view/history-list';
import CommentPanel from './components/file-view/comment-panel';
import OutlineView from './components/markdown-view/outline';
import Loading from './components/loading';
import { findRange } from '@seafile/slate-react';
import './css/markdown-viewer/markdown-editor.css';
@@ -274,7 +264,6 @@ class MarkdownEditor extends React.Component {
this.draftPlainValue = '';
this.state = {
markdownContent: '',
oldMarkdownContent: '',
loading: true,
mode: 'editor',
fileInfo: {
@@ -288,27 +277,20 @@ class MarkdownEditor extends React.Component {
lastModifier: '',
id: '',
},
editorMode: 'viewer',
editorMode: 'rich',
collabServer: seafileCollabServer ? seafileCollabServer : null,
relatedFiles: [],
fileTagList: [],
localDraftDialog: false,
showRelatedFileDialog: false,
showEditFileTagDialog: false,
showMarkdownEditorDialog: false,
showShareLinkDialog: false,
showCommentDialog: false,
showInsertFileDialog: false,
showDraftSaved: false,
collabUsers: userInfo ?
[{user: userInfo, is_editing: false}] : [],
commentsNumber: null,
loadingDiff: false,
value: null,
isShowComments: false,
isShowHistory: false,
isShowOutline: true,
viewMode:'list_related_file',
readOnly: true,
};
if (this.state.collabServer) {
@@ -389,11 +371,8 @@ class MarkdownEditor extends React.Component {
toggleCancel = () => {
this.setState({
showRelatedFileDialog: false,
showEditFileTagDialog: false,
showMarkdownEditorDialog: false,
showShareLinkDialog: false,
showCommentDialog: false,
showInsertFileDialog: false,
});
}
@@ -469,27 +448,8 @@ class MarkdownEditor extends React.Component {
openDialogs = (option) => {
switch(option)
{
case 'related_files':
if (this.state.relatedFiles.length > 0) {
this.setState({
showRelatedFileDialog: true,
showMarkdownEditorDialog: true,
viewMode: 'list_related_file',
});
}
else {
this.setState({
showRelatedFileDialog: true,
showMarkdownEditorDialog: true,
viewMode: 'add_related_file',
});
}
break;
case 'tags':
this.setState({
showEditFileTagDialog: true,
showMarkdownEditorDialog: true,
});
case 'help':
window.richMarkdownEditor.showHelpDialog();
break;
case 'share_link':
this.setState({
@@ -497,12 +457,6 @@ class MarkdownEditor extends React.Component {
showShareLinkDialog: true,
});
break;
case 'comment':
this.setState({
showMarkdownEditorDialog: true,
showCommentDialog: true,
});
break;
case 'insert_file':
this.setState({
showMarkdownEditorDialog: true,
@@ -525,7 +479,6 @@ class MarkdownEditor extends React.Component {
contact_email: editorUtilities.contact_email,
},
});
document.removeEventListener('selectionchange', this.setBtnPosition);
}
componentDidMount() {
@@ -562,7 +515,7 @@ class MarkdownEditor extends React.Component {
// 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',
readOnly: !(hasPermission && (isDraft || (isEditMode && !hasDraft) || (isBlankFile && !hasDraft))),
value: value,
});
});
@@ -587,11 +540,8 @@ class MarkdownEditor extends React.Component {
});
}
this.checkDraft();
this.listRelatedFiles();
this.listFileTags();
this.getCommentsNumber();
document.addEventListener('selectionchange', this.setBtnPosition);
setTimeout(() => {
let url = new URL(window.location.href);
if (url.hash) {
@@ -600,34 +550,6 @@ class MarkdownEditor extends React.Component {
}, 100);
}
listRelatedFiles = () => {
seafileAPI.listRelatedFiles(repoID, filePath).then(res => {
this.setState({
relatedFiles: res.data.related_files
});
});
}
listFileTags = () => {
seafileAPI.listFileTags(repoID, filePath).then(res => {
let fileTagList = res.data.file_tags;
for (let i = 0, length = fileTagList.length; i < length; i++) {
fileTagList[i].id = fileTagList[i].file_tag_id;
}
this.setState({
fileTagList: fileTagList
});
});
}
onRelatedFileChange = () => {
this.listRelatedFiles();
}
onFileTagChanged = () => {
this.listFileTags();
}
setFileInfoMtime = (fileInfo) => {
this.setState({
fileInfo: Object.assign({}, this.state.fileInfo, { mtime: fileInfo.mtime, id: fileInfo.id, lastModifier: fileInfo.last_modifier_name })
@@ -685,9 +607,12 @@ class MarkdownEditor extends React.Component {
window.location.href = editorUtilities.getParentDectionaryUrl();
}
onEdit = (event) => {
event.preventDefault();
this.setEditorMode('rich');
onEdit = (mode) => {
if (mode === 'rich') {
window.seafileEditor.switchToRichTextEditor();
} else if (mode === 'plain') {
window.seafileEditor.switchToPlainTextEditor();
}
}
toggleShareLinkDialog = () => {
@@ -708,242 +633,23 @@ class MarkdownEditor extends React.Component {
this.toggleCancel();
}
showDiffViewer = () => {
this.setState({
loadingDiff: false,
});
}
setDiffViewerContent = (markdownContent, oldMarkdownContent) => {
this.setState({
markdownContent: markdownContent,
oldMarkdownContent: oldMarkdownContent
});
this.showDiffViewer();
}
reloadDiffContent = () =>{
this.setState({
loadingDiff: true,
});
}
setBtnPosition = (e) => {
if (!this.state.isShowComments) return;
const nativeSelection = window.getSelection();
if (!nativeSelection.rangeCount) {
this.range = null;
return;
}
if (nativeSelection.isCollapsed === false) {
const nativeRange = nativeSelection.getRangeAt(0);
const focusNode = nativeSelection.focusNode;
if ((focusNode.tagName === 'I') ||
(focusNode.nodeType !== 3 && focusNode.getAttribute('class') === 'language-type')) {
// fix select last paragraph
let fragment = nativeRange.cloneContents();
let startNode = fragment.firstChild.firstChild;
if (!startNode) return;
let newNativeRange = document.createRange();
newNativeRange.setStartBefore(startNode);
newNativeRange.setEndAfter(startNode);
let editor = {value: this.state.value};
this.range = findRange(nativeRange, editor);
}
else {
let editor = {value: this.state.value};
this.range = findRange(nativeRange, editor);
}
if (!this.range) return;
let rect = nativeRange.getBoundingClientRect();
// fix Safari bug
if (navigator.userAgent.indexOf('Chrome') < 0 && navigator.userAgent.indexOf('Safari') > 0) {
if (nativeRange.collapsed && rect.top == 0 && rect.height == 0) {
if (nativeRange.startOffset == 0) {
nativeRange.setEnd(nativeRange.endContainer, 1);
} else {
nativeRange.setStart(nativeRange.startContainer, nativeRange.startOffset - 1);
}
rect = nativeRange.getBoundingClientRect();
if (rect.top == 0 && rect.height == 0) {
if (nativeRange.getClientRects().length) {
rect = nativeRange.getClientRects()[0];
}
}
}
}
let style = this.refs.commentbtn.style;
style.top = `${rect.top - 63 + this.refs.markdownContainer.scrollTop}px`;
style.right = '0px';
}
else {
let style = this.refs.commentbtn.style;
style.top = '-1000px';
}
}
addComment = (e) => {
e.stopPropagation();
this.getQuote();
this.openDialogs('comment');
}
getQuote = () => {
let range = this.range;
if (!range) return;
const { document } = this.state.value;
let { anchor, focus } = range;
const anchorText = document.getNode(anchor.key);
const focusText = document.getNode(focus.key);
const anchorInline = document.getClosestInline(anchor.key);
const focusInline = document.getClosestInline(focus.key);
// COMPAT: If the selection is at the end of a non-void inline node, and
// there is a node after it, put it in the node after instead. This
// standardizes the behavior, since it's indistinguishable to the user.
if (anchorInline && anchor.offset == anchorText.text.length) {
const block = document.getClosestBlock(anchor.key);
const nextText = block.getNextText(anchor.key);
if (nextText) {
range = range.moveAnchorTo(nextText.key, 0);
}
}
if (focusInline && focus.offset == focusText.text.length) {
const block = document.getClosestBlock(focus.key);
const nextText = block.getNextText(focus.key);
if (nextText) {
range = range.moveFocusTo(nextText.key, 0);
}
}
let fragment = document.getFragmentAtRange(range);
let nodes = this.removeNullNode(fragment.nodes);
let newFragment = Document.create({
nodes: nodes
});
let newValue = Value.create({
document: newFragment
});
this.quote = serialize(newValue.toJSON());
let selection = document.createSelection(range);
selection = selection.setIsFocused(true);
this.setState({
commentPosition: selection.anchor.path
});
}
removeNullNode = (oldNodes) => {
let newNodes = [];
oldNodes.map((node) => {
const text = node.text.trim();
const childNodes = node.nodes;
if (!text) return;
if ((childNodes && childNodes.size === 1) || (!childNodes)) {
newNodes.push(node);
}
else if (childNodes.size > 1) {
let nodes = this.removeNullNode(childNodes);
let newNode = Block.create({
nodes: nodes,
data: node.data,
key: node.key,
type: node.type
});
newNodes.push(newNode);
}
});
return newNodes;
}
scrollToNode = (node) => {
let url = new URL(window.location.href);
url.set('hash', 'user-content-' + node.text);
window.location.href = url.toString();
}
findScrollContainer = (el, window) => {
let parent = el.parentNode;
const OVERFLOWS = ['auto', 'overlay', 'scroll'];
let scroller;
while (!scroller) {
if (!parent.parentNode) break;
const style = window.getComputedStyle(parent);
const { overflowY } = style;
if (OVERFLOWS.includes(overflowY)) {
scroller = parent;
break;
}
parent = parent.parentNode;
}
if (!scroller) {
return window.document.body;
}
return scroller;
}
scrollToQuote = (path) => {
if (!path) return;
const win = window;
if (path.length > 2) {
// deal with code block or chart
path[0] = path[0] > 1 ? path[0] - 1 : path[0] + 1;
path = path.slice(0, 1);
}
let node = this.state.value.document.getNode(path);
if (!node) {
path = path.slice(0, 1);
node = this.state.value.document.getNode(path);
}
if (node) {
let element = win.document.querySelector(`[data-key="${node.key}"]`);
while (element.tagName === 'CODE') {
element = element.parentNode;
}
const scroller = this.findScrollContainer(element, win);
const isWindow = scroller == win.document.body || scroller == win.document.documentElement;
if (isWindow) {
win.scrollTo(0, element.offsetTop);
} else {
scroller.scrollTop = element.offsetTop;
}
}
}
toggleHistory = () => {
if (!isDocs) {
window.location.href = siteRoot + 'repo/file_revisions/' + repoID + '/?p=' + Utils.encodePath(filePath);
return;
}
if (this.state.isShowHistory) {
this.setState({
isShowHistory: false,
isShowOutline: true,
isShowComments: false,
});
} else {
this.setState({
isShowHistory: true,
isShowOutline: false,
isShowComments: false,
});
}
window.location.href = siteRoot + 'repo/file_revisions/' + repoID + '/?p=' + Utils.encodePath(filePath);
}
toggleCommentList = () => {
if (this.state.isShowComments) {
this.setState({
isShowHistory: false,
isShowOutline: true,
isShowComments: false,
});
} else {
this.setState({
isShowHistory: false,
isShowOutline: false,
isShowComments: true,
});
}
this.setState({ isShowComments: !this.state.isShowComments });
}
getInsertLink = (repoID, filePath) => {
@@ -965,8 +671,6 @@ class MarkdownEditor extends React.Component {
render() {
let component;
let sidePanel = this.state.isShowHistory ? true : false;
let markdownViewer = sidePanel ? 'seafile-md-viewer-slate side-panel-on' :
(this.state.isShowComments ? 'seafile-md-viewer-slate comment-on' : 'seafile-md-viewer-slate');
if (this.state.loading) {
return (
<div className="empty-loading-page">
@@ -974,109 +678,61 @@ class MarkdownEditor extends React.Component {
</div>
);
} else if (this.state.mode === 'editor') {
if (this.state.editorMode === 'viewer') {
component = (
<div className="seafile-md-viewer d-flex flex-column">
<MarkdownViewerToolbar
isDocs={isDocs}
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}
toggleShareLinkDialog={this.toggleShareLinkDialog}
onEdit={this.onEdit}
toggleNewDraft={editorUtilities.createDraftFile}
commentsNumber={this.state.commentsNumber}
toggleCommentList={this.toggleCommentList}
showFileHistory={this.state.isShowHistory ? false : true }
toggleHistory={this.toggleHistory}
/>
<div className="seafile-md-viewer d-flex">
<div className={sidePanel ? 'seafile-md-viewer-container side-panel-on' : 'seafile-md-viewer-container'} ref="markdownContainer">
{
this.state.isShowHistory ?
<div className="diff-container">
<div className="diff-wrapper article">
{ this.state.loadingDiff ?
<Loading/> :
<DiffViewer
newMarkdownContent={this.state.markdownContent}
oldMarkdownContent={this.state.oldMarkdownContent}
/>
}
</div>
</div>
:
<div className={markdownViewer}>
<MarkdownViewerSlate
relatedFiles={this.state.relatedFiles}
siteRoot={siteRoot}
value={this.state.value}
/>
{this.state.isShowComments &&
<i className="fa fa-plus-square seafile-viewer-comment-btn" ref="commentbtn" onMouseDown={this.addComment}></i>}
</div>
}
{
this.state.isShowOutline &&
<OutlineView
isViewer={true}
document={this.state.value.document}
scrollToNode={this.scrollToNode}
/>
}
{this.state.isShowComments &&
<CommentPanel toggleCommentPanel={this.toggleCommentList} commentsNumber={this.state.commentsNumber}/>}
</div>
<div className="seafile-md-viewer-side-panel">
{
this.state.isShowHistory &&
<HistoryList
editorUtilities={editorUtilities}
showDiffViewer={this.showDiffViewer}
setDiffViewerContent={this.setDiffViewerContent}
reloadDiffContent={this.reloadDiffContent}
toggleHistoryPanel={this.toggleHistory}
/>
}
</div>
component = (
<Fragment>
<MarkdownViewerToolbar
isDocs={isDocs}
hasDraft={hasDraft}
isDraft={isDraft}
editorUtilities={editorUtilities}
collabUsers={this.state.collabUsers}
fileInfo={this.state.fileInfo}
toggleStar={this.toggleStar}
backToParentDirectory={this.backToParentDirectory}
openDialogs={this.openDialogs}
toggleShareLinkDialog={this.toggleShareLinkDialog}
onEdit={this.onEdit}
toggleNewDraft={editorUtilities.createDraftFile}
commentsNumber={this.state.commentsNumber}
toggleCommentList={this.toggleCommentList}
showFileHistory={this.state.isShowHistory ? false : true }
toggleHistory={this.toggleHistory}
readOnly={this.state.readOnly}
mode={this.state.mode}
editorMode={this.state.editorMode}
/>
<SeafileEditor
fileInfo={this.state.fileInfo}
markdownContent={this.state.markdownContent}
editorUtilities={editorUtilities}
collabUsers={this.state.collabUsers}
setFileInfoMtime={this.setFileInfoMtime}
toggleStar={this.toggleStar}
showFileHistory={true}
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}
showDraftSaved={this.state.showDraftSaved}
readOnly={this.state.readOnly}
/>
{this.state.isShowComments &&
<div className="seafile-md-comment">
<CommentPanel toggleCommentPanel={this.toggleCommentList} commentsNumber={this.state.commentsNumber}/>
</div>
</div>
);
} else {
component = <SeafileEditor
fileInfo={this.state.fileInfo}
markdownContent={this.state.markdownContent}
editorUtilities={editorUtilities}
collabUsers={this.state.collabUsers}
setFileInfoMtime={this.setFileInfoMtime}
toggleStar={this.toggleStar}
showFileHistory={true}
setEditorMode={this.setEditorMode}
setContent={this.setContent}
draftID={draftID}
isDraft={isDraft}
mode={this.state.mode}
emitSwitchEditor={this.emitSwitchEditor}
hasDraft={hasDraft}
editorMode={this.state.editorMode}
relatedFiles={this.state.relatedFiles}
siteRoot={siteRoot}
autoSaveDraft={this.autoSaveDraft}
setDraftValue={this.setDraftValue}
clearTimer={this.clearTimer}
openDialogs={this.openDialogs}
fileTagList={this.state.fileTagList}
deleteDraft={this.deleteDraft}
showDraftSaved={this.state.showDraftSaved}
/>;
}
}
</Fragment>
);
return (
<React.Fragment>
@@ -1093,30 +749,6 @@ class MarkdownEditor extends React.Component {
{component}
{this.state.showMarkdownEditorDialog && (
<React.Fragment>
{this.state.showEditFileTagDialog &&
<ModalPortal>
<EditFileTagDialog
repoID={repoID}
filePath={filePath}
fileTagList={this.state.fileTagList}
toggleCancel={this.toggleCancel}
onFileTagChanged={this.onFileTagChanged}
/>
</ModalPortal>
}
{this.state.showRelatedFileDialog &&
<ModalPortal>
<RelatedFileDialogs
repoID={repoID}
filePath={filePath}
relatedFiles={this.state.relatedFiles}
toggleCancel={this.toggleCancel}
onRelatedFileChange={this.onRelatedFileChange}
dirent={this.state.fileInfo}
viewMode={this.state.viewMode}
/>
</ModalPortal>
}
{this.state.showInsertFileDialog &&
<ModalPortal>
<InsertFileDialog
@@ -1140,17 +772,6 @@ class MarkdownEditor extends React.Component {
/>
</ModalPortal>
}
{this.state.showCommentDialog &&
<ModalPortal>
<CommentDialog
toggleCommentDialog={this.toggleCancel}
editorUtilities={editorUtilities}
onCommentAdded={this.onCommentAdded}
commentPosition={this.state.commentPosition}
quote={this.quote}
/>
</ModalPortal>
}
</React.Fragment>
)}
</React.Fragment>