@stream-io/feeds-client 0.2.18 → 0.2.20

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