decidim-comments 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/assets/javascripts/decidim/comments/bundle.js +28 -23
  4. data/app/assets/javascripts/decidim/comments/bundle.js.map +1 -1
  5. data/app/commands/decidim/comments/create_comment.rb +10 -0
  6. data/app/forms/decidim/comments/comment_form.rb +1 -1
  7. data/app/frontend/comments/add_comment_form.component.jsx +171 -87
  8. data/app/frontend/comments/add_comment_form.component.test.jsx +41 -22
  9. data/app/frontend/comments/add_comment_form.mutation.graphql +5 -3
  10. data/app/frontend/comments/add_comment_form_commentable.fragment.graphql +4 -0
  11. data/app/frontend/comments/add_comment_form_session.fragment.graphql +6 -0
  12. data/app/frontend/comments/comment.component.jsx +18 -18
  13. data/app/frontend/comments/comment.component.test.jsx +22 -21
  14. data/app/frontend/comments/comment.fragment.graphql +4 -4
  15. data/app/frontend/comments/comment_data.fragment.graphql +4 -3
  16. data/app/frontend/comments/comment_order_selector.component.jsx +3 -3
  17. data/app/frontend/comments/comment_thread.component.jsx +8 -3
  18. data/app/frontend/comments/comment_thread.component.test.jsx +3 -3
  19. data/app/frontend/comments/comment_thread.fragment.graphql +1 -1
  20. data/app/frontend/comments/comments.component.jsx +52 -33
  21. data/app/frontend/comments/comments.component.test.jsx +51 -38
  22. data/app/frontend/comments/comments.query.graphql +10 -4
  23. data/app/frontend/comments/down_vote_button.component.jsx +6 -3
  24. data/app/frontend/comments/up_vote_button.component.jsx +7 -4
  25. data/app/frontend/comments/vote_button.component.jsx +5 -0
  26. data/app/frontend/comments/vote_button_component.test.jsx +1 -1
  27. data/app/frontend/entry.test.js +2 -0
  28. data/app/frontend/support/generate_comments_data.js +4 -4
  29. data/app/mailers/decidim/comments/comment_notification_mailer.rb +31 -0
  30. data/app/models/decidim/comments/comment.rb +13 -9
  31. data/app/queries/decidim/comments/{comments_with_replies.rb → sorted_comments.rb} +3 -8
  32. data/app/types/decidim/comments/commentable_interface.rb +44 -0
  33. data/app/types/decidim/comments/commentable_mutation_type.rb +29 -0
  34. data/app/types/decidim/comments/commentable_type.rb +14 -0
  35. data/app/views/decidim/comments/comment_notification_mailer/comment_created.html.erb +18 -0
  36. data/app/views/decidim/comments/comment_notification_mailer/reply_created.html.erb +18 -0
  37. data/config/locales/ca.yml +20 -4
  38. data/config/locales/en.yml +22 -5
  39. data/config/locales/es.yml +20 -4
  40. data/config/locales/eu.yml +5 -0
  41. data/lib/decidim/comments.rb +5 -0
  42. data/{app/types/decidim/comments → lib/decidim/comments/api}/add_comment_type.rb +0 -0
  43. data/{app/types/decidim/comments → lib/decidim/comments/api}/comment_mutation_type.rb +0 -0
  44. data/{app/types/decidim/comments → lib/decidim/comments/api}/comment_type.rb +11 -17
  45. data/lib/decidim/comments/commentable.rb +45 -0
  46. data/{app/helpers → lib}/decidim/comments/comments_helper.rb +15 -10
  47. data/lib/decidim/comments/mutation_extensions.rb +8 -16
  48. data/lib/decidim/comments/query_extensions.rb +5 -8
  49. data/lib/decidim/comments/test/factories.rb +3 -3
  50. metadata +21 -12
  51. data/app/frontend/comments/add_comment_form.fragment.graphql +0 -6
@@ -41,16 +41,16 @@ describe('<CommentThread />', () => {
41
41
  comment = filter(fragment, commentsData[0]);
42
42
  });
43
43
 
44
- describe("when comment doesn't have replies", () => {
44
+ describe("when comment doesn't have comments", () => {
45
45
  it("should not render a title with author name", () => {
46
46
  const wrapper = shallow(<CommentThread comment={comment} session={session} />);
47
47
  expect(wrapper.find('h6.comment-thread__title')).not.to.present();
48
48
  });
49
49
  });
50
50
 
51
- describe("when comment does have replies", () => {
51
+ describe("when comment does has comments", () => {
52
52
  beforeEach(() => {
53
- comment.hasReplies = true;
53
+ comment.hasComments = true;
54
54
  });
55
55
 
56
56
  it("should render a h6 comment-thread__title with author name", () => {
@@ -2,6 +2,6 @@ fragment CommentThread on Comment {
2
2
  author {
3
3
  name
4
4
  }
5
- hasReplies,
5
+ hasComments
6
6
  ...Comment
7
7
  }
@@ -21,7 +21,7 @@ import commentsQuery from './comments.query.graphql';
21
21
  */
22
22
  export class Comments extends Component {
23
23
  render() {
24
- const { comments, reorderComments, orderBy, loading } = this.props;
24
+ const { commentable: { comments }, reorderComments, orderBy, loading } = this.props;
25
25
  let commentClasses = "comments";
26
26
  let commentHeader = I18n.t("components.comments.title", { count: comments.length });
27
27
 
@@ -42,6 +42,7 @@ export class Comments extends Component {
42
42
  defaultOrderBy={orderBy}
43
43
  />
44
44
  </div>
45
+ {this._renderBlockedCommentsWarning()}
45
46
  {this._renderCommentThreads()}
46
47
  {this._renderAddCommentForm()}
47
48
  </section>
@@ -49,20 +50,39 @@ export class Comments extends Component {
49
50
  );
50
51
  }
51
52
 
53
+ /**
54
+ * Renders a warning message if the commentable doesn't accept new comments.
55
+ * @private
56
+ * @returns {Void|DOMElement} - A warning message or nothing.
57
+ */
58
+ _renderBlockedCommentsWarning() {
59
+ const { commentable: { acceptsNewComments } } = this.props;
60
+
61
+ if (!acceptsNewComments) {
62
+ return (
63
+ <div className="callout warning">
64
+ <p>{ I18n.t("components.comments.blocked_comments_warning") }</p>
65
+ </div>
66
+ );
67
+ }
68
+
69
+ return null;
70
+ }
71
+
52
72
  /**
53
73
  * Iterates the comment's collection and render a CommentThread for each one
54
74
  * @private
55
75
  * @returns {ReactComponent[]} - A collection of CommentThread components
56
76
  */
57
77
  _renderCommentThreads() {
58
- const { comments, session, options: { votable } } = this.props;
78
+ const { session, commentable: { comments, commentsHaveVotes } } = this.props;
59
79
 
60
80
  return comments.map((comment) => (
61
81
  <CommentThread
62
82
  key={comment.id}
63
83
  comment={filter(CommentThread.fragments.comment, comment)}
64
84
  session={session}
65
- votable={votable}
85
+ votable={commentsHaveVotes}
66
86
  />
67
87
  ))
68
88
  }
@@ -73,15 +93,15 @@ export class Comments extends Component {
73
93
  * @returns {Void|ReactComponent} - A AddCommentForm component or nothing
74
94
  */
75
95
  _renderAddCommentForm() {
76
- const { session, commentableId, commentableType, options: { arguable } } = this.props;
96
+ const { session, commentable } = this.props;
97
+ const { acceptsNewComments, commentsHaveAlignment } = commentable;
77
98
 
78
- if (session) {
99
+ if (acceptsNewComments) {
79
100
  return (
80
101
  <AddCommentForm
81
102
  session={session}
82
- commentableId={commentableId}
83
- commentableType={commentableType}
84
- arguable={arguable}
103
+ commentable={commentable}
104
+ arguable={commentsHaveAlignment}
85
105
  />
86
106
  );
87
107
  }
@@ -92,21 +112,29 @@ export class Comments extends Component {
92
112
 
93
113
  Comments.propTypes = {
94
114
  loading: PropTypes.bool,
95
- comments: PropTypes.arrayOf(PropTypes.shape({
96
- id: PropTypes.string.isRequired
97
- })),
98
115
  session: PropTypes.shape({
99
116
  user: PropTypes.any.isRequired
100
117
  }),
101
- commentableId: PropTypes.string.isRequired,
102
- commentableType: PropTypes.string.isRequired,
103
- options: PropTypes.shape({
104
- arguable: PropTypes.bool
105
- }).isRequired,
118
+ commentable: PropTypes.shape({
119
+ acceptsNewComments: PropTypes.bool,
120
+ commentsHaveAlignment: PropTypes.bool,
121
+ commentsHaveVotes: PropTypes.bool,
122
+ comments: PropTypes.arrayOf(PropTypes.shape({
123
+ id: PropTypes.string.isRequired
124
+ }))
125
+ }),
106
126
  orderBy: PropTypes.string.isRequired,
107
127
  reorderComments: PropTypes.func.isRequired
108
128
  };
109
129
 
130
+ Comments.defaultProps = {
131
+ loading: false,
132
+ session: null,
133
+ commentable: {
134
+ comments: []
135
+ }
136
+ };
137
+
110
138
  /**
111
139
  * Wrap the Comments component with a GraphQL query and children
112
140
  * fragments.
@@ -116,20 +144,18 @@ window.Comments = Comments;
116
144
 
117
145
  const CommentsWithData = graphql(gql`
118
146
  ${commentsQuery}
119
- ${AddCommentForm.fragments.user}
147
+ ${AddCommentForm.fragments.session}
148
+ ${AddCommentForm.fragments.commentable}
120
149
  ${CommentThread.fragments.comment}
121
150
  `, {
122
151
  options: {
123
152
  pollInterval: 15000
124
153
  },
125
- props: ({ ownProps, data: { loading, session, comments, refetch }}) => ({
126
- loading: loading,
127
- comments: comments || [],
154
+ props: ({ ownProps, data: { loading, session, commentable, refetch }}) => ({
155
+ loading,
128
156
  session,
129
- commentableId: ownProps.commentableId,
130
- commentableType: ownProps.commentableType,
157
+ commentable,
131
158
  orderBy: ownProps.orderBy,
132
- options: ownProps.options,
133
159
  reorderComments: (orderBy) => {
134
160
  return refetch({
135
161
  orderBy
@@ -143,12 +169,11 @@ const CommentsWithData = graphql(gql`
143
169
  * connect it with Apollo client and store.
144
170
  * @returns {ReactComponent} - A component wrapped within an Application component
145
171
  */
146
- const CommentsApplication = ({ locale, commentableId, commentableType, options }) => (
172
+ const CommentsApplication = ({ locale, commentableId, commentableType }) => (
147
173
  <Application locale={locale}>
148
174
  <CommentsWithData
149
175
  commentableId={commentableId}
150
176
  commentableType={commentableType}
151
- options={options}
152
177
  orderBy="older"
153
178
  />
154
179
  </Application>
@@ -156,14 +181,8 @@ const CommentsApplication = ({ locale, commentableId, commentableType, options }
156
181
 
157
182
  CommentsApplication.propTypes = {
158
183
  locale: PropTypes.string.isRequired,
159
- commentableId: React.PropTypes.oneOfType([
160
- PropTypes.string,
161
- PropTypes.number
162
- ]),
163
- commentableType: PropTypes.string.isRequired,
164
- options: PropTypes.shape({
165
- arguable: PropTypes.bool
166
- }).isRequired
184
+ commentableId: PropTypes.string.isRequired,
185
+ commentableType: PropTypes.string.isRequired
167
186
  };
168
187
 
169
188
  export default CommentsApplication;
@@ -15,10 +15,10 @@ import generateUserData from '../support/generate_user_data';
15
15
  import resolveGraphQLQuery from '../support/resolve_graphql_query';
16
16
 
17
17
  describe('<Comments />', () => {
18
- let comments = [];
18
+ let commentable = {};
19
19
  let session = null;
20
20
  const commentableId = "1";
21
- const commentableType = "Decidim::ParticipatoryProcess";
21
+ const commentableType = "Decidim::DummyResource";
22
22
  const orderBy = "older";
23
23
  const reorderComments = () => {};
24
24
 
@@ -28,14 +28,20 @@ describe('<Comments />', () => {
28
28
  }
29
29
  `;
30
30
 
31
- const addCommentFragment = gql`
32
- fragment AddCommentForm on User {
31
+ const addCommentFormSessionFragment = gql`
32
+ fragment AddCommentFormSession on Session {
33
33
  verifiedUserGroups {
34
34
  id
35
35
  }
36
36
  }
37
37
  `;
38
38
 
39
+ const addCommentFormCommentableFragment = gql`
40
+ fragment AddCommentFormCommentable on Commentable {
41
+ id
42
+ }
43
+ `;
44
+
39
45
  stubComponent(CommentOrderSelector)
40
46
 
41
47
  stubComponent(CommentThread, {
@@ -46,7 +52,8 @@ describe('<Comments />', () => {
46
52
 
47
53
  stubComponent(AddCommentForm, {
48
54
  fragments: {
49
- user: addCommentFragment
55
+ session: addCommentFormSessionFragment,
56
+ commentable: addCommentFormCommentableFragment
50
57
  }
51
58
  });
52
59
 
@@ -57,7 +64,8 @@ describe('<Comments />', () => {
57
64
  const query = gql`
58
65
  ${commentsQuery}
59
66
  ${commentThreadFragment}
60
- ${addCommentFragment}
67
+ ${addCommentFormSessionFragment}
68
+ ${addCommentFormCommentableFragment}
61
69
  `;
62
70
 
63
71
  const result = resolveGraphQLQuery(query, {
@@ -66,7 +74,12 @@ describe('<Comments />', () => {
66
74
  session: {
67
75
  user: userData
68
76
  },
69
- comments: commentsData
77
+ commentable: {
78
+ acceptsNewComments: true,
79
+ commentsHaveAlignment: true,
80
+ commentsHaveVotes: true,
81
+ comments: commentsData
82
+ }
70
83
  },
71
84
  variables: {
72
85
  orderBy,
@@ -76,82 +89,82 @@ describe('<Comments />', () => {
76
89
  });
77
90
 
78
91
  session = result.session;
79
- comments = result.comments;
92
+ commentable = result.commentable;
80
93
  });
81
94
 
82
- it("should render loading-comments calss and the respective loading text", () => {
83
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} loading />);
95
+ it("renders loading-comments class and the respective loading text", () => {
96
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} loading />);
84
97
  expect(wrapper.find('.loading-comments')).to.be.present();
85
98
  expect(wrapper.find('h2')).to.have.text("Loading comments ...");
86
99
  });
87
100
 
88
- it("should render a div of id comments", () => {
89
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} />);
101
+ it("renders a div of id comments", () => {
102
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
90
103
  expect(wrapper.find('#comments')).to.be.present();
91
104
  });
92
105
 
93
- describe("should render a CommentThread component for each comment", () => {
106
+ describe("renders a CommentThread component for each comment", () => {
94
107
  it("and pass filter comment data as a prop to it", () => {
95
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} />);
96
- expect(wrapper).to.have.exactly(comments.length).descendants(CommentThread);
108
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
109
+ expect(wrapper).to.have.exactly(commentable.comments.length).descendants(CommentThread);
97
110
  wrapper.find(CommentThread).forEach((node, idx) => {
98
- expect(node).to.have.prop("comment").deep.equal(filter(commentThreadFragment, comments[idx]));
111
+ expect(node).to.have.prop("comment").deep.equal(filter(commentThreadFragment, commentable.comments[idx]));
99
112
  });
100
113
  });
101
114
 
102
115
  it("and pass the session as a prop to it", () => {
103
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} />);
104
- expect(wrapper).to.have.exactly(comments.length).descendants(CommentThread);
116
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
117
+ expect(wrapper).to.have.exactly(commentable.comments.length).descendants(CommentThread);
105
118
  wrapper.find(CommentThread).forEach((node) => {
106
119
  expect(node).to.have.prop("session").deep.equal(session);
107
120
  });
108
121
  });
109
122
 
110
- it("and pass the option votable as a prop to it", () => {
111
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{ votable: true }} reorderComments={reorderComments} orderBy={orderBy} />);
112
- expect(wrapper).to.have.exactly(comments.length).descendants(CommentThread);
123
+ it("and pass the commentable 'commentsHaveVotes' property as a prop to it", () => {
124
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
125
+ expect(wrapper).to.have.exactly(commentable.comments.length).descendants(CommentThread);
113
126
  wrapper.find(CommentThread).forEach((node) => {
114
127
  expect(node).to.have.prop("votable").equal(true);
115
128
  });
116
129
  });
117
130
  });
118
131
 
119
- it("should render comments count", () => {
120
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} />);
121
- const rex = new RegExp(`${comments.length} comments`);
132
+ it("renders comments count", () => {
133
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
134
+ const rex = new RegExp(`${commentable.comments.length} comments`);
122
135
  expect(wrapper.find('h2.section-heading')).to.have.text().match(rex);
123
136
  });
124
137
 
125
- it("should render a AddCommentForm component and pass 'options.arguable' as a prop", () => {
126
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{ arguable: true }} reorderComments={reorderComments} orderBy={orderBy} />);
138
+ it("renders a AddCommentForm component and pass the commentable 'commentsHaveAlignment' as a prop", () => {
139
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
127
140
  expect(wrapper).to.have.exactly(1).descendants(AddCommentForm);
128
141
  expect(wrapper.find(AddCommentForm)).to.have.prop('arguable').equal(true);
129
142
  });
130
143
 
131
- describe("if session is not present", () => {
144
+ describe("when the commentable cannot accept new comments", () => {
132
145
  beforeEach(() => {
133
- session = null;
146
+ commentable.acceptsNewComments = false;
134
147
  });
135
148
 
136
- it("should not render a AddCommentForm component", () => {
137
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} />);
149
+ it("doesn't render an AddCommentForm component", () => {
150
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
138
151
  expect(wrapper.find(AddCommentForm)).not.to.be.present();
139
152
  });
140
- });
141
153
 
142
- describe("should render a CommentOrderSelector component", () => {
143
- it("should render a CommentOrderSelector component", () => {
144
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} />);
145
- expect(wrapper.find(CommentOrderSelector)).to.be.present();
146
- });
154
+ it("renders a callout message to inform the user that comments are blocked", () => {
155
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
156
+ expect(wrapper.find('.callout.warning')).to.include.text("disabled");
157
+ });
158
+ });
147
159
 
160
+ describe("renders a CommentOrderSelector component", () => {
148
161
  it("and pass the reorderComments as a prop to it", () => {
149
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} />);
162
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
150
163
  expect(wrapper.find(CommentOrderSelector)).to.have.prop('reorderComments').deep.equal(reorderComments);
151
164
  });
152
165
 
153
166
  it("and pass the orderBy as a prop to it", () => {
154
- const wrapper = shallow(<Comments comments={comments} commentableId={commentableId} commentableType={commentableType} session={session} options={{}} reorderComments={reorderComments} orderBy={orderBy} />);
167
+ const wrapper = shallow(<Comments commentable={commentable} session={session} reorderComments={reorderComments} orderBy={orderBy} />);
155
168
  expect(wrapper.find(CommentOrderSelector)).to.have.prop('defaultOrderBy').equal('older');
156
169
  });
157
170
  });
@@ -4,10 +4,16 @@ query GetComments($commentableId: String!, $commentableType: String!, $orderBy:
4
4
  name
5
5
  avatarUrl
6
6
  }
7
- ...AddCommentForm
7
+ ...AddCommentFormSession
8
8
  }
9
- comments(commentableId: $commentableId, commentableType: $commentableType, orderBy: $orderBy) {
10
- id
11
- ...CommentThread
9
+ commentable(id: $commentableId, type: $commentableType) {
10
+ acceptsNewComments
11
+ commentsHaveAlignment
12
+ commentsHaveVotes
13
+ comments(orderBy: $orderBy) {
14
+ id
15
+ ...CommentThread
16
+ }
17
+ ...AddCommentFormCommentable
12
18
  }
13
19
  }
@@ -71,20 +71,23 @@ const DownVoteButtonWithMutation = graphql(gql`
71
71
  updateQueries: {
72
72
  GetComments: (prev, { mutationResult: { data } }) => {
73
73
  const commentReducer = (comment) => {
74
- const replies = comment.replies || [];
74
+ const replies = comment.comments || [];
75
75
 
76
76
  if (comment.id === ownProps.comment.id) {
77
77
  return data.comment.downVote;
78
78
  }
79
79
  return {
80
80
  ...comment,
81
- replies: replies.map(commentReducer)
81
+ comments: replies.map(commentReducer)
82
82
  };
83
83
  };
84
84
 
85
85
  return {
86
86
  ...prev,
87
- comments: prev.comments.map(commentReducer)
87
+ commentable: {
88
+ ...prev.commentable,
89
+ comments: prev.commentable.comments.map(commentReducer)
90
+ }
88
91
  }
89
92
  }
90
93
  }
@@ -46,7 +46,7 @@ UpVoteButton.propTypes = {
46
46
 
47
47
  const UpVoteButtonWithMutation = graphql(gql`
48
48
  ${upVoteMutation}
49
- ${commentFragment}
49
+ ${commentFragment}
50
50
  ${commentDataFragment}
51
51
  ${upVoteFragment}
52
52
  ${downVoteFragment}
@@ -71,20 +71,23 @@ const UpVoteButtonWithMutation = graphql(gql`
71
71
  updateQueries: {
72
72
  GetComments: (prev, { mutationResult: { data } }) => {
73
73
  const commentReducer = (comment) => {
74
- const replies = comment.replies || [];
74
+ const replies = comment.comments || [];
75
75
 
76
76
  if (comment.id === ownProps.comment.id) {
77
77
  return data.comment.upVote;
78
78
  }
79
79
  return {
80
80
  ...comment,
81
- replies: replies.map(commentReducer)
81
+ comments: replies.map(commentReducer)
82
82
  };
83
83
  };
84
84
 
85
85
  return {
86
86
  ...prev,
87
- comments: prev.comments.map(commentReducer)
87
+ commentable: {
88
+ ...prev.commentable,
89
+ comments: prev.commentable.comments.map(commentReducer)
90
+ }
88
91
  }
89
92
  }
90
93
  }