@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
@@ -2686,6 +2686,13 @@ function removeConnectionEventListeners(cb) {
2686
2686
  window.removeEventListener('online', cb);
2687
2687
  }
2688
2688
  }
2689
+ const streamDevToken = (userId) => {
2690
+ return [
2691
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', // {"alg": "HS256", "typ": "JWT"}
2692
+ window.btoa(JSON.stringify({ user_id: userId })),
2693
+ 'devtoken', // hardcoded signature
2694
+ ].join('.');
2695
+ };
2689
2696
  const debounce = (fn, timeout = 0, { leading = false, trailing = true, } = {}) => {
2690
2697
  let runningTimeout = null;
2691
2698
  let argsForTrailingExecution = null;
@@ -2782,7 +2789,7 @@ class TokenManager {
2782
2789
  }
2783
2790
  catch (e) {
2784
2791
  const numberOfFailures = ++previousFailuresCount;
2785
- await sleep(retryInterval(numberOfFailures));
2792
+ await sleep(1000);
2786
2793
  if (numberOfFailures === 3) {
2787
2794
  this.loadTokenPromise = null;
2788
2795
  return reject(new Error(`Stream error: tried to get token ${numberOfFailures} times, but it failed with ${e}. Check your token provider`, { cause: e }));
@@ -2995,7 +3002,10 @@ class StableWSConnection {
2995
3002
  this.onmessage = (wsID, event) => {
2996
3003
  if (this.wsID !== wsID)
2997
3004
  return;
2998
- this._log('onmessage() - onmessage callback', { event, wsID });
3005
+ this._log('onmessage() - onmessage callback', {
3006
+ event: { ...event, data: JSON.parse(event.data) },
3007
+ wsID,
3008
+ });
2999
3009
  let data = typeof event.data === 'string' ? JSON.parse(event.data) : null;
3000
3010
  this.decoders.forEach((decode) => {
3001
3011
  data = decode(data);
@@ -3627,7 +3637,7 @@ class ApiClient {
3627
3637
  rate_limit: getRateLimitFromResponseHeader(response_headers),
3628
3638
  };
3629
3639
  };
3630
- this.baseUrl = options?.base_url ?? 'https://video.stream-io-api.com';
3640
+ this.baseUrl = options?.base_url ?? 'https://feeds.stream-io-api.com';
3631
3641
  this.timeout = options?.timeout ?? 3000;
3632
3642
  this.axiosInstance = axios.create({
3633
3643
  baseURL: this.baseUrl,
@@ -3921,6 +3931,13 @@ const removeReactionFromActivities = (event, activities, isCurrentUser) => {
3921
3931
  return updateActivityInActivities$1(updatedActivity, activities);
3922
3932
  };
3923
3933
 
3934
+ // Helper function to check if two bookmarks are the same
3935
+ // A bookmark is identified by activity_id + folder_id + user_id
3936
+ const isSameBookmark = (bookmark1, bookmark2) => {
3937
+ return (bookmark1.user.id === bookmark2.user.id &&
3938
+ bookmark1.activity.id === bookmark2.activity.id &&
3939
+ bookmark1.folder?.id === bookmark2.folder?.id);
3940
+ };
3924
3941
  const updateActivityInActivities = (updatedActivity, activities) => {
3925
3942
  const index = activities.findIndex((a) => a.id === updatedActivity.id);
3926
3943
  if (index !== -1) {
@@ -3947,8 +3964,7 @@ const addBookmarkToActivity = (event, activity, isCurrentUser) => {
3947
3964
  const removeBookmarkFromActivity = (event, activity, isCurrentUser) => {
3948
3965
  // Update own_bookmarks if the bookmark is from the current user
3949
3966
  const ownBookmarks = isCurrentUser
3950
- ? (activity.own_bookmarks || []).filter((bookmark) => bookmark.user.id !== event.bookmark.user.id ||
3951
- bookmark.activity.id !== event.bookmark.activity.id)
3967
+ ? (activity.own_bookmarks || []).filter((bookmark) => !isSameBookmark(bookmark, event.bookmark))
3952
3968
  : activity.own_bookmarks;
3953
3969
  return {
3954
3970
  ...activity,
@@ -3960,8 +3976,7 @@ const updateBookmarkInActivity = (event, activity, isCurrentUser) => {
3960
3976
  // Update own_bookmarks if the bookmark is from the current user
3961
3977
  let ownBookmarks = activity.own_bookmarks || [];
3962
3978
  if (isCurrentUser) {
3963
- const bookmarkIndex = ownBookmarks.findIndex((bookmark) => bookmark.user.id === event.bookmark.user.id &&
3964
- bookmark.activity.id === event.bookmark.activity.id);
3979
+ const bookmarkIndex = ownBookmarks.findIndex((bookmark) => isSameBookmark(bookmark, event.bookmark));
3965
3980
  if (bookmarkIndex !== -1) {
3966
3981
  ownBookmarks = [...ownBookmarks];
3967
3982
  ownBookmarks[bookmarkIndex] = event.bookmark;
@@ -4010,8 +4025,22 @@ const updateBookmarkInActivities = (event, activities, isCurrentUser) => {
4010
4025
  return updateActivityInActivities(updatedActivity, activities);
4011
4026
  };
4012
4027
 
4013
- const END_OF_LIST = 'eol';
4014
- const DEFAULT_COMMENT_PAGINATION = 'first';
4028
+ const isImageFile = (file) => {
4029
+ // photoshop files begin with 'image/'
4030
+ return file.type.startsWith('image/') && !file.type.endsWith('.photoshop');
4031
+ };
4032
+ const isVideoFile = (file) => {
4033
+ return file.type.startsWith('video/');
4034
+ };
4035
+ const checkHasAnotherPage = (v, cursor) => (typeof v === 'undefined' && typeof cursor === 'undefined') ||
4036
+ typeof cursor === 'string';
4037
+ const isCommentResponse = (entity) => {
4038
+ return typeof entity?.object_id === 'string';
4039
+ };
4040
+ const Constants = {
4041
+ DEFAULT_COMMENT_PAGINATION: 'first',
4042
+ };
4043
+
4015
4044
  class Feed extends FeedApi {
4016
4045
  constructor(client, groupId, id, data) {
4017
4046
  // Need this ugly cast because fileUpload endpoints :(
@@ -4036,7 +4065,7 @@ class Feed extends FeedApi {
4036
4065
  },
4037
4066
  'feeds.activity.reaction.added': (event) => {
4038
4067
  const currentActivities = this.currentState.activities;
4039
- const connectedUser = this.client.state.getLatestValue().connectedUser;
4068
+ const connectedUser = this.client.state.getLatestValue().connected_user;
4040
4069
  const isCurrentUser = Boolean(connectedUser && event.reaction.user.id === connectedUser.id);
4041
4070
  const result = addReactionToActivities(event, currentActivities, isCurrentUser);
4042
4071
  if (result.changed) {
@@ -4045,7 +4074,7 @@ class Feed extends FeedApi {
4045
4074
  },
4046
4075
  'feeds.activity.reaction.deleted': (event) => {
4047
4076
  const currentActivities = this.currentState.activities;
4048
- const connectedUser = this.client.state.getLatestValue().connectedUser;
4077
+ const connectedUser = this.client.state.getLatestValue().connected_user;
4049
4078
  const isCurrentUser = Boolean(connectedUser && event.reaction.user.id === connectedUser.id);
4050
4079
  const result = removeReactionFromActivities(event, currentActivities, isCurrentUser);
4051
4080
  if (result.changed) {
@@ -4084,7 +4113,7 @@ class Feed extends FeedApi {
4084
4113
  const entityState = currentState.comments_by_entity_id[forId];
4085
4114
  const newComments = entityState?.comments?.concat([]) ?? [];
4086
4115
  if (entityState?.pagination?.sort === 'last' &&
4087
- entityState?.pagination.next === END_OF_LIST) {
4116
+ !checkHasAnotherPage(entityState.comments, entityState?.pagination.next)) {
4088
4117
  newComments.unshift(comment);
4089
4118
  }
4090
4119
  else if (entityState?.pagination?.sort === 'first') {
@@ -4171,7 +4200,7 @@ class Feed extends FeedApi {
4171
4200
  ...currentState,
4172
4201
  ...event.follow.source_feed,
4173
4202
  };
4174
- if (currentState.following_pagination?.next === END_OF_LIST) {
4203
+ if (!checkHasAnotherPage(currentState.following, currentState.following_pagination?.next)) {
4175
4204
  // TODO: respect sort
4176
4205
  newState.following = currentState.following
4177
4206
  ? currentState.following.concat(event.follow)
@@ -4184,7 +4213,7 @@ class Feed extends FeedApi {
4184
4213
  // someone followed this feed
4185
4214
  event.follow.target_feed.fid === this.fid) {
4186
4215
  const source = event.follow.source_feed;
4187
- const connectedUser = this.client.state.getLatestValue().connectedUser;
4216
+ const connectedUser = this.client.state.getLatestValue().connected_user;
4188
4217
  this.state.next((currentState) => {
4189
4218
  const newState = { ...currentState, ...event.follow.target_feed };
4190
4219
  if (source.created_by.id === connectedUser?.id) {
@@ -4192,7 +4221,7 @@ class Feed extends FeedApi {
4192
4221
  ? currentState.own_follows.concat(event.follow)
4193
4222
  : [event.follow];
4194
4223
  }
4195
- if (currentState.followers_pagination?.next === END_OF_LIST) {
4224
+ if (!checkHasAnotherPage(currentState.followers, currentState.followers_pagination?.next)) {
4196
4225
  // TODO: respect sort
4197
4226
  newState.followers = currentState.followers
4198
4227
  ? currentState.followers.concat(event.follow)
@@ -4217,7 +4246,7 @@ class Feed extends FeedApi {
4217
4246
  // someone unfollowed this feed
4218
4247
  event.follow.target_feed.fid === this.fid) {
4219
4248
  const source = event.follow.source_feed;
4220
- const connectedUser = this.client.state.getLatestValue().connectedUser;
4249
+ const connectedUser = this.client.state.getLatestValue().connected_user;
4221
4250
  this.state.next((currentState) => {
4222
4251
  const newState = { ...currentState, ...event.follow.target_feed };
4223
4252
  if (source.created_by.id === connectedUser?.id) {
@@ -4233,40 +4262,60 @@ class Feed extends FeedApi {
4233
4262
  'feeds.comment.reaction.deleted': this.handleCommentReactionEvent.bind(this),
4234
4263
  'feeds.comment.reaction.updated': Feed.noop,
4235
4264
  'feeds.feed_member.added': (event) => {
4236
- const { member } = event;
4237
- // do not add a member if the pagination has reached the end of the list
4238
- if (this.currentState.member_pagination?.next !== END_OF_LIST)
4239
- return;
4265
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4240
4266
  this.state.next((currentState) => {
4241
- return {
4242
- ...currentState,
4243
- // TODO: respect sort
4244
- members: currentState.members
4245
- ? currentState.members.concat(member)
4246
- : [member],
4247
- };
4267
+ let newState;
4268
+ if (!checkHasAnotherPage(currentState.members, currentState.member_pagination?.next)) {
4269
+ newState ?? (newState = {
4270
+ ...currentState,
4271
+ });
4272
+ newState.members = newState.members?.concat(event.member) ?? [
4273
+ event.member,
4274
+ ];
4275
+ }
4276
+ if (connectedUser?.id === event.member.user.id) {
4277
+ newState ?? (newState = {
4278
+ ...currentState,
4279
+ });
4280
+ newState.own_membership = event.member;
4281
+ }
4282
+ return newState ?? currentState;
4248
4283
  });
4249
4284
  },
4250
4285
  'feeds.feed_member.removed': (event) => {
4286
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4251
4287
  this.state.next((currentState) => {
4252
- return {
4288
+ const newState = {
4253
4289
  ...currentState,
4254
4290
  members: currentState.members?.filter((member) => member.user.id !== event.user?.id),
4255
4291
  };
4292
+ if (connectedUser?.id === event.member_id) {
4293
+ delete newState.own_membership;
4294
+ }
4295
+ return newState;
4256
4296
  });
4257
4297
  },
4258
4298
  'feeds.feed_member.updated': (event) => {
4299
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4259
4300
  this.state.next((currentState) => {
4260
4301
  const memberIndex = currentState.members?.findIndex((member) => member.user.id === event.member.user.id) ?? -1;
4302
+ let newState;
4261
4303
  if (memberIndex !== -1) {
4304
+ // if there's an index, there's a member to update
4262
4305
  const newMembers = [...currentState.members];
4263
4306
  newMembers[memberIndex] = event.member;
4264
- return {
4307
+ newState ?? (newState = {
4265
4308
  ...currentState,
4266
- members: newMembers,
4267
- };
4309
+ });
4310
+ newState.members = newMembers;
4268
4311
  }
4269
- return currentState;
4312
+ if (connectedUser?.id === event.member.user.id) {
4313
+ newState ?? (newState = {
4314
+ ...currentState,
4315
+ });
4316
+ newState.own_membership = event.member;
4317
+ }
4318
+ return newState ?? currentState;
4270
4319
  });
4271
4320
  },
4272
4321
  // the poll events should be removed from here
@@ -4312,7 +4361,7 @@ class Feed extends FeedApi {
4312
4361
  }
4313
4362
  handleCommentReactionEvent(event) {
4314
4363
  const { comment, reaction } = event;
4315
- const connectedUser = this.client.state.getLatestValue().connectedUser;
4364
+ const connectedUser = this.client.state.getLatestValue().connected_user;
4316
4365
  this.state.next((currentState) => {
4317
4366
  const forId = comment.parent_id ?? comment.object_id;
4318
4367
  const entityState = currentState.comments_by_entity_id[forId];
@@ -4392,29 +4441,6 @@ class Feed extends FeedApi {
4392
4441
  ...currentState,
4393
4442
  ...responseCopy,
4394
4443
  };
4395
- // if there is no next cursor, set it to END_OF_LIST
4396
- // request has to have a limit set for this to work
4397
- if ((request?.followers_pagination?.limit ?? 0) > 0 &&
4398
- typeof nextState.followers_pagination?.next === 'undefined') {
4399
- nextState.followers_pagination = {
4400
- ...nextState.followers_pagination,
4401
- next: END_OF_LIST,
4402
- };
4403
- }
4404
- if ((request?.following_pagination?.limit ?? 0) > 0 &&
4405
- typeof nextState.following_pagination?.next === 'undefined') {
4406
- nextState.following_pagination = {
4407
- ...nextState.following_pagination,
4408
- next: END_OF_LIST,
4409
- };
4410
- }
4411
- if ((request?.member_pagination?.limit ?? 0) > 0 &&
4412
- typeof nextState.member_pagination?.next === 'undefined') {
4413
- nextState.member_pagination = {
4414
- ...nextState.member_pagination,
4415
- next: END_OF_LIST,
4416
- };
4417
- }
4418
4444
  if (!request?.followers_pagination?.limit) {
4419
4445
  delete nextState.followers;
4420
4446
  }
@@ -4437,7 +4463,7 @@ class Feed extends FeedApi {
4437
4463
  }
4438
4464
  handleBookmarkAdded(event) {
4439
4465
  const currentActivities = this.currentState.activities;
4440
- const { connectedUser } = this.client.state.getLatestValue();
4466
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4441
4467
  const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4442
4468
  const result = addBookmarkToActivities(event, currentActivities, isCurrentUser);
4443
4469
  if (result.changed) {
@@ -4446,7 +4472,7 @@ class Feed extends FeedApi {
4446
4472
  }
4447
4473
  handleBookmarkDeleted(event) {
4448
4474
  const currentActivities = this.currentState.activities;
4449
- const { connectedUser } = this.client.state.getLatestValue();
4475
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4450
4476
  const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4451
4477
  const result = removeBookmarkFromActivities(event, currentActivities, isCurrentUser);
4452
4478
  if (result.changed) {
@@ -4455,7 +4481,7 @@ class Feed extends FeedApi {
4455
4481
  }
4456
4482
  handleBookmarkUpdated(event) {
4457
4483
  const currentActivities = this.currentState.activities;
4458
- const { connectedUser } = this.client.state.getLatestValue();
4484
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4459
4485
  const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4460
4486
  const result = updateBookmarkInActivities(event, currentActivities, isCurrentUser);
4461
4487
  if (result.changed) {
@@ -4505,6 +4531,7 @@ class Feed extends FeedApi {
4505
4531
  });
4506
4532
  }
4507
4533
  async loadNextPageComments({ forId, base, sort, parentId, }) {
4534
+ let error;
4508
4535
  try {
4509
4536
  this.state.next((currentState) => ({
4510
4537
  ...currentState,
@@ -4519,7 +4546,7 @@ class Feed extends FeedApi {
4519
4546
  },
4520
4547
  },
4521
4548
  }));
4522
- const { next: newNextCursor = END_OF_LIST, comments } = await base();
4549
+ const { next: newNextCursor, comments } = await base();
4523
4550
  this.state.next((currentState) => {
4524
4551
  const newPagination = {
4525
4552
  ...currentState.comments_by_entity_id[forId]?.pagination,
@@ -4544,9 +4571,8 @@ class Feed extends FeedApi {
4544
4571
  };
4545
4572
  });
4546
4573
  }
4547
- catch (error) {
4548
- console.error(error);
4549
- // TODO: figure out how to handle errorss
4574
+ catch (e) {
4575
+ error = e;
4550
4576
  }
4551
4577
  finally {
4552
4578
  this.state.next((currentState) => ({
@@ -4563,15 +4589,21 @@ class Feed extends FeedApi {
4563
4589
  },
4564
4590
  }));
4565
4591
  }
4592
+ if (error) {
4593
+ throw error;
4594
+ }
4566
4595
  }
4567
4596
  async loadNextPageActivityComments(activity, request) {
4568
- const pagination = this.currentState.comments_by_entity_id[activity.id]?.pagination;
4569
- const currentNextCursor = pagination?.next;
4570
- const currentSort = pagination?.sort;
4571
- const isLoading = pagination?.loading_next_page;
4572
- const sort = currentSort ?? request?.sort ?? DEFAULT_COMMENT_PAGINATION;
4573
- if (isLoading || currentNextCursor === END_OF_LIST)
4597
+ const currentEntityState = this.currentState.comments_by_entity_id[activity.id];
4598
+ const currentPagination = currentEntityState?.pagination;
4599
+ const currentNextCursor = currentPagination?.next;
4600
+ const currentSort = currentPagination?.sort;
4601
+ const isLoading = currentPagination?.loading_next_page;
4602
+ const sort = currentSort ?? request?.sort ?? Constants.DEFAULT_COMMENT_PAGINATION;
4603
+ if (isLoading ||
4604
+ !checkHasAnotherPage(currentEntityState?.comments, currentNextCursor)) {
4574
4605
  return;
4606
+ }
4575
4607
  await this.loadNextPageComments({
4576
4608
  forId: activity.id,
4577
4609
  base: () => this.client.getComments({
@@ -4585,20 +4617,25 @@ class Feed extends FeedApi {
4585
4617
  });
4586
4618
  }
4587
4619
  async loadNextPageCommentReplies(comment, request) {
4588
- const pagination = this.currentState.comments_by_entity_id[comment.id]?.pagination;
4589
- const currentNextCursor = pagination?.next;
4590
- const currentSort = pagination?.sort;
4591
- const isLoading = pagination?.loading_next_page;
4592
- const sort = currentSort ?? request?.sort ?? DEFAULT_COMMENT_PAGINATION;
4593
- if (isLoading || currentNextCursor === END_OF_LIST)
4620
+ const currentEntityState = this.currentState.comments_by_entity_id[comment.id];
4621
+ const currentPagination = currentEntityState?.pagination;
4622
+ const currentNextCursor = currentPagination?.next;
4623
+ const currentSort = currentPagination?.sort;
4624
+ const isLoading = currentPagination?.loading_next_page;
4625
+ const sort = currentSort ?? request?.sort ?? Constants.DEFAULT_COMMENT_PAGINATION;
4626
+ if (isLoading ||
4627
+ !checkHasAnotherPage(currentEntityState?.comments, currentNextCursor)) {
4594
4628
  return;
4629
+ }
4595
4630
  await this.loadNextPageComments({
4596
4631
  forId: comment.id,
4597
4632
  base: () => this.client.getCommentReplies({
4598
4633
  ...request,
4599
4634
  comment_id: comment.id,
4600
4635
  // use known sort first (prevents broken pagination)
4601
- sort: currentSort ?? request?.sort ?? DEFAULT_COMMENT_PAGINATION,
4636
+ sort: currentSort ??
4637
+ request?.sort ??
4638
+ Constants.DEFAULT_COMMENT_PAGINATION,
4602
4639
  next: currentNextCursor,
4603
4640
  }),
4604
4641
  parentId: comment.parent_id ?? comment.object_id,
@@ -4608,10 +4645,14 @@ class Feed extends FeedApi {
4608
4645
  async loadNextPageFollows(type, request) {
4609
4646
  const paginationKey = `${type}_pagination`;
4610
4647
  const method = `query${capitalize(type)}`;
4648
+ const currentFollows = this.currentState[type];
4611
4649
  const currentNextCursor = this.currentState[paginationKey]?.next;
4612
4650
  const isLoading = this.currentState[paginationKey]?.loading_next_page;
4613
- if (isLoading || currentNextCursor === END_OF_LIST)
4651
+ const sort = this.currentState[paginationKey]?.sort ?? request.sort;
4652
+ let error;
4653
+ if (isLoading || !checkHasAnotherPage(currentFollows, currentNextCursor)) {
4614
4654
  return;
4655
+ }
4615
4656
  try {
4616
4657
  this.state.next((currentState) => {
4617
4658
  return {
@@ -4622,9 +4663,10 @@ class Feed extends FeedApi {
4622
4663
  },
4623
4664
  };
4624
4665
  });
4625
- const { next: newNextCursor = END_OF_LIST, follows } = await this[method]({
4666
+ const { next: newNextCursor, follows } = await this[method]({
4626
4667
  ...request,
4627
4668
  next: currentNextCursor,
4669
+ sort,
4628
4670
  });
4629
4671
  this.state.next((currentState) => ({
4630
4672
  ...currentState,
@@ -4634,12 +4676,12 @@ class Feed extends FeedApi {
4634
4676
  [paginationKey]: {
4635
4677
  ...currentState[paginationKey],
4636
4678
  next: newNextCursor,
4679
+ sort,
4637
4680
  },
4638
4681
  }));
4639
4682
  }
4640
- catch (error) {
4641
- console.error(error);
4642
- // TODO: figure out how to handle errorss
4683
+ catch (e) {
4684
+ error = e;
4643
4685
  }
4644
4686
  finally {
4645
4687
  this.state.next((currentState) => {
@@ -4652,6 +4694,9 @@ class Feed extends FeedApi {
4652
4694
  };
4653
4695
  });
4654
4696
  }
4697
+ if (error) {
4698
+ throw error;
4699
+ }
4655
4700
  }
4656
4701
  async loadNextPageFollowers(request) {
4657
4702
  await this.loadNextPageFollows('followers', request);
@@ -4659,6 +4704,59 @@ class Feed extends FeedApi {
4659
4704
  async loadNextPageFollowing(request) {
4660
4705
  await this.loadNextPageFollows('following', request);
4661
4706
  }
4707
+ async loadNextPageMembers(request) {
4708
+ const currentMembers = this.currentState.members;
4709
+ const currentNextCursor = this.currentState.member_pagination?.next;
4710
+ const isLoading = this.currentState.member_pagination?.loading_next_page;
4711
+ const sort = this.currentState.member_pagination?.sort ?? request.sort;
4712
+ let error;
4713
+ if (isLoading || !checkHasAnotherPage(currentMembers, currentNextCursor)) {
4714
+ return;
4715
+ }
4716
+ try {
4717
+ this.state.next((currentState) => ({
4718
+ ...currentState,
4719
+ member_pagination: {
4720
+ ...currentState.member_pagination,
4721
+ loading_next_page: true,
4722
+ },
4723
+ }));
4724
+ const { next: newNextCursor, members } = await this.client.queryFeedMembers({
4725
+ ...request,
4726
+ sort,
4727
+ feed_id: this.id,
4728
+ feed_group_id: this.group,
4729
+ next: currentNextCursor,
4730
+ });
4731
+ this.state.next((currentState) => ({
4732
+ ...currentState,
4733
+ members: currentState.members
4734
+ ? currentState.members.concat(members)
4735
+ : members,
4736
+ member_pagination: {
4737
+ ...currentState.member_pagination,
4738
+ next: newNextCursor,
4739
+ // set sort if not defined yet
4740
+ sort: currentState.member_pagination?.sort ?? request.sort,
4741
+ },
4742
+ }));
4743
+ }
4744
+ catch (e) {
4745
+ error = e;
4746
+ }
4747
+ finally {
4748
+ this.state.next((currentState) => ({
4749
+ ...currentState,
4750
+ member_pagination: {
4751
+ ...currentState.member_pagination,
4752
+ loading_next_page: false,
4753
+ },
4754
+ }));
4755
+ }
4756
+ if (error) {
4757
+ throw error;
4758
+ }
4759
+ }
4662
4760
  /**
4663
4761
  * Method which queries followers of this feed (feeds which target this feed).
4664
4762
  *
@@ -4938,7 +5036,8 @@ class StreamPoll {
4938
5036
  if (!isPollVoteCastedEvent(event))
4939
5037
  return;
4940
5038
  const currentState = this.data;
4941
- const isOwnVote = event.poll_vote.user_id === this.client.state.getLatestValue().connectedUser?.id;
5039
+ const isOwnVote = event.poll_vote.user_id ===
5040
+ this.client.state.getLatestValue().connected_user?.id;
4942
5041
  let latestAnswers = [...currentState.latest_answers];
4943
5042
  let ownAnswer = currentState.own_answer;
4944
5043
  const ownVotesByOptionId = currentState.own_votes_by_option_id;
@@ -4962,7 +5061,7 @@ class StreamPoll {
4962
5061
  else {
4963
5062
  maxVotedOptionIds = getMaxVotedOptionIds(event.poll.vote_counts_by_option);
4964
5063
  }
4965
- const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option } = event.poll;
5064
+ const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option, } = event.poll;
4966
5065
  this.state.partialNext({
4967
5066
  answers_count,
4968
5067
  // @ts-expect-error Incompatibility between PollResponseData and Poll due to teams_role, remove when OpenAPI spec is fixed
@@ -4983,7 +5082,8 @@ class StreamPoll {
4983
5082
  if (!isPollVoteChangedEvent(event))
4984
5083
  return;
4985
5084
  const currentState = this.data;
4986
- const isOwnVote = event.poll_vote.user_id === this.client.state.getLatestValue().connectedUser?.id;
5085
+ const isOwnVote = event.poll_vote.user_id ===
5086
+ this.client.state.getLatestValue().connected_user?.id;
4987
5087
  let latestAnswers = [...currentState.latest_answers];
4988
5088
  let ownAnswer = currentState.own_answer;
4989
5089
  let ownVotesByOptionId = currentState.own_votes_by_option_id;
@@ -5030,7 +5130,7 @@ class StreamPoll {
5030
5130
  else {
5031
5131
  maxVotedOptionIds = getMaxVotedOptionIds(event.poll.vote_counts_by_option);
5032
5132
  }
5033
- const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option } = event.poll;
5133
+ const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option, } = event.poll;
5034
5134
  this.state.partialNext({
5035
5135
  answers_count,
5036
5136
  // @ts-expect-error Incompatibility between PollResponseData and Poll due to teams_role, remove when OpenAPI spec is fixed
@@ -5050,7 +5150,8 @@ class StreamPoll {
5050
5150
  if (!isPollVoteRemovedEvent(event))
5051
5151
  return;
5052
5152
  const currentState = this.data;
5053
- const isOwnVote = event.poll_vote.user_id === this.client.state.getLatestValue().connectedUser?.id;
5153
+ const isOwnVote = event.poll_vote.user_id ===
5154
+ this.client.state.getLatestValue().connected_user?.id;
5054
5155
  let latestAnswers = [...currentState.latest_answers];
5055
5156
  let ownAnswer = currentState.own_answer;
5056
5157
  const ownVotesByOptionId = { ...currentState.own_votes_by_option_id };
@@ -5068,7 +5169,7 @@ class StreamPoll {
5068
5169
  delete ownVotesByOptionId[event.poll_vote.option_id];
5069
5170
  }
5070
5171
  }
5071
- const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option } = event.poll;
5172
+ const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option, } = event.poll;
5072
5173
  this.state.partialNext({
5073
5174
  answers_count,
5074
5175
  // @ts-expect-error Incompatibility between PollResponseData and Poll due to teams_role, remove when OpenAPI spec is fixed
@@ -5126,7 +5227,7 @@ class FeedsClient extends FeedsApi {
5126
5227
  this.healthyConnectionChangedEventCount = 0;
5127
5228
  this.pollFromState = (id) => this.polls_by_id.get(id);
5128
5229
  this.connectUser = async (user, tokenProvider) => {
5129
- if (this.state.getLatestValue().connectedUser !== undefined ||
5230
+ if (this.state.getLatestValue().connected_user !== undefined ||
5130
5231
  this.wsConnection) {
5131
5232
  throw new Error(`Can't connect a new user, call "disconnectUser" first`);
5132
5233
  }
@@ -5140,8 +5241,8 @@ class FeedsClient extends FeedsApi {
5140
5241
  this.wsConnection.on('all', (event) => this.eventDispatcher.dispatch(event));
5141
5242
  const connectedEvent = await this.wsConnection.connect();
5142
5243
  this.state.partialNext({
5143
- connectedUser: connectedEvent?.me,
5144
- isWsConnectionHealthy: this.wsConnection.isHealthy,
5244
+ connected_user: connectedEvent?.me,
5245
+ is_ws_connection_healthy: !!this.wsConnection?.isHealthy,
5145
5246
  });
5146
5247
  }
5147
5248
  catch (err) {
@@ -5149,6 +5250,9 @@ class FeedsClient extends FeedsApi {
5149
5250
  throw err;
5150
5251
  }
5151
5252
  };
5253
+ this.devToken = (userId) => {
5254
+ return streamDevToken(userId);
5255
+ };
5152
5256
  this.closePoll = async (request) => {
5153
5257
  return await this.updatePollPartial({
5154
5258
  poll_id: request.poll_id,
@@ -5197,7 +5301,10 @@ class FeedsClient extends FeedsApi {
5197
5301
  removeConnectionEventListeners(this.updateNetworkConnectionStatus);
5198
5302
  this.connectionIdManager.reset();
5199
5303
  this.tokenManager.reset();
5200
- this.state.partialNext({ connectedUser: undefined, isWsConnectionHealthy: false });
5304
+ this.state.partialNext({
5305
+ connected_user: undefined,
5306
+ is_ws_connection_healthy: false,
5307
+ });
5201
5308
  };
5202
5309
  this.on = this.eventDispatcher.on;
5203
5310
  this.off = this.eventDispatcher.off;
@@ -5223,8 +5330,8 @@ class FeedsClient extends FeedsApi {
5223
5330
  }
5224
5331
  };
5225
5332
  this.state = new StateStore({
5226
- connectedUser: undefined,
5227
- isWsConnectionHealthy: false,
5333
+ connected_user: undefined,
5334
+ is_ws_connection_healthy: false,
5228
5335
  });
5229
5336
  this.moderation = new ModerationClient(apiClient);
5230
5337
  this.tokenManager = tokenManager;
@@ -5236,7 +5343,7 @@ class FeedsClient extends FeedsApi {
5236
5343
  switch (event.type) {
5237
5344
  case 'connection.changed': {
5238
5345
  const { online } = event;
5239
- this.state.partialNext({ isWsConnectionHealthy: online });
5346
+ this.state.partialNext({ is_ws_connection_healthy: online });
5240
5347
  if (online) {
5241
5348
  this.healthyConnectionChangedEventCount++;
5242
5349
  // we skip the first event as we could potentially be querying twice
@@ -5694,7 +5801,7 @@ class ActivitySearchSource extends BaseSearchSource {
5694
5801
  this.client = client;
5695
5802
  }
5696
5803
  async query(searchQuery) {
5697
- const { connectedUser } = this.client.state.getLatestValue();
5804
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
5698
5805
  if (!connectedUser)
5699
5806
  return { items: [] };
5700
5807
  const { activities: items, next } = await this.client.queryActivities({
@@ -5709,10 +5816,10 @@ class ActivitySearchSource extends BaseSearchSource {
5709
5816
  return items;
5710
5817
  }
5711
5818
  }
5712
- // filter: {
5713
- // 'feed.name': { $autocomplete: searchQuery }
5714
- // 'feed.description': { $autocomplete: searchQuery }
5715
- // 'created_by.name': { $autocomplete: searchQuery }
5819
+ // filter: {
5820
+ // 'feed.name': { $autocomplete: searchQuery }
5821
+ // 'feed.description': { $autocomplete: searchQuery }
5822
+ // 'created_by.name': { $autocomplete: searchQuery }
5716
5823
  // },
5717
5824
 
5718
5825
  class UserSearchSource extends BaseSearchSource {
@@ -5728,7 +5835,7 @@ class UserSearchSource extends BaseSearchSource {
5728
5835
  this.client = client;
5729
5836
  }
5730
5837
  async query(searchQuery) {
5731
- const { connectedUser } = this.client.state.getLatestValue();
5838
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
5732
5839
  if (!connectedUser)
5733
5840
  return { items: [] };
5734
5841
  // const channelFilters: ChannelFilters = {
@@ -5805,7 +5912,7 @@ class FeedSearchSource extends BaseSearchSource {
5805
5912
  this.client = client;
5806
5913
  }
5807
5914
  async query(searchQuery) {
5808
- const { connectedUser } = this.client.state.getLatestValue();
5915
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
5809
5916
  if (!connectedUser)
5810
5917
  return { items: [] };
5811
5918
  // const channelFilters: ChannelFilters = {
@@ -5870,10 +5977,5 @@ class FeedSearchSource extends BaseSearchSource {
5870
5977
  }
5871
5978
  }
5872
5979
 
5873
- const isImageFile = (file) => {
5874
- // photoshop files begin with 'image/'
5875
- return file.type.startsWith('image/') && !file.type.endsWith('.photoshop');
5876
- };
5877
-
5878
- export { ActivitySearchSource, BaseSearchSource, ChannelOwnCapability, Feed, FeedOwnCapability, FeedSearchSource, FeedsClient, MergedStateStore, SearchController, StateStore, StreamApiError, StreamPoll, UserSearchSource, isImageFile, isPatch, isVoteAnswer };
5980
+ export { ActivitySearchSource, BaseSearchSource, ChannelOwnCapability, Constants, Feed, FeedOwnCapability, FeedSearchSource, FeedsClient, MergedStateStore, SearchController, StateStore, StreamApiError, StreamPoll, UserSearchSource, checkHasAnotherPage, isCommentResponse, isImageFile, isPatch, isVideoFile, isVoteAnswer };
5879
5981
  //# sourceMappingURL=index.browser.js.map