1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-04 16:31:13 +00:00

Refactor draft (#3018)

* refactor draft

* update
This commit is contained in:
陈钦亮
2019-03-01 16:49:35 +08:00
committed by Daniel Pan
parent b25c2752a7
commit 6137e720f0
47 changed files with 497 additions and 2942 deletions

View File

@@ -1,81 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import { gettext, siteRoot } from '../../utils/constants';
import { seafileAPI } from '../../utils/seafile-api';
import moment from 'moment';
import { Utils } from '../../utils/utils';
import Review from '../../models/review';
const propTypes = {
repoID: PropTypes.string.isRequired,
toggle: PropTypes.func.isRequired,
};
class ListRepoReviewsDialog extends React.Component {
constructor(props) {
super(props);
this.state = {
reviews: [],
};
}
componentDidMount() {
seafileAPI.listRepoReviews(this.props.repoID).then(res => {
let reviews = res.data.reviews.map(item =>{
let review = new Review(item);
return review;
});
this.setState({
reviews: reviews
});
});
}
toggle = () => {
this.props.toggle();
}
render() {
let reviews = this.state.reviews;
return (
<Modal isOpen={true}>
<ModalHeader toggle={this.toggle}>{gettext('Reviews')}</ModalHeader>
<ModalBody className="dialog-list-container">
<table>
<thead>
<tr>
<th width='50%' className="ellipsis">{gettext('Name')}</th>
<th width='25%'>{gettext('Owner')}</th>
<th width='25%'>{gettext('Last Update')}</th>
</tr>
</thead>
<tbody>
{this.state.reviews.map((review) => {
let href = siteRoot + 'drafts/review/' + review.id;
return (
<tr key={review.id}>
<td className="name">
<a href={href} target='_blank'>{Utils.getFileName(review.draftFilePath)}</a>
</td>
<td>{review.creatorName}</td>
<td>{moment(review.createdStr).fromNow()}</td>
</tr>
);
})}
</tbody>
</table>
</ModalBody>
<ModalFooter>
<Button color="secondary" onClick={this.toggle}>{gettext('Close')}</Button>
</ModalFooter>
</Modal>
);
}
}
ListRepoReviewsDialog.propTypes = propTypes;
export default ListRepoReviewsDialog;

View File

@@ -12,8 +12,6 @@ const propTypes = {
isDraft: PropTypes.bool,
hasDraft: PropTypes.bool,
goDraftPage: PropTypes.func.isRequired,
reviewStatus: PropTypes.any,
goReviewPage: PropTypes.func.isRequired,
isFileLoading: PropTypes.bool.isRequired,
isFileLoadedErr: PropTypes.bool.isRequired,
filePermission: PropTypes.bool,
@@ -61,11 +59,6 @@ class DirColumnFile extends React.Component {
this.props.goDraftPage();
}
goReviewPage = (e) => {
e.preventDefault();
this.props.goReviewPage();
}
render() {
if (this.props.isFileLoadedErr) {
return (
@@ -85,15 +78,7 @@ class DirColumnFile extends React.Component {
<span className='wiki-open-file position-fixed' onClick={this.onOpenFile}>
<i className="fas fa-expand-arrows-alt"></i>
</span>
{this.props.reviewStatus === 'open' &&
<div className='seafile-btn-view-review text-center'>
<div className='tag tag-green'>
{gettext('This file is in review stage')}
<span className="ml-2" onClick={this.goReviewPage}>{gettext('View Review')}</span>
</div>
</div>
}
{(this.props.reviewStatus !== 'open' && !this.props.isDraft && this.props.hasDraft) &&
{(!this.props.isDraft && this.props.hasDraft) &&
<div className='seafile-btn-view-review text-center'>
<div className='tag tag-green'>
{gettext('This file is in draft stage.')}

View File

@@ -34,8 +34,6 @@ const propTypes = {
isDraft: PropTypes.bool.isRequired,
hasDraft: PropTypes.bool.isRequired,
goDraftPage: PropTypes.func.isRequired,
reviewStatus: PropTypes.string,
goReviewPage: PropTypes.func.isRequired,
filePermission: PropTypes.bool.isRequired,
content: PropTypes.string,
lastModified: PropTypes.string,
@@ -44,7 +42,6 @@ const propTypes = {
// repo content
isRepoInfoBarShow: PropTypes.bool.isRequired,
draftCounts: PropTypes.number.isRequired,
reviewCounts: PropTypes.number.isRequired,
usedRepoTags: PropTypes.array.isRequired,
readmeMarkdown: PropTypes.object,
updateUsedRepoTags: PropTypes.func.isRequired,
@@ -176,8 +173,6 @@ class DirColumnView extends React.Component {
isDraft={this.props.isDraft}
hasDraft={this.props.hasDraft}
goDraftPage={this.props.goDraftPage}
reviewStatus={this.props.reviewStatus}
goReviewPage={this.props.goReviewPage}
isFileLoading={this.props.isFileLoading}
isFileLoadedErr={this.props.isFileLoadedErr}
filePermission={this.props.filePermission}
@@ -197,7 +192,6 @@ class DirColumnView extends React.Component {
usedRepoTags={this.props.usedRepoTags}
readmeMarkdown={this.props.readmeMarkdown}
draftCounts={this.props.draftCounts}
reviewCounts={this.props.draftCounts}
updateUsedRepoTags={this.props.updateUsedRepoTags}
isDirentListLoading={this.props.isDirentListLoading}
direntList={this.props.direntList}

View File

@@ -13,7 +13,6 @@ const propTypes = {
usedRepoTags: PropTypes.array.isRequired,
readmeMarkdown: PropTypes.object,
draftCounts: PropTypes.number,
reviewCounts: PropTypes.number,
updateUsedRepoTags: PropTypes.func.isRequired,
isDirentListLoading: PropTypes.bool.isRequired,
direntList: PropTypes.array.isRequired,
@@ -45,7 +44,6 @@ class DirListView extends React.Component {
currentPath={this.props.path}
readmeMarkdown={this.props.readmeMarkdown}
draftCounts={this.props.draftCounts}
reviewCounts={this.props.reviewCounts}
usedRepoTags={this.props.usedRepoTags}
updateUsedRepoTags={this.props.updateUsedRepoTags}
/>

View File

@@ -12,7 +12,7 @@ const propTypes = {
onFreezedItem: PropTypes.func.isRequired,
onUnfreezedItem: PropTypes.func.isRequired,
onDeleteHandler: PropTypes.func.isRequired,
onReviewHandler: PropTypes.func.isRequired,
onPublishHandler: PropTypes.func.isRequired,
};
class DraftListItem extends React.Component {
@@ -70,8 +70,8 @@ class DraftListItem extends React.Component {
this.props.onDeleteHandler(this.props.draft);
}
onReviewHandler = () => {
this.props.onReviewHandler(this.props.draft);
onPublishHandler = () => {
this.props.onPublishHandler(this.props.draft);
}
render() {
@@ -79,7 +79,7 @@ class DraftListItem extends React.Component {
let repoID = draft.origin_repo_id;
let filePath = draft.draft_file_path;
let fileName = Utils.getFileName(filePath);
let draftUrl = siteRoot + 'lib/' + repoID + '/file' + filePath + '?mode=edit';
let draftUrl = siteRoot + 'drafts/' + draft.id + '/';
let libraryUrl = siteRoot + 'library/' + repoID + '/' + encodeURIComponent(draft.repo_name) + '/' ;
let reviewUrl = siteRoot + 'drafts/review/' + draft.review_id + '/';
let localTime = moment.utc(draft.updated_at).toDate();
@@ -95,15 +95,9 @@ class DraftListItem extends React.Component {
<td className="library">
<a href={libraryUrl} target="_blank">{draft.repo_name}</a>
</td>
<td className="review">
{(draft.review_id && draft.review_status === 'open') ?
<a href={reviewUrl} target="_blank">#{draft.review_id}</a> :
<span>--</span>
}
</td>
<td className="update">{localTime}</td>
<td className="text-center">
{(this.props.draft.review_status !== 'open' && this.state.isMenuIconShow) && (
{this.state.isMenuIconShow && (
<Dropdown isOpen={this.state.isItemMenuShow} toggle={this.toggleOperationMenu}>
<DropdownToggle
tag="i"
@@ -115,7 +109,7 @@ class DraftListItem extends React.Component {
/>
<DropdownMenu>
<DropdownItem onClick={this.onDeleteHandler}>{gettext('Delete')}</DropdownItem>
<DropdownItem onClick={this.onReviewHandler}>{gettext('Ask for review')}</DropdownItem>
<DropdownItem onClick={this.onPublishHandler}>{gettext('Publish')}</DropdownItem>
</DropdownMenu>
</Dropdown>
)}

View File

@@ -1,45 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
const propTypes = {
currentDraft: PropTypes.object.isRequired,
isMenuShow: PropTypes.bool.isRequired,
menuPosition: PropTypes.object.isRequired,
onDeleteHandler: PropTypes.func.isRequired,
onPublishHandler: PropTypes.func.isRequired,
onReviewHandler: PropTypes.func.isRequired,
};
class DraftListMenu extends React.Component {
render() {
let style = {};
let {isMenuShow, menuPosition, currentDraft} = this.props;
if (isMenuShow) {
style = {position: 'fixed', top: menuPosition.top, left: menuPosition.left, display: 'block'};
}
if (currentDraft.review_status === null) {
return (
<ul className="dropdown-menu" style={style}>
<li className="dropdown-item" onClick={this.props.onDeleteHandler}>{gettext('Delete')}</li>
{/* <li className="dropdown-item" onClick={this.props.onPublishHandler}>{gettext('Publish')}</li> */}
<li className="dropdown-item" onClick={this.props.onReviewHandler}>{gettext('Ask for review')}</li>
</ul>
);
}
if (currentDraft.review_status === 'closed' ) {
return (
<ul className="dropdown-menu" style={style}>
<li className="dropdown-item" onClick={this.props.onDeleteHandler}>{gettext('Delete')}</li>
<li className="dropdown-item" onClick={this.props.onReviewHandler}>{gettext('Ask for review')}</li>
</ul>
);
}
}
}
DraftListMenu.propTypes = propTypes;
export default DraftListMenu;

View File

@@ -6,7 +6,7 @@ import DraftListItem from './draft-list-item';
const propTypes = {
draftList: PropTypes.array.isRequired,
onDeleteHandler: PropTypes.func.isRequired,
onReviewHandler: PropTypes.func.isRequired,
onPublishHandler: PropTypes.func.isRequired,
};
class DraftListView extends React.Component {
@@ -34,8 +34,7 @@ class DraftListView extends React.Component {
<tr>
<th style={{width: '4%'}}>{/*img*/}</th>
<th style={{width: '46%'}}>{gettext('Name')}</th>
<th style={{width: '20%'}}>{gettext('Library')}</th>
<th style={{width: '10%'}}>{gettext('Review')}</th>
<th style={{width: '30%'}}>{gettext('Library')}</th>
<th style={{width: '10%'}}>{gettext('Last Update')}</th>
<th style={{width: '10%'}}></th>
</tr>
@@ -50,7 +49,7 @@ class DraftListView extends React.Component {
onFreezedItem={this.onFreezedItem}
onUnfreezedItem={this.onUnfreezedItem}
onDeleteHandler={this.props.onDeleteHandler}
onReviewHandler={this.props.onReviewHandler}
onPublishHandler={this.props.onPublishHandler}
/>
);
})}

View File

@@ -22,8 +22,6 @@ const propTypes = {
isDraft: PropTypes.bool,
hasDraft: PropTypes.bool,
goDraftPage: PropTypes.func.isRequired,
reviewStatus: PropTypes.any,
goReviewPage: PropTypes.func.isRequired,
isFileLoading: PropTypes.bool.isRequired,
filePermission: PropTypes.bool,
content: PropTypes.string,
@@ -63,11 +61,6 @@ class FileContentView extends React.Component {
this.props.goDraftPage();
}
goReviewPage = (e) => {
e.preventDefault();
this.props.goReviewPage();
}
render() {
let repoID = this.props.repoID;
return (
@@ -116,15 +109,7 @@ class FileContentView extends React.Component {
onLinkClick={this.props.onLinkClick}
>
<Fragment>
{this.props.reviewStatus === 'open' &&
<div className='seafile-btn-view-review text-center'>
<div className='tag tag-green'>
{gettext('This file is in review stage')}
<span className="ml-2" onClick={this.goReviewPage}>{gettext('View Review')}</span>
</div>
</div>
}
{(this.props.reviewStatus !== 'open' && !this.props.isDraft && this.props.hasDraft) &&
{(!this.props.isDraft && this.props.hasDraft) &&
<div className='seafile-btn-view-review text-center'>
<div className='tag tag-green'>
{gettext('This file is in draft stage.')}

View File

@@ -204,7 +204,7 @@ class MainSideNav extends React.Component {
</li>
}
<li className="nav-item" onClick={() => this.tabItemClick('drafts')}>
<Link className={`nav-link ellipsis ${this.getActiveClass('drafts') || this.getActiveClass('reviews')}`} to={siteRoot + 'drafts/'} title={gettext('Drafts')}>
<Link className={`nav-link ellipsis ${this.getActiveClass('drafts')}`} to={siteRoot + 'drafts/'} title={gettext('Drafts')}>
<span className="sf2-icon-edit" aria-hidden="true"></span>
<span className="draft-info nav-text">
{gettext('Drafts')}

View File

@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import ModalPortal from './modal-portal';
import ListTaggedFilesDialog from './dialog/list-taggedfiles-dialog';
import ListRepoDraftsDialog from './dialog/list-repo-drafts-dialog';
import ListRepoReviewsDialog from './dialog/list-repo-reviews-dialog';
import ReadmeDialog from './dialog/readme-dialog';
import { siteRoot, gettext } from '../utils/constants';
import { Utils } from '../utils/utils';
@@ -16,7 +15,6 @@ const propTypes = {
usedRepoTags: PropTypes.array.isRequired,
readmeMarkdown: PropTypes.object,
draftCounts: PropTypes.number,
reviewCounts: PropTypes.number,
updateUsedRepoTags: PropTypes.func.isRequired,
};
@@ -28,7 +26,6 @@ class RepoInfoBar extends React.Component {
currentTag: null,
isListTaggedFileShow: false,
showRepoDrafts: false,
showRepoReviews: false,
showReadmeDialog: false,
};
}
@@ -52,12 +49,6 @@ class RepoInfoBar extends React.Component {
});
}
toggleReviews = () => {
this.setState({
showRepoReviews: !this.state.showRepoReviews
});
}
toggleReadme = () => {
this.setState({
showReadmeDialog: !this.state.showReadmeDialog
@@ -107,15 +98,6 @@ class RepoInfoBar extends React.Component {
</span>
</span>
}
{this.props.reviewCounts > 0 &&
<span className="file-info">
<span className="info-icon sf2-icon-review"></span>
<span className="used-tag-name">{gettext('review')}</span>
<span className="used-tag-files" onClick={this.toggleReviews}>
{this.props.reviewCounts > 1 ? this.props.reviewCounts + ' files' : this.props.reviewCounts + ' file'}
</span>
</span>
}
</div>
{this.state.isListTaggedFileShow && (
<ModalPortal>
@@ -138,15 +120,6 @@ class RepoInfoBar extends React.Component {
</ModalPortal>
)}
{this.state.showRepoReviews && (
<ModalPortal>
<ListRepoReviewsDialog
toggle={this.toggleReviews}
repoID={this.props.repoID}
/>
</ModalPortal>
)}
{this.state.showReadmeDialog && (
<ModalPortal>
<ReadmeDialog

View File

@@ -1,129 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { Tooltip } from 'reactstrap';
import { siteRoot, lang } from '../../utils/constants';
import { Utils } from '../../utils/utils';
moment.locale(lang);
const propTypes = {
isItemFreezed: PropTypes.bool.isRequired,
item: PropTypes.object.isRequired,
};
class Reviewers extends React.Component {
constructor(props) {
super(props);
this.state = {
reviewerTipOpen: false,
reviewerList:''
};
}
toggle = () => {
this.reviewerListItem();
this.setState({
reviewerTipOpen: !this.state.reviewerTipOpen
});
}
reviewerListItem = () => {
let reviewers = '';
this.props.item.reviewers.map(item => {
reviewers = reviewers + ' and ' + item.user_name;
});
this.setState({
reviewerList: reviewers.substr(5,)
});
}
render() {
let items = this.props.item;
let { reviewerList } = this.state;
return (
<div className='position-relative reviewer-list'>
<span id={'reviewers' + items.id}>
{items.reviewers.map((item, index) => (
<img key={index} id={'reviewer-tip' + '-' + items.id + '-' + index} className="avatar avatar-sm reviewer-avatar" src={item.avatar_url} alt={item.user_name} />
))}
</span>
<Tooltip placement="bottom-end" isOpen={this.state.reviewerTipOpen} target={'reviewers' + items.id} toggle={this.toggle}>
{reviewerList}
</Tooltip>
</div>
);
}
}
class ReviewListItem extends React.Component {
constructor(props) {
super(props);
this.state = {
highlight: '',
authorTipOpen: false
};
}
onMouseEnter = () => {
if (!this.props.isItemFreezed) {
this.setState({
highlight: 'tr-highlight'
});
}
}
onMouseLeave = () => {
if (!this.props.isItemFreezed) {
this.setState({
highlight: ''
});
}
}
toggle = () => {
this.setState({
authorTipOpen: !this.state.authorTipOpen
});
}
getFileName(filePath) {
let lastIndex = filePath.lastIndexOf('/');
return filePath.slice(lastIndex+1);
}
render() {
let item = this.props.item;
let fileName = this.getFileName(item.draft_file_path);
let reviewUrl = siteRoot + 'drafts/review/' + item.id;
let localTime = moment.utc(item.updated_at).toDate();
localTime = moment(localTime).fromNow();
let iconUrl = Utils.getFileIconUrl(fileName);
return (
<tr className={this.state.highlight} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
<td className="text-center" style={{width: '4%'}}><img src={iconUrl} width="24" alt="" /></td>
<td style={{width: '26%'}}><a href={reviewUrl} target="_blank">{fileName}</a></td>
<td className='library' style={{width: '25%'}}>{item.draft_origin_repo_name}</td>
<td className="update" style={{width: '20%'}}>{localTime}</td>
<td className="author" style={{width: '10%'}}><img className="avatar avatar-sm cursor-pointer" id={'tip-' + item.id} src={item.author.avatar_url} alt={item.user_name} /></td>
<td className="reviewer" style={{width: '15%'}}><Reviewers item={item}/></td>
<Tooltip placement="bottom-end" isOpen={this.state.authorTipOpen} target={'tip-' + item.id} toggle={this.toggle}>
{item.author.user_name}
</Tooltip>
</tr>
);
}
}
ReviewListItem.propTypes = propTypes;
Reviewers.propTypes = {
item: PropTypes.object.isRequired,
};
export default ReviewListItem;

View File

@@ -1,88 +0,0 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { Nav, NavItem, NavLink } from 'reactstrap';
import classnames from 'classnames';
import { gettext } from '../../utils/constants';
import ReviewListItem from './review-list-item';
const propTypes = {
isItemFreezed: PropTypes.bool.isRequired,
itemsList: PropTypes.array.isRequired,
getReviewList: PropTypes.func.isRequired,
activeTab: PropTypes.string.isRequired,
};
class ReviewListView extends React.Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
}
toggle(tab) {
if (this.props.activeTab !== tab) {
this.props.getReviewList(tab);
}
}
render() {
let items = this.props.itemsList;
return (
<Fragment>
<Nav pills>
<NavItem className="pt-4">
<NavLink
className={classnames({ active: this.props.activeTab === 'open' })}
onClick={() => { this.toggle('open');}}
>
{gettext('Open')}
</NavLink>
</NavItem>
<NavItem className="pt-4">
<NavLink
className={classnames({ active: this.props.activeTab === 'finished' })}
onClick={() => { this.toggle('finished');}}
>
{gettext('Published')}
</NavLink>
</NavItem>
<NavItem className="pt-4">
<NavLink
className={classnames({ active: this.props.activeTab === 'closed' })}
onClick={() => { this.toggle('closed');}}
>
{gettext('Closed')}
</NavLink>
</NavItem>
</Nav>
<table>
<thead>
<tr>
<th style={{width: '4%'}}>{/*img*/}</th>
<th style={{width: '26%'}}>{gettext('Name')}</th>
<th style={{width: '25%'}}>{gettext('Library')}</th>
<th style={{width: '20%'}}>{gettext('Last Update')}</th>
<th style={{width: '10%'}}>{gettext('Author')}</th>
<th style={{width: '15%'}}>{gettext('Reviewers')}</th>
</tr>
</thead>
<tbody>
{ items && items.map((item) => {
return (
<ReviewListItem
key={item.id}
item={item}
isItemFreezed={this.props.isItemFreezed}
/>
);
})}
</tbody>
</table>
</Fragment>
);
}
}
ReviewListView.propTypes = propTypes;
export default ReviewListView;