@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
@@ -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;
@@ -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);
@@ -4012,8 +4022,22 @@ const updateBookmarkInActivities = (event, activities, isCurrentUser) => {
4012
4022
  return updateActivityInActivities(updatedActivity, activities);
4013
4023
  };
4014
4024
 
4015
- const END_OF_LIST = 'eol';
4016
- const DEFAULT_COMMENT_PAGINATION = 'first';
4025
+ const isImageFile = (file) => {
4026
+ // photoshop files begin with 'image/'
4027
+ return file.type.startsWith('image/') && !file.type.endsWith('.photoshop');
4028
+ };
4029
+ const isVideoFile = (file) => {
4030
+ return file.type.startsWith('video/');
4031
+ };
4032
+ const checkHasAnotherPage = (v, cursor) => (typeof v === 'undefined' && typeof cursor === 'undefined') ||
4033
+ typeof cursor === 'string';
4034
+ const isCommentResponse = (entity) => {
4035
+ return typeof entity?.object_id === 'string';
4036
+ };
4037
+ const Constants = {
4038
+ DEFAULT_COMMENT_PAGINATION: 'first',
4039
+ };
4040
+
4017
4041
  class Feed extends FeedApi {
4018
4042
  constructor(client, groupId, id, data) {
4019
4043
  // Need this ugly cast because fileUpload endpoints :(
@@ -4086,7 +4110,7 @@ class Feed extends FeedApi {
4086
4110
  const entityState = currentState.comments_by_entity_id[forId];
4087
4111
  const newComments = entityState?.comments?.concat([]) ?? [];
4088
4112
  if (entityState?.pagination?.sort === 'last' &&
4089
- entityState?.pagination.next === END_OF_LIST) {
4113
+ !checkHasAnotherPage(entityState.comments, entityState?.pagination.next)) {
4090
4114
  newComments.unshift(comment);
4091
4115
  }
4092
4116
  else if (entityState?.pagination?.sort === 'first') {
@@ -4173,7 +4197,7 @@ class Feed extends FeedApi {
4173
4197
  ...currentState,
4174
4198
  ...event.follow.source_feed,
4175
4199
  };
4176
- if (currentState.following_pagination?.next === END_OF_LIST) {
4200
+ if (!checkHasAnotherPage(currentState.following, currentState.following_pagination?.next)) {
4177
4201
  // TODO: respect sort
4178
4202
  newState.following = currentState.following
4179
4203
  ? currentState.following.concat(event.follow)
@@ -4194,7 +4218,7 @@ class Feed extends FeedApi {
4194
4218
  ? currentState.own_follows.concat(event.follow)
4195
4219
  : [event.follow];
4196
4220
  }
4197
- if (currentState.followers_pagination?.next === END_OF_LIST) {
4221
+ if (!checkHasAnotherPage(currentState.followers, currentState.followers_pagination?.next)) {
4198
4222
  // TODO: respect sort
4199
4223
  newState.followers = currentState.followers
4200
4224
  ? currentState.followers.concat(event.follow)
@@ -4235,40 +4259,60 @@ class Feed extends FeedApi {
4235
4259
  'feeds.comment.reaction.deleted': this.handleCommentReactionEvent.bind(this),
4236
4260
  'feeds.comment.reaction.updated': Feed.noop,
4237
4261
  '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;
4262
+ const { connectedUser } = this.client.state.getLatestValue();
4242
4263
  this.state.next((currentState) => {
4243
- return {
4244
- ...currentState,
4245
- // TODO: respect sort
4246
- members: currentState.members
4247
- ? currentState.members.concat(member)
4248
- : [member],
4249
- };
4264
+ let newState;
4265
+ if (!checkHasAnotherPage(currentState.members, currentState.member_pagination?.next)) {
4266
+ newState ?? (newState = {
4267
+ ...currentState,
4268
+ });
4269
+ newState.members = newState.members?.concat(event.member) ?? [
4270
+ event.member,
4271
+ ];
4272
+ }
4273
+ if (connectedUser?.id === event.member.user.id) {
4274
+ newState ?? (newState = {
4275
+ ...currentState,
4276
+ });
4277
+ newState.own_membership = event.member;
4278
+ }
4279
+ return newState ?? currentState;
4250
4280
  });
4251
4281
  },
4252
4282
  'feeds.feed_member.removed': (event) => {
4283
+ const { connectedUser } = this.client.state.getLatestValue();
4253
4284
  this.state.next((currentState) => {
4254
- return {
4285
+ const newState = {
4255
4286
  ...currentState,
4256
4287
  members: currentState.members?.filter((member) => member.user.id !== event.user?.id),
4257
4288
  };
4289
+ if (connectedUser?.id === event.member_id) {
4290
+ delete newState.own_membership;
4291
+ }
4292
+ return newState;
4258
4293
  });
4259
4294
  },
4260
4295
  'feeds.feed_member.updated': (event) => {
4296
+ const { connectedUser } = this.client.state.getLatestValue();
4261
4297
  this.state.next((currentState) => {
4262
4298
  const memberIndex = currentState.members?.findIndex((member) => member.user.id === event.member.user.id) ?? -1;
4299
+ let newState;
4263
4300
  if (memberIndex !== -1) {
4301
+ // if there's an index, there's a member to update
4264
4302
  const newMembers = [...currentState.members];
4265
4303
  newMembers[memberIndex] = event.member;
4266
- return {
4304
+ newState ?? (newState = {
4267
4305
  ...currentState,
4268
- members: newMembers,
4269
- };
4306
+ });
4307
+ newState.members = newMembers;
4270
4308
  }
4271
- return currentState;
4309
+ if (connectedUser?.id === event.member.user.id) {
4310
+ newState ?? (newState = {
4311
+ ...currentState,
4312
+ });
4313
+ newState.own_membership = event.member;
4314
+ }
4315
+ return newState ?? currentState;
4272
4316
  });
4273
4317
  },
4274
4318
  // the poll events should be removed from here
@@ -4394,29 +4438,6 @@ class Feed extends FeedApi {
4394
4438
  ...currentState,
4395
4439
  ...responseCopy,
4396
4440
  };
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
4441
  if (!request?.followers_pagination?.limit) {
4421
4442
  delete nextState.followers;
4422
4443
  }
@@ -4507,6 +4528,7 @@ class Feed extends FeedApi {
4507
4528
  });
4508
4529
  }
4509
4530
  async loadNextPageComments({ forId, base, sort, parentId, }) {
4531
+ let error;
4510
4532
  try {
4511
4533
  this.state.next((currentState) => ({
4512
4534
  ...currentState,
@@ -4521,7 +4543,7 @@ class Feed extends FeedApi {
4521
4543
  },
4522
4544
  },
4523
4545
  }));
4524
- const { next: newNextCursor = END_OF_LIST, comments } = await base();
4546
+ const { next: newNextCursor, comments } = await base();
4525
4547
  this.state.next((currentState) => {
4526
4548
  const newPagination = {
4527
4549
  ...currentState.comments_by_entity_id[forId]?.pagination,
@@ -4546,9 +4568,8 @@ class Feed extends FeedApi {
4546
4568
  };
4547
4569
  });
4548
4570
  }
4549
- catch (error) {
4550
- console.error(error);
4551
- // TODO: figure out how to handle errorss
4571
+ catch (e) {
4572
+ error = e;
4552
4573
  }
4553
4574
  finally {
4554
4575
  this.state.next((currentState) => ({
@@ -4565,15 +4586,21 @@ class Feed extends FeedApi {
4565
4586
  },
4566
4587
  }));
4567
4588
  }
4589
+ if (error) {
4590
+ throw error;
4591
+ }
4568
4592
  }
4569
4593
  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)
4594
+ const currentEntityState = this.currentState.comments_by_entity_id[activity.id];
4595
+ const currentPagination = currentEntityState?.pagination;
4596
+ const currentNextCursor = currentPagination?.next;
4597
+ const currentSort = currentPagination?.sort;
4598
+ const isLoading = currentPagination?.loading_next_page;
4599
+ const sort = currentSort ?? request?.sort ?? Constants.DEFAULT_COMMENT_PAGINATION;
4600
+ if (isLoading ||
4601
+ !checkHasAnotherPage(currentEntityState?.comments, currentNextCursor)) {
4576
4602
  return;
4603
+ }
4577
4604
  await this.loadNextPageComments({
4578
4605
  forId: activity.id,
4579
4606
  base: () => this.client.getComments({
@@ -4587,20 +4614,25 @@ class Feed extends FeedApi {
4587
4614
  });
4588
4615
  }
4589
4616
  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)
4617
+ const currentEntityState = this.currentState.comments_by_entity_id[comment.id];
4618
+ const currentPagination = currentEntityState?.pagination;
4619
+ const currentNextCursor = currentPagination?.next;
4620
+ const currentSort = currentPagination?.sort;
4621
+ const isLoading = currentPagination?.loading_next_page;
4622
+ const sort = currentSort ?? request?.sort ?? Constants.DEFAULT_COMMENT_PAGINATION;
4623
+ if (isLoading ||
4624
+ !checkHasAnotherPage(currentEntityState?.comments, currentNextCursor)) {
4596
4625
  return;
4626
+ }
4597
4627
  await this.loadNextPageComments({
4598
4628
  forId: comment.id,
4599
4629
  base: () => this.client.getCommentReplies({
4600
4630
  ...request,
4601
4631
  comment_id: comment.id,
4602
4632
  // use known sort first (prevents broken pagination)
4603
- sort: currentSort ?? request?.sort ?? DEFAULT_COMMENT_PAGINATION,
4633
+ sort: currentSort ??
4634
+ request?.sort ??
4635
+ Constants.DEFAULT_COMMENT_PAGINATION,
4604
4636
  next: currentNextCursor,
4605
4637
  }),
4606
4638
  parentId: comment.parent_id ?? comment.object_id,
@@ -4610,10 +4642,14 @@ class Feed extends FeedApi {
4610
4642
  async loadNextPageFollows(type, request) {
4611
4643
  const paginationKey = `${type}_pagination`;
4612
4644
  const method = `query${capitalize(type)}`;
4645
+ const currentFollows = this.currentState[type];
4613
4646
  const currentNextCursor = this.currentState[paginationKey]?.next;
4614
4647
  const isLoading = this.currentState[paginationKey]?.loading_next_page;
4615
- if (isLoading || currentNextCursor === END_OF_LIST)
4648
+ const sort = this.currentState[paginationKey]?.sort ?? request.sort;
4649
+ let error;
4650
+ if (isLoading || !checkHasAnotherPage(currentFollows, currentNextCursor)) {
4616
4651
  return;
4652
+ }
4617
4653
  try {
4618
4654
  this.state.next((currentState) => {
4619
4655
  return {
@@ -4624,9 +4660,10 @@ class Feed extends FeedApi {
4624
4660
  },
4625
4661
  };
4626
4662
  });
4627
- const { next: newNextCursor = END_OF_LIST, follows } = await this[method]({
4663
+ const { next: newNextCursor, follows } = await this[method]({
4628
4664
  ...request,
4629
4665
  next: currentNextCursor,
4666
+ sort,
4630
4667
  });
4631
4668
  this.state.next((currentState) => ({
4632
4669
  ...currentState,
@@ -4636,12 +4673,12 @@ class Feed extends FeedApi {
4636
4673
  [paginationKey]: {
4637
4674
  ...currentState[paginationKey],
4638
4675
  next: newNextCursor,
4676
+ sort,
4639
4677
  },
4640
4678
  }));
4641
4679
  }
4642
- catch (error) {
4643
- console.error(error);
4644
- // TODO: figure out how to handle errorss
4680
+ catch (e) {
4681
+ error = e;
4645
4682
  }
4646
4683
  finally {
4647
4684
  this.state.next((currentState) => {
@@ -4654,6 +4691,9 @@ class Feed extends FeedApi {
4654
4691
  };
4655
4692
  });
4656
4693
  }
4694
+ if (error) {
4695
+ throw error;
4696
+ }
4657
4697
  }
4658
4698
  async loadNextPageFollowers(request) {
4659
4699
  await this.loadNextPageFollows('followers', request);
@@ -4661,6 +4701,59 @@ class Feed extends FeedApi {
4661
4701
  async loadNextPageFollowing(request) {
4662
4702
  await this.loadNextPageFollows('following', request);
4663
4703
  }
4704
+ async loadNextPageMembers(request) {
4705
+ const currentMembers = this.currentState.members;
4706
+ const currentNextCursor = this.currentState.member_pagination?.next;
4707
+ const isLoading = this.currentState.member_pagination?.loading_next_page;
4708
+ const sort = this.currentState.member_pagination?.sort ?? request.sort;
4709
+ let error;
4710
+ if (isLoading || !checkHasAnotherPage(currentMembers, currentNextCursor)) {
4711
+ return;
4712
+ }
4713
+ try {
4714
+ this.state.next((currentState) => ({
4715
+ ...currentState,
4716
+ member_pagination: {
4717
+ ...currentState.member_pagination,
4718
+ loading_next_page: true,
4719
+ },
4720
+ }));
4721
+ const { next: newNextCursor, members } = await this.client.queryFeedMembers({
4722
+ ...request,
4723
+ sort,
4724
+ feed_id: this.id,
4725
+ feed_group_id: this.group,
4726
+ next: currentNextCursor,
4727
+ });
4728
+ this.state.next((currentState) => ({
4729
+ ...currentState,
4730
+ members: currentState.members
4731
+ ? currentState.members.concat(members)
4732
+ : members,
4733
+ member_pagination: {
4734
+ ...currentState.member_pagination,
4735
+ next: newNextCursor,
4736
+ // set sort if not defined yet
4737
+ sort: currentState.member_pagination?.sort ?? request.sort,
4738
+ },
4739
+ }));
4740
+ }
4741
+ catch (e) {
4742
+ error = e;
4743
+ }
4744
+ finally {
4745
+ this.state.next((currentState) => ({
4746
+ ...currentState,
4747
+ member_pagination: {
4748
+ ...currentState.member_pagination,
4749
+ loading_next_page: false,
4750
+ },
4751
+ }));
4752
+ }
4753
+ if (error) {
4754
+ throw error;
4755
+ }
4756
+ }
4664
4757
  /**
4665
4758
  * Method which queries followers of this feed (feeds which target this feed).
4666
4759
  *
@@ -5151,6 +5244,9 @@ class FeedsClient extends FeedsApi {
5151
5244
  throw err;
5152
5245
  }
5153
5246
  };
5247
+ this.devToken = (userId) => {
5248
+ return streamDevToken(userId);
5249
+ };
5154
5250
  this.closePoll = async (request) => {
5155
5251
  return await this.updatePollPartial({
5156
5252
  poll_id: request.poll_id,
@@ -5872,14 +5968,10 @@ class FeedSearchSource extends BaseSearchSource {
5872
5968
  }
5873
5969
  }
5874
5970
 
5875
- const isImageFile = (file) => {
5876
- // photoshop files begin with 'image/'
5877
- return file.type.startsWith('image/') && !file.type.endsWith('.photoshop');
5878
- };
5879
-
5880
5971
  exports.ActivitySearchSource = ActivitySearchSource;
5881
5972
  exports.BaseSearchSource = BaseSearchSource;
5882
5973
  exports.ChannelOwnCapability = ChannelOwnCapability;
5974
+ exports.Constants = Constants;
5883
5975
  exports.Feed = Feed;
5884
5976
  exports.FeedOwnCapability = FeedOwnCapability;
5885
5977
  exports.FeedSearchSource = FeedSearchSource;
@@ -5890,7 +5982,10 @@ exports.StateStore = StateStore;
5890
5982
  exports.StreamApiError = StreamApiError;
5891
5983
  exports.StreamPoll = StreamPoll;
5892
5984
  exports.UserSearchSource = UserSearchSource;
5985
+ exports.checkHasAnotherPage = checkHasAnotherPage;
5986
+ exports.isCommentResponse = isCommentResponse;
5893
5987
  exports.isImageFile = isImageFile;
5894
5988
  exports.isPatch = isPatch;
5989
+ exports.isVideoFile = isVideoFile;
5895
5990
  exports.isVoteAnswer = isVoteAnswer;
5896
5991
  //# sourceMappingURL=index.node.cjs.map