mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-13 05:39:59 +00:00
Add participant @ (#3907)
* add related users add react mention * detail panel @ participant * update seafile api * change catch error
This commit is contained in:
61
frontend/package-lock.json
generated
61
frontend/package-lock.json
generated
@@ -14883,6 +14883,11 @@
|
|||||||
"prop-types": "^15.5.8"
|
"prop-types": "^15.5.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-is": {
|
||||||
|
"version": "16.8.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
|
||||||
|
"integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA=="
|
||||||
|
},
|
||||||
"react-is-deprecated": {
|
"react-is-deprecated": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/react-is-deprecated/-/react-is-deprecated-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/react-is-deprecated/-/react-is-deprecated-0.1.2.tgz",
|
||||||
@@ -14893,6 +14898,27 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||||
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
||||||
},
|
},
|
||||||
|
"react-mentions": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-mentions/-/react-mentions-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-xwyNEBV45lC2inf1Eqf/EdkX5GiVUP25/7m7pfIpfm1mV8iGFOpWJUBX0mFH83PDsmMBjTQIqsZ5XHKzYfwQWg==",
|
||||||
|
"requires": {
|
||||||
|
"invariant": "^2.2.4",
|
||||||
|
"lodash": "^4.5.1",
|
||||||
|
"prop-types": "^15.5.8",
|
||||||
|
"substyle": "^6.3.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"invariant": {
|
||||||
|
"version": "2.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
||||||
|
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
|
||||||
|
"requires": {
|
||||||
|
"loose-envify": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-modal": {
|
"react-modal": {
|
||||||
"version": "3.8.1",
|
"version": "3.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.8.1.tgz",
|
||||||
@@ -16001,9 +16027,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"seafile-js": {
|
"seafile-js": {
|
||||||
"version": "0.2.107",
|
"version": "0.2.108",
|
||||||
"resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.107.tgz",
|
"resolved": "https://registry.npmjs.org/seafile-js/-/seafile-js-0.2.108.tgz",
|
||||||
"integrity": "sha512-Mv2JIs0dw7n+cXk532dKiPU1IenpwX8a7p+xs6evXJw63CX2dnYFc9xZUl27GGB4/uQ6V2UbEw7S+EqD0wfxSA==",
|
"integrity": "sha512-uGBqFfGxywsf4z+I4UX8Prz4xJe46Gvy/aCRed3751B0fCUG3+OSiKtJXnZH9TN3lOVp4sJpm7MkER1Bf1ln9A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"axios": "^0.18.0",
|
"axios": "^0.18.0",
|
||||||
"form-data": "^2.3.2",
|
"form-data": "^2.3.2",
|
||||||
@@ -16965,6 +16991,35 @@
|
|||||||
"resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz",
|
||||||
"integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw=="
|
"integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw=="
|
||||||
},
|
},
|
||||||
|
"substyle": {
|
||||||
|
"version": "6.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/substyle/-/substyle-6.3.1.tgz",
|
||||||
|
"integrity": "sha512-S25YRgVQB25cLXgGAJ7AXTYewkIB/9Fa1Y7jxkN48U4N6rbK9YhVEiF7vtPGLlJl8ebSOJ3N4t8cd6jL8MH7Uw==",
|
||||||
|
"requires": {
|
||||||
|
"hoist-non-react-statics": "^3.1.0",
|
||||||
|
"invariant": "^2.2.0",
|
||||||
|
"prop-types": "^15.5.8",
|
||||||
|
"warning": "^2.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"hoist-non-react-statics": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==",
|
||||||
|
"requires": {
|
||||||
|
"react-is": "^16.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warning": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/warning/-/warning-2.1.0.tgz",
|
||||||
|
"integrity": "sha1-ISINnGOvx3qMkhEeARr3Bc4MaQE=",
|
||||||
|
"requires": {
|
||||||
|
"loose-envify": "^1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"supports-color": {
|
"supports-color": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz",
|
||||||
|
@@ -33,11 +33,12 @@
|
|||||||
"react-cookies": "^0.1.0",
|
"react-cookies": "^0.1.0",
|
||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.8.6",
|
||||||
"react-image-lightbox": "^5.1.0",
|
"react-image-lightbox": "^5.1.0",
|
||||||
|
"react-mentions": "^3.0.2",
|
||||||
"react-moment": "^0.7.9",
|
"react-moment": "^0.7.9",
|
||||||
"react-responsive": "^6.1.2",
|
"react-responsive": "^6.1.2",
|
||||||
"react-select": "^2.4.1",
|
"react-select": "^2.4.1",
|
||||||
"reactstrap": "^6.4.0",
|
"reactstrap": "^6.4.0",
|
||||||
"seafile-js": "^0.2.107",
|
"seafile-js": "^0.2.108",
|
||||||
"socket.io-client": "^2.2.0",
|
"socket.io-client": "^2.2.0",
|
||||||
"sw-precache-webpack-plugin": "0.11.4",
|
"sw-precache-webpack-plugin": "0.11.4",
|
||||||
"unified": "^7.0.0",
|
"unified": "^7.0.0",
|
||||||
|
@@ -3,11 +3,13 @@ import PropTypes from 'prop-types';
|
|||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { processor } from '@seafile/seafile-editor/dist/utils/seafile-markdown2html';
|
import { processor } from '@seafile/seafile-editor/dist/utils/seafile-markdown2html';
|
||||||
import { Button, Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
|
import { Button, Dropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
|
||||||
import { gettext, username } from '../../utils/constants';
|
import { gettext, username, siteRoot } from '../../utils/constants';
|
||||||
import { seafileAPI } from '../../utils/seafile-api';
|
import { seafileAPI } from '../../utils/seafile-api';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
import toaster from '../toast';
|
import toaster from '../toast';
|
||||||
import ParticipantsList from '../file-view/participants-list';
|
import ParticipantsList from '../file-view/participants-list';
|
||||||
|
import { MentionsInput, Mention } from 'react-mentions';
|
||||||
|
import { defaultStyle, defaultMentionStyle } from '../../css/react-mentions-default-style';
|
||||||
import '../../css/comments-list.css';
|
import '../../css/comments-list.css';
|
||||||
|
|
||||||
const DetailCommentListPropTypes = {
|
const DetailCommentListPropTypes = {
|
||||||
@@ -23,13 +25,15 @@ class DetailCommentList extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
commentsList: [],
|
commentsList: [],
|
||||||
|
relatedUsers: null,
|
||||||
|
comment: '',
|
||||||
};
|
};
|
||||||
this.isParticipant = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.listComments();
|
this.listComments();
|
||||||
this.checkParticipant();
|
this.checkParticipant();
|
||||||
|
this.listRepoRelatedUsers();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
@@ -38,15 +42,8 @@ class DetailCommentList extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkParticipant = () => {
|
handleCommentChange = (event) => {
|
||||||
const fileParticipantList = this.props.fileParticipantList;
|
this.setState({ comment: event.target.value });
|
||||||
if (fileParticipantList.length === 0) {
|
|
||||||
this.isParticipant = false;
|
|
||||||
} else {
|
|
||||||
this.isParticipant = fileParticipantList.some((participant) => {
|
|
||||||
return participant.email === username;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
listComments = (filePath) => {
|
listComments = (filePath) => {
|
||||||
@@ -58,12 +55,8 @@ class DetailCommentList extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommentChange = (event) => {
|
|
||||||
this.setState({ comment: event.target.value });
|
|
||||||
}
|
|
||||||
|
|
||||||
submitComment = () => {
|
submitComment = () => {
|
||||||
let comment = this.refs.commentTextarea.value;
|
let comment = this.state.comment;
|
||||||
const { repoID, filePath } = this.props;
|
const { repoID, filePath } = this.props;
|
||||||
if (comment.trim()) {
|
if (comment.trim()) {
|
||||||
seafileAPI.postComment(repoID, filePath, comment.trim()).then(() => {
|
seafileAPI.postComment(repoID, filePath, comment.trim()).then(() => {
|
||||||
@@ -72,21 +65,9 @@ class DetailCommentList extends React.Component {
|
|||||||
let errMessage = Utils.getErrorMsg(error);
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
toaster.danger(errMessage);
|
toaster.danger(errMessage);
|
||||||
});
|
});
|
||||||
this.addParticipant();
|
this.addParticipant(username);
|
||||||
}
|
}
|
||||||
this.refs.commentTextarea.value = '';
|
this.setState({ comment: '' });
|
||||||
}
|
|
||||||
|
|
||||||
addParticipant = () => {
|
|
||||||
const { repoID, filePath } = this.props;
|
|
||||||
if (this.isParticipant) return;
|
|
||||||
seafileAPI.addFileParticipant(repoID, filePath, username).then((res) => {
|
|
||||||
this.isParticipant = true;
|
|
||||||
this.props.onParticipantsChange(repoID, filePath);
|
|
||||||
}).catch((err) => {
|
|
||||||
let errMessage = Utils.getErrorMsg(err);
|
|
||||||
toaster.danger(errMessage);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveComment = (event) => {
|
resolveComment = (event) => {
|
||||||
@@ -119,6 +100,39 @@ class DetailCommentList extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listRepoRelatedUsers = () => {
|
||||||
|
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};
|
||||||
|
});
|
||||||
|
this.setState({ relatedUsers: users });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
checkParticipant = (email) => {
|
||||||
|
return this.props.fileParticipantList.map((participant) => {return participant.email;}).includes(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderUserSuggestion = (entry, search, highlightedDisplay, index, focused) => {
|
||||||
|
return <div className={`user ${focused ? 'focused' : ''}`}>{highlightedDisplay}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMention = (id, display) => {
|
||||||
|
const email = id.slice(id.lastIndexOf('/') + 1);
|
||||||
|
this.addParticipant(email);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { repoID, filePath, fileParticipantList } = this.props;
|
const { repoID, filePath, fileParticipantList } = this.props;
|
||||||
return (
|
return (
|
||||||
@@ -151,10 +165,20 @@ class DetailCommentList extends React.Component {
|
|||||||
showIconTip={true}
|
showIconTip={true}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
<textarea
|
<MentionsInput
|
||||||
className="add-comment-input" ref="commentTextarea" placeholder={gettext('Add a comment.')}
|
value={this.state.comment}
|
||||||
clos="100" rows="3" warp="virtual"
|
onChange={this.handleCommentChange}
|
||||||
></textarea>
|
placeholder={gettext('Add a comment.')}
|
||||||
|
style={defaultStyle}
|
||||||
|
>
|
||||||
|
<Mention
|
||||||
|
trigger="@"
|
||||||
|
data={this.state.relatedUsers}
|
||||||
|
renderSuggestion={this.renderUserSuggestion}
|
||||||
|
style={defaultMentionStyle}
|
||||||
|
onAdd={this.handleMention}
|
||||||
|
/>
|
||||||
|
</MentionsInput>
|
||||||
<Button className="submit-comment" color="primary" size="sm" onClick={this.submitComment}>{gettext('Submit')}</Button>
|
<Button className="submit-comment" color="primary" size="sm" onClick={this.submitComment}>{gettext('Submit')}</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -8,6 +8,9 @@ import { seafileAPI } from '../../utils/seafile-api';
|
|||||||
import ParticipantsList from './participants-list';
|
import ParticipantsList from './participants-list';
|
||||||
import { Utils } from '../../utils/utils';
|
import { Utils } from '../../utils/utils';
|
||||||
import toaster from '../toast';
|
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';
|
import '../../css/comments-list.css';
|
||||||
|
|
||||||
const { username, repoID, filePath } = window.app.pageOptions;
|
const { username, repoID, filePath } = window.app.pageOptions;
|
||||||
@@ -27,14 +30,13 @@ class CommentPanel extends React.Component {
|
|||||||
commentsList: [],
|
commentsList: [],
|
||||||
showResolvedComment: true,
|
showResolvedComment: true,
|
||||||
participants: null,
|
participants: null,
|
||||||
|
relatedUsers: null,
|
||||||
|
comment: '',
|
||||||
};
|
};
|
||||||
this.isParticipant = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleResolvedComment = () => {
|
toggleResolvedComment = () => {
|
||||||
this.setState({
|
this.setState({ showResolvedComment: !this.state.showResolvedComment });
|
||||||
showResolvedComment: !this.state.showResolvedComment
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
listComments = () => {
|
listComments = () => {
|
||||||
@@ -48,14 +50,21 @@ class CommentPanel extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommentChange = (event) => {
|
listRepoRelatedUsers = () => {
|
||||||
this.setState({
|
seafileAPI.listRepoRelatedUsers(repoID).then((res) => {
|
||||||
comment: event.target.value,
|
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 = () => {
|
submitComment = () => {
|
||||||
let comment = this.refs.commentTextarea.value;
|
let comment = this.state.comment;
|
||||||
if (comment.trim()) {
|
if (comment.trim()) {
|
||||||
seafileAPI.postComment(repoID, filePath, comment).then(() => {
|
seafileAPI.postComment(repoID, filePath, comment).then(() => {
|
||||||
this.listComments();
|
this.listComments();
|
||||||
@@ -63,20 +72,9 @@ class CommentPanel extends React.Component {
|
|||||||
let errMessage = Utils.getErrorMsg(error);
|
let errMessage = Utils.getErrorMsg(error);
|
||||||
toaster.danger(errMessage);
|
toaster.danger(errMessage);
|
||||||
});
|
});
|
||||||
this.addParticipant();
|
this.addParticipant(username);
|
||||||
}
|
}
|
||||||
this.refs.commentTextarea.value = '';
|
this.setState({ comment: '' });
|
||||||
}
|
|
||||||
|
|
||||||
addParticipant = () => {
|
|
||||||
if (this.isParticipant) return;
|
|
||||||
seafileAPI.addFileParticipant(repoID, filePath, username).then((res) => {
|
|
||||||
this.isParticipant = true;
|
|
||||||
this.onParticipantsChange(repoID, filePath);
|
|
||||||
}).catch((err) => {
|
|
||||||
let errMessage = Utils.getErrorMsg(err);
|
|
||||||
toaster.danger(errMessage);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveComment = (event) => {
|
resolveComment = (event) => {
|
||||||
@@ -116,32 +114,41 @@ class CommentPanel extends React.Component {
|
|||||||
|
|
||||||
getParticipants = () => {
|
getParticipants = () => {
|
||||||
if (this.props.participants) {
|
if (this.props.participants) {
|
||||||
this.setState({ participants: this.props.participants }, () => {
|
this.setState({ participants: this.props.participants });
|
||||||
this.checkParticipant();
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
seafileAPI.listFileParticipants(repoID, filePath).then((res) => {
|
seafileAPI.listFileParticipants(repoID, filePath).then((res) => {
|
||||||
this.setState({ participants: res.data.participant_list }, () => {
|
this.setState({ participants: res.data.participant_list });
|
||||||
this.checkParticipant();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkParticipant = () => {
|
checkParticipant = (email) => {
|
||||||
const participants = this.state.participants;
|
return this.state.participants.map((participant) => {return participant.email;}).includes(email);
|
||||||
if (participants.length === 0) {
|
}
|
||||||
this.isParticipant = false;
|
|
||||||
} else {
|
addParticipant = (email) => {
|
||||||
this.isParticipant = participants.some((participant) => {
|
if (this.checkParticipant(email)) return;
|
||||||
return participant.email === username;
|
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 <div className={`user ${focused ? 'focused' : ''}`}>{highlightedDisplay}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMention = (id, display) => {
|
||||||
|
const email = id.slice(id.lastIndexOf('/') + 1);
|
||||||
|
this.addParticipant(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.listComments();
|
this.listComments();
|
||||||
this.getParticipants();
|
this.getParticipants();
|
||||||
|
this.listRepoRelatedUsers();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
@@ -154,18 +161,18 @@ class CommentPanel extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { participants } = this.state;
|
const { participants, commentsList } = this.state;
|
||||||
return (
|
return (
|
||||||
<div className="seafile-comment">
|
<div className="seafile-comment">
|
||||||
<div className="seafile-comment-title">
|
<div className="seafile-comment-title">
|
||||||
<div onClick={this.props.toggleCommentPanel} className={'seafile-comment-title-close'}>
|
<div onClick={this.props.toggleCommentPanel} className="seafile-comment-title-close">
|
||||||
<i className={'fa fa-times-circle'}/>
|
<i className="fa fa-times-circle"/>
|
||||||
</div>
|
</div>
|
||||||
<div className={'seafile-comment-title-text'}>{gettext('Comments')}</div>
|
<div className="seafile-comment-title-text">{gettext('Comments')}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="seafile-comment-toggle-resolved">
|
<div className="seafile-comment-toggle-resolved">
|
||||||
<div className={'seafile-comment-title-text'}>{gettext('Show resolved comments')}</div>
|
<div className="seafile-comment-title-text">{gettext('Show resolved comments')}</div>
|
||||||
<div className={'seafile-comment-title-toggle d-flex'}>
|
<div className="seafile-comment-title-toggle d-flex">
|
||||||
<label className="custom-switch" id="toggle-resolved-comments">
|
<label className="custom-switch" id="toggle-resolved-comments">
|
||||||
<input type="checkbox" name="option" className="custom-switch-input"
|
<input type="checkbox" name="option" className="custom-switch-input"
|
||||||
onChange={this.toggleResolvedComment}
|
onChange={this.toggleResolvedComment}
|
||||||
@@ -175,9 +182,9 @@ class CommentPanel extends React.Component {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul className={'seafile-comment-list'}>
|
<ul className="seafile-comment-list">
|
||||||
{this.state.commentsList.length > 0 &&
|
{commentsList.length > 0 &&
|
||||||
this.state.commentsList.map((item, index = 0, arr) => {
|
commentsList.map((item, index = 0, arr) => {
|
||||||
let oldTime = (new Date(item.created_at)).getTime();
|
let oldTime = (new Date(item.created_at)).getTime();
|
||||||
let time = moment(oldTime).format('YYYY-MM-DD HH:mm');
|
let time = moment(oldTime).format('YYYY-MM-DD HH:mm');
|
||||||
return (
|
return (
|
||||||
@@ -193,7 +200,7 @@ class CommentPanel extends React.Component {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
{(this.state.commentsList.length == 0 ) &&
|
{commentsList.length == 0 &&
|
||||||
<li className="comment-vacant">{gettext('No comment yet.')}</li>}
|
<li className="comment-vacant">{gettext('No comment yet.')}</li>}
|
||||||
</ul>
|
</ul>
|
||||||
<div className="seafile-comment-footer">
|
<div className="seafile-comment-footer">
|
||||||
@@ -206,10 +213,20 @@ class CommentPanel extends React.Component {
|
|||||||
showIconTip={true}
|
showIconTip={true}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
<textarea
|
<MentionsInput
|
||||||
className="add-comment-input" ref="commentTextarea"
|
value={this.state.comment}
|
||||||
|
onChange={this.handleCommentChange}
|
||||||
placeholder={gettext('Add a comment.')}
|
placeholder={gettext('Add a comment.')}
|
||||||
clos="100" rows="3" warp="virtual"></textarea>
|
style={defaultStyle}
|
||||||
|
>
|
||||||
|
<Mention
|
||||||
|
trigger="@"
|
||||||
|
data={this.state.relatedUsers}
|
||||||
|
renderSuggestion={this.renderUserSuggestion}
|
||||||
|
style={defaultMentionStyle}
|
||||||
|
onAdd={this.handleMention}
|
||||||
|
/>
|
||||||
|
</MentionsInput>
|
||||||
<Button
|
<Button
|
||||||
className="submit-comment" color="primary"
|
className="submit-comment" color="primary"
|
||||||
size="sm" onClick={this.submitComment} >
|
size="sm" onClick={this.submitComment} >
|
||||||
|
@@ -111,7 +111,6 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 182px;
|
min-height: 182px;
|
||||||
}
|
}
|
||||||
.seafile-comment-footer .add-comment-input,
|
|
||||||
.seafile-edit-comment .edit-comment-input {
|
.seafile-edit-comment .edit-comment-input {
|
||||||
border: 1px solid #e6e6dd;
|
border: 1px solid #e6e6dd;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
@@ -142,6 +141,6 @@
|
|||||||
.detail-comments .seafile-comment-footer {
|
.detail-comments .seafile-comment-footer {
|
||||||
min-height: 175px;
|
min-height: 175px;
|
||||||
}
|
}
|
||||||
.detail-comments .seafile-comment-footer .add-comment-input, .detail-comments .seafile-edit-comment .edit-comment-input {
|
.detail-comments .seafile-edit-comment .edit-comment-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
/* participants-list */
|
/* participants-list */
|
||||||
.participants {
|
.participants {
|
||||||
min-height: 30px;
|
min-height: 20px;
|
||||||
}
|
}
|
||||||
.participants .avatar {
|
.participants .avatar {
|
||||||
width: 28px;
|
width: 28px;
|
||||||
|
69
frontend/src/css/react-mentions-default-style.js
vendored
Normal file
69
frontend/src/css/react-mentions-default-style.js
vendored
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
const defaultStyle = {
|
||||||
|
control: {
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: 'normal',
|
||||||
|
},
|
||||||
|
highlighter: {
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
margin: 0,
|
||||||
|
},
|
||||||
|
'&singleLine': {
|
||||||
|
control: {
|
||||||
|
display: 'inline-block',
|
||||||
|
width: 130,
|
||||||
|
},
|
||||||
|
highlighter: {
|
||||||
|
padding: 1,
|
||||||
|
border: '2px inset transparent',
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
padding: 1,
|
||||||
|
border: '2px inset',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'&multiLine': {
|
||||||
|
control: {
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
border: '1px solid silver',
|
||||||
|
},
|
||||||
|
highlighter: {
|
||||||
|
padding: 9,
|
||||||
|
},
|
||||||
|
input: {
|
||||||
|
padding: 5,
|
||||||
|
minHeight: 90,
|
||||||
|
height: 90,
|
||||||
|
outline: 0,
|
||||||
|
border: '1px solid #e6e6dd',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
overfflowY: 'auto'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
suggestions: {
|
||||||
|
list: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
border: '1px solid rgba(0,0,0,0.15)',
|
||||||
|
fontSize: 14,
|
||||||
|
maxHeight: 200,
|
||||||
|
overflow: 'auto',
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 14,
|
||||||
|
},
|
||||||
|
item: {
|
||||||
|
padding: '5px 15px',
|
||||||
|
borderBottom: '1px solid rgba(0,0,0,0.15)',
|
||||||
|
'&focused': {
|
||||||
|
backgroundColor: '#cee4e5',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultMentionStyle = {
|
||||||
|
backgroundColor: '#cee4e5'
|
||||||
|
};
|
||||||
|
|
||||||
|
export { defaultStyle, defaultMentionStyle };
|
Reference in New Issue
Block a user