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')} + + + + + {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} /> }