@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,188 +0,0 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import { Feed } from '../../../feed';
3
- import { FeedsClient } from '../../../feeds-client';
4
- import { handleBookmarkDeleted } from './handle-bookmark-deleted';
5
- import {
6
- generateActivityPinResponse,
7
- generateActivityResponse,
8
- generateFeedResponse,
9
- generateOwnUser,
10
- getHumanId,
11
- generateFeedReactionResponse,
12
- generateBookmarkDeletedEvent,
13
- generateBookmarkResponse,
14
- } from '../../../test-utils/response-generators';
15
-
16
- describe(handleBookmarkDeleted.name, () => {
17
- let feed: Feed;
18
- let client: FeedsClient;
19
- let currentUserId: string;
20
-
21
- beforeEach(() => {
22
- client = new FeedsClient('mock-api-key');
23
- currentUserId = getHumanId();
24
- client.state.partialNext({
25
- connected_user: generateOwnUser({ id: currentUserId }),
26
- });
27
- const feedResponse = generateFeedResponse({
28
- id: 'main',
29
- group_id: 'user',
30
- created_by: { id: currentUserId },
31
- });
32
- feed = new Feed(
33
- client,
34
- feedResponse.group_id,
35
- feedResponse.id,
36
- feedResponse,
37
- );
38
- });
39
-
40
- it('removes a bookmark for the current user and updates activities', () => {
41
- const event = generateBookmarkDeletedEvent({
42
- bookmark: {
43
- activity: {
44
- own_reactions: [],
45
- bookmark_count: 0,
46
- },
47
- user: { id: currentUserId },
48
- },
49
- });
50
- const activity = generateActivityResponse({
51
- id: event.bookmark.activity.id,
52
- bookmark_count: 1,
53
- own_bookmarks: [
54
- generateBookmarkResponse({
55
- activity: { id: event.bookmark.activity.id },
56
- user: { id: currentUserId },
57
- }),
58
- ],
59
- own_reactions: [generateFeedReactionResponse()],
60
- });
61
- const activityPin = generateActivityPinResponse({
62
- activity: { ...activity },
63
- });
64
- feed.state.partialNext({
65
- activities: [activity],
66
- pinned_activities: [activityPin],
67
- });
68
-
69
- const stateBefore = feed.currentState;
70
- expect(stateBefore.activities![0].own_bookmarks).toHaveLength(1);
71
- expect(
72
- stateBefore.pinned_activities![0].activity.own_bookmarks,
73
- ).toHaveLength(1);
74
- expect(stateBefore.activities![0].bookmark_count).toBe(1);
75
- expect(stateBefore.pinned_activities![0].activity.bookmark_count).toBe(
76
- 1,
77
- );
78
-
79
- handleBookmarkDeleted.call(feed, event);
80
-
81
- const stateAfter = feed.currentState;
82
- expect(stateAfter.activities![0].own_bookmarks).toHaveLength(0);
83
- expect(
84
- stateAfter.pinned_activities![0].activity.own_bookmarks,
85
- ).toHaveLength(0);
86
- expect(stateAfter.activities![0].own_reactions).toBe(
87
- stateBefore.activities![0].own_reactions,
88
- );
89
- expect(stateAfter.pinned_activities![0].activity.own_reactions).toBe(
90
- stateBefore.pinned_activities![0].activity.own_reactions,
91
- );
92
- expect(stateAfter.activities![0].bookmark_count).toBe(0);
93
- expect(stateAfter.pinned_activities![0].activity.bookmark_count).toBe(0);
94
- });
95
-
96
- it('does not remove from own_bookmarks if bookmark is from another user but still updates activity', () => {
97
- const event = generateBookmarkDeletedEvent({
98
- bookmark: {
99
- activity: {
100
- own_reactions: [],
101
- bookmark_count: 0,
102
- },
103
- user: { id: 'other-user-id' },
104
- },
105
- });
106
- const activity = generateActivityResponse({
107
- id: event.bookmark.activity.id,
108
- bookmark_count: 1,
109
- own_bookmarks: [
110
- generateBookmarkResponse({
111
- activity: { id: event.bookmark.activity.id },
112
- user: { id: currentUserId },
113
- }),
114
- ],
115
- own_reactions: [generateFeedReactionResponse()],
116
- });
117
- const activityPin = generateActivityPinResponse({
118
- activity: { ...activity },
119
- });
120
- feed.state.partialNext({
121
- activities: [activity],
122
- pinned_activities: [activityPin],
123
- });
124
-
125
- const stateBefore = feed.currentState;
126
- expect(stateBefore.activities![0].own_bookmarks).toHaveLength(1);
127
- expect(
128
- stateBefore.pinned_activities![0].activity.own_bookmarks,
129
- ).toHaveLength(1);
130
- expect(stateBefore.activities![0].bookmark_count).toBe(1);
131
- expect(stateBefore.pinned_activities![0].activity.bookmark_count).toBe(
132
- 1,
133
- );
134
-
135
- handleBookmarkDeleted.call(feed, event);
136
-
137
- const stateAfter = feed.currentState;
138
- expect(stateAfter.activities![0].own_bookmarks).toBe(
139
- stateBefore.activities![0].own_bookmarks,
140
- );
141
- expect(stateAfter.pinned_activities![0].activity.own_bookmarks).toBe(
142
- stateBefore.pinned_activities![0].activity.own_bookmarks,
143
- );
144
- expect(stateAfter.activities![0].own_reactions).toBe(
145
- stateBefore.activities![0].own_reactions,
146
- );
147
- expect(stateAfter.pinned_activities![0].activity.own_reactions).toBe(
148
- stateBefore.pinned_activities![0].activity.own_reactions,
149
- );
150
- expect(stateAfter.activities![0].bookmark_count).toBe(0);
151
- expect(stateAfter.pinned_activities![0].activity.bookmark_count).toBe(0);
152
- });
153
-
154
- it('does nothing if activity is not found', () => {
155
- const event = generateBookmarkDeletedEvent({
156
- bookmark: {
157
- activity: {
158
- own_reactions: [],
159
- bookmark_count: 0,
160
- },
161
- user: { id: currentUserId },
162
- },
163
- });
164
- const activity = generateActivityResponse({
165
- id: 'another-activity-id',
166
- bookmark_count: 1,
167
- own_bookmarks: [
168
- generateBookmarkResponse({
169
- activity: { id: 'another-activity-id', },
170
- user: { id: currentUserId },
171
- }),
172
- ],
173
- own_reactions: [generateFeedReactionResponse()],
174
- });
175
- const activityPin = generateActivityPinResponse({
176
- activity: { ...activity },
177
- });
178
- feed.state.partialNext({
179
- activities: [activity],
180
- pinned_activities: [activityPin],
181
- });
182
-
183
- const stateBefore = feed.currentState;
184
- handleBookmarkDeleted.call(feed, event);
185
- const stateAfter = feed.currentState;
186
- expect(stateAfter).toBe(stateBefore);
187
- });
188
- });
@@ -1,196 +0,0 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import { Feed } from '../../../feed';
3
- import { FeedsClient } from '../../../feeds-client';
4
- import { handleBookmarkUpdated } from './handle-bookmark-updated';
5
- import {
6
- generateActivityPinResponse,
7
- generateActivityResponse,
8
- generateFeedResponse,
9
- generateOwnUser,
10
- getHumanId,
11
- generateFeedReactionResponse,
12
- generateBookmarkUpdatedEvent,
13
- generateBookmarkResponse,
14
- } from '../../../test-utils/response-generators';
15
-
16
- describe(handleBookmarkUpdated.name, () => {
17
- let feed: Feed;
18
- let client: FeedsClient;
19
- let currentUserId: string;
20
-
21
- beforeEach(() => {
22
- client = new FeedsClient('mock-api-key');
23
- currentUserId = getHumanId();
24
- client.state.partialNext({
25
- connected_user: generateOwnUser({ id: currentUserId }),
26
- });
27
- const feedResponse = generateFeedResponse({
28
- id: 'main',
29
- group_id: 'user',
30
- created_by: { id: currentUserId },
31
- });
32
- feed = new Feed(
33
- client,
34
- feedResponse.group_id,
35
- feedResponse.id,
36
- feedResponse,
37
- );
38
- });
39
-
40
- it('updates a bookmark for the current user and updates activities', () => {
41
- const event = generateBookmarkUpdatedEvent({
42
- bookmark: {
43
- activity: {
44
- own_reactions: [],
45
- bookmark_count: 1,
46
- },
47
- user: { id: currentUserId },
48
- updated_at: new Date('2025-08-06T12:00:00Z'),
49
- },
50
- });
51
- const activity = generateActivityResponse({
52
- id: event.bookmark.activity.id,
53
- bookmark_count: 1,
54
- own_bookmarks: [
55
- generateBookmarkResponse({
56
- activity: { id: event.bookmark.activity.id },
57
- user: { id: currentUserId },
58
- updated_at: new Date('2025-08-05T12:00:00Z'),
59
- }),
60
- ],
61
- own_reactions: [generateFeedReactionResponse()],
62
- });
63
- const activityPin = generateActivityPinResponse({
64
- activity: { ...activity },
65
- });
66
- feed.state.partialNext({
67
- activities: [activity],
68
- pinned_activities: [activityPin],
69
- });
70
-
71
- const stateBefore = feed.currentState;
72
- expect(stateBefore.activities![0].own_bookmarks).toHaveLength(1);
73
- expect(
74
- stateBefore.pinned_activities![0].activity.own_bookmarks,
75
- ).toHaveLength(1);
76
- expect(stateBefore.activities![0].own_bookmarks[0].updated_at).not.toBe(
77
- event.bookmark.updated_at,
78
- );
79
- expect(
80
- stateBefore.pinned_activities![0].activity.own_bookmarks[0].updated_at,
81
- ).not.toBe(event.bookmark.updated_at);
82
-
83
- handleBookmarkUpdated.call(feed, event);
84
-
85
- const stateAfter = feed.currentState;
86
- expect(stateAfter.activities![0].own_bookmarks).toHaveLength(1);
87
- expect(stateAfter.pinned_activities![0].activity.own_bookmarks).toHaveLength(1);
88
- expect(stateAfter.activities![0].own_bookmarks[0]).toBe(event.bookmark);
89
- expect(stateAfter.pinned_activities![0].activity.own_bookmarks[0]).toBe(
90
- event.bookmark,
91
- );
92
- expect(stateAfter.activities![0].own_reactions).toBe(
93
- stateBefore.activities![0].own_reactions,
94
- );
95
- expect(stateAfter.pinned_activities![0].activity.own_reactions).toBe(
96
- stateBefore.pinned_activities![0].activity.own_reactions,
97
- );
98
- expect(stateAfter.activities![0].bookmark_count).toBe(1);
99
- expect(stateAfter.pinned_activities![0].activity.bookmark_count).toBe(1);
100
- });
101
-
102
- it('does not update own_bookmarks if bookmark is from another user but still updates activity', () => {
103
- const event = generateBookmarkUpdatedEvent({
104
- bookmark: {
105
- activity: {
106
- own_reactions: [],
107
- bookmark_count: 2,
108
- },
109
- user: { id: 'other-user-id' },
110
- updated_at: new Date('2025-08-06T12:00:00Z'),
111
- },
112
- });
113
- const activity = generateActivityResponse({
114
- id: event.bookmark.activity.id,
115
- bookmark_count: 1,
116
- own_bookmarks: [
117
- generateBookmarkResponse({
118
- activity: { id: event.bookmark.activity.id },
119
- user: { id: currentUserId },
120
- updated_at: new Date('2025-08-05T12:00:00Z'),
121
- }),
122
- ],
123
- own_reactions: [generateFeedReactionResponse()],
124
- });
125
- const activityPin = generateActivityPinResponse({
126
- activity: { ...activity },
127
- });
128
- feed.state.partialNext({
129
- activities: [activity],
130
- pinned_activities: [activityPin],
131
- });
132
-
133
- const stateBefore = feed.currentState;
134
- expect(stateBefore.activities![0].own_bookmarks).toHaveLength(1);
135
- expect(
136
- stateBefore.pinned_activities![0].activity.own_bookmarks,
137
- ).toHaveLength(1);
138
- expect(stateBefore.activities![0].bookmark_count).toBe(1);
139
- expect(stateBefore.pinned_activities![0].activity.bookmark_count).toBe(
140
- 1,
141
- );
142
-
143
- handleBookmarkUpdated.call(feed, event);
144
-
145
- const stateAfter = feed.currentState;
146
- expect(stateAfter.activities![0].own_bookmarks).toBe(
147
- stateBefore.activities![0].own_bookmarks,
148
- );
149
- expect(stateAfter.pinned_activities![0].activity.own_bookmarks).toBe(
150
- stateBefore.pinned_activities![0].activity.own_bookmarks,
151
- );
152
- expect(stateAfter.activities![0].own_reactions).toBe(
153
- stateBefore.activities![0].own_reactions,
154
- );
155
- expect(stateAfter.pinned_activities![0].activity.own_reactions).toBe(
156
- stateBefore.pinned_activities![0].activity.own_reactions,
157
- );
158
- expect(stateAfter.activities![0].bookmark_count).toBe(2);
159
- expect(stateAfter.pinned_activities![0].activity.bookmark_count).toBe(2);
160
- });
161
-
162
- it('does nothing if activity is not found', () => {
163
- const event = generateBookmarkUpdatedEvent({
164
- bookmark: {
165
- activity: {
166
- own_reactions: [],
167
- bookmark_count: 1,
168
- },
169
- user: { id: currentUserId },
170
- },
171
- });
172
- const activity = generateActivityResponse({
173
- id: 'unrelated-activity-id',
174
- bookmark_count: 1,
175
- own_bookmarks: [
176
- generateBookmarkResponse({
177
- activity: { id: 'unrelated-activity-id' },
178
- user: { id: currentUserId },
179
- }),
180
- ],
181
- own_reactions: [generateFeedReactionResponse()],
182
- });
183
- const activityPin = generateActivityPinResponse({
184
- activity: { ...activity },
185
- });
186
- feed.state.partialNext({
187
- activities: [activity],
188
- pinned_activities: [activityPin],
189
- });
190
-
191
- const stateBefore = feed.currentState;
192
- handleBookmarkUpdated.call(feed, event);
193
- const stateAfter = feed.currentState;
194
- expect(stateAfter).toBe(stateBefore);
195
- });
196
- });
@@ -1,271 +0,0 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import { Feed } from '../../../feed';
3
- import { FeedsClient } from '../../../feeds-client';
4
- import { handleCommentAdded } from './handle-comment-added';
5
- import {
6
- generateFeedResponse,
7
- generateOwnUser,
8
- getHumanId,
9
- generateCommentAddedEvent,
10
- generateCommentResponse,
11
- generateUserResponseCommonFields,
12
- } from '../../../test-utils';
13
- import type { CommentResponse, UserResponseCommonFields } from '../../../gen/models';
14
- import type { EventPayload } from '../../../types-internal';
15
- import { shouldUpdateState } from '../../../utils';
16
-
17
- describe(handleCommentAdded.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('appends a new comment when pagination.sort is not "last"', () => {
44
- const existingComment = generateCommentResponse({
45
- object_id: activityId,
46
- });
47
-
48
- feed.state.partialNext({
49
- comments_by_entity_id: {
50
- [activityId]: {
51
- comments: [existingComment],
52
- pagination: { sort: 'first' },
53
- },
54
- },
55
- });
56
-
57
- const event = generateCommentAddedEvent({
58
- comment: { object_id: activityId },
59
- });
60
-
61
- const stateBefore = feed.currentState;
62
- handleCommentAdded.call(feed, event);
63
- const stateAfter = feed.currentState;
64
-
65
- expect(stateAfter.comments_by_entity_id[activityId]!.comments).not.toBe(
66
- stateBefore.comments_by_entity_id[activityId]!.comments,
67
- );
68
- expect(stateAfter.comments_by_entity_id[activityId]!.comments).toHaveLength(
69
- 2,
70
- );
71
- expect(stateAfter.comments_by_entity_id[activityId]!.comments!.at(-1)).toBe(
72
- event.comment,
73
- );
74
- });
75
-
76
- it('prepends a new comment when pagination.sort is "last"', () => {
77
- const existingComment = generateCommentResponse({
78
- object_id: activityId,
79
- });
80
-
81
- feed.state.partialNext({
82
- comments_by_entity_id: {
83
- [activityId]: {
84
- comments: [existingComment],
85
- pagination: { sort: 'last' },
86
- },
87
- },
88
- });
89
-
90
- const event = generateCommentAddedEvent({
91
- comment: { object_id: activityId },
92
- });
93
-
94
- const stateBefore = feed.currentState;
95
- handleCommentAdded.call(feed, event);
96
- const stateAfter = feed.currentState;
97
-
98
- expect(stateAfter.comments_by_entity_id[activityId]!.comments).not.toBe(
99
- stateBefore.comments_by_entity_id[activityId]!.comments,
100
- );
101
- expect(stateAfter.comments_by_entity_id[activityId]!.comments).toHaveLength(
102
- 2,
103
- );
104
- expect(stateAfter.comments_by_entity_id[activityId]!.comments!.at(0)).toBe(
105
- event.comment,
106
- );
107
- });
108
-
109
- it('stores the comment in the correct parent entity state (prefers parent_id)', () => {
110
- const parentId = `comment-${getHumanId()}`;
111
- const existingComment = generateCommentResponse({
112
- object_id: activityId,
113
- parent_id: parentId,
114
- });
115
-
116
- feed.state.partialNext({
117
- comments_by_entity_id: {
118
- [parentId]: {
119
- comments: [existingComment],
120
- pagination: { sort: 'best' },
121
- },
122
- },
123
- });
124
-
125
- const event = generateCommentAddedEvent({
126
- comment: { parent_id: parentId, object_id: activityId },
127
- });
128
-
129
- const prevState = feed.currentState;
130
- handleCommentAdded.call(feed, event);
131
- const nextState = feed.currentState;
132
-
133
- expect(nextState.comments_by_entity_id[parentId]!.comments).not.toBe(
134
- prevState.comments_by_entity_id[parentId]!.comments,
135
- );
136
- expect(nextState.comments_by_entity_id[parentId]!.comments).toHaveLength(2);
137
- expect(nextState.comments_by_entity_id[parentId]!.comments!.at(-1)).toBe(
138
- event.comment,
139
- );
140
- });
141
-
142
- it('does nothing if entity state does not exist (comments have not been loaded yet)', () => {
143
- const event = generateCommentAddedEvent({
144
- comment: { object_id: activityId },
145
- });
146
- const stateBefore = feed.currentState;
147
- handleCommentAdded.call(feed, event);
148
- const stateAfter = feed.currentState;
149
- expect(stateAfter).toBe(stateBefore);
150
- });
151
-
152
- describe(`Comment added ${shouldUpdateState.name} integration`, () => {
153
- let currentUserPayload: EventPayload<'feeds.comment.added'>;
154
- let existingComment: CommentResponse;
155
- let newComment: CommentResponse;
156
- let commentId: string;
157
-
158
- beforeEach(() => {
159
- commentId = `comment-${getHumanId()}`;
160
- existingComment = generateCommentResponse({
161
- id: commentId,
162
- object_id: activityId,
163
- });
164
- newComment = generateCommentResponse({
165
- id: `comment-${getHumanId()}`,
166
- object_id: activityId,
167
- });
168
-
169
- currentUserPayload = generateCommentAddedEvent({
170
- comment: newComment,
171
- user: client.state.getLatestValue()
172
- .connected_user as UserResponseCommonFields,
173
- });
174
-
175
- feed.state.partialNext({
176
- comments_by_entity_id: {
177
- [activityId]: {
178
- comments: [existingComment],
179
- pagination: { sort: 'first' },
180
- },
181
- },
182
- });
183
- feed.state.partialNext({ watch: true });
184
- });
185
-
186
- it(`skips update if ${shouldUpdateState.name} returns false`, () => {
187
- // 1. HTTP and then WS
188
-
189
- handleCommentAdded.call(feed, currentUserPayload, false);
190
-
191
- let stateBefore = feed.currentState;
192
-
193
- handleCommentAdded.call(feed, currentUserPayload);
194
-
195
- let stateAfter = feed.currentState;
196
-
197
- expect(stateAfter).toBe(stateBefore);
198
- // @ts-expect-error Using Feed internals for tests only
199
- expect(feed.stateUpdateQueue.size).toEqual(0);
200
-
201
- // 2. WS and the HTTP
202
-
203
- handleCommentAdded.call(feed, currentUserPayload);
204
-
205
- stateBefore = feed.currentState;
206
-
207
- handleCommentAdded.call(feed, currentUserPayload, false);
208
-
209
- stateAfter = feed.currentState;
210
-
211
- expect(stateAfter).toBe(stateBefore);
212
- // @ts-expect-error Using Feed internals for tests only
213
- expect(feed.stateUpdateQueue.size).toEqual(0);
214
- });
215
-
216
- it('allows update again from WS after clearing the stateUpdateQueue', () => {
217
- handleCommentAdded.call(feed, currentUserPayload);
218
-
219
- // Clear the queue
220
- (feed as any).stateUpdateQueue.clear();
221
-
222
- // Now update should be allowed from another WS event
223
- handleCommentAdded.call(feed, currentUserPayload);
224
-
225
- const comments =
226
- feed.currentState.comments_by_entity_id[activityId]?.comments;
227
- const [latestComment] = (comments ?? []).toReversed();
228
-
229
- expect(comments?.length).toEqual(3);
230
- expect(latestComment).toMatchObject(newComment);
231
- });
232
-
233
- it('allows update again from HTTP response after clearing the stateUpdateQueue', () => {
234
- handleCommentAdded.call(feed, currentUserPayload, false);
235
-
236
- // Clear the queue
237
- (feed as any).stateUpdateQueue.clear();
238
-
239
- // Now update should be allowed from another HTTP response
240
- handleCommentAdded.call(feed, currentUserPayload, false);
241
-
242
- const comments =
243
- feed.currentState.comments_by_entity_id[activityId]?.comments;
244
- const [latestComment] = (comments ?? []).toReversed();
245
-
246
- expect(comments?.length).toEqual(3);
247
- expect(latestComment).toMatchObject(newComment);
248
- });
249
-
250
- it('should not insert anything into the stateUpdateQueue if the connected_user did not trigger the comment reaction deletion', () => {
251
- const otherUserPayload = generateCommentAddedEvent({
252
- comment: newComment,
253
- user: generateUserResponseCommonFields({ id: getHumanId() }),
254
- });
255
-
256
- handleCommentAdded.call(feed, otherUserPayload);
257
-
258
- expect((feed as any).stateUpdateQueue).toEqual(new Set());
259
-
260
- handleCommentAdded.call(feed, otherUserPayload);
261
-
262
- const comments =
263
- feed.currentState.comments_by_entity_id[activityId]?.comments;
264
- const [latestComment] = (comments ?? []).toReversed();
265
-
266
- expect((feed as any).stateUpdateQueue).toEqual(new Set());
267
- expect(comments?.length).toEqual(3);
268
- expect(latestComment).toMatchObject(newComment);
269
- });
270
- });
271
- });