@stream-io/feeds-client 0.2.9 → 0.2.10
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.
- package/CHANGELOG.md +7 -0
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react-bindings.js +1 -1
- package/dist/es/index.mjs +3 -2
- package/dist/es/react-bindings.mjs +1 -1
- package/dist/{index-B0Mm2xFU.js → index-C49kZoN7.js} +273 -51
- package/dist/index-C49kZoN7.js.map +1 -0
- package/dist/{index-rSXIDTdA.mjs → index-EeFSq3sq.mjs} +273 -51
- package/dist/index-EeFSq3sq.mjs.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/feed/event-handlers/activity/handle-activity-reaction-added.d.ts +4 -5
- package/dist/types/feed/event-handlers/activity/handle-activity-reaction-added.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/activity/handle-activity-reaction-deleted.d.ts +1 -2
- package/dist/types/feed/event-handlers/activity/handle-activity-reaction-deleted.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/activity/handle-activity-updated.d.ts +6 -5
- package/dist/types/feed/event-handlers/activity/handle-activity-updated.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/comment/handle-comment-added.d.ts +4 -3
- package/dist/types/feed/event-handlers/comment/handle-comment-added.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/comment/handle-comment-deleted.d.ts +4 -3
- package/dist/types/feed/event-handlers/comment/handle-comment-deleted.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/comment/handle-comment-reaction-added.d.ts +5 -0
- package/dist/types/feed/event-handlers/comment/handle-comment-reaction-added.d.ts.map +1 -0
- package/dist/types/feed/event-handlers/comment/handle-comment-reaction-deleted.d.ts +5 -0
- package/dist/types/feed/event-handlers/comment/handle-comment-reaction-deleted.d.ts.map +1 -0
- package/dist/types/feed/event-handlers/comment/handle-comment-updated.d.ts +4 -3
- package/dist/types/feed/event-handlers/comment/handle-comment-updated.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/comment/index.d.ts +2 -1
- package/dist/types/feed/event-handlers/comment/index.d.ts.map +1 -1
- package/dist/types/feed/event-handlers/comment/utils/index.d.ts +2 -0
- package/dist/types/feed/event-handlers/comment/utils/index.d.ts.map +1 -0
- package/dist/types/feed/event-handlers/comment/utils/update-comment-count.d.ts +8 -0
- package/dist/types/feed/event-handlers/comment/utils/update-comment-count.d.ts.map +1 -0
- package/dist/types/feed/feed.d.ts.map +1 -1
- package/dist/types/feeds-client/feeds-client.d.ts +19 -1
- package/dist/types/feeds-client/feeds-client.d.ts.map +1 -1
- package/dist/types/types-internal.d.ts +4 -2
- package/dist/types/types-internal.d.ts.map +1 -1
- package/dist/types/utils/ensure-exhausted.d.ts +2 -0
- package/dist/types/utils/ensure-exhausted.d.ts.map +1 -0
- package/dist/types/utils/event-triggered-by-connected-user.d.ts +6 -0
- package/dist/types/utils/event-triggered-by-connected-user.d.ts.map +1 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/index.d.ts.map +1 -1
- package/dist/types/utils/logger.d.ts +10 -1
- package/dist/types/utils/logger.d.ts.map +1 -1
- package/dist/types/utils/state-update-queue.d.ts +22 -2
- package/dist/types/utils/state-update-queue.d.ts.map +1 -1
- package/dist/types/utils/type-assertions.d.ts +2 -5
- package/dist/types/utils/type-assertions.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/feed/event-handlers/activity/handle-activity-reaction-added.ts +15 -10
- package/src/feed/event-handlers/activity/handle-activity-reaction-deleted.test.ts +1 -1
- package/src/feed/event-handlers/activity/handle-activity-reaction-deleted.ts +1 -1
- package/src/feed/event-handlers/activity/handle-activity-updated.test.ts +131 -1
- package/src/feed/event-handlers/activity/handle-activity-updated.ts +38 -15
- package/src/feed/event-handlers/comment/handle-comment-added.test.ts +131 -7
- package/src/feed/event-handlers/comment/handle-comment-added.ts +24 -4
- package/src/feed/event-handlers/comment/handle-comment-deleted.test.ts +124 -2
- package/src/feed/event-handlers/comment/handle-comment-deleted.ts +29 -3
- package/src/feed/event-handlers/comment/{handle-comment-reaction.test.ts → handle-comment-reaction-added.test.ts} +152 -138
- package/src/feed/event-handlers/comment/handle-comment-reaction-added.ts +72 -0
- package/src/feed/event-handlers/comment/handle-comment-reaction-deleted.test.ts +343 -0
- package/src/feed/event-handlers/comment/handle-comment-reaction-deleted.ts +74 -0
- package/src/feed/event-handlers/comment/handle-comment-updated.test.ts +137 -1
- package/src/feed/event-handlers/comment/handle-comment-updated.ts +29 -4
- package/src/feed/event-handlers/comment/index.ts +3 -1
- package/src/feed/event-handlers/comment/utils/index.ts +1 -0
- package/src/feed/event-handlers/comment/utils/update-comment-count.test.ts +320 -0
- package/src/feed/event-handlers/comment/utils/update-comment-count.ts +51 -0
- package/src/feed/event-handlers/follow/handle-follow-deleted.ts +1 -1
- package/src/feed/feed.ts +4 -3
- package/src/feeds-client/feeds-client.ts +104 -0
- package/src/test-utils/response-generators.ts +18 -1
- package/src/types-internal.ts +4 -4
- package/src/utils/ensure-exhausted.ts +5 -0
- package/src/utils/event-triggered-by-connected-user.test.ts +73 -0
- package/src/utils/event-triggered-by-connected-user.ts +15 -0
- package/src/utils/index.ts +2 -1
- package/src/utils/logger.ts +2 -1
- package/src/utils/state-update-queue.ts +89 -25
- package/src/utils/type-assertions.ts +2 -3
- package/dist/index-B0Mm2xFU.js.map +0 -1
- package/dist/index-rSXIDTdA.mjs.map +0 -1
- package/dist/types/feed/event-handlers/comment/handle-comment-reaction.d.ts +0 -4
- package/dist/types/feed/event-handlers/comment/handle-comment-reaction.d.ts.map +0 -1
- package/src/feed/event-handlers/comment/handle-comment-reaction.ts +0 -61
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { Feed } from '../../../feed';
|
|
3
|
+
import { FeedsClient } from '../../../feeds-client';
|
|
4
|
+
import { handleCommentReactionDeleted } from './handle-comment-reaction-deleted';
|
|
5
|
+
import {
|
|
6
|
+
generateCommentResponse,
|
|
7
|
+
generateFeedReactionResponse,
|
|
8
|
+
generateFeedResponse,
|
|
9
|
+
generateOwnUser,
|
|
10
|
+
getHumanId,
|
|
11
|
+
generateCommentReactionDeletedEvent,
|
|
12
|
+
} from '../../../test-utils';
|
|
13
|
+
import { shouldUpdateState } from '../../../utils';
|
|
14
|
+
import { CommentResponse, FeedsReactionResponse } from '../../../gen/models';
|
|
15
|
+
import { EventPayload } from '../../../types-internal';
|
|
16
|
+
|
|
17
|
+
describe(handleCommentReactionDeleted.name, () => {
|
|
18
|
+
let feed: Feed;
|
|
19
|
+
let client: FeedsClient;
|
|
20
|
+
let currentUserId: string;
|
|
21
|
+
let activityId: string;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
client = new FeedsClient('mock-api-key');
|
|
25
|
+
currentUserId = getHumanId();
|
|
26
|
+
client.state.partialNext({
|
|
27
|
+
connected_user: generateOwnUser({ id: currentUserId }),
|
|
28
|
+
});
|
|
29
|
+
const feedResponse = generateFeedResponse({
|
|
30
|
+
id: 'main',
|
|
31
|
+
group_id: 'user',
|
|
32
|
+
created_by: { id: currentUserId },
|
|
33
|
+
});
|
|
34
|
+
feed = new Feed(
|
|
35
|
+
client,
|
|
36
|
+
feedResponse.group_id,
|
|
37
|
+
feedResponse.id,
|
|
38
|
+
feedResponse,
|
|
39
|
+
);
|
|
40
|
+
activityId = `activity-${getHumanId()}`;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('removes from own_reactions when the target reaction belongs to the current user', () => {
|
|
44
|
+
const commentId = `comment-${getHumanId()}`;
|
|
45
|
+
const existingReaction = generateFeedReactionResponse({
|
|
46
|
+
type: 'heart',
|
|
47
|
+
user: { id: currentUserId },
|
|
48
|
+
activity_id: activityId,
|
|
49
|
+
comment_id: commentId,
|
|
50
|
+
});
|
|
51
|
+
const existingComment = generateCommentResponse({
|
|
52
|
+
id: commentId,
|
|
53
|
+
object_id: activityId,
|
|
54
|
+
own_reactions: [existingReaction],
|
|
55
|
+
latest_reactions: [],
|
|
56
|
+
reaction_groups: {},
|
|
57
|
+
});
|
|
58
|
+
feed.state.partialNext({
|
|
59
|
+
comments_by_entity_id: {
|
|
60
|
+
[activityId]: {
|
|
61
|
+
comments: [existingComment],
|
|
62
|
+
pagination: { sort: 'first' },
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const event = generateCommentReactionDeletedEvent({
|
|
68
|
+
comment: {
|
|
69
|
+
...existingComment,
|
|
70
|
+
latest_reactions: [],
|
|
71
|
+
reaction_groups: {},
|
|
72
|
+
},
|
|
73
|
+
reaction: {
|
|
74
|
+
type: 'heart',
|
|
75
|
+
user: { id: currentUserId },
|
|
76
|
+
activity_id: activityId,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const stateBefore = feed.currentState;
|
|
81
|
+
expect(
|
|
82
|
+
stateBefore.comments_by_entity_id[activityId]!.comments![0].own_reactions,
|
|
83
|
+
).toHaveLength(1);
|
|
84
|
+
handleCommentReactionDeleted.call(feed, event);
|
|
85
|
+
const stateAfter = feed.currentState;
|
|
86
|
+
const [updated] = stateAfter.comments_by_entity_id[activityId]!.comments!;
|
|
87
|
+
expect(updated.own_reactions).toHaveLength(0);
|
|
88
|
+
expect(updated.latest_reactions).toBe(event.comment.latest_reactions);
|
|
89
|
+
expect(updated.reaction_groups).toBe(event.comment.reaction_groups);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('does not remove from own_reactions when target reaction does not belong to the current user', () => {
|
|
93
|
+
const commentId = `comment-${getHumanId()}`;
|
|
94
|
+
const ownReaction = generateFeedReactionResponse({
|
|
95
|
+
type: 'wow',
|
|
96
|
+
user: { id: currentUserId },
|
|
97
|
+
activity_id: activityId,
|
|
98
|
+
comment_id: commentId,
|
|
99
|
+
});
|
|
100
|
+
const existingComment = generateCommentResponse({
|
|
101
|
+
id: commentId,
|
|
102
|
+
object_id: activityId,
|
|
103
|
+
own_reactions: [ownReaction],
|
|
104
|
+
latest_reactions: [],
|
|
105
|
+
reaction_groups: {},
|
|
106
|
+
});
|
|
107
|
+
feed.state.partialNext({
|
|
108
|
+
comments_by_entity_id: {
|
|
109
|
+
[activityId]: {
|
|
110
|
+
comments: [existingComment],
|
|
111
|
+
pagination: { sort: 'first' },
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const event = generateCommentReactionDeletedEvent({
|
|
117
|
+
comment: {
|
|
118
|
+
...existingComment,
|
|
119
|
+
latest_reactions: [],
|
|
120
|
+
reaction_groups: {},
|
|
121
|
+
},
|
|
122
|
+
reaction: {
|
|
123
|
+
type: 'wow',
|
|
124
|
+
user: { id: 'other-user' },
|
|
125
|
+
comment_id: existingComment.id,
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
handleCommentReactionDeleted.call(feed, event);
|
|
130
|
+
const stateAfter = feed.currentState;
|
|
131
|
+
const [updated] = stateAfter.comments_by_entity_id[activityId]!.comments!;
|
|
132
|
+
expect(updated.own_reactions).toHaveLength(1);
|
|
133
|
+
expect(updated.own_reactions[0]).toBe(ownReaction);
|
|
134
|
+
expect(updated.latest_reactions).toBe(event.comment.latest_reactions);
|
|
135
|
+
expect(updated.reaction_groups).toBe(event.comment.reaction_groups);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('does changes to the proper entity state (prefers parent_id)', () => {
|
|
139
|
+
const parentId = `comment-${getHumanId()}`;
|
|
140
|
+
const existingComment = generateCommentResponse({
|
|
141
|
+
object_id: activityId,
|
|
142
|
+
parent_id: parentId,
|
|
143
|
+
latest_reactions: [],
|
|
144
|
+
reaction_groups: {},
|
|
145
|
+
own_reactions: [],
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const existingReaction = generateFeedReactionResponse({
|
|
149
|
+
type: 'like',
|
|
150
|
+
user: { id: currentUserId },
|
|
151
|
+
activity_id: activityId,
|
|
152
|
+
comment_id: existingComment.id,
|
|
153
|
+
});
|
|
154
|
+
existingComment.own_reactions = [existingReaction];
|
|
155
|
+
|
|
156
|
+
feed.state.partialNext({
|
|
157
|
+
comments_by_entity_id: {
|
|
158
|
+
[parentId]: {
|
|
159
|
+
comments: [existingComment],
|
|
160
|
+
pagination: { sort: 'first' },
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const deletedEvent = generateCommentReactionDeletedEvent({
|
|
166
|
+
comment: {
|
|
167
|
+
id: existingComment.id,
|
|
168
|
+
object_id: activityId,
|
|
169
|
+
parent_id: parentId,
|
|
170
|
+
latest_reactions: [],
|
|
171
|
+
reaction_groups: {},
|
|
172
|
+
},
|
|
173
|
+
reaction: {
|
|
174
|
+
type: 'like',
|
|
175
|
+
user: { id: currentUserId },
|
|
176
|
+
activity_id: activityId,
|
|
177
|
+
comment_id: existingComment.id,
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const stateBefore = feed.currentState;
|
|
182
|
+
expect(
|
|
183
|
+
stateBefore.comments_by_entity_id[parentId]?.comments?.[0]?.own_reactions,
|
|
184
|
+
).toHaveLength(1);
|
|
185
|
+
|
|
186
|
+
handleCommentReactionDeleted.call(feed, deletedEvent);
|
|
187
|
+
const stateAfter = feed.currentState;
|
|
188
|
+
const [updated] = stateAfter.comments_by_entity_id[parentId]!.comments!;
|
|
189
|
+
expect(updated.own_reactions).toHaveLength(0);
|
|
190
|
+
expect(updated.latest_reactions).toBe(
|
|
191
|
+
deletedEvent.comment.latest_reactions,
|
|
192
|
+
);
|
|
193
|
+
expect(updated.reaction_groups).toBe(deletedEvent.comment.reaction_groups);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('does nothing if comment is not found in state', () => {
|
|
197
|
+
const deletedEvent = generateCommentReactionDeletedEvent({
|
|
198
|
+
comment: { object_id: activityId },
|
|
199
|
+
reaction: { user: { id: currentUserId } },
|
|
200
|
+
});
|
|
201
|
+
const stateBefore = feed.currentState;
|
|
202
|
+
|
|
203
|
+
handleCommentReactionDeleted.call(feed, deletedEvent);
|
|
204
|
+
const stateAfter = feed.currentState;
|
|
205
|
+
expect(stateAfter).toBe(stateBefore);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe(`Comment reaction deleted ${shouldUpdateState.name} integration`, () => {
|
|
209
|
+
let currentUserPayload: EventPayload<'feeds.comment.reaction.deleted'>;
|
|
210
|
+
let existingComment: CommentResponse;
|
|
211
|
+
let existingReaction: FeedsReactionResponse;
|
|
212
|
+
let commentId: string;
|
|
213
|
+
|
|
214
|
+
beforeEach(() => {
|
|
215
|
+
commentId = `comment-${getHumanId()}`;
|
|
216
|
+
existingReaction = generateFeedReactionResponse({
|
|
217
|
+
type: 'like',
|
|
218
|
+
user: { id: currentUserId },
|
|
219
|
+
activity_id: activityId,
|
|
220
|
+
comment_id: commentId,
|
|
221
|
+
});
|
|
222
|
+
existingComment = generateCommentResponse({
|
|
223
|
+
id: commentId,
|
|
224
|
+
object_id: activityId,
|
|
225
|
+
own_reactions: [existingReaction],
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
currentUserPayload = generateCommentReactionDeletedEvent({
|
|
229
|
+
comment: {
|
|
230
|
+
...existingComment,
|
|
231
|
+
own_reactions: [],
|
|
232
|
+
},
|
|
233
|
+
reaction: existingReaction,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
feed.state.partialNext({
|
|
237
|
+
comments_by_entity_id: {
|
|
238
|
+
[activityId]: {
|
|
239
|
+
comments: [existingComment],
|
|
240
|
+
pagination: { sort: 'first' },
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
feed.state.partialNext({ watch: true });
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it(`skips update if ${shouldUpdateState.name} returns false`, () => {
|
|
248
|
+
// 1. HTTP and then WS
|
|
249
|
+
|
|
250
|
+
handleCommentReactionDeleted.call(feed, currentUserPayload, false);
|
|
251
|
+
|
|
252
|
+
let stateBefore = feed.currentState;
|
|
253
|
+
|
|
254
|
+
handleCommentReactionDeleted.call(feed, currentUserPayload);
|
|
255
|
+
|
|
256
|
+
let stateAfter = feed.currentState;
|
|
257
|
+
|
|
258
|
+
expect(stateAfter).toBe(stateBefore);
|
|
259
|
+
// @ts-expect-error Using Feed internals for tests only
|
|
260
|
+
expect(feed.stateUpdateQueue.size).toEqual(0);
|
|
261
|
+
|
|
262
|
+
// 2. WS and the HTTP
|
|
263
|
+
|
|
264
|
+
handleCommentReactionDeleted.call(feed, currentUserPayload);
|
|
265
|
+
|
|
266
|
+
stateBefore = feed.currentState;
|
|
267
|
+
|
|
268
|
+
handleCommentReactionDeleted.call(feed, currentUserPayload, false);
|
|
269
|
+
|
|
270
|
+
stateAfter = feed.currentState;
|
|
271
|
+
|
|
272
|
+
expect(stateAfter).toBe(stateBefore);
|
|
273
|
+
// @ts-expect-error Using Feed internals for tests only
|
|
274
|
+
expect(feed.stateUpdateQueue.size).toEqual(0);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('allows update again from WS after clearing the stateUpdateQueue', () => {
|
|
278
|
+
handleCommentReactionDeleted.call(feed, currentUserPayload);
|
|
279
|
+
|
|
280
|
+
// Clear the queue
|
|
281
|
+
(feed as any).stateUpdateQueue.clear();
|
|
282
|
+
|
|
283
|
+
// Now update should be allowed from another WS event
|
|
284
|
+
handleCommentReactionDeleted.call(feed, currentUserPayload);
|
|
285
|
+
|
|
286
|
+
const comments =
|
|
287
|
+
feed.currentState.comments_by_entity_id[activityId]?.comments;
|
|
288
|
+
const comment = comments?.find((a) => a.id === commentId);
|
|
289
|
+
const [latestReaction] = comment?.own_reactions ?? [];
|
|
290
|
+
|
|
291
|
+
expect(comment?.own_reactions.length).toEqual(0);
|
|
292
|
+
expect(latestReaction).toBeUndefined();
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('allows update again from HTTP response after clearing the stateUpdateQueue', () => {
|
|
296
|
+
handleCommentReactionDeleted.call(feed, currentUserPayload, false);
|
|
297
|
+
|
|
298
|
+
// Clear the queue
|
|
299
|
+
(feed as any).stateUpdateQueue.clear();
|
|
300
|
+
|
|
301
|
+
// Now update should be allowed from another HTTP response
|
|
302
|
+
handleCommentReactionDeleted.call(feed, currentUserPayload, false);
|
|
303
|
+
|
|
304
|
+
const comments =
|
|
305
|
+
feed.currentState.comments_by_entity_id[activityId]?.comments;
|
|
306
|
+
const comment = comments?.find((a) => a.id === commentId);
|
|
307
|
+
const [latestReaction] = comment?.own_reactions ?? [];
|
|
308
|
+
|
|
309
|
+
expect(comment?.own_reactions.length).toEqual(0);
|
|
310
|
+
expect(latestReaction).toBeUndefined();
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it('should not insert anything into the stateUpdateQueue if the connected_user did not trigger the comment reaction deletion', () => {
|
|
314
|
+
const otherUserPayload = generateCommentReactionDeletedEvent({
|
|
315
|
+
comment: {
|
|
316
|
+
...existingComment,
|
|
317
|
+
own_reactions: [],
|
|
318
|
+
},
|
|
319
|
+
reaction: {
|
|
320
|
+
...existingReaction,
|
|
321
|
+
user: {
|
|
322
|
+
id: getHumanId(),
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
handleCommentReactionDeleted.call(feed, otherUserPayload);
|
|
328
|
+
|
|
329
|
+
expect((feed as any).stateUpdateQueue).toEqual(new Set());
|
|
330
|
+
|
|
331
|
+
handleCommentReactionDeleted.call(feed, otherUserPayload);
|
|
332
|
+
|
|
333
|
+
const comments =
|
|
334
|
+
feed.currentState.comments_by_entity_id[activityId]?.comments;
|
|
335
|
+
const comment = comments?.find((a) => a.id === commentId);
|
|
336
|
+
const [latestReaction] = comment?.own_reactions ?? [];
|
|
337
|
+
|
|
338
|
+
expect((feed as any).stateUpdateQueue).toEqual(new Set());
|
|
339
|
+
expect(comment?.own_reactions.length).toEqual(1);
|
|
340
|
+
expect(latestReaction).toBe(existingReaction);
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Feed } from '../../feed';
|
|
2
|
+
import { EventPayload, type PartializeAllBut } from '../../../types-internal';
|
|
3
|
+
import { getStateUpdateQueueId, shouldUpdateState } from '../../../utils';
|
|
4
|
+
|
|
5
|
+
export type CommentReactionDeletedPayload = PartializeAllBut<
|
|
6
|
+
EventPayload<'feeds.comment.reaction.deleted'>,
|
|
7
|
+
'comment' | 'reaction'
|
|
8
|
+
>;
|
|
9
|
+
|
|
10
|
+
export function handleCommentReactionDeleted(
|
|
11
|
+
this: Feed,
|
|
12
|
+
payload: CommentReactionDeletedPayload,
|
|
13
|
+
fromWs?: boolean,
|
|
14
|
+
) {
|
|
15
|
+
const { comment, reaction } = payload;
|
|
16
|
+
const connectedUser = this.client.state.getLatestValue().connected_user;
|
|
17
|
+
|
|
18
|
+
const isOwnReaction = reaction.user.id === connectedUser?.id;
|
|
19
|
+
|
|
20
|
+
if (
|
|
21
|
+
!shouldUpdateState({
|
|
22
|
+
stateUpdateQueueId: getStateUpdateQueueId(
|
|
23
|
+
payload,
|
|
24
|
+
'comment-reaction-deleted',
|
|
25
|
+
),
|
|
26
|
+
stateUpdateQueue: this.stateUpdateQueue,
|
|
27
|
+
watch: this.currentState.watch,
|
|
28
|
+
fromWs,
|
|
29
|
+
isTriggeredByConnectedUser: isOwnReaction,
|
|
30
|
+
})
|
|
31
|
+
) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
this.state.next((currentState) => {
|
|
36
|
+
const commentIndex = this.getCommentIndex(comment, currentState);
|
|
37
|
+
|
|
38
|
+
if (commentIndex === -1) return currentState;
|
|
39
|
+
|
|
40
|
+
const forId = comment.parent_id ?? comment.object_id;
|
|
41
|
+
|
|
42
|
+
const entityState = currentState.comments_by_entity_id[forId];
|
|
43
|
+
|
|
44
|
+
const newComments = entityState?.comments?.concat([]) ?? [];
|
|
45
|
+
|
|
46
|
+
let ownReactions = newComments[commentIndex].own_reactions;
|
|
47
|
+
|
|
48
|
+
if (isOwnReaction) {
|
|
49
|
+
ownReactions = ownReactions.filter(
|
|
50
|
+
(r) => r.type !== reaction.type,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
newComments[commentIndex] = {
|
|
55
|
+
...newComments[commentIndex],
|
|
56
|
+
reaction_count: comment.reaction_count ?? 0,
|
|
57
|
+
// TODO: FIXME this should be handled by the backend
|
|
58
|
+
latest_reactions: comment.latest_reactions ?? [],
|
|
59
|
+
reaction_groups: comment.reaction_groups ?? {},
|
|
60
|
+
own_reactions: ownReactions,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
...currentState,
|
|
65
|
+
comments_by_entity_id: {
|
|
66
|
+
...currentState.comments_by_entity_id,
|
|
67
|
+
[forId]: {
|
|
68
|
+
...entityState,
|
|
69
|
+
comments: newComments,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
}
|
|
@@ -7,8 +7,12 @@ import {
|
|
|
7
7
|
generateCommentUpdatedEvent,
|
|
8
8
|
generateFeedResponse,
|
|
9
9
|
generateOwnUser,
|
|
10
|
+
generateUserResponseCommonFields,
|
|
10
11
|
getHumanId,
|
|
11
|
-
} from '../../../test-utils
|
|
12
|
+
} from '../../../test-utils';
|
|
13
|
+
import { CommentResponse, UserResponseCommonFields } from '../../../gen/models';
|
|
14
|
+
import { shouldUpdateState } from '../../../utils';
|
|
15
|
+
import { EventPayload } from '../../../types-internal';
|
|
12
16
|
|
|
13
17
|
describe(handleCommentUpdated.name, () => {
|
|
14
18
|
let feed: Feed;
|
|
@@ -128,4 +132,136 @@ describe(handleCommentUpdated.name, () => {
|
|
|
128
132
|
const stateAfter = feed.currentState;
|
|
129
133
|
expect(stateAfter).toBe(stateBefore);
|
|
130
134
|
});
|
|
135
|
+
|
|
136
|
+
describe(`Comment updated ${shouldUpdateState.name} integration`, () => {
|
|
137
|
+
let currentUserPayload: EventPayload<'feeds.comment.updated'>;
|
|
138
|
+
let existingComment: CommentResponse;
|
|
139
|
+
let commentBeforeUpdate: CommentResponse;
|
|
140
|
+
let commentAfterUpdate: CommentResponse;
|
|
141
|
+
let commentId: string;
|
|
142
|
+
|
|
143
|
+
beforeEach(() => {
|
|
144
|
+
commentId = `comment-${getHumanId()}`;
|
|
145
|
+
commentBeforeUpdate = generateCommentResponse({
|
|
146
|
+
id: commentId,
|
|
147
|
+
object_id: activityId,
|
|
148
|
+
});
|
|
149
|
+
commentAfterUpdate = {
|
|
150
|
+
...commentBeforeUpdate,
|
|
151
|
+
reply_count: commentBeforeUpdate.reply_count + 10,
|
|
152
|
+
};
|
|
153
|
+
existingComment = generateCommentResponse({
|
|
154
|
+
id: `comment-${getHumanId()}`,
|
|
155
|
+
object_id: activityId,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
currentUserPayload = generateCommentUpdatedEvent({
|
|
159
|
+
comment: commentAfterUpdate,
|
|
160
|
+
user: client.state.getLatestValue()
|
|
161
|
+
.connected_user as UserResponseCommonFields,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
feed.state.partialNext({
|
|
165
|
+
comments_by_entity_id: {
|
|
166
|
+
[activityId]: {
|
|
167
|
+
comments: [existingComment, commentBeforeUpdate],
|
|
168
|
+
pagination: { sort: 'first' },
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
feed.state.partialNext({ watch: true });
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it(`skips update if ${shouldUpdateState.name} returns false`, () => {
|
|
176
|
+
// 1. HTTP and then WS
|
|
177
|
+
|
|
178
|
+
handleCommentUpdated.call(feed, currentUserPayload, false);
|
|
179
|
+
|
|
180
|
+
let stateBefore = feed.currentState;
|
|
181
|
+
|
|
182
|
+
handleCommentUpdated.call(feed, currentUserPayload);
|
|
183
|
+
|
|
184
|
+
let stateAfter = feed.currentState;
|
|
185
|
+
|
|
186
|
+
expect(stateAfter).toBe(stateBefore);
|
|
187
|
+
// @ts-expect-error Using Feed internals for tests only
|
|
188
|
+
expect(feed.stateUpdateQueue.size).toEqual(0);
|
|
189
|
+
|
|
190
|
+
// 2. WS and the HTTP
|
|
191
|
+
|
|
192
|
+
handleCommentUpdated.call(feed, currentUserPayload);
|
|
193
|
+
|
|
194
|
+
stateBefore = feed.currentState;
|
|
195
|
+
|
|
196
|
+
handleCommentUpdated.call(feed, currentUserPayload, false);
|
|
197
|
+
|
|
198
|
+
stateAfter = feed.currentState;
|
|
199
|
+
|
|
200
|
+
expect(stateAfter).toBe(stateBefore);
|
|
201
|
+
// @ts-expect-error Using Feed internals for tests only
|
|
202
|
+
expect(feed.stateUpdateQueue.size).toEqual(0);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('allows update again from WS after clearing the stateUpdateQueue', () => {
|
|
206
|
+
handleCommentUpdated.call(feed, currentUserPayload);
|
|
207
|
+
|
|
208
|
+
// Clear the queue
|
|
209
|
+
(feed as any).stateUpdateQueue.clear();
|
|
210
|
+
|
|
211
|
+
// Now update should be allowed from another WS event
|
|
212
|
+
handleCommentUpdated.call(feed, currentUserPayload);
|
|
213
|
+
|
|
214
|
+
const comments =
|
|
215
|
+
feed.currentState.comments_by_entity_id[activityId]?.comments;
|
|
216
|
+
const [latestComment, previousComment] = (comments ?? []).toReversed();
|
|
217
|
+
|
|
218
|
+
expect(comments?.length).toEqual(2);
|
|
219
|
+
expect(latestComment).toMatchObject(commentAfterUpdate);
|
|
220
|
+
expect(previousComment).toMatchObject(existingComment);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('allows update again from HTTP response after clearing the stateUpdateQueue', () => {
|
|
224
|
+
handleCommentUpdated.call(feed, currentUserPayload, false);
|
|
225
|
+
|
|
226
|
+
// Clear the queue
|
|
227
|
+
(feed as any).stateUpdateQueue.clear();
|
|
228
|
+
|
|
229
|
+
// Now update should be allowed from another HTTP response
|
|
230
|
+
handleCommentUpdated.call(feed, currentUserPayload, false);
|
|
231
|
+
|
|
232
|
+
const comments =
|
|
233
|
+
feed.currentState.comments_by_entity_id[activityId]?.comments;
|
|
234
|
+
const [latestComment, previousComment] = (comments ?? []).toReversed();
|
|
235
|
+
|
|
236
|
+
expect(comments?.length).toEqual(2);
|
|
237
|
+
expect(latestComment).toMatchObject(commentAfterUpdate);
|
|
238
|
+
expect(previousComment).toMatchObject(existingComment);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should not insert anything into the stateUpdateQueue if the connected_user did not trigger the comment reaction deletion', () => {
|
|
242
|
+
const otherCommentUpdate = generateCommentResponse({
|
|
243
|
+
...commentBeforeUpdate,
|
|
244
|
+
reply_count: commentBeforeUpdate.reply_count + 1,
|
|
245
|
+
});
|
|
246
|
+
const otherUserPayload = generateCommentUpdatedEvent({
|
|
247
|
+
comment: otherCommentUpdate,
|
|
248
|
+
user: generateUserResponseCommonFields({ id: getHumanId() }),
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
handleCommentUpdated.call(feed, otherUserPayload);
|
|
252
|
+
|
|
253
|
+
expect((feed as any).stateUpdateQueue).toEqual(new Set());
|
|
254
|
+
|
|
255
|
+
handleCommentUpdated.call(feed, otherUserPayload);
|
|
256
|
+
|
|
257
|
+
const comments =
|
|
258
|
+
feed.currentState.comments_by_entity_id[activityId]?.comments;
|
|
259
|
+
const [latestComment, previousComment] = (comments ?? []).toReversed();
|
|
260
|
+
|
|
261
|
+
expect((feed as any).stateUpdateQueue).toEqual(new Set());
|
|
262
|
+
expect(comments?.length).toEqual(2);
|
|
263
|
+
expect(latestComment).toMatchObject(otherCommentUpdate);
|
|
264
|
+
expect(previousComment).toMatchObject(existingComment);
|
|
265
|
+
});
|
|
266
|
+
});
|
|
131
267
|
});
|
|
@@ -1,13 +1,38 @@
|
|
|
1
|
-
import { Feed } from '
|
|
2
|
-
import { EventPayload } from '../../../types-internal';
|
|
1
|
+
import { Feed } from '../../feed';
|
|
2
|
+
import { EventPayload, type PartializeAllBut } from '../../../types-internal';
|
|
3
|
+
import { getStateUpdateQueueId, shouldUpdateState } from '../../../utils';
|
|
4
|
+
import {
|
|
5
|
+
eventTriggeredByConnectedUser
|
|
6
|
+
} from '../../../utils/event-triggered-by-connected-user';
|
|
7
|
+
|
|
8
|
+
export type CommentUpdatedPayload = PartializeAllBut<
|
|
9
|
+
EventPayload<'feeds.comment.updated'>,
|
|
10
|
+
'comment'
|
|
11
|
+
>;
|
|
3
12
|
|
|
4
13
|
export function handleCommentUpdated(
|
|
5
14
|
this: Feed,
|
|
6
|
-
|
|
15
|
+
payload: CommentUpdatedPayload,
|
|
16
|
+
fromWs?: boolean,
|
|
7
17
|
) {
|
|
8
|
-
const { comment } =
|
|
18
|
+
const { comment } = payload;
|
|
9
19
|
const entityId = comment.parent_id ?? comment.object_id;
|
|
10
20
|
|
|
21
|
+
if (
|
|
22
|
+
!shouldUpdateState({
|
|
23
|
+
stateUpdateQueueId: getStateUpdateQueueId(
|
|
24
|
+
payload,
|
|
25
|
+
'comment-updated',
|
|
26
|
+
),
|
|
27
|
+
stateUpdateQueue: this.stateUpdateQueue,
|
|
28
|
+
watch: this.currentState.watch,
|
|
29
|
+
fromWs,
|
|
30
|
+
isTriggeredByConnectedUser: eventTriggeredByConnectedUser.call(this, payload),
|
|
31
|
+
})
|
|
32
|
+
) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
11
36
|
this.state.next((currentState) => {
|
|
12
37
|
const entityState = currentState.comments_by_entity_id[entityId];
|
|
13
38
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export * from './handle-comment-added';
|
|
2
2
|
export * from './handle-comment-deleted';
|
|
3
3
|
export * from './handle-comment-updated';
|
|
4
|
-
export * from './handle-comment-reaction';
|
|
4
|
+
export * from './handle-comment-reaction-added';
|
|
5
|
+
export * from './handle-comment-reaction-deleted';
|
|
6
|
+
// export * from './handle-comment-reaction';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './update-comment-count';
|