Add comment deleting for project owners

This commit is contained in:
Paul Kaplan 2018-10-03 13:21:36 -04:00
parent 9b94849de6
commit 24b456873b
6 changed files with 101 additions and 7 deletions

View file

@ -86,6 +86,15 @@ module.exports.previewReducer = (state, action) => {
return Object.assign({}, state, {
comments: [...state.comments, ...action.items] // TODO: consider a different way of doing this?
});
case 'SET_COMMENT_DELETED':
return Object.assign({}, state, {
comments: state.comments.map(comment => {
if (comment.id === action.commentId) {
return Object.assign({}, comment, {deleted: true});
}
return comment;
})
});
case 'SET_REPLIES':
return Object.assign({}, state, {
replies: merge({}, state.replies, action.replies)
@ -191,6 +200,11 @@ module.exports.setStudioFetchStatus = (studioId, status) => ({
status: status
});
module.exports.setCommentDeleted = commentId => ({
type: 'SET_COMMENT_DELETED',
commentId: commentId
});
module.exports.getProjectInfo = (id, token) => (dispatch => {
const opts = {
uri: `/projects/${id}`
@ -562,6 +576,26 @@ module.exports.updateProject = (id, jsonData, username, token) => (dispatch => {
});
});
module.exports.deleteComment = (projectId, commentId, token) => (dispatch => {
/* TODO fetching/fetched/error states updates for comment deleting */
api({
uri: `/proxy/comments/project/${projectId}`,
authentication: token,
withCredentials: true,
method: 'DELETE',
useCsrf: true,
json: {
id: commentId
}
}, (err, body, res) => {
if (err || res.statusCode !== 200) {
log.error(err || res.body);
return;
}
dispatch(module.exports.setCommentDeleted(commentId));
});
});
module.exports.reportProject = (id, jsonData) => (dispatch => {
dispatch(module.exports.setFetchStatus('report', module.exports.Status.FETCHING));
// scratchr2 will fail if no thumbnail base64 string provided. We don't yet have

View file

@ -1,5 +1,6 @@
const React = require('react');
const PropTypes = require('prop-types');
const classNames = require('classnames');
const FlexRow = require('../../../components/flex-row/flex-row.jsx');
const Avatar = require('../../../components/avatar/avatar.jsx');
@ -9,8 +10,11 @@ require('./comment.scss');
const Comment = ({
author,
deletable,
deleted,
content,
datetimeCreated,
onDelete,
id
}) => (
<div
@ -27,12 +31,25 @@ const Comment = ({
href={`/users/${author.username}`}
>{author.username}</a>
<div className="action-list">
{/* TODO: Hook these up to API calls/logic */}
<span className="comment-delete">Delete</span>
<span className="comment-report">Report</span>
{deletable ? (
<span
className="comment-delete"
onClick={onDelete}
>
Delete {/* TODO internationalize */}
</span>
) : null}
<span className="comment-report">
Report {/* TODO internationalize */}
</span>
</div>
</FlexRow>
<div className="comment-bubble">
<div
className={classNames({
'comment-bubble': true,
'comment-bubble-deleted': deleted
})}
>
{/* TODO: at the moment, comment content does not properly display
* emojis/easter eggs
* @user links in replies
@ -63,7 +80,10 @@ Comment.propTypes = {
}),
content: PropTypes.string,
datetimeCreated: PropTypes.string,
id: PropTypes.number
deletable: PropTypes.bool,
deleted: PropTypes.bool,
id: PropTypes.number,
onDelete: PropTypes.func
};
module.exports = Comment;

View file

@ -131,6 +131,16 @@
height: 9px;
content: "";
}
&.comment-bubble-deleted {
border-color: #FF6680;
background-color: rgb(236, 206, 223);
&:before {
border-color: #FF6680 transparent #FF6680 #FF6680;
background: rgb(236, 206, 223);
}
}
}
.comment-content {

View file

@ -12,7 +12,8 @@ class TopLevelComment extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleExpandThread'
'handleExpandThread',
'handleDelete'
]);
this.state = {
expanded: false
@ -25,18 +26,27 @@ class TopLevelComment extends React.Component {
});
}
handleDelete () {
this.props.onDelete(this.props.id);
}
render () {
const {
author,
content,
datetimeCreated,
deletable,
deleted,
id,
replies
} = this.props;
return (
<FlexRow className="comment-container">
<Comment {...{author, content, datetimeCreated, id}} />
<Comment
onDelete={this.handleDelete}
{...{author, content, datetimeCreated, deletable, deleted, id}}
/>
{replies.length > 0 &&
<FlexRow
className={classNames(
@ -51,8 +61,11 @@ class TopLevelComment extends React.Component {
author={reply.author}
content={reply.content}
datetimeCreated={reply.datetime_created}
deletable={deletable}
deleted={reply.deleted}
id={reply.id}
key={reply.id}
onDelete={this.handleDelete}
/>
))}
</FlexRow>
@ -76,7 +89,10 @@ TopLevelComment.propTypes = {
}),
content: PropTypes.string,
datetimeCreated: PropTypes.string,
deletable: PropTypes.bool,
deleted: PropTypes.bool,
id: PropTypes.number,
onDelete: PropTypes.func,
parentId: PropTypes.number,
projectId: PropTypes.string,
replies: PropTypes.arrayOf(PropTypes.object)

View file

@ -64,6 +64,7 @@ const PreviewPresentation = ({
projectStudios,
studios,
userOwnsProject,
onDeleteComment,
onFavoriteClicked,
onLoadMore,
onLoveClicked,
@ -316,11 +317,14 @@ const PreviewPresentation = ({
author={comment.author}
content={comment.content}
datetimeCreated={comment.datetime_created}
deletable={userOwnsProject}
deleted={comment.deleted}
id={comment.id}
key={comment.id}
parentId={comment.parent_id}
projectId={projectId}
replies={replies && replies[comment.id] ? replies[comment.id] : []}
onDelete={onDeleteComment}
/>
))}
{comments.length < projectInfo.stats.comments &&
@ -365,6 +369,7 @@ PreviewPresentation.propTypes = {
loved: PropTypes.bool,
onAddToStudioClicked: PropTypes.func,
onAddToStudioClosed: PropTypes.func,
onDeleteComment: PropTypes.func,
onFavoriteClicked: PropTypes.func,
onLoadMore: PropTypes.func,
onLoveClicked: PropTypes.func,

View file

@ -33,6 +33,7 @@ class Preview extends React.Component {
super(props);
bindAll(this, [
'addEventListeners',
'handleDeleteComment',
'handleToggleStudio',
'handleFavoriteToggle',
'handleLoadMore',
@ -164,6 +165,9 @@ class Preview extends React.Component {
});
});
}
handleDeleteComment (id) {
this.props.handleDeleteComment(this.state.projectId, id, this.props.user.token);
}
handleReportClick () {
this.setState({reportOpen: true});
}
@ -338,6 +342,7 @@ class Preview extends React.Component {
userOwnsProject={this.props.userOwnsProject}
onAddToStudioClicked={this.handleAddToStudioClick}
onAddToStudioClosed={this.handleAddToStudioClose}
onDeleteComment={this.handleDeleteComment}
onFavoriteClicked={this.handleFavoriteToggle}
onLoadMore={this.handleLoadMore}
onLoveClicked={this.handleLoveToggle}
@ -393,6 +398,7 @@ Preview.propTypes = {
getProjectStudios: PropTypes.func.isRequired,
getRemixes: PropTypes.func.isRequired,
getTopLevelComments: PropTypes.func.isRequired,
handleDeleteComment: PropTypes.func,
handleLogIn: PropTypes.func,
handleLogOut: PropTypes.func,
handleOpenRegistration: PropTypes.func,
@ -519,6 +525,9 @@ const mapStateToProps = state => {
};
const mapDispatchToProps = dispatch => ({
handleDeleteComment: (projectId, commentId, token) => {
dispatch(previewActions.deleteComment(projectId, commentId, token));
},
handleOpenRegistration: event => {
event.preventDefault();
dispatch(navigationActions.setRegistrationOpen(true));