@stream-io/feeds-client 0.2.17 → 0.2.19

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