@stream-io/feeds-client 0.1.5 → 0.1.7

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 (44) hide show
  1. package/@react-bindings/hooks/feed-state-hooks/index.ts +2 -0
  2. package/CHANGELOG.md +25 -0
  3. package/dist/@react-bindings/hooks/client-state-hooks/useWsConnectionState.d.ts +1 -1
  4. package/dist/@react-bindings/hooks/feed-state-hooks/index.d.ts +2 -0
  5. package/dist/@react-bindings/hooks/feed-state-hooks/useComments.d.ts +2 -2
  6. package/dist/@react-bindings/hooks/feed-state-hooks/useFeedActivities.d.ts +2 -2
  7. package/dist/@react-bindings/hooks/feed-state-hooks/useFeedMetadata.d.ts +12 -0
  8. package/dist/@react-bindings/hooks/feed-state-hooks/useFollowers.d.ts +2 -2
  9. package/dist/@react-bindings/hooks/feed-state-hooks/useFollowing.d.ts +2 -2
  10. package/dist/@react-bindings/hooks/feed-state-hooks/useOwnCapabilities.d.ts +30 -30
  11. package/dist/@react-bindings/hooks/feed-state-hooks/useOwnFollows.d.ts +8 -0
  12. package/dist/index-react-bindings.browser.cjs +147 -85
  13. package/dist/index-react-bindings.browser.cjs.map +1 -1
  14. package/dist/index-react-bindings.browser.js +146 -86
  15. package/dist/index-react-bindings.browser.js.map +1 -1
  16. package/dist/index-react-bindings.node.cjs +147 -85
  17. package/dist/index-react-bindings.node.cjs.map +1 -1
  18. package/dist/index-react-bindings.node.js +146 -86
  19. package/dist/index-react-bindings.node.js.map +1 -1
  20. package/dist/index.browser.cjs +67 -37
  21. package/dist/index.browser.cjs.map +1 -1
  22. package/dist/index.browser.js +67 -37
  23. package/dist/index.browser.js.map +1 -1
  24. package/dist/index.node.cjs +67 -37
  25. package/dist/index.node.cjs.map +1 -1
  26. package/dist/index.node.js +67 -37
  27. package/dist/index.node.js.map +1 -1
  28. package/dist/src/FeedsClient.d.ts +2 -2
  29. package/dist/src/gen/feeds/FeedApi.d.ts +3 -0
  30. package/dist/src/gen/feeds/FeedsApi.d.ts +5 -0
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +6 -3
  33. package/src/Feed.ts +17 -11
  34. package/src/FeedsClient.ts +15 -10
  35. package/src/common/ActivitySearchSource.ts +5 -5
  36. package/src/common/ApiClient.ts +1 -1
  37. package/src/common/FeedSearchSource.ts +1 -1
  38. package/src/common/Poll.ts +35 -10
  39. package/src/common/TokenManager.ts +2 -3
  40. package/src/common/UserSearchSource.ts +1 -1
  41. package/src/gen/feeds/FeedApi.ts +10 -0
  42. package/src/gen/feeds/FeedsApi.ts +25 -0
  43. package/src/state-updates/bookmark-utils.test.ts +134 -8
  44. package/src/state-updates/bookmark-utils.ts +17 -7
@@ -2246,6 +2246,18 @@ class FeedsApi {
2246
2246
  decoders.RejectFeedMemberInviteResponse?.(response.body);
2247
2247
  return { ...response.body, metadata: response.metadata };
2248
2248
  }
2249
+ async stopWatchingFeed(request) {
2250
+ const queryParams = {
2251
+ connection_id: request?.connection_id,
2252
+ };
2253
+ const pathParams = {
2254
+ feed_group_id: request?.feed_group_id,
2255
+ feed_id: request?.feed_id,
2256
+ };
2257
+ const response = await this.apiClient.sendRequest('DELETE', '/api/v2/feeds/feed_groups/{feed_group_id}/feeds/{feed_id}/watch', pathParams, queryParams);
2258
+ decoders.Response?.(response.body);
2259
+ return { ...response.body, metadata: response.metadata };
2260
+ }
2249
2261
  async getFollowSuggestions(request) {
2250
2262
  const queryParams = {
2251
2263
  limit: request?.limit,
@@ -2783,7 +2795,7 @@ class TokenManager {
2783
2795
  }
2784
2796
  catch (e) {
2785
2797
  const numberOfFailures = ++previousFailuresCount;
2786
- await sleep(retryInterval(numberOfFailures));
2798
+ await sleep(1000);
2787
2799
  if (numberOfFailures === 3) {
2788
2800
  this.loadTokenPromise = null;
2789
2801
  return reject(new Error(`Stream error: tried to get token ${numberOfFailures} times, but it failed with ${e}. Check your token provider`, { cause: e }));
@@ -3631,7 +3643,7 @@ class ApiClient {
3631
3643
  rate_limit: getRateLimitFromResponseHeader(response_headers),
3632
3644
  };
3633
3645
  };
3634
- this.baseUrl = options?.base_url ?? 'https://video.stream-io-api.com';
3646
+ this.baseUrl = options?.base_url ?? 'https://feeds.stream-io-api.com';
3635
3647
  this.timeout = options?.timeout ?? 3000;
3636
3648
  this.axiosInstance = axios.create({
3637
3649
  baseURL: this.baseUrl,
@@ -3795,6 +3807,13 @@ class FeedApi {
3795
3807
  ...request,
3796
3808
  });
3797
3809
  }
3810
+ stopWatching(request) {
3811
+ return this.feedsApi.stopWatchingFeed({
3812
+ feed_id: this.id,
3813
+ feed_group_id: this.group,
3814
+ ...request,
3815
+ });
3816
+ }
3798
3817
  }
3799
3818
 
3800
3819
  const addActivitiesToState = (newActivities, activities, position) => {
@@ -3925,6 +3944,13 @@ const removeReactionFromActivities = (event, activities, isCurrentUser) => {
3925
3944
  return updateActivityInActivities$1(updatedActivity, activities);
3926
3945
  };
3927
3946
 
3947
+ // Helper function to check if two bookmarks are the same
3948
+ // A bookmark is identified by activity_id + folder_id + user_id
3949
+ const isSameBookmark = (bookmark1, bookmark2) => {
3950
+ return (bookmark1.user.id === bookmark2.user.id &&
3951
+ bookmark1.activity.id === bookmark2.activity.id &&
3952
+ bookmark1.folder?.id === bookmark2.folder?.id);
3953
+ };
3928
3954
  const updateActivityInActivities = (updatedActivity, activities) => {
3929
3955
  const index = activities.findIndex((a) => a.id === updatedActivity.id);
3930
3956
  if (index !== -1) {
@@ -3951,8 +3977,7 @@ const addBookmarkToActivity = (event, activity, isCurrentUser) => {
3951
3977
  const removeBookmarkFromActivity = (event, activity, isCurrentUser) => {
3952
3978
  // Update own_bookmarks if the bookmark is from the current user
3953
3979
  const ownBookmarks = isCurrentUser
3954
- ? (activity.own_bookmarks || []).filter((bookmark) => bookmark.user.id !== event.bookmark.user.id ||
3955
- bookmark.activity.id !== event.bookmark.activity.id)
3980
+ ? (activity.own_bookmarks || []).filter((bookmark) => !isSameBookmark(bookmark, event.bookmark))
3956
3981
  : activity.own_bookmarks;
3957
3982
  return {
3958
3983
  ...activity,
@@ -3964,8 +3989,7 @@ const updateBookmarkInActivity = (event, activity, isCurrentUser) => {
3964
3989
  // Update own_bookmarks if the bookmark is from the current user
3965
3990
  let ownBookmarks = activity.own_bookmarks || [];
3966
3991
  if (isCurrentUser) {
3967
- const bookmarkIndex = ownBookmarks.findIndex((bookmark) => bookmark.user.id === event.bookmark.user.id &&
3968
- bookmark.activity.id === event.bookmark.activity.id);
3992
+ const bookmarkIndex = ownBookmarks.findIndex((bookmark) => isSameBookmark(bookmark, event.bookmark));
3969
3993
  if (bookmarkIndex !== -1) {
3970
3994
  ownBookmarks = [...ownBookmarks];
3971
3995
  ownBookmarks[bookmarkIndex] = event.bookmark;
@@ -4047,7 +4071,7 @@ class Feed extends FeedApi {
4047
4071
  },
4048
4072
  'feeds.activity.reaction.added': (event) => {
4049
4073
  const currentActivities = this.currentState.activities;
4050
- const connectedUser = this.client.state.getLatestValue().connectedUser;
4074
+ const connectedUser = this.client.state.getLatestValue().connected_user;
4051
4075
  const isCurrentUser = Boolean(connectedUser && event.reaction.user.id === connectedUser.id);
4052
4076
  const result = addReactionToActivities(event, currentActivities, isCurrentUser);
4053
4077
  if (result.changed) {
@@ -4056,7 +4080,7 @@ class Feed extends FeedApi {
4056
4080
  },
4057
4081
  'feeds.activity.reaction.deleted': (event) => {
4058
4082
  const currentActivities = this.currentState.activities;
4059
- const connectedUser = this.client.state.getLatestValue().connectedUser;
4083
+ const connectedUser = this.client.state.getLatestValue().connected_user;
4060
4084
  const isCurrentUser = Boolean(connectedUser && event.reaction.user.id === connectedUser.id);
4061
4085
  const result = removeReactionFromActivities(event, currentActivities, isCurrentUser);
4062
4086
  if (result.changed) {
@@ -4195,7 +4219,7 @@ class Feed extends FeedApi {
4195
4219
  // someone followed this feed
4196
4220
  event.follow.target_feed.fid === this.fid) {
4197
4221
  const source = event.follow.source_feed;
4198
- const connectedUser = this.client.state.getLatestValue().connectedUser;
4222
+ const connectedUser = this.client.state.getLatestValue().connected_user;
4199
4223
  this.state.next((currentState) => {
4200
4224
  const newState = { ...currentState, ...event.follow.target_feed };
4201
4225
  if (source.created_by.id === connectedUser?.id) {
@@ -4228,7 +4252,7 @@ class Feed extends FeedApi {
4228
4252
  // someone unfollowed this feed
4229
4253
  event.follow.target_feed.fid === this.fid) {
4230
4254
  const source = event.follow.source_feed;
4231
- const connectedUser = this.client.state.getLatestValue().connectedUser;
4255
+ const connectedUser = this.client.state.getLatestValue().connected_user;
4232
4256
  this.state.next((currentState) => {
4233
4257
  const newState = { ...currentState, ...event.follow.target_feed };
4234
4258
  if (source.created_by.id === connectedUser?.id) {
@@ -4244,7 +4268,7 @@ class Feed extends FeedApi {
4244
4268
  'feeds.comment.reaction.deleted': this.handleCommentReactionEvent.bind(this),
4245
4269
  'feeds.comment.reaction.updated': Feed.noop,
4246
4270
  'feeds.feed_member.added': (event) => {
4247
- const { connectedUser } = this.client.state.getLatestValue();
4271
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4248
4272
  this.state.next((currentState) => {
4249
4273
  let newState;
4250
4274
  if (!checkHasAnotherPage(currentState.members, currentState.member_pagination?.next)) {
@@ -4265,7 +4289,7 @@ class Feed extends FeedApi {
4265
4289
  });
4266
4290
  },
4267
4291
  'feeds.feed_member.removed': (event) => {
4268
- const { connectedUser } = this.client.state.getLatestValue();
4292
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4269
4293
  this.state.next((currentState) => {
4270
4294
  const newState = {
4271
4295
  ...currentState,
@@ -4278,7 +4302,7 @@ class Feed extends FeedApi {
4278
4302
  });
4279
4303
  },
4280
4304
  'feeds.feed_member.updated': (event) => {
4281
- const { connectedUser } = this.client.state.getLatestValue();
4305
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4282
4306
  this.state.next((currentState) => {
4283
4307
  const memberIndex = currentState.members?.findIndex((member) => member.user.id === event.member.user.id) ?? -1;
4284
4308
  let newState;
@@ -4343,7 +4367,7 @@ class Feed extends FeedApi {
4343
4367
  }
4344
4368
  handleCommentReactionEvent(event) {
4345
4369
  const { comment, reaction } = event;
4346
- const connectedUser = this.client.state.getLatestValue().connectedUser;
4370
+ const connectedUser = this.client.state.getLatestValue().connected_user;
4347
4371
  this.state.next((currentState) => {
4348
4372
  const forId = comment.parent_id ?? comment.object_id;
4349
4373
  const entityState = currentState.comments_by_entity_id[forId];
@@ -4445,7 +4469,7 @@ class Feed extends FeedApi {
4445
4469
  }
4446
4470
  handleBookmarkAdded(event) {
4447
4471
  const currentActivities = this.currentState.activities;
4448
- const { connectedUser } = this.client.state.getLatestValue();
4472
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4449
4473
  const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4450
4474
  const result = addBookmarkToActivities(event, currentActivities, isCurrentUser);
4451
4475
  if (result.changed) {
@@ -4454,7 +4478,7 @@ class Feed extends FeedApi {
4454
4478
  }
4455
4479
  handleBookmarkDeleted(event) {
4456
4480
  const currentActivities = this.currentState.activities;
4457
- const { connectedUser } = this.client.state.getLatestValue();
4481
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4458
4482
  const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4459
4483
  const result = removeBookmarkFromActivities(event, currentActivities, isCurrentUser);
4460
4484
  if (result.changed) {
@@ -4463,7 +4487,7 @@ class Feed extends FeedApi {
4463
4487
  }
4464
4488
  handleBookmarkUpdated(event) {
4465
4489
  const currentActivities = this.currentState.activities;
4466
- const { connectedUser } = this.client.state.getLatestValue();
4490
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4467
4491
  const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4468
4492
  const result = updateBookmarkInActivities(event, currentActivities, isCurrentUser);
4469
4493
  if (result.changed) {
@@ -5018,7 +5042,8 @@ class StreamPoll {
5018
5042
  if (!isPollVoteCastedEvent(event))
5019
5043
  return;
5020
5044
  const currentState = this.data;
5021
- const isOwnVote = event.poll_vote.user_id === this.client.state.getLatestValue().connectedUser?.id;
5045
+ const isOwnVote = event.poll_vote.user_id ===
5046
+ this.client.state.getLatestValue().connected_user?.id;
5022
5047
  let latestAnswers = [...currentState.latest_answers];
5023
5048
  let ownAnswer = currentState.own_answer;
5024
5049
  const ownVotesByOptionId = currentState.own_votes_by_option_id;
@@ -5042,7 +5067,7 @@ class StreamPoll {
5042
5067
  else {
5043
5068
  maxVotedOptionIds = getMaxVotedOptionIds(event.poll.vote_counts_by_option);
5044
5069
  }
5045
- const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option } = event.poll;
5070
+ const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option, } = event.poll;
5046
5071
  this.state.partialNext({
5047
5072
  answers_count,
5048
5073
  // @ts-expect-error Incompatibility between PollResponseData and Poll due to teams_role, remove when OpenAPI spec is fixed
@@ -5063,7 +5088,8 @@ class StreamPoll {
5063
5088
  if (!isPollVoteChangedEvent(event))
5064
5089
  return;
5065
5090
  const currentState = this.data;
5066
- const isOwnVote = event.poll_vote.user_id === this.client.state.getLatestValue().connectedUser?.id;
5091
+ const isOwnVote = event.poll_vote.user_id ===
5092
+ this.client.state.getLatestValue().connected_user?.id;
5067
5093
  let latestAnswers = [...currentState.latest_answers];
5068
5094
  let ownAnswer = currentState.own_answer;
5069
5095
  let ownVotesByOptionId = currentState.own_votes_by_option_id;
@@ -5110,7 +5136,7 @@ class StreamPoll {
5110
5136
  else {
5111
5137
  maxVotedOptionIds = getMaxVotedOptionIds(event.poll.vote_counts_by_option);
5112
5138
  }
5113
- const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option } = event.poll;
5139
+ const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option, } = event.poll;
5114
5140
  this.state.partialNext({
5115
5141
  answers_count,
5116
5142
  // @ts-expect-error Incompatibility between PollResponseData and Poll due to teams_role, remove when OpenAPI spec is fixed
@@ -5130,7 +5156,8 @@ class StreamPoll {
5130
5156
  if (!isPollVoteRemovedEvent(event))
5131
5157
  return;
5132
5158
  const currentState = this.data;
5133
- const isOwnVote = event.poll_vote.user_id === this.client.state.getLatestValue().connectedUser?.id;
5159
+ const isOwnVote = event.poll_vote.user_id ===
5160
+ this.client.state.getLatestValue().connected_user?.id;
5134
5161
  let latestAnswers = [...currentState.latest_answers];
5135
5162
  let ownAnswer = currentState.own_answer;
5136
5163
  const ownVotesByOptionId = { ...currentState.own_votes_by_option_id };
@@ -5148,7 +5175,7 @@ class StreamPoll {
5148
5175
  delete ownVotesByOptionId[event.poll_vote.option_id];
5149
5176
  }
5150
5177
  }
5151
- const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option } = event.poll;
5178
+ const { answers_count, latest_votes_by_option, vote_count, vote_counts_by_option, } = event.poll;
5152
5179
  this.state.partialNext({
5153
5180
  answers_count,
5154
5181
  // @ts-expect-error Incompatibility between PollResponseData and Poll due to teams_role, remove when OpenAPI spec is fixed
@@ -5206,7 +5233,7 @@ class FeedsClient extends FeedsApi {
5206
5233
  this.healthyConnectionChangedEventCount = 0;
5207
5234
  this.pollFromState = (id) => this.polls_by_id.get(id);
5208
5235
  this.connectUser = async (user, tokenProvider) => {
5209
- if (this.state.getLatestValue().connectedUser !== undefined ||
5236
+ if (this.state.getLatestValue().connected_user !== undefined ||
5210
5237
  this.wsConnection) {
5211
5238
  throw new Error(`Can't connect a new user, call "disconnectUser" first`);
5212
5239
  }
@@ -5220,8 +5247,8 @@ class FeedsClient extends FeedsApi {
5220
5247
  this.wsConnection.on('all', (event) => this.eventDispatcher.dispatch(event));
5221
5248
  const connectedEvent = await this.wsConnection.connect();
5222
5249
  this.state.partialNext({
5223
- connectedUser: connectedEvent?.me,
5224
- isWsConnectionHealthy: this.wsConnection.isHealthy,
5250
+ connected_user: connectedEvent?.me,
5251
+ is_ws_connection_healthy: !!this.wsConnection?.isHealthy,
5225
5252
  });
5226
5253
  }
5227
5254
  catch (err) {
@@ -5280,7 +5307,10 @@ class FeedsClient extends FeedsApi {
5280
5307
  removeConnectionEventListeners(this.updateNetworkConnectionStatus);
5281
5308
  this.connectionIdManager.reset();
5282
5309
  this.tokenManager.reset();
5283
- this.state.partialNext({ connectedUser: undefined, isWsConnectionHealthy: false });
5310
+ this.state.partialNext({
5311
+ connected_user: undefined,
5312
+ is_ws_connection_healthy: false,
5313
+ });
5284
5314
  };
5285
5315
  this.on = this.eventDispatcher.on;
5286
5316
  this.off = this.eventDispatcher.off;
@@ -5306,8 +5336,8 @@ class FeedsClient extends FeedsApi {
5306
5336
  }
5307
5337
  };
5308
5338
  this.state = new StateStore({
5309
- connectedUser: undefined,
5310
- isWsConnectionHealthy: false,
5339
+ connected_user: undefined,
5340
+ is_ws_connection_healthy: false,
5311
5341
  });
5312
5342
  this.moderation = new ModerationClient(apiClient);
5313
5343
  this.tokenManager = tokenManager;
@@ -5319,7 +5349,7 @@ class FeedsClient extends FeedsApi {
5319
5349
  switch (event.type) {
5320
5350
  case 'connection.changed': {
5321
5351
  const { online } = event;
5322
- this.state.partialNext({ isWsConnectionHealthy: online });
5352
+ this.state.partialNext({ is_ws_connection_healthy: online });
5323
5353
  if (online) {
5324
5354
  this.healthyConnectionChangedEventCount++;
5325
5355
  // we skip the first event as we could potentially be querying twice
@@ -5503,11 +5533,11 @@ const useFeedsClient = () => {
5503
5533
  */
5504
5534
  const useClientConnectedUser = () => {
5505
5535
  const client = useFeedsClient();
5506
- const { user } = useStateStore(client?.state, selector$5) ?? {};
5536
+ const { user } = useStateStore(client?.state, selector$7) ?? {};
5507
5537
  return user;
5508
5538
  };
5509
- const selector$5 = (nextState) => ({
5510
- user: nextState.connectedUser,
5539
+ const selector$7 = (nextState) => ({
5540
+ user: nextState.connected_user,
5511
5541
  });
5512
5542
 
5513
5543
  /**
@@ -5515,11 +5545,11 @@ const selector$5 = (nextState) => ({
5515
5545
  */
5516
5546
  const useWsConnectionState = () => {
5517
5547
  const client = useFeedsClient();
5518
- const { isHealthy } = useStateStore(client?.state, selector$4) ?? {};
5519
- return { isHealthy };
5548
+ const { is_healthy } = useStateStore(client?.state, selector$6) ?? {};
5549
+ return { is_healthy };
5520
5550
  };
5521
- const selector$4 = (nextState) => ({
5522
- isHealthy: nextState.isWsConnectionHealthy,
5551
+ const selector$6 = (nextState) => ({
5552
+ is_healthy: nextState.is_ws_connection_healthy,
5523
5553
  });
5524
5554
 
5525
5555
  const StreamFeedContext = react.createContext(undefined);
@@ -5568,19 +5598,19 @@ const useStableCallback = (callback) => {
5568
5598
  const useFeedActivities = (feedFromProps) => {
5569
5599
  const feedFromContext = useFeedContext();
5570
5600
  const feed = feedFromProps ?? feedFromContext;
5571
- const data = useStateStore(feed?.state, selector$3);
5601
+ const data = useStateStore(feed?.state, selector$5);
5572
5602
  const loadNextPage = useStableCallback(async () => {
5573
- if (!feed || !data?.hasNextPage || data?.isLoading) {
5603
+ if (!feed || !data?.has_next_page || data?.is_loading) {
5574
5604
  return;
5575
5605
  }
5576
5606
  await feed.getNextPage();
5577
5607
  });
5578
5608
  return react.useMemo(() => ({ ...data, loadNextPage }), [data, loadNextPage]);
5579
5609
  };
5580
- const selector$3 = (nextState) => ({
5581
- isLoading: nextState.is_loading_activities,
5582
- hasNextPage: typeof nextState.next !== 'undefined',
5583
- activities: nextState.activities ?? [],
5610
+ const selector$5 = ({ is_loading_activities, next, activities = [] }) => ({
5611
+ is_loading: is_loading_activities,
5612
+ has_next_page: typeof next !== 'undefined',
5613
+ activities,
5584
5614
  });
5585
5615
 
5586
5616
  function useComments({ feed: feedFromProps, parent, }) {
@@ -5609,8 +5639,8 @@ function useComments({ feed: feedFromProps, parent, }) {
5609
5639
  }
5610
5640
  return {
5611
5641
  ...data,
5612
- hasNextPage: checkHasAnotherPage(data.comments, data.comments_pagination?.next),
5613
- isLoadingNextPage: data?.comments_pagination?.loading_next_page ?? false,
5642
+ has_next_page: checkHasAnotherPage(data.comments, data.comments_pagination?.next),
5643
+ is_loading_next_page: data?.comments_pagination?.loading_next_page ?? false,
5614
5644
  loadNextPage,
5615
5645
  };
5616
5646
  }, [data, loadNextPage]);
@@ -5650,48 +5680,48 @@ const FeedOwnCapability = {
5650
5680
  };
5651
5681
 
5652
5682
  const stableEmptyArray = [];
5653
- const selector$2 = (currentState) => ({
5683
+ const selector$4 = (currentState) => ({
5654
5684
  oc: currentState.own_capabilities ?? stableEmptyArray,
5655
5685
  });
5656
5686
  const useOwnCapabilities = (feedFromProps) => {
5657
5687
  const feedFromContext = useFeedContext();
5658
5688
  const feed = feedFromProps ?? feedFromContext;
5659
- const { oc = stableEmptyArray } = useStateStore(feed?.state, selector$2) ?? {};
5689
+ const { oc = stableEmptyArray } = useStateStore(feed?.state, selector$4) ?? {};
5660
5690
  return react.useMemo(() => ({
5661
- canAddActivity: oc.indexOf(FeedOwnCapability.ADD_ACTIVITY) > -1,
5662
- canAddActivityReaction: oc.indexOf(FeedOwnCapability.ADD_ACTIVITY_REACTION) > -1,
5663
- canAddComment: oc.indexOf(FeedOwnCapability.ADD_COMMENT) > -1,
5664
- canAddCommentReaction: oc.indexOf(FeedOwnCapability.ADD_COMMENT_REACTION) > -1,
5665
- canBookmarkActivity: oc.indexOf(FeedOwnCapability.BOOKMARK_ACTIVITY) > -1,
5666
- canCreateFeed: oc.indexOf(FeedOwnCapability.CREATE_FEED) > -1,
5667
- canDeleteBookmark: oc.indexOf(FeedOwnCapability.DELETE_BOOKMARK) > -1,
5668
- canDeleteComment: oc.indexOf(FeedOwnCapability.DELETE_COMMENT) > -1,
5669
- canDeleteFeed: oc.indexOf(FeedOwnCapability.DELETE_FEED) > -1,
5670
- canEditBookmark: oc.indexOf(FeedOwnCapability.EDIT_BOOKMARK) > -1,
5671
- canFollow: oc.indexOf(FeedOwnCapability.FOLLOW) > -1,
5672
- canRemoveActivity: oc.indexOf(FeedOwnCapability.REMOVE_ACTIVITY) > -1,
5673
- canRemoveActivityReaction: oc.indexOf(FeedOwnCapability.REMOVE_ACTIVITY_REACTION) > -1,
5674
- canRemoveCommentReaction: oc.indexOf(FeedOwnCapability.REMOVE_COMMENT_REACTION) > -1,
5675
- canUnfollow: oc.indexOf(FeedOwnCapability.UNFOLLOW) > -1,
5676
- canUpdateFeed: oc.indexOf(FeedOwnCapability.UPDATE_FEED) > -1,
5677
- canInviteFeed: oc.indexOf(FeedOwnCapability.INVITE_FEED) > -1,
5678
- canJoinFeed: oc.indexOf(FeedOwnCapability.JOIN_FEED) > -1,
5679
- canLeaveFeed: oc.indexOf(FeedOwnCapability.LEAVE_FEED) > -1,
5680
- canManageFeedGroup: oc.indexOf(FeedOwnCapability.MANAGE_FEED_GROUP) > -1,
5681
- canMarkActivity: oc.indexOf(FeedOwnCapability.MARK_ACTIVITY) > -1,
5682
- canPinActivity: oc.indexOf(FeedOwnCapability.PIN_ACTIVITY) > -1,
5683
- canQueryFeedMembers: oc.indexOf(FeedOwnCapability.QUERY_FEED_MEMBERS) > -1,
5684
- canQueryFollows: oc.indexOf(FeedOwnCapability.QUERY_FOLLOWS) > -1,
5685
- canReadActivities: oc.indexOf(FeedOwnCapability.READ_ACTIVITIES) > -1,
5686
- canReadFeed: oc.indexOf(FeedOwnCapability.READ_FEED) > -1,
5687
- canUpdateActivity: oc.indexOf(FeedOwnCapability.UPDATE_ACTIVITY) > -1,
5688
- canUpdateComment: oc.indexOf(FeedOwnCapability.UPDATE_COMMENT) > -1,
5689
- canUpdateFeedFollowers: oc.indexOf(FeedOwnCapability.UPDATE_FEED_FOLLOWERS) > -1,
5690
- canUpdateFeedMembers: oc.indexOf(FeedOwnCapability.UPDATE_FEED_MEMBERS) > -1,
5691
+ can_add_activity: oc.indexOf(FeedOwnCapability.ADD_ACTIVITY) > -1,
5692
+ can_add_activity_reaction: oc.indexOf(FeedOwnCapability.ADD_ACTIVITY_REACTION) > -1,
5693
+ can_add_comment: oc.indexOf(FeedOwnCapability.ADD_COMMENT) > -1,
5694
+ can_add_comment_reaction: oc.indexOf(FeedOwnCapability.ADD_COMMENT_REACTION) > -1,
5695
+ can_bookmark_activity: oc.indexOf(FeedOwnCapability.BOOKMARK_ACTIVITY) > -1,
5696
+ can_create_feed: oc.indexOf(FeedOwnCapability.CREATE_FEED) > -1,
5697
+ can_delete_bookmark: oc.indexOf(FeedOwnCapability.DELETE_BOOKMARK) > -1,
5698
+ can_delete_comment: oc.indexOf(FeedOwnCapability.DELETE_COMMENT) > -1,
5699
+ can_delete_feed: oc.indexOf(FeedOwnCapability.DELETE_FEED) > -1,
5700
+ can_edit_bookmark: oc.indexOf(FeedOwnCapability.EDIT_BOOKMARK) > -1,
5701
+ can_follow: oc.indexOf(FeedOwnCapability.FOLLOW) > -1,
5702
+ can_remove_activity: oc.indexOf(FeedOwnCapability.REMOVE_ACTIVITY) > -1,
5703
+ can_remove_activity_reaction: oc.indexOf(FeedOwnCapability.REMOVE_ACTIVITY_REACTION) > -1,
5704
+ can_remove_comment_reaction: oc.indexOf(FeedOwnCapability.REMOVE_COMMENT_REACTION) > -1,
5705
+ can_unfollow: oc.indexOf(FeedOwnCapability.UNFOLLOW) > -1,
5706
+ can_update_feed: oc.indexOf(FeedOwnCapability.UPDATE_FEED) > -1,
5707
+ can_invite_feed: oc.indexOf(FeedOwnCapability.INVITE_FEED) > -1,
5708
+ can_join_feed: oc.indexOf(FeedOwnCapability.JOIN_FEED) > -1,
5709
+ can_leave_feed: oc.indexOf(FeedOwnCapability.LEAVE_FEED) > -1,
5710
+ can_manage_feed_group: oc.indexOf(FeedOwnCapability.MANAGE_FEED_GROUP) > -1,
5711
+ can_mark_activity: oc.indexOf(FeedOwnCapability.MARK_ACTIVITY) > -1,
5712
+ can_pin_activity: oc.indexOf(FeedOwnCapability.PIN_ACTIVITY) > -1,
5713
+ can_query_feed_members: oc.indexOf(FeedOwnCapability.QUERY_FEED_MEMBERS) > -1,
5714
+ can_query_follows: oc.indexOf(FeedOwnCapability.QUERY_FOLLOWS) > -1,
5715
+ can_read_activities: oc.indexOf(FeedOwnCapability.READ_ACTIVITIES) > -1,
5716
+ can_read_feed: oc.indexOf(FeedOwnCapability.READ_FEED) > -1,
5717
+ can_update_activity: oc.indexOf(FeedOwnCapability.UPDATE_ACTIVITY) > -1,
5718
+ can_update_comment: oc.indexOf(FeedOwnCapability.UPDATE_COMMENT) > -1,
5719
+ can_update_feed_followers: oc.indexOf(FeedOwnCapability.UPDATE_FEED_FOLLOWERS) > -1,
5720
+ can_update_feed_members: oc.indexOf(FeedOwnCapability.UPDATE_FEED_MEMBERS) > -1,
5691
5721
  }), [oc]);
5692
5722
  };
5693
5723
 
5694
- const selector$1 = ({ follower_count, followers, followers_pagination, }) => ({
5724
+ const selector$3 = ({ follower_count, followers, followers_pagination, }) => ({
5695
5725
  follower_count,
5696
5726
  followers,
5697
5727
  followers_pagination,
@@ -5699,7 +5729,7 @@ const selector$1 = ({ follower_count, followers, followers_pagination, }) => ({
5699
5729
  function useFollowers(feedFromProps) {
5700
5730
  const feedFromContext = useFeedContext();
5701
5731
  const feed = feedFromProps ?? feedFromContext;
5702
- const data = useStateStore(feed?.state, selector$1);
5732
+ const data = useStateStore(feed?.state, selector$3);
5703
5733
  const loadNextPage = react.useCallback((...options) => feed?.loadNextPageFollowers(...options), [feed]);
5704
5734
  return react.useMemo(() => {
5705
5735
  if (!data) {
@@ -5707,14 +5737,14 @@ function useFollowers(feedFromProps) {
5707
5737
  }
5708
5738
  return {
5709
5739
  ...data,
5710
- isLoadingNextPage: data.followers_pagination?.loading_next_page ?? false,
5711
- hasNextPage: checkHasAnotherPage(data.followers, data.followers_pagination?.next),
5740
+ is_loading_next_page: data.followers_pagination?.loading_next_page ?? false,
5741
+ has_next_page: checkHasAnotherPage(data.followers, data.followers_pagination?.next),
5712
5742
  loadNextPage,
5713
5743
  };
5714
5744
  }, [data, loadNextPage]);
5715
5745
  }
5716
5746
 
5717
- const selector = ({ following_count, following, following_pagination, }) => ({
5747
+ const selector$2 = ({ following_count, following, following_pagination, }) => ({
5718
5748
  following_count,
5719
5749
  following,
5720
5750
  following_pagination,
@@ -5722,7 +5752,7 @@ const selector = ({ following_count, following, following_pagination, }) => ({
5722
5752
  function useFollowing(feedFromProps) {
5723
5753
  const feedFromContext = useFeedContext();
5724
5754
  const feed = feedFromProps ?? feedFromContext;
5725
- const data = useStateStore(feed?.state, selector);
5755
+ const data = useStateStore(feed?.state, selector$2);
5726
5756
  const loadNextPage = react.useCallback((...options) => feed?.loadNextPageFollowing(...options), [feed]);
5727
5757
  return react.useMemo(() => {
5728
5758
  if (!data) {
@@ -5730,13 +5760,43 @@ function useFollowing(feedFromProps) {
5730
5760
  }
5731
5761
  return {
5732
5762
  ...data,
5733
- isLoadingNextPage: data.following_pagination?.loading_next_page ?? false,
5734
- hasNextPage: checkHasAnotherPage(data.following, data.following_pagination?.next),
5763
+ is_loading_next_page: data.following_pagination?.loading_next_page ?? false,
5764
+ has_next_page: checkHasAnotherPage(data.following, data.following_pagination?.next),
5735
5765
  loadNextPage,
5736
5766
  };
5737
5767
  }, [data, loadNextPage]);
5738
5768
  }
5739
5769
 
5770
+ /**
5771
+ * A React hook that returns a reactive object containing some often used
5772
+ * metadata for a feed.
5773
+ */
5774
+ const useFeedMetadata = (feedFromProps) => {
5775
+ const feedFromContext = useFeedContext();
5776
+ const feed = feedFromProps ?? feedFromContext;
5777
+ return useStateStore(feed?.state, selector$1);
5778
+ };
5779
+ const selector$1 = ({ follower_count = 0, following_count = 0, created_by, created_at, updated_at, }) => ({
5780
+ created_by,
5781
+ follower_count,
5782
+ following_count,
5783
+ created_at,
5784
+ updated_at,
5785
+ });
5786
+
5787
+ /**
5788
+ * A React hook that returns a reactive array of feeds that the current user
5789
+ * owns and are following the respective feed that we are observing.
5790
+ */
5791
+ const useOwnFollows = (feedFromProps) => {
5792
+ const feedFromContext = useFeedContext();
5793
+ const feed = feedFromProps ?? feedFromContext;
5794
+ return useStateStore(feed?.state, selector);
5795
+ };
5796
+ const selector = ({ own_follows }) => ({
5797
+ own_follows,
5798
+ });
5799
+
5740
5800
  /**
5741
5801
  * A utility hook that takes in an entity and a reaction type, and creates reaction actions
5742
5802
  * that can then be used on the UI. The entity can be either an ActivityResponse or a CommentResponse
@@ -5792,10 +5852,12 @@ exports.useComments = useComments;
5792
5852
  exports.useCreateFeedsClient = useCreateFeedsClient;
5793
5853
  exports.useFeedActivities = useFeedActivities;
5794
5854
  exports.useFeedContext = useFeedContext;
5855
+ exports.useFeedMetadata = useFeedMetadata;
5795
5856
  exports.useFeedsClient = useFeedsClient;
5796
5857
  exports.useFollowers = useFollowers;
5797
5858
  exports.useFollowing = useFollowing;
5798
5859
  exports.useOwnCapabilities = useOwnCapabilities;
5860
+ exports.useOwnFollows = useOwnFollows;
5799
5861
  exports.useReactionActions = useReactionActions;
5800
5862
  exports.useStateStore = useStateStore;
5801
5863
  exports.useWsConnectionState = useWsConnectionState;