1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-02 15:38:15 +00:00

display readme.md (#2772)

This commit is contained in:
王健辉
2019-01-09 12:10:33 +08:00
committed by Daniel Pan
parent ea37b0d83f
commit ca117bd2f0
9 changed files with 209 additions and 126 deletions

View File

@@ -12,7 +12,7 @@ import DirentDetail from '../dirent-detail/dirent-details';
import FileUploader from '../file-uploader/file-uploader'; import FileUploader from '../file-uploader/file-uploader';
import ModalPortal from '../modal-portal'; import ModalPortal from '../modal-portal';
import LibDecryptDialog from '../dialog/lib-decrypt-dialog'; import LibDecryptDialog from '../dialog/lib-decrypt-dialog';
import FileTagsViewer from '../../components/filetags-viewer'; import RepoInfoBar from '../repo-info-bar';
const propTypes = { const propTypes = {
currentRepoInfo: PropTypes.object, currentRepoInfo: PropTypes.object,
@@ -51,6 +51,7 @@ const propTypes = {
onSearchedClick: PropTypes.func.isRequired, onSearchedClick: PropTypes.func.isRequired,
onFileUploadSuccess: PropTypes.func.isRequired, onFileUploadSuccess: PropTypes.func.isRequired,
usedRepoTags: PropTypes.array.isRequired, usedRepoTags: PropTypes.array.isRequired,
readmeMarkdown: PropTypes.object,
}; };
class DirPanel extends React.Component { class DirPanel extends React.Component {
@@ -183,14 +184,13 @@ class DirPanel extends React.Component {
{!this.props.pathExist ? {!this.props.pathExist ?
errMessage : errMessage :
<Fragment> <Fragment>
{(this.props.usedRepoTags.length > 0 && this.props.path === '/') && ( {this.props.path === '/' && !(this.props.usedRepoTags.length === 0 && this.props.readmeMarkdown === null) && (
<div className="tags-summary-bar"> <RepoInfoBar
<FileTagsViewer repoID={this.props.repoID}
repoID={this.props.repoID} currentPath={this.props.path}
currentPath={this.props.path} usedRepoTags={this.props.usedRepoTags}
usedRepoTags={this.props.usedRepoTags} readmeMarkdown={this.props.readmeMarkdown}
/> />
</div>
)} )}
<DirentListView <DirentListView
path={this.props.path} path={this.props.path}

View File

@@ -42,6 +42,7 @@ class DirView extends React.Component {
dirID: '', dirID: '',
errorMsg: '', errorMsg: '',
usedRepoTags: [], usedRepoTags: [],
readmeMarkdown: null,
}; };
window.onpopstate = this.onpopstate; window.onpopstate = this.onpopstate;
this.lastModifyTime = new Date(); this.lastModifyTime = new Date();
@@ -144,11 +145,26 @@ class DirView extends React.Component {
this.setState({usedRepoTags: newUsedRepoTags}); this.setState({usedRepoTags: newUsedRepoTags});
} }
updateReadmeMarkdown = (direntList) => {
this.setState({readmeMarkdown: null});
direntList.some(item => {
let fileName = item.name.toLowerCase();
if (fileName === 'readme.md' || fileName === 'readme.markdown') {
this.setState({readmeMarkdown: item});
return true;
}
});
}
updateDirentList = (filePath) => { updateDirentList = (filePath) => {
let repoID = this.state.repoID; let repoID = this.state.repoID;
this.setState({isDirentListLoading: true}); this.setState({isDirentListLoading: true});
seafileAPI.listDir(repoID, filePath).then(res => { seafileAPI.listDir(repoID, filePath).then(res => {
let direntList = res.data.map(item => { let direntList = res.data.map(item => {
let fileName = item.name.toLowerCase();
if (fileName === 'readme.md' || fileName === 'readme.markdown') {
this.setState({readmeMarkdown: item});
}
return new Dirent(item); return new Dirent(item);
}); });
this.setState({ this.setState({
@@ -198,6 +214,7 @@ class DirView extends React.Component {
let dirent = this.createDirent(name, 'file', res.data); let dirent = this.createDirent(name, 'file', res.data);
let direntList = this.addItem(dirent, 'file'); let direntList = this.addItem(dirent, 'file');
this.setState({direntList: direntList}); this.setState({direntList: direntList});
this.updateReadmeMarkdown(direntList);
}); });
} }
@@ -215,6 +232,7 @@ class DirView extends React.Component {
seafileAPI.deleteFile(repoID, direntPath).then(() => { seafileAPI.deleteFile(repoID, direntPath).then(() => {
let direntList = this.deleteItem(dirent); let direntList = this.deleteItem(dirent);
this.setState({direntList: direntList}); this.setState({direntList: direntList});
this.updateReadmeMarkdown(direntList);
}).catch(() => { }).catch(() => {
// todo // todo
}) })
@@ -235,6 +253,7 @@ class DirView extends React.Component {
seafileAPI.renameFile(repoID, direntPath, newName).then(() => { seafileAPI.renameFile(repoID, direntPath, newName).then(() => {
let direntList = this.renameItem(dirent, newName); let direntList = this.renameItem(dirent, newName);
this.setState({direntList: direntList}); this.setState({direntList: direntList});
this.updateReadmeMarkdown(direntList);
}).catch(() => { }).catch(() => {
//todo //todo
}); });
@@ -248,6 +267,7 @@ class DirView extends React.Component {
let direntList = this.deleteItem(dirent); let direntList = this.deleteItem(dirent);
this.setState({direntList: direntList}); this.setState({direntList: direntList});
this.updateReadmeMarkdown(direntList);
let message = gettext('Successfully moved %(name)s.'); let message = gettext('Successfully moved %(name)s.');
message = message.replace('%(name)s', dirName); message = message.replace('%(name)s', dirName);
@@ -319,6 +339,7 @@ class DirView extends React.Component {
isDirentSelected: false, isDirentSelected: false,
selectedDirentList: [], selectedDirentList: [],
}); });
this.updateReadmeMarkdown(direntList);
let message = gettext('Successfully moved %(name)s.'); let message = gettext('Successfully moved %(name)s.');
message = message.replace('%(name)s', dirNames); message = message.replace('%(name)s', dirNames);
toaster.success(message); toaster.success(message);
@@ -348,6 +369,7 @@ class DirView extends React.Component {
let repoID = this.state.repoID; let repoID = this.state.repoID;
seafileAPI.deleteMutipleDirents(repoID, this.state.path, dirNames).then(res => { seafileAPI.deleteMutipleDirents(repoID, this.state.path, dirNames).then(res => {
let direntList = this.deleteItems(dirNames); let direntList = this.deleteItems(dirNames);
this.updateReadmeMarkdown(direntList);
this.setState({ this.setState({
direntList: direntList, direntList: direntList,
isDirentSelected: false, isDirentSelected: false,
@@ -446,6 +468,7 @@ class DirView extends React.Component {
this.setState({direntList: [dirent, ...this.state.direntList]}); this.setState({direntList: [dirent, ...this.state.direntList]});
} else { } else {
this.setState({direntList: [...this.state.direntList, dirent]}); this.setState({direntList: [...this.state.direntList, dirent]});
this.updateReadmeMarkdown(this.state.direntList);
} }
} }
} }
@@ -632,6 +655,7 @@ class DirView extends React.Component {
libNeedDecrypt={this.state.libNeedDecrypt} libNeedDecrypt={this.state.libNeedDecrypt}
onLibDecryptDialog={this.onLibDecryptDialog} onLibDecryptDialog={this.onLibDecryptDialog}
usedRepoTags={this.state.usedRepoTags} usedRepoTags={this.state.usedRepoTags}
readmeMarkdown={this.state.readmeMarkdown}
/> />
); );
} }

View File

@@ -1,74 +0,0 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import ModalPortal from './modal-portal';
import { Modal } from 'reactstrap';
import ListTaggedFilesDialog from './dialog/list-taggedfiles-dialog';
const propTypes = {
repoID: PropTypes.string.isRequired,
currentPath: PropTypes.string.isRequired,
usedRepoTags: PropTypes.array.isRequired,
};
class FileTagsViewer extends React.Component {
constructor(props) {
super(props);
this.state = {
usedRepoTags: [],
currentTag: null,
isListTaggedFileShow: false,
};
}
onListTaggedFiles = (currentTag) => {
this.setState({
currentTag: currentTag,
isListTaggedFileShow: !this.state.isListTaggedFileShow,
});
}
onCloseDialog = () => {
this.setState({
isListTaggedFileShow: false
});
}
render() {
let {usedRepoTags, repoID} = this.props;
return (
<Fragment>
<ul className="used-tag-list">
{usedRepoTags.map((usedRepoTag) => {
return (
<li key={usedRepoTag.id} className="used-tag-item">
<span className={`used-tag bg-${usedRepoTag.color}`}></span>
<span className="used-tag-name" title={usedRepoTag.name}>{usedRepoTag.name}</span>
<span className="used-tag-files" onClick={this.onListTaggedFiles.bind(this, usedRepoTag)}>
{usedRepoTag.fileCount > 1 ? usedRepoTag.fileCount + ' files' : usedRepoTag.fileCount + ' file'}
</span>
</li>
);
})}
</ul>
{this.state.isListTaggedFileShow && (
<ModalPortal>
<Modal isOpen={true}>
<ListTaggedFilesDialog
repoID={repoID}
currentTag={this.state.currentTag}
onClose={this.onCloseDialog}
toggleCancel={this.onListTaggedFiles}
/>
</Modal>
</ModalPortal>
)}
</Fragment>
);
}
}
FileTagsViewer.propTypes = propTypes;
export default FileTagsViewer;

View File

@@ -179,7 +179,7 @@ class MainSideNav extends React.Component {
<li className="nav-item"> <li className="nav-item">
<Link className={`nav-link ellipsis ${this.getActiveClass('wikis')}`} to={siteRoot + 'wikis/'} title={gettext('Wikis')} onClick={() => this.tabItemClick('wikis')}> <Link className={`nav-link ellipsis ${this.getActiveClass('wikis')}`} to={siteRoot + 'wikis/'} title={gettext('Wikis')} onClick={() => this.tabItemClick('wikis')}>
<span className="sf2-icon-wiki-view" aria-hidden="true"></span> <span className="sf2-icon-wiki-view" aria-hidden="true"></span>
{gettext('Wikis')} <span className="nav-text">{gettext('Wikis')}</span>
</Link> </Link>
</li> </li>
} }

View File

@@ -0,0 +1,87 @@
import React from 'react';
import PropTypes from 'prop-types';
import ModalPortal from './modal-portal';
import { Modal } from 'reactstrap';
import ListTaggedFilesDialog from './dialog/list-taggedfiles-dialog';
import { siteRoot } from '../utils/constants';
import { Utils } from '../utils/utils';
import '../css/repo-info-bar.css';
const propTypes = {
repoID: PropTypes.string.isRequired,
currentPath: PropTypes.string.isRequired,
usedRepoTags: PropTypes.array.isRequired,
readmeMarkdown: PropTypes.object,
};
class RepoInfoBar extends React.Component {
constructor(props) {
super(props);
this.state = {
currentTag: null,
isListTaggedFileShow: false,
};
}
onListTaggedFiles = (currentTag) => {
this.setState({
currentTag: currentTag,
isListTaggedFileShow: !this.state.isListTaggedFileShow,
});
}
onCloseDialog = () => {
this.setState({
isListTaggedFileShow: false
});
}
render() {
let {repoID, currentPath, usedRepoTags, readmeMarkdown} = this.props;
let href = readmeMarkdown !== null ? siteRoot + 'lib/' + repoID + '/file' + Utils.joinPath(currentPath, readmeMarkdown.name) : '';
return (
<div className="repo-info-bar">
{usedRepoTags.length > 0 && (
<ul className="used-tag-list">
{usedRepoTags.map((usedRepoTag) => {
return (
<li key={usedRepoTag.id} className="used-tag-item">
<span className={`used-tag bg-${usedRepoTag.color}`}></span>
<span className="used-tag-name" title={usedRepoTag.name}>{usedRepoTag.name}</span>
<span className="used-tag-files" onClick={this.onListTaggedFiles.bind(this, usedRepoTag)}>
{usedRepoTag.fileCount > 1 ? usedRepoTag.fileCount + ' files' : usedRepoTag.fileCount + ' file'}
</span>
</li>
);
})}
</ul>
)}
{readmeMarkdown !== null && (
<div className="readme-file">
<i className="readme-flag fa fa-flag"></i>
<a className="readme-name" href={href} target='_blank'>{readmeMarkdown.name}</a>
</div>
)}
{this.state.isListTaggedFileShow && (
<ModalPortal>
<Modal isOpen={true}>
<ListTaggedFilesDialog
repoID={repoID}
currentTag={this.state.currentTag}
onClose={this.onCloseDialog}
toggleCancel={this.onListTaggedFiles}
/>
</Modal>
</ModalPortal>
)}
</div>
);
}
}
RepoInfoBar.propTypes = propTypes;
export default RepoInfoBar;

View File

@@ -0,0 +1,56 @@
.repo-info-bar {
margin-bottom: 5px;
padding: 0 10px;
border: 1px solid #e6e6dd;
border-radius: 5px;
background: #f8f8f8;
}
.used-tag-list {
list-style: none;
margin: 8px 0;
}
.used-tag-item {
display: inline-block;
margin: auto 15px;
}
.used-tag {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
}
.used-tag-name {
margin: 0 0.25rem;
}
.used-tag-files {
color: #666;
font-size: 14px;
}
.used-tag-files:hover {
cursor: pointer;
text-decoration: underline;
}
.readme-file {
margin: 8px 15px;
}
.readme-file a {
color: #333;
}
.readme-flag {
font-size: 12px;
color: #f19645;
}
.readme-name {
margin: 0 0.25rem;
font-size: 14px;
}

View File

@@ -93,35 +93,3 @@
.tag-dialog-back:focus { .tag-dialog-back:focus {
color: #444; color: #444;
} }
.tags-summary-bar {
margin-bottom: 5px;
padding: 0 10px;
border: 1px solid #e6e6dd;
border-radius: 5px;
}
.used-tag-list {
list-style: none;
line-height: 40px;
}
.used-tag-item {
display: inline-block;
margin: auto 15px;
}
.used-tag {
display: inline-block;
width: 0.8rem;
height: 0.8rem;
border-radius: 50%;
}
.used-tag-name {
margin: 0 0.25rem;
}
.used-tag-files {
color: #666;
font-size: 14px;
}
.used-tag-files:hover {
cursor: pointer;
text-decoration: underline;
}

View File

@@ -14,7 +14,7 @@ import MarkdownContentViewer from '../../components/markdown-viewer';
import DirentListView from '../../components/dirent-list-view/dirent-list-view'; import DirentListView from '../../components/dirent-list-view/dirent-list-view';
import DirentDetail from '../../components/dirent-detail/dirent-details'; import DirentDetail from '../../components/dirent-detail/dirent-details';
import FileUploader from '../../components/file-uploader/file-uploader'; import FileUploader from '../../components/file-uploader/file-uploader';
import FileTagsViewer from '../../components/filetags-viewer'; import RepoInfoBar from '../../components/repo-info-bar';
const propTypes = { const propTypes = {
content: PropTypes.string, content: PropTypes.string,
@@ -61,6 +61,7 @@ const propTypes = {
goDraftPage: PropTypes.func, goDraftPage: PropTypes.func,
reviewID: PropTypes.any, reviewID: PropTypes.any,
usedRepoTags: PropTypes.array.isRequired, usedRepoTags: PropTypes.array.isRequired,
readmeMarkdown: PropTypes.object,
}; };
class MainPanel extends Component { class MainPanel extends Component {
@@ -256,14 +257,13 @@ class MainPanel extends Component {
goReviewPage={this.props.goReviewPage} goReviewPage={this.props.goReviewPage}
/> : /> :
<Fragment> <Fragment>
{(this.props.usedRepoTags.length > 0 && this.props.path === '/') && ( {this.props.path === '/' && !(this.props.usedRepoTags.length === 0 && this.props.readmeMarkdown === null) && (
<div className="tags-summary-bar"> <RepoInfoBar
<FileTagsViewer repoID={repoID}
repoID={repoID} currentPath={this.props.path}
currentPath={this.props.path} usedRepoTags={this.props.usedRepoTags}
usedRepoTags={this.props.usedRepoTags} readmeMarkdown={this.props.readmeMarkdown}
/> />
</div>
)} )}
<DirentListView <DirentListView
path={this.props.path} path={this.props.path}

View File

@@ -54,6 +54,7 @@ class Wiki extends Component {
draftFilePath: '', draftFilePath: '',
dirID: '', dirID: '',
usedRepoTags: [], usedRepoTags: [],
readmeMarkdown: null,
}; };
window.onpopstate = this.onpopstate; window.onpopstate = this.onpopstate;
this.hash = ''; this.hash = '';
@@ -99,7 +100,7 @@ class Wiki extends Component {
} }
componentWillUnmount() { componentWillUnmount() {
collabServer.unwatchRepo(repoID); collabServer.unwatchRepo(repoID, this.onRepoUpdateEvent);
} }
componentDidUpdate() { componentDidUpdate() {
@@ -311,11 +312,26 @@ class Wiki extends Component {
window.history.pushState({ url: url, path: path}, path, url); window.history.pushState({ url: url, path: path}, path, url);
} }
updateReadmeMarkdown = (direntList) => {
this.setState({readmeMarkdown: null});
direntList.some(item => {
let fileName = item.name.toLowerCase();
if (fileName === 'readme.md' || fileName === 'readme.markdown') {
this.setState({readmeMarkdown: item});
return true;
}
});
}
loadDirentList = (filePath) => { loadDirentList = (filePath) => {
this.setState({isDirentListLoading: true}); this.setState({isDirentListLoading: true});
seafileAPI.listDir(repoID, filePath).then(res => { seafileAPI.listDir(repoID, filePath).then(res => {
let direntList = []; let direntList = [];
res.data.forEach(item => { res.data.forEach(item => {
let fileName = item.name.toLowerCase();
if (fileName === 'readme.md' || fileName === 'readme.markdown') {
this.setState({readmeMarkdown: item});
}
let dirent = new Dirent(item); let dirent = new Dirent(item);
direntList.push(dirent); direntList.push(dirent);
}); });
@@ -513,6 +529,7 @@ class Wiki extends Component {
return item; return item;
}); });
this.setState({ direntList: direntList }); this.setState({ direntList: direntList });
this.updateReadmeMarkdown(direntList);
} else if (Utils.isAncestorPath(direntPath, this.state.path)) { } else if (Utils.isAncestorPath(direntPath, this.state.path)) {
// example: direntPath = /A/B, state.path = /A/B/C // example: direntPath = /A/B, state.path = /A/B/C
let newPath = Utils.renameAncestorPath(this.state.path, direntPath, newDirentPath); let newPath = Utils.renameAncestorPath(this.state.path, direntPath, newDirentPath);
@@ -532,6 +549,7 @@ class Wiki extends Component {
return item.name !== name; return item.name !== name;
}); });
this.setState({ direntList: direntList }); this.setState({ direntList: direntList });
this.updateReadmeMarkdown(direntList);
} else if (Utils.isAncestorPath(direntPath, this.state.path)) { } else if (Utils.isAncestorPath(direntPath, this.state.path)) {
// the deleted item is ancester of the current item // the deleted item is ancester of the current item
let parentPath = Utils.getDirName(direntPath); let parentPath = Utils.getDirName(direntPath);
@@ -565,6 +583,7 @@ class Wiki extends Component {
} }
} }
this.setState({direntList: direntList}); this.setState({direntList: direntList});
this.updateReadmeMarkdown(direntList);
} }
moveDirent = (filePath) => { moveDirent = (filePath) => {
@@ -573,6 +592,7 @@ class Wiki extends Component {
return item.name !== name; return item.name !== name;
}); });
this.setState({direntList: direntList}); this.setState({direntList: direntList});
this.updateReadmeMarkdown(direntList);
} }
onFileUploadSuccess = (direntObject) => { onFileUploadSuccess = (direntObject) => {
@@ -597,6 +617,7 @@ class Wiki extends Component {
this.setState({direntList: [dirent, ...this.state.direntList]}); this.setState({direntList: [dirent, ...this.state.direntList]});
} else { } else {
this.setState({direntList: [...this.state.direntList, dirent]}); this.setState({direntList: [...this.state.direntList, dirent]});
this.updateReadmeMarkdown(this.state.direntList);
} }
} }
} }
@@ -991,6 +1012,7 @@ class Wiki extends Component {
goDraftPage={this.goDraftPage} goDraftPage={this.goDraftPage}
goReviewPage={this.goReviewPage} goReviewPage={this.goReviewPage}
usedRepoTags={this.state.usedRepoTags} usedRepoTags={this.state.usedRepoTags}
readmeMarkdown={this.state.readmeMarkdown}
/> />
</div> </div>
); );