Use topLevelCommentId to simplify adding and deleting comments

This fixes the nested comment deletion problem: https://github.com/LLK/scratch-www/issues/2151
This commit is contained in:
Paul Kaplan 2018-10-05 13:08:51 -04:00
parent 744c90501e
commit 24fe4fef65
4 changed files with 56 additions and 48 deletions

View file

@ -87,6 +87,19 @@ module.exports.previewReducer = (state, action) => {
comments: [...state.comments, ...action.items] // TODO: consider a different way of doing this?
});
case 'SET_COMMENT_DELETED':
if (action.topLevelCommentId) {
return Object.assign({}, state, {
replies: Object.assign({}, state.replies, {
[action.topLevelCommentId]: state.replies[action.topLevelCommentId].map(comment => {
if (comment.id === action.commentId) {
return Object.assign({}, comment, {deleted: true});
}
return comment;
})
})
});
}
return Object.assign({}, state, {
comments: state.comments.map(comment => {
if (comment.id === action.commentId) {
@ -96,30 +109,13 @@ module.exports.previewReducer = (state, action) => {
})
});
case 'ADD_NEW_COMMENT':
if (action.comment.parent_id) {
let topLevelParent = action.comment.parent_id;
// If this is a nested reply, we need to look up which top level comment
// to put this new reply under.
if (!state.replies[topLevelParent]) {
Object.keys(state.replies).forEach(topLevelCommentId => {
state.replies[topLevelCommentId].forEach(reply => {
if (reply.id === action.comment.parent_id) {
topLevelParent = topLevelCommentId;
}
});
});
}
if (state.replies[topLevelParent]) {
const replies = JSON.parse(JSON.stringify(state.replies));
// Replies to comments go at the end of the thread
replies[topLevelParent] = replies[topLevelParent].concat(action.comment);
return Object.assign({}, state, {replies: replies});
}
log.error('Could not find top level parent to put reply in');
return state;
if (action.topLevelCommentId) {
return Object.assign({}, state, {
replies: Object.assign({}, state.replies, {
// Replies to comments go at the end of the thread
[action.topLevelCommentId]: state.replies[action.topLevelCommentId].concat(action.comment)
})
});
}
// Reply to the top level project, put the reply at the beginning
@ -232,14 +228,16 @@ module.exports.setStudioFetchStatus = (studioId, status) => ({
status: status
});
module.exports.setCommentDeleted = commentId => ({
module.exports.setCommentDeleted = (commentId, topLevelCommentId) => ({
type: 'SET_COMMENT_DELETED',
commentId: commentId
commentId: commentId,
topLevelCommentId: topLevelCommentId
});
module.exports.addNewComment = comment => ({
module.exports.addNewComment = (comment, topLevelCommentId) => ({
type: 'ADD_NEW_COMMENT',
comment: comment
comment: comment,
topLevelCommentId: topLevelCommentId
});
module.exports.getProjectInfo = (id, token) => (dispatch => {

View file

@ -15,6 +15,7 @@ class Comment extends React.Component {
super(props);
bindAll(this, [
'handleDelete',
'handlePostReply',
'handleToggleReplying'
]);
@ -33,6 +34,10 @@ class Comment extends React.Component {
this.setState({replying: !this.state.replying});
}
handleDelete () {
this.props.onDelete(this.props.id);
}
render () {
const {
author,
@ -41,7 +46,6 @@ class Comment extends React.Component {
canReply,
content,
datetimeCreated,
onDelete,
id,
projectId
} = this.props;
@ -64,7 +68,7 @@ class Comment extends React.Component {
{deletable ? (
<span
className="comment-delete"
onClick={onDelete}
onClick={this.handleDelete}
>
Delete {/* TODO internationalize */}
</span>

View file

@ -13,7 +13,8 @@ class TopLevelComment extends React.Component {
super(props);
bindAll(this, [
'handleExpandThread',
'handleDelete'
'handleAddComment',
'handleDeleteComment'
]);
this.state = {
expanded: false
@ -26,8 +27,14 @@ class TopLevelComment extends React.Component {
});
}
handleDelete () {
this.props.onDelete(this.props.id);
handleDeleteReply (commentId) {
// Only apply topLevelCommentId for deleting replies
// The top level comment itself just gets passed onDelete directly
this.props.onDelete(commentId, this.props.id);
}
handleAddComment (comment) {
this.props.onAddComment(comment, this.props.id);
}
render () {
@ -39,18 +46,17 @@ class TopLevelComment extends React.Component {
deletable,
deleted,
id,
onDelete,
replies,
projectId,
onAddComment
projectId
} = this.props;
return (
<FlexRow className="comment-container">
<Comment
projectId={projectId}
onAddComment={onAddComment}
onDelete={this.handleDelete}
{...{author, content, datetimeCreated, deletable, deleted, canReply, id}}
onAddComment={this.handleAddComment}
{...{author, content, datetimeCreated, deletable, deleted, canReply, id, onDelete}}
/>
{replies.length > 0 &&
<FlexRow
@ -72,8 +78,8 @@ class TopLevelComment extends React.Component {
id={reply.id}
key={reply.id}
projectId={projectId}
onAddComment={this.props.onAddComment}
onDelete={this.handleDelete}
onAddComment={this.handleAddComment}
onDelete={this.handleDeleteReply}
/>
))}
</FlexRow>

View file

@ -167,11 +167,11 @@ class Preview extends React.Component {
});
});
}
handleAddComment (comment) {
this.props.handleAddComment(comment);
handleAddComment (comment, topLevelCommentId) {
this.props.handleAddComment(comment, topLevelCommentId);
}
handleDeleteComment (id) {
this.props.handleDeleteComment(this.state.projectId, id, this.props.user.token);
handleDeleteComment (id, topLevelCommentId) {
this.props.handleDeleteComment(this.state.projectId, id, topLevelCommentId, this.props.user.token);
}
handleReportClick () {
this.setState({reportOpen: true});
@ -532,11 +532,11 @@ const mapStateToProps = state => {
};
const mapDispatchToProps = dispatch => ({
handleAddComment: comment => {
dispatch(previewActions.addNewComment(comment));
handleAddComment: (comment, topLevelCommentId) => {
dispatch(previewActions.addNewComment(comment, topLevelCommentId));
},
handleDeleteComment: (projectId, commentId, token) => {
dispatch(previewActions.deleteComment(projectId, commentId, token));
handleDeleteComment: (projectId, commentId, topLevelCommentId, token) => {
dispatch(previewActions.deleteComment(projectId, commentId, topLevelCommentId, token));
},
handleOpenRegistration: event => {
event.preventDefault();