@stream-io/feeds-client 0.1.4 → 0.1.6

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 (62) hide show
  1. package/@react-bindings/hooks/client-state-hooks/index.ts +2 -0
  2. package/@react-bindings/hooks/feed-state-hooks/index.ts +7 -0
  3. package/@react-bindings/hooks/internal/index.ts +1 -0
  4. package/@react-bindings/hooks/util/index.ts +1 -0
  5. package/@react-bindings/index.ts +6 -3
  6. package/CHANGELOG.md +31 -0
  7. package/dist/@react-bindings/contexts/StreamFeedContext.d.ts +12 -0
  8. package/dist/@react-bindings/hooks/client-state-hooks/index.d.ts +2 -0
  9. package/dist/@react-bindings/hooks/client-state-hooks/useClientConnectedUser.d.ts +4 -0
  10. package/dist/@react-bindings/hooks/client-state-hooks/useWsConnectionState.d.ts +6 -0
  11. package/dist/@react-bindings/hooks/feed-state-hooks/index.d.ts +7 -0
  12. package/dist/@react-bindings/hooks/feed-state-hooks/useComments.d.ts +19 -0
  13. package/dist/@react-bindings/hooks/feed-state-hooks/useFeedActivities.d.ts +11 -0
  14. package/dist/@react-bindings/hooks/feed-state-hooks/useFeedMetadata.d.ts +12 -0
  15. package/dist/@react-bindings/hooks/feed-state-hooks/useFollowers.d.ts +16 -0
  16. package/dist/@react-bindings/hooks/feed-state-hooks/useFollowing.d.ts +16 -0
  17. package/dist/@react-bindings/hooks/feed-state-hooks/useOwnCapabilities.d.ts +33 -0
  18. package/dist/@react-bindings/hooks/feed-state-hooks/useOwnFollows.d.ts +8 -0
  19. package/dist/@react-bindings/hooks/internal/index.d.ts +1 -0
  20. package/dist/@react-bindings/hooks/internal/useStableCallback.d.ts +25 -0
  21. package/dist/@react-bindings/hooks/util/index.d.ts +1 -0
  22. package/dist/@react-bindings/hooks/util/useReactionActions.d.ts +17 -0
  23. package/dist/@react-bindings/index.d.ts +5 -3
  24. package/dist/@react-bindings/wrappers/StreamFeed.d.ts +12 -0
  25. package/dist/index-react-bindings.browser.cjs +521 -210
  26. package/dist/index-react-bindings.browser.cjs.map +1 -1
  27. package/dist/index-react-bindings.browser.js +514 -212
  28. package/dist/index-react-bindings.browser.js.map +1 -1
  29. package/dist/index-react-bindings.node.cjs +521 -210
  30. package/dist/index-react-bindings.node.cjs.map +1 -1
  31. package/dist/index-react-bindings.node.js +514 -212
  32. package/dist/index-react-bindings.node.js.map +1 -1
  33. package/dist/index.browser.cjs +212 -106
  34. package/dist/index.browser.cjs.map +1 -1
  35. package/dist/index.browser.js +209 -107
  36. package/dist/index.browser.js.map +1 -1
  37. package/dist/index.node.cjs +212 -106
  38. package/dist/index.node.cjs.map +1 -1
  39. package/dist/index.node.js +209 -107
  40. package/dist/index.node.js.map +1 -1
  41. package/dist/src/Feed.d.ts +7 -3
  42. package/dist/src/FeedsClient.d.ts +6 -5
  43. package/dist/src/types.d.ts +7 -0
  44. package/dist/src/utils.d.ts +9 -1
  45. package/dist/tsconfig.tsbuildinfo +1 -1
  46. package/package.json +6 -2
  47. package/src/Feed.ts +214 -97
  48. package/src/FeedsClient.ts +22 -12
  49. package/src/common/ActivitySearchSource.ts +5 -5
  50. package/src/common/ApiClient.ts +1 -1
  51. package/src/common/FeedSearchSource.ts +1 -1
  52. package/src/common/Poll.ts +35 -10
  53. package/src/common/TokenManager.ts +2 -3
  54. package/src/common/UserSearchSource.ts +1 -1
  55. package/src/common/real-time/StableWSConnection.ts +4 -1
  56. package/src/state-updates/bookmark-utils.test.ts +134 -8
  57. package/src/state-updates/bookmark-utils.ts +17 -7
  58. package/src/types.ts +12 -1
  59. package/src/utils.ts +25 -1
  60. package/dist/@react-bindings/hooks/clientStateHooks.d.ts +0 -10
  61. package/dist/@react-bindings/hooks/useComments.d.ts +0 -12
  62. package/dist/@react-bindings/hooks/useOwnCapabilities.d.ts +0 -33
@@ -2688,6 +2688,13 @@ function removeConnectionEventListeners(cb) {
2688
2688
  window.removeEventListener('online', cb);
2689
2689
  }
2690
2690
  }
2691
+ const streamDevToken = (userId) => {
2692
+ return [
2693
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', // {"alg": "HS256", "typ": "JWT"}
2694
+ window.btoa(JSON.stringify({ user_id: userId })),
2695
+ 'devtoken', // hardcoded signature
2696
+ ].join('.');
2697
+ };
2691
2698
  const debounce = (fn, timeout = 0, { leading = false, trailing = true, } = {}) => {
2692
2699
  let runningTimeout = null;
2693
2700
  let argsForTrailingExecution = null;
@@ -2784,7 +2791,7 @@ class TokenManager {
2784
2791
  }
2785
2792
  catch (e) {
2786
2793
  const numberOfFailures = ++previousFailuresCount;
2787
- await sleep(retryInterval(numberOfFailures));
2794
+ await sleep(1000);
2788
2795
  if (numberOfFailures === 3) {
2789
2796
  this.loadTokenPromise = null;
2790
2797
  return reject(new Error(`Stream error: tried to get token ${numberOfFailures} times, but it failed with ${e}. Check your token provider`, { cause: e }));
@@ -2997,7 +3004,10 @@ class StableWSConnection {
2997
3004
  this.onmessage = (wsID, event) => {
2998
3005
  if (this.wsID !== wsID)
2999
3006
  return;
3000
- this._log('onmessage() - onmessage callback', { event, wsID });
3007
+ this._log('onmessage() - onmessage callback', {
3008
+ event: { ...event, data: JSON.parse(event.data) },
3009
+ wsID,
3010
+ });
3001
3011
  let data = typeof event.data === 'string' ? JSON.parse(event.data) : null;
3002
3012
  this.decoders.forEach((decode) => {
3003
3013
  data = decode(data);
@@ -3629,7 +3639,7 @@ class ApiClient {
3629
3639
  rate_limit: getRateLimitFromResponseHeader(response_headers),
3630
3640
  };
3631
3641
  };
3632
- this.baseUrl = options?.base_url ?? 'https://video.stream-io-api.com';
3642
+ this.baseUrl = options?.base_url ?? 'https://feeds.stream-io-api.com';
3633
3643
  this.timeout = options?.timeout ?? 3000;
3634
3644
  this.axiosInstance = axios.create({
3635
3645
  baseURL: this.baseUrl,
@@ -3923,6 +3933,13 @@ const removeReactionFromActivities = (event, activities, isCurrentUser) => {
3923
3933
  return updateActivityInActivities$1(updatedActivity, activities);
3924
3934
  };
3925
3935
 
3936
+ // Helper function to check if two bookmarks are the same
3937
+ // A bookmark is identified by activity_id + folder_id + user_id
3938
+ const isSameBookmark = (bookmark1, bookmark2) => {
3939
+ return (bookmark1.user.id === bookmark2.user.id &&
3940
+ bookmark1.activity.id === bookmark2.activity.id &&
3941
+ bookmark1.folder?.id === bookmark2.folder?.id);
3942
+ };
3926
3943
  const updateActivityInActivities = (updatedActivity, activities) => {
3927
3944
  const index = activities.findIndex((a) => a.id === updatedActivity.id);
3928
3945
  if (index !== -1) {
@@ -3949,8 +3966,7 @@ const addBookmarkToActivity = (event, activity, isCurrentUser) => {
3949
3966
  const removeBookmarkFromActivity = (event, activity, isCurrentUser) => {
3950
3967
  // Update own_bookmarks if the bookmark is from the current user
3951
3968
  const ownBookmarks = isCurrentUser
3952
- ? (activity.own_bookmarks || []).filter((bookmark) => bookmark.user.id !== event.bookmark.user.id ||
3953
- bookmark.activity.id !== event.bookmark.activity.id)
3969
+ ? (activity.own_bookmarks || []).filter((bookmark) => !isSameBookmark(bookmark, event.bookmark))
3954
3970
  : activity.own_bookmarks;
3955
3971
  return {
3956
3972
  ...activity,
@@ -3962,8 +3978,7 @@ const updateBookmarkInActivity = (event, activity, isCurrentUser) => {
3962
3978
  // Update own_bookmarks if the bookmark is from the current user
3963
3979
  let ownBookmarks = activity.own_bookmarks || [];
3964
3980
  if (isCurrentUser) {
3965
- const bookmarkIndex = ownBookmarks.findIndex((bookmark) => bookmark.user.id === event.bookmark.user.id &&
3966
- bookmark.activity.id === event.bookmark.activity.id);
3981
+ const bookmarkIndex = ownBookmarks.findIndex((bookmark) => isSameBookmark(bookmark, event.bookmark));
3967
3982
  if (bookmarkIndex !== -1) {
3968
3983
  ownBookmarks = [...ownBookmarks];
3969
3984
  ownBookmarks[bookmarkIndex] = event.bookmark;
@@ -4012,8 +4027,22 @@ const updateBookmarkInActivities = (event, activities, isCurrentUser) => {
4012
4027
  return updateActivityInActivities(updatedActivity, activities);
4013
4028
  };
4014
4029
 
4015
- const END_OF_LIST = 'eol';
4016
- const DEFAULT_COMMENT_PAGINATION = 'first';
4030
+ const isImageFile = (file) => {
4031
+ // photoshop files begin with 'image/'
4032
+ return file.type.startsWith('image/') && !file.type.endsWith('.photoshop');
4033
+ };
4034
+ const isVideoFile = (file) => {
4035
+ return file.type.startsWith('video/');
4036
+ };
4037
+ const checkHasAnotherPage = (v, cursor) => (typeof v === 'undefined' && typeof cursor === 'undefined') ||
4038
+ typeof cursor === 'string';
4039
+ const isCommentResponse = (entity) => {
4040
+ return typeof entity?.object_id === 'string';
4041
+ };
4042
+ const Constants = {
4043
+ DEFAULT_COMMENT_PAGINATION: 'first',
4044
+ };
4045
+
4017
4046
  class Feed extends FeedApi {
4018
4047
  constructor(client, groupId, id, data) {
4019
4048
  // Need this ugly cast because fileUpload endpoints :(
@@ -4038,7 +4067,7 @@ class Feed extends FeedApi {
4038
4067
  },
4039
4068
  'feeds.activity.reaction.added': (event) => {
4040
4069
  const currentActivities = this.currentState.activities;
4041
- const connectedUser = this.client.state.getLatestValue().connectedUser;
4070
+ const connectedUser = this.client.state.getLatestValue().connected_user;
4042
4071
  const isCurrentUser = Boolean(connectedUser && event.reaction.user.id === connectedUser.id);
4043
4072
  const result = addReactionToActivities(event, currentActivities, isCurrentUser);
4044
4073
  if (result.changed) {
@@ -4047,7 +4076,7 @@ class Feed extends FeedApi {
4047
4076
  },
4048
4077
  'feeds.activity.reaction.deleted': (event) => {
4049
4078
  const currentActivities = this.currentState.activities;
4050
- const connectedUser = this.client.state.getLatestValue().connectedUser;
4079
+ const connectedUser = this.client.state.getLatestValue().connected_user;
4051
4080
  const isCurrentUser = Boolean(connectedUser && event.reaction.user.id === connectedUser.id);
4052
4081
  const result = removeReactionFromActivities(event, currentActivities, isCurrentUser);
4053
4082
  if (result.changed) {
@@ -4086,7 +4115,7 @@ class Feed extends FeedApi {
4086
4115
  const entityState = currentState.comments_by_entity_id[forId];
4087
4116
  const newComments = entityState?.comments?.concat([]) ?? [];
4088
4117
  if (entityState?.pagination?.sort === 'last' &&
4089
- entityState?.pagination.next === END_OF_LIST) {
4118
+ !checkHasAnotherPage(entityState.comments, entityState?.pagination.next)) {
4090
4119
  newComments.unshift(comment);
4091
4120
  }
4092
4121
  else if (entityState?.pagination?.sort === 'first') {
@@ -4173,7 +4202,7 @@ class Feed extends FeedApi {
4173
4202
  ...currentState,
4174
4203
  ...event.follow.source_feed,
4175
4204
  };
4176
- if (currentState.following_pagination?.next === END_OF_LIST) {
4205
+ if (!checkHasAnotherPage(currentState.following, currentState.following_pagination?.next)) {
4177
4206
  // TODO: respect sort
4178
4207
  newState.following = currentState.following
4179
4208
  ? currentState.following.concat(event.follow)
@@ -4186,7 +4215,7 @@ class Feed extends FeedApi {
4186
4215
  // someone followed this feed
4187
4216
  event.follow.target_feed.fid === this.fid) {
4188
4217
  const source = event.follow.source_feed;
4189
- const connectedUser = this.client.state.getLatestValue().connectedUser;
4218
+ const connectedUser = this.client.state.getLatestValue().connected_user;
4190
4219
  this.state.next((currentState) => {
4191
4220
  const newState = { ...currentState, ...event.follow.target_feed };
4192
4221
  if (source.created_by.id === connectedUser?.id) {
@@ -4194,7 +4223,7 @@ class Feed extends FeedApi {
4194
4223
  ? currentState.own_follows.concat(event.follow)
4195
4224
  : [event.follow];
4196
4225
  }
4197
- if (currentState.followers_pagination?.next === END_OF_LIST) {
4226
+ if (!checkHasAnotherPage(currentState.followers, currentState.followers_pagination?.next)) {
4198
4227
  // TODO: respect sort
4199
4228
  newState.followers = currentState.followers
4200
4229
  ? currentState.followers.concat(event.follow)
@@ -4219,7 +4248,7 @@ class Feed extends FeedApi {
4219
4248
  // someone unfollowed this feed
4220
4249
  event.follow.target_feed.fid === this.fid) {
4221
4250
  const source = event.follow.source_feed;
4222
- const connectedUser = this.client.state.getLatestValue().connectedUser;
4251
+ const connectedUser = this.client.state.getLatestValue().connected_user;
4223
4252
  this.state.next((currentState) => {
4224
4253
  const newState = { ...currentState, ...event.follow.target_feed };
4225
4254
  if (source.created_by.id === connectedUser?.id) {
@@ -4235,40 +4264,60 @@ class Feed extends FeedApi {
4235
4264
  'feeds.comment.reaction.deleted': this.handleCommentReactionEvent.bind(this),
4236
4265
  'feeds.comment.reaction.updated': Feed.noop,
4237
4266
  'feeds.feed_member.added': (event) => {
4238
- const { member } = event;
4239
- // do not add a member if the pagination has reached the end of the list
4240
- if (this.currentState.member_pagination?.next !== END_OF_LIST)
4241
- return;
4267
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4242
4268
  this.state.next((currentState) => {
4243
- return {
4244
- ...currentState,
4245
- // TODO: respect sort
4246
- members: currentState.members
4247
- ? currentState.members.concat(member)
4248
- : [member],
4249
- };
4269
+ let newState;
4270
+ if (!checkHasAnotherPage(currentState.members, currentState.member_pagination?.next)) {
4271
+ newState ?? (newState = {
4272
+ ...currentState,
4273
+ });
4274
+ newState.members = newState.members?.concat(event.member) ?? [
4275
+ event.member,
4276
+ ];
4277
+ }
4278
+ if (connectedUser?.id === event.member.user.id) {
4279
+ newState ?? (newState = {
4280
+ ...currentState,
4281
+ });
4282
+ newState.own_membership = event.member;
4283
+ }
4284
+ return newState ?? currentState;
4250
4285
  });
4251
4286
  },
4252
4287
  'feeds.feed_member.removed': (event) => {
4288
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4253
4289
  this.state.next((currentState) => {
4254
- return {
4290
+ const newState = {
4255
4291
  ...currentState,
4256
4292
  members: currentState.members?.filter((member) => member.user.id !== event.user?.id),
4257
4293
  };
4294
+ if (connectedUser?.id === event.member_id) {
4295
+ delete newState.own_membership;
4296
+ }
4297
+ return newState;
4258
4298
  });
4259
4299
  },
4260
4300
  'feeds.feed_member.updated': (event) => {
4301
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4261
4302
  this.state.next((currentState) => {
4262
4303
  const memberIndex = currentState.members?.findIndex((member) => member.user.id === event.member.user.id) ?? -1;
4304
+ let newState;
4263
4305
  if (memberIndex !== -1) {
4306
+ // if there's an index, there's a member to update
4264
4307
  const newMembers = [...currentState.members];
4265
4308
  newMembers[memberIndex] = event.member;
4266
- return {
4309
+ newState ?? (newState = {
4267
4310
  ...currentState,
4268
- members: newMembers,
4269
- };
4311
+ });
4312
+ newState.members = newMembers;
4270
4313
  }
4271
- return currentState;
4314
+ if (connectedUser?.id === event.member.user.id) {
4315
+ newState ?? (newState = {
4316
+ ...currentState,
4317
+ });
4318
+ newState.own_membership = event.member;
4319
+ }
4320
+ return newState ?? currentState;
4272
4321
  });
4273
4322
  },
4274
4323
  // the poll events should be removed from here
@@ -4314,7 +4363,7 @@ class Feed extends FeedApi {
4314
4363
  }
4315
4364
  handleCommentReactionEvent(event) {
4316
4365
  const { comment, reaction } = event;
4317
- const connectedUser = this.client.state.getLatestValue().connectedUser;
4366
+ const connectedUser = this.client.state.getLatestValue().connected_user;
4318
4367
  this.state.next((currentState) => {
4319
4368
  const forId = comment.parent_id ?? comment.object_id;
4320
4369
  const entityState = currentState.comments_by_entity_id[forId];
@@ -4394,29 +4443,6 @@ class Feed extends FeedApi {
4394
4443
  ...currentState,
4395
4444
  ...responseCopy,
4396
4445
  };
4397
- // if there is no next cursor, set it to END_OF_LIST
4398
- // request has to have a limit set for this to work
4399
- if ((request?.followers_pagination?.limit ?? 0) > 0 &&
4400
- typeof nextState.followers_pagination?.next === 'undefined') {
4401
- nextState.followers_pagination = {
4402
- ...nextState.followers_pagination,
4403
- next: END_OF_LIST,
4404
- };
4405
- }
4406
- if ((request?.following_pagination?.limit ?? 0) > 0 &&
4407
- typeof nextState.following_pagination?.next === 'undefined') {
4408
- nextState.following_pagination = {
4409
- ...nextState.following_pagination,
4410
- next: END_OF_LIST,
4411
- };
4412
- }
4413
- if ((request?.member_pagination?.limit ?? 0) > 0 &&
4414
- typeof nextState.member_pagination?.next === 'undefined') {
4415
- nextState.member_pagination = {
4416
- ...nextState.member_pagination,
4417
- next: END_OF_LIST,
4418
- };
4419
- }
4420
4446
  if (!request?.followers_pagination?.limit) {
4421
4447
  delete nextState.followers;
4422
4448
  }
@@ -4439,7 +4465,7 @@ class Feed extends FeedApi {
4439
4465
  }
4440
4466
  handleBookmarkAdded(event) {
4441
4467
  const currentActivities = this.currentState.activities;
4442
- const { connectedUser } = this.client.state.getLatestValue();
4468
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4443
4469
  const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4444
4470
  const result = addBookmarkToActivities(event, currentActivities, isCurrentUser);
4445
4471
  if (result.changed) {
@@ -4448,7 +4474,7 @@ class Feed extends FeedApi {
4448
4474
  }
4449
4475
  handleBookmarkDeleted(event) {
4450
4476
  const currentActivities = this.currentState.activities;
4451
- const { connectedUser } = this.client.state.getLatestValue();
4477
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4452
4478
  const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4453
4479
  const result = removeBookmarkFromActivities(event, currentActivities, isCurrentUser);
4454
4480
  if (result.changed) {
@@ -4457,7 +4483,7 @@ class Feed extends FeedApi {
4457
4483
  }
4458
4484
  handleBookmarkUpdated(event) {
4459
4485
  const currentActivities = this.currentState.activities;
4460
- const { connectedUser } = this.client.state.getLatestValue();
4486
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4461
4487
  const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4462
4488
  const result = updateBookmarkInActivities(event, currentActivities, isCurrentUser);
4463
4489
  if (result.changed) {
@@ -4507,6 +4533,7 @@ class Feed extends FeedApi {
4507
4533
  });
4508
4534
  }
4509
4535
  async loadNextPageComments({ forId, base, sort, parentId, }) {
4536
+ let error;
4510
4537
  try {
4511
4538
  this.state.next((currentState) => ({
4512
4539
  ...currentState,
@@ -4521,7 +4548,7 @@ class Feed extends FeedApi {
4521
4548
  },
4522
4549
  },
4523
4550
  }));
4524
- const { next: newNextCursor = END_OF_LIST, comments } = await base();
4551
+ const { next: newNextCursor, comments } = await base();
4525
4552
  this.state.next((currentState) => {
4526
4553
  const newPagination = {
4527
4554
  ...currentState.comments_by_entity_id[forId]?.pagination,
@@ -4546,9 +4573,8 @@ class Feed extends FeedApi {
4546
4573
  };
4547
4574
  });
4548
4575
  }
4549
- catch (error) {
4550
- console.error(error);
4551
- // TODO: figure out how to handle errorss
4576
+ catch (e) {
4577
+ error = e;
4552
4578
  }
4553
4579
  finally {
4554
4580
  this.state.next((currentState) => ({
@@ -4565,15 +4591,21 @@ class Feed extends FeedApi {
4565
4591
  },
4566
4592
  }));
4567
4593
  }
4594
+ if (error) {
4595
+ throw error;
4596
+ }
4568
4597
  }
4569
4598
  async loadNextPageActivityComments(activity, request) {
4570
- const pagination = this.currentState.comments_by_entity_id[activity.id]?.pagination;
4571
- const currentNextCursor = pagination?.next;
4572
- const currentSort = pagination?.sort;
4573
- const isLoading = pagination?.loading_next_page;
4574
- const sort = currentSort ?? request?.sort ?? DEFAULT_COMMENT_PAGINATION;
4575
- if (isLoading || currentNextCursor === END_OF_LIST)
4599
+ const currentEntityState = this.currentState.comments_by_entity_id[activity.id];
4600
+ const currentPagination = currentEntityState?.pagination;
4601
+ const currentNextCursor = currentPagination?.next;
4602
+ const currentSort = currentPagination?.sort;
4603
+ const isLoading = currentPagination?.loading_next_page;
4604
+ const sort = currentSort ?? request?.sort ?? Constants.DEFAULT_COMMENT_PAGINATION;
4605
+ if (isLoading ||
4606
+ !checkHasAnotherPage(currentEntityState?.comments, currentNextCursor)) {
4576
4607
  return;
4608
+ }
4577
4609
  await this.loadNextPageComments({
4578
4610
  forId: activity.id,
4579
4611
  base: () => this.client.getComments({
@@ -4587,20 +4619,25 @@ class Feed extends FeedApi {
4587
4619
  });
4588
4620
  }
4589
4621
  async loadNextPageCommentReplies(comment, request) {
4590
- const pagination = this.currentState.comments_by_entity_id[comment.id]?.pagination;
4591
- const currentNextCursor = pagination?.next;
4592
- const currentSort = pagination?.sort;
4593
- const isLoading = pagination?.loading_next_page;
4594
- const sort = currentSort ?? request?.sort ?? DEFAULT_COMMENT_PAGINATION;
4595
- if (isLoading || currentNextCursor === END_OF_LIST)
4622
+ const currentEntityState = this.currentState.comments_by_entity_id[comment.id];
4623
+ const currentPagination = currentEntityState?.pagination;
4624
+ const currentNextCursor = currentPagination?.next;
4625
+ const currentSort = currentPagination?.sort;
4626
+ const isLoading = currentPagination?.loading_next_page;
4627
+ const sort = currentSort ?? request?.sort ?? Constants.DEFAULT_COMMENT_PAGINATION;
4628
+ if (isLoading ||
4629
+ !checkHasAnotherPage(currentEntityState?.comments, currentNextCursor)) {
4596
4630
  return;
4631
+ }
4597
4632
  await this.loadNextPageComments({
4598
4633
  forId: comment.id,
4599
4634
  base: () => this.client.getCommentReplies({
4600
4635
  ...request,
4601
4636
  comment_id: comment.id,
4602
4637
  // use known sort first (prevents broken pagination)
4603
- sort: currentSort ?? request?.sort ?? DEFAULT_COMMENT_PAGINATION,
4638
+ sort: currentSort ??
4639
+ request?.sort ??
4640
+ Constants.DEFAULT_COMMENT_PAGINATION,
4604
4641
  next: currentNextCursor,
4605
4642
  }),
4606
4643
  parentId: comment.parent_id ?? comment.object_id,
@@ -4610,10 +4647,14 @@ class Feed extends FeedApi {
4610
4647
  async loadNextPageFollows(type, request) {
4611
4648
  const paginationKey = `${type}_pagination`;
4612
4649
  const method = `query${capitalize(type)}`;
4650
+ const currentFollows = this.currentState[type];
4613
4651
  const currentNextCursor = this.currentState[paginationKey]?.next;
4614
4652
  const isLoading = this.currentState[paginationKey]?.loading_next_page;
4615
- if (isLoading || currentNextCursor === END_OF_LIST)
4653
+ const sort = this.currentState[paginationKey]?.sort ?? request.sort;
4654
+ let error;
4655
+ if (isLoading || !checkHasAnotherPage(currentFollows, currentNextCursor)) {
4616
4656
  return;
4657
+ }
4617
4658
  try {
4618
4659
  this.state.next((currentState) => {
4619
4660
  return {
@@ -4624,9 +4665,10 @@ class Feed extends FeedApi {
4624
4665
  },
4625
4666
  };
4626
4667
  });
4627
- const { next: newNextCursor = END_OF_LIST, follows } = await this[method]({
4668
+ const { next: newNextCursor, follows } = await this[method]({
4628
4669
  ...request,
4629
4670
  next: currentNextCursor,
4671
+ sort,
4630
4672
  });
4631
4673
  this.state.next((currentState) => ({
4632
4674
  ...currentState,
@@ -4636,12 +4678,12 @@ class Feed extends FeedApi {
4636
4678
  [paginationKey]: {
4637
4679
  ...currentState[paginationKey],
4638
4680
  next: newNextCursor,
4681
+ sort,
4639
4682
  },
4640
4683
  }));
4641
4684
  }
4642
- catch (error) {
4643
- console.error(error);
4644
- // TODO: figure out how to handle errorss
4685
+ catch (e) {
4686
+ error = e;
4645
4687
  }
4646
4688
  finally {
4647
4689
  this.state.next((currentState) => {
@@ -4654,6 +4696,9 @@ class Feed extends FeedApi {
4654
4696
  };
4655
4697
  });
4656
4698
  }
4699
+ if (error) {
4700
+ throw error;
4701
+ }
4657
4702
  }
4658
4703
  async loadNextPageFollowers(request) {
4659
4704
  await this.loadNextPageFollows('followers', request);
@@ -4661,6 +4706,59 @@ class Feed extends FeedApi {
4661
4706
  async loadNextPageFollowing(request) {
4662
4707
  await this.loadNextPageFollows('following', request);
4663
4708
  }
4709
+ async loadNextPageMembers(request) {
4710
+ const currentMembers = this.currentState.members;
4711
+ const currentNextCursor = this.currentState.member_pagination?.next;
4712
+ const isLoading = this.currentState.member_pagination?.loading_next_page;
4713
+ const sort = this.currentState.member_pagination?.sort ?? request.sort;
4714
+ let error;
4715
+ if (isLoading || !checkHasAnotherPage(currentMembers, currentNextCursor)) {
4716
+ return;
4717
+ }
4718
+ try {
4719
+ this.state.next((currentState) => ({
4720
+ ...currentState,
4721
+ member_pagination: {
4722
+ ...currentState.member_pagination,
4723
+ loading_next_page: true,
4724
+ },
4725
+ }));
4726
+ const { next: newNextCursor, members } = await this.client.queryFeedMembers({
4727
+ ...request,
4728
+ sort,
4729
+ feed_id: this.id,
4730
+ feed_group_id: this.group,
4731
+ next: currentNextCursor,
4732
+ });
4733
+ this.state.next((currentState) => ({
4734
+ ...currentState,
4735
+ members: currentState.members
4736
+ ? currentState.members.concat(members)
4737
+ : members,
4738
+ member_pagination: {
4739
+ ...currentState.member_pagination,
4740
+ next: newNextCursor,
4741
+ // set sort if not defined yet
4742
+ sort: currentState.member_pagination?.sort ?? request.sort,
4743
+ },
4744
+ }));
4745
+ }
4746
+ catch (e) {
4747
+ error = e;
4748
+ }
4749
+ finally {
4750
+ this.state.next((currentState) => ({
4751
+ ...currentState,
4752
+ member_pagination: {
4753
+ ...currentState.member_pagination,
4754
+ loading_next_page: false,
4755
+ },
4756
+ }));
4757
+ }
4758
+ if (error) {
4759
+ throw error;
4760
+ }
4761
+ }
4664
4762
  /**
4665
4763
  * Method which queries followers of this feed (feeds which target this feed).
4666
4764
  *
@@ -4940,7 +5038,8 @@ class StreamPoll {
4940
5038
  if (!isPollVoteCastedEvent(event))
4941
5039
  return;
4942
5040
  const currentState = this.data;
4943
- const isOwnVote = event.poll_vote.user_id === this.client.state.getLatestValue().connectedUser?.id;
5041
+ const isOwnVote = event.poll_vote.user_id ===
5042
+ this.client.state.getLatestValue().connected_user?.id;
4944
5043
  let latestAnswers = [...currentState.latest_answers];
4945
5044
  let ownAnswer = currentState.own_answer;
4946
5045
  const ownVotesByOptionId = currentState.own_votes_by_option_id;
@@ -4964,7 +5063,7 @@ class StreamPoll {
4964
5063
  else {
4965
5064
  maxVotedOptionIds = getMaxVotedOptionIds(event.poll.vote_counts_by_option);
4966
5065
  }
4967
- const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option } = event.poll;
5066
+ const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option, } = event.poll;
4968
5067
  this.state.partialNext({
4969
5068
  answers_count,
4970
5069
  // @ts-expect-error Incompatibility between PollResponseData and Poll due to teams_role, remove when OpenAPI spec is fixed
@@ -4985,7 +5084,8 @@ class StreamPoll {
4985
5084
  if (!isPollVoteChangedEvent(event))
4986
5085
  return;
4987
5086
  const currentState = this.data;
4988
- const isOwnVote = event.poll_vote.user_id === this.client.state.getLatestValue().connectedUser?.id;
5087
+ const isOwnVote = event.poll_vote.user_id ===
5088
+ this.client.state.getLatestValue().connected_user?.id;
4989
5089
  let latestAnswers = [...currentState.latest_answers];
4990
5090
  let ownAnswer = currentState.own_answer;
4991
5091
  let ownVotesByOptionId = currentState.own_votes_by_option_id;
@@ -5032,7 +5132,7 @@ class StreamPoll {
5032
5132
  else {
5033
5133
  maxVotedOptionIds = getMaxVotedOptionIds(event.poll.vote_counts_by_option);
5034
5134
  }
5035
- const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option } = event.poll;
5135
+ const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option, } = event.poll;
5036
5136
  this.state.partialNext({
5037
5137
  answers_count,
5038
5138
  // @ts-expect-error Incompatibility between PollResponseData and Poll due to teams_role, remove when OpenAPI spec is fixed
@@ -5052,7 +5152,8 @@ class StreamPoll {
5052
5152
  if (!isPollVoteRemovedEvent(event))
5053
5153
  return;
5054
5154
  const currentState = this.data;
5055
- const isOwnVote = event.poll_vote.user_id === this.client.state.getLatestValue().connectedUser?.id;
5155
+ const isOwnVote = event.poll_vote.user_id ===
5156
+ this.client.state.getLatestValue().connected_user?.id;
5056
5157
  let latestAnswers = [...currentState.latest_answers];
5057
5158
  let ownAnswer = currentState.own_answer;
5058
5159
  const ownVotesByOptionId = { ...currentState.own_votes_by_option_id };
@@ -5070,7 +5171,7 @@ class StreamPoll {
5070
5171
  delete ownVotesByOptionId[event.poll_vote.option_id];
5071
5172
  }
5072
5173
  }
5073
- const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option } = event.poll;
5174
+ const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option, } = event.poll;
5074
5175
  this.state.partialNext({
5075
5176
  answers_count,
5076
5177
  // @ts-expect-error Incompatibility between PollResponseData and Poll due to teams_role, remove when OpenAPI spec is fixed
@@ -5128,7 +5229,7 @@ class FeedsClient extends FeedsApi {
5128
5229
  this.healthyConnectionChangedEventCount = 0;
5129
5230
  this.pollFromState = (id) => this.polls_by_id.get(id);
5130
5231
  this.connectUser = async (user, tokenProvider) => {
5131
- if (this.state.getLatestValue().connectedUser !== undefined ||
5232
+ if (this.state.getLatestValue().connected_user !== undefined ||
5132
5233
  this.wsConnection) {
5133
5234
  throw new Error(`Can't connect a new user, call "disconnectUser" first`);
5134
5235
  }
@@ -5142,8 +5243,8 @@ class FeedsClient extends FeedsApi {
5142
5243
  this.wsConnection.on('all', (event) => this.eventDispatcher.dispatch(event));
5143
5244
  const connectedEvent = await this.wsConnection.connect();
5144
5245
  this.state.partialNext({
5145
- connectedUser: connectedEvent?.me,
5146
- isWsConnectionHealthy: this.wsConnection.isHealthy,
5246
+ connected_user: connectedEvent?.me,
5247
+ is_ws_connection_healthy: !!this.wsConnection?.isHealthy,
5147
5248
  });
5148
5249
  }
5149
5250
  catch (err) {
@@ -5151,6 +5252,9 @@ class FeedsClient extends FeedsApi {
5151
5252
  throw err;
5152
5253
  }
5153
5254
  };
5255
+ this.devToken = (userId) => {
5256
+ return streamDevToken(userId);
5257
+ };
5154
5258
  this.closePoll = async (request) => {
5155
5259
  return await this.updatePollPartial({
5156
5260
  poll_id: request.poll_id,
@@ -5199,7 +5303,10 @@ class FeedsClient extends FeedsApi {
5199
5303
  removeConnectionEventListeners(this.updateNetworkConnectionStatus);
5200
5304
  this.connectionIdManager.reset();
5201
5305
  this.tokenManager.reset();
5202
- this.state.partialNext({ connectedUser: undefined, isWsConnectionHealthy: false });
5306
+ this.state.partialNext({
5307
+ connected_user: undefined,
5308
+ is_ws_connection_healthy: false,
5309
+ });
5203
5310
  };
5204
5311
  this.on = this.eventDispatcher.on;
5205
5312
  this.off = this.eventDispatcher.off;
@@ -5225,8 +5332,8 @@ class FeedsClient extends FeedsApi {
5225
5332
  }
5226
5333
  };
5227
5334
  this.state = new StateStore({
5228
- connectedUser: undefined,
5229
- isWsConnectionHealthy: false,
5335
+ connected_user: undefined,
5336
+ is_ws_connection_healthy: false,
5230
5337
  });
5231
5338
  this.moderation = new ModerationClient(apiClient);
5232
5339
  this.tokenManager = tokenManager;
@@ -5238,7 +5345,7 @@ class FeedsClient extends FeedsApi {
5238
5345
  switch (event.type) {
5239
5346
  case 'connection.changed': {
5240
5347
  const { online } = event;
5241
- this.state.partialNext({ isWsConnectionHealthy: online });
5348
+ this.state.partialNext({ is_ws_connection_healthy: online });
5242
5349
  if (online) {
5243
5350
  this.healthyConnectionChangedEventCount++;
5244
5351
  // we skip the first event as we could potentially be querying twice
@@ -5696,7 +5803,7 @@ class ActivitySearchSource extends BaseSearchSource {
5696
5803
  this.client = client;
5697
5804
  }
5698
5805
  async query(searchQuery) {
5699
- const { connectedUser } = this.client.state.getLatestValue();
5806
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
5700
5807
  if (!connectedUser)
5701
5808
  return { items: [] };
5702
5809
  const { activities: items, next } = await this.client.queryActivities({
@@ -5711,10 +5818,10 @@ class ActivitySearchSource extends BaseSearchSource {
5711
5818
  return items;
5712
5819
  }
5713
5820
  }
5714
- // filter: {
5715
- // 'feed.name': { $autocomplete: searchQuery }
5716
- // 'feed.description': { $autocomplete: searchQuery }
5717
- // 'created_by.name': { $autocomplete: searchQuery }
5821
+ // filter: {
5822
+ // 'feed.name': { $autocomplete: searchQuery }
5823
+ // 'feed.description': { $autocomplete: searchQuery }
5824
+ // 'created_by.name': { $autocomplete: searchQuery }
5718
5825
  // },
5719
5826
 
5720
5827
  class UserSearchSource extends BaseSearchSource {
@@ -5730,7 +5837,7 @@ class UserSearchSource extends BaseSearchSource {
5730
5837
  this.client = client;
5731
5838
  }
5732
5839
  async query(searchQuery) {
5733
- const { connectedUser } = this.client.state.getLatestValue();
5840
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
5734
5841
  if (!connectedUser)
5735
5842
  return { items: [] };
5736
5843
  // const channelFilters: ChannelFilters = {
@@ -5807,7 +5914,7 @@ class FeedSearchSource extends BaseSearchSource {
5807
5914
  this.client = client;
5808
5915
  }
5809
5916
  async query(searchQuery) {
5810
- const { connectedUser } = this.client.state.getLatestValue();
5917
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
5811
5918
  if (!connectedUser)
5812
5919
  return { items: [] };
5813
5920
  // const channelFilters: ChannelFilters = {
@@ -5872,14 +5979,10 @@ class FeedSearchSource extends BaseSearchSource {
5872
5979
  }
5873
5980
  }
5874
5981
 
5875
- const isImageFile = (file) => {
5876
- // photoshop files begin with 'image/'
5877
- return file.type.startsWith('image/') && !file.type.endsWith('.photoshop');
5878
- };
5879
-
5880
5982
  exports.ActivitySearchSource = ActivitySearchSource;
5881
5983
  exports.BaseSearchSource = BaseSearchSource;
5882
5984
  exports.ChannelOwnCapability = ChannelOwnCapability;
5985
+ exports.Constants = Constants;
5883
5986
  exports.Feed = Feed;
5884
5987
  exports.FeedOwnCapability = FeedOwnCapability;
5885
5988
  exports.FeedSearchSource = FeedSearchSource;
@@ -5890,7 +5993,10 @@ exports.StateStore = StateStore;
5890
5993
  exports.StreamApiError = StreamApiError;
5891
5994
  exports.StreamPoll = StreamPoll;
5892
5995
  exports.UserSearchSource = UserSearchSource;
5996
+ exports.checkHasAnotherPage = checkHasAnotherPage;
5997
+ exports.isCommentResponse = isCommentResponse;
5893
5998
  exports.isImageFile = isImageFile;
5894
5999
  exports.isPatch = isPatch;
6000
+ exports.isVideoFile = isVideoFile;
5895
6001
  exports.isVoteAnswer = isVoteAnswer;
5896
6002
  //# sourceMappingURL=index.node.cjs.map