import React from 'react'; import PropTypes from 'prop-types'; import moment from 'moment'; import { processor } from '@seafile/seafile-editor/dist/utils/seafile-markdown2html'; import { Button, Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap'; import { gettext } from '../../utils/constants'; import { seafileAPI } from '../../utils/seafile-api'; import ParticipantsList from './participants-list'; import { Utils } from '../../utils/utils'; import toaster from '../toast'; import { siteRoot } from '../../utils/constants'; import { MentionsInput, Mention } from 'react-mentions'; import { defaultStyle, defaultMentionStyle } from '../../css/react-mentions-default-style'; import '../../css/comments-list.css'; const { username, repoID, filePath } = window.app.pageOptions; const CommentPanelPropTypes = { toggleCommentPanel: PropTypes.func.isRequired, commentsNumber: PropTypes.number, participants: PropTypes.array, onParticipantsChange: PropTypes.func, }; class CommentPanel extends React.Component { constructor(props) { super(props); this.state = { commentsList: [], showResolvedComment: true, participants: null, relatedUsers: null, comment: '', }; } toggleResolvedComment = () => { this.setState({ showResolvedComment: !this.state.showResolvedComment }); } listComments = () => { seafileAPI.listComments(repoID, filePath).then((res) => { this.setState({ commentsList: res.data.comments }); }).catch(error => { let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); }); } listRepoRelatedUsers = () => { seafileAPI.listRepoRelatedUsers(repoID).then((res) => { let users = res.data.user_list.map((item) => { return { id: (siteRoot + 'profile/' + item.email), display: item.name}; }); this.setState({ relatedUsers: users }); }); } handleCommentChange = (event) => { this.setState({ comment: event.target.value }); } submitComment = () => { let comment = this.state.comment; if (comment.trim()) { seafileAPI.postComment(repoID, filePath, comment).then(() => { this.listComments(); }).catch(error => { let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); }); this.addParticipant(username); } this.setState({ comment: '' }); } resolveComment = (event) => { seafileAPI.updateComment(repoID, event.target.id, 'true').then(() => { this.listComments(); }).catch(error => { let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); }); } deleteComment = (event) => { seafileAPI.deleteComment(repoID, event.target.id).then(() => { this.listComments(); }).catch(error => { let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); }); } editComment = (commentID, newComment) => { seafileAPI.updateComment(repoID, commentID, null, null, newComment).then((res) => { this.listComments(); }).catch(error => { let errMessage = Utils.getErrorMsg(error); toaster.danger(errMessage); }); } onParticipantsChange = () => { if (this.props.onParticipantsChange) { this.props.onParticipantsChange(); } else { this.getParticipants(); } } getParticipants = () => { if (this.props.participants) { this.setState({ participants: this.props.participants }); } else { seafileAPI.listFileParticipants(repoID, filePath).then((res) => { this.setState({ participants: res.data.participant_list }); }); } } checkParticipant = (email) => { return this.state.participants.map((participant) => {return participant.email;}).includes(email); } addParticipant = (email) => { if (this.checkParticipant(email)) return; seafileAPI.addFileParticipant(repoID, filePath, email).then((res) => { this.onParticipantsChange(repoID, filePath); }).catch((err) => { let errMessage = Utils.getErrorMsg(err); toaster.danger(errMessage); }); } renderUserSuggestion = (entry, search, highlightedDisplay, index, focused) => { return
{highlightedDisplay}
; } handleMention = (id, display) => { const email = id.slice(id.lastIndexOf('/') + 1); this.addParticipant(email); } componentDidMount() { this.listComments(); this.getParticipants(); this.listRepoRelatedUsers(); } componentWillReceiveProps(nextProps) { if (this.props.commentsNumber !== nextProps.commentsNumber) { this.listComments(); } if (this.props.participants !== nextProps.participants) { this.setState({ participants: nextProps.participants }); } } render() { const { participants, commentsList } = this.state; return (
{gettext('Comments')}
{gettext('Show resolved comments')}
{participants && }
); } } CommentPanel.propTypes = CommentPanelPropTypes; const commentItemPropTypes = { time: PropTypes.string.isRequired, item: PropTypes.object.isRequired, deleteComment: PropTypes.func.isRequired, resolveComment: PropTypes.func.isRequired, showResolvedComment: PropTypes.bool.isRequired, editComment: PropTypes.func.isRequired, }; class CommentItem extends React.Component { constructor(props) { super(props); this.state = { dropdownOpen: false, html: '', newComment: this.props.item.comment, editable: false, }; } toggleDropDownMenu = () => { this.setState({ dropdownOpen: !this.state.dropdownOpen, }); } convertComment = (mdFile) => { processor.process(mdFile).then( (result) => { let html = String(result); this.setState({ html: html }); } ); } toggleEditComment = () => { this.setState({ editable: !this.state.editable }); } updateComment = (event) => { const newComment = this.state.newComment; if (this.props.item.comment !== newComment) { this.props.editComment(event.target.id, newComment); } this.toggleEditComment(); } handleCommentChange = (event) => { this.setState({ newComment: event.target.value, }); } componentWillMount() { this.convertComment(this.props.item.comment); } componentWillReceiveProps(nextProps) { this.convertComment(nextProps.item.comment); } render() { const item = this.props.item; if (item.resolved && !this.props.showResolvedComment) { return null; } if (this.state.editable) { return(
  • {item.user_name}
    {this.props.time}
    {' '}
  • ); } return (
  • {item.user_name}
    {this.props.time}
    { (item.user_email === username) && {gettext('Delete')}} { (item.user_email === username) && {gettext('Edit')} } { !item.resolved && {gettext('Mark as resolved')} }
  • ); } } CommentItem.propTypes = commentItemPropTypes; export default CommentPanel;