diff --git a/frontend/src/components/dialog/file-participant-dialog.js b/frontend/src/components/dialog/file-participant-dialog.js
new file mode 100644
index 0000000000..e4d571bc09
--- /dev/null
+++ b/frontend/src/components/dialog/file-participant-dialog.js
@@ -0,0 +1,151 @@
+import React, {Component} from 'react';
+import PropTypes from 'prop-types';
+import {Modal, ModalHeader, ModalBody, ModalFooter, Button} from 'reactstrap';
+import {gettext} from '../../utils/constants';
+import UserSelect from '../user-select';
+import {seafileAPI} from '../../utils/seafile-api';
+import toaster from '../toast';
+
+
+class FileParticipantListItem extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ isOperationShow: false,
+ };
+ }
+
+ onMouseOver = () => {
+ this.setState({
+ isOperationShow: true,
+ });
+ };
+
+ onMouseLeave = () => {
+ this.setState({
+ isOperationShow: false,
+ });
+ };
+
+ render() {
+ return (
+
+
+

+
{this.props.participant.name}
+
+ {this.state.isOperationShow &&
+
+ }
+
+ );
+ }
+}
+
+const fileParticipantListItemPropTypes = {
+ participant: PropTypes.object.isRequired,
+ deleteFileParticipant: PropTypes.func.isRequired,
+};
+
+FileParticipantListItem.propTypes = fileParticipantListItemPropTypes;
+
+
+class FileParticipantDialog extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ selectedOption: null,
+ };
+ }
+
+ handleSelectChange = (option) => {
+ this.setState({selectedOption: option});
+ };
+
+ deleteFileParticipant = (email) => {
+ seafileAPI.deleteFileParticipant(this.props.repoID, this.props.filePath, email).then((res) => {
+ this.props.onRelatedFileChange(this.props.dirent, this.props.filePath);
+ }).catch((error) => {
+ this.handleError(error);
+ });
+ this.refs.userSelect.clearSelect();
+ };
+
+ addFileParticipant = () => {
+ if (!this.state.selectedOption || this.state.selectedOption.length === 0) {
+ return;
+ }
+ let email = this.state.selectedOption.email;
+ seafileAPI.addFileParticipant(this.props.repoID, this.props.filePath, email).then((res) => {
+ this.props.onRelatedFileChange(this.props.dirent, this.props.filePath);
+ }).catch((error) => {
+ this.handleError(error);
+ });
+ this.setState({
+ selectedOption: null,
+ });
+ this.refs.userSelect.clearSelect();
+ };
+
+ handleError = (e) => {
+ if (e.response) {
+ toaster.danger(e.response.data.error_msg || e.response.data.detail || gettext('Error'), {duration: 3});
+ } else {
+ toaster.danger(gettext('Please check the network.'), {duration: 3});
+ }
+ };
+
+ render() {
+ const renderParticipantList = this.props.fileParticipantList.map((participant, index) => {
+ return (
+
+ );
+ });
+
+ return (
+
+ {gettext('Participants')}
+
+
+
+
+
+
+
+
+ {renderParticipantList}
+
+
+
+ );
+ }
+}
+
+const fileParticipantDialogPropTypes = {
+ repoID: PropTypes.string.isRequired,
+ filePath: PropTypes.string.isRequired,
+ toggleFileParticipantDialog: PropTypes.func.isRequired,
+ fileParticipantList: PropTypes.array.isRequired,
+ onRelatedFileChange: PropTypes.func.isRequired,
+ dirent: PropTypes.object.isRequired,
+};
+
+FileParticipantDialog.propTypes = fileParticipantDialogPropTypes;
+
+export default FileParticipantDialog;
diff --git a/frontend/src/components/dirent-detail/detail-list-view.js b/frontend/src/components/dirent-detail/detail-list-view.js
index b872128bb6..73d042ad38 100644
--- a/frontend/src/components/dirent-detail/detail-list-view.js
+++ b/frontend/src/components/dirent-detail/detail-list-view.js
@@ -6,6 +6,7 @@ import { Utils } from '../../utils/utils';
import EditFileTagDialog from '../dialog/edit-filetag-dialog';
import ModalPortal from '../modal-portal';
import RelatedFileDialogs from '../dialog/related-file-dialogs';
+import FileParticipantDialog from '../dialog/file-participant-dialog';
const propTypes = {
repoInfo: PropTypes.object.isRequired,
@@ -18,6 +19,7 @@ const propTypes = {
relatedFiles: PropTypes.array.isRequired,
onFileTagChanged: PropTypes.func.isRequired,
onRelatedFileChange: PropTypes.func.isRequired,
+ fileParticipantList: PropTypes.array.isRequired,
};
class DetailListView extends React.Component {
@@ -27,6 +29,7 @@ class DetailListView extends React.Component {
this.state = {
isEditFileTagShow: false,
showRelatedFileDialog: false,
+ isFileParticipantDialogShow : false,
};
}
@@ -77,9 +80,15 @@ class DetailListView extends React.Component {
showRelatedFileDialog: false,
});
}
+
+ toggleFileParticipantDialog = () => {
+ this.setState({
+ isFileParticipantDialogShow: !this.state.isFileParticipantDialogShow,
+ });
+ }
render() {
- let { direntType, direntDetail, fileTagList, relatedFiles } = this.props;
+ let { direntType, direntDetail, fileTagList, relatedFiles, fileParticipantList } = this.props;
let position = this.getDirentPostion();
let direntPath = this.getDirentPath();
if (direntType === 'dir') {
@@ -137,6 +146,21 @@ class DetailListView extends React.Component {
+
+ {gettext('Participants')} |
+
+
+ {fileParticipantList.map((fileParticipant, index) => {
+ return (
+ -
+ {fileParticipant.name}
+
+ );
+ })}
+
+
+ |
+
{this.state.showRelatedFileDialog &&
@@ -162,6 +186,17 @@ class DetailListView extends React.Component {
onFileTagChanged={this.onFileTagChanged}
/>
}
+ {
+ this.state.isFileParticipantDialogShow &&
+
+ }
);
}
diff --git a/frontend/src/components/dirent-detail/dirent-details.js b/frontend/src/components/dirent-detail/dirent-details.js
index 0eb8b753da..01293075d6 100644
--- a/frontend/src/components/dirent-detail/dirent-details.js
+++ b/frontend/src/components/dirent-detail/dirent-details.js
@@ -32,6 +32,7 @@ class DirentDetail extends React.Component {
relatedFiles: [],
folderDirent: null,
activeTab: 'info',
+ fileParticipantList: [],
};
}
@@ -109,6 +110,11 @@ class DirentDetail extends React.Component {
});
}
});
+ seafileAPI.listFileParticipants(repoID, direntPath).then((res) => {
+ this.setState({
+ fileParticipantList: res.data.participant_list,
+ });
+ });
} else {
seafileAPI.getDirInfo(repoID, direntPath).then(res => {
this.setState({
@@ -180,6 +186,7 @@ class DirentDetail extends React.Component {
relatedFiles={this.state.relatedFiles}
onFileTagChanged={this.props.onFileTagChanged}
onRelatedFileChange={this.onRelatedFileChange}
+ fileParticipantList={this.state.fileParticipantList}
/>
}