@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,82 +0,0 @@
1
- import { beforeEach, describe, expect, it } from 'vitest';
2
- import { Feed } from '../../../feed';
3
- import { FeedsClient } from '../../../feeds-client';
4
- import { handleFeedMemberRemoved } from './handle-feed-member-removed';
5
- import {
6
- generateFeedResponse,
7
- generateFeedMemberRemovedEvent,
8
- generateFeedMemberResponse,
9
- generateOwnUser,
10
- getHumanId,
11
- } from '../../../test-utils/response-generators';
12
-
13
- describe(handleFeedMemberRemoved.name, () => {
14
- let feed: Feed;
15
- let client: FeedsClient;
16
- let currentUserId: string;
17
-
18
- beforeEach(() => {
19
- client = new FeedsClient('mock-api-key');
20
- currentUserId = getHumanId();
21
- client.state.partialNext({
22
- connected_user: generateOwnUser({ id: currentUserId }),
23
- });
24
- const feedResponse = generateFeedResponse({
25
- id: 'main',
26
- group_id: 'user',
27
- created_by: { id: currentUserId },
28
- });
29
- feed = new Feed(
30
- client,
31
- feedResponse.group_id,
32
- feedResponse.id,
33
- feedResponse,
34
- );
35
- });
36
-
37
- it('removes from members if present', () => {
38
- const memberToRemove = generateFeedMemberResponse();
39
- const memberToKeep = generateFeedMemberResponse();
40
- feed.state.partialNext({ members: [memberToRemove, memberToKeep] });
41
-
42
- const event = generateFeedMemberRemovedEvent({
43
- member_id: memberToRemove.user.id,
44
- });
45
-
46
- handleFeedMemberRemoved.call(feed, event);
47
-
48
- const stateAfter = feed.currentState;
49
- expect(stateAfter.members).toHaveLength(1);
50
- expect(stateAfter.members?.[0]).toBe(memberToKeep);
51
- });
52
-
53
- it('does not change state when members array is undefined', () => {
54
- const event = generateFeedMemberRemovedEvent();
55
-
56
- const stateBefore = feed.currentState;
57
- expect(stateBefore.members).toBeUndefined();
58
-
59
- handleFeedMemberRemoved.call(feed, event);
60
-
61
- const stateAfter = feed.currentState;
62
- expect(stateAfter).toBe(stateBefore);
63
- });
64
-
65
- it('deletes own_membership when the removed member is the connected user', () => {
66
- const ownMember = generateFeedMemberResponse({
67
- user: { id: currentUserId },
68
- });
69
- feed.state.partialNext({ own_membership: ownMember, members: [] });
70
-
71
- const event = generateFeedMemberRemovedEvent({ member_id: currentUserId });
72
-
73
- const stateBefore = feed.currentState;
74
- expect(stateBefore.own_membership).toBe(ownMember);
75
-
76
- handleFeedMemberRemoved.call(feed, event);
77
-
78
- const stateAfter = feed.currentState;
79
- expect(stateAfter.own_membership).toBeUndefined();
80
- expect(stateAfter.members).toBe(stateBefore.members);
81
- });
82
- });
@@ -1,84 +0,0 @@
1
- import { beforeEach, describe, expect, it } from 'vitest';
2
- import { Feed } from '../../../feed';
3
- import { FeedsClient } from '../../../feeds-client';
4
- import { handleFeedMemberUpdated } from './handle-feed-member-updated';
5
- import {
6
- generateFeedResponse,
7
- generateFeedMemberUpdatedEvent,
8
- generateFeedMemberResponse,
9
- generateOwnUser,
10
- getHumanId,
11
- } from '../../../test-utils/response-generators';
12
-
13
- describe(handleFeedMemberUpdated.name, () => {
14
- let feed: Feed;
15
- let client: FeedsClient;
16
- let currentUserId: string;
17
-
18
- beforeEach(() => {
19
- client = new FeedsClient('mock-api-key');
20
- currentUserId = getHumanId();
21
- client.state.partialNext({
22
- connected_user: generateOwnUser({ id: currentUserId }),
23
- });
24
- const feedResponse = generateFeedResponse({
25
- id: 'main',
26
- group_id: 'user',
27
- created_by: { id: currentUserId },
28
- });
29
- feed = new Feed(
30
- client,
31
- feedResponse.group_id,
32
- feedResponse.id,
33
- feedResponse,
34
- );
35
- });
36
-
37
- it('updates an existing member in members array', () => {
38
- const member1 = generateFeedMemberResponse();
39
- const member2 = generateFeedMemberResponse();
40
- feed.state.partialNext({ members: [member1, member2] });
41
-
42
- const event = generateFeedMemberUpdatedEvent({
43
- member: { user: { id: member1.user.id }, role: 'admin' },
44
- });
45
-
46
- handleFeedMemberUpdated.call(feed, event);
47
-
48
- const stateAfter = feed.currentState;
49
- expect(stateAfter.members).toHaveLength(2);
50
- expect(stateAfter.members?.[0]).toBe(event.member);
51
- expect(stateAfter.members?.[1]).toBe(member2);
52
- });
53
-
54
- it('does not modify members when target is not present', () => {
55
- const existingMember = generateFeedMemberResponse();
56
- feed.state.partialNext({ members: [existingMember] });
57
-
58
- const event = generateFeedMemberUpdatedEvent();
59
-
60
- const stateBefore = feed.currentState;
61
- handleFeedMemberUpdated.call(feed, event);
62
-
63
- const stateAfter = feed.currentState;
64
- expect(stateAfter).toBe(stateBefore);
65
- });
66
-
67
- it('sets own_membership when the updated member is the connected user', () => {
68
- const event = generateFeedMemberUpdatedEvent({
69
- member: {
70
- user: { id: currentUserId },
71
- role: 'owner',
72
- status: 'member',
73
- },
74
- });
75
-
76
- const stateBefore = feed.currentState;
77
- expect(stateBefore.own_membership).toBeUndefined();
78
-
79
- handleFeedMemberUpdated.call(feed, event);
80
-
81
- const stateAfter = feed.currentState;
82
- expect(stateAfter.own_membership).toBe(event.member);
83
- });
84
- });
@@ -1,219 +0,0 @@
1
- import { beforeEach, describe, expect, it } from 'vitest';
2
- import type { FollowResponse } from '../../../gen/models';
3
- import { shouldUpdateState } from '../../../utils/state-update-queue';
4
- import { handleFollowUpdated } from './handle-follow-updated';
5
- import { Feed, handleFollowCreated, handleFollowDeleted } from '../../../feed';
6
- import { FeedsClient } from '../../../feeds-client';
7
- import {
8
- generateFeedResponse,
9
- generateFollowResponse,
10
- generateOwnUser,
11
- getHumanId,
12
- } from '../../../test-utils';
13
-
14
- describe(`Follow ${shouldUpdateState.name} integration`, () => {
15
- let feed: Feed;
16
- let client: FeedsClient;
17
- let follow: FollowResponse;
18
- let otherFollow: FollowResponse;
19
- let ownFollow: FollowResponse;
20
- let userId: string;
21
-
22
- beforeEach(() => {
23
- userId = getHumanId();
24
- client = new FeedsClient('mock-api-key');
25
-
26
- client.state.partialNext({
27
- connected_user: generateOwnUser({ id: userId }),
28
- });
29
-
30
- const feedResponse = generateFeedResponse({
31
- id: 'main',
32
- group_id: 'user',
33
- created_by: { id: userId },
34
- });
35
- feed = new Feed(client, 'user', 'main', feedResponse);
36
- // Setup follows
37
- follow = generateFollowResponse({
38
- source_feed: generateFeedResponse({
39
- id: 'main',
40
- group_id: 'user',
41
- created_by: { id: userId },
42
- }),
43
- target_feed: generateFeedResponse({
44
- id: 'target',
45
- group_id: 'user',
46
- }),
47
- });
48
-
49
- otherFollow = generateFollowResponse({
50
- source_feed: generateFeedResponse({
51
- id: 'other',
52
- group_id: 'user',
53
- created_by: { id: getHumanId() },
54
- }),
55
- target_feed: generateFeedResponse({
56
- id: 'main',
57
- group_id: 'user',
58
- }),
59
- });
60
-
61
- ownFollow = generateFollowResponse({
62
- source_feed: generateFeedResponse({
63
- id: 'other',
64
- group_id: 'user',
65
- created_by: { id: userId },
66
- }),
67
- target_feed: generateFeedResponse({
68
- id: 'main',
69
- group_id: 'user',
70
- }),
71
- });
72
- // Set up initial state
73
- feed.state.next((currentState) => ({
74
- ...currentState,
75
- following: [follow],
76
- followers: [otherFollow],
77
- own_follows: [ownFollow],
78
- }));
79
- });
80
-
81
- describe.each(
82
- [
83
- {
84
- handler: handleFollowCreated,
85
- state: {
86
- status: 'accepted',
87
- },
88
- count: 2,
89
- },
90
- {
91
- handler: handleFollowUpdated,
92
- state: {
93
- status: 'accepted',
94
- },
95
- count: 1,
96
- },
97
- { handler: handleFollowDeleted, state: undefined, count: 0 },
98
- ].map(({ handler, state, count }) => [
99
- handler.name,
100
- handler,
101
- state as Partial<FollowResponse>,
102
- count,
103
- ]),
104
- )(`%s`, (_name, handler, state, count) => {
105
- it(`skips update if ${shouldUpdateState.name} returns false`, () => {
106
- // Prepare feed, set as watched
107
- feed.state.partialNext({ watch: true });
108
-
109
- const updatedFollow: FollowResponse = { ...follow, status: 'pending' };
110
-
111
- // 1. HTTP and then WS event
112
-
113
- // Call once as HTTP response to populate the queue
114
- handler.call(
115
- feed,
116
- {
117
- follow: { ...updatedFollow, ...state },
118
- },
119
- false,
120
- );
121
-
122
- // Call again as WS event, should be skipped
123
- let stateBefore = feed.currentState;
124
- handler.call(feed, { follow: updatedFollow });
125
-
126
- // State should not change
127
- let stateAfter = feed.currentState;
128
- expect(stateAfter).toBe(stateBefore);
129
- // @ts-expect-error Using Feed internals for tests only
130
- expect(feed.stateUpdateQueue.size).toEqual(0);
131
-
132
- // 2. WS and then HTTP
133
-
134
- // Call once as WS event to populate the queue
135
- handler.call(
136
- feed,
137
- {
138
- follow: { ...updatedFollow, ...state },
139
- },
140
- );
141
-
142
- // Call again as HTTP response, should be skipped
143
- stateBefore = feed.currentState;
144
- handler.call(feed, { follow: updatedFollow }, false);
145
-
146
- // State should not change
147
- stateAfter = feed.currentState;
148
- expect(stateAfter).toBe(stateBefore);
149
- // @ts-expect-error Using Feed internals for tests only
150
- expect(feed.stateUpdateQueue.size).toEqual(0);
151
- });
152
-
153
- it('allows update again from WS after clearing the stateUpdateQueue', () => {
154
- const updatedFollow: FollowResponse = { ...follow, status: 'pending' };
155
-
156
- handler.call(feed, { follow: updatedFollow });
157
-
158
- // Clear the queue
159
- (feed as any).stateUpdateQueue.clear();
160
-
161
- // Now update should be allowed from another WS event
162
- handler.call(feed, {
163
- follow: { ...updatedFollow, ...state },
164
- });
165
-
166
- const following = feed.currentState.following!;
167
- const [updatedFollowAfter] = following;
168
-
169
- expect(following.length).toEqual(count);
170
- expect(updatedFollowAfter).toMatchObject(state!);
171
- });
172
-
173
- it('allows update again from HTTP response after clearing the stateUpdateQueue', () => {
174
- const updatedFollow: FollowResponse = { ...follow, status: 'pending' };
175
-
176
- handler.call(feed, { follow: updatedFollow }, false);
177
-
178
- // Clear the queue
179
- (feed as any).stateUpdateQueue.clear();
180
-
181
- // Now update should be allowed from another HTTP response
182
- handler.call(
183
- feed,
184
- {
185
- follow: { ...updatedFollow, ...state },
186
- },
187
- false,
188
- );
189
-
190
- const following = feed.currentState.following!;
191
- const [updatedFollowAfter] = following;
192
-
193
- expect(following.length).toEqual(count);
194
- expect(updatedFollowAfter).toMatchObject(state!);
195
- });
196
-
197
- it('should not insert anything into the stateUpdateQueue if the connected_user did not trigger the follow', () => {
198
- const updatedFollow: FollowResponse = {
199
- ...otherFollow,
200
- status: 'pending',
201
- };
202
-
203
- handler.call(feed, { follow: updatedFollow });
204
-
205
- expect((feed as any).stateUpdateQueue).toEqual(new Set());
206
-
207
- handler.call(feed, {
208
- follow: { ...updatedFollow, ...state },
209
- });
210
-
211
- const following = feed.currentState.following!;
212
- const [updatedFollowAfter] = following;
213
-
214
- expect((feed as any).stateUpdateQueue).toEqual(new Set());
215
- expect(following.length).toEqual(1);
216
- expect(updatedFollowAfter).toMatchObject(follow);
217
- });
218
- });
219
- });
@@ -1,250 +0,0 @@
1
- import type {
2
- FeedResponse,
3
- FollowResponse,
4
- UserResponse,
5
- } from '../../../gen/models';
6
- import { generateFollowResponse } from '../../../test-utils';
7
- import { updateStateFollowCreated } from './handle-follow-created';
8
-
9
- import { describe, it, expect, beforeEach } from 'vitest';
10
-
11
- describe('handle-follow-created', () => {
12
- describe(updateStateFollowCreated.name, () => {
13
- let mockFollow: FollowResponse;
14
- let mockFeed: FeedResponse;
15
- let mockUser: UserResponse;
16
-
17
- beforeEach(() => {
18
- mockFollow = generateFollowResponse();
19
- mockFeed = mockFollow.source_feed;
20
- mockUser = mockFeed.created_by;
21
- });
22
-
23
- it('should return unchanged state for non-accepted follows', () => {
24
- const follow: FollowResponse = {
25
- ...mockFollow,
26
- status: 'pending',
27
- };
28
-
29
- // @ts-expect-error - we're not testing the full state here
30
- const currentState: FeedState = {
31
- followers: [],
32
- following: [],
33
- };
34
-
35
- const result = updateStateFollowCreated(
36
- follow,
37
- currentState,
38
- 'user:feed-1',
39
- 'user-1',
40
- );
41
-
42
- expect(result.changed).toBe(false);
43
- });
44
-
45
- it('should handle when this feed follows someone', () => {
46
- const follow: FollowResponse = {
47
- ...mockFollow,
48
- source_feed: {
49
- ...mockFeed,
50
- id: 'feed-x',
51
- feed: 'user:feed-x',
52
- created_by: {
53
- ...mockUser,
54
- id: 'user-x',
55
- },
56
- following_count: 1,
57
- },
58
- target_feed: {
59
- ...mockFeed,
60
- id: 'other-feed',
61
- feed: 'user:other-feed',
62
- created_by: mockUser,
63
- },
64
- };
65
-
66
- // @ts-expect-error - we're not testing the full state here
67
- const currentState: FeedState = {
68
- following: [],
69
- following_count: 0,
70
- };
71
-
72
- const result = updateStateFollowCreated(
73
- follow,
74
- currentState,
75
- 'user:feed-x',
76
- 'user-1',
77
- );
78
-
79
- expect(result.changed).toBe(true);
80
- expect(result.data.following).toHaveLength(1);
81
- expect(result.data.following?.[0]).toBe(follow);
82
- expect(result.data).toMatchObject(follow.source_feed);
83
- expect(result.data.own_follows).toBeUndefined();
84
- expect(result.data.following_count).toBe(1);
85
- });
86
-
87
- it('should handle when someone follows this feed', () => {
88
- const follow: FollowResponse = {
89
- ...mockFollow,
90
- source_feed: {
91
- ...mockFeed,
92
- id: 'other-feed',
93
- feed: 'user:other-feed',
94
- created_by: {
95
- ...mockUser,
96
- id: 'other-user',
97
- },
98
- },
99
- target_feed: {
100
- ...mockFeed,
101
- id: 'feed-1',
102
- feed: 'user:feed-1',
103
- created_by: mockUser,
104
- follower_count: 1,
105
- },
106
- };
107
-
108
- // @ts-expect-error - we're not testing the full state here
109
- const currentState: FeedState = {
110
- followers: [],
111
- follower_count: 0,
112
- };
113
-
114
- const result = updateStateFollowCreated(
115
- follow,
116
- currentState,
117
- 'user:feed-1',
118
- 'user-1',
119
- );
120
-
121
- expect(result.changed).toBe(true);
122
- expect(result.data.followers).toHaveLength(1);
123
- expect(result.data.followers?.[0]).toBe(follow);
124
- expect(result.data).toMatchObject(follow.target_feed);
125
- expect(result.data.own_follows).toBeUndefined();
126
- expect(result.data.follower_count).toBe(1);
127
- });
128
-
129
- it('should add to own_follows when connected user is the source', () => {
130
- const follow: FollowResponse = {
131
- ...mockFollow,
132
- source_feed: {
133
- ...mockFeed,
134
- id: 'feed-1',
135
- feed: 'user:feed-1',
136
- created_by: { ...mockUser, id: 'user-1' },
137
- },
138
- target_feed: {
139
- ...mockFeed,
140
- id: 'feed-x',
141
- feed: 'user:feed-x',
142
- created_by: {
143
- ...mockUser,
144
- id: 'user-x',
145
- },
146
- },
147
- };
148
-
149
- // @ts-expect-error - we're not testing the full state here
150
- const currentState: FeedState = {
151
- followers: [],
152
- own_follows: [],
153
- };
154
-
155
- const result = updateStateFollowCreated(
156
- follow,
157
- currentState,
158
- 'user:feed-x',
159
- 'user-1',
160
- );
161
-
162
- expect(result.changed).toBe(true);
163
- expect(result.data.own_follows).toHaveLength(1);
164
- expect(result.data.own_follows?.[0]).toBe(follow);
165
- });
166
-
167
- it('should not update followers/following when they are undefined', () => {
168
- const follow: FollowResponse = {
169
- ...mockFollow,
170
- source_feed: {
171
- ...mockFeed,
172
- id: 'other-feed',
173
- feed: 'user:other-feed',
174
- created_by: mockUser,
175
- },
176
- target_feed: {
177
- ...mockFeed,
178
- id: 'feed-1',
179
- feed: 'user:feed-1',
180
- created_by: mockUser,
181
- },
182
- };
183
-
184
- // @ts-expect-error - we're not testing the full state here
185
- const currentState: FeedState = {
186
- followers: undefined,
187
- following: undefined,
188
- own_follows: undefined,
189
- };
190
-
191
- const result = updateStateFollowCreated(
192
- follow,
193
- currentState,
194
- 'user:feed-1',
195
- 'user-1',
196
- );
197
-
198
- expect(result.changed).toBe(true);
199
- expect(result.data.followers).toBeUndefined();
200
- expect(result.data).toMatchObject(follow.target_feed);
201
- });
202
-
203
- it('should add new followers to the top of existing arrays', () => {
204
- const existingFollow: FollowResponse = {
205
- ...mockFollow,
206
- source_feed: {
207
- ...mockFeed,
208
- id: 'existing-feed',
209
- feed: 'user:existing-feed',
210
- created_by: mockUser,
211
- },
212
- };
213
-
214
- const follow: FollowResponse = {
215
- ...mockFollow,
216
- source_feed: {
217
- ...mockFeed,
218
- id: 'other-feed',
219
- feed: 'user:other-feed',
220
- created_by: mockUser,
221
- },
222
- target_feed: {
223
- ...mockFeed,
224
- id: 'feed-1',
225
- feed: 'user:feed-1',
226
- created_by: mockUser,
227
- },
228
- };
229
-
230
- // @ts-expect-error - we're not testing the full state here
231
- const currentState: FeedState = {
232
- followers: [existingFollow],
233
- following: undefined,
234
- own_follows: undefined,
235
- };
236
-
237
- const result = updateStateFollowCreated(
238
- follow,
239
- currentState,
240
- 'user:feed-1',
241
- 'user-1',
242
- );
243
-
244
- expect(result.changed).toBe(true);
245
- expect(result.data.followers).toHaveLength(2);
246
- expect(result.data.followers?.[0]).toBe(follow);
247
- expect(result.data.followers?.[1]).toBe(existingFollow);
248
- });
249
- });
250
- });