2018-10-15 07:51:29 +00:00
|
|
|
import React from 'react';
|
|
|
|
import ReactDOM from 'react-dom';
|
2018-10-16 10:19:51 +00:00
|
|
|
/* eslint-disable */
|
2018-10-15 07:51:29 +00:00
|
|
|
import Prism from 'prismjs';
|
2018-10-16 10:19:51 +00:00
|
|
|
/* eslint-enable */
|
2018-11-09 09:32:36 +00:00
|
|
|
import { siteRoot, gettext, draftID, reviewID, draftOriginFilePath, draftFilePath, draftOriginRepoID, draftFileName, opStatus, publishFileVersion, originFileVersion, author, authorAvatar } from './utils/constants';
|
2018-10-15 07:51:29 +00:00
|
|
|
import { seafileAPI } from './utils/seafile-api';
|
|
|
|
import axios from 'axios';
|
|
|
|
import DiffViewer from '@seafile/seafile-editor/dist/viewer/diff-viewer';
|
|
|
|
import Loading from './components/loading';
|
|
|
|
import Toast from './components/toast';
|
2018-10-23 05:13:44 +00:00
|
|
|
import ReviewComments from './components/review-list-view/review-comments';
|
2018-11-07 03:03:26 +00:00
|
|
|
import { Tooltip } from 'reactstrap';
|
2018-11-05 02:33:33 +00:00
|
|
|
import AddReviewerDialog from './components/dialog/add-reviewer-dialog.js';
|
2018-10-15 07:51:29 +00:00
|
|
|
|
|
|
|
import 'seafile-ui';
|
|
|
|
import './assets/css/fa-solid.css';
|
|
|
|
import './assets/css/fa-regular.css';
|
|
|
|
import './assets/css/fontawesome.css';
|
|
|
|
import './css/layout.css';
|
|
|
|
import './css/initial-style.css';
|
|
|
|
import './css/toolbar.css';
|
2018-10-23 05:13:44 +00:00
|
|
|
import './css/draft-review.css';
|
2018-10-15 07:51:29 +00:00
|
|
|
|
|
|
|
require('@seafile/seafile-editor/dist/editor/code-hight-package');
|
|
|
|
|
|
|
|
class DraftReview extends React.Component {
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
this.state = {
|
|
|
|
draftContent: '',
|
|
|
|
draftOriginContent: '',
|
|
|
|
reviewStatus: opStatus,
|
|
|
|
isLoading: true,
|
2018-10-23 05:13:44 +00:00
|
|
|
commentsNumber: null,
|
|
|
|
isShowComments: false,
|
2018-10-25 02:39:16 +00:00
|
|
|
inResizing: false,
|
|
|
|
commentWidth: 30,
|
2018-10-30 03:07:01 +00:00
|
|
|
isShowDiff: true,
|
|
|
|
showDiffTip: false,
|
2018-11-05 02:33:33 +00:00
|
|
|
showReviewerDialog: false,
|
2018-11-07 03:03:26 +00:00
|
|
|
reviewers: [],
|
2018-10-15 07:51:29 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
if (publishFileVersion == 'None') {
|
2018-10-16 10:19:51 +00:00
|
|
|
axios.all([
|
|
|
|
seafileAPI.getFileDownloadLink(draftOriginRepoID, draftFilePath),
|
|
|
|
seafileAPI.getFileDownloadLink(draftOriginRepoID, draftOriginFilePath)
|
2018-10-15 07:51:29 +00:00
|
|
|
]).then(axios.spread((res1, res2) => {
|
|
|
|
axios.all([
|
|
|
|
seafileAPI.getFileContent(res1.data),
|
|
|
|
seafileAPI.getFileContent(res2.data)
|
|
|
|
]).then(axios.spread((draftContent, draftOriginContent) => {
|
|
|
|
this.setState({
|
|
|
|
draftContent: draftContent.data,
|
|
|
|
draftOriginContent: draftOriginContent.data,
|
|
|
|
isLoading: false
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
}));
|
|
|
|
} else {
|
|
|
|
let dl0 = siteRoot + 'repo/' + draftOriginRepoID + '/' + publishFileVersion + '/download?' + 'p=' + draftOriginFilePath;
|
|
|
|
let dl = siteRoot + 'repo/' + draftOriginRepoID + '/' + originFileVersion + '/download?' + 'p=' + draftOriginFilePath;
|
|
|
|
axios.all([
|
|
|
|
seafileAPI.getFileContent(dl0),
|
|
|
|
seafileAPI.getFileContent(dl)
|
|
|
|
]).then(axios.spread((draftContent, draftOriginContent) => {
|
|
|
|
this.setState({
|
|
|
|
draftContent: draftContent.data,
|
|
|
|
draftOriginContent: draftOriginContent.data,
|
|
|
|
isLoading: false,
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onCloseReview = () => {
|
|
|
|
seafileAPI.updateReviewStatus(reviewID, 'closed').then(res => {
|
|
|
|
this.setState({reviewStatus: 'closed'});
|
2018-10-25 09:50:47 +00:00
|
|
|
let msg_s = gettext('Successfully closed review %(reviewID)s.');
|
|
|
|
msg_s = msg_s.replace('%(reviewID)s', reviewID);
|
|
|
|
Toast.success(msg_s);
|
2018-10-15 07:51:29 +00:00
|
|
|
}).catch(() => {
|
2018-10-25 09:50:47 +00:00
|
|
|
let msg_s = gettext('Failed to close review %(reviewID)s');
|
|
|
|
msg_s = msg_s.replace('%(reviewID)s', reviewID);
|
|
|
|
Toast.error(msg_s);
|
2018-10-15 07:51:29 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
onPublishReview = () => {
|
|
|
|
seafileAPI.updateReviewStatus(reviewID, 'finished').then(res => {
|
|
|
|
this.setState({reviewStatus: 'finished'});
|
2018-11-15 10:05:20 +00:00
|
|
|
let msg_s = gettext('Successfully published draft.');
|
2018-10-25 09:50:47 +00:00
|
|
|
Toast.success(msg_s);
|
2018-10-15 07:51:29 +00:00
|
|
|
}).catch(() => {
|
2018-11-15 10:05:20 +00:00
|
|
|
let msg_s = gettext('Failed to publish draft.');
|
2018-10-25 09:50:47 +00:00
|
|
|
Toast.error(msg_s);
|
2018-10-15 07:51:29 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-10-23 05:13:44 +00:00
|
|
|
toggleCommentList = () => {
|
|
|
|
this.setState({
|
|
|
|
isShowComments: !this.state.isShowComments
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
getCommentsNumber = () => {
|
|
|
|
seafileAPI.listReviewComments(reviewID).then((res) => {
|
|
|
|
let number = res.data.total_count;
|
|
|
|
this.setState({
|
|
|
|
commentsNumber: number,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-10-25 02:39:16 +00:00
|
|
|
onResizeMouseUp = () => {
|
|
|
|
if(this.state.inResizing) {
|
|
|
|
this.setState({
|
|
|
|
inResizing: false
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
onResizeMouseDown = () => {
|
|
|
|
this.setState({
|
|
|
|
inResizing: true
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
onResizeMouseMove = (e) => {
|
|
|
|
let rate = 100 - e.nativeEvent.clientX / this.refs.main.clientWidth * 100;
|
|
|
|
if(rate < 20 || rate > 60) {
|
|
|
|
this.setState({
|
|
|
|
inResizing: false
|
|
|
|
});
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
this.setState({
|
|
|
|
commentWidth: rate
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2018-10-30 03:07:01 +00:00
|
|
|
onSwitchShowDiff = () => {
|
|
|
|
this.setState({
|
|
|
|
isShowDiff: !this.state.isShowDiff,
|
2018-11-07 03:03:26 +00:00
|
|
|
});
|
2018-10-30 03:07:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
toggleDiffTip = () => {
|
|
|
|
this.setState({
|
|
|
|
showDiffTip: !this.state.showDiffTip
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-11-05 02:33:33 +00:00
|
|
|
toggleAddReviewerDialog = () => {
|
2018-11-12 01:28:02 +00:00
|
|
|
if (this.state.showReviewerDialog) {
|
|
|
|
this.listReviewers();
|
|
|
|
}
|
2018-11-05 02:33:33 +00:00
|
|
|
this.setState({
|
|
|
|
showReviewerDialog: !this.state.showReviewerDialog
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-11-07 03:03:26 +00:00
|
|
|
listReviewers = () => {
|
|
|
|
seafileAPI.listReviewers(reviewID).then((res) => {
|
|
|
|
this.setState({
|
|
|
|
reviewers: res.data.reviewers
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-10-23 05:13:44 +00:00
|
|
|
componentWillMount() {
|
|
|
|
this.getCommentsNumber();
|
2018-11-07 03:03:26 +00:00
|
|
|
this.listReviewers();
|
2018-10-23 05:13:44 +00:00
|
|
|
}
|
|
|
|
|
2018-10-15 07:51:29 +00:00
|
|
|
render() {
|
2018-10-25 02:39:16 +00:00
|
|
|
const onResizeMove = this.state.inResizing ? this.onResizeMouseMove : null;
|
2018-10-30 06:20:02 +00:00
|
|
|
const draftLink = siteRoot + 'lib/' + draftOriginRepoID + '/file' + draftFilePath + '?mode=edit';
|
2018-11-19 08:18:11 +00:00
|
|
|
const OriginFileLink = siteRoot + 'lib/' + draftOriginRepoID + '/file' + draftOriginFilePath + '/';
|
2018-10-15 07:51:29 +00:00
|
|
|
return(
|
|
|
|
<div className="wrapper">
|
|
|
|
<div id="header" className="header review">
|
|
|
|
<div className="cur-file-info">
|
|
|
|
<div className="info-item file-feature">
|
|
|
|
<span className="fas fa-code-merge"></span>
|
|
|
|
</div>
|
|
|
|
<div className="info-item file-info">
|
2018-10-30 03:07:01 +00:00
|
|
|
<React.Fragment>
|
|
|
|
<span className="file-name">{draftFileName}</span>
|
|
|
|
<span className="file-copywriting">{gettext('review')}</span>
|
2018-11-19 08:18:11 +00:00
|
|
|
{ opStatus == 'open' && <a href={draftLink} className="draft-link">{gettext('Edit draft')}</a>}
|
|
|
|
{ opStatus !== 'open' && <a href={OriginFileLink} className="view-file-link">{gettext('View File')}</a>}
|
2018-10-30 03:07:01 +00:00
|
|
|
</React.Fragment>
|
2018-11-19 08:18:11 +00:00
|
|
|
</div>
|
2018-10-15 07:51:29 +00:00
|
|
|
</div>
|
2018-10-23 05:13:44 +00:00
|
|
|
<div className="button-group">
|
2018-10-30 03:07:01 +00:00
|
|
|
<div className={'seafile-toggle-diff'}>
|
|
|
|
<label className="custom-switch" id="toggle-diff">
|
2018-11-06 10:03:41 +00:00
|
|
|
<input type="checkbox" checked={this.state.isShowDiff && 'checked'}
|
2018-10-30 06:20:02 +00:00
|
|
|
name="option" className="custom-switch-input"
|
2018-11-06 10:03:41 +00:00
|
|
|
onChange={this.onSwitchShowDiff}/>
|
2018-10-30 03:07:01 +00:00
|
|
|
<span className="custom-switch-indicator"></span>
|
|
|
|
</label>
|
|
|
|
<Tooltip placement="bottom" isOpen={this.state.showDiffTip}
|
|
|
|
target="toggle-diff" toggle={this.toggleDiffTip}>
|
|
|
|
{gettext('View diff')}</Tooltip>
|
|
|
|
</div>
|
2018-10-23 05:13:44 +00:00
|
|
|
<button className="btn btn-icon btn-secondary btn-active common-list-btn"
|
|
|
|
id="commentsNumber" type="button" data-active="false"
|
|
|
|
onMouseDown={this.toggleCommentList}>
|
|
|
|
<i className="fa fa-comments"></i>
|
|
|
|
{ this.state.commentsNumber > 0 &&
|
|
|
|
<span> {this.state.commentsNumber}</span>
|
|
|
|
}
|
|
|
|
</button>
|
|
|
|
{
|
|
|
|
this.state.reviewStatus === 'open' &&
|
|
|
|
<div className="cur-file-operation">
|
2018-11-15 10:05:20 +00:00
|
|
|
<button className='btn btn-secondary file-operation-btn' title={gettext('Close review')}
|
|
|
|
onClick={this.onCloseReview}>{gettext('Close')}</button>
|
|
|
|
<button className='btn btn-success file-operation-btn' title={gettext('Publish draft')}
|
|
|
|
onClick={this.onPublishReview}>{gettext('Publish')}</button>
|
2018-10-23 05:13:44 +00:00
|
|
|
</div>
|
|
|
|
}
|
|
|
|
{
|
|
|
|
this.state.reviewStatus === 'finished' &&
|
|
|
|
<div className="review-state review-state-finished">{gettext('Finished')}</div>
|
|
|
|
}
|
|
|
|
{
|
|
|
|
this.state.reviewStatus === 'closed' &&
|
|
|
|
<div className="review-state review-state-closed">{gettext('Closed')}</div>
|
|
|
|
}
|
|
|
|
</div>
|
2018-10-15 07:51:29 +00:00
|
|
|
</div>
|
2018-10-25 02:39:16 +00:00
|
|
|
<div id="main" className="main" ref="main">
|
|
|
|
<div className="cur-view-container content-container"
|
|
|
|
onMouseMove={onResizeMove} onMouseUp={this.onResizeMouseUp} ref="comment">
|
|
|
|
<div style={{width:(100-this.state.commentWidth)+'%'}}
|
|
|
|
className={!this.state.isShowComments ? 'cur-view-content' : 'cur-view-content cur-view-content-commenton'} >
|
2018-11-07 03:03:26 +00:00
|
|
|
{this.state.isLoading ?
|
2018-10-30 03:07:01 +00:00
|
|
|
<div className="markdown-viewer-render-content article">
|
|
|
|
<Loading />
|
|
|
|
</div>
|
|
|
|
:
|
|
|
|
<div className="markdown-viewer-render-content article">
|
|
|
|
{this.state.isShowDiff ?
|
2018-11-16 06:35:44 +00:00
|
|
|
<DiffViewer newMarkdownContent={this.state.draftContent} oldMarkdownContent={this.state.draftOriginContent} />
|
2018-10-30 03:07:01 +00:00
|
|
|
:
|
2018-11-16 06:35:44 +00:00
|
|
|
<DiffViewer newMarkdownContent={this.state.draftContent} oldMarkdownContent={this.state.draftContent} />
|
2018-10-30 03:07:01 +00:00
|
|
|
}
|
|
|
|
</div>
|
|
|
|
}
|
2018-10-15 07:51:29 +00:00
|
|
|
</div>
|
2018-10-23 05:13:44 +00:00
|
|
|
{ this.state.isShowComments &&
|
2018-10-25 02:39:16 +00:00
|
|
|
<div className="cur-view-right-part" style={{width:(this.state.commentWidth)+'%'}}>
|
|
|
|
<div className="seafile-comment-resize" onMouseDown={this.onResizeMouseDown}></div>
|
|
|
|
<ReviewComments
|
|
|
|
toggleCommentList={this.toggleCommentList}
|
|
|
|
commentsNumber={this.state.commentsNumber}
|
|
|
|
getCommentsNumber={this.getCommentsNumber}
|
|
|
|
inResizing={this.state.inResizing}
|
|
|
|
/>
|
|
|
|
</div>
|
2018-10-23 05:13:44 +00:00
|
|
|
}
|
2018-11-07 03:03:26 +00:00
|
|
|
{ !this.state.isShowComments &&
|
|
|
|
<div className="cur-view-right-part" style={{width:(this.state.commentWidth)+'%'}}>
|
|
|
|
<div className="seafile-comment-resize" onMouseDown={this.onResizeMouseDown}></div>
|
|
|
|
<div className="review-side-panel">
|
|
|
|
<div className="review-side-panel-head">{gettext('Review #')}{reviewID}</div>
|
|
|
|
<div className="review-side-panel-body">
|
|
|
|
<div className="review-side-panel-reviewers">
|
|
|
|
<div className="reviewers-header">
|
|
|
|
<div className="review-side-panel-header">{gettext('Reviewers')}</div>
|
|
|
|
<i className="fa fa-cog" onClick={this.toggleAddReviewerDialog}></i>
|
|
|
|
</div>
|
|
|
|
{ this.state.reviewers.length > 0 ?
|
|
|
|
this.state.reviewers.map((item, index = 0, arr) => {
|
|
|
|
return (
|
|
|
|
<div className="reviewer-info" key={index}>
|
|
|
|
<img className="avatar reviewer-avatar" src={item.avatar_url} alt=""/>
|
|
|
|
<span className="reviewer-name">{item.user_name}</span>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
})
|
|
|
|
:
|
|
|
|
<span>{gettext('No reviewer yet.')}</span>
|
|
|
|
}
|
|
|
|
</div>
|
|
|
|
<div className="review-side-panel-author">
|
|
|
|
<div className="author-header">
|
|
|
|
<div className="review-side-panel-header">{gettext('Author')}</div>
|
|
|
|
</div>
|
|
|
|
<div className="author-info">
|
2018-11-09 09:32:36 +00:00
|
|
|
<img className="avatar author-avatar" src={authorAvatar} alt=""/>
|
|
|
|
<span className="author-name">{author}</span>
|
2018-11-07 03:03:26 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
}
|
2018-10-15 07:51:29 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2018-11-05 02:33:33 +00:00
|
|
|
{ this.state.showReviewerDialog &&
|
|
|
|
<AddReviewerDialog
|
|
|
|
showReviewerDialog={this.state.showReviewerDialog}
|
|
|
|
toggleAddReviewerDialog={this.toggleAddReviewerDialog}
|
|
|
|
reviewID={reviewID}
|
2018-11-07 03:03:26 +00:00
|
|
|
reviewers={this.state.reviewers}
|
2018-11-05 02:33:33 +00:00
|
|
|
/>
|
|
|
|
}
|
2018-10-15 07:51:29 +00:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ReactDOM.render (
|
|
|
|
<DraftReview />,
|
|
|
|
document.getElementById('wrapper')
|
2018-10-25 09:50:47 +00:00
|
|
|
);
|