mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-22 03:47:09 +00:00
optimize wiki module
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { MarkdownViewer } from '@seafile/seafile-editor';
|
||||
import { gettext, repoID, slug, serviceURL, isPublicWiki, sharedToken, mediaUrl } from '../utils/constants';
|
||||
import { EXTERNAL_EVENTS, EventBus, MarkdownViewer } from '@seafile/seafile-editor';
|
||||
import { gettext, isPublicWiki, mediaUrl, repoID, serviceURL, sharedToken, slug } from '../utils/constants';
|
||||
import Loading from './loading';
|
||||
import { Utils } from '../utils/utils';
|
||||
|
||||
@@ -25,109 +25,30 @@ class WikiMarkdownViewer extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
activeTitleIndex: 0,
|
||||
};
|
||||
this.markdownContainer = React.createRef();
|
||||
this.links = [];
|
||||
this.titlesInfo = [];
|
||||
this.scrollRef = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Bind event when first loaded
|
||||
this.links = document.querySelectorAll(`.${contentClass} a`);
|
||||
this.links.forEach(link => {
|
||||
link.addEventListener('click', this.onLinkClick);
|
||||
});
|
||||
|
||||
this.getTitlesInfo();
|
||||
const eventBus = EventBus.getInstance();
|
||||
this.unsubscribeLinkClick = eventBus.subscribe(EXTERNAL_EVENTS.ON_LINK_CLICK, this.onLinkClick);
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
if (this.props.markdownContent === nextProps.markdownContent) {
|
||||
return;
|
||||
}
|
||||
// Unbound event when updating
|
||||
this.links.forEach(link => {
|
||||
link.removeEventListener('click', this.onLinkClick);
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
// Update completed, rebind event
|
||||
this.links = document.querySelectorAll(`.${contentClass} a`);
|
||||
this.links.forEach(link => {
|
||||
link.addEventListener('click', this.onLinkClick);
|
||||
});
|
||||
if (this.titlesInfo.length === 0) {
|
||||
this.getTitlesInfo();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// Unbound events when the component is destroyed
|
||||
this.links.forEach(link => {
|
||||
link.removeEventListener('click', this.onLinkClick);
|
||||
});
|
||||
this.unsubscribeLinkClick();
|
||||
}
|
||||
|
||||
getTitlesInfo = () => {
|
||||
let titlesInfo = [];
|
||||
const titleDom = document.querySelectorAll('h1[id^="user-content"]')[0];
|
||||
if (titleDom) {
|
||||
const id = titleDom.getAttribute('id');
|
||||
let content = id && id.replace('user-content-', '');
|
||||
content = content ? `${content} - ${slug}` : slug;
|
||||
Utils.updateTabTitle(content);
|
||||
}
|
||||
let headingList = document.querySelectorAll('h2[id^="user-content"], h3[id^="user-content"]');
|
||||
for (let i = 0; i < headingList.length; i++) {
|
||||
titlesInfo.push(headingList[i].offsetTop);
|
||||
}
|
||||
this.titlesInfo = titlesInfo;
|
||||
};
|
||||
|
||||
onLinkClick = (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
let link = '';
|
||||
if (event.target.tagName !== 'A') {
|
||||
let target = event.target.parentNode;
|
||||
while (target.tagName !== 'A') {
|
||||
target = target.parentNode;
|
||||
}
|
||||
link = target.href;
|
||||
} else {
|
||||
link = event.target.href;
|
||||
let target = event.target;
|
||||
while (!target.dataset || !target.dataset.url) {
|
||||
target = target.parentNode;
|
||||
}
|
||||
if (!target) return;
|
||||
link = target.dataset.url;
|
||||
this.props.onLinkClick(link);
|
||||
};
|
||||
|
||||
onScrollHandler = () => {
|
||||
const contentScrollTop = this.markdownContainer.current.scrollTop + 180;
|
||||
let titlesLength = this.titlesInfo.length;
|
||||
let activeTitleIndex;
|
||||
if (contentScrollTop <= this.titlesInfo[0]) {
|
||||
activeTitleIndex = 0;
|
||||
this.setState({activeTitleIndex: activeTitleIndex});
|
||||
return;
|
||||
}
|
||||
if (contentScrollTop > this.titlesInfo[titlesLength - 1]) {
|
||||
activeTitleIndex = this.titlesInfo.length - 1;
|
||||
this.setState({activeTitleIndex: activeTitleIndex});
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < titlesLength; i++) {
|
||||
if (contentScrollTop > this.titlesInfo[i]) {
|
||||
continue;
|
||||
} else {
|
||||
activeTitleIndex = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.setState({activeTitleIndex: activeTitleIndex});
|
||||
};
|
||||
|
||||
changeInlineNode = (item) => {
|
||||
let url, imagePath;
|
||||
|
||||
@@ -149,21 +70,21 @@ class WikiMarkdownViewer extends React.Component {
|
||||
}
|
||||
item.data.src = serviceURL + '/view-image-via-public-wiki/?slug=' + slug + '&path=' + imagePath;
|
||||
} else if (item.type == 'link') { // change link url
|
||||
url = item.data.href;
|
||||
url = item.url;
|
||||
if (Utils.isInternalFileLink(url, repoID)) { // change file url
|
||||
if (Utils.isInternalMarkdownLink(url, repoID)) {
|
||||
let path = Utils.getPathFromInternalMarkdownLink(url, repoID);
|
||||
// replace url
|
||||
item.data.href = serviceURL + '/published/' + slug + path;
|
||||
item.url = serviceURL + '/published/' + slug + path;
|
||||
} else {
|
||||
item.data.href = url.replace(/(.*)lib\/([-0-9a-f]{36})\/file(.*)/g, (match, p1, p2, p3) => {
|
||||
item.url = url.replace(/(.*)lib\/([-0-9a-f]{36})\/file(.*)/g, (match, p1, p2, p3) => {
|
||||
return `${p1}d/${sharedToken}/files/?p=${p3}&dl=1`;
|
||||
});
|
||||
}
|
||||
} else if (Utils.isInternalDirLink(url, repoID)) { // change dir url
|
||||
let path = Utils.getPathFromInternalDirLink(url, repoID);
|
||||
// replace url
|
||||
item.data.href = serviceURL + '/published/' + slug + path;
|
||||
item.url = serviceURL + '/published/' + slug + path;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,30 +97,16 @@ class WikiMarkdownViewer extends React.Component {
|
||||
};
|
||||
|
||||
renderMarkdown = () => {
|
||||
let isTOCShow = true;
|
||||
if (this.props.isTOCShow === false) {
|
||||
isTOCShow = false;
|
||||
}
|
||||
if (this.props.isWiki) {
|
||||
return (
|
||||
<MarkdownViewer
|
||||
showTOC={isTOCShow}
|
||||
scriptSource={mediaUrl + 'js/mathjax/tex-svg.js'}
|
||||
markdownContent={this.props.markdownContent}
|
||||
activeTitleIndex={this.state.activeTitleIndex}
|
||||
modifyValueBeforeRender={this.modifyValueBeforeRender}
|
||||
/>
|
||||
);
|
||||
}
|
||||
const { isTOCShow = true, isWiki, markdownContent } = this.props;
|
||||
const props = {
|
||||
isShowOutline: isTOCShow,
|
||||
mathJaxSource: `${mediaUrl}js/mathjax/tex-svg.js`,
|
||||
value: markdownContent,
|
||||
scrollRef: this.scrollRef,
|
||||
...(isWiki && {beforeRenderCallback: this.modifyValueBeforeRender})
|
||||
};
|
||||
|
||||
return (
|
||||
<MarkdownViewer
|
||||
showTOC={isTOCShow}
|
||||
scriptSource={mediaUrl + 'js/mathjax/tex-svg.js'}
|
||||
markdownContent={this.props.markdownContent}
|
||||
activeTitleIndex={this.state.activeTitleIndex}
|
||||
/>
|
||||
);
|
||||
return <MarkdownViewer {...props} />;
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -209,7 +116,7 @@ class WikiMarkdownViewer extends React.Component {
|
||||
// In dir-column-file repoID is one of props, width is 100%; In wiki-viewer repoID is not props, width isn't 100%
|
||||
let contentClassName = `${this.props.repoID ? contentClass + ' w-100' : contentClass}`;
|
||||
return (
|
||||
<div ref={this.markdownContainer} className="wiki-page-container" onScroll={this.onScrollHandler.bind(this)}>
|
||||
<div ref={this.scrollRef} className="wiki-page-container">
|
||||
<div className={contentClassName}>
|
||||
{this.props.children}
|
||||
{this.renderMarkdown()}
|
||||
|
Reference in New Issue
Block a user