@stream-io/feeds-client 0.1.4 → 0.1.5

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 (51) hide show
  1. package/@react-bindings/hooks/client-state-hooks/index.ts +2 -0
  2. package/@react-bindings/hooks/feed-state-hooks/index.ts +5 -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 +16 -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 +5 -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/useFollowers.d.ts +16 -0
  15. package/dist/@react-bindings/hooks/feed-state-hooks/useFollowing.d.ts +16 -0
  16. package/dist/@react-bindings/hooks/{useOwnCapabilities.d.ts → feed-state-hooks/useOwnCapabilities.d.ts} +2 -2
  17. package/dist/@react-bindings/hooks/internal/index.d.ts +1 -0
  18. package/dist/@react-bindings/hooks/internal/useStableCallback.d.ts +25 -0
  19. package/dist/@react-bindings/hooks/util/index.d.ts +1 -0
  20. package/dist/@react-bindings/hooks/util/useReactionActions.d.ts +17 -0
  21. package/dist/@react-bindings/index.d.ts +5 -3
  22. package/dist/@react-bindings/wrappers/StreamFeed.d.ts +12 -0
  23. package/dist/index-react-bindings.browser.cjs +451 -183
  24. package/dist/index-react-bindings.browser.cjs.map +1 -1
  25. package/dist/index-react-bindings.browser.js +446 -185
  26. package/dist/index-react-bindings.browser.js.map +1 -1
  27. package/dist/index-react-bindings.node.cjs +451 -183
  28. package/dist/index-react-bindings.node.cjs.map +1 -1
  29. package/dist/index-react-bindings.node.js +446 -185
  30. package/dist/index-react-bindings.node.js.map +1 -1
  31. package/dist/index.browser.cjs +167 -72
  32. package/dist/index.browser.cjs.map +1 -1
  33. package/dist/index.browser.js +164 -73
  34. package/dist/index.browser.js.map +1 -1
  35. package/dist/index.node.cjs +167 -72
  36. package/dist/index.node.cjs.map +1 -1
  37. package/dist/index.node.js +164 -73
  38. package/dist/index.node.js.map +1 -1
  39. package/dist/src/Feed.d.ts +7 -3
  40. package/dist/src/FeedsClient.d.ts +4 -3
  41. package/dist/src/types.d.ts +7 -0
  42. package/dist/src/utils.d.ts +9 -1
  43. package/dist/tsconfig.tsbuildinfo +1 -1
  44. package/package.json +2 -1
  45. package/src/Feed.ts +200 -89
  46. package/src/FeedsClient.ts +8 -3
  47. package/src/common/real-time/StableWSConnection.ts +4 -1
  48. package/src/types.ts +12 -1
  49. package/src/utils.ts +25 -1
  50. package/dist/@react-bindings/hooks/clientStateHooks.d.ts +0 -10
  51. package/dist/@react-bindings/hooks/useComments.d.ts +0 -12
@@ -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;
@@ -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);
@@ -4010,8 +4020,22 @@ const updateBookmarkInActivities = (event, activities, isCurrentUser) => {
4010
4020
  return updateActivityInActivities(updatedActivity, activities);
4011
4021
  };
4012
4022
 
4013
- const END_OF_LIST = 'eol';
4014
- const DEFAULT_COMMENT_PAGINATION = 'first';
4023
+ const isImageFile = (file) => {
4024
+ // photoshop files begin with 'image/'
4025
+ return file.type.startsWith('image/') && !file.type.endsWith('.photoshop');
4026
+ };
4027
+ const isVideoFile = (file) => {
4028
+ return file.type.startsWith('video/');
4029
+ };
4030
+ const checkHasAnotherPage = (v, cursor) => (typeof v === 'undefined' && typeof cursor === 'undefined') ||
4031
+ typeof cursor === 'string';
4032
+ const isCommentResponse = (entity) => {
4033
+ return typeof entity?.object_id === 'string';
4034
+ };
4035
+ const Constants = {
4036
+ DEFAULT_COMMENT_PAGINATION: 'first',
4037
+ };
4038
+
4015
4039
  class Feed extends FeedApi {
4016
4040
  constructor(client, groupId, id, data) {
4017
4041
  // Need this ugly cast because fileUpload endpoints :(
@@ -4084,7 +4108,7 @@ class Feed extends FeedApi {
4084
4108
  const entityState = currentState.comments_by_entity_id[forId];
4085
4109
  const newComments = entityState?.comments?.concat([]) ?? [];
4086
4110
  if (entityState?.pagination?.sort === 'last' &&
4087
- entityState?.pagination.next === END_OF_LIST) {
4111
+ !checkHasAnotherPage(entityState.comments, entityState?.pagination.next)) {
4088
4112
  newComments.unshift(comment);
4089
4113
  }
4090
4114
  else if (entityState?.pagination?.sort === 'first') {
@@ -4171,7 +4195,7 @@ class Feed extends FeedApi {
4171
4195
  ...currentState,
4172
4196
  ...event.follow.source_feed,
4173
4197
  };
4174
- if (currentState.following_pagination?.next === END_OF_LIST) {
4198
+ if (!checkHasAnotherPage(currentState.following, currentState.following_pagination?.next)) {
4175
4199
  // TODO: respect sort
4176
4200
  newState.following = currentState.following
4177
4201
  ? currentState.following.concat(event.follow)
@@ -4192,7 +4216,7 @@ class Feed extends FeedApi {
4192
4216
  ? currentState.own_follows.concat(event.follow)
4193
4217
  : [event.follow];
4194
4218
  }
4195
- if (currentState.followers_pagination?.next === END_OF_LIST) {
4219
+ if (!checkHasAnotherPage(currentState.followers, currentState.followers_pagination?.next)) {
4196
4220
  // TODO: respect sort
4197
4221
  newState.followers = currentState.followers
4198
4222
  ? currentState.followers.concat(event.follow)
@@ -4233,40 +4257,60 @@ class Feed extends FeedApi {
4233
4257
  'feeds.comment.reaction.deleted': this.handleCommentReactionEvent.bind(this),
4234
4258
  'feeds.comment.reaction.updated': Feed.noop,
4235
4259
  '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;
4260
+ const { connectedUser } = this.client.state.getLatestValue();
4240
4261
  this.state.next((currentState) => {
4241
- return {
4242
- ...currentState,
4243
- // TODO: respect sort
4244
- members: currentState.members
4245
- ? currentState.members.concat(member)
4246
- : [member],
4247
- };
4262
+ let newState;
4263
+ if (!checkHasAnotherPage(currentState.members, currentState.member_pagination?.next)) {
4264
+ newState ?? (newState = {
4265
+ ...currentState,
4266
+ });
4267
+ newState.members = newState.members?.concat(event.member) ?? [
4268
+ event.member,
4269
+ ];
4270
+ }
4271
+ if (connectedUser?.id === event.member.user.id) {
4272
+ newState ?? (newState = {
4273
+ ...currentState,
4274
+ });
4275
+ newState.own_membership = event.member;
4276
+ }
4277
+ return newState ?? currentState;
4248
4278
  });
4249
4279
  },
4250
4280
  'feeds.feed_member.removed': (event) => {
4281
+ const { connectedUser } = this.client.state.getLatestValue();
4251
4282
  this.state.next((currentState) => {
4252
- return {
4283
+ const newState = {
4253
4284
  ...currentState,
4254
4285
  members: currentState.members?.filter((member) => member.user.id !== event.user?.id),
4255
4286
  };
4287
+ if (connectedUser?.id === event.member_id) {
4288
+ delete newState.own_membership;
4289
+ }
4290
+ return newState;
4256
4291
  });
4257
4292
  },
4258
4293
  'feeds.feed_member.updated': (event) => {
4294
+ const { connectedUser } = this.client.state.getLatestValue();
4259
4295
  this.state.next((currentState) => {
4260
4296
  const memberIndex = currentState.members?.findIndex((member) => member.user.id === event.member.user.id) ?? -1;
4297
+ let newState;
4261
4298
  if (memberIndex !== -1) {
4299
+ // if there's an index, there's a member to update
4262
4300
  const newMembers = [...currentState.members];
4263
4301
  newMembers[memberIndex] = event.member;
4264
- return {
4302
+ newState ?? (newState = {
4265
4303
  ...currentState,
4266
- members: newMembers,
4267
- };
4304
+ });
4305
+ newState.members = newMembers;
4268
4306
  }
4269
- return currentState;
4307
+ if (connectedUser?.id === event.member.user.id) {
4308
+ newState ?? (newState = {
4309
+ ...currentState,
4310
+ });
4311
+ newState.own_membership = event.member;
4312
+ }
4313
+ return newState ?? currentState;
4270
4314
  });
4271
4315
  },
4272
4316
  // the poll events should be removed from here
@@ -4392,29 +4436,6 @@ class Feed extends FeedApi {
4392
4436
  ...currentState,
4393
4437
  ...responseCopy,
4394
4438
  };
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
4439
  if (!request?.followers_pagination?.limit) {
4419
4440
  delete nextState.followers;
4420
4441
  }
@@ -4505,6 +4526,7 @@ class Feed extends FeedApi {
4505
4526
  });
4506
4527
  }
4507
4528
  async loadNextPageComments({ forId, base, sort, parentId, }) {
4529
+ let error;
4508
4530
  try {
4509
4531
  this.state.next((currentState) => ({
4510
4532
  ...currentState,
@@ -4519,7 +4541,7 @@ class Feed extends FeedApi {
4519
4541
  },
4520
4542
  },
4521
4543
  }));
4522
- const { next: newNextCursor = END_OF_LIST, comments } = await base();
4544
+ const { next: newNextCursor, comments } = await base();
4523
4545
  this.state.next((currentState) => {
4524
4546
  const newPagination = {
4525
4547
  ...currentState.comments_by_entity_id[forId]?.pagination,
@@ -4544,9 +4566,8 @@ class Feed extends FeedApi {
4544
4566
  };
4545
4567
  });
4546
4568
  }
4547
- catch (error) {
4548
- console.error(error);
4549
- // TODO: figure out how to handle errorss
4569
+ catch (e) {
4570
+ error = e;
4550
4571
  }
4551
4572
  finally {
4552
4573
  this.state.next((currentState) => ({
@@ -4563,15 +4584,21 @@ class Feed extends FeedApi {
4563
4584
  },
4564
4585
  }));
4565
4586
  }
4587
+ if (error) {
4588
+ throw error;
4589
+ }
4566
4590
  }
4567
4591
  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)
4592
+ const currentEntityState = this.currentState.comments_by_entity_id[activity.id];
4593
+ const currentPagination = currentEntityState?.pagination;
4594
+ const currentNextCursor = currentPagination?.next;
4595
+ const currentSort = currentPagination?.sort;
4596
+ const isLoading = currentPagination?.loading_next_page;
4597
+ const sort = currentSort ?? request?.sort ?? Constants.DEFAULT_COMMENT_PAGINATION;
4598
+ if (isLoading ||
4599
+ !checkHasAnotherPage(currentEntityState?.comments, currentNextCursor)) {
4574
4600
  return;
4601
+ }
4575
4602
  await this.loadNextPageComments({
4576
4603
  forId: activity.id,
4577
4604
  base: () => this.client.getComments({
@@ -4585,20 +4612,25 @@ class Feed extends FeedApi {
4585
4612
  });
4586
4613
  }
4587
4614
  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)
4615
+ const currentEntityState = this.currentState.comments_by_entity_id[comment.id];
4616
+ const currentPagination = currentEntityState?.pagination;
4617
+ const currentNextCursor = currentPagination?.next;
4618
+ const currentSort = currentPagination?.sort;
4619
+ const isLoading = currentPagination?.loading_next_page;
4620
+ const sort = currentSort ?? request?.sort ?? Constants.DEFAULT_COMMENT_PAGINATION;
4621
+ if (isLoading ||
4622
+ !checkHasAnotherPage(currentEntityState?.comments, currentNextCursor)) {
4594
4623
  return;
4624
+ }
4595
4625
  await this.loadNextPageComments({
4596
4626
  forId: comment.id,
4597
4627
  base: () => this.client.getCommentReplies({
4598
4628
  ...request,
4599
4629
  comment_id: comment.id,
4600
4630
  // use known sort first (prevents broken pagination)
4601
- sort: currentSort ?? request?.sort ?? DEFAULT_COMMENT_PAGINATION,
4631
+ sort: currentSort ??
4632
+ request?.sort ??
4633
+ Constants.DEFAULT_COMMENT_PAGINATION,
4602
4634
  next: currentNextCursor,
4603
4635
  }),
4604
4636
  parentId: comment.parent_id ?? comment.object_id,
@@ -4608,10 +4640,14 @@ class Feed extends FeedApi {
4608
4640
  async loadNextPageFollows(type, request) {
4609
4641
  const paginationKey = `${type}_pagination`;
4610
4642
  const method = `query${capitalize(type)}`;
4643
+ const currentFollows = this.currentState[type];
4611
4644
  const currentNextCursor = this.currentState[paginationKey]?.next;
4612
4645
  const isLoading = this.currentState[paginationKey]?.loading_next_page;
4613
- if (isLoading || currentNextCursor === END_OF_LIST)
4646
+ const sort = this.currentState[paginationKey]?.sort ?? request.sort;
4647
+ let error;
4648
+ if (isLoading || !checkHasAnotherPage(currentFollows, currentNextCursor)) {
4614
4649
  return;
4650
+ }
4615
4651
  try {
4616
4652
  this.state.next((currentState) => {
4617
4653
  return {
@@ -4622,9 +4658,10 @@ class Feed extends FeedApi {
4622
4658
  },
4623
4659
  };
4624
4660
  });
4625
- const { next: newNextCursor = END_OF_LIST, follows } = await this[method]({
4661
+ const { next: newNextCursor, follows } = await this[method]({
4626
4662
  ...request,
4627
4663
  next: currentNextCursor,
4664
+ sort,
4628
4665
  });
4629
4666
  this.state.next((currentState) => ({
4630
4667
  ...currentState,
@@ -4634,12 +4671,12 @@ class Feed extends FeedApi {
4634
4671
  [paginationKey]: {
4635
4672
  ...currentState[paginationKey],
4636
4673
  next: newNextCursor,
4674
+ sort,
4637
4675
  },
4638
4676
  }));
4639
4677
  }
4640
- catch (error) {
4641
- console.error(error);
4642
- // TODO: figure out how to handle errorss
4678
+ catch (e) {
4679
+ error = e;
4643
4680
  }
4644
4681
  finally {
4645
4682
  this.state.next((currentState) => {
@@ -4652,6 +4689,9 @@ class Feed extends FeedApi {
4652
4689
  };
4653
4690
  });
4654
4691
  }
4692
+ if (error) {
4693
+ throw error;
4694
+ }
4655
4695
  }
4656
4696
  async loadNextPageFollowers(request) {
4657
4697
  await this.loadNextPageFollows('followers', request);
@@ -4659,6 +4699,59 @@ class Feed extends FeedApi {
4659
4699
  async loadNextPageFollowing(request) {
4660
4700
  await this.loadNextPageFollows('following', request);
4661
4701
  }
4702
+ async loadNextPageMembers(request) {
4703
+ const currentMembers = this.currentState.members;
4704
+ const currentNextCursor = this.currentState.member_pagination?.next;
4705
+ const isLoading = this.currentState.member_pagination?.loading_next_page;
4706
+ const sort = this.currentState.member_pagination?.sort ?? request.sort;
4707
+ let error;
4708
+ if (isLoading || !checkHasAnotherPage(currentMembers, currentNextCursor)) {
4709
+ return;
4710
+ }
4711
+ try {
4712
+ this.state.next((currentState) => ({
4713
+ ...currentState,
4714
+ member_pagination: {
4715
+ ...currentState.member_pagination,
4716
+ loading_next_page: true,
4717
+ },
4718
+ }));
4719
+ const { next: newNextCursor, members } = await this.client.queryFeedMembers({
4720
+ ...request,
4721
+ sort,
4722
+ feed_id: this.id,
4723
+ feed_group_id: this.group,
4724
+ next: currentNextCursor,
4725
+ });
4726
+ this.state.next((currentState) => ({
4727
+ ...currentState,
4728
+ members: currentState.members
4729
+ ? currentState.members.concat(members)
4730
+ : members,
4731
+ member_pagination: {
4732
+ ...currentState.member_pagination,
4733
+ next: newNextCursor,
4734
+ // set sort if not defined yet
4735
+ sort: currentState.member_pagination?.sort ?? request.sort,
4736
+ },
4737
+ }));
4738
+ }
4739
+ catch (e) {
4740
+ error = e;
4741
+ }
4742
+ finally {
4743
+ this.state.next((currentState) => ({
4744
+ ...currentState,
4745
+ member_pagination: {
4746
+ ...currentState.member_pagination,
4747
+ loading_next_page: false,
4748
+ },
4749
+ }));
4750
+ }
4751
+ if (error) {
4752
+ throw error;
4753
+ }
4754
+ }
4662
4755
  /**
4663
4756
  * Method which queries followers of this feed (feeds which target this feed).
4664
4757
  *
@@ -5149,6 +5242,9 @@ class FeedsClient extends FeedsApi {
5149
5242
  throw err;
5150
5243
  }
5151
5244
  };
5245
+ this.devToken = (userId) => {
5246
+ return streamDevToken(userId);
5247
+ };
5152
5248
  this.closePoll = async (request) => {
5153
5249
  return await this.updatePollPartial({
5154
5250
  poll_id: request.poll_id,
@@ -5870,10 +5966,5 @@ class FeedSearchSource extends BaseSearchSource {
5870
5966
  }
5871
5967
  }
5872
5968
 
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 };
5969
+ export { ActivitySearchSource, BaseSearchSource, ChannelOwnCapability, Constants, Feed, FeedOwnCapability, FeedSearchSource, FeedsClient, MergedStateStore, SearchController, StateStore, StreamApiError, StreamPoll, UserSearchSource, checkHasAnotherPage, isCommentResponse, isImageFile, isPatch, isVideoFile, isVoteAnswer };
5879
5970
  //# sourceMappingURL=index.browser.js.map