From ec87c48002974c84002b448ac2269cc87b0d3270 Mon Sep 17 00:00:00 2001 From: Michael An <2331806369@qq.com> Date: Fri, 26 Jul 2019 16:51:47 +0800 Subject: [PATCH] Change participants UI and API (#3889) * change tooltip ID * change dialog API * change mention style * change comment panel API change detail panel API * change Tooltip * change mention color update seafile-api change comment content click --- frontend/package-lock.json | 6 +- frontend/package.json | 2 +- .../dialog/file-participant-dialog.js | 14 ++- .../dirent-detail/detail-comments-list.js | 69 ++++++++----- .../src/components/file-view/comment-panel.js | 98 ++++++++++--------- .../components/file-view/participants-list.js | 19 +++- .../src/css/react-mentions-default-style.js | 22 +++-- 7 files changed, 133 insertions(+), 97 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 854d30e9c1..7d4b6c1781 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -16272,9 +16272,9 @@ } }, "seafile-js": { - "version": "0.2.108", - "resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.108.tgz", - "integrity": "sha512-uGBqFfGxywsf4z+I4UX8Prz4xJe46Gvy/aCRed3751B0fCUG3+OSiKtJXnZH9TN3lOVp4sJpm7MkER1Bf1ln9A==", + "version": "0.2.109", + "resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.109.tgz", + "integrity": "sha512-qcFKdC8hA1G3Mbe4PpJ/ircBD9Miaduur2qIPkjyYIzJ8MvownGxku+r13UsNh6fLp3oCs2GqzuY4UDO5y1gcw==", "requires": { "axios": "^0.18.0", "form-data": "^2.3.2", diff --git a/frontend/package.json b/frontend/package.json index 3bcc44c244..3f02963623 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -38,7 +38,7 @@ "react-responsive": "^6.1.2", "react-select": "^2.4.1", "reactstrap": "^6.4.0", - "seafile-js": "^0.2.108", + "seafile-js": "^0.2.109", "socket.io-client": "^2.2.0", "sw-precache-webpack-plugin": "0.11.4", "unified": "^7.0.0", diff --git a/frontend/src/components/dialog/file-participant-dialog.js b/frontend/src/components/dialog/file-participant-dialog.js index d894eae9d5..2ccbf4c36c 100644 --- a/frontend/src/components/dialog/file-participant-dialog.js +++ b/frontend/src/components/dialog/file-participant-dialog.js @@ -89,14 +89,12 @@ class FileParticipantDialog extends Component { if (!selectedOption || selectedOption.length === 0) { return; } - for (let i = 0; i < selectedOption.length; i++) { - seafileAPI.addFileParticipant(repoID, filePath, selectedOption[i].email).then((res) => { - this.props.onParticipantsChange(repoID, filePath); - }).catch(error => { - let errMessage = Utils.getErrorMsg(error); - toaster.danger(errMessage); - }); - } + let emails = selectedOption.map((option) => {return option.email;}); + seafileAPI.addFileParticipants(repoID, filePath, emails).then((res) => { + this.props.onParticipantsChange(repoID, filePath); + }).catch(error => { + toaster.danger(Utils.getErrorMsg(error)); + }); this.setState({ selectedOption: null }); this.refs.userSelect.clearSelect(); }; diff --git a/frontend/src/components/dirent-detail/detail-comments-list.js b/frontend/src/components/dirent-detail/detail-comments-list.js index 3ca187c363..d0b1f9fc3c 100644 --- a/frontend/src/components/dirent-detail/detail-comments-list.js +++ b/frontend/src/components/dirent-detail/detail-comments-list.js @@ -28,6 +28,7 @@ class DetailCommentList extends React.Component { relatedUsers: null, comment: '', }; + this.toBeAddedParticipant = []; } componentDidMount() { @@ -55,21 +56,33 @@ class DetailCommentList extends React.Component { }); } - submitComment = () => { - let comment = this.state.comment; + addComment = () => { const { repoID, filePath } = this.props; - if (comment.trim()) { - seafileAPI.postComment(repoID, filePath, comment.trim()).then(() => { - this.listComments(); - }).catch(error => { - let errMessage = Utils.getErrorMsg(error); - toaster.danger(errMessage); - }); - this.addParticipant(username); - } + if (!this.state.comment.trim()) return; + seafileAPI.postComment(repoID, filePath, this.state.comment.trim()).then(() => { + this.listComments(); + }).catch(err => { + toaster.danger(Utils.getErrorMsg(err)); + }); this.setState({ comment: '' }); } + onSubmit = () => { + this.addParticipant(username); + if (this.toBeAddedParticipant.length === 0) { + this.addComment(); + } else { + const { repoID, filePath } = this.props; + seafileAPI.addFileParticipants(repoID, filePath, this.toBeAddedParticipant).then((res) => { + this.props.onParticipantsChange(repoID, filePath); + this.toBeAddedParticipant = []; + this.addComment(); + }).catch((err) => { + toaster.danger(Utils.getErrorMsg(err)); + }); + } + } + resolveComment = (event) => { const { repoID } = this.props; seafileAPI.updateComment(repoID, event.target.id, 'true').then(() => { @@ -104,7 +117,7 @@ class DetailCommentList extends React.Component { const { repoID } = this.props; seafileAPI.listRepoRelatedUsers(repoID).then((res) => { let users = res.data.user_list.map((item) => { - return { id: (siteRoot + 'profile/' + item.email), display: item.name}; + return { id: item.email, display: item.name}; }); this.setState({ relatedUsers: users }); }); @@ -116,23 +129,13 @@ class DetailCommentList extends React.Component { addParticipant = (email) => { if (this.checkParticipant(email)) return; - const { repoID, filePath } = this.props; - seafileAPI.addFileParticipant(repoID, filePath, email).then((res) => { - this.props.onParticipantsChange(repoID, filePath); - }).catch((err) => { - toaster.danger(Utils.getErrorMsg(err)); - }); + this.toBeAddedParticipant.push(email); } renderUserSuggestion = (entry, search, highlightedDisplay, index, focused) => { return
{highlightedDisplay}
; } - handleMention = (id, display) => { - const email = id.slice(id.lastIndexOf('/') + 1); - this.addParticipant(email); - } - render() { const { repoID, filePath, fileParticipantList } = this.props; return ( @@ -173,13 +176,14 @@ class DetailCommentList extends React.Component { > `@${display}`} data={this.state.relatedUsers} renderSuggestion={this.renderUserSuggestion} style={defaultMentionStyle} - onAdd={this.handleMention} + onAdd={(id, display) => {this.addParticipant(id);}} /> - + ); @@ -243,6 +247,15 @@ class CommentItem extends React.Component { this.setState({ newComment: event.target.value }); } + onCommentClick = (e) => { + // click participant link, page shouldn't jump + if (e.target.nodeName !== 'A') return; + const preNode = e.target.previousSibling; + if (preNode && preNode.nodeType === 3 && preNode.nodeValue.slice(-1) === '@') { + e.preventDefault(); + } + } + renderInfo = (item) => { return ( @@ -293,7 +306,11 @@ class CommentItem extends React.Component { -
+
this.onCommentClick(e)} + >
); } diff --git a/frontend/src/components/file-view/comment-panel.js b/frontend/src/components/file-view/comment-panel.js index 0d1f962c72..8b9cc5e4ef 100644 --- a/frontend/src/components/file-view/comment-panel.js +++ b/frontend/src/components/file-view/comment-panel.js @@ -8,7 +8,6 @@ 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'; @@ -33,6 +32,7 @@ class CommentPanel extends React.Component { relatedUsers: null, comment: '', }; + this.toBeAddedParticipant = []; } toggleResolvedComment = () => { @@ -53,7 +53,7 @@ class CommentPanel extends React.Component { listRepoRelatedUsers = () => { seafileAPI.listRepoRelatedUsers(repoID).then((res) => { let users = res.data.user_list.map((item) => { - return { id: (siteRoot + 'profile/' + item.email), display: item.name}; + return { id: item.email, display: item.name}; }); this.setState({ relatedUsers: users }); }); @@ -63,18 +63,29 @@ class CommentPanel extends React.Component { 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); - } + addComment = () => { + if (!this.state.comment.trim()) return; + seafileAPI.postComment(repoID, filePath, this.state.comment.trim()).then(() => { + this.listComments(); + }).catch(err => { + toaster.danger(Utils.getErrorMsg(err)); + }); this.setState({ comment: '' }); + } + + onSubmit = () => { + this.addParticipant(username); + if (this.toBeAddedParticipant.length === 0) { + this.addComment(); + } else { + seafileAPI.addFileParticipants(repoID, filePath, this.toBeAddedParticipant).then((res) => { + this.onParticipantsChange(repoID, filePath); + this.toBeAddedParticipant = []; + this.addComment(); + }).catch((err) => { + toaster.danger(Utils.getErrorMsg(err)); + }); + } } resolveComment = (event) => { @@ -128,23 +139,13 @@ class CommentPanel extends React.Component { 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); - }); + this.toBeAddedParticipant.push(email); } 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(); @@ -221,15 +222,14 @@ class CommentPanel extends React.Component { > `@${display}`} data={this.state.relatedUsers} renderSuggestion={this.renderUserSuggestion} style={defaultMentionStyle} - onAdd={this.handleMention} + onAdd={(id, display) => {this.addParticipant(id);}} /> - @@ -268,14 +268,10 @@ class CommentItem extends React.Component { } convertComment = (mdFile) => { - processor.process(mdFile).then( - (result) => { - let html = String(result); - this.setState({ - html: html - }); - } - ); + processor.process(mdFile).then((result) => { + let html = String(result); + this.setState({ html: html }); + }); } toggleEditComment = () => { @@ -298,6 +294,15 @@ class CommentItem extends React.Component { }); } + onCommentClick = (e) => { + // click participant link, page shouldn't jump + if (e.target.nodeName !== 'A') return; + const preNode = e.target.previousSibling; + if (preNode && preNode.nodeType === 3 && preNode.nodeValue.slice(-1) === '@') { + e.preventDefault(); + } + } + componentWillMount() { this.convertComment(this.props.item.comment); } @@ -324,7 +329,7 @@ class CommentItem extends React.Component {
{' '} - +
); @@ -344,24 +349,25 @@ class CommentItem extends React.Component { - { - (item.user_email === username) && + {(item.user_email === username) && {gettext('Delete')}} - { - (item.user_email === username) && - {gettext('Edit')} + {(item.user_email === username) && + {gettext('Edit')} } - { - !item.resolved && + {!item.resolved && {gettext('Mark as resolved')} } -
+
this.onCommentClick(e)} + >
); } diff --git a/frontend/src/components/file-view/participants-list.js b/frontend/src/components/file-view/participants-list.js index 164f042704..eb0e3b0c7f 100644 --- a/frontend/src/components/file-view/participants-list.js +++ b/frontend/src/components/file-view/participants-list.js @@ -22,6 +22,7 @@ class ParticipantsList extends React.Component { showDialog : false, tooltipOpen: false, }; + this.targetID = 'add-participant-icon'; } toggleDialog = () => { @@ -32,6 +33,10 @@ class ParticipantsList extends React.Component { this.setState({ tooltipOpen: !this.state.tooltipOpen }); } + componentWillMount() { + this.targetID = this.targetID + Math.floor(Math.random() * 1000); + } + render() { const { participants, repoID, filePath, showIconTip } = this.props; return ( @@ -39,11 +44,11 @@ class ParticipantsList extends React.Component { {participants.map((item, index) => { return ; })} - + {showIconTip && - + {gettext('Add participants')} } @@ -77,19 +82,23 @@ class Participant extends React.Component { this.state = { showAvatarTooltip: false, }; + this.targetID = 'participant-avatar-'; } toggleAvatarTooltip = () => { this.setState({ showAvatarTooltip: !this.state.showAvatarTooltip }); } + componentWillMount() { + this.targetID = this.targetID + this.props.index + Math.floor(Math.random() * 1000); + } + render() { const { item, index } = this.props; - const target = 'participant-avatar-' + index; return ( - avatar - + avatar + {item.name} diff --git a/frontend/src/css/react-mentions-default-style.js b/frontend/src/css/react-mentions-default-style.js index 1450b6b034..6761f07e07 100644 --- a/frontend/src/css/react-mentions-default-style.js +++ b/frontend/src/css/react-mentions-default-style.js @@ -26,20 +26,24 @@ const defaultStyle = { }, '&multiLine': { control: { - fontFamily: 'monospace', - border: '1px solid silver', }, highlighter: { padding: 9, }, input: { - padding: 5, + padding: '8px 6px', minHeight: 90, height: 90, - outline: 0, border: '1px solid #e6e6dd', - backgroundColor: '#fff', - overfflowY: 'auto' + borderRadius: '5px', + overfflowY: 'auto', + '&focused': { + backgroundColor: '#cee4e5', + outlineOffset: '-2px', + outlineColor: '-webkit-focus-ring-color', + outlineStyle: 'auto', + outlineWidth: '5px', + }, }, }, suggestions: { @@ -54,16 +58,18 @@ const defaultStyle = { }, item: { padding: '5px 15px', + minWidth: '50px', borderBottom: '1px solid rgba(0,0,0,0.15)', '&focused': { - backgroundColor: '#cee4e5', + backgroundColor: '#f19654', + color: '#fff', + fontWeight: '700', }, }, }, }; const defaultMentionStyle = { - backgroundColor: '#cee4e5' }; export { defaultStyle, defaultMentionStyle };