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

Draft review (#2416)

This commit is contained in:
C_Q
2018-10-15 15:51:29 +08:00
committed by Daniel Pan
parent 70aa4a0257
commit 2eef50d05e
46 changed files with 1261 additions and 441 deletions

View File

@@ -5,6 +5,8 @@ import { siteRoot } from './utils/constants';
import SidePanel from './components/side-panel';
import MainPanel from './components/main-panel';
import DraftsView from './pages/drafts/drafts-view';
import DraftContent from './pages/drafts/draft-content';
import ReviewContent from './pages/drafts/review-content';
import FilesActivities from './pages/dashboard/files-activities';
import Starred from './pages/starred/starred';
@@ -49,7 +51,10 @@ class App extends Component {
<MainPanel onShowSidePanel={this.onShowSidePanel}>
<Router>
<FilesActivities path={siteRoot + 'dashboard'} />
<DraftsView path={siteRoot + 'drafts'} />
<DraftsView path={siteRoot + 'drafts'} currentTab={currentTab}>
<DraftContent path='/' />
<ReviewContent path='reviews' />
</DraftsView>
<Starred path={siteRoot + 'starred'} />
</Router>
</MainPanel>

View File

@@ -47,8 +47,22 @@ class DraftListItem extends React.Component {
onDraftEditClick = () => {
let draft = this.props.draft;
let filePath = draft.draft_file_path;
let repoID = draft.draft_repo_id;
window.location.href= siteRoot + 'lib/' + repoID + '/file' + filePath + '?mode=edit';
let repoID = draft.origin_repo_id;
let url = siteRoot + 'lib/' + repoID + '/file' + filePath + '?mode=edit&draft_id=' + draft.id;
window.open(url)
}
onLibraryClick = () => {
let draft = this.props.draft;
let repoID = draft.origin_repo_id;
let url = siteRoot + '#common/lib/' + repoID;
window.open(url)
}
onReviewClick = () => {
let draft = this.props.draft;
let url = siteRoot + 'drafts/review/' + draft.review_id + '/';
window.open(url);
}
getFileName(filePath) {
@@ -65,13 +79,19 @@ class DraftListItem extends React.Component {
<tr className={this.state.highlight} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
<td className="icon"><img src={siteRoot + 'media/img/file/192/txt.png'} /></td>
<td className="name a-simulate" onClick={this.onDraftEditClick}>{fileName}</td>
<td className="owner">{draft.owner}</td>
<td className="library a-simulate" onClick={this.onLibraryClick}>{draft.repo_name}</td>
<td className="review">
{ draft.review_id && draft.review_status === 'open' ? <span className="a-simulate" onClick={this.onReviewClick}>#{draft.review_id}</span> : <span>--</span> }
</td>
<td className="update">{localTime}</td>
<td className="menu-toggle">
<NodeMenuControl
isShow={this.state.isMenuControlShow}
onClick={this.onMenuToggleClick}
/>
{
this.props.draft.review_status !== 'open' &&
<NodeMenuControl
isShow={this.state.isMenuControlShow}
onClick={this.onMenuToggleClick}
/>
}
</td>
</tr>
);

View File

@@ -13,18 +13,28 @@ class DraftListMenu extends React.Component {
render() {
let style = {};
let {isMenuShow, menuPosition} = this.props;
let {isMenuShow, menuPosition, currentDraft} = this.props;
if (isMenuShow) {
style = {position: 'fixed', top: menuPosition.top, left: menuPosition.left, display: 'block'};
}
return (
<div>
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>
</div>
);
);
}
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>
);
}
}
}

View File

@@ -20,8 +20,9 @@ class DraftListView extends React.Component {
<tr>
<th style={{width: '4%'}}>{/*img*/}</th>
<th style={{width: '46%'}}>{gettext('Name')}</th>
<th style={{width: '20%'}}>{gettext('Owner')}</th>
<th style={{width: '20%'}}>{gettext('Last Update')}</th>
<th style={{width: '20%'}}>{gettext('Library')}</th>
<th style={{width: '10%'}}>{gettext('Review')}</th>
<th style={{width: '10%'}}>{gettext('Last Update')}</th>
<th style={{width: '10%'}}></th>
</tr>
</thead>

View File

@@ -147,13 +147,13 @@ class MainSideNav extends React.Component {
<h3 className="sf-heading">Tools</h3>
<ul className="side-tabnav-tabs">
<li className={`tab ${this.state.currentTab === 'starred' ? 'tab-cur' : ''}`}>
<a href={siteRoot + 'starred/'} title={gettext('Favorites')} onClick={() => this.tabItemClick('favorites')}>
<Link to={siteRoot + 'starred/'} title={gettext('Favorites')} onClick={() => this.tabItemClick('favorites')}>
<span className="sf2-icon-star" aria-hidden="true"></span>
{gettext('Favorites')}
</a>
</Link>
</li>
<li className={`tab ${this.state.currentTab === 'dashboard' ? 'tab-cur' : ''}`}>
<Link to={siteRoot + 'dashboard'} title={gettext('Acitivities')} onClick={() => this.tabItemClick('dashboard')}>
<Link to={siteRoot + 'dashboard/'} title={gettext('Acitivities')} onClick={() => this.tabItemClick('dashboard')}>
<span className="sf2-icon-clock" aria-hidden="true"></span>
{gettext('Acitivities')}
</Link>
@@ -165,7 +165,7 @@ class MainSideNav extends React.Component {
</a>
</li>
<li className={`tab ${this.state.currentTab === 'drafts' ? 'tab-cur' : ''}`} onClick={() => this.tabItemClick('drafts')}>
<Link to={siteRoot + 'drafts'} title={gettext('Drafts')}>
<Link to={siteRoot + 'drafts/'} title={gettext('Drafts')}>
<span className="sf2-icon-edit" aria-hidden="true"></span>
{gettext('Drafts')}
</Link>

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { processor, processorGetAST } from '@seafile/seafile-editor/src/lib/seafile-markdown2html';
import { processor, processorGetAST } from '@seafile/seafile-editor/dist/utils/seafile-markdown2html';
import Prism from 'prismjs';
import WikiOutline from './wiki-outline';
@@ -7,7 +7,7 @@ var URL = require('url-parse');
const gettext = window.gettext;
require('@seafile/seafile-editor/src/lib/code-hight-package');
require('@seafile/seafile-editor/dist/editor/code-hight-package');
const contentClass = "wiki-md-viewer-rendered-content";

View File

@@ -0,0 +1,68 @@
import React from 'react';
import PropTypes from 'prop-types';
import { siteRoot, lang } from '../../utils/constants';
import moment from 'moment';
moment.locale(lang);
const propTypes = {
isItemFreezed: PropTypes.bool.isRequired,
}
class ReviewListItem extends React.Component {
constructor(props) {
super(props);
this.state = {
highlight: '',
};
}
onMouseEnter = () => {
if (!this.props.isItemFreezed) {
this.setState({
highlight: 'tr-highlight'
});
}
}
onMouseLeave = () => {
if (!this.props.isItemFreezed) {
this.setState({
highlight: ''
});
}
}
onReviewsClick = () => {
let item = this.props.item;
let filePath = item.draft_file_path;
let itemID = item.id;
window.open(siteRoot + 'drafts/review/' + itemID);
}
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 localTime = moment.utc(item.updated_at).toDate();
localTime = moment(localTime).fromNow();
return (
<tr className={this.state.highlight} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>
<td className="icon" style={{width: "4%"}}><img src={siteRoot + "media/img/file/192/txt.png"} /></td>
<td className="name a-simulate" style={{width: "46%"}} onClick={this.onReviewsClick}>{fileName}</td>
<td className='library'>{item.draft_origin_repo_name}</td>
<td className="status" style={{width: "20%"}}>{item.status}</td>
<td className="update" style={{width: "20%"}}>{localTime}</td>
<td className="menu-toggle"></td>
</tr>
);
}
}
ReviewListItem.propTypes = propTypes;
export default ReviewListItem;

View File

@@ -0,0 +1,47 @@
import React from 'react';
import PropTypes from 'prop-types';
import { gettext } from '../../utils/constants';
import ReviewListItem from './review-list-item';
const propTypes = {
isItemFreezed: PropTypes.bool.isRequired,
itemsList: PropTypes.array.isRequired,
};
class ReviewListView extends React.Component {
render() {
let items = this.props.itemsList;
return (
<div className="table-container">
<table>
<thead>
<tr>
<th style={{width: '4%'}}>{/*img*/}</th>
<th style={{width: '26%'}}>{gettext('Name')}</th>
<th style={{width: '20%'}}>{gettext('Library')}</th>
<th style={{width: '20%'}}>{gettext('Status')}</th>
<th style={{width: '20%'}}>{gettext('Last Update')}</th>
<th style={{width: '10%'}}></th>
</tr>
</thead>
<tbody>
{ items && items.map((item) => {
return (
<ReviewListItem
key={item.id}
item={item}
isItemFreezed={this.props.isItemFreezed}
/>
);
})}
</tbody>
</table>
</div>
);
}
}
ReviewListView.propTypes = propTypes;
export default ReviewListView;

View File

@@ -142,3 +142,14 @@
.article hr.active {
border-top: 1px solid #eb8205;
}
.content-container {
background-color: #fafaf9;
border-radius: 10px;
}
.markdown-viewer-render-content {
margin: 20px 40px;
background-color: #fff;
border: 1px solid #e6e6dd;
}

View File

@@ -1,4 +1,4 @@
#wrapper {
#wrapper, .wrapper {
width: 100%;
height: 100%;
display: flex;
@@ -7,7 +7,6 @@
/* for top bottom layout*/
#header {
height: 49px;
display: flex;
}
@@ -112,4 +111,82 @@
[role=group] {
display: flex;
flex: 1;
}
}
.header {
padding: 0.625rem;
display: flex;
flex-shrink:0;
align-items: center;
justify-content: space-between;
background-color: #fff;
border-bottom: 1px solid #e5e5e5;
box-shadow: 0 3px 2px -2px rgba(200,200,200,.15);
}
.header .cur-file-info {
display: flex;
margin-left: 0.5rem;
}
.header .info-item {
display: flex;
font-size: 1.2rem;
font-weight: bold;
margin-right: 0.5rem;
align-items: center;
justify-content: center;
}
.header .file-copywriting {
margin-left: 0.5rem;
font-size: 1rem;
font-weight: normal;
color: #999;
}
.header .file-feature {
width: 2.9375rem;
height: 2.9375rem;
font-size: 1.8rem;
background-color: #fbcb09;
color: #fff;
}
.header .file-operation-btn {
margin-right: 0.25rem;
}
.review {
padding: 0;
}
.review .cur-file-info {
margin: 0;
}
.review .file-feature {
width: 4.1875rem;
height: 4.1875rem;
font-size: 2rem;
}
.review-state {
position: relative;
padding: 0.75rem 1.25rem;
margin: auto 0.5rem;
border: 1px solid transparent;
border-radius: 3px;
}
.review-state-finished {
color: #316100;
background-color: #dff1cc;
border-color: #d2ecb8;
}
.review-state-closed {
color: #6b1110;
background-color: #f5d2d2;
border-color: #f1c1c0;
}

View File

@@ -0,0 +1,133 @@
import React from 'react';
import ReactDOM from 'react-dom';
import Prism from 'prismjs';
import { siteRoot, gettext, draftID, reviewID, draftOriginFilePath, draftFilePath, draftOriginRepoID, draftFileName, opStatus, publishFileVersion, originFileVersion } from './utils/constants';
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';
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';
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,
};
}
componentDidMount() {
if (publishFileVersion == 'None') {
axios.all([
seafileAPI.getFileDownloadLink(draftOriginRepoID, draftFilePath),
seafileAPI.getFileDownloadLink(draftOriginRepoID, draftOriginFilePath)
]).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'});
Toast.success('Review close succeeded.')
}).catch(() => {
Toast.error('Review close failed.')
});
}
onPublishReview = () => {
seafileAPI.updateReviewStatus(reviewID, 'finished').then(res => {
this.setState({reviewStatus: 'finished'});
Toast.success('Review publish succeeded.')
}).catch(() => {
Toast.error('Review publish failed.')
});
}
render() {
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">
<span className="file-name">{draftFileName}</span>
<span className="file-copywriting">{gettext('review')}</span>
</div>
</div>
{
this.state.reviewStatus === 'open' &&
<div className="cur-file-operation">
<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 Review')} onClick={this.onPublishReview}>{gettext("Publish")}</button>
</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>
<div id="main" className="main">
<div className="cur-view-container content-container">
<div className="cur-view-content">
<div className="markdown-viewer-render-content article">
{
this.state.isLoading ?
<Loading /> :
<DiffViewer markdownContent={this.state.draftContent} markdownContent1={this.state.draftOriginContent} />
}
</div>
</div>
</div>
</div>
</div>
);
}
}
ReactDOM.render (
<DraftReview />,
document.getElementById('wrapper')
);

View File

@@ -10,6 +10,7 @@ let siteRoot = window.app.config.siteRoot;
let domain = window.app.pageOptions.domain;
let protocol = window.app.pageOptions.protocol;
let mode = window.app.pageOptions.mode;
let draftID = window.app.pageOptions.draftID;
let dirPath = '/';
const serviceUrl = window.app.config.serviceUrl;
@@ -25,6 +26,7 @@ function getImageFileNameWithTimestamp() {
return 'image-' + d.toString() + '.png';
}
class EditorUtilities {
constructor () {
@@ -170,6 +172,14 @@ class EditorUtilities {
getFileHistoryVersion(commitID) {
return seafileAPI.getFileRevision(repoID, commitID, filePath);
}
createDraftReview() {
return seafileAPI.createDraftReview(draftID)
.then(res => {
let url = serviceUrl + '/drafts/review/' + res.data.id;
return url;
})
}
}
@@ -243,6 +253,7 @@ class MarkdownEditor extends React.Component {
collabServer={this.state.collabServer}
showFileHistory={true}
mode={mode}
draftID={draftID}
/>
);
}

View File

@@ -0,0 +1,136 @@
import React from 'react';
import { siteRoot, gettext } from '../../utils/constants';
import editUtilties from '../../utils/editor-utilties';
import Toast from '../../components/toast';
import Loading from '../../components/loading';
import DraftListView from '../../components/draft-list-view/draft-list-view';
import DraftListMenu from '../../components/draft-list-view/draft-list-menu';
class DraftContent extends React.Component {
constructor(props) {
super(props);
this.state = {
draftList: [],
isLoadingDraft: true,
isMenuShow: false,
menuPosition: {top:'', left: ''},
currentDraft: null,
isItemFreezed: false,
};
}
componentDidMount() {
this.initDraftList();
document.addEventListener('click', this.onHideContextMenu);
}
componentWillUnmount() {
document.removeEventListener('click', this.onHideContextMenu);
}
initDraftList() {
this.setState({isLoadingDraft: true});
editUtilties.listDrafts().then(res => {
this.setState({
draftList: res.data.data,
isLoadingDraft: false,
});
});
}
onDeleteHandler = () => {
let draft = this.state.currentDraft;
editUtilties.deleteDraft(draft.id).then(res => {
this.initDraftList();
Toast.success('Delete draft succeeded.');
}).catch(() => {
Toast.error('Delete draft failed.');
});
}
onPublishHandler = () => {
let draft = this.state.currentDraft;
editUtilties.publishDraft(draft.id).then(res => {
this.initDraftList();
Toast.success('Publish draft succeeded.');
}).catch(() => {
Toast.error('Publish draft failed.');
});
}
onReviewHandler = () => {
let draft = this.state.currentDraft;
editUtilties.createDraftReview(draft.id).then(res => {
window.open(siteRoot + 'drafts/review/' + res.data.id);
})
.catch((error) => {
if (error.response.status == '409') {
Toast.error("The draft review is existing.");
}
});
}
onMenuToggleClick = (e, draft) => {
if (this.state.isMenuShow) {
this.onHideContextMenu();
} else {
this.onShowContextMenu(e, draft);
}
}
onShowContextMenu = (e, draft) => {
let left = e.clientX - 8*16;
let top = e.clientY + 10;
let position = {top: top, left: left};
this.setState({
isMenuShow: true,
menuPosition: position,
currentDraft: draft,
isItemFreezed: true
});
}
onHideContextMenu = () => {
this.setState({
isMenuShow: false,
currentDraft: null,
isItemFreezed: false
});
}
render() {
return (
<div className="cur-view-content" style={{padding: 0}}>
{this.state.isLoadingDraft && <Loading /> }
{(!this.state.isLoadingDraft && this.state.draftList.length !==0) &&
<DraftListView
draftList={this.state.draftList}
isItemFreezed={this.state.isItemFreezed}
onMenuToggleClick={this.onMenuToggleClick}
/>
}
{(!this.state.isLoadingDraft && this.state.draftList.length === 0) &&
<div className="message empty-tip">
<h2>{gettext('No draft yet')}</h2>
<p>{gettext('Draft is a way to let you collaborate with others on files. You can create a draft from a file, edit the draft and then ask for a review. The original file will be updated only after the draft be reviewed.')}</p>
</div>
}
{this.state.isMenuShow &&
<DraftListMenu
isMenuShow={this.state.isMenuShow}
currentDraft={this.state.currentDraft}
menuPosition={this.state.menuPosition}
onPublishHandler={this.onPublishHandler}
onDeleteHandler={this.onDeleteHandler}
onReviewHandler={this.onReviewHandler}
/>
}
</div>
);
}
}
export default DraftContent;

View File

@@ -1,124 +1,43 @@
import React from 'react';
import { gettext } from '../../utils/constants';
import editUtilties from '../../utils/editor-utilties';
import Toast from '../../components/toast/';
import Loading from '../../components/loading';
import DraftListView from '../../components/draft-list-view/draft-list-view';
import DraftListMenu from '../../components/draft-list-view/draft-list-menu';
import { siteRoot, gettext } from '../../utils/constants';
import { Link } from '@reach/router';
class DraftsView extends React.Component {
constructor(props) {
super(props);
this.state = {
draftList: [],
isLoadingDraft: true,
isMenuShow: false,
menuPosition: {top:'', left: ''},
currentDraft: null,
isItemFreezed: false,
currentTab: this.props.currentTab
};
}
componentDidMount() {
this.initDraftList();
document.addEventListener('click', this.onHideContextMenu);
}
componentWillUnmount() {
document.removeEventListener('click', this.onHideContextMenu);
}
initDraftList() {
this.setState({isLoadingDraft: true});
editUtilties.listDrafts().then(res => {
this.setState({
draftList: res.data.data,
isLoadingDraft: false,
});
});
}
onDeleteHandler = () => {
let draft = this.state.currentDraft;
editUtilties.deleteDraft(draft.id).then(res => {
this.initDraftList();
Toast.success(gettext('Delete draft succeeded.'));
}).catch(() => {
Toast.error(gettext('Delete draft failed.'));
});
}
onPublishHandler = () => {
let draft = this.state.currentDraft;
editUtilties.publishDraft(draft.id).then(res => {
this.initDraftList();
Toast.success(gettext('Publish draft succeeded.'));
}).catch(() => {
Toast.error(gettext('Publish draft failed.'));
});
}
onMenuToggleClick = (e, draft) => {
if (this.state.isMenuShow) {
this.onHideContextMenu();
} else {
this.onShowContextMenu(e, draft);
}
}
onShowContextMenu = (e, draft) => {
let left = e.clientX - 8*16;
let top = e.clientY + 10;
let position = {top: top, left: left};
tabItemClick = (param) => {
this.setState({
isMenuShow: true,
menuPosition: position,
currentDraft: draft,
isItemFreezed: true
});
}
onHideContextMenu = () => {
this.setState({
isMenuShow: false,
currentDraft: null,
isItemFreezed: false
});
currentTab: param
})
}
render() {
return (
<div className="cur-view-container">
<div className="cur-view-path panel-heading text-left">{gettext('Drafts')}</div>
<div className="cur-view-content" style={{padding: 0}}>
{this.state.isLoadingDraft && <Loading /> }
{(!this.state.isLoadingDraft && this.state.draftList.length !==0) &&
<DraftListView
draftList={this.state.draftList}
isItemFreezed={this.state.isItemFreezed}
onMenuToggleClick={this.onMenuToggleClick}
/>
}
{(!this.state.isLoadingDraft && this.state.draftList.length === 0) &&
<div className="message empty-tip">
<h2>{gettext('No draft yet')}</h2>
<p>{gettext('Draft is a way to let you collaborate with others on files. You can create a draft from a file, edit the draft and then ask for a review. The original file will be updated only after the draft be reviewed.')}</p>
</div>
}
{this.state.isMenuShow &&
<DraftListMenu
isMenuShow={this.state.isMenuShow}
currentDraft={this.state.currentDraft}
menuPosition={this.state.menuPosition}
onPublishHandler={this.onPublishHandler}
onDeleteHandler={this.onDeleteHandler}
/>
}
<div className="cur-view-path">
<ul className="tab-tabs-nav">
<li className={`tab ${this.state.currentTab === 'drafts' ? 'ui-state-active': ''}`} onClick={() => this.tabItemClick('drafts')}>
<Link className='a' to={siteRoot + 'drafts'} title={gettext('Drafts')}>
{gettext('Drafts')}
</Link>
</li>
<li className={`tab ${this.state.currentTab === 'reviews' ? 'ui-state-active': ''}`} onClick={() => this.tabItemClick('reviews')}>
<Link className='a' to={siteRoot + 'drafts/reviews'} title={gettext('reviews')}>
{gettext('Reviews')}
</Link>
</li>
</ul>
</div>
{this.props.children}
</div>
);
}
}
export default DraftsView;
export default DraftsView;

View File

@@ -0,0 +1,52 @@
import React from 'react';
import { gettext } from '../../utils/constants';
import editUtilties from '../../utils/editor-utilties';
import Loading from '../../components/loading';
import ReviewListView from '../../components/review-list-view/review-list-view';
class ReviewContent extends React.Component {
constructor(props) {
super(props);
this.state = {
reviewsList: [],
isLoadingReviews: true,
isItemFreezed: false,
};
}
componentDidMount() {
this.initReviewList();
}
initReviewList() {
this.setState({isLoadingReviews: true});
editUtilties.listReviews().then(res => {
this.setState({
reviewsList: res.data.data,
isLoadingReviews: false,
});
});
}
render() {
return (
<div className="cur-view-content" style={{padding: 0}}>
{this.state.isLoadingReviews && <Loading /> }
{(!this.state.isLoadingReviews && this.state.reviewsList.length !==0) &&
<ReviewListView
itemsList={this.state.reviewsList}
isItemFreezed={this.state.isItemFreezed}
/>
}
{(!this.state.isLoadingReviews && this.state.reviewsList.length === 0) &&
<div className="message empty-tip">
<h2>{gettext('There is no Review file existing')}</h2>
</div>
}
</div>
);
}
}
export default ReviewContent;

View File

@@ -3,10 +3,10 @@ import PropTypes from 'prop-types';
import Prism from 'prismjs';
import Loading from '../../components/loading';
import CommonToolbar from '../../components/toolbar/common-toolbar';
import DiffViewer from '@seafile/seafile-editor/dist/diff-viewer/diff-viewer';
import DiffViewer from '@seafile/seafile-editor/dist/viewer/diff-viewer';
import '../../css/initial-style.css';
require('@seafile/seafile-editor/src/lib/code-hight-package');
require('@seafile/seafile-editor/dist/editor/code-hight-package');
const contentClass = 'markdown-viewer-render-content';
const propTypes = {

View File

@@ -115,7 +115,7 @@ class MainPanel extends Component {
<div className="btn-group">
<button className="btn btn-secondary btn-icon sf-view-mode-change-btn sf2-icon-list-view" id='list' title={gettext('List')} onClick={this.switchViewMode}></button>
<button className="btn btn-secondary btn-icon sf-view-mode-change-btn sf2-icon-grid-view" id='grid' title={gettext('Grid')} onClick={this.switchViewMode}></button>
<button className={`btn btn-secondary btn-icon sf-view-mode-change-btn sf2-icon-wiki-view ${this.state.isWikiMode ? 'current-mode' : ''}`} id='wiki' title={gettext('wiki')} onClick={this.switchViewMode}></button>
<button className={`btn btn-secondary btn-icon sf-view-mode-change-btn sf2-icon-two-columns ${this.state.isWikiMode ? 'current-mode' : ''}`} id='wiki' title={gettext('wiki')} onClick={this.switchViewMode}></button>
</div>
</div>
<CommonToolbar onSearchedClick={this.props.onSearchedClick} searchPlaceholder={'Search files in this library'}/>

View File

@@ -26,3 +26,13 @@ export const historyRepoID = window.fileHistory ? window.fileHistory.pageOptions
export const repoName = window.fileHistory ? window.fileHistory.pageOptions.repoName : '';
export const filePath = window.fileHistory ? window.fileHistory.pageOptions.filePath : '';
export const fileName = window.fileHistory ? window.fileHistory.pageOptions.fileName : '';
// Draft review
export const draftFilePath = window.draftReview ? window.draftReview.config.draftFilePath: '';
export const draftOriginFilePath = window.draftReview ? window.draftReview.config.draftOriginFilePath: '';
export const draftOriginRepoID = window.draftReview ? window.draftReview.config.draftOriginRepoID: '';
export const draftFileName = window.draftReview ? window.draftReview.config.draftFileName: '';
export const reviewID = window.draftReview ? window.draftReview.config.reviewID : '';
export const opStatus = window.draftReview ? window.draftReview.config.opStatus : '';
export const publishFileVersion = window.draftReview ? window.draftReview.config.publishFileVersion : '';
export const originFileVersion = window.draftReview ? window.draftReview.config.originFileVersion : '';

View File

@@ -119,6 +119,14 @@ class EditorUtilities {
return seafileAPI.cancelZipTask(zip_token);
}
createDraftReview(id) {
return seafileAPI.createDraftReview(id);
}
listReviews() {
return seafileAPI.listReviews();
}
}
const editorUtilities = new EditorUtilities();