1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-06-30 17:02:15 +00:00
seahub/frontend/src/wiki.js

507 lines
14 KiB
JavaScript
Raw Normal View History

2018-08-06 10:29:12 +00:00
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
2018-08-30 07:10:52 +00:00
import SidePanel from './pages/wiki/side-panel';
import MainPanel from './pages/wiki/main-panel';
2018-08-06 10:29:12 +00:00
import moment from 'moment';
2018-11-30 03:22:58 +00:00
import { slug, repoID, siteRoot, initialPath } from './utils/constants';
2018-09-04 09:16:50 +00:00
import editorUtilities from './utils/editor-utilties';
2018-09-19 01:57:17 +00:00
import Node from './components/tree-view/node';
import Tree from './components/tree-view/tree';
2018-08-06 10:29:12 +00:00
import './assets/css/fa-solid.css';
import './assets/css/fa-regular.css';
import './assets/css/fontawesome.css';
2018-09-21 06:16:15 +00:00
import './css/layout.css';
2018-08-06 10:29:12 +00:00
import './css/side-panel.css';
import './css/wiki.css';
2018-09-19 01:57:17 +00:00
import './css/toolbar.css';
2018-08-17 04:23:55 +00:00
import './css/search.css';
2018-08-06 10:29:12 +00:00
class Wiki extends Component {
constructor(props) {
super(props);
this.state = {
content: '',
2018-09-04 09:16:50 +00:00
tree_data: new Tree(),
2018-08-06 10:29:12 +00:00
closeSideBar: false,
filePath: '',
latestContributor: '',
lastModified: '',
2018-08-17 04:23:55 +00:00
permission: '',
isFileLoading: false,
2018-09-04 09:16:50 +00:00
changedNode: null,
isViewFileState: true
2018-08-06 10:29:12 +00:00
};
window.onpopstate = this.onpopstate;
}
componentDidMount() {
2018-11-30 03:22:58 +00:00
this.initWikiData(initialPath);
2018-08-06 10:29:12 +00:00
}
2018-09-05 02:44:58 +00:00
initWikiData(filePath){
this.setState({isFileLoading: true});
editorUtilities.getFiles().then((files) => {
// construct the tree object
var treeData = new Tree();
treeData.parseListToTree(files);
let node = treeData.getNodeByPath(filePath);
2018-09-12 03:50:41 +00:00
treeData.expandNode(node);
2018-09-05 02:44:58 +00:00
if (node.isDir()) {
this.exitViewFileState(treeData, node);
this.setState({isFileLoading: false});
} else {
2018-09-12 03:50:41 +00:00
treeData.expandNode(node);
2018-09-05 02:44:58 +00:00
editorUtilities.getWikiFileContent(slug, filePath).then(res => {
this.setState({
tree_data: treeData,
content: res.data.content,
latestContributor: res.data.latest_contributor,
lastModified: moment.unix(res.data.last_modified).fromNow(),
permission: res.data.permission,
filePath: filePath,
isFileLoading: false
2018-09-19 01:57:17 +00:00
});
2018-09-05 02:44:58 +00:00
});
const hash = window.location.hash;
2018-11-29 09:55:14 +00:00
let fileUrl = siteRoot + 'wikis/' + slug + filePath + hash;
2018-09-05 02:44:58 +00:00
window.history.pushState({urlPath: fileUrl, filePath: filePath}, filePath, fileUrl);
}
}, () => {
this.setState({
isLoadFailed: true
2018-09-19 01:57:17 +00:00
});
});
2018-08-06 10:29:12 +00:00
}
initMainPanelData(filePath){
2018-09-04 09:16:50 +00:00
this.setState({isFileLoading: true});
editorUtilities.getWikiFileContent(slug, filePath)
.then(res => {
this.setState({
content: res.data.content,
latestContributor: res.data.latest_contributor,
lastModified: moment.unix(res.data.last_modified).fromNow(),
permission: res.data.permission,
filePath: filePath,
isFileLoading: false
2018-09-19 01:57:17 +00:00
});
});
2018-08-06 10:29:12 +00:00
2018-09-19 01:57:17 +00:00
const hash = window.location.hash;
2018-11-29 09:55:14 +00:00
let fileUrl = siteRoot + 'wikis/' + slug + filePath + hash;
2018-09-19 01:57:17 +00:00
window.history.pushState({urlPath: fileUrl, filePath: filePath}, filePath, fileUrl);
2018-08-06 10:29:12 +00:00
}
onLinkClick = (event) => {
const url = event.target.href;
if (this.isInternalMarkdownLink(url)) {
let path = this.getPathFromInternalMarkdownLink(url);
2018-09-04 09:16:50 +00:00
this.initMainPanelData(path);
2018-09-07 14:25:13 +00:00
} else if (this.isInternalDirLink(url)) {
let path = this.getPathFromInternalDirLink(url);
this.initWikiData(path);
2018-08-28 03:34:03 +00:00
} else {
window.location.href = url;
2018-08-06 10:29:12 +00:00
}
}
2018-09-04 09:16:50 +00:00
onpopstate = (event) => {
if (event.state && event.state.filePath) {
this.initMainPanelData(event.state.filePath);
}
}
onSearchedClick = (item) => {
let path = item.path;
2018-11-30 03:22:58 +00:00
if (this.state.filePath !== path) {
2018-09-04 09:16:50 +00:00
this.initMainPanelData(path);
let tree = this.state.tree_data.clone();
let node = tree.getNodeByPath(path);
2018-09-12 03:50:41 +00:00
tree.expandNode(node);
this.enterViewFileState(tree, node, node.path);
2018-08-17 04:23:55 +00:00
}
}
2018-09-04 09:16:50 +00:00
onMainNavBarClick = (nodePath) => {
let tree = this.state.tree_data.clone();
let node = tree.getNodeByPath(nodePath);
2018-09-12 03:50:41 +00:00
tree.expandNode(node);
2018-09-04 09:16:50 +00:00
this.exitViewFileState(tree, node);
// update location url
2018-11-29 09:55:14 +00:00
let fileUrl = siteRoot + 'wikis/' + slug + node.path;
2018-09-04 09:16:50 +00:00
window.history.pushState({urlPath: fileUrl, filePath: node.path},node.path, fileUrl);
}
onMainNodeClick = (node) => {
let tree = this.state.tree_data.clone();
2018-09-12 03:50:41 +00:00
tree.expandNode(node);
2018-08-06 10:29:12 +00:00
if (node.isMarkdown()) {
2018-09-04 09:16:50 +00:00
this.initMainPanelData(node.path);
this.enterViewFileState(tree, node, node.path);
} else if (node.isDir()){
this.exitViewFileState(tree, node);
} else {
const w=window.open('about:blank');
2018-11-29 09:55:14 +00:00
const url = siteRoot + 'lib/' + repoID + '/file' + node.path;
w.location.href = url;
2018-08-06 10:29:12 +00:00
}
}
2018-11-30 03:22:58 +00:00
onNodeClick = (node) => {
2018-09-04 09:16:50 +00:00
if (node instanceof Node && node.isMarkdown()){
let tree = this.state.tree_data.clone();
this.initMainPanelData(node.path);
this.enterViewFileState(tree, node, node.path);
} else if(node instanceof Node && node.isDir()){
let tree = this.state.tree_data.clone();
2018-09-12 03:50:41 +00:00
if (this.state.filePath === node.path) {
if (node.isExpanded) {
tree.collapseNode(node);
} else {
tree.expandNode(node);
}
}
2018-09-04 09:16:50 +00:00
this.exitViewFileState(tree, node);
} else {
const w=window.open('about:blank');
2018-11-29 09:55:14 +00:00
const url = siteRoot + 'lib/' + repoID + '/file' + node.path;
2018-09-04 09:16:50 +00:00
w.location.href = url;
}
2018-08-06 10:29:12 +00:00
}
2018-11-30 03:22:58 +00:00
onDirCollapse = (node) => {
2018-09-04 09:16:50 +00:00
let tree = this.state.tree_data.clone();
let findNode = tree.getNodeByPath(node.path);
findNode.isExpanded = !findNode.isExpanded;
this.setState({tree_data: tree});
2018-08-06 10:29:12 +00:00
}
onMenuClick = () => {
this.setState({
closeSideBar: !this.state.closeSideBar,
2018-09-19 01:57:17 +00:00
});
2018-08-06 10:29:12 +00:00
}
onCloseSide = () => {
this.setState({
closeSideBar: !this.state.closeSideBar,
2018-09-19 01:57:17 +00:00
});
2018-08-06 10:29:12 +00:00
}
2018-09-04 09:16:50 +00:00
onAddFolderNode = (dirPath) => {
editorUtilities.createDir(dirPath).then(res => {
let tree = this.state.tree_data.clone();
let name = this.getFileNameByPath(dirPath);
2018-09-19 01:57:17 +00:00
let index = dirPath.lastIndexOf('/');
2018-09-04 09:16:50 +00:00
let parentPath = dirPath.substring(0, index);
if (!parentPath) {
2018-09-19 01:57:17 +00:00
parentPath = '/';
2018-09-04 09:16:50 +00:00
}
2018-09-19 01:57:17 +00:00
let node = this.buildNewNode(name, 'dir');
2018-09-04 09:16:50 +00:00
let parentNode = tree.getNodeByPath(parentPath);
tree.addNodeToParent(node, parentNode);
if (this.state.isViewFileState) {
2018-09-12 03:50:41 +00:00
tree.expandNode(node);
2018-09-04 09:16:50 +00:00
this.setState({
tree_data: tree,
changedNode: node
2018-09-19 01:57:17 +00:00
});
2018-09-04 09:16:50 +00:00
} else {
this.exitViewFileState(tree, parentNode);
}
2018-09-19 01:57:17 +00:00
});
2018-09-04 09:16:50 +00:00
}
onAddFileNode = (filePath) => {
editorUtilities.createFile(filePath).then(res => {
let tree = this.state.tree_data.clone();
let name = this.getFileNameByPath(filePath);
2018-09-19 01:57:17 +00:00
let index = filePath.lastIndexOf('/');
2018-09-04 09:16:50 +00:00
let parentPath = filePath.substring(0, index);
if (!parentPath) {
2018-09-19 01:57:17 +00:00
parentPath = '/';
2018-09-04 09:16:50 +00:00
}
2018-09-19 01:57:17 +00:00
let node = this.buildNewNode(name, 'file');
2018-09-04 09:16:50 +00:00
let parentNode = tree.getNodeByPath(parentPath);
tree.addNodeToParent(node, parentNode);
if (this.state.isViewFileState) {
2018-09-12 03:50:41 +00:00
tree.expandNode(node);
2018-09-04 09:16:50 +00:00
this.setState({
tree_data: tree,
changedNode: node
2018-09-19 01:57:17 +00:00
});
2018-09-04 09:16:50 +00:00
} else {
this.exitViewFileState(tree, parentNode);
}
2018-09-19 01:57:17 +00:00
});
2018-09-04 09:16:50 +00:00
}
onRenameNode = (node, newName) => {
let tree = this.state.tree_data.clone();
let filePath = node.path;
if (node.isMarkdown()) {
editorUtilities.renameFile(filePath, newName).then(res => {
let cloneNode = node.clone();
2018-09-19 01:57:17 +00:00
tree.updateNodeParam(node, 'name', newName);
2018-09-04 09:16:50 +00:00
node.name = newName;
let date = new Date().getTime()/1000;
2018-09-19 01:57:17 +00:00
tree.updateNodeParam(node, 'last_update_time', moment.unix(date).fromNow());
2018-09-04 09:16:50 +00:00
node.last_update_time = moment.unix(date).fromNow();
2018-09-04 09:16:50 +00:00
if (this.state.isViewFileState) {
if (this.isModifyCurrentFile(cloneNode)) {
2018-09-12 03:50:41 +00:00
tree.expandNode(node);
2018-09-04 09:16:50 +00:00
this.setState({
tree_data: tree,
changedNode: node
});
this.initMainPanelData(node.path);
} else {
this.setState({tree_data: tree});
}
} else {
let parentNode = tree.findNodeParentFromTree(node);
this.setState({
tree_data: tree,
changedNode: parentNode
});
2018-09-04 09:16:50 +00:00
}
2018-09-19 01:57:17 +00:00
});
2018-09-04 09:16:50 +00:00
} else if (node.isDir()) {
editorUtilities.renameDir(filePath, newName).then(res => {
2018-09-05 02:44:58 +00:00
let currentFilePath = this.state.filePath;
2018-09-04 09:16:50 +00:00
let currentFileNode = tree.getNodeByPath(currentFilePath);
let nodePath = node.path;
2018-09-19 01:57:17 +00:00
tree.updateNodeParam(node, 'name', newName);
node.name = newName;
let date = new Date().getTime()/1000;
2018-09-19 01:57:17 +00:00
tree.updateNodeParam(node, 'last_update_time', moment.unix(date).fromNow());
2018-09-04 09:16:50 +00:00
node.last_update_time = moment.unix(date).fromNow();
2018-09-05 02:44:58 +00:00
2018-09-04 09:16:50 +00:00
if (this.state.isViewFileState) {
if (currentFilePath.indexOf(nodePath) > -1) {
2018-09-12 03:50:41 +00:00
tree.expandNode(currentFileNode);
2018-09-04 09:16:50 +00:00
this.setState({
tree_data: tree,
changedNode: currentFileNode
});
this.initMainPanelData(currentFileNode.path);
} else {
this.setState({tree_data: tree});
}
} else {
if (nodePath === currentFilePath) { // old node
2018-09-12 03:50:41 +00:00
tree.expandNode(node);
this.exitViewFileState(tree, node);
} else if (node.path.indexOf(currentFilePath) > -1) { // new node
2018-09-12 03:50:41 +00:00
tree.expandNode(currentFileNode);
2018-09-04 09:16:50 +00:00
this.exitViewFileState(tree, currentFileNode);
} else {
this.setState({tree_data: tree});
}
}
2018-09-19 01:57:17 +00:00
});
2018-09-04 09:16:50 +00:00
}
}
onDeleteNode = (node) => {
let filePath = node.path;
2018-09-29 07:47:53 +00:00
if (node.isDir()) {
2018-09-04 09:16:50 +00:00
editorUtilities.deleteDir(filePath);
} else {
2018-09-29 07:47:53 +00:00
editorUtilities.deleteFile(filePath);
2018-09-04 09:16:50 +00:00
}
2018-09-29 07:47:53 +00:00
2018-09-04 09:16:50 +00:00
let isCurrentFile = false;
if (node.isDir()) {
isCurrentFile = this.isModifyContainsCurrentFile(node);
} else {
isCurrentFile = this.isModifyCurrentFile(node);
}
let tree = this.state.tree_data.clone();
if (this.state.isViewFileState) {
if (isCurrentFile) {
let homeNode = this.getHomeNode(tree);
2018-09-12 03:50:41 +00:00
tree.expandNode(homeNode);
2018-09-04 09:16:50 +00:00
this.setState({
tree_data: tree,
changedNode: homeNode
2018-09-19 01:57:17 +00:00
});
2018-09-04 09:16:50 +00:00
this.initMainPanelData(homeNode.path);
} else {
2018-09-19 01:57:17 +00:00
this.setState({tree_data: tree});
2018-09-04 09:16:50 +00:00
}
} else {
let parentNode = tree.getNodeByPath(this.state.filePath);
let isChild = tree.isNodeChild(parentNode, node);
if (isChild) {
this.exitViewFileState(tree, parentNode);
} else {
this.setState({tree_data: tree});
}
}
tree.deleteNode(node);
}
enterViewFileState(newTree, newNode, newPath) {
this.setState({
tree_data: newTree,
changedNode: newNode,
filePath: newPath,
isViewFileState: true
});
}
exitViewFileState(newTree, newNode) {
this.setState({
tree_data: newTree,
changedNode: newNode,
filePath: newNode.path,
isViewFileState: false
});
2018-11-29 09:55:14 +00:00
let fileUrl = siteRoot + 'wikis/' + slug + newNode.path;
2018-09-04 09:16:50 +00:00
window.history.pushState({urlPath: fileUrl, filePath: newNode.path}, newNode.path, fileUrl);
}
getFileNameByPath(path) {
2018-09-19 01:57:17 +00:00
let index = path.lastIndexOf('/');
2018-09-04 09:16:50 +00:00
if (index === -1) {
2018-09-19 01:57:17 +00:00
return '';
2018-09-04 09:16:50 +00:00
}
return path.slice(index+1);
}
getHomeNode(treeData) {
2018-09-19 01:57:17 +00:00
return treeData.getNodeByPath('/home.md');
2018-09-04 09:16:50 +00:00
}
buildNewNode(name, type) {
let date = new Date().getTime()/1000;
let node = new Node({
2018-09-19 01:57:17 +00:00
name : name,
type: type,
size: '0',
last_update_time: moment.unix(date).fromNow(),
isExpanded: false,
children: []
2018-09-04 09:16:50 +00:00
});
return node;
}
isModifyCurrentFile(node) {
let nodeName = node.name;
let fileName = this.getFileNameByPath(this.state.filePath);
return nodeName === fileName;
}
isModifyContainsCurrentFile(node) {
let filePath = this.state.filePath;
let nodePath = node.path;
if (filePath.indexOf(nodePath) > -1) {
return true;
}
return false;
}
isMarkdownFile(filePath) {
2018-09-19 01:57:17 +00:00
let index = filePath.lastIndexOf('.');
2018-09-04 09:16:50 +00:00
if (index === -1) {
return false;
} else {
let type = filePath.substring(index).toLowerCase();
2018-09-19 01:57:17 +00:00
if (type === '.md' || type === '.markdown') {
2018-09-04 09:16:50 +00:00
return true;
} else {
return false;
}
}
}
isInternalMarkdownLink(url) {
2018-11-29 09:55:14 +00:00
var re = new RegExp(siteRoot + 'lib/' + repoID + '/file' + '.*\.md$');
2018-09-04 09:16:50 +00:00
return re.test(url);
}
2018-09-07 14:25:13 +00:00
isInternalDirLink(url) {
2018-11-29 09:55:14 +00:00
var re = new RegExp(siteRoot + '#[a-z\-]*?/lib/' + repoID + '/.*');
2018-09-07 14:25:13 +00:00
return re.test(url);
}
2018-09-04 09:16:50 +00:00
getPathFromInternalMarkdownLink(url) {
2018-11-29 09:55:14 +00:00
var re = new RegExp(siteRoot + 'lib/' + repoID + '/file' + '(.*\.md)');
2018-09-04 09:16:50 +00:00
var array = re.exec(url);
var path = decodeURIComponent(array[1]);
return path;
}
2018-09-07 14:25:13 +00:00
getPathFromInternalDirLink(url) {
2018-11-29 09:55:14 +00:00
var re = new RegExp(siteRoot + '#[a-z\-]*?/lib/' + repoID + '(/.*)');
2018-09-07 14:25:13 +00:00
var array = re.exec(url);
var path = decodeURIComponent(array[1]);
var dirPath = path.substring(1);
2018-09-19 01:57:17 +00:00
re = new RegExp('(^/.*)');
2018-09-07 14:25:13 +00:00
if (re.test(dirPath)) {
path = dirPath;
} else {
2018-09-19 01:57:17 +00:00
path = '/' + dirPath;
2018-09-07 14:25:13 +00:00
}
return path;
}
2018-08-06 10:29:12 +00:00
render() {
return (
<div id="main" className="wiki-main">
<SidePanel
2018-09-04 09:16:50 +00:00
onNodeClick={this.onNodeClick}
2018-08-06 10:29:12 +00:00
closeSideBar={this.state.closeSideBar}
onCloseSide ={this.onCloseSide}
2018-09-04 09:16:50 +00:00
treeData={this.state.tree_data}
2018-11-30 03:22:58 +00:00
currentPath={this.state.filePath}
2018-09-04 09:16:50 +00:00
changedNode={this.state.changedNode}
onAddFolderNode={this.onAddFolderNode}
onAddFileNode={this.onAddFileNode}
onRenameNode={this.onRenameNode}
onDeleteNode={this.onDeleteNode}
onDirCollapse={this.onDirCollapse}
2018-08-06 10:29:12 +00:00
/>
<MainPanel
content={this.state.content}
filePath={this.state.filePath}
latestContributor={this.state.latestContributor}
lastModified={this.state.lastModified}
permission={this.state.permission}
2018-09-04 09:16:50 +00:00
isViewFileState={this.state.isViewFileState}
changedNode={this.state.changedNode}
2018-08-17 04:23:55 +00:00
isFileLoading={this.state.isFileLoading}
2018-09-04 09:16:50 +00:00
onLinkClick={this.onLinkClick}
onMenuClick={this.onMenuClick}
onSearchedClick={this.onSearchedClick}
onMainNavBarClick={this.onMainNavBarClick}
onMainNodeClick={this.onMainNodeClick}
2018-09-29 07:47:53 +00:00
onDeleteNode={this.onDeleteNode}
onRenameNode={this.onRenameNode}
2018-08-06 10:29:12 +00:00
/>
</div>
2018-09-19 01:57:17 +00:00
);
2018-08-06 10:29:12 +00:00
}
}
ReactDOM.render (
<Wiki />,
document.getElementById('wrapper')
2018-09-19 01:57:17 +00:00
);