@stream-io/feeds-client 0.2.0 → 0.2.2

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 (83) hide show
  1. package/@react-bindings/hooks/feed-state-hooks/index.ts +4 -0
  2. package/CHANGELOG.md +16 -0
  3. package/dist/@react-bindings/hooks/feed-state-hooks/index.d.ts +4 -0
  4. package/dist/@react-bindings/hooks/feed-state-hooks/useAggregatedActivities.d.ts +11 -0
  5. package/dist/@react-bindings/hooks/feed-state-hooks/useIsAggregatedActivityRead.d.ts +6 -0
  6. package/dist/@react-bindings/hooks/feed-state-hooks/useIsAggregatedActivitySeen.d.ts +6 -0
  7. package/dist/@react-bindings/hooks/feed-state-hooks/useNotificationStatus.d.ts +13 -0
  8. package/dist/@react-bindings/wrappers/StreamFeed.d.ts +1 -1
  9. package/dist/index-react-bindings.browser.cjs +505 -222
  10. package/dist/index-react-bindings.browser.cjs.map +1 -1
  11. package/dist/index-react-bindings.browser.js +502 -223
  12. package/dist/index-react-bindings.browser.js.map +1 -1
  13. package/dist/index-react-bindings.node.cjs +505 -222
  14. package/dist/index-react-bindings.node.cjs.map +1 -1
  15. package/dist/index-react-bindings.node.js +502 -223
  16. package/dist/index-react-bindings.node.js.map +1 -1
  17. package/dist/index.browser.cjs +440 -205
  18. package/dist/index.browser.cjs.map +1 -1
  19. package/dist/index.browser.js +440 -206
  20. package/dist/index.browser.js.map +1 -1
  21. package/dist/index.node.cjs +440 -205
  22. package/dist/index.node.cjs.map +1 -1
  23. package/dist/index.node.js +440 -206
  24. package/dist/index.node.js.map +1 -1
  25. package/dist/src/feed/event-handlers/activity/handle-activity-deleted.d.ts +12 -3
  26. package/dist/src/feed/event-handlers/activity/handle-activity-marked.d.ts +11 -0
  27. package/dist/src/feed/event-handlers/activity/handle-activity-pinned.d.ts +3 -0
  28. package/dist/src/feed/event-handlers/activity/handle-activity-reaction-added.d.ts +10 -6
  29. package/dist/src/feed/event-handlers/activity/handle-activity-reaction-deleted.d.ts +10 -6
  30. package/dist/src/feed/event-handlers/activity/handle-activity-unpinned.d.ts +3 -0
  31. package/dist/src/feed/event-handlers/activity/handle-activity-updated.d.ts +7 -3
  32. package/dist/src/feed/event-handlers/activity/index.d.ts +1 -0
  33. package/dist/src/feed/event-handlers/bookmark/handle-bookmark-added.d.ts +10 -6
  34. package/dist/src/feed/event-handlers/bookmark/handle-bookmark-deleted.d.ts +10 -6
  35. package/dist/src/feed/event-handlers/bookmark/handle-bookmark-updated.d.ts +10 -6
  36. package/dist/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts +8 -1
  37. package/dist/src/feed/feed.d.ts +2 -2
  38. package/dist/src/gen/models/index.d.ts +36 -1
  39. package/dist/src/test-utils/response-generators.d.ts +66 -1
  40. package/dist/src/utils/index.d.ts +1 -0
  41. package/dist/src/utils/update-entity-in-array.d.ts +27 -0
  42. package/dist/tsconfig.tsbuildinfo +1 -1
  43. package/package.json +1 -1
  44. package/src/feed/event-handlers/activity/activity-marked-utils.test.ts +208 -0
  45. package/src/feed/event-handlers/activity/activity-reaction-utils.test.ts +108 -96
  46. package/src/feed/event-handlers/activity/activity-utils.test.ts +84 -122
  47. package/src/feed/event-handlers/activity/handle-activity-deleted.ts +43 -10
  48. package/src/feed/event-handlers/activity/handle-activity-marked.ts +68 -0
  49. package/src/feed/event-handlers/activity/handle-activity-pinned.test.ts +60 -0
  50. package/src/feed/event-handlers/activity/handle-activity-pinned.ts +30 -0
  51. package/src/feed/event-handlers/activity/handle-activity-reaction-added.test.ts +157 -0
  52. package/src/feed/event-handlers/activity/handle-activity-reaction-added.ts +82 -40
  53. package/src/feed/event-handlers/activity/handle-activity-reaction-deleted.test.ts +200 -0
  54. package/src/feed/event-handlers/activity/handle-activity-reaction-deleted.ts +89 -51
  55. package/src/feed/event-handlers/activity/handle-activity-unpinned.test.ts +95 -0
  56. package/src/feed/event-handlers/activity/handle-activity-unpinned.ts +30 -0
  57. package/src/feed/event-handlers/activity/handle-activity-updated.test.ts +115 -0
  58. package/src/feed/event-handlers/activity/handle-activity-updated.ts +73 -35
  59. package/src/feed/event-handlers/activity/index.ts +2 -1
  60. package/src/feed/event-handlers/bookmark/bookmark-utils.test.ts +121 -109
  61. package/src/feed/event-handlers/bookmark/handle-bookmark-added.test.ts +178 -0
  62. package/src/feed/event-handlers/bookmark/handle-bookmark-added.ts +82 -39
  63. package/src/feed/event-handlers/bookmark/handle-bookmark-deleted.test.ts +188 -0
  64. package/src/feed/event-handlers/bookmark/handle-bookmark-deleted.ts +86 -48
  65. package/src/feed/event-handlers/bookmark/handle-bookmark-updated.test.ts +196 -0
  66. package/src/feed/event-handlers/bookmark/handle-bookmark-updated.ts +83 -44
  67. package/src/feed/event-handlers/comment/handle-comment-added.test.ts +147 -0
  68. package/src/feed/event-handlers/comment/handle-comment-deleted.test.ts +133 -0
  69. package/src/feed/event-handlers/comment/handle-comment-deleted.ts +24 -10
  70. package/src/feed/event-handlers/comment/handle-comment-reaction.test.ts +315 -0
  71. package/src/feed/event-handlers/comment/handle-comment-updated.test.ts +131 -0
  72. package/src/feed/event-handlers/follow/handle-follow-created.test.ts +7 -7
  73. package/src/feed/event-handlers/follow/handle-follow-deleted.test.ts +2 -2
  74. package/src/feed/event-handlers/follow/handle-follow-updated.test.ts +1 -1
  75. package/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.test.ts +120 -0
  76. package/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.ts +47 -3
  77. package/src/feed/feed.ts +4 -2
  78. package/src/gen/model-decoders/decoders.ts +14 -1
  79. package/src/gen/models/index.ts +73 -2
  80. package/src/gen/moderation/ModerationApi.ts +1 -0
  81. package/src/test-utils/response-generators.ts +383 -0
  82. package/src/utils/index.ts +1 -0
  83. package/src/utils/update-entity-in-array.ts +51 -0
@@ -277,7 +277,7 @@ function useStateStore(store, selector) {
277
277
 
278
278
  const decoders = {};
279
279
  const decodeDatetimeType = (input) => typeof input === 'number'
280
- ? new Date(Math.floor(input / 1000000))
280
+ ? new Date(Math.floor(input / 1e6))
281
281
  : new Date(input);
282
282
  decoders.DatetimeType = decodeDatetimeType;
283
283
  const decode = (typeMappings, input) => {
@@ -613,6 +613,7 @@ decoders.Channel = (input) => {
613
613
  updated_at: { type: 'DatetimeType', isSingle: true },
614
614
  deleted_at: { type: 'DatetimeType', isSingle: true },
615
615
  last_message_at: { type: 'DatetimeType', isSingle: true },
616
+ message_count_updated_at: { type: 'DatetimeType', isSingle: true },
616
617
  active_live_locations: { type: 'SharedLocation', isSingle: false },
617
618
  invites: { type: 'ChannelMember', isSingle: false },
618
619
  members: { type: 'ChannelMember', isSingle: false },
@@ -890,6 +891,7 @@ decoders.FeedMemberResponse = (input) => {
890
891
  user: { type: 'UserResponse', isSingle: true },
891
892
  invite_accepted_at: { type: 'DatetimeType', isSingle: true },
892
893
  invite_rejected_at: { type: 'DatetimeType', isSingle: true },
894
+ membership_level: { type: 'MembershipLevelResponse', isSingle: true },
893
895
  };
894
896
  return decode(typeMappings, input);
895
897
  };
@@ -1062,6 +1064,13 @@ decoders.ListDevicesResponse = (input) => {
1062
1064
  };
1063
1065
  return decode(typeMappings, input);
1064
1066
  };
1067
+ decoders.MembershipLevelResponse = (input) => {
1068
+ const typeMappings = {
1069
+ created_at: { type: 'DatetimeType', isSingle: true },
1070
+ updated_at: { type: 'DatetimeType', isSingle: true },
1071
+ };
1072
+ return decode(typeMappings, input);
1073
+ };
1065
1074
  decoders.Message = (input) => {
1066
1075
  const typeMappings = {
1067
1076
  created_at: { type: 'DatetimeType', isSingle: true },
@@ -3812,6 +3821,7 @@ class ModerationApi {
3812
3821
  block_list_config: request?.block_list_config,
3813
3822
  bodyguard_config: request?.bodyguard_config,
3814
3823
  google_vision_config: request?.google_vision_config,
3824
+ llm_config: request?.llm_config,
3815
3825
  rule_builder_config: request?.rule_builder_config,
3816
3826
  velocity_filter_config: request?.velocity_filter_config,
3817
3827
  video_call_rule_config: request?.video_call_rule_config,
@@ -4288,6 +4298,23 @@ function getStateUpdateQueueId(data, prefix) {
4288
4298
  throw new Error(`Cannot create state update queueId for data: ${JSON.stringify(data)}`);
4289
4299
  }
4290
4300
 
4301
+ function updateEntityInArray({ matcher, updater, entities, }) {
4302
+ if (!entities || !entities.length) {
4303
+ return { changed: false, entities };
4304
+ }
4305
+ const index = entities.findIndex(matcher);
4306
+ if (index === -1) {
4307
+ return { changed: false, entities };
4308
+ }
4309
+ const newEntity = updater(entities[index]);
4310
+ if (newEntity === entities[index]) {
4311
+ return { changed: false, entities };
4312
+ }
4313
+ const updatedEntities = [...entities];
4314
+ updatedEntities[index] = newEntity;
4315
+ return { changed: true, entities: updatedEntities };
4316
+ }
4317
+
4291
4318
  const updateStateFollowCreated = (follow, currentState, currentFeedId, connectedUserId) => {
4292
4319
  // filter non-accepted follows (the way getOrCreate does by default)
4293
4320
  if (follow.status !== 'accepted') {
@@ -4478,20 +4505,29 @@ function handleCommentAdded(event) {
4478
4505
  function handleCommentDeleted({ comment }) {
4479
4506
  const entityId = comment.parent_id ?? comment.object_id;
4480
4507
  this.state.next((currentState) => {
4481
- const newCommentsByEntityId = {
4482
- ...currentState.comments_by_entity_id,
4483
- [entityId]: {
4484
- ...currentState.comments_by_entity_id[entityId],
4485
- },
4486
- };
4508
+ let newCommentsByEntityId;
4487
4509
  const index = this.getCommentIndex(comment, currentState);
4488
- if (newCommentsByEntityId?.[entityId]?.comments?.length && index !== -1) {
4510
+ if (index !== -1) {
4511
+ newCommentsByEntityId ?? (newCommentsByEntityId = {
4512
+ ...currentState.comments_by_entity_id,
4513
+ [entityId]: {
4514
+ ...currentState.comments_by_entity_id[entityId],
4515
+ },
4516
+ });
4489
4517
  newCommentsByEntityId[entityId].comments = [
4490
4518
  ...newCommentsByEntityId[entityId].comments,
4491
4519
  ];
4492
4520
  newCommentsByEntityId[entityId]?.comments?.splice(index, 1);
4493
4521
  }
4494
- delete newCommentsByEntityId[comment.id];
4522
+ if (typeof currentState.comments_by_entity_id[comment.id] !== 'undefined') {
4523
+ newCommentsByEntityId ?? (newCommentsByEntityId = {
4524
+ ...currentState.comments_by_entity_id,
4525
+ });
4526
+ delete newCommentsByEntityId[comment.id];
4527
+ }
4528
+ if (!newCommentsByEntityId) {
4529
+ return currentState;
4530
+ }
4495
4531
  return {
4496
4532
  ...currentState,
4497
4533
  comments_by_entity_id: newCommentsByEntityId,
@@ -4623,6 +4659,179 @@ function handleFeedMemberRemoved(event) {
4623
4659
  });
4624
4660
  }
4625
4661
 
4662
+ const sharedUpdateActivity$5 = ({ currentActivity, event, eventBelongsToCurrentUser, }) => {
4663
+ let newOwnBookmarks = currentActivity.own_bookmarks;
4664
+ if (eventBelongsToCurrentUser) {
4665
+ newOwnBookmarks = [...newOwnBookmarks, event.bookmark];
4666
+ }
4667
+ return {
4668
+ ...event.bookmark.activity,
4669
+ own_bookmarks: newOwnBookmarks,
4670
+ own_reactions: currentActivity.own_reactions,
4671
+ };
4672
+ };
4673
+ const addBookmarkToActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
4674
+ entities: activities,
4675
+ matcher: (activity) => activity.id === event.bookmark.activity.id,
4676
+ updater: (matchedActivity) => sharedUpdateActivity$5({
4677
+ currentActivity: matchedActivity,
4678
+ event,
4679
+ eventBelongsToCurrentUser,
4680
+ }),
4681
+ });
4682
+ const addBookmarkToPinnedActivities = (event, pinnedActivities, eventBelongsToCurrentUser) => updateEntityInArray({
4683
+ entities: pinnedActivities,
4684
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.bookmark.activity.id,
4685
+ updater: (matchedPinnedActivity) => {
4686
+ const newActivity = sharedUpdateActivity$5({
4687
+ currentActivity: matchedPinnedActivity.activity,
4688
+ event,
4689
+ eventBelongsToCurrentUser,
4690
+ });
4691
+ if (newActivity === matchedPinnedActivity.activity) {
4692
+ return matchedPinnedActivity;
4693
+ }
4694
+ return {
4695
+ ...matchedPinnedActivity,
4696
+ activity: newActivity,
4697
+ };
4698
+ },
4699
+ });
4700
+ function handleBookmarkAdded(event) {
4701
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4702
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4703
+ const eventBelongsToCurrentUser = event.bookmark.user.id === connectedUser?.id;
4704
+ const [result1, result2] = [
4705
+ addBookmarkToActivities(event, currentActivities, eventBelongsToCurrentUser),
4706
+ addBookmarkToPinnedActivities(event, currentPinnedActivities, eventBelongsToCurrentUser),
4707
+ ];
4708
+ if (result1.changed || result2.changed) {
4709
+ this.state.partialNext({
4710
+ activities: result1.entities,
4711
+ pinned_activities: result2.entities,
4712
+ });
4713
+ }
4714
+ }
4715
+
4716
+ // Helper function to check if two bookmarks are the same
4717
+ // A bookmark is identified by activity_id + folder_id + user_id
4718
+ const isSameBookmark = (bookmark1, bookmark2) => {
4719
+ return (bookmark1.user.id === bookmark2.user.id &&
4720
+ bookmark1.activity.id === bookmark2.activity.id &&
4721
+ bookmark1.folder?.id === bookmark2.folder?.id);
4722
+ };
4723
+ const sharedUpdateActivity$4 = ({ currentActivity, event, eventBelongsToCurrentUser, }) => {
4724
+ let newOwnBookmarks = currentActivity.own_bookmarks;
4725
+ if (eventBelongsToCurrentUser) {
4726
+ newOwnBookmarks = currentActivity.own_bookmarks.filter((bookmark) => !isSameBookmark(bookmark, event.bookmark));
4727
+ }
4728
+ return {
4729
+ ...event.bookmark.activity,
4730
+ own_bookmarks: newOwnBookmarks,
4731
+ own_reactions: currentActivity.own_reactions,
4732
+ };
4733
+ };
4734
+ const removeBookmarkFromActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
4735
+ entities: activities,
4736
+ matcher: (activity) => activity.id === event.bookmark.activity.id,
4737
+ updater: (matchedActivity) => sharedUpdateActivity$4({
4738
+ currentActivity: matchedActivity,
4739
+ event,
4740
+ eventBelongsToCurrentUser,
4741
+ }),
4742
+ });
4743
+ const removeBookmarkFromPinnedActivities = (event, pinnedActivities, eventBelongsToCurrentUser) => updateEntityInArray({
4744
+ entities: pinnedActivities,
4745
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.bookmark.activity.id,
4746
+ updater: (matchedPinnedActivity) => {
4747
+ const newActivity = sharedUpdateActivity$4({
4748
+ currentActivity: matchedPinnedActivity.activity,
4749
+ event,
4750
+ eventBelongsToCurrentUser,
4751
+ });
4752
+ if (newActivity === matchedPinnedActivity.activity) {
4753
+ return matchedPinnedActivity;
4754
+ }
4755
+ return {
4756
+ ...matchedPinnedActivity,
4757
+ activity: newActivity,
4758
+ };
4759
+ },
4760
+ });
4761
+ function handleBookmarkDeleted(event) {
4762
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4763
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4764
+ const eventBelongsToCurrentUser = event.bookmark.user.id === connectedUser?.id;
4765
+ const [result1, result2] = [
4766
+ removeBookmarkFromActivities(event, currentActivities, eventBelongsToCurrentUser),
4767
+ removeBookmarkFromPinnedActivities(event, currentPinnedActivities, eventBelongsToCurrentUser),
4768
+ ];
4769
+ if (result1.changed || result2.changed) {
4770
+ this.state.partialNext({
4771
+ activities: result1.entities,
4772
+ pinned_activities: result2.entities,
4773
+ });
4774
+ }
4775
+ }
4776
+
4777
+ const sharedUpdateActivity$3 = ({ currentActivity, event, eventBelongsToCurrentUser, }) => {
4778
+ let newOwnBookmarks = currentActivity.own_bookmarks;
4779
+ if (eventBelongsToCurrentUser) {
4780
+ const bookmarkIndex = newOwnBookmarks.findIndex((bookmark) => isSameBookmark(bookmark, event.bookmark));
4781
+ if (bookmarkIndex !== -1) {
4782
+ newOwnBookmarks = [...newOwnBookmarks];
4783
+ newOwnBookmarks[bookmarkIndex] = event.bookmark;
4784
+ }
4785
+ }
4786
+ return {
4787
+ ...event.bookmark.activity,
4788
+ own_bookmarks: newOwnBookmarks,
4789
+ own_reactions: currentActivity.own_reactions,
4790
+ };
4791
+ };
4792
+ const updateBookmarkInActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
4793
+ entities: activities,
4794
+ matcher: (activity) => activity.id === event.bookmark.activity.id,
4795
+ updater: (matchedActivity) => sharedUpdateActivity$3({
4796
+ currentActivity: matchedActivity,
4797
+ event,
4798
+ eventBelongsToCurrentUser,
4799
+ }),
4800
+ });
4801
+ const updateBookmarkInPinnedActivities = (event, pinnedActivities, eventBelongsToCurrentUser) => updateEntityInArray({
4802
+ entities: pinnedActivities,
4803
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.bookmark.activity.id,
4804
+ updater: (matchedPinnedActivity) => {
4805
+ const newActivity = sharedUpdateActivity$3({
4806
+ currentActivity: matchedPinnedActivity.activity,
4807
+ event,
4808
+ eventBelongsToCurrentUser,
4809
+ });
4810
+ if (newActivity === matchedPinnedActivity.activity) {
4811
+ return matchedPinnedActivity;
4812
+ }
4813
+ return {
4814
+ ...matchedPinnedActivity,
4815
+ activity: newActivity,
4816
+ };
4817
+ },
4818
+ });
4819
+ function handleBookmarkUpdated(event) {
4820
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4821
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4822
+ const eventBelongsToCurrentUser = event.bookmark.user.id === connectedUser?.id;
4823
+ const [result1, result2] = [
4824
+ updateBookmarkInActivities(event, currentActivities, eventBelongsToCurrentUser),
4825
+ updateBookmarkInPinnedActivities(event, currentPinnedActivities, eventBelongsToCurrentUser),
4826
+ ];
4827
+ if (result1.changed || result2.changed) {
4828
+ this.state.partialNext({
4829
+ activities: result1.entities,
4830
+ pinned_activities: result2.entities,
4831
+ });
4832
+ }
4833
+ }
4834
+
4626
4835
  const addActivitiesToState = (newActivities, activities, position) => {
4627
4836
  let result;
4628
4837
  if (activities === undefined) {
@@ -4667,7 +4876,8 @@ function handleActivityAdded(event) {
4667
4876
  }
4668
4877
 
4669
4878
  const removeActivityFromState = (activityResponse, activities) => {
4670
- const index = activities.findIndex((a) => a.id === activityResponse.id);
4879
+ const index = activities?.findIndex((activity) => activity.id === activityResponse.id) ??
4880
+ -1;
4671
4881
  if (index !== -1) {
4672
4882
  const newActivities = [...activities];
4673
4883
  newActivities.splice(index, 1);
@@ -4677,13 +4887,28 @@ const removeActivityFromState = (activityResponse, activities) => {
4677
4887
  return { changed: false, activities };
4678
4888
  }
4679
4889
  };
4890
+ const removePinnedActivityFromState = (activityResponse, pinnedActivities) => {
4891
+ const index = pinnedActivities?.findIndex((pinnedActivity) => pinnedActivity.activity.id === activityResponse.id) ?? -1;
4892
+ if (index !== -1) {
4893
+ const newActivities = [...pinnedActivities];
4894
+ newActivities.splice(index, 1);
4895
+ return { changed: true, activities: newActivities };
4896
+ }
4897
+ else {
4898
+ return { changed: false, pinned_activities: pinnedActivities };
4899
+ }
4900
+ };
4680
4901
  function handleActivityDeleted(event) {
4681
- const currentActivities = this.currentState.activities;
4682
- if (currentActivities) {
4683
- const result = removeActivityFromState(event.activity, currentActivities);
4684
- if (result.changed) {
4685
- this.state.partialNext({ activities: result.activities });
4686
- }
4902
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4903
+ const [result1, result2] = [
4904
+ removeActivityFromState(event.activity, currentActivities),
4905
+ removePinnedActivityFromState(event.activity, currentPinnedActivities),
4906
+ ];
4907
+ if (result1.changed || result2.changed) {
4908
+ this.state.partialNext({
4909
+ activities: result1.activities,
4910
+ pinned_activities: result2.pinned_activities,
4911
+ });
4687
4912
  }
4688
4913
  }
4689
4914
 
@@ -4697,233 +4922,242 @@ function handleActivityRemovedFromFeed(event) {
4697
4922
  }
4698
4923
  }
4699
4924
 
4700
- const updateActivityInState = (updatedActivityResponse, activities, replaceCompletely = false) => {
4701
- const index = activities.findIndex((a) => a.id === updatedActivityResponse.id);
4702
- if (index !== -1) {
4703
- const newActivities = [...activities];
4704
- const activity = activities[index];
4705
- if (replaceCompletely) {
4706
- newActivities[index] = updatedActivityResponse;
4707
- }
4708
- else {
4709
- newActivities[index] = {
4710
- ...updatedActivityResponse,
4711
- own_reactions: activity.own_reactions,
4712
- own_bookmarks: activity.own_bookmarks,
4713
- latest_reactions: activity.latest_reactions,
4714
- reaction_groups: activity.reaction_groups,
4715
- };
4925
+ const sharedUpdateActivity$2 = ({ currentActivity, event, }) => {
4926
+ return {
4927
+ ...event.activity,
4928
+ own_reactions: currentActivity.own_reactions,
4929
+ own_bookmarks: currentActivity.own_bookmarks,
4930
+ };
4931
+ };
4932
+ const updateActivityInState = (event, activities) => updateEntityInArray({
4933
+ entities: activities,
4934
+ matcher: (activity) => activity.id === event.activity.id,
4935
+ updater: (matchedActivity) => sharedUpdateActivity$2({
4936
+ currentActivity: matchedActivity,
4937
+ event,
4938
+ }),
4939
+ });
4940
+ const updatePinnedActivityInState = (event, pinnedActivities) => updateEntityInArray({
4941
+ entities: pinnedActivities,
4942
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.activity.id,
4943
+ updater: (matchedPinnedActivity) => {
4944
+ const newActivity = sharedUpdateActivity$2({
4945
+ currentActivity: matchedPinnedActivity.activity,
4946
+ event,
4947
+ });
4948
+ if (newActivity === matchedPinnedActivity.activity) {
4949
+ return matchedPinnedActivity;
4716
4950
  }
4717
- return { changed: true, activities: newActivities };
4718
- }
4719
- else {
4720
- return { changed: false, activities };
4721
- }
4722
- };
4951
+ return {
4952
+ ...matchedPinnedActivity,
4953
+ activity: newActivity,
4954
+ };
4955
+ },
4956
+ });
4723
4957
  function handleActivityUpdated(event) {
4724
- const currentActivities = this.currentState.activities;
4725
- if (currentActivities) {
4726
- const result = updateActivityInState(event.activity, currentActivities);
4727
- if (result.changed) {
4728
- this.client.hydratePollCache([event.activity]);
4729
- this.state.partialNext({ activities: result.activities });
4730
- }
4958
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4959
+ const [result1, result2] = [
4960
+ updateActivityInState(event, currentActivities),
4961
+ updatePinnedActivityInState(event, currentPinnedActivities),
4962
+ ];
4963
+ if (result1.changed || result2.changed) {
4964
+ this.client.hydratePollCache([event.activity]);
4965
+ this.state.partialNext({
4966
+ activities: result1.entities,
4967
+ pinned_activities: result2.entities,
4968
+ });
4731
4969
  }
4732
4970
  }
4733
4971
 
4734
- const addReactionToActivity = (event, activity, isCurrentUser) => {
4735
- // Update own_reactions if the reaction is from the current user
4736
- const ownReactions = [...(activity.own_reactions || [])];
4737
- if (isCurrentUser) {
4738
- ownReactions.push(event.reaction);
4972
+ // shared function to update the activity with the new reaction
4973
+ const sharedUpdateActivity$1 = ({ currentActivity, event, eventBelongsToCurrentUser, }) => {
4974
+ let newOwnReactions = currentActivity.own_reactions;
4975
+ if (eventBelongsToCurrentUser) {
4976
+ newOwnReactions = [...currentActivity.own_reactions, event.reaction];
4739
4977
  }
4740
4978
  return {
4741
- ...activity,
4742
- own_reactions: ownReactions,
4743
- latest_reactions: event.activity.latest_reactions,
4744
- reaction_groups: event.activity.reaction_groups,
4745
- changed: true,
4746
- };
4747
- };
4748
- const addReactionToActivities = (event, activities, isCurrentUser) => {
4749
- if (!activities) {
4750
- return { changed: false, activities: [] };
4751
- }
4752
- const activityIndex = activities.findIndex((a) => a.id === event.activity.id);
4753
- if (activityIndex === -1) {
4754
- return { changed: false, activities };
4755
- }
4756
- const activity = activities[activityIndex];
4757
- const updatedActivity = addReactionToActivity(event, activity, isCurrentUser);
4758
- return updateActivityInState(updatedActivity, activities, true);
4759
- };
4979
+ ...event.activity,
4980
+ own_reactions: newOwnReactions,
4981
+ own_bookmarks: currentActivity.own_bookmarks,
4982
+ };
4983
+ };
4984
+ const addReactionToActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
4985
+ entities: activities,
4986
+ matcher: (activity) => activity.id === event.activity.id,
4987
+ updater: (matchedActivity) => sharedUpdateActivity$1({
4988
+ currentActivity: matchedActivity,
4989
+ event,
4990
+ eventBelongsToCurrentUser,
4991
+ }),
4992
+ });
4993
+ const addReactionToPinnedActivities = (event, pinnedActivities, eventBelongsToCurrentUser) => updateEntityInArray({
4994
+ entities: pinnedActivities,
4995
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.activity.id,
4996
+ updater: (matchedPinnedActivity) => {
4997
+ const newActivity = sharedUpdateActivity$1({
4998
+ currentActivity: matchedPinnedActivity.activity,
4999
+ event,
5000
+ eventBelongsToCurrentUser,
5001
+ });
5002
+ // this should never happen, but just in case
5003
+ if (newActivity === matchedPinnedActivity.activity) {
5004
+ return matchedPinnedActivity;
5005
+ }
5006
+ return {
5007
+ ...matchedPinnedActivity,
5008
+ activity: newActivity,
5009
+ };
5010
+ },
5011
+ });
4760
5012
  function handleActivityReactionAdded(event) {
4761
- const currentActivities = this.currentState.activities;
5013
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4762
5014
  const connectedUser = this.client.state.getLatestValue().connected_user;
4763
- const isCurrentUser = Boolean(connectedUser && event.reaction.user.id === connectedUser.id);
4764
- const result = addReactionToActivities(event, currentActivities, isCurrentUser);
4765
- if (result.changed) {
4766
- this.state.partialNext({ activities: result.activities });
5015
+ const eventBelongsToCurrentUser = typeof connectedUser !== 'undefined' &&
5016
+ event.reaction.user.id === connectedUser.id;
5017
+ const [result1, result2] = [
5018
+ addReactionToActivities(event, currentActivities, eventBelongsToCurrentUser),
5019
+ addReactionToPinnedActivities(event, currentPinnedActivities, eventBelongsToCurrentUser),
5020
+ ];
5021
+ if (result1.changed || result2.changed) {
5022
+ this.state.partialNext({
5023
+ activities: result1.entities,
5024
+ pinned_activities: result2.entities,
5025
+ });
4767
5026
  }
4768
5027
  }
4769
5028
 
4770
- const removeReactionFromActivity = (event, activity, isCurrentUser) => {
4771
- // Update own_reactions if the reaction is from the current user
4772
- const ownReactions = isCurrentUser
4773
- ? (activity.own_reactions || []).filter((r) => !(r.type === event.reaction.type &&
4774
- r.user.id === event.reaction.user.id))
4775
- : activity.own_reactions;
4776
- return {
4777
- ...activity,
4778
- own_reactions: ownReactions,
4779
- latest_reactions: event.activity.latest_reactions,
4780
- reaction_groups: event.activity.reaction_groups,
4781
- changed: true,
4782
- };
4783
- };
4784
- const removeReactionFromActivities = (event, activities, isCurrentUser) => {
4785
- if (!activities) {
4786
- return { changed: false, activities: [] };
4787
- }
4788
- const activityIndex = activities.findIndex((a) => a.id === event.activity.id);
4789
- if (activityIndex === -1) {
4790
- return { changed: false, activities };
5029
+ const sharedUpdateActivity = ({ currentActivity, event, eventBelongsToCurrentUser, }) => {
5030
+ let newOwnReactions = currentActivity.own_reactions;
5031
+ if (eventBelongsToCurrentUser) {
5032
+ newOwnReactions = currentActivity.own_reactions.filter((reaction) => !(reaction.type === event.reaction.type &&
5033
+ reaction.user.id === event.reaction.user.id));
4791
5034
  }
4792
- const activity = activities[activityIndex];
4793
- const updatedActivity = removeReactionFromActivity(event, activity, isCurrentUser);
4794
- return updateActivityInState(updatedActivity, activities, true);
4795
- };
5035
+ return {
5036
+ ...event.activity,
5037
+ own_reactions: newOwnReactions,
5038
+ own_bookmarks: currentActivity.own_bookmarks,
5039
+ };
5040
+ };
5041
+ const removeReactionFromActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
5042
+ entities: activities,
5043
+ matcher: (activity) => activity.id === event.activity.id,
5044
+ updater: (matchedActivity) => sharedUpdateActivity({
5045
+ currentActivity: matchedActivity,
5046
+ event,
5047
+ eventBelongsToCurrentUser,
5048
+ }),
5049
+ });
5050
+ const removeReactionFromPinnedActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
5051
+ entities: activities,
5052
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.activity.id,
5053
+ updater: (matchedPinnedActivity) => {
5054
+ const newActivity = sharedUpdateActivity({
5055
+ currentActivity: matchedPinnedActivity.activity,
5056
+ event,
5057
+ eventBelongsToCurrentUser,
5058
+ });
5059
+ if (newActivity === matchedPinnedActivity.activity) {
5060
+ return matchedPinnedActivity;
5061
+ }
5062
+ return {
5063
+ ...matchedPinnedActivity,
5064
+ activity: newActivity,
5065
+ };
5066
+ },
5067
+ });
4796
5068
  function handleActivityReactionDeleted(event) {
4797
- const currentActivities = this.currentState.activities;
5069
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4798
5070
  const connectedUser = this.client.state.getLatestValue().connected_user;
4799
- const isCurrentUser = Boolean(connectedUser && event.reaction.user.id === connectedUser.id);
4800
- const result = removeReactionFromActivities(event, currentActivities, isCurrentUser);
4801
- if (result.changed) {
4802
- this.state.partialNext({ activities: result.activities });
5071
+ const eventBelongsToCurrentUser = typeof connectedUser !== 'undefined' &&
5072
+ event.reaction.user.id === connectedUser.id;
5073
+ const [result1, result2] = [
5074
+ removeReactionFromActivities(event, currentActivities, eventBelongsToCurrentUser),
5075
+ removeReactionFromPinnedActivities(event, currentPinnedActivities, eventBelongsToCurrentUser),
5076
+ ];
5077
+ if (result1.changed || result2.changed) {
5078
+ this.state.partialNext({
5079
+ activities: result1.entities,
5080
+ pinned_activities: result2.entities,
5081
+ });
4803
5082
  }
4804
5083
  }
4805
5084
 
4806
- const addBookmarkToActivity = (event, activity, isCurrentUser) => {
4807
- // Update own_bookmarks if the bookmark is from the current user
4808
- const ownBookmarks = [...(activity.own_bookmarks || [])];
4809
- if (isCurrentUser) {
4810
- ownBookmarks.push(event.bookmark);
5085
+ const updateNotificationStatusFromActivityMarked = (event, currentNotificationStatus, aggregatedActivities = []) => {
5086
+ if (!currentNotificationStatus) {
5087
+ return {
5088
+ changed: false,
5089
+ };
4811
5090
  }
4812
- return {
4813
- ...activity,
4814
- own_bookmarks: ownBookmarks,
4815
- changed: true,
5091
+ const newState = {
5092
+ ...currentNotificationStatus,
4816
5093
  };
4817
- };
4818
- const addBookmarkToActivities = (event, activities, isCurrentUser) => {
4819
- if (!activities) {
4820
- return { changed: false, activities: [] };
4821
- }
4822
- const activityIndex = activities.findIndex((a) => a.id === event.bookmark.activity.id);
4823
- if (activityIndex === -1) {
4824
- return { changed: false, activities };
4825
- }
4826
- const activity = activities[activityIndex];
4827
- const updatedActivity = addBookmarkToActivity(event, activity, isCurrentUser);
4828
- return updateActivityInState(updatedActivity, activities, true);
4829
- };
4830
- function handleBookmarkAdded(event) {
4831
- const currentActivities = this.currentState.activities;
4832
- const { connected_user: connectedUser } = this.client.state.getLatestValue();
4833
- const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4834
- const result = addBookmarkToActivities(event, currentActivities, isCurrentUser);
4835
- if (result.changed) {
4836
- this.state.partialNext({ activities: result.activities });
5094
+ if (event.mark_all_read) {
5095
+ const allGroupIds = aggregatedActivities.map((activity) => activity.group);
5096
+ newState.read_activities = [
5097
+ ...new Set([
5098
+ ...(currentNotificationStatus.read_activities ?? []),
5099
+ ...allGroupIds,
5100
+ ]),
5101
+ ];
4837
5102
  }
4838
- }
4839
-
4840
- // Helper function to check if two bookmarks are the same
4841
- // A bookmark is identified by activity_id + folder_id + user_id
4842
- const isSameBookmark = (bookmark1, bookmark2) => {
4843
- return (bookmark1.user.id === bookmark2.user.id &&
4844
- bookmark1.activity.id === bookmark2.activity.id &&
4845
- bookmark1.folder?.id === bookmark2.folder?.id);
4846
- };
4847
- const removeBookmarkFromActivities = (event, activities, isCurrentUser) => {
4848
- if (!activities) {
4849
- return { changed: false, activities: [] };
5103
+ if (event.mark_read && event.mark_read.length > 0) {
5104
+ newState.read_activities = [
5105
+ ...new Set([
5106
+ ...(currentNotificationStatus?.read_activities ?? []),
5107
+ ...event.mark_read,
5108
+ ]),
5109
+ ];
4850
5110
  }
4851
- const activityIndex = activities.findIndex((a) => a.id === event.bookmark.activity.id);
4852
- if (activityIndex === -1) {
4853
- return { changed: false, activities };
5111
+ if (event.mark_all_seen) {
5112
+ newState.last_seen_at = new Date();
4854
5113
  }
4855
- const activity = activities[activityIndex];
4856
- const updatedActivity = removeBookmarkFromActivity(event, activity, isCurrentUser);
4857
- return updateActivityInState(updatedActivity, activities, true);
4858
- };
4859
- const removeBookmarkFromActivity = (event, activity, isCurrentUser) => {
4860
- // Update own_bookmarks if the bookmark is from the current user
4861
- const ownBookmarks = isCurrentUser
4862
- ? (activity.own_bookmarks || []).filter((bookmark) => !isSameBookmark(bookmark, event.bookmark))
4863
- : activity.own_bookmarks;
4864
5114
  return {
4865
- ...activity,
4866
- own_bookmarks: ownBookmarks,
4867
5115
  changed: true,
5116
+ data: { notification_status: newState },
4868
5117
  };
4869
5118
  };
4870
- function handleBookmarkDeleted(event) {
4871
- const currentActivities = this.currentState.activities;
4872
- const { connected_user: connectedUser } = this.client.state.getLatestValue();
4873
- const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4874
- const result = removeBookmarkFromActivities(event, currentActivities, isCurrentUser);
5119
+ function handleActivityMarked(event) {
5120
+ const result = updateNotificationStatusFromActivityMarked(event, this.currentState.notification_status, this.currentState.aggregated_activities);
4875
5121
  if (result.changed) {
4876
- this.state.partialNext({ activities: result.activities });
5122
+ this.state.partialNext({
5123
+ notification_status: result.data?.notification_status,
5124
+ });
4877
5125
  }
4878
5126
  }
4879
5127
 
4880
- const updateBookmarkInActivity = (event, activity, isCurrentUser) => {
4881
- // Update own_bookmarks if the bookmark is from the current user
4882
- let ownBookmarks = activity.own_bookmarks || [];
4883
- if (isCurrentUser) {
4884
- const bookmarkIndex = ownBookmarks.findIndex((bookmark) => isSameBookmark(bookmark, event.bookmark));
4885
- if (bookmarkIndex !== -1) {
4886
- ownBookmarks = [...ownBookmarks];
4887
- ownBookmarks[bookmarkIndex] = event.bookmark;
4888
- }
5128
+ function handleFeedUpdated(event) {
5129
+ this.state.partialNext({ ...event.feed });
5130
+ }
5131
+
5132
+ const updateNotificationFeedFromEvent = (event) => {
5133
+ const updates = {};
5134
+ if (event.notification_status) {
5135
+ updates.notification_status = event.notification_status;
4889
5136
  }
4890
- return {
4891
- ...activity,
4892
- own_bookmarks: ownBookmarks,
4893
- changed: true,
4894
- };
4895
- };
4896
- const updateBookmarkInActivities = (event, activities, isCurrentUser) => {
4897
- if (!activities) {
4898
- return { changed: false, activities: [] };
5137
+ if (event.aggregated_activities) {
5138
+ updates.aggregated_activities = event.aggregated_activities;
4899
5139
  }
4900
- const activityIndex = activities.findIndex((a) => a.id === event.bookmark.activity.id);
4901
- if (activityIndex === -1) {
4902
- return { changed: false, activities };
5140
+ // Only return changed if we have actual updates
5141
+ if (Object.keys(updates).length > 0) {
5142
+ return {
5143
+ changed: true,
5144
+ data: updates,
5145
+ };
4903
5146
  }
4904
- const activity = activities[activityIndex];
4905
- const updatedActivity = updateBookmarkInActivity(event, activity, isCurrentUser);
4906
- return updateActivityInState(updatedActivity, activities, true);
5147
+ return {
5148
+ changed: false,
5149
+ };
4907
5150
  };
4908
- function handleBookmarkUpdated(event) {
4909
- const currentActivities = this.currentState.activities;
4910
- const { connected_user: connectedUser } = this.client.state.getLatestValue();
4911
- const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4912
- const result = updateBookmarkInActivities(event, currentActivities, isCurrentUser);
5151
+ function handleNotificationFeedUpdated(event) {
5152
+ const result = updateNotificationFeedFromEvent(event);
4913
5153
  if (result.changed) {
4914
- this.state.partialNext({ activities: result.activities });
5154
+ this.state.partialNext({
5155
+ notification_status: result.data?.notification_status,
5156
+ aggregated_activities: result.data?.aggregated_activities,
5157
+ });
4915
5158
  }
4916
5159
  }
4917
5160
 
4918
- function handleFeedUpdated(event) {
4919
- this.state.partialNext({ ...event.feed });
4920
- }
4921
-
4922
- function handleNotificationFeedUpdated(event) {
4923
- console.info('notification feed updated', event);
4924
- // TODO: handle notification feed updates
4925
- }
4926
-
4927
5161
  function handleWatchStarted() {
4928
5162
  this.state.partialNext({ watch: true });
4929
5163
  }
@@ -4976,7 +5210,7 @@ class Feed extends FeedApi {
4976
5210
  'feeds.poll.vote_removed': Feed.noop,
4977
5211
  'feeds.activity.pinned': Feed.noop,
4978
5212
  'feeds.activity.unpinned': Feed.noop,
4979
- 'feeds.activity.marked': Feed.noop,
5213
+ 'feeds.activity.marked': handleActivityMarked.bind(this),
4980
5214
  'moderation.custom_action': Feed.noop,
4981
5215
  'moderation.flagged': Feed.noop,
4982
5216
  'moderation.mark_reviewed': Feed.noop,
@@ -5848,10 +6082,10 @@ const useFeedsClient = () => {
5848
6082
  */
5849
6083
  const useClientConnectedUser = () => {
5850
6084
  const client = useFeedsClient();
5851
- const { user } = useStateStore(client?.state, selector$a) ?? {};
6085
+ const { user } = useStateStore(client?.state, selector$c) ?? {};
5852
6086
  return user;
5853
6087
  };
5854
- const selector$a = (nextState) => ({
6088
+ const selector$c = (nextState) => ({
5855
6089
  user: nextState.connected_user,
5856
6090
  });
5857
6091
 
@@ -5860,10 +6094,10 @@ const selector$a = (nextState) => ({
5860
6094
  */
5861
6095
  const useWsConnectionState = () => {
5862
6096
  const client = useFeedsClient();
5863
- const { is_healthy } = useStateStore(client?.state, selector$9) ?? {};
6097
+ const { is_healthy } = useStateStore(client?.state, selector$b) ?? {};
5864
6098
  return { is_healthy };
5865
6099
  };
5866
- const selector$9 = (nextState) => ({
6100
+ const selector$b = (nextState) => ({
5867
6101
  is_healthy: nextState.is_ws_connection_healthy,
5868
6102
  });
5869
6103
 
@@ -5913,7 +6147,7 @@ const useStableCallback = (callback) => {
5913
6147
  const useFeedActivities = (feedFromProps) => {
5914
6148
  const feedFromContext = useFeedContext();
5915
6149
  const feed = feedFromProps ?? feedFromContext;
5916
- const data = useStateStore(feed?.state, selector$8);
6150
+ const data = useStateStore(feed?.state, selector$a);
5917
6151
  const loadNextPage = useStableCallback(async () => {
5918
6152
  if (!feed || !data?.has_next_page || data?.is_loading) {
5919
6153
  return;
@@ -5922,7 +6156,7 @@ const useFeedActivities = (feedFromProps) => {
5922
6156
  });
5923
6157
  return react.useMemo(() => ({ ...data, loadNextPage }), [data, loadNextPage]);
5924
6158
  };
5925
- const selector$8 = ({ is_loading_activities, next, activities = [] }) => ({
6159
+ const selector$a = ({ is_loading_activities, next, activities = [] }) => ({
5926
6160
  is_loading: is_loading_activities,
5927
6161
  has_next_page: typeof next !== 'undefined',
5928
6162
  activities,
@@ -5995,13 +6229,13 @@ const FeedOwnCapability = {
5995
6229
  };
5996
6230
 
5997
6231
  const stableEmptyArray = [];
5998
- const selector$7 = (currentState) => ({
6232
+ const selector$9 = (currentState) => ({
5999
6233
  oc: currentState.own_capabilities ?? stableEmptyArray,
6000
6234
  });
6001
6235
  const useOwnCapabilities = (feedFromProps) => {
6002
6236
  const feedFromContext = useFeedContext();
6003
6237
  const feed = feedFromProps ?? feedFromContext;
6004
- const { oc = stableEmptyArray } = useStateStore(feed?.state, selector$7) ?? {};
6238
+ const { oc = stableEmptyArray } = useStateStore(feed?.state, selector$9) ?? {};
6005
6239
  return react.useMemo(() => ({
6006
6240
  can_add_activity: oc.indexOf(FeedOwnCapability.ADD_ACTIVITY) > -1,
6007
6241
  can_add_activity_reaction: oc.indexOf(FeedOwnCapability.ADD_ACTIVITY_REACTION) > -1,
@@ -6036,7 +6270,7 @@ const useOwnCapabilities = (feedFromProps) => {
6036
6270
  }), [oc]);
6037
6271
  };
6038
6272
 
6039
- const selector$6 = ({ follower_count, followers, followers_pagination, }) => ({
6273
+ const selector$8 = ({ follower_count, followers, followers_pagination, }) => ({
6040
6274
  follower_count,
6041
6275
  followers,
6042
6276
  followers_pagination,
@@ -6044,7 +6278,7 @@ const selector$6 = ({ follower_count, followers, followers_pagination, }) => ({
6044
6278
  function useFollowers(feedFromProps) {
6045
6279
  const feedFromContext = useFeedContext();
6046
6280
  const feed = feedFromProps ?? feedFromContext;
6047
- const data = useStateStore(feed?.state, selector$6);
6281
+ const data = useStateStore(feed?.state, selector$8);
6048
6282
  const loadNextPage = react.useCallback((...options) => feed?.loadNextPageFollowers(...options), [feed]);
6049
6283
  return react.useMemo(() => {
6050
6284
  if (!data) {
@@ -6059,7 +6293,7 @@ function useFollowers(feedFromProps) {
6059
6293
  }, [data, loadNextPage]);
6060
6294
  }
6061
6295
 
6062
- const selector$5 = ({ following_count, following, following_pagination, }) => ({
6296
+ const selector$7 = ({ following_count, following, following_pagination, }) => ({
6063
6297
  following_count,
6064
6298
  following,
6065
6299
  following_pagination,
@@ -6067,7 +6301,7 @@ const selector$5 = ({ following_count, following, following_pagination, }) => ({
6067
6301
  function useFollowing(feedFromProps) {
6068
6302
  const feedFromContext = useFeedContext();
6069
6303
  const feed = feedFromProps ?? feedFromContext;
6070
- const data = useStateStore(feed?.state, selector$5);
6304
+ const data = useStateStore(feed?.state, selector$7);
6071
6305
  const loadNextPage = react.useCallback((...options) => feed?.loadNextPageFollowing(...options), [feed]);
6072
6306
  return react.useMemo(() => {
6073
6307
  if (!data) {
@@ -6089,9 +6323,9 @@ function useFollowing(feedFromProps) {
6089
6323
  const useFeedMetadata = (feedFromProps) => {
6090
6324
  const feedFromContext = useFeedContext();
6091
6325
  const feed = feedFromProps ?? feedFromContext;
6092
- return useStateStore(feed?.state, selector$4);
6326
+ return useStateStore(feed?.state, selector$6);
6093
6327
  };
6094
- const selector$4 = ({ follower_count = 0, following_count = 0, created_by, created_at, updated_at, }) => ({
6328
+ const selector$6 = ({ follower_count = 0, following_count = 0, created_by, created_at, updated_at, }) => ({
6095
6329
  created_by,
6096
6330
  follower_count,
6097
6331
  following_count,
@@ -6106,12 +6340,57 @@ const selector$4 = ({ follower_count = 0, following_count = 0, created_by, creat
6106
6340
  const useOwnFollows = (feedFromProps) => {
6107
6341
  const feedFromContext = useFeedContext();
6108
6342
  const feed = feedFromProps ?? feedFromContext;
6109
- return useStateStore(feed?.state, selector$3);
6343
+ return useStateStore(feed?.state, selector$5);
6110
6344
  };
6111
- const selector$3 = ({ own_follows }) => ({
6345
+ const selector$5 = ({ own_follows }) => ({
6112
6346
  own_follows,
6113
6347
  });
6114
6348
 
6349
+ const selector$4 = ({ notification_status }) => ({
6350
+ unread: notification_status?.unread ?? 0,
6351
+ unseen: notification_status?.unseen ?? 0,
6352
+ last_read_at: notification_status?.last_read_at,
6353
+ last_seen_at: notification_status?.last_seen_at,
6354
+ read_activities: notification_status?.read_activities,
6355
+ seen_activities: notification_status?.seen_activities,
6356
+ });
6357
+ function useNotificationStatus(feedFromProps) {
6358
+ const feedFromContext = useFeedContext();
6359
+ const feed = feedFromProps ?? feedFromContext;
6360
+ return useStateStore(feed?.state, selector$4);
6361
+ }
6362
+
6363
+ const selector$3 = ({ aggregated_activities }) => ({
6364
+ aggregated_activities,
6365
+ });
6366
+ function useAggregatedActivities(feedFromProps) {
6367
+ const feedFromContext = useFeedContext();
6368
+ const feed = feedFromProps ?? feedFromContext;
6369
+ return useStateStore(feed?.state, selector$3);
6370
+ }
6371
+
6372
+ const useIsAggregatedActivityRead = ({ feed: feedFromProps, aggregatedActivity, }) => {
6373
+ const feedFromContext = useFeedContext();
6374
+ const feed = feedFromProps ?? feedFromContext;
6375
+ const { read_activities: readActivities, /* last_read_at: lastReadAt */ } = useNotificationStatus(feed) ?? {};
6376
+ const group = aggregatedActivity.group;
6377
+ return react.useMemo(() =>
6378
+ // FIXME: This part of the condition does not work as marking individual groups as read also updates the last_read_at. Should be uncommented once it's fixed on the backend.
6379
+ // (lastReadAt &&
6380
+ // aggregatedActivity.updated_at.getTime() <= lastReadAt.getTime()) ||
6381
+ (readActivities ?? []).includes(group), [readActivities, group]);
6382
+ };
6383
+
6384
+ const useIsAggregatedActivitySeen = ({ feed: feedFromProps, aggregatedActivity, }) => {
6385
+ const feedFromContext = useFeedContext();
6386
+ const feed = feedFromProps ?? feedFromContext;
6387
+ const { seen_activities: seenActivities, last_seen_at: lastSeenAt } = useNotificationStatus(feed) ?? {};
6388
+ const group = aggregatedActivity.group;
6389
+ return react.useMemo(() => (lastSeenAt &&
6390
+ aggregatedActivity.updated_at.getTime() < lastSeenAt.getTime()) ||
6391
+ (seenActivities ?? []).includes(group), [lastSeenAt, aggregatedActivity.updated_at, seenActivities, group]);
6392
+ };
6393
+
6115
6394
  const StreamSearchResultsContext = react.createContext(undefined);
6116
6395
  /**
6117
6396
  * Hook to access the nearest SearchSource instance.
@@ -6228,7 +6507,7 @@ const StreamFeeds = ({ client, children }) => {
6228
6507
  };
6229
6508
  StreamFeeds.displayName = 'StreamFeeds';
6230
6509
 
6231
- const StreamFeed = ({ feed, children }) => {
6510
+ const StreamFeed = ({ feed, children, }) => {
6232
6511
  return (jsxRuntime.jsx(StreamFeedContext.Provider, { value: feed, children: children }));
6233
6512
  };
6234
6513
  StreamFeed.displayName = 'StreamFeed';
@@ -6251,6 +6530,7 @@ exports.StreamSearch = StreamSearch;
6251
6530
  exports.StreamSearchContext = StreamSearchContext;
6252
6531
  exports.StreamSearchResults = StreamSearchResults;
6253
6532
  exports.StreamSearchResultsContext = StreamSearchResultsContext;
6533
+ exports.useAggregatedActivities = useAggregatedActivities;
6254
6534
  exports.useBookmarkActions = useBookmarkActions;
6255
6535
  exports.useClientConnectedUser = useClientConnectedUser;
6256
6536
  exports.useComments = useComments;
@@ -6261,6 +6541,9 @@ exports.useFeedMetadata = useFeedMetadata;
6261
6541
  exports.useFeedsClient = useFeedsClient;
6262
6542
  exports.useFollowers = useFollowers;
6263
6543
  exports.useFollowing = useFollowing;
6544
+ exports.useIsAggregatedActivityRead = useIsAggregatedActivityRead;
6545
+ exports.useIsAggregatedActivitySeen = useIsAggregatedActivitySeen;
6546
+ exports.useNotificationStatus = useNotificationStatus;
6264
6547
  exports.useOwnCapabilities = useOwnCapabilities;
6265
6548
  exports.useOwnFollows = useOwnFollows;
6266
6549
  exports.useReactionActions = useReactionActions;