mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-30 10:58:23 -05:00
Merge pull request #2164 from paulkaplan/comment-report-delete
Comment report action and modal flow for deleting
This commit is contained in:
commit
1f8342b987
11 changed files with 385 additions and 21 deletions
81
src/components/modal/comments/delete-comment.jsx
Normal file
81
src/components/modal/comments/delete-comment.jsx
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||||
|
const injectIntl = require('react-intl').injectIntl;
|
||||||
|
const intlShape = require('react-intl').intlShape;
|
||||||
|
const Modal = require('../base/modal.jsx');
|
||||||
|
|
||||||
|
const Button = require('../../forms/button.jsx');
|
||||||
|
const FlexRow = require('../../flex-row/flex-row.jsx');
|
||||||
|
|
||||||
|
require('../../forms/button.scss');
|
||||||
|
require('./modal.scss');
|
||||||
|
|
||||||
|
const DeleteModal = ({
|
||||||
|
intl,
|
||||||
|
onDelete,
|
||||||
|
onReport,
|
||||||
|
onRequestClose,
|
||||||
|
...modalProps
|
||||||
|
}) => (
|
||||||
|
<Modal
|
||||||
|
useStandardSizes
|
||||||
|
className="mod-report"
|
||||||
|
contentLabel={intl.formatMessage({id: 'comments.deleteModal.title'})}
|
||||||
|
onRequestClose={onRequestClose}
|
||||||
|
{...modalProps}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div className="report-modal-header">
|
||||||
|
<div className="report-content-label">
|
||||||
|
<FormattedMessage id="comments.deleteModal.title" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="report-modal-content">
|
||||||
|
<div>
|
||||||
|
<div className="instructions">
|
||||||
|
<FormattedMessage id="comments.deleteModal.body" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<FlexRow className="action-buttons">
|
||||||
|
<div className="action-buttons-overflow-fix">
|
||||||
|
<Button
|
||||||
|
className="action-button submit-button"
|
||||||
|
type="button"
|
||||||
|
onClick={onRequestClose}
|
||||||
|
>
|
||||||
|
<div className="action-button-text">
|
||||||
|
<FormattedMessage id="general.close" />
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="action-button submit-button"
|
||||||
|
type="button"
|
||||||
|
onClick={onReport}
|
||||||
|
>
|
||||||
|
<FormattedMessage id="comments.report" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="action-button submit-button"
|
||||||
|
type="button"
|
||||||
|
onClick={onDelete}
|
||||||
|
>
|
||||||
|
<FormattedMessage id="comments.delete" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</FlexRow>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
DeleteModal.propTypes = {
|
||||||
|
intl: intlShape,
|
||||||
|
onDelete: PropTypes.func,
|
||||||
|
onReport: PropTypes.func,
|
||||||
|
onRequestClose: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = injectIntl(DeleteModal);
|
44
src/components/modal/comments/modal.scss
Normal file
44
src/components/modal/comments/modal.scss
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
@import "../../../colors";
|
||||||
|
@import "../../../frameless";
|
||||||
|
|
||||||
|
$medium-and-small: "screen and (max-width : #{$tablet}-1)";
|
||||||
|
|
||||||
|
.mod-report * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mod-report {
|
||||||
|
margin: 100px auto;
|
||||||
|
outline: none;
|
||||||
|
padding: 0;
|
||||||
|
width: 36.25rem; /* 580px; */
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-modal-header {
|
||||||
|
border-radius: 1rem 1rem 0 0;
|
||||||
|
box-shadow: inset 0 -1px 0 0 $ui-coral-dark;
|
||||||
|
background-color: $ui-coral;
|
||||||
|
padding-top: .75rem;
|
||||||
|
width: 100%;
|
||||||
|
height: 3rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-content-label {
|
||||||
|
text-align: center;
|
||||||
|
color: $type-white;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-modal-content {
|
||||||
|
margin: 1rem auto;
|
||||||
|
width: 80%;
|
||||||
|
font-size: .875rem;
|
||||||
|
|
||||||
|
.instructions {
|
||||||
|
line-height: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
84
src/components/modal/comments/report-comment.jsx
Normal file
84
src/components/modal/comments/report-comment.jsx
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
const PropTypes = require('prop-types');
|
||||||
|
const React = require('react');
|
||||||
|
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||||
|
const injectIntl = require('react-intl').injectIntl;
|
||||||
|
const intlShape = require('react-intl').intlShape;
|
||||||
|
const Modal = require('../base/modal.jsx');
|
||||||
|
|
||||||
|
const Button = require('../../forms/button.jsx');
|
||||||
|
const FlexRow = require('../../flex-row/flex-row.jsx');
|
||||||
|
|
||||||
|
require('../../forms/button.scss');
|
||||||
|
require('./modal.scss');
|
||||||
|
|
||||||
|
const ReportModal = ({
|
||||||
|
intl,
|
||||||
|
isConfirmed,
|
||||||
|
onReport,
|
||||||
|
onRequestClose,
|
||||||
|
...modalProps
|
||||||
|
}) => (
|
||||||
|
<Modal
|
||||||
|
useStandardSizes
|
||||||
|
className="mod-report"
|
||||||
|
contentLabel={intl.formatMessage({id: 'comments.reportModal.title'})}
|
||||||
|
onRequestClose={onRequestClose}
|
||||||
|
{...modalProps}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div className="report-modal-header">
|
||||||
|
<div className="report-content-label">
|
||||||
|
<FormattedMessage id="comments.reportModal.title" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="report-modal-content">
|
||||||
|
<div>
|
||||||
|
<div className="instructions">
|
||||||
|
{isConfirmed ? (
|
||||||
|
<FormattedMessage id="comments.reportModal.reported" />
|
||||||
|
) : (
|
||||||
|
<FormattedMessage id="comments.reportModal.prompt" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<FlexRow className="action-buttons">
|
||||||
|
<div className="action-buttons-overflow-fix">
|
||||||
|
<Button
|
||||||
|
className="action-button submit-button"
|
||||||
|
type="button"
|
||||||
|
onClick={onRequestClose}
|
||||||
|
>
|
||||||
|
<div className="action-button-text">
|
||||||
|
<FormattedMessage id="general.close" />
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
{isConfirmed ? null : (
|
||||||
|
<Button
|
||||||
|
className="action-button submit-button"
|
||||||
|
type="button"
|
||||||
|
onClick={onReport}
|
||||||
|
>
|
||||||
|
<div className="action-button-text">
|
||||||
|
<FormattedMessage id="comments.report" />
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</FlexRow>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
ReportModal.propTypes = {
|
||||||
|
intl: intlShape,
|
||||||
|
isConfirmed: PropTypes.bool,
|
||||||
|
isOwnSpace: PropTypes.bool,
|
||||||
|
onReport: PropTypes.func,
|
||||||
|
onRequestClose: PropTypes.func,
|
||||||
|
type: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = injectIntl(ReportModal);
|
|
@ -202,5 +202,14 @@
|
||||||
"report.tooShortError": "That's too short. Please describe in detail what's inappropriate or disrespectful about the project.",
|
"report.tooShortError": "That's too short. Please describe in detail what's inappropriate or disrespectful about the project.",
|
||||||
"report.send": "Send",
|
"report.send": "Send",
|
||||||
"report.sending": "Sending...",
|
"report.sending": "Sending...",
|
||||||
"report.textMissing": "Please tell us why you are reporting this project"
|
"report.textMissing": "Please tell us why you are reporting this project",
|
||||||
|
|
||||||
|
"comments.report": "Report",
|
||||||
|
"comments.delete": "Delete",
|
||||||
|
"comments.reportModal.title": "Report Comment",
|
||||||
|
"comments.reportModal.reported": "The comment has been reported, and the Scratch Team has been notified.",
|
||||||
|
"comments.reportModal.prompt": "Are you sure you want to report this comment?",
|
||||||
|
"comments.deleteModal.title": "Delete Comment",
|
||||||
|
"comments.deleteModal.body": "Delete this comment? If the comment is mean or disrespectful, please click Report instead to let the Scratch Team know about it.",
|
||||||
|
"comments.reply": "reply"
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,13 +86,13 @@ module.exports.previewReducer = (state, action) => {
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
comments: [...state.comments, ...action.items] // TODO: consider a different way of doing this?
|
comments: [...state.comments, ...action.items] // TODO: consider a different way of doing this?
|
||||||
});
|
});
|
||||||
case 'SET_COMMENT_DELETED':
|
case 'UPDATE_COMMENT':
|
||||||
if (action.topLevelCommentId) {
|
if (action.topLevelCommentId) {
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
replies: Object.assign({}, state.replies, {
|
replies: Object.assign({}, state.replies, {
|
||||||
[action.topLevelCommentId]: state.replies[action.topLevelCommentId].map(comment => {
|
[action.topLevelCommentId]: state.replies[action.topLevelCommentId].map(comment => {
|
||||||
if (comment.id === action.commentId) {
|
if (comment.id === action.commentId) {
|
||||||
return Object.assign({}, comment, {deleted: true});
|
return Object.assign({}, comment, action.comment);
|
||||||
}
|
}
|
||||||
return comment;
|
return comment;
|
||||||
})
|
})
|
||||||
|
@ -103,7 +103,7 @@ module.exports.previewReducer = (state, action) => {
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
comments: state.comments.map(comment => {
|
comments: state.comments.map(comment => {
|
||||||
if (comment.id === action.commentId) {
|
if (comment.id === action.commentId) {
|
||||||
return Object.assign({}, comment, {deleted: true});
|
return Object.assign({}, comment, action.comment);
|
||||||
}
|
}
|
||||||
return comment;
|
return comment;
|
||||||
})
|
})
|
||||||
|
@ -229,9 +229,21 @@ module.exports.setStudioFetchStatus = (studioId, status) => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports.setCommentDeleted = (commentId, topLevelCommentId) => ({
|
module.exports.setCommentDeleted = (commentId, topLevelCommentId) => ({
|
||||||
type: 'SET_COMMENT_DELETED',
|
type: 'UPDATE_COMMENT',
|
||||||
commentId: commentId,
|
commentId: commentId,
|
||||||
topLevelCommentId: topLevelCommentId
|
topLevelCommentId: topLevelCommentId,
|
||||||
|
comment: {
|
||||||
|
deleted: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.setCommentReported = (commentId, topLevelCommentId) => ({
|
||||||
|
type: 'UPDATE_COMMENT',
|
||||||
|
commentId: commentId,
|
||||||
|
topLevelCommentId: topLevelCommentId,
|
||||||
|
comment: {
|
||||||
|
reported: true
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports.addNewComment = (comment, topLevelCommentId) => ({
|
module.exports.addNewComment = (comment, topLevelCommentId) => ({
|
||||||
|
@ -611,7 +623,7 @@ module.exports.updateProject = (id, jsonData, username, token) => (dispatch => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports.deleteComment = (projectId, commentId, token) => (dispatch => {
|
module.exports.deleteComment = (projectId, commentId, topLevelCommentId, token) => (dispatch => {
|
||||||
/* TODO fetching/fetched/error states updates for comment deleting */
|
/* TODO fetching/fetched/error states updates for comment deleting */
|
||||||
api({
|
api({
|
||||||
uri: `/proxy/comments/project/${projectId}`,
|
uri: `/proxy/comments/project/${projectId}`,
|
||||||
|
@ -627,7 +639,24 @@ module.exports.deleteComment = (projectId, commentId, token) => (dispatch => {
|
||||||
log.error(err || res.body);
|
log.error(err || res.body);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dispatch(module.exports.setCommentDeleted(commentId));
|
dispatch(module.exports.setCommentDeleted(commentId, topLevelCommentId));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.reportComment = (projectId, commentId, topLevelCommentId, token) => (dispatch => {
|
||||||
|
api({
|
||||||
|
uri: `/proxy/project/${projectId}/comment/${commentId}/report`,
|
||||||
|
authentication: token,
|
||||||
|
withCredentials: true,
|
||||||
|
method: 'POST',
|
||||||
|
useCsrf: true
|
||||||
|
}, (err, body, res) => {
|
||||||
|
if (err || res.statusCode !== 200) {
|
||||||
|
log.error(err || res.body);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO use the reportId in the response for unreporting functionality
|
||||||
|
dispatch(module.exports.setCommentReported(commentId, topLevelCommentId));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,10 @@ const classNames = require('classnames');
|
||||||
const FlexRow = require('../../../components/flex-row/flex-row.jsx');
|
const FlexRow = require('../../../components/flex-row/flex-row.jsx');
|
||||||
const Avatar = require('../../../components/avatar/avatar.jsx');
|
const Avatar = require('../../../components/avatar/avatar.jsx');
|
||||||
const FormattedRelative = require('react-intl').FormattedRelative;
|
const FormattedRelative = require('react-intl').FormattedRelative;
|
||||||
|
const FormattedMessage = require('react-intl').FormattedMessage;
|
||||||
const ComposeComment = require('./compose-comment.jsx');
|
const ComposeComment = require('./compose-comment.jsx');
|
||||||
|
const DeleteCommentModal = require('../../../components/modal/comments/delete-comment.jsx');
|
||||||
|
const ReportCommentModal = require('../../../components/modal/comments/report-comment.jsx');
|
||||||
|
|
||||||
require('./comment.scss');
|
require('./comment.scss');
|
||||||
|
|
||||||
|
@ -15,10 +18,18 @@ class Comment extends React.Component {
|
||||||
super(props);
|
super(props);
|
||||||
bindAll(this, [
|
bindAll(this, [
|
||||||
'handleDelete',
|
'handleDelete',
|
||||||
|
'handleCancelDelete',
|
||||||
|
'handleConfirmDelete',
|
||||||
|
'handleReport',
|
||||||
|
'handleConfirmReport',
|
||||||
|
'handleCancelReport',
|
||||||
'handlePostReply',
|
'handlePostReply',
|
||||||
'handleToggleReplying'
|
'handleToggleReplying'
|
||||||
]);
|
]);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
deleting: false,
|
||||||
|
reporting: false,
|
||||||
|
reportConfirmed: false,
|
||||||
replying: false
|
replying: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -33,9 +44,39 @@ class Comment extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDelete () {
|
handleDelete () {
|
||||||
|
this.setState({deleting: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConfirmDelete () {
|
||||||
|
this.setState({deleting: false});
|
||||||
this.props.onDelete(this.props.id);
|
this.props.onDelete(this.props.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleCancelDelete () {
|
||||||
|
this.setState({deleting: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleReport () {
|
||||||
|
this.setState({reporting: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConfirmReport () {
|
||||||
|
this.setState({
|
||||||
|
reporting: false,
|
||||||
|
reportConfirmed: true,
|
||||||
|
deleting: false // To close delete modal if reported from delete modal
|
||||||
|
});
|
||||||
|
|
||||||
|
this.props.onReport(this.props.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCancelReport () {
|
||||||
|
this.setState({
|
||||||
|
reporting: false,
|
||||||
|
reportConfirmed: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const {
|
const {
|
||||||
author,
|
author,
|
||||||
|
@ -45,7 +86,8 @@ class Comment extends React.Component {
|
||||||
content,
|
content,
|
||||||
datetimeCreated,
|
datetimeCreated,
|
||||||
id,
|
id,
|
||||||
projectId
|
projectId,
|
||||||
|
reported
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -68,18 +110,22 @@ class Comment extends React.Component {
|
||||||
className="comment-delete"
|
className="comment-delete"
|
||||||
onClick={this.handleDelete}
|
onClick={this.handleDelete}
|
||||||
>
|
>
|
||||||
Delete {/* TODO internationalize */}
|
<FormattedMessage id="comments.delete" />
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
<span className="comment-report">
|
<span
|
||||||
Report {/* TODO internationalize */}
|
className="comment-report"
|
||||||
|
onClick={this.handleReport}
|
||||||
|
>
|
||||||
|
<FormattedMessage id="comments.report" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
<div
|
<div
|
||||||
className={classNames({
|
className={classNames({
|
||||||
'comment-bubble': true,
|
'comment-bubble': true,
|
||||||
'comment-bubble-deleted': deleted
|
'comment-bubble-deleted': deleted,
|
||||||
|
'comment-bubble-reported': reported
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{/* TODO: at the moment, comment content does not properly display
|
{/* TODO: at the moment, comment content does not properly display
|
||||||
|
@ -97,11 +143,12 @@ class Comment extends React.Component {
|
||||||
className="comment-reply"
|
className="comment-reply"
|
||||||
onClick={this.handleToggleReplying}
|
onClick={this.handleToggleReplying}
|
||||||
>
|
>
|
||||||
reply
|
<FormattedMessage id="comments.reply" />
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{this.state.replying ? (
|
{this.state.replying ? (
|
||||||
<FlexRow className="comment-reply-row">
|
<FlexRow className="comment-reply-row">
|
||||||
<ComposeComment
|
<ComposeComment
|
||||||
|
@ -113,6 +160,24 @@ class Comment extends React.Component {
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
) : null}
|
) : null}
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
|
{this.state.deleting ? (
|
||||||
|
<DeleteCommentModal
|
||||||
|
isOpen
|
||||||
|
key="delete-comment-modal"
|
||||||
|
onDelete={this.handleConfirmDelete}
|
||||||
|
onReport={this.handleConfirmReport}
|
||||||
|
onRequestClose={this.handleCancelDelete}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{(this.state.reporting || this.state.reportConfirmed) ? (
|
||||||
|
<ReportCommentModal
|
||||||
|
isOpen
|
||||||
|
isConfirmed={this.state.reportConfirmed}
|
||||||
|
key="report-comment-modal"
|
||||||
|
onReport={this.handleConfirmReport}
|
||||||
|
onRequestClose={this.handleCancelReport}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -132,7 +197,9 @@ Comment.propTypes = {
|
||||||
id: PropTypes.number,
|
id: PropTypes.number,
|
||||||
onAddComment: PropTypes.func,
|
onAddComment: PropTypes.func,
|
||||||
onDelete: PropTypes.func,
|
onDelete: PropTypes.func,
|
||||||
projectId: PropTypes.number
|
onReport: PropTypes.func,
|
||||||
|
projectId: PropTypes.string,
|
||||||
|
reported: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Comment;
|
module.exports = Comment;
|
||||||
|
|
|
@ -151,8 +151,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.comment-bubble-deleted {
|
&.comment-bubble-deleted {
|
||||||
$deleted-outline: #ff6680;
|
$deleted-outline: $active-gray;
|
||||||
$deleted-background: rgb(236, 206, 223);
|
$deleted-background: rgb(215, 222, 234);
|
||||||
|
|
||||||
border-color: $deleted-outline;
|
border-color: $deleted-outline;
|
||||||
background-color: $deleted-background;
|
background-color: $deleted-background;
|
||||||
|
@ -162,6 +162,19 @@
|
||||||
background: $deleted-background;
|
background: $deleted-background;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.comment-bubble-reported {
|
||||||
|
$reported-outline: #ff6680;
|
||||||
|
$reported-background: rgb(236, 206, 223);
|
||||||
|
|
||||||
|
border-color: $reported-outline;
|
||||||
|
background-color: $reported-background;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
border-color: $reported-outline transparent $reported-outline $reported-outline;
|
||||||
|
background: $reported-background;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-content {
|
.comment-content {
|
||||||
|
|
|
@ -183,7 +183,7 @@ ComposeComment.propTypes = {
|
||||||
onAddComment: PropTypes.func,
|
onAddComment: PropTypes.func,
|
||||||
onCancel: PropTypes.func,
|
onCancel: PropTypes.func,
|
||||||
parentId: PropTypes.number,
|
parentId: PropTypes.number,
|
||||||
projectId: PropTypes.number,
|
projectId: PropTypes.string,
|
||||||
user: PropTypes.shape({
|
user: PropTypes.shape({
|
||||||
id: PropTypes.number,
|
id: PropTypes.number,
|
||||||
username: PropTypes.string,
|
username: PropTypes.string,
|
||||||
|
|
|
@ -14,7 +14,8 @@ class TopLevelComment extends React.Component {
|
||||||
bindAll(this, [
|
bindAll(this, [
|
||||||
'handleExpandThread',
|
'handleExpandThread',
|
||||||
'handleAddComment',
|
'handleAddComment',
|
||||||
'handleDeleteReply'
|
'handleDeleteReply',
|
||||||
|
'handleReportReply'
|
||||||
]);
|
]);
|
||||||
this.state = {
|
this.state = {
|
||||||
expanded: false
|
expanded: false
|
||||||
|
@ -33,6 +34,12 @@ class TopLevelComment extends React.Component {
|
||||||
this.props.onDelete(commentId, this.props.id);
|
this.props.onDelete(commentId, this.props.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleReportReply (commentId) {
|
||||||
|
// Only apply topLevelCommentId for reporting replies
|
||||||
|
// The top level comment itself just gets passed onReport directly
|
||||||
|
this.props.onReport(commentId, this.props.id);
|
||||||
|
}
|
||||||
|
|
||||||
handleAddComment (comment) {
|
handleAddComment (comment) {
|
||||||
this.props.onAddComment(comment, this.props.id);
|
this.props.onAddComment(comment, this.props.id);
|
||||||
}
|
}
|
||||||
|
@ -47,7 +54,9 @@ class TopLevelComment extends React.Component {
|
||||||
deleted,
|
deleted,
|
||||||
id,
|
id,
|
||||||
onDelete,
|
onDelete,
|
||||||
|
onReport,
|
||||||
replies,
|
replies,
|
||||||
|
reported,
|
||||||
projectId
|
projectId
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
@ -56,7 +65,18 @@ class TopLevelComment extends React.Component {
|
||||||
<Comment
|
<Comment
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
onAddComment={this.handleAddComment}
|
onAddComment={this.handleAddComment}
|
||||||
{...{author, content, datetimeCreated, deletable, deleted, canReply, id, onDelete}}
|
{...{
|
||||||
|
author,
|
||||||
|
content,
|
||||||
|
datetimeCreated,
|
||||||
|
deletable,
|
||||||
|
deleted,
|
||||||
|
canReply,
|
||||||
|
id,
|
||||||
|
onDelete,
|
||||||
|
onReport,
|
||||||
|
reported
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{replies.length > 0 &&
|
{replies.length > 0 &&
|
||||||
<FlexRow
|
<FlexRow
|
||||||
|
@ -78,8 +98,10 @@ class TopLevelComment extends React.Component {
|
||||||
id={reply.id}
|
id={reply.id}
|
||||||
key={reply.id}
|
key={reply.id}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
|
reported={reply.reported}
|
||||||
onAddComment={this.handleAddComment}
|
onAddComment={this.handleAddComment}
|
||||||
onDelete={this.handleDeleteReply}
|
onDelete={this.handleDeleteReply}
|
||||||
|
onReport={this.handleReportReply}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
|
@ -109,9 +131,11 @@ TopLevelComment.propTypes = {
|
||||||
id: PropTypes.number,
|
id: PropTypes.number,
|
||||||
onAddComment: PropTypes.func,
|
onAddComment: PropTypes.func,
|
||||||
onDelete: PropTypes.func,
|
onDelete: PropTypes.func,
|
||||||
|
onReport: PropTypes.func,
|
||||||
parentId: PropTypes.number,
|
parentId: PropTypes.number,
|
||||||
projectId: PropTypes.string,
|
projectId: PropTypes.string,
|
||||||
replies: PropTypes.arrayOf(PropTypes.object)
|
replies: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
reported: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = TopLevelComment;
|
module.exports = TopLevelComment;
|
||||||
|
|
|
@ -74,6 +74,7 @@ const PreviewPresentation = ({
|
||||||
onLoveClicked,
|
onLoveClicked,
|
||||||
onReportClicked,
|
onReportClicked,
|
||||||
onReportClose,
|
onReportClose,
|
||||||
|
onReportComment,
|
||||||
onReportSubmit,
|
onReportSubmit,
|
||||||
onAddToStudioClicked,
|
onAddToStudioClicked,
|
||||||
onAddToStudioClosed,
|
onAddToStudioClosed,
|
||||||
|
@ -367,8 +368,10 @@ const PreviewPresentation = ({
|
||||||
parentId={comment.parent_id}
|
parentId={comment.parent_id}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
replies={replies && replies[comment.id] ? replies[comment.id] : []}
|
replies={replies && replies[comment.id] ? replies[comment.id] : []}
|
||||||
|
reported={comment.reported}
|
||||||
onAddComment={onAddComment}
|
onAddComment={onAddComment}
|
||||||
onDelete={onDeleteComment}
|
onDelete={onDeleteComment}
|
||||||
|
onReport={onReportComment}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{comments.length < projectInfo.stats.comments &&
|
{comments.length < projectInfo.stats.comments &&
|
||||||
|
@ -421,6 +424,7 @@ PreviewPresentation.propTypes = {
|
||||||
onLoveClicked: PropTypes.func,
|
onLoveClicked: PropTypes.func,
|
||||||
onReportClicked: PropTypes.func.isRequired,
|
onReportClicked: PropTypes.func.isRequired,
|
||||||
onReportClose: PropTypes.func.isRequired,
|
onReportClose: PropTypes.func.isRequired,
|
||||||
|
onReportComment: PropTypes.func.isRequired,
|
||||||
onReportSubmit: PropTypes.func.isRequired,
|
onReportSubmit: PropTypes.func.isRequired,
|
||||||
onSeeInside: PropTypes.func,
|
onSeeInside: PropTypes.func,
|
||||||
onToggleComments: PropTypes.func,
|
onToggleComments: PropTypes.func,
|
||||||
|
|
|
@ -42,6 +42,7 @@ class Preview extends React.Component {
|
||||||
'handlePopState',
|
'handlePopState',
|
||||||
'handleReportClick',
|
'handleReportClick',
|
||||||
'handleReportClose',
|
'handleReportClose',
|
||||||
|
'handleReportComment',
|
||||||
'handleReportSubmit',
|
'handleReportSubmit',
|
||||||
'handleAddToStudioClick',
|
'handleAddToStudioClick',
|
||||||
'handleAddToStudioClose',
|
'handleAddToStudioClose',
|
||||||
|
@ -182,6 +183,9 @@ class Preview extends React.Component {
|
||||||
handleDeleteComment (id, topLevelCommentId) {
|
handleDeleteComment (id, topLevelCommentId) {
|
||||||
this.props.handleDeleteComment(this.state.projectId, id, topLevelCommentId, this.props.user.token);
|
this.props.handleDeleteComment(this.state.projectId, id, topLevelCommentId, this.props.user.token);
|
||||||
}
|
}
|
||||||
|
handleReportComment (id, topLevelCommentId) {
|
||||||
|
this.props.handleReportComment(this.state.projectId, id, topLevelCommentId, this.props.user.token);
|
||||||
|
}
|
||||||
handleReportClick () {
|
handleReportClick () {
|
||||||
this.setState({reportOpen: true});
|
this.setState({reportOpen: true});
|
||||||
}
|
}
|
||||||
|
@ -363,6 +367,7 @@ class Preview extends React.Component {
|
||||||
onLoveClicked={this.handleLoveToggle}
|
onLoveClicked={this.handleLoveToggle}
|
||||||
onReportClicked={this.handleReportClick}
|
onReportClicked={this.handleReportClick}
|
||||||
onReportClose={this.handleReportClose}
|
onReportClose={this.handleReportClose}
|
||||||
|
onReportComment={this.handleReportComment}
|
||||||
onReportSubmit={this.handleReportSubmit}
|
onReportSubmit={this.handleReportSubmit}
|
||||||
onSeeInside={this.handleSeeInside}
|
onSeeInside={this.handleSeeInside}
|
||||||
onToggleComments={this.handleToggleComments}
|
onToggleComments={this.handleToggleComments}
|
||||||
|
@ -419,6 +424,7 @@ Preview.propTypes = {
|
||||||
handleLogIn: PropTypes.func,
|
handleLogIn: PropTypes.func,
|
||||||
handleLogOut: PropTypes.func,
|
handleLogOut: PropTypes.func,
|
||||||
handleOpenRegistration: PropTypes.func,
|
handleOpenRegistration: PropTypes.func,
|
||||||
|
handleReportComment: PropTypes.func,
|
||||||
handleToggleLoginOpen: PropTypes.func,
|
handleToggleLoginOpen: PropTypes.func,
|
||||||
isEditable: PropTypes.bool,
|
isEditable: PropTypes.bool,
|
||||||
isLoggedIn: PropTypes.bool,
|
isLoggedIn: PropTypes.bool,
|
||||||
|
@ -548,6 +554,9 @@ const mapDispatchToProps = dispatch => ({
|
||||||
handleDeleteComment: (projectId, commentId, topLevelCommentId, token) => {
|
handleDeleteComment: (projectId, commentId, topLevelCommentId, token) => {
|
||||||
dispatch(previewActions.deleteComment(projectId, commentId, topLevelCommentId, token));
|
dispatch(previewActions.deleteComment(projectId, commentId, topLevelCommentId, token));
|
||||||
},
|
},
|
||||||
|
handleReportComment: (projectId, commentId, topLevelCommentId, token) => {
|
||||||
|
dispatch(previewActions.reportComment(projectId, commentId, topLevelCommentId, token));
|
||||||
|
},
|
||||||
handleOpenRegistration: event => {
|
handleOpenRegistration: event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
dispatch(navigationActions.setRegistrationOpen(true));
|
dispatch(navigationActions.setRegistrationOpen(true));
|
||||||
|
|
Loading…
Reference in a new issue