decidim-comments 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/app/assets/javascripts/decidim/comments/bundle.js +28 -23
- data/app/assets/javascripts/decidim/comments/bundle.js.map +1 -1
- data/app/commands/decidim/comments/create_comment.rb +10 -0
- data/app/forms/decidim/comments/comment_form.rb +1 -1
- data/app/frontend/comments/add_comment_form.component.jsx +171 -87
- data/app/frontend/comments/add_comment_form.component.test.jsx +41 -22
- data/app/frontend/comments/add_comment_form.mutation.graphql +5 -3
- data/app/frontend/comments/add_comment_form_commentable.fragment.graphql +4 -0
- data/app/frontend/comments/add_comment_form_session.fragment.graphql +6 -0
- data/app/frontend/comments/comment.component.jsx +18 -18
- data/app/frontend/comments/comment.component.test.jsx +22 -21
- data/app/frontend/comments/comment.fragment.graphql +4 -4
- data/app/frontend/comments/comment_data.fragment.graphql +4 -3
- data/app/frontend/comments/comment_order_selector.component.jsx +3 -3
- data/app/frontend/comments/comment_thread.component.jsx +8 -3
- data/app/frontend/comments/comment_thread.component.test.jsx +3 -3
- data/app/frontend/comments/comment_thread.fragment.graphql +1 -1
- data/app/frontend/comments/comments.component.jsx +52 -33
- data/app/frontend/comments/comments.component.test.jsx +51 -38
- data/app/frontend/comments/comments.query.graphql +10 -4
- data/app/frontend/comments/down_vote_button.component.jsx +6 -3
- data/app/frontend/comments/up_vote_button.component.jsx +7 -4
- data/app/frontend/comments/vote_button.component.jsx +5 -0
- data/app/frontend/comments/vote_button_component.test.jsx +1 -1
- data/app/frontend/entry.test.js +2 -0
- data/app/frontend/support/generate_comments_data.js +4 -4
- data/app/mailers/decidim/comments/comment_notification_mailer.rb +31 -0
- data/app/models/decidim/comments/comment.rb +13 -9
- data/app/queries/decidim/comments/{comments_with_replies.rb → sorted_comments.rb} +3 -8
- data/app/types/decidim/comments/commentable_interface.rb +44 -0
- data/app/types/decidim/comments/commentable_mutation_type.rb +29 -0
- data/app/types/decidim/comments/commentable_type.rb +14 -0
- data/app/views/decidim/comments/comment_notification_mailer/comment_created.html.erb +18 -0
- data/app/views/decidim/comments/comment_notification_mailer/reply_created.html.erb +18 -0
- data/config/locales/ca.yml +20 -4
- data/config/locales/en.yml +22 -5
- data/config/locales/es.yml +20 -4
- data/config/locales/eu.yml +5 -0
- data/lib/decidim/comments.rb +5 -0
- data/{app/types/decidim/comments → lib/decidim/comments/api}/add_comment_type.rb +0 -0
- data/{app/types/decidim/comments → lib/decidim/comments/api}/comment_mutation_type.rb +0 -0
- data/{app/types/decidim/comments → lib/decidim/comments/api}/comment_type.rb +11 -17
- data/lib/decidim/comments/commentable.rb +45 -0
- data/{app/helpers → lib}/decidim/comments/comments_helper.rb +15 -10
- data/lib/decidim/comments/mutation_extensions.rb +8 -16
- data/lib/decidim/comments/query_extensions.rb +5 -8
- data/lib/decidim/comments/test/factories.rb +3 -3
- metadata +21 -12
- 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
|
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
|
51
|
+
describe("when comment does has comments", () => {
|
52
52
|
beforeEach(() => {
|
53
|
-
comment.
|
53
|
+
comment.hasComments = true;
|
54
54
|
});
|
55
55
|
|
56
56
|
it("should render a h6 comment-thread__title with author name", () => {
|
@@ -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 {
|
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={
|
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,
|
96
|
+
const { session, commentable } = this.props;
|
97
|
+
const { acceptsNewComments, commentsHaveAlignment } = commentable;
|
77
98
|
|
78
|
-
if (
|
99
|
+
if (acceptsNewComments) {
|
79
100
|
return (
|
80
101
|
<AddCommentForm
|
81
102
|
session={session}
|
82
|
-
|
83
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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.
|
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,
|
126
|
-
loading
|
127
|
-
comments: comments || [],
|
154
|
+
props: ({ ownProps, data: { loading, session, commentable, refetch }}) => ({
|
155
|
+
loading,
|
128
156
|
session,
|
129
|
-
|
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
|
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:
|
160
|
-
|
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
|
18
|
+
let commentable = {};
|
19
19
|
let session = null;
|
20
20
|
const commentableId = "1";
|
21
|
-
const commentableType = "Decidim::
|
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
|
32
|
-
fragment
|
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
|
-
|
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
|
-
${
|
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
|
-
|
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
|
-
|
92
|
+
commentable = result.commentable;
|
80
93
|
});
|
81
94
|
|
82
|
-
it("
|
83
|
-
const wrapper = shallow(<Comments
|
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("
|
89
|
-
const wrapper = shallow(<Comments
|
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("
|
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
|
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
|
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
|
111
|
-
const wrapper = shallow(<Comments
|
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("
|
120
|
-
const wrapper = shallow(<Comments
|
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("
|
126
|
-
const wrapper = shallow(<Comments
|
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("
|
144
|
+
describe("when the commentable cannot accept new comments", () => {
|
132
145
|
beforeEach(() => {
|
133
|
-
|
146
|
+
commentable.acceptsNewComments = false;
|
134
147
|
});
|
135
148
|
|
136
|
-
it("
|
137
|
-
const wrapper = shallow(<Comments
|
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
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
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
|
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
|
-
...
|
7
|
+
...AddCommentFormSession
|
8
8
|
}
|
9
|
-
|
10
|
-
|
11
|
-
|
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.
|
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
|
-
|
81
|
+
comments: replies.map(commentReducer)
|
82
82
|
};
|
83
83
|
};
|
84
84
|
|
85
85
|
return {
|
86
86
|
...prev,
|
87
|
-
|
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.
|
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
|
-
|
81
|
+
comments: replies.map(commentReducer)
|
82
82
|
};
|
83
83
|
};
|
84
84
|
|
85
85
|
return {
|
86
86
|
...prev,
|
87
|
-
|
87
|
+
commentable: {
|
88
|
+
...prev.commentable,
|
89
|
+
comments: prev.commentable.comments.map(commentReducer)
|
90
|
+
}
|
88
91
|
}
|
89
92
|
}
|
90
93
|
}
|