@stream-io/feeds-client 0.1.8 → 0.1.9

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 (40) hide show
  1. package/@react-bindings/hooks/util/index.ts +1 -0
  2. package/CHANGELOG.md +15 -0
  3. package/dist/@react-bindings/hooks/util/index.d.ts +1 -0
  4. package/dist/@react-bindings/hooks/util/useBookmarkActions.d.ts +13 -0
  5. package/dist/@react-bindings/hooks/util/useReactionActions.d.ts +1 -1
  6. package/dist/index-react-bindings.browser.cjs +346 -140
  7. package/dist/index-react-bindings.browser.cjs.map +1 -1
  8. package/dist/index-react-bindings.browser.js +346 -141
  9. package/dist/index-react-bindings.browser.js.map +1 -1
  10. package/dist/index-react-bindings.node.cjs +346 -140
  11. package/dist/index-react-bindings.node.cjs.map +1 -1
  12. package/dist/index-react-bindings.node.js +346 -141
  13. package/dist/index-react-bindings.node.js.map +1 -1
  14. package/dist/index.browser.cjs +320 -139
  15. package/dist/index.browser.cjs.map +1 -1
  16. package/dist/index.browser.js +320 -140
  17. package/dist/index.browser.js.map +1 -1
  18. package/dist/index.node.cjs +320 -139
  19. package/dist/index.node.cjs.map +1 -1
  20. package/dist/index.node.js +320 -140
  21. package/dist/index.node.js.map +1 -1
  22. package/dist/src/Feed.d.ts +40 -9
  23. package/dist/src/FeedsClient.d.ts +8 -1
  24. package/dist/src/gen-imports.d.ts +1 -1
  25. package/dist/src/state-updates/follow-utils.d.ts +19 -0
  26. package/dist/src/state-updates/state-update-queue.d.ts +15 -0
  27. package/dist/src/utils.d.ts +1 -0
  28. package/dist/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +1 -1
  30. package/src/Feed.ts +226 -192
  31. package/src/FeedsClient.ts +75 -3
  32. package/src/gen-imports.ts +1 -1
  33. package/src/state-updates/activity-reaction-utils.test.ts +1 -0
  34. package/src/state-updates/activity-utils.test.ts +1 -0
  35. package/src/state-updates/follow-utils.test.ts +552 -0
  36. package/src/state-updates/follow-utils.ts +126 -0
  37. package/src/state-updates/state-update-queue.test.ts +53 -0
  38. package/src/state-updates/state-update-queue.ts +35 -0
  39. package/src/utils.test.ts +175 -0
  40. package/src/utils.ts +20 -0
@@ -3,12 +3,14 @@ import {
3
3
  ActivityResponse,
4
4
  FeedResponse,
5
5
  FileUploadRequest,
6
+ FollowBatchRequest,
6
7
  ImageUploadRequest,
7
8
  OwnUser,
8
9
  PollResponse,
9
10
  PollVotesResponse,
10
11
  QueryFeedsRequest,
11
12
  QueryPollVotesRequest,
13
+ SingleFollowRequest,
12
14
  UserRequest,
13
15
  WSEvent,
14
16
  } from './gen/models';
@@ -97,6 +99,10 @@ export class FeedsClient extends FeedsApi {
97
99
  activeFeed.synchronize();
98
100
  }
99
101
  }
102
+ } else {
103
+ for (const activeFeed of Object.values(this.activeFeeds)) {
104
+ activeFeed.handleWatchStopped();
105
+ }
100
106
  }
101
107
  break;
102
108
  }
@@ -335,7 +341,7 @@ export class FeedsClient extends FeedsApi {
335
341
  const response = await this.feedsQueryFeeds(request);
336
342
 
337
343
  const feeds = response.feeds.map((f) =>
338
- this.getOrCreateActiveFeed(f.group_id, f.id, f),
344
+ this.getOrCreateActiveFeed(f.group_id, f.id, f, request?.watch),
339
345
  );
340
346
 
341
347
  return {
@@ -357,16 +363,82 @@ export class FeedsClient extends FeedsApi {
357
363
  this.eventDispatcher.dispatch(networkEvent);
358
364
  };
359
365
 
366
+ // For follow API endpoints we update the state after HTTP response to allow queryFeeds with watch: false
367
+ async follow(request: SingleFollowRequest) {
368
+ const response = await super.follow(request);
369
+
370
+ [response.follow.source_feed.fid, response.follow.target_feed.fid].forEach(
371
+ (fid) => {
372
+ const feed = this.activeFeeds[fid];
373
+ if (feed) {
374
+ feed.handleFollowCreated(response.follow);
375
+ }
376
+ },
377
+ );
378
+
379
+ return response;
380
+ }
381
+
382
+ async followBatch(request: FollowBatchRequest) {
383
+ const response = await super.followBatch(request);
384
+
385
+ response.follows.forEach((follow) => {
386
+ const feed = this.activeFeeds[follow.source_feed.fid];
387
+ if (feed) {
388
+ feed.handleFollowCreated(follow);
389
+ }
390
+ });
391
+
392
+ return response;
393
+ }
394
+
395
+ async unfollow(request: SingleFollowRequest) {
396
+ const response = await super.unfollow(request);
397
+
398
+ [request.source, request.target].forEach((fid) => {
399
+ const feed = this.activeFeeds[fid];
400
+ if (feed) {
401
+ feed.handleFollowDeleted({
402
+ source_feed: { fid: request.source },
403
+ target_feed: { fid: request.target },
404
+ });
405
+ }
406
+ });
407
+
408
+ return response;
409
+ }
410
+
411
+ async stopWatchingFeed(request: { feed_group_id: string; feed_id: string }) {
412
+ const connectionId = await this.connectionIdManager.getConnectionId();
413
+ const response = await super.stopWatchingFeed({
414
+ ...request,
415
+ connection_id: connectionId,
416
+ });
417
+
418
+ const feed =
419
+ this.activeFeeds[`${request.feed_group_id}:${request.feed_id}`];
420
+ if (feed) {
421
+ feed.handleWatchStopped();
422
+ }
423
+
424
+ return response;
425
+ }
426
+
360
427
  private readonly getOrCreateActiveFeed = (
361
428
  group: string,
362
429
  id: string,
363
430
  data?: FeedResponse,
431
+ watch?: boolean,
364
432
  ) => {
365
433
  const fid = `${group}:${id}`;
366
434
  if (this.activeFeeds[fid]) {
367
- return this.activeFeeds[fid];
435
+ const feed = this.activeFeeds[fid];
436
+ if (watch && !feed.currentState.watch) {
437
+ feed.handleWatchStarted();
438
+ }
439
+ return feed;
368
440
  } else {
369
- const feed = new Feed(this, group, id, data);
441
+ const feed = new Feed(this, group, id, data, watch);
370
442
  this.activeFeeds[fid] = feed;
371
443
  return feed;
372
444
  }
@@ -1,3 +1,3 @@
1
1
  export type { ApiClient } from './common/ApiClient';
2
2
  export type { StreamResponse } from './common/types';
3
- export { FeedsApi } from './gen/feeds/FeedsApi';
3
+ export { FeedsClient as FeedsApi } from './FeedsClient';
@@ -35,6 +35,7 @@ const createMockActivity = (id: string): ActivityResponse => ({
35
35
  search_data: {},
36
36
  popularity: 0,
37
37
  score: 0,
38
+ reaction_count: 0,
38
39
  user: {
39
40
  id: 'user1',
40
41
  created_at: new Date(),
@@ -31,6 +31,7 @@ const createMockActivity = (id: string, text?: string): ActivityResponse =>
31
31
  text: text,
32
32
  popularity: 0,
33
33
  score: 0,
34
+ reaction_count: 0,
34
35
  user: {
35
36
  id: 'user1',
36
37
  created_at: new Date(),
@@ -0,0 +1,552 @@
1
+ import { describe, it, expect } from 'vitest';
2
+
3
+ import {
4
+ handleFollowCreated,
5
+ handleFollowDeleted,
6
+ handleFollowUpdated,
7
+ } from './follow-utils';
8
+ import { FollowResponse, FeedResponse, UserResponse } from '../gen/models';
9
+ import { FeedState } from '../Feed';
10
+
11
+ describe('follow-utils', () => {
12
+ const mockUser: UserResponse = {
13
+ id: 'user-1',
14
+ created_at: new Date(),
15
+ updated_at: new Date(),
16
+ banned: false,
17
+ language: 'en',
18
+ online: false,
19
+ role: 'user',
20
+ blocked_user_ids: [],
21
+ teams: [],
22
+ custom: {},
23
+ };
24
+
25
+ const mockFeed: FeedResponse = {
26
+ id: 'feed-1',
27
+ group_id: 'user',
28
+ created_at: new Date(),
29
+ updated_at: new Date(),
30
+ description: 'Test feed',
31
+ fid: 'user:feed-1',
32
+ follower_count: 0,
33
+ following_count: 0,
34
+ member_count: 0,
35
+ name: 'Test Feed',
36
+ pin_count: 0,
37
+ created_by: mockUser,
38
+ custom: {},
39
+ };
40
+
41
+ const mockFollow: FollowResponse = {
42
+ created_at: new Date(),
43
+ updated_at: new Date(),
44
+ follower_role: 'user',
45
+ push_preference: 'all',
46
+ status: 'accepted',
47
+ source_feed: {
48
+ ...mockFeed,
49
+ id: 'source-feed',
50
+ fid: 'user:source-feed',
51
+ created_by: mockUser,
52
+ },
53
+ target_feed: {
54
+ ...mockFeed,
55
+ id: 'target-feed',
56
+ fid: 'user:target-feed',
57
+ created_by: mockUser,
58
+ },
59
+ };
60
+
61
+ describe('handleFollowCreated', () => {
62
+ it('should return unchanged state for non-accepted follows', () => {
63
+ const follow: FollowResponse = {
64
+ ...mockFollow,
65
+ status: 'pending',
66
+ };
67
+
68
+ // @ts-expect-error - we're not testing the full state here
69
+ const currentState: FeedState = {
70
+ followers: [],
71
+ following: [],
72
+ };
73
+
74
+ const result = handleFollowCreated(
75
+ follow,
76
+ currentState,
77
+ 'user:feed-1',
78
+ 'user-1',
79
+ );
80
+
81
+ expect(result.changed).toBe(false);
82
+ });
83
+
84
+ it('should handle when this feed follows someone', () => {
85
+ const follow: FollowResponse = {
86
+ ...mockFollow,
87
+ source_feed: {
88
+ ...mockFeed,
89
+ id: 'feed-x',
90
+ fid: 'user:feed-x',
91
+ created_by: {
92
+ ...mockUser,
93
+ id: 'user-x',
94
+ },
95
+ following_count: 1,
96
+ },
97
+ target_feed: {
98
+ ...mockFeed,
99
+ id: 'other-feed',
100
+ fid: 'user:other-feed',
101
+ created_by: mockUser,
102
+ },
103
+ };
104
+
105
+ // @ts-expect-error - we're not testing the full state here
106
+ const currentState: FeedState = {
107
+ following: [],
108
+ following_count: 0,
109
+ };
110
+
111
+ const result = handleFollowCreated(
112
+ follow,
113
+ currentState,
114
+ 'user:feed-x',
115
+ 'user-1',
116
+ );
117
+
118
+ expect(result.changed).toBe(true);
119
+ expect(result.data.following).toHaveLength(1);
120
+ expect(result.data.following?.[0]).toEqual(follow);
121
+ expect(result.data).toMatchObject(follow.source_feed);
122
+ expect(result.data.own_follows).toBeUndefined();
123
+ expect(result.data.following_count).toEqual(1);
124
+ });
125
+
126
+ it('should handle when someone follows this feed', () => {
127
+ const follow: FollowResponse = {
128
+ ...mockFollow,
129
+ source_feed: {
130
+ ...mockFeed,
131
+ id: 'other-feed',
132
+ fid: 'user:other-feed',
133
+ created_by: {
134
+ ...mockUser,
135
+ id: 'other-user',
136
+ },
137
+ },
138
+ target_feed: {
139
+ ...mockFeed,
140
+ id: 'feed-1',
141
+ fid: 'user:feed-1',
142
+ created_by: mockUser,
143
+ follower_count: 1,
144
+ },
145
+ };
146
+
147
+ // @ts-expect-error - we're not testing the full state here
148
+ const currentState: FeedState = {
149
+ followers: [],
150
+ follower_count: 0,
151
+ };
152
+
153
+ const result = handleFollowCreated(
154
+ follow,
155
+ currentState,
156
+ 'user:feed-1',
157
+ 'user-1',
158
+ );
159
+
160
+ expect(result.changed).toBe(true);
161
+ expect(result.data.followers).toHaveLength(1);
162
+ expect(result.data.followers?.[0]).toEqual(follow);
163
+ expect(result.data).toMatchObject(follow.target_feed);
164
+ expect(result.data.own_follows).toBeUndefined();
165
+ expect(result.data.follower_count).toEqual(1);
166
+ });
167
+
168
+ it('should add to own_follows when connected user is the source', () => {
169
+ const follow: FollowResponse = {
170
+ ...mockFollow,
171
+ source_feed: {
172
+ ...mockFeed,
173
+ id: 'feed-1',
174
+ fid: 'user:feed-1',
175
+ created_by: { ...mockUser, id: 'user-1' },
176
+ },
177
+ target_feed: {
178
+ ...mockFeed,
179
+ id: 'feed-x',
180
+ fid: 'user:feed-x',
181
+ created_by: {
182
+ ...mockUser,
183
+ id: 'user-x',
184
+ },
185
+ },
186
+ };
187
+
188
+ // @ts-expect-error - we're not testing the full state here
189
+ const currentState: FeedState = {
190
+ followers: [],
191
+ own_follows: [],
192
+ };
193
+
194
+ const result = handleFollowCreated(
195
+ follow,
196
+ currentState,
197
+ 'user:feed-x',
198
+ 'user-1',
199
+ );
200
+
201
+ expect(result.changed).toBe(true);
202
+ expect(result.data.own_follows).toHaveLength(1);
203
+ expect(result.data.own_follows?.[0]).toEqual(follow);
204
+ });
205
+
206
+ it('should not update followers/following when they are undefined', () => {
207
+ const follow: FollowResponse = {
208
+ ...mockFollow,
209
+ source_feed: {
210
+ ...mockFeed,
211
+ id: 'other-feed',
212
+ fid: 'user:other-feed',
213
+ created_by: mockUser,
214
+ },
215
+ target_feed: {
216
+ ...mockFeed,
217
+ id: 'feed-1',
218
+ fid: 'user:feed-1',
219
+ created_by: mockUser,
220
+ },
221
+ };
222
+
223
+ // @ts-expect-error - we're not testing the full state here
224
+ const currentState: FeedState = {
225
+ followers: undefined,
226
+ following: undefined,
227
+ own_follows: undefined,
228
+ };
229
+
230
+ const result = handleFollowCreated(
231
+ follow,
232
+ currentState,
233
+ 'user:feed-1',
234
+ 'user-1',
235
+ );
236
+
237
+ expect(result.changed).toBe(true);
238
+ expect(result.data.followers).toBeUndefined();
239
+ expect(result.data).toMatchObject(follow.target_feed);
240
+ });
241
+
242
+ it('should add new followers to the top of existing arrays', () => {
243
+ const existingFollow: FollowResponse = {
244
+ ...mockFollow,
245
+ source_feed: {
246
+ ...mockFeed,
247
+ id: 'existing-feed',
248
+ fid: 'user:existing-feed',
249
+ created_by: mockUser,
250
+ },
251
+ };
252
+
253
+ const follow: FollowResponse = {
254
+ ...mockFollow,
255
+ source_feed: {
256
+ ...mockFeed,
257
+ id: 'other-feed',
258
+ fid: 'user:other-feed',
259
+ created_by: mockUser,
260
+ },
261
+ target_feed: {
262
+ ...mockFeed,
263
+ id: 'feed-1',
264
+ fid: 'user:feed-1',
265
+ created_by: mockUser,
266
+ },
267
+ };
268
+
269
+ // @ts-expect-error - we're not testing the full state here
270
+ const currentState: FeedState = {
271
+ followers: [existingFollow],
272
+ following: undefined,
273
+ own_follows: undefined,
274
+ };
275
+
276
+ const result = handleFollowCreated(
277
+ follow,
278
+ currentState,
279
+ 'user:feed-1',
280
+ 'user-1',
281
+ );
282
+
283
+ expect(result.changed).toBe(true);
284
+ expect(result.data.followers).toHaveLength(2);
285
+ expect(result.data.followers?.[0]).toEqual(follow);
286
+ expect(result.data.followers?.[1]).toEqual(existingFollow);
287
+ });
288
+ });
289
+
290
+ describe('handleFollowDeleted', () => {
291
+ it('should handle when this feed unfollows someone', () => {
292
+ const existingFollow: FollowResponse = {
293
+ ...mockFollow,
294
+ source_feed: {
295
+ ...mockFeed,
296
+ id: 'feed-1',
297
+ fid: 'user:feed-1',
298
+ created_by: mockUser,
299
+ },
300
+ target_feed: {
301
+ ...mockFeed,
302
+ id: 'other-feed',
303
+ fid: 'user:other-feed',
304
+ created_by: mockUser,
305
+ },
306
+ };
307
+
308
+ const follow: FollowResponse = existingFollow;
309
+
310
+ // @ts-expect-error - we're not testing the full state here
311
+ const currentState: FeedState = {
312
+ following: [existingFollow],
313
+ following_count: 1,
314
+ };
315
+
316
+ const result = handleFollowDeleted(
317
+ follow,
318
+ currentState,
319
+ 'user:feed-1',
320
+ 'user-1',
321
+ );
322
+
323
+ expect(result.changed).toBe(true);
324
+ expect(result.data.following).toHaveLength(0);
325
+ expect(result.data).toMatchObject(follow.source_feed);
326
+ });
327
+
328
+ it('should handle when someone unfollows this feed', () => {
329
+ const existingFollow: FollowResponse = {
330
+ ...mockFollow,
331
+ source_feed: {
332
+ ...mockFeed,
333
+ id: 'other-feed',
334
+ fid: 'user:other-feed',
335
+ created_by: {
336
+ ...mockUser,
337
+ id: 'other-user',
338
+ },
339
+ },
340
+ target_feed: {
341
+ ...mockFeed,
342
+ id: 'feed-1',
343
+ fid: 'user:feed-1',
344
+ created_by: mockUser,
345
+ },
346
+ };
347
+
348
+ const follow: FollowResponse = existingFollow;
349
+
350
+ // @ts-expect-error - we're not testing the full state here
351
+ const currentState: FeedState = {
352
+ followers: [existingFollow],
353
+ own_follows: [existingFollow],
354
+ following_count: 1,
355
+ };
356
+
357
+ const result = handleFollowDeleted(
358
+ follow,
359
+ currentState,
360
+ 'user:feed-1',
361
+ 'user-1',
362
+ );
363
+
364
+ expect(result.changed).toBe(true);
365
+ expect(result.data.followers).toHaveLength(0);
366
+ expect(result.data.own_follows).toEqual(currentState.own_follows);
367
+ expect(result.data).toMatchObject(follow.target_feed);
368
+ });
369
+
370
+ it('should only remove own_follows when connected user is the source', () => {
371
+ const existingFollow: FollowResponse = {
372
+ ...mockFollow,
373
+ source_feed: {
374
+ ...mockFeed,
375
+ id: 'other-feed',
376
+ fid: 'user:other-feed',
377
+ created_by: { ...mockUser, id: 'user-1' },
378
+ },
379
+ target_feed: {
380
+ ...mockFeed,
381
+ id: 'feed-1',
382
+ fid: 'user:feed-1',
383
+ created_by: mockUser,
384
+ },
385
+ };
386
+
387
+ const follow: FollowResponse = existingFollow;
388
+
389
+ // @ts-expect-error - we're not testing the full state here
390
+ const currentState: FeedState = {
391
+ followers: [existingFollow],
392
+ own_follows: [existingFollow],
393
+ following_count: 1,
394
+ };
395
+
396
+ const result = handleFollowDeleted(
397
+ follow,
398
+ currentState,
399
+ 'user:feed-1',
400
+ 'user-1',
401
+ );
402
+
403
+ expect(result.changed).toBe(true);
404
+ expect(result.data.followers).toHaveLength(0);
405
+ expect(result.data.own_follows).toHaveLength(0);
406
+ });
407
+
408
+ it('should not remove own_follows when connected user is not the source', () => {
409
+ const existingFollow: FollowResponse = {
410
+ ...mockFollow,
411
+ source_feed: {
412
+ ...mockFeed,
413
+ id: 'other-feed',
414
+ fid: 'user:other-feed',
415
+ created_by: { ...mockUser, id: 'other-user' },
416
+ },
417
+ target_feed: {
418
+ ...mockFeed,
419
+ id: 'feed-1',
420
+ fid: 'user:feed-1',
421
+ created_by: mockUser,
422
+ },
423
+ };
424
+
425
+ const follow: FollowResponse = existingFollow;
426
+
427
+ // @ts-expect-error - we're not testing the full state here
428
+ const currentState: FeedState = {
429
+ followers: [existingFollow],
430
+ own_follows: [existingFollow],
431
+ };
432
+
433
+ const result = handleFollowDeleted(
434
+ follow,
435
+ currentState,
436
+ 'user:feed-1',
437
+ 'user-1',
438
+ );
439
+
440
+ expect(result.changed).toBe(true);
441
+ expect(result.data.followers).toHaveLength(0);
442
+ expect(result.data.own_follows).toHaveLength(1); // Should remain unchanged
443
+ });
444
+
445
+ it('should not update followers/following when they are undefined in delete', () => {
446
+ const existingFollow: FollowResponse = {
447
+ ...mockFollow,
448
+ source_feed: {
449
+ ...mockFeed,
450
+ id: 'other-feed',
451
+ fid: 'user:other-feed',
452
+ created_by: mockUser,
453
+ },
454
+ target_feed: {
455
+ ...mockFeed,
456
+ id: 'feed-1',
457
+ fid: 'user:feed-1',
458
+ created_by: mockUser,
459
+ },
460
+ };
461
+
462
+ const follow: FollowResponse = existingFollow;
463
+
464
+ // @ts-expect-error - we're not testing the full state here
465
+ const currentState: FeedState = {
466
+ followers: undefined,
467
+ own_follows: undefined,
468
+ };
469
+
470
+ const result = handleFollowDeleted(
471
+ follow,
472
+ currentState,
473
+ 'user:feed-1',
474
+ 'user-1',
475
+ );
476
+
477
+ expect(result.changed).toBe(true);
478
+ expect(result.data.followers).toBeUndefined();
479
+ expect(result.data.own_follows).toBeUndefined();
480
+ expect(result.data).toMatchObject(follow.target_feed);
481
+ });
482
+
483
+ it('should filter out the correct follow by target feed id', () => {
484
+ const followToRemove: FollowResponse = {
485
+ ...mockFollow,
486
+ source_feed: {
487
+ ...mockFeed,
488
+ id: 'feed-1',
489
+ fid: 'user:feed-1',
490
+ created_by: mockUser,
491
+ },
492
+ target_feed: {
493
+ ...mockFeed,
494
+ id: 'target-to-remove',
495
+ fid: 'user:target-to-remove',
496
+ created_by: mockUser,
497
+ },
498
+ };
499
+
500
+ const followToKeep: FollowResponse = {
501
+ ...mockFollow,
502
+ source_feed: {
503
+ ...mockFeed,
504
+ id: 'feed-1',
505
+ fid: 'user:feed-1',
506
+ created_by: mockUser,
507
+ },
508
+ target_feed: {
509
+ ...mockFeed,
510
+ id: 'target-to-keep',
511
+ fid: 'user:target-to-keep',
512
+ created_by: mockUser,
513
+ },
514
+ };
515
+
516
+ const follow: FollowResponse = followToRemove;
517
+
518
+ // @ts-expect-error - we're not testing the full state here
519
+ const currentState: FeedState = {
520
+ following: [followToRemove, followToKeep],
521
+ following_count: 2,
522
+ };
523
+
524
+ const result = handleFollowDeleted(
525
+ follow,
526
+ currentState,
527
+ 'user:feed-1',
528
+ 'user-1',
529
+ );
530
+
531
+ expect(result.changed).toBe(true);
532
+ expect(result.data.following).toHaveLength(1);
533
+ expect(result.data.following?.[0]).toEqual(followToKeep);
534
+ });
535
+ });
536
+
537
+ describe('handleFollowUpdated', () => {
538
+ // TODO: not yet implemented
539
+ it.skip('should return unchanged state (no-op)', () => {
540
+ // @ts-expect-error - we're not testing the full state here
541
+ const currentState: FeedState = {
542
+ followers: [],
543
+ following: [],
544
+ following_count: 0,
545
+ };
546
+
547
+ const result = handleFollowUpdated(currentState);
548
+
549
+ expect(result.changed).toBe(false);
550
+ });
551
+ });
552
+ });