@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
@@ -275,7 +275,7 @@ function useStateStore(store, selector) {
275
275
 
276
276
  const decoders = {};
277
277
  const decodeDatetimeType = (input) => typeof input === 'number'
278
- ? new Date(Math.floor(input / 1000000))
278
+ ? new Date(Math.floor(input / 1e6))
279
279
  : new Date(input);
280
280
  decoders.DatetimeType = decodeDatetimeType;
281
281
  const decode = (typeMappings, input) => {
@@ -611,6 +611,7 @@ decoders.Channel = (input) => {
611
611
  updated_at: { type: 'DatetimeType', isSingle: true },
612
612
  deleted_at: { type: 'DatetimeType', isSingle: true },
613
613
  last_message_at: { type: 'DatetimeType', isSingle: true },
614
+ message_count_updated_at: { type: 'DatetimeType', isSingle: true },
614
615
  active_live_locations: { type: 'SharedLocation', isSingle: false },
615
616
  invites: { type: 'ChannelMember', isSingle: false },
616
617
  members: { type: 'ChannelMember', isSingle: false },
@@ -888,6 +889,7 @@ decoders.FeedMemberResponse = (input) => {
888
889
  user: { type: 'UserResponse', isSingle: true },
889
890
  invite_accepted_at: { type: 'DatetimeType', isSingle: true },
890
891
  invite_rejected_at: { type: 'DatetimeType', isSingle: true },
892
+ membership_level: { type: 'MembershipLevelResponse', isSingle: true },
891
893
  };
892
894
  return decode(typeMappings, input);
893
895
  };
@@ -1060,6 +1062,13 @@ decoders.ListDevicesResponse = (input) => {
1060
1062
  };
1061
1063
  return decode(typeMappings, input);
1062
1064
  };
1065
+ decoders.MembershipLevelResponse = (input) => {
1066
+ const typeMappings = {
1067
+ created_at: { type: 'DatetimeType', isSingle: true },
1068
+ updated_at: { type: 'DatetimeType', isSingle: true },
1069
+ };
1070
+ return decode(typeMappings, input);
1071
+ };
1063
1072
  decoders.Message = (input) => {
1064
1073
  const typeMappings = {
1065
1074
  created_at: { type: 'DatetimeType', isSingle: true },
@@ -3810,6 +3819,7 @@ class ModerationApi {
3810
3819
  block_list_config: request?.block_list_config,
3811
3820
  bodyguard_config: request?.bodyguard_config,
3812
3821
  google_vision_config: request?.google_vision_config,
3822
+ llm_config: request?.llm_config,
3813
3823
  rule_builder_config: request?.rule_builder_config,
3814
3824
  velocity_filter_config: request?.velocity_filter_config,
3815
3825
  video_call_rule_config: request?.video_call_rule_config,
@@ -4286,6 +4296,23 @@ function getStateUpdateQueueId(data, prefix) {
4286
4296
  throw new Error(`Cannot create state update queueId for data: ${JSON.stringify(data)}`);
4287
4297
  }
4288
4298
 
4299
+ function updateEntityInArray({ matcher, updater, entities, }) {
4300
+ if (!entities || !entities.length) {
4301
+ return { changed: false, entities };
4302
+ }
4303
+ const index = entities.findIndex(matcher);
4304
+ if (index === -1) {
4305
+ return { changed: false, entities };
4306
+ }
4307
+ const newEntity = updater(entities[index]);
4308
+ if (newEntity === entities[index]) {
4309
+ return { changed: false, entities };
4310
+ }
4311
+ const updatedEntities = [...entities];
4312
+ updatedEntities[index] = newEntity;
4313
+ return { changed: true, entities: updatedEntities };
4314
+ }
4315
+
4289
4316
  const updateStateFollowCreated = (follow, currentState, currentFeedId, connectedUserId) => {
4290
4317
  // filter non-accepted follows (the way getOrCreate does by default)
4291
4318
  if (follow.status !== 'accepted') {
@@ -4476,20 +4503,29 @@ function handleCommentAdded(event) {
4476
4503
  function handleCommentDeleted({ comment }) {
4477
4504
  const entityId = comment.parent_id ?? comment.object_id;
4478
4505
  this.state.next((currentState) => {
4479
- const newCommentsByEntityId = {
4480
- ...currentState.comments_by_entity_id,
4481
- [entityId]: {
4482
- ...currentState.comments_by_entity_id[entityId],
4483
- },
4484
- };
4506
+ let newCommentsByEntityId;
4485
4507
  const index = this.getCommentIndex(comment, currentState);
4486
- if (newCommentsByEntityId?.[entityId]?.comments?.length && index !== -1) {
4508
+ if (index !== -1) {
4509
+ newCommentsByEntityId ?? (newCommentsByEntityId = {
4510
+ ...currentState.comments_by_entity_id,
4511
+ [entityId]: {
4512
+ ...currentState.comments_by_entity_id[entityId],
4513
+ },
4514
+ });
4487
4515
  newCommentsByEntityId[entityId].comments = [
4488
4516
  ...newCommentsByEntityId[entityId].comments,
4489
4517
  ];
4490
4518
  newCommentsByEntityId[entityId]?.comments?.splice(index, 1);
4491
4519
  }
4492
- delete newCommentsByEntityId[comment.id];
4520
+ if (typeof currentState.comments_by_entity_id[comment.id] !== 'undefined') {
4521
+ newCommentsByEntityId ?? (newCommentsByEntityId = {
4522
+ ...currentState.comments_by_entity_id,
4523
+ });
4524
+ delete newCommentsByEntityId[comment.id];
4525
+ }
4526
+ if (!newCommentsByEntityId) {
4527
+ return currentState;
4528
+ }
4493
4529
  return {
4494
4530
  ...currentState,
4495
4531
  comments_by_entity_id: newCommentsByEntityId,
@@ -4621,6 +4657,179 @@ function handleFeedMemberRemoved(event) {
4621
4657
  });
4622
4658
  }
4623
4659
 
4660
+ const sharedUpdateActivity$5 = ({ currentActivity, event, eventBelongsToCurrentUser, }) => {
4661
+ let newOwnBookmarks = currentActivity.own_bookmarks;
4662
+ if (eventBelongsToCurrentUser) {
4663
+ newOwnBookmarks = [...newOwnBookmarks, event.bookmark];
4664
+ }
4665
+ return {
4666
+ ...event.bookmark.activity,
4667
+ own_bookmarks: newOwnBookmarks,
4668
+ own_reactions: currentActivity.own_reactions,
4669
+ };
4670
+ };
4671
+ const addBookmarkToActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
4672
+ entities: activities,
4673
+ matcher: (activity) => activity.id === event.bookmark.activity.id,
4674
+ updater: (matchedActivity) => sharedUpdateActivity$5({
4675
+ currentActivity: matchedActivity,
4676
+ event,
4677
+ eventBelongsToCurrentUser,
4678
+ }),
4679
+ });
4680
+ const addBookmarkToPinnedActivities = (event, pinnedActivities, eventBelongsToCurrentUser) => updateEntityInArray({
4681
+ entities: pinnedActivities,
4682
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.bookmark.activity.id,
4683
+ updater: (matchedPinnedActivity) => {
4684
+ const newActivity = sharedUpdateActivity$5({
4685
+ currentActivity: matchedPinnedActivity.activity,
4686
+ event,
4687
+ eventBelongsToCurrentUser,
4688
+ });
4689
+ if (newActivity === matchedPinnedActivity.activity) {
4690
+ return matchedPinnedActivity;
4691
+ }
4692
+ return {
4693
+ ...matchedPinnedActivity,
4694
+ activity: newActivity,
4695
+ };
4696
+ },
4697
+ });
4698
+ function handleBookmarkAdded(event) {
4699
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4700
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4701
+ const eventBelongsToCurrentUser = event.bookmark.user.id === connectedUser?.id;
4702
+ const [result1, result2] = [
4703
+ addBookmarkToActivities(event, currentActivities, eventBelongsToCurrentUser),
4704
+ addBookmarkToPinnedActivities(event, currentPinnedActivities, eventBelongsToCurrentUser),
4705
+ ];
4706
+ if (result1.changed || result2.changed) {
4707
+ this.state.partialNext({
4708
+ activities: result1.entities,
4709
+ pinned_activities: result2.entities,
4710
+ });
4711
+ }
4712
+ }
4713
+
4714
+ // Helper function to check if two bookmarks are the same
4715
+ // A bookmark is identified by activity_id + folder_id + user_id
4716
+ const isSameBookmark = (bookmark1, bookmark2) => {
4717
+ return (bookmark1.user.id === bookmark2.user.id &&
4718
+ bookmark1.activity.id === bookmark2.activity.id &&
4719
+ bookmark1.folder?.id === bookmark2.folder?.id);
4720
+ };
4721
+ const sharedUpdateActivity$4 = ({ currentActivity, event, eventBelongsToCurrentUser, }) => {
4722
+ let newOwnBookmarks = currentActivity.own_bookmarks;
4723
+ if (eventBelongsToCurrentUser) {
4724
+ newOwnBookmarks = currentActivity.own_bookmarks.filter((bookmark) => !isSameBookmark(bookmark, event.bookmark));
4725
+ }
4726
+ return {
4727
+ ...event.bookmark.activity,
4728
+ own_bookmarks: newOwnBookmarks,
4729
+ own_reactions: currentActivity.own_reactions,
4730
+ };
4731
+ };
4732
+ const removeBookmarkFromActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
4733
+ entities: activities,
4734
+ matcher: (activity) => activity.id === event.bookmark.activity.id,
4735
+ updater: (matchedActivity) => sharedUpdateActivity$4({
4736
+ currentActivity: matchedActivity,
4737
+ event,
4738
+ eventBelongsToCurrentUser,
4739
+ }),
4740
+ });
4741
+ const removeBookmarkFromPinnedActivities = (event, pinnedActivities, eventBelongsToCurrentUser) => updateEntityInArray({
4742
+ entities: pinnedActivities,
4743
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.bookmark.activity.id,
4744
+ updater: (matchedPinnedActivity) => {
4745
+ const newActivity = sharedUpdateActivity$4({
4746
+ currentActivity: matchedPinnedActivity.activity,
4747
+ event,
4748
+ eventBelongsToCurrentUser,
4749
+ });
4750
+ if (newActivity === matchedPinnedActivity.activity) {
4751
+ return matchedPinnedActivity;
4752
+ }
4753
+ return {
4754
+ ...matchedPinnedActivity,
4755
+ activity: newActivity,
4756
+ };
4757
+ },
4758
+ });
4759
+ function handleBookmarkDeleted(event) {
4760
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4761
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4762
+ const eventBelongsToCurrentUser = event.bookmark.user.id === connectedUser?.id;
4763
+ const [result1, result2] = [
4764
+ removeBookmarkFromActivities(event, currentActivities, eventBelongsToCurrentUser),
4765
+ removeBookmarkFromPinnedActivities(event, currentPinnedActivities, eventBelongsToCurrentUser),
4766
+ ];
4767
+ if (result1.changed || result2.changed) {
4768
+ this.state.partialNext({
4769
+ activities: result1.entities,
4770
+ pinned_activities: result2.entities,
4771
+ });
4772
+ }
4773
+ }
4774
+
4775
+ const sharedUpdateActivity$3 = ({ currentActivity, event, eventBelongsToCurrentUser, }) => {
4776
+ let newOwnBookmarks = currentActivity.own_bookmarks;
4777
+ if (eventBelongsToCurrentUser) {
4778
+ const bookmarkIndex = newOwnBookmarks.findIndex((bookmark) => isSameBookmark(bookmark, event.bookmark));
4779
+ if (bookmarkIndex !== -1) {
4780
+ newOwnBookmarks = [...newOwnBookmarks];
4781
+ newOwnBookmarks[bookmarkIndex] = event.bookmark;
4782
+ }
4783
+ }
4784
+ return {
4785
+ ...event.bookmark.activity,
4786
+ own_bookmarks: newOwnBookmarks,
4787
+ own_reactions: currentActivity.own_reactions,
4788
+ };
4789
+ };
4790
+ const updateBookmarkInActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
4791
+ entities: activities,
4792
+ matcher: (activity) => activity.id === event.bookmark.activity.id,
4793
+ updater: (matchedActivity) => sharedUpdateActivity$3({
4794
+ currentActivity: matchedActivity,
4795
+ event,
4796
+ eventBelongsToCurrentUser,
4797
+ }),
4798
+ });
4799
+ const updateBookmarkInPinnedActivities = (event, pinnedActivities, eventBelongsToCurrentUser) => updateEntityInArray({
4800
+ entities: pinnedActivities,
4801
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.bookmark.activity.id,
4802
+ updater: (matchedPinnedActivity) => {
4803
+ const newActivity = sharedUpdateActivity$3({
4804
+ currentActivity: matchedPinnedActivity.activity,
4805
+ event,
4806
+ eventBelongsToCurrentUser,
4807
+ });
4808
+ if (newActivity === matchedPinnedActivity.activity) {
4809
+ return matchedPinnedActivity;
4810
+ }
4811
+ return {
4812
+ ...matchedPinnedActivity,
4813
+ activity: newActivity,
4814
+ };
4815
+ },
4816
+ });
4817
+ function handleBookmarkUpdated(event) {
4818
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4819
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4820
+ const eventBelongsToCurrentUser = event.bookmark.user.id === connectedUser?.id;
4821
+ const [result1, result2] = [
4822
+ updateBookmarkInActivities(event, currentActivities, eventBelongsToCurrentUser),
4823
+ updateBookmarkInPinnedActivities(event, currentPinnedActivities, eventBelongsToCurrentUser),
4824
+ ];
4825
+ if (result1.changed || result2.changed) {
4826
+ this.state.partialNext({
4827
+ activities: result1.entities,
4828
+ pinned_activities: result2.entities,
4829
+ });
4830
+ }
4831
+ }
4832
+
4624
4833
  const addActivitiesToState = (newActivities, activities, position) => {
4625
4834
  let result;
4626
4835
  if (activities === undefined) {
@@ -4665,7 +4874,8 @@ function handleActivityAdded(event) {
4665
4874
  }
4666
4875
 
4667
4876
  const removeActivityFromState = (activityResponse, activities) => {
4668
- const index = activities.findIndex((a) => a.id === activityResponse.id);
4877
+ const index = activities?.findIndex((activity) => activity.id === activityResponse.id) ??
4878
+ -1;
4669
4879
  if (index !== -1) {
4670
4880
  const newActivities = [...activities];
4671
4881
  newActivities.splice(index, 1);
@@ -4675,13 +4885,28 @@ const removeActivityFromState = (activityResponse, activities) => {
4675
4885
  return { changed: false, activities };
4676
4886
  }
4677
4887
  };
4888
+ const removePinnedActivityFromState = (activityResponse, pinnedActivities) => {
4889
+ const index = pinnedActivities?.findIndex((pinnedActivity) => pinnedActivity.activity.id === activityResponse.id) ?? -1;
4890
+ if (index !== -1) {
4891
+ const newActivities = [...pinnedActivities];
4892
+ newActivities.splice(index, 1);
4893
+ return { changed: true, activities: newActivities };
4894
+ }
4895
+ else {
4896
+ return { changed: false, pinned_activities: pinnedActivities };
4897
+ }
4898
+ };
4678
4899
  function handleActivityDeleted(event) {
4679
- const currentActivities = this.currentState.activities;
4680
- if (currentActivities) {
4681
- const result = removeActivityFromState(event.activity, currentActivities);
4682
- if (result.changed) {
4683
- this.state.partialNext({ activities: result.activities });
4684
- }
4900
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4901
+ const [result1, result2] = [
4902
+ removeActivityFromState(event.activity, currentActivities),
4903
+ removePinnedActivityFromState(event.activity, currentPinnedActivities),
4904
+ ];
4905
+ if (result1.changed || result2.changed) {
4906
+ this.state.partialNext({
4907
+ activities: result1.activities,
4908
+ pinned_activities: result2.pinned_activities,
4909
+ });
4685
4910
  }
4686
4911
  }
4687
4912
 
@@ -4695,233 +4920,242 @@ function handleActivityRemovedFromFeed(event) {
4695
4920
  }
4696
4921
  }
4697
4922
 
4698
- const updateActivityInState = (updatedActivityResponse, activities, replaceCompletely = false) => {
4699
- const index = activities.findIndex((a) => a.id === updatedActivityResponse.id);
4700
- if (index !== -1) {
4701
- const newActivities = [...activities];
4702
- const activity = activities[index];
4703
- if (replaceCompletely) {
4704
- newActivities[index] = updatedActivityResponse;
4705
- }
4706
- else {
4707
- newActivities[index] = {
4708
- ...updatedActivityResponse,
4709
- own_reactions: activity.own_reactions,
4710
- own_bookmarks: activity.own_bookmarks,
4711
- latest_reactions: activity.latest_reactions,
4712
- reaction_groups: activity.reaction_groups,
4713
- };
4923
+ const sharedUpdateActivity$2 = ({ currentActivity, event, }) => {
4924
+ return {
4925
+ ...event.activity,
4926
+ own_reactions: currentActivity.own_reactions,
4927
+ own_bookmarks: currentActivity.own_bookmarks,
4928
+ };
4929
+ };
4930
+ const updateActivityInState = (event, activities) => updateEntityInArray({
4931
+ entities: activities,
4932
+ matcher: (activity) => activity.id === event.activity.id,
4933
+ updater: (matchedActivity) => sharedUpdateActivity$2({
4934
+ currentActivity: matchedActivity,
4935
+ event,
4936
+ }),
4937
+ });
4938
+ const updatePinnedActivityInState = (event, pinnedActivities) => updateEntityInArray({
4939
+ entities: pinnedActivities,
4940
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.activity.id,
4941
+ updater: (matchedPinnedActivity) => {
4942
+ const newActivity = sharedUpdateActivity$2({
4943
+ currentActivity: matchedPinnedActivity.activity,
4944
+ event,
4945
+ });
4946
+ if (newActivity === matchedPinnedActivity.activity) {
4947
+ return matchedPinnedActivity;
4714
4948
  }
4715
- return { changed: true, activities: newActivities };
4716
- }
4717
- else {
4718
- return { changed: false, activities };
4719
- }
4720
- };
4949
+ return {
4950
+ ...matchedPinnedActivity,
4951
+ activity: newActivity,
4952
+ };
4953
+ },
4954
+ });
4721
4955
  function handleActivityUpdated(event) {
4722
- const currentActivities = this.currentState.activities;
4723
- if (currentActivities) {
4724
- const result = updateActivityInState(event.activity, currentActivities);
4725
- if (result.changed) {
4726
- this.client.hydratePollCache([event.activity]);
4727
- this.state.partialNext({ activities: result.activities });
4728
- }
4956
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4957
+ const [result1, result2] = [
4958
+ updateActivityInState(event, currentActivities),
4959
+ updatePinnedActivityInState(event, currentPinnedActivities),
4960
+ ];
4961
+ if (result1.changed || result2.changed) {
4962
+ this.client.hydratePollCache([event.activity]);
4963
+ this.state.partialNext({
4964
+ activities: result1.entities,
4965
+ pinned_activities: result2.entities,
4966
+ });
4729
4967
  }
4730
4968
  }
4731
4969
 
4732
- const addReactionToActivity = (event, activity, isCurrentUser) => {
4733
- // Update own_reactions if the reaction is from the current user
4734
- const ownReactions = [...(activity.own_reactions || [])];
4735
- if (isCurrentUser) {
4736
- ownReactions.push(event.reaction);
4970
+ // shared function to update the activity with the new reaction
4971
+ const sharedUpdateActivity$1 = ({ currentActivity, event, eventBelongsToCurrentUser, }) => {
4972
+ let newOwnReactions = currentActivity.own_reactions;
4973
+ if (eventBelongsToCurrentUser) {
4974
+ newOwnReactions = [...currentActivity.own_reactions, event.reaction];
4737
4975
  }
4738
4976
  return {
4739
- ...activity,
4740
- own_reactions: ownReactions,
4741
- latest_reactions: event.activity.latest_reactions,
4742
- reaction_groups: event.activity.reaction_groups,
4743
- changed: true,
4744
- };
4745
- };
4746
- const addReactionToActivities = (event, activities, isCurrentUser) => {
4747
- if (!activities) {
4748
- return { changed: false, activities: [] };
4749
- }
4750
- const activityIndex = activities.findIndex((a) => a.id === event.activity.id);
4751
- if (activityIndex === -1) {
4752
- return { changed: false, activities };
4753
- }
4754
- const activity = activities[activityIndex];
4755
- const updatedActivity = addReactionToActivity(event, activity, isCurrentUser);
4756
- return updateActivityInState(updatedActivity, activities, true);
4757
- };
4977
+ ...event.activity,
4978
+ own_reactions: newOwnReactions,
4979
+ own_bookmarks: currentActivity.own_bookmarks,
4980
+ };
4981
+ };
4982
+ const addReactionToActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
4983
+ entities: activities,
4984
+ matcher: (activity) => activity.id === event.activity.id,
4985
+ updater: (matchedActivity) => sharedUpdateActivity$1({
4986
+ currentActivity: matchedActivity,
4987
+ event,
4988
+ eventBelongsToCurrentUser,
4989
+ }),
4990
+ });
4991
+ const addReactionToPinnedActivities = (event, pinnedActivities, eventBelongsToCurrentUser) => updateEntityInArray({
4992
+ entities: pinnedActivities,
4993
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.activity.id,
4994
+ updater: (matchedPinnedActivity) => {
4995
+ const newActivity = sharedUpdateActivity$1({
4996
+ currentActivity: matchedPinnedActivity.activity,
4997
+ event,
4998
+ eventBelongsToCurrentUser,
4999
+ });
5000
+ // this should never happen, but just in case
5001
+ if (newActivity === matchedPinnedActivity.activity) {
5002
+ return matchedPinnedActivity;
5003
+ }
5004
+ return {
5005
+ ...matchedPinnedActivity,
5006
+ activity: newActivity,
5007
+ };
5008
+ },
5009
+ });
4758
5010
  function handleActivityReactionAdded(event) {
4759
- const currentActivities = this.currentState.activities;
5011
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4760
5012
  const connectedUser = this.client.state.getLatestValue().connected_user;
4761
- const isCurrentUser = Boolean(connectedUser && event.reaction.user.id === connectedUser.id);
4762
- const result = addReactionToActivities(event, currentActivities, isCurrentUser);
4763
- if (result.changed) {
4764
- this.state.partialNext({ activities: result.activities });
5013
+ const eventBelongsToCurrentUser = typeof connectedUser !== 'undefined' &&
5014
+ event.reaction.user.id === connectedUser.id;
5015
+ const [result1, result2] = [
5016
+ addReactionToActivities(event, currentActivities, eventBelongsToCurrentUser),
5017
+ addReactionToPinnedActivities(event, currentPinnedActivities, eventBelongsToCurrentUser),
5018
+ ];
5019
+ if (result1.changed || result2.changed) {
5020
+ this.state.partialNext({
5021
+ activities: result1.entities,
5022
+ pinned_activities: result2.entities,
5023
+ });
4765
5024
  }
4766
5025
  }
4767
5026
 
4768
- const removeReactionFromActivity = (event, activity, isCurrentUser) => {
4769
- // Update own_reactions if the reaction is from the current user
4770
- const ownReactions = isCurrentUser
4771
- ? (activity.own_reactions || []).filter((r) => !(r.type === event.reaction.type &&
4772
- r.user.id === event.reaction.user.id))
4773
- : activity.own_reactions;
4774
- return {
4775
- ...activity,
4776
- own_reactions: ownReactions,
4777
- latest_reactions: event.activity.latest_reactions,
4778
- reaction_groups: event.activity.reaction_groups,
4779
- changed: true,
4780
- };
4781
- };
4782
- const removeReactionFromActivities = (event, activities, isCurrentUser) => {
4783
- if (!activities) {
4784
- return { changed: false, activities: [] };
4785
- }
4786
- const activityIndex = activities.findIndex((a) => a.id === event.activity.id);
4787
- if (activityIndex === -1) {
4788
- return { changed: false, activities };
5027
+ const sharedUpdateActivity = ({ currentActivity, event, eventBelongsToCurrentUser, }) => {
5028
+ let newOwnReactions = currentActivity.own_reactions;
5029
+ if (eventBelongsToCurrentUser) {
5030
+ newOwnReactions = currentActivity.own_reactions.filter((reaction) => !(reaction.type === event.reaction.type &&
5031
+ reaction.user.id === event.reaction.user.id));
4789
5032
  }
4790
- const activity = activities[activityIndex];
4791
- const updatedActivity = removeReactionFromActivity(event, activity, isCurrentUser);
4792
- return updateActivityInState(updatedActivity, activities, true);
4793
- };
5033
+ return {
5034
+ ...event.activity,
5035
+ own_reactions: newOwnReactions,
5036
+ own_bookmarks: currentActivity.own_bookmarks,
5037
+ };
5038
+ };
5039
+ const removeReactionFromActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
5040
+ entities: activities,
5041
+ matcher: (activity) => activity.id === event.activity.id,
5042
+ updater: (matchedActivity) => sharedUpdateActivity({
5043
+ currentActivity: matchedActivity,
5044
+ event,
5045
+ eventBelongsToCurrentUser,
5046
+ }),
5047
+ });
5048
+ const removeReactionFromPinnedActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
5049
+ entities: activities,
5050
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.activity.id,
5051
+ updater: (matchedPinnedActivity) => {
5052
+ const newActivity = sharedUpdateActivity({
5053
+ currentActivity: matchedPinnedActivity.activity,
5054
+ event,
5055
+ eventBelongsToCurrentUser,
5056
+ });
5057
+ if (newActivity === matchedPinnedActivity.activity) {
5058
+ return matchedPinnedActivity;
5059
+ }
5060
+ return {
5061
+ ...matchedPinnedActivity,
5062
+ activity: newActivity,
5063
+ };
5064
+ },
5065
+ });
4794
5066
  function handleActivityReactionDeleted(event) {
4795
- const currentActivities = this.currentState.activities;
5067
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4796
5068
  const connectedUser = this.client.state.getLatestValue().connected_user;
4797
- const isCurrentUser = Boolean(connectedUser && event.reaction.user.id === connectedUser.id);
4798
- const result = removeReactionFromActivities(event, currentActivities, isCurrentUser);
4799
- if (result.changed) {
4800
- this.state.partialNext({ activities: result.activities });
5069
+ const eventBelongsToCurrentUser = typeof connectedUser !== 'undefined' &&
5070
+ event.reaction.user.id === connectedUser.id;
5071
+ const [result1, result2] = [
5072
+ removeReactionFromActivities(event, currentActivities, eventBelongsToCurrentUser),
5073
+ removeReactionFromPinnedActivities(event, currentPinnedActivities, eventBelongsToCurrentUser),
5074
+ ];
5075
+ if (result1.changed || result2.changed) {
5076
+ this.state.partialNext({
5077
+ activities: result1.entities,
5078
+ pinned_activities: result2.entities,
5079
+ });
4801
5080
  }
4802
5081
  }
4803
5082
 
4804
- const addBookmarkToActivity = (event, activity, isCurrentUser) => {
4805
- // Update own_bookmarks if the bookmark is from the current user
4806
- const ownBookmarks = [...(activity.own_bookmarks || [])];
4807
- if (isCurrentUser) {
4808
- ownBookmarks.push(event.bookmark);
5083
+ const updateNotificationStatusFromActivityMarked = (event, currentNotificationStatus, aggregatedActivities = []) => {
5084
+ if (!currentNotificationStatus) {
5085
+ return {
5086
+ changed: false,
5087
+ };
4809
5088
  }
4810
- return {
4811
- ...activity,
4812
- own_bookmarks: ownBookmarks,
4813
- changed: true,
5089
+ const newState = {
5090
+ ...currentNotificationStatus,
4814
5091
  };
4815
- };
4816
- const addBookmarkToActivities = (event, activities, isCurrentUser) => {
4817
- if (!activities) {
4818
- return { changed: false, activities: [] };
4819
- }
4820
- const activityIndex = activities.findIndex((a) => a.id === event.bookmark.activity.id);
4821
- if (activityIndex === -1) {
4822
- return { changed: false, activities };
4823
- }
4824
- const activity = activities[activityIndex];
4825
- const updatedActivity = addBookmarkToActivity(event, activity, isCurrentUser);
4826
- return updateActivityInState(updatedActivity, activities, true);
4827
- };
4828
- function handleBookmarkAdded(event) {
4829
- const currentActivities = this.currentState.activities;
4830
- const { connected_user: connectedUser } = this.client.state.getLatestValue();
4831
- const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4832
- const result = addBookmarkToActivities(event, currentActivities, isCurrentUser);
4833
- if (result.changed) {
4834
- this.state.partialNext({ activities: result.activities });
5092
+ if (event.mark_all_read) {
5093
+ const allGroupIds = aggregatedActivities.map((activity) => activity.group);
5094
+ newState.read_activities = [
5095
+ ...new Set([
5096
+ ...(currentNotificationStatus.read_activities ?? []),
5097
+ ...allGroupIds,
5098
+ ]),
5099
+ ];
4835
5100
  }
4836
- }
4837
-
4838
- // Helper function to check if two bookmarks are the same
4839
- // A bookmark is identified by activity_id + folder_id + user_id
4840
- const isSameBookmark = (bookmark1, bookmark2) => {
4841
- return (bookmark1.user.id === bookmark2.user.id &&
4842
- bookmark1.activity.id === bookmark2.activity.id &&
4843
- bookmark1.folder?.id === bookmark2.folder?.id);
4844
- };
4845
- const removeBookmarkFromActivities = (event, activities, isCurrentUser) => {
4846
- if (!activities) {
4847
- return { changed: false, activities: [] };
5101
+ if (event.mark_read && event.mark_read.length > 0) {
5102
+ newState.read_activities = [
5103
+ ...new Set([
5104
+ ...(currentNotificationStatus?.read_activities ?? []),
5105
+ ...event.mark_read,
5106
+ ]),
5107
+ ];
4848
5108
  }
4849
- const activityIndex = activities.findIndex((a) => a.id === event.bookmark.activity.id);
4850
- if (activityIndex === -1) {
4851
- return { changed: false, activities };
5109
+ if (event.mark_all_seen) {
5110
+ newState.last_seen_at = new Date();
4852
5111
  }
4853
- const activity = activities[activityIndex];
4854
- const updatedActivity = removeBookmarkFromActivity(event, activity, isCurrentUser);
4855
- return updateActivityInState(updatedActivity, activities, true);
4856
- };
4857
- const removeBookmarkFromActivity = (event, activity, isCurrentUser) => {
4858
- // Update own_bookmarks if the bookmark is from the current user
4859
- const ownBookmarks = isCurrentUser
4860
- ? (activity.own_bookmarks || []).filter((bookmark) => !isSameBookmark(bookmark, event.bookmark))
4861
- : activity.own_bookmarks;
4862
5112
  return {
4863
- ...activity,
4864
- own_bookmarks: ownBookmarks,
4865
5113
  changed: true,
5114
+ data: { notification_status: newState },
4866
5115
  };
4867
5116
  };
4868
- function handleBookmarkDeleted(event) {
4869
- const currentActivities = this.currentState.activities;
4870
- const { connected_user: connectedUser } = this.client.state.getLatestValue();
4871
- const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4872
- const result = removeBookmarkFromActivities(event, currentActivities, isCurrentUser);
5117
+ function handleActivityMarked(event) {
5118
+ const result = updateNotificationStatusFromActivityMarked(event, this.currentState.notification_status, this.currentState.aggregated_activities);
4873
5119
  if (result.changed) {
4874
- this.state.partialNext({ activities: result.activities });
5120
+ this.state.partialNext({
5121
+ notification_status: result.data?.notification_status,
5122
+ });
4875
5123
  }
4876
5124
  }
4877
5125
 
4878
- const updateBookmarkInActivity = (event, activity, isCurrentUser) => {
4879
- // Update own_bookmarks if the bookmark is from the current user
4880
- let ownBookmarks = activity.own_bookmarks || [];
4881
- if (isCurrentUser) {
4882
- const bookmarkIndex = ownBookmarks.findIndex((bookmark) => isSameBookmark(bookmark, event.bookmark));
4883
- if (bookmarkIndex !== -1) {
4884
- ownBookmarks = [...ownBookmarks];
4885
- ownBookmarks[bookmarkIndex] = event.bookmark;
4886
- }
5126
+ function handleFeedUpdated(event) {
5127
+ this.state.partialNext({ ...event.feed });
5128
+ }
5129
+
5130
+ const updateNotificationFeedFromEvent = (event) => {
5131
+ const updates = {};
5132
+ if (event.notification_status) {
5133
+ updates.notification_status = event.notification_status;
4887
5134
  }
4888
- return {
4889
- ...activity,
4890
- own_bookmarks: ownBookmarks,
4891
- changed: true,
4892
- };
4893
- };
4894
- const updateBookmarkInActivities = (event, activities, isCurrentUser) => {
4895
- if (!activities) {
4896
- return { changed: false, activities: [] };
5135
+ if (event.aggregated_activities) {
5136
+ updates.aggregated_activities = event.aggregated_activities;
4897
5137
  }
4898
- const activityIndex = activities.findIndex((a) => a.id === event.bookmark.activity.id);
4899
- if (activityIndex === -1) {
4900
- return { changed: false, activities };
5138
+ // Only return changed if we have actual updates
5139
+ if (Object.keys(updates).length > 0) {
5140
+ return {
5141
+ changed: true,
5142
+ data: updates,
5143
+ };
4901
5144
  }
4902
- const activity = activities[activityIndex];
4903
- const updatedActivity = updateBookmarkInActivity(event, activity, isCurrentUser);
4904
- return updateActivityInState(updatedActivity, activities, true);
5145
+ return {
5146
+ changed: false,
5147
+ };
4905
5148
  };
4906
- function handleBookmarkUpdated(event) {
4907
- const currentActivities = this.currentState.activities;
4908
- const { connected_user: connectedUser } = this.client.state.getLatestValue();
4909
- const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4910
- const result = updateBookmarkInActivities(event, currentActivities, isCurrentUser);
5149
+ function handleNotificationFeedUpdated(event) {
5150
+ const result = updateNotificationFeedFromEvent(event);
4911
5151
  if (result.changed) {
4912
- this.state.partialNext({ activities: result.activities });
5152
+ this.state.partialNext({
5153
+ notification_status: result.data?.notification_status,
5154
+ aggregated_activities: result.data?.aggregated_activities,
5155
+ });
4913
5156
  }
4914
5157
  }
4915
5158
 
4916
- function handleFeedUpdated(event) {
4917
- this.state.partialNext({ ...event.feed });
4918
- }
4919
-
4920
- function handleNotificationFeedUpdated(event) {
4921
- console.info('notification feed updated', event);
4922
- // TODO: handle notification feed updates
4923
- }
4924
-
4925
5159
  function handleWatchStarted() {
4926
5160
  this.state.partialNext({ watch: true });
4927
5161
  }
@@ -4974,7 +5208,7 @@ class Feed extends FeedApi {
4974
5208
  'feeds.poll.vote_removed': Feed.noop,
4975
5209
  'feeds.activity.pinned': Feed.noop,
4976
5210
  'feeds.activity.unpinned': Feed.noop,
4977
- 'feeds.activity.marked': Feed.noop,
5211
+ 'feeds.activity.marked': handleActivityMarked.bind(this),
4978
5212
  'moderation.custom_action': Feed.noop,
4979
5213
  'moderation.flagged': Feed.noop,
4980
5214
  'moderation.mark_reviewed': Feed.noop,
@@ -5846,10 +6080,10 @@ const useFeedsClient = () => {
5846
6080
  */
5847
6081
  const useClientConnectedUser = () => {
5848
6082
  const client = useFeedsClient();
5849
- const { user } = useStateStore(client?.state, selector$a) ?? {};
6083
+ const { user } = useStateStore(client?.state, selector$c) ?? {};
5850
6084
  return user;
5851
6085
  };
5852
- const selector$a = (nextState) => ({
6086
+ const selector$c = (nextState) => ({
5853
6087
  user: nextState.connected_user,
5854
6088
  });
5855
6089
 
@@ -5858,10 +6092,10 @@ const selector$a = (nextState) => ({
5858
6092
  */
5859
6093
  const useWsConnectionState = () => {
5860
6094
  const client = useFeedsClient();
5861
- const { is_healthy } = useStateStore(client?.state, selector$9) ?? {};
6095
+ const { is_healthy } = useStateStore(client?.state, selector$b) ?? {};
5862
6096
  return { is_healthy };
5863
6097
  };
5864
- const selector$9 = (nextState) => ({
6098
+ const selector$b = (nextState) => ({
5865
6099
  is_healthy: nextState.is_ws_connection_healthy,
5866
6100
  });
5867
6101
 
@@ -5911,7 +6145,7 @@ const useStableCallback = (callback) => {
5911
6145
  const useFeedActivities = (feedFromProps) => {
5912
6146
  const feedFromContext = useFeedContext();
5913
6147
  const feed = feedFromProps ?? feedFromContext;
5914
- const data = useStateStore(feed?.state, selector$8);
6148
+ const data = useStateStore(feed?.state, selector$a);
5915
6149
  const loadNextPage = useStableCallback(async () => {
5916
6150
  if (!feed || !data?.has_next_page || data?.is_loading) {
5917
6151
  return;
@@ -5920,7 +6154,7 @@ const useFeedActivities = (feedFromProps) => {
5920
6154
  });
5921
6155
  return useMemo(() => ({ ...data, loadNextPage }), [data, loadNextPage]);
5922
6156
  };
5923
- const selector$8 = ({ is_loading_activities, next, activities = [] }) => ({
6157
+ const selector$a = ({ is_loading_activities, next, activities = [] }) => ({
5924
6158
  is_loading: is_loading_activities,
5925
6159
  has_next_page: typeof next !== 'undefined',
5926
6160
  activities,
@@ -5993,13 +6227,13 @@ const FeedOwnCapability = {
5993
6227
  };
5994
6228
 
5995
6229
  const stableEmptyArray = [];
5996
- const selector$7 = (currentState) => ({
6230
+ const selector$9 = (currentState) => ({
5997
6231
  oc: currentState.own_capabilities ?? stableEmptyArray,
5998
6232
  });
5999
6233
  const useOwnCapabilities = (feedFromProps) => {
6000
6234
  const feedFromContext = useFeedContext();
6001
6235
  const feed = feedFromProps ?? feedFromContext;
6002
- const { oc = stableEmptyArray } = useStateStore(feed?.state, selector$7) ?? {};
6236
+ const { oc = stableEmptyArray } = useStateStore(feed?.state, selector$9) ?? {};
6003
6237
  return useMemo(() => ({
6004
6238
  can_add_activity: oc.indexOf(FeedOwnCapability.ADD_ACTIVITY) > -1,
6005
6239
  can_add_activity_reaction: oc.indexOf(FeedOwnCapability.ADD_ACTIVITY_REACTION) > -1,
@@ -6034,7 +6268,7 @@ const useOwnCapabilities = (feedFromProps) => {
6034
6268
  }), [oc]);
6035
6269
  };
6036
6270
 
6037
- const selector$6 = ({ follower_count, followers, followers_pagination, }) => ({
6271
+ const selector$8 = ({ follower_count, followers, followers_pagination, }) => ({
6038
6272
  follower_count,
6039
6273
  followers,
6040
6274
  followers_pagination,
@@ -6042,7 +6276,7 @@ const selector$6 = ({ follower_count, followers, followers_pagination, }) => ({
6042
6276
  function useFollowers(feedFromProps) {
6043
6277
  const feedFromContext = useFeedContext();
6044
6278
  const feed = feedFromProps ?? feedFromContext;
6045
- const data = useStateStore(feed?.state, selector$6);
6279
+ const data = useStateStore(feed?.state, selector$8);
6046
6280
  const loadNextPage = useCallback((...options) => feed?.loadNextPageFollowers(...options), [feed]);
6047
6281
  return useMemo(() => {
6048
6282
  if (!data) {
@@ -6057,7 +6291,7 @@ function useFollowers(feedFromProps) {
6057
6291
  }, [data, loadNextPage]);
6058
6292
  }
6059
6293
 
6060
- const selector$5 = ({ following_count, following, following_pagination, }) => ({
6294
+ const selector$7 = ({ following_count, following, following_pagination, }) => ({
6061
6295
  following_count,
6062
6296
  following,
6063
6297
  following_pagination,
@@ -6065,7 +6299,7 @@ const selector$5 = ({ following_count, following, following_pagination, }) => ({
6065
6299
  function useFollowing(feedFromProps) {
6066
6300
  const feedFromContext = useFeedContext();
6067
6301
  const feed = feedFromProps ?? feedFromContext;
6068
- const data = useStateStore(feed?.state, selector$5);
6302
+ const data = useStateStore(feed?.state, selector$7);
6069
6303
  const loadNextPage = useCallback((...options) => feed?.loadNextPageFollowing(...options), [feed]);
6070
6304
  return useMemo(() => {
6071
6305
  if (!data) {
@@ -6087,9 +6321,9 @@ function useFollowing(feedFromProps) {
6087
6321
  const useFeedMetadata = (feedFromProps) => {
6088
6322
  const feedFromContext = useFeedContext();
6089
6323
  const feed = feedFromProps ?? feedFromContext;
6090
- return useStateStore(feed?.state, selector$4);
6324
+ return useStateStore(feed?.state, selector$6);
6091
6325
  };
6092
- const selector$4 = ({ follower_count = 0, following_count = 0, created_by, created_at, updated_at, }) => ({
6326
+ const selector$6 = ({ follower_count = 0, following_count = 0, created_by, created_at, updated_at, }) => ({
6093
6327
  created_by,
6094
6328
  follower_count,
6095
6329
  following_count,
@@ -6104,12 +6338,57 @@ const selector$4 = ({ follower_count = 0, following_count = 0, created_by, creat
6104
6338
  const useOwnFollows = (feedFromProps) => {
6105
6339
  const feedFromContext = useFeedContext();
6106
6340
  const feed = feedFromProps ?? feedFromContext;
6107
- return useStateStore(feed?.state, selector$3);
6341
+ return useStateStore(feed?.state, selector$5);
6108
6342
  };
6109
- const selector$3 = ({ own_follows }) => ({
6343
+ const selector$5 = ({ own_follows }) => ({
6110
6344
  own_follows,
6111
6345
  });
6112
6346
 
6347
+ const selector$4 = ({ notification_status }) => ({
6348
+ unread: notification_status?.unread ?? 0,
6349
+ unseen: notification_status?.unseen ?? 0,
6350
+ last_read_at: notification_status?.last_read_at,
6351
+ last_seen_at: notification_status?.last_seen_at,
6352
+ read_activities: notification_status?.read_activities,
6353
+ seen_activities: notification_status?.seen_activities,
6354
+ });
6355
+ function useNotificationStatus(feedFromProps) {
6356
+ const feedFromContext = useFeedContext();
6357
+ const feed = feedFromProps ?? feedFromContext;
6358
+ return useStateStore(feed?.state, selector$4);
6359
+ }
6360
+
6361
+ const selector$3 = ({ aggregated_activities }) => ({
6362
+ aggregated_activities,
6363
+ });
6364
+ function useAggregatedActivities(feedFromProps) {
6365
+ const feedFromContext = useFeedContext();
6366
+ const feed = feedFromProps ?? feedFromContext;
6367
+ return useStateStore(feed?.state, selector$3);
6368
+ }
6369
+
6370
+ const useIsAggregatedActivityRead = ({ feed: feedFromProps, aggregatedActivity, }) => {
6371
+ const feedFromContext = useFeedContext();
6372
+ const feed = feedFromProps ?? feedFromContext;
6373
+ const { read_activities: readActivities, /* last_read_at: lastReadAt */ } = useNotificationStatus(feed) ?? {};
6374
+ const group = aggregatedActivity.group;
6375
+ return useMemo(() =>
6376
+ // 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.
6377
+ // (lastReadAt &&
6378
+ // aggregatedActivity.updated_at.getTime() <= lastReadAt.getTime()) ||
6379
+ (readActivities ?? []).includes(group), [readActivities, group]);
6380
+ };
6381
+
6382
+ const useIsAggregatedActivitySeen = ({ feed: feedFromProps, aggregatedActivity, }) => {
6383
+ const feedFromContext = useFeedContext();
6384
+ const feed = feedFromProps ?? feedFromContext;
6385
+ const { seen_activities: seenActivities, last_seen_at: lastSeenAt } = useNotificationStatus(feed) ?? {};
6386
+ const group = aggregatedActivity.group;
6387
+ return useMemo(() => (lastSeenAt &&
6388
+ aggregatedActivity.updated_at.getTime() < lastSeenAt.getTime()) ||
6389
+ (seenActivities ?? []).includes(group), [lastSeenAt, aggregatedActivity.updated_at, seenActivities, group]);
6390
+ };
6391
+
6113
6392
  const StreamSearchResultsContext = createContext(undefined);
6114
6393
  /**
6115
6394
  * Hook to access the nearest SearchSource instance.
@@ -6226,7 +6505,7 @@ const StreamFeeds = ({ client, children }) => {
6226
6505
  };
6227
6506
  StreamFeeds.displayName = 'StreamFeeds';
6228
6507
 
6229
- const StreamFeed = ({ feed, children }) => {
6508
+ const StreamFeed = ({ feed, children, }) => {
6230
6509
  return (jsx(StreamFeedContext.Provider, { value: feed, children: children }));
6231
6510
  };
6232
6511
  StreamFeed.displayName = 'StreamFeed';
@@ -6241,5 +6520,5 @@ const StreamSearchResults = ({ source, children, }) => {
6241
6520
  };
6242
6521
  StreamSearchResults.displayName = 'StreamSearchResults';
6243
6522
 
6244
- export { StreamFeed, StreamFeedContext, StreamFeeds, StreamFeedsContext, StreamSearch, StreamSearchContext, StreamSearchResults, StreamSearchResultsContext, useBookmarkActions, useClientConnectedUser, useComments, useCreateFeedsClient, useFeedActivities, useFeedContext, useFeedMetadata, useFeedsClient, useFollowers, useFollowing, useOwnCapabilities, useOwnFollows, useReactionActions, useSearchContext, useSearchQuery, useSearchResult, useSearchResultsContext, useSearchSources, useStateStore, useWsConnectionState };
6523
+ export { StreamFeed, StreamFeedContext, StreamFeeds, StreamFeedsContext, StreamSearch, StreamSearchContext, StreamSearchResults, StreamSearchResultsContext, useAggregatedActivities, useBookmarkActions, useClientConnectedUser, useComments, useCreateFeedsClient, useFeedActivities, useFeedContext, useFeedMetadata, useFeedsClient, useFollowers, useFollowing, useIsAggregatedActivityRead, useIsAggregatedActivitySeen, useNotificationStatus, useOwnCapabilities, useOwnFollows, useReactionActions, useSearchContext, useSearchQuery, useSearchResult, useSearchResultsContext, useSearchSources, useStateStore, useWsConnectionState };
6245
6524
  //# sourceMappingURL=index-react-bindings.node.js.map