1
0
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:
杨顺强
2023-12-08 11:20:12 +08:00
parent da89c9a338
commit 2e2d830c7d
13 changed files with 1034 additions and 1214 deletions

View File

@@ -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()}