From 24fe4fef65d87700aeca111847dbecbd387372cb Mon Sep 17 00:00:00 2001 From: Paul Kaplan Date: Fri, 5 Oct 2018 13:08:51 -0400 Subject: [PATCH] Use topLevelCommentId to simplify adding and deleting comments This fixes the nested comment deletion problem: https://github.com/LLK/scratch-www/issues/2151 --- src/redux/preview.js | 54 +++++++++---------- src/views/preview/comment/comment.jsx | 8 ++- .../preview/comment/top-level-comment.jsx | 26 +++++---- src/views/preview/preview.jsx | 16 +++--- 4 files changed, 56 insertions(+), 48 deletions(-) diff --git a/src/redux/preview.js b/src/redux/preview.js index e115cdd3c..a151fe41c 100644 --- a/src/redux/preview.js +++ b/src/redux/preview.js @@ -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 => { diff --git a/src/views/preview/comment/comment.jsx b/src/views/preview/comment/comment.jsx index 25b0a9f94..378a76b24 100644 --- a/src/views/preview/comment/comment.jsx +++ b/src/views/preview/comment/comment.jsx @@ -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 ? ( Delete {/* TODO internationalize */} diff --git a/src/views/preview/comment/top-level-comment.jsx b/src/views/preview/comment/top-level-comment.jsx index 77a2bed28..1f0899970 100644 --- a/src/views/preview/comment/top-level-comment.jsx +++ b/src/views/preview/comment/top-level-comment.jsx @@ -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 ( {replies.length > 0 && ))} diff --git a/src/views/preview/preview.jsx b/src/views/preview/preview.jsx index a4ea7d051..76ed08b89 100644 --- a/src/views/preview/preview.jsx +++ b/src/views/preview/preview.jsx @@ -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();