mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-27 09:35:56 -05:00
Add restore functionality to comments for admins
This commit is contained in:
parent
b0ac4018ee
commit
592c0e5703
7 changed files with 87 additions and 16 deletions
|
@ -206,6 +206,7 @@
|
||||||
|
|
||||||
"comments.report": "Report",
|
"comments.report": "Report",
|
||||||
"comments.delete": "Delete",
|
"comments.delete": "Delete",
|
||||||
|
"comments.restore": "Restore",
|
||||||
"comments.reportModal.title": "Report Comment",
|
"comments.reportModal.title": "Report Comment",
|
||||||
"comments.reportModal.reported": "The comment has been reported, and the Scratch Team has been notified.",
|
"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.reportModal.prompt": "Are you sure you want to report this comment?",
|
||||||
|
|
|
@ -246,6 +246,15 @@ module.exports.setCommentReported = (commentId, topLevelCommentId) => ({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.exports.setCommentRestored = (commentId, topLevelCommentId) => ({
|
||||||
|
type: 'UPDATE_COMMENT',
|
||||||
|
commentId: commentId,
|
||||||
|
topLevelCommentId: topLevelCommentId,
|
||||||
|
comment: {
|
||||||
|
visibility: 'visible'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.exports.addNewComment = (comment, topLevelCommentId) => ({
|
module.exports.addNewComment = (comment, topLevelCommentId) => ({
|
||||||
type: 'ADD_NEW_COMMENT',
|
type: 'ADD_NEW_COMMENT',
|
||||||
comment: comment,
|
comment: comment,
|
||||||
|
@ -659,6 +668,22 @@ module.exports.reportComment = (projectId, commentId, topLevelCommentId, token)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.exports.restoreComment = (projectId, commentId, topLevelCommentId, token) => (dispatch => {
|
||||||
|
api({
|
||||||
|
uri: `/proxy/admin/project/${projectId}/comment/${commentId}/undelete`,
|
||||||
|
authentication: token,
|
||||||
|
withCredentials: true,
|
||||||
|
method: 'PUT',
|
||||||
|
useCsrf: true
|
||||||
|
}, (err, body, res) => {
|
||||||
|
if (err || res.statusCode !== 200) {
|
||||||
|
log.error(err || res.body);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatch(module.exports.setCommentRestored(commentId, topLevelCommentId));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
module.exports.reportProject = (id, jsonData, token) => (dispatch => {
|
module.exports.reportProject = (id, jsonData, token) => (dispatch => {
|
||||||
dispatch(module.exports.setFetchStatus('report', module.exports.Status.FETCHING));
|
dispatch(module.exports.setFetchStatus('report', module.exports.Status.FETCHING));
|
||||||
// scratchr2 will fail if no thumbnail base64 string provided. We don't yet have
|
// scratchr2 will fail if no thumbnail base64 string provided. We don't yet have
|
||||||
|
|
|
@ -13,10 +13,6 @@ const ReportCommentModal = require('../../../components/modal/comments/report-co
|
||||||
|
|
||||||
require('./comment.scss');
|
require('./comment.scss');
|
||||||
|
|
||||||
const CommentVisibility = {
|
|
||||||
VISIBLE: 'visible' // Has to match the server response for visibility
|
|
||||||
};
|
|
||||||
|
|
||||||
class Comment extends React.Component {
|
class Comment extends React.Component {
|
||||||
constructor (props) {
|
constructor (props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -28,7 +24,8 @@ class Comment extends React.Component {
|
||||||
'handleConfirmReport',
|
'handleConfirmReport',
|
||||||
'handleCancelReport',
|
'handleCancelReport',
|
||||||
'handlePostReply',
|
'handlePostReply',
|
||||||
'handleToggleReplying'
|
'handleToggleReplying',
|
||||||
|
'handleRestore'
|
||||||
]);
|
]);
|
||||||
this.state = {
|
this.state = {
|
||||||
deleting: false,
|
deleting: false,
|
||||||
|
@ -64,6 +61,10 @@ class Comment extends React.Component {
|
||||||
this.setState({reporting: true});
|
this.setState({reporting: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleRestore () {
|
||||||
|
this.props.onRestore(this.props.id);
|
||||||
|
}
|
||||||
|
|
||||||
handleConfirmReport () {
|
handleConfirmReport () {
|
||||||
this.setState({
|
this.setState({
|
||||||
reporting: false,
|
reporting: false,
|
||||||
|
@ -93,7 +94,7 @@ class Comment extends React.Component {
|
||||||
visibility
|
visibility
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const visible = visibility === CommentVisibility.VISIBLE;
|
const visible = visibility === 'visible';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -128,10 +129,19 @@ class Comment extends React.Component {
|
||||||
</span>
|
</span>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
) : (
|
) : (
|
||||||
|
<React.Fragment>
|
||||||
<span className="comment-visibility">
|
<span className="comment-visibility">
|
||||||
<FormattedMessage id={`comments.status.${visibility}`} />
|
<FormattedMessage id={`comments.status.${visibility}`} />
|
||||||
{/* TODO restore action will go here */}
|
|
||||||
</span>
|
</span>
|
||||||
|
{this.props.onRestore && (
|
||||||
|
<span
|
||||||
|
className="comment-restore"
|
||||||
|
onClick={this.handleRestore}
|
||||||
|
>
|
||||||
|
<FormattedMessage id="comments.restore" />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
|
@ -210,6 +220,7 @@ Comment.propTypes = {
|
||||||
onAddComment: PropTypes.func,
|
onAddComment: PropTypes.func,
|
||||||
onDelete: PropTypes.func,
|
onDelete: PropTypes.func,
|
||||||
onReport: PropTypes.func,
|
onReport: PropTypes.func,
|
||||||
|
onRestore: PropTypes.func,
|
||||||
projectId: PropTypes.string,
|
projectId: PropTypes.string,
|
||||||
visibility: PropTypes.string
|
visibility: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
|
@ -78,7 +78,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment-delete,
|
.comment-delete,
|
||||||
.comment-report {
|
.comment-report,
|
||||||
|
.comment-restore {
|
||||||
opacity: .5;
|
opacity: .5;
|
||||||
font-size: .75rem;
|
font-size: .75rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
@ -119,6 +120,18 @@
|
||||||
vertical-align: -.125rem;
|
vertical-align: -.125rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comment-restore {
|
||||||
|
margin-left: 1rem;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
margin-right: .25rem;
|
||||||
|
background-image: url("/svgs/project/restore-gray.svg");
|
||||||
|
width: .75rem;
|
||||||
|
height: .75rem;
|
||||||
|
vertical-align: -.125rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
|
|
|
@ -16,7 +16,8 @@ class TopLevelComment extends React.Component {
|
||||||
'handleExpandThread',
|
'handleExpandThread',
|
||||||
'handleAddComment',
|
'handleAddComment',
|
||||||
'handleDeleteReply',
|
'handleDeleteReply',
|
||||||
'handleReportReply'
|
'handleReportReply',
|
||||||
|
'handleRestoreReply'
|
||||||
]);
|
]);
|
||||||
this.state = {
|
this.state = {
|
||||||
expanded: false
|
expanded: false
|
||||||
|
@ -29,16 +30,20 @@ class TopLevelComment extends React.Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteReply (commentId) {
|
handleDeleteReply (replyId) {
|
||||||
// Only apply topLevelCommentId for deleting replies
|
// Only apply topLevelCommentId for deleting replies
|
||||||
// The top level comment itself just gets passed onDelete directly
|
// The top level comment itself just gets passed onDelete directly
|
||||||
this.props.onDelete(commentId, this.props.id);
|
this.props.onDelete(replyId, this.props.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReportReply (commentId) {
|
handleReportReply (replyId) {
|
||||||
// Only apply topLevelCommentId for reporting replies
|
// Only apply topLevelCommentId for reporting replies
|
||||||
// The top level comment itself just gets passed onReport directly
|
// The top level comment itself just gets passed onReport directly
|
||||||
this.props.onReport(commentId, this.props.id);
|
this.props.onReport(replyId, this.props.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRestoreReply (replyId) {
|
||||||
|
this.props.onRestore(replyId, this.props.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAddComment (comment) {
|
handleAddComment (comment) {
|
||||||
|
@ -55,6 +60,7 @@ class TopLevelComment extends React.Component {
|
||||||
id,
|
id,
|
||||||
onDelete,
|
onDelete,
|
||||||
onReport,
|
onReport,
|
||||||
|
onRestore,
|
||||||
replies,
|
replies,
|
||||||
projectId,
|
projectId,
|
||||||
visibility
|
visibility
|
||||||
|
@ -74,6 +80,7 @@ class TopLevelComment extends React.Component {
|
||||||
id,
|
id,
|
||||||
onDelete,
|
onDelete,
|
||||||
onReport,
|
onReport,
|
||||||
|
onRestore,
|
||||||
visibility
|
visibility
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -100,6 +107,7 @@ class TopLevelComment extends React.Component {
|
||||||
onAddComment={this.handleAddComment}
|
onAddComment={this.handleAddComment}
|
||||||
onDelete={this.handleDeleteReply}
|
onDelete={this.handleDeleteReply}
|
||||||
onReport={this.handleReportReply}
|
onReport={this.handleReportReply}
|
||||||
|
onRestore={this.handleRestoreReply}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</FlexRow>
|
</FlexRow>
|
||||||
|
@ -136,6 +144,7 @@ TopLevelComment.propTypes = {
|
||||||
onAddComment: PropTypes.func,
|
onAddComment: PropTypes.func,
|
||||||
onDelete: PropTypes.func,
|
onDelete: PropTypes.func,
|
||||||
onReport: PropTypes.func,
|
onReport: PropTypes.func,
|
||||||
|
onRestore: PropTypes.func,
|
||||||
parentId: PropTypes.number,
|
parentId: PropTypes.number,
|
||||||
projectId: PropTypes.string,
|
projectId: PropTypes.string,
|
||||||
replies: PropTypes.arrayOf(PropTypes.object),
|
replies: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
|
|
@ -78,6 +78,7 @@ const PreviewPresentation = ({
|
||||||
onReportClose,
|
onReportClose,
|
||||||
onReportComment,
|
onReportComment,
|
||||||
onReportSubmit,
|
onReportSubmit,
|
||||||
|
onRestoreComment,
|
||||||
onAddToStudioClicked,
|
onAddToStudioClicked,
|
||||||
onAddToStudioClosed,
|
onAddToStudioClosed,
|
||||||
onToggleStudio,
|
onToggleStudio,
|
||||||
|
@ -375,6 +376,7 @@ const PreviewPresentation = ({
|
||||||
onAddComment={onAddComment}
|
onAddComment={onAddComment}
|
||||||
onDelete={onDeleteComment}
|
onDelete={onDeleteComment}
|
||||||
onReport={onReportComment}
|
onReport={onReportComment}
|
||||||
|
onRestore={onRestoreComment}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{comments.length < projectInfo.stats.comments &&
|
{comments.length < projectInfo.stats.comments &&
|
||||||
|
@ -431,6 +433,7 @@ PreviewPresentation.propTypes = {
|
||||||
onReportClose: PropTypes.func.isRequired,
|
onReportClose: PropTypes.func.isRequired,
|
||||||
onReportComment: PropTypes.func.isRequired,
|
onReportComment: PropTypes.func.isRequired,
|
||||||
onReportSubmit: PropTypes.func.isRequired,
|
onReportSubmit: PropTypes.func.isRequired,
|
||||||
|
onRestoreComment: PropTypes.func,
|
||||||
onSeeInside: PropTypes.func,
|
onSeeInside: PropTypes.func,
|
||||||
onShare: PropTypes.func,
|
onShare: PropTypes.func,
|
||||||
onToggleComments: PropTypes.func,
|
onToggleComments: PropTypes.func,
|
||||||
|
|
|
@ -44,6 +44,7 @@ class Preview extends React.Component {
|
||||||
'handleReportClose',
|
'handleReportClose',
|
||||||
'handleReportComment',
|
'handleReportComment',
|
||||||
'handleReportSubmit',
|
'handleReportSubmit',
|
||||||
|
'handleRestoreComment',
|
||||||
'handleAddToStudioClick',
|
'handleAddToStudioClick',
|
||||||
'handleAddToStudioClose',
|
'handleAddToStudioClose',
|
||||||
'handleSeeInside',
|
'handleSeeInside',
|
||||||
|
@ -188,6 +189,9 @@ class Preview extends React.Component {
|
||||||
handleReportComment (id, topLevelCommentId) {
|
handleReportComment (id, topLevelCommentId) {
|
||||||
this.props.handleReportComment(this.state.projectId, id, topLevelCommentId, this.props.user.token);
|
this.props.handleReportComment(this.state.projectId, id, topLevelCommentId, this.props.user.token);
|
||||||
}
|
}
|
||||||
|
handleRestoreComment (id, topLevelCommentId) {
|
||||||
|
this.props.handleRestoreComment(this.state.projectId, id, topLevelCommentId, this.props.user.token);
|
||||||
|
}
|
||||||
handleReportClick () {
|
handleReportClick () {
|
||||||
this.setState({reportOpen: true});
|
this.setState({reportOpen: true});
|
||||||
}
|
}
|
||||||
|
@ -379,6 +383,7 @@ class Preview extends React.Component {
|
||||||
onReportClose={this.handleReportClose}
|
onReportClose={this.handleReportClose}
|
||||||
onReportComment={this.handleReportComment}
|
onReportComment={this.handleReportComment}
|
||||||
onReportSubmit={this.handleReportSubmit}
|
onReportSubmit={this.handleReportSubmit}
|
||||||
|
onRestoreComment={this.props.isAdmin && this.handleRestoreComment}
|
||||||
onSeeInside={this.handleSeeInside}
|
onSeeInside={this.handleSeeInside}
|
||||||
onShare={this.handleShare}
|
onShare={this.handleShare}
|
||||||
onToggleComments={this.handleToggleComments}
|
onToggleComments={this.handleToggleComments}
|
||||||
|
@ -448,6 +453,7 @@ Preview.propTypes = {
|
||||||
handleLogOut: PropTypes.func,
|
handleLogOut: PropTypes.func,
|
||||||
handleOpenRegistration: PropTypes.func,
|
handleOpenRegistration: PropTypes.func,
|
||||||
handleReportComment: PropTypes.func,
|
handleReportComment: PropTypes.func,
|
||||||
|
handleRestoreComment: PropTypes.func,
|
||||||
handleToggleLoginOpen: PropTypes.func,
|
handleToggleLoginOpen: PropTypes.func,
|
||||||
isAdmin: PropTypes.bool,
|
isAdmin: PropTypes.bool,
|
||||||
isEditable: PropTypes.bool,
|
isEditable: PropTypes.bool,
|
||||||
|
@ -588,6 +594,9 @@ const mapDispatchToProps = dispatch => ({
|
||||||
handleReportComment: (projectId, commentId, topLevelCommentId, token) => {
|
handleReportComment: (projectId, commentId, topLevelCommentId, token) => {
|
||||||
dispatch(previewActions.reportComment(projectId, commentId, topLevelCommentId, token));
|
dispatch(previewActions.reportComment(projectId, commentId, topLevelCommentId, token));
|
||||||
},
|
},
|
||||||
|
handleRestoreComment: (projectId, commentId, topLevelCommentId, token) => {
|
||||||
|
dispatch(previewActions.restoreComment(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