@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
@@ -4,7 +4,7 @@ var axios = require('axios');
4
4
 
5
5
  const decoders = {};
6
6
  const decodeDatetimeType = (input) => typeof input === 'number'
7
- ? new Date(Math.floor(input / 1000000))
7
+ ? new Date(Math.floor(input / 1e6))
8
8
  : new Date(input);
9
9
  decoders.DatetimeType = decodeDatetimeType;
10
10
  const decode = (typeMappings, input) => {
@@ -340,6 +340,7 @@ decoders.Channel = (input) => {
340
340
  updated_at: { type: 'DatetimeType', isSingle: true },
341
341
  deleted_at: { type: 'DatetimeType', isSingle: true },
342
342
  last_message_at: { type: 'DatetimeType', isSingle: true },
343
+ message_count_updated_at: { type: 'DatetimeType', isSingle: true },
343
344
  active_live_locations: { type: 'SharedLocation', isSingle: false },
344
345
  invites: { type: 'ChannelMember', isSingle: false },
345
346
  members: { type: 'ChannelMember', isSingle: false },
@@ -617,6 +618,7 @@ decoders.FeedMemberResponse = (input) => {
617
618
  user: { type: 'UserResponse', isSingle: true },
618
619
  invite_accepted_at: { type: 'DatetimeType', isSingle: true },
619
620
  invite_rejected_at: { type: 'DatetimeType', isSingle: true },
621
+ membership_level: { type: 'MembershipLevelResponse', isSingle: true },
620
622
  };
621
623
  return decode(typeMappings, input);
622
624
  };
@@ -789,6 +791,13 @@ decoders.ListDevicesResponse = (input) => {
789
791
  };
790
792
  return decode(typeMappings, input);
791
793
  };
794
+ decoders.MembershipLevelResponse = (input) => {
795
+ const typeMappings = {
796
+ created_at: { type: 'DatetimeType', isSingle: true },
797
+ updated_at: { type: 'DatetimeType', isSingle: true },
798
+ };
799
+ return decode(typeMappings, input);
800
+ };
792
801
  decoders.Message = (input) => {
793
802
  const typeMappings = {
794
803
  created_at: { type: 'DatetimeType', isSingle: true },
@@ -3820,6 +3829,7 @@ class ModerationApi {
3820
3829
  block_list_config: request?.block_list_config,
3821
3830
  bodyguard_config: request?.bodyguard_config,
3822
3831
  google_vision_config: request?.google_vision_config,
3832
+ llm_config: request?.llm_config,
3823
3833
  rule_builder_config: request?.rule_builder_config,
3824
3834
  velocity_filter_config: request?.velocity_filter_config,
3825
3835
  video_call_rule_config: request?.video_call_rule_config,
@@ -4303,6 +4313,23 @@ function getStateUpdateQueueId(data, prefix) {
4303
4313
  throw new Error(`Cannot create state update queueId for data: ${JSON.stringify(data)}`);
4304
4314
  }
4305
4315
 
4316
+ function updateEntityInArray({ matcher, updater, entities, }) {
4317
+ if (!entities || !entities.length) {
4318
+ return { changed: false, entities };
4319
+ }
4320
+ const index = entities.findIndex(matcher);
4321
+ if (index === -1) {
4322
+ return { changed: false, entities };
4323
+ }
4324
+ const newEntity = updater(entities[index]);
4325
+ if (newEntity === entities[index]) {
4326
+ return { changed: false, entities };
4327
+ }
4328
+ const updatedEntities = [...entities];
4329
+ updatedEntities[index] = newEntity;
4330
+ return { changed: true, entities: updatedEntities };
4331
+ }
4332
+
4306
4333
  const updateStateFollowCreated = (follow, currentState, currentFeedId, connectedUserId) => {
4307
4334
  // filter non-accepted follows (the way getOrCreate does by default)
4308
4335
  if (follow.status !== 'accepted') {
@@ -4493,20 +4520,29 @@ function handleCommentAdded(event) {
4493
4520
  function handleCommentDeleted({ comment }) {
4494
4521
  const entityId = comment.parent_id ?? comment.object_id;
4495
4522
  this.state.next((currentState) => {
4496
- const newCommentsByEntityId = {
4497
- ...currentState.comments_by_entity_id,
4498
- [entityId]: {
4499
- ...currentState.comments_by_entity_id[entityId],
4500
- },
4501
- };
4523
+ let newCommentsByEntityId;
4502
4524
  const index = this.getCommentIndex(comment, currentState);
4503
- if (newCommentsByEntityId?.[entityId]?.comments?.length && index !== -1) {
4525
+ if (index !== -1) {
4526
+ newCommentsByEntityId ?? (newCommentsByEntityId = {
4527
+ ...currentState.comments_by_entity_id,
4528
+ [entityId]: {
4529
+ ...currentState.comments_by_entity_id[entityId],
4530
+ },
4531
+ });
4504
4532
  newCommentsByEntityId[entityId].comments = [
4505
4533
  ...newCommentsByEntityId[entityId].comments,
4506
4534
  ];
4507
4535
  newCommentsByEntityId[entityId]?.comments?.splice(index, 1);
4508
4536
  }
4509
- delete newCommentsByEntityId[comment.id];
4537
+ if (typeof currentState.comments_by_entity_id[comment.id] !== 'undefined') {
4538
+ newCommentsByEntityId ?? (newCommentsByEntityId = {
4539
+ ...currentState.comments_by_entity_id,
4540
+ });
4541
+ delete newCommentsByEntityId[comment.id];
4542
+ }
4543
+ if (!newCommentsByEntityId) {
4544
+ return currentState;
4545
+ }
4510
4546
  return {
4511
4547
  ...currentState,
4512
4548
  comments_by_entity_id: newCommentsByEntityId,
@@ -4638,6 +4674,179 @@ function handleFeedMemberRemoved(event) {
4638
4674
  });
4639
4675
  }
4640
4676
 
4677
+ const sharedUpdateActivity$5 = ({ currentActivity, event, eventBelongsToCurrentUser, }) => {
4678
+ let newOwnBookmarks = currentActivity.own_bookmarks;
4679
+ if (eventBelongsToCurrentUser) {
4680
+ newOwnBookmarks = [...newOwnBookmarks, event.bookmark];
4681
+ }
4682
+ return {
4683
+ ...event.bookmark.activity,
4684
+ own_bookmarks: newOwnBookmarks,
4685
+ own_reactions: currentActivity.own_reactions,
4686
+ };
4687
+ };
4688
+ const addBookmarkToActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
4689
+ entities: activities,
4690
+ matcher: (activity) => activity.id === event.bookmark.activity.id,
4691
+ updater: (matchedActivity) => sharedUpdateActivity$5({
4692
+ currentActivity: matchedActivity,
4693
+ event,
4694
+ eventBelongsToCurrentUser,
4695
+ }),
4696
+ });
4697
+ const addBookmarkToPinnedActivities = (event, pinnedActivities, eventBelongsToCurrentUser) => updateEntityInArray({
4698
+ entities: pinnedActivities,
4699
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.bookmark.activity.id,
4700
+ updater: (matchedPinnedActivity) => {
4701
+ const newActivity = sharedUpdateActivity$5({
4702
+ currentActivity: matchedPinnedActivity.activity,
4703
+ event,
4704
+ eventBelongsToCurrentUser,
4705
+ });
4706
+ if (newActivity === matchedPinnedActivity.activity) {
4707
+ return matchedPinnedActivity;
4708
+ }
4709
+ return {
4710
+ ...matchedPinnedActivity,
4711
+ activity: newActivity,
4712
+ };
4713
+ },
4714
+ });
4715
+ function handleBookmarkAdded(event) {
4716
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4717
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4718
+ const eventBelongsToCurrentUser = event.bookmark.user.id === connectedUser?.id;
4719
+ const [result1, result2] = [
4720
+ addBookmarkToActivities(event, currentActivities, eventBelongsToCurrentUser),
4721
+ addBookmarkToPinnedActivities(event, currentPinnedActivities, eventBelongsToCurrentUser),
4722
+ ];
4723
+ if (result1.changed || result2.changed) {
4724
+ this.state.partialNext({
4725
+ activities: result1.entities,
4726
+ pinned_activities: result2.entities,
4727
+ });
4728
+ }
4729
+ }
4730
+
4731
+ // Helper function to check if two bookmarks are the same
4732
+ // A bookmark is identified by activity_id + folder_id + user_id
4733
+ const isSameBookmark = (bookmark1, bookmark2) => {
4734
+ return (bookmark1.user.id === bookmark2.user.id &&
4735
+ bookmark1.activity.id === bookmark2.activity.id &&
4736
+ bookmark1.folder?.id === bookmark2.folder?.id);
4737
+ };
4738
+ const sharedUpdateActivity$4 = ({ currentActivity, event, eventBelongsToCurrentUser, }) => {
4739
+ let newOwnBookmarks = currentActivity.own_bookmarks;
4740
+ if (eventBelongsToCurrentUser) {
4741
+ newOwnBookmarks = currentActivity.own_bookmarks.filter((bookmark) => !isSameBookmark(bookmark, event.bookmark));
4742
+ }
4743
+ return {
4744
+ ...event.bookmark.activity,
4745
+ own_bookmarks: newOwnBookmarks,
4746
+ own_reactions: currentActivity.own_reactions,
4747
+ };
4748
+ };
4749
+ const removeBookmarkFromActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
4750
+ entities: activities,
4751
+ matcher: (activity) => activity.id === event.bookmark.activity.id,
4752
+ updater: (matchedActivity) => sharedUpdateActivity$4({
4753
+ currentActivity: matchedActivity,
4754
+ event,
4755
+ eventBelongsToCurrentUser,
4756
+ }),
4757
+ });
4758
+ const removeBookmarkFromPinnedActivities = (event, pinnedActivities, eventBelongsToCurrentUser) => updateEntityInArray({
4759
+ entities: pinnedActivities,
4760
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.bookmark.activity.id,
4761
+ updater: (matchedPinnedActivity) => {
4762
+ const newActivity = sharedUpdateActivity$4({
4763
+ currentActivity: matchedPinnedActivity.activity,
4764
+ event,
4765
+ eventBelongsToCurrentUser,
4766
+ });
4767
+ if (newActivity === matchedPinnedActivity.activity) {
4768
+ return matchedPinnedActivity;
4769
+ }
4770
+ return {
4771
+ ...matchedPinnedActivity,
4772
+ activity: newActivity,
4773
+ };
4774
+ },
4775
+ });
4776
+ function handleBookmarkDeleted(event) {
4777
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4778
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4779
+ const eventBelongsToCurrentUser = event.bookmark.user.id === connectedUser?.id;
4780
+ const [result1, result2] = [
4781
+ removeBookmarkFromActivities(event, currentActivities, eventBelongsToCurrentUser),
4782
+ removeBookmarkFromPinnedActivities(event, currentPinnedActivities, eventBelongsToCurrentUser),
4783
+ ];
4784
+ if (result1.changed || result2.changed) {
4785
+ this.state.partialNext({
4786
+ activities: result1.entities,
4787
+ pinned_activities: result2.entities,
4788
+ });
4789
+ }
4790
+ }
4791
+
4792
+ const sharedUpdateActivity$3 = ({ currentActivity, event, eventBelongsToCurrentUser, }) => {
4793
+ let newOwnBookmarks = currentActivity.own_bookmarks;
4794
+ if (eventBelongsToCurrentUser) {
4795
+ const bookmarkIndex = newOwnBookmarks.findIndex((bookmark) => isSameBookmark(bookmark, event.bookmark));
4796
+ if (bookmarkIndex !== -1) {
4797
+ newOwnBookmarks = [...newOwnBookmarks];
4798
+ newOwnBookmarks[bookmarkIndex] = event.bookmark;
4799
+ }
4800
+ }
4801
+ return {
4802
+ ...event.bookmark.activity,
4803
+ own_bookmarks: newOwnBookmarks,
4804
+ own_reactions: currentActivity.own_reactions,
4805
+ };
4806
+ };
4807
+ const updateBookmarkInActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
4808
+ entities: activities,
4809
+ matcher: (activity) => activity.id === event.bookmark.activity.id,
4810
+ updater: (matchedActivity) => sharedUpdateActivity$3({
4811
+ currentActivity: matchedActivity,
4812
+ event,
4813
+ eventBelongsToCurrentUser,
4814
+ }),
4815
+ });
4816
+ const updateBookmarkInPinnedActivities = (event, pinnedActivities, eventBelongsToCurrentUser) => updateEntityInArray({
4817
+ entities: pinnedActivities,
4818
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.bookmark.activity.id,
4819
+ updater: (matchedPinnedActivity) => {
4820
+ const newActivity = sharedUpdateActivity$3({
4821
+ currentActivity: matchedPinnedActivity.activity,
4822
+ event,
4823
+ eventBelongsToCurrentUser,
4824
+ });
4825
+ if (newActivity === matchedPinnedActivity.activity) {
4826
+ return matchedPinnedActivity;
4827
+ }
4828
+ return {
4829
+ ...matchedPinnedActivity,
4830
+ activity: newActivity,
4831
+ };
4832
+ },
4833
+ });
4834
+ function handleBookmarkUpdated(event) {
4835
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4836
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
4837
+ const eventBelongsToCurrentUser = event.bookmark.user.id === connectedUser?.id;
4838
+ const [result1, result2] = [
4839
+ updateBookmarkInActivities(event, currentActivities, eventBelongsToCurrentUser),
4840
+ updateBookmarkInPinnedActivities(event, currentPinnedActivities, eventBelongsToCurrentUser),
4841
+ ];
4842
+ if (result1.changed || result2.changed) {
4843
+ this.state.partialNext({
4844
+ activities: result1.entities,
4845
+ pinned_activities: result2.entities,
4846
+ });
4847
+ }
4848
+ }
4849
+
4641
4850
  const addActivitiesToState = (newActivities, activities, position) => {
4642
4851
  let result;
4643
4852
  if (activities === undefined) {
@@ -4682,7 +4891,8 @@ function handleActivityAdded(event) {
4682
4891
  }
4683
4892
 
4684
4893
  const removeActivityFromState = (activityResponse, activities) => {
4685
- const index = activities.findIndex((a) => a.id === activityResponse.id);
4894
+ const index = activities?.findIndex((activity) => activity.id === activityResponse.id) ??
4895
+ -1;
4686
4896
  if (index !== -1) {
4687
4897
  const newActivities = [...activities];
4688
4898
  newActivities.splice(index, 1);
@@ -4692,13 +4902,28 @@ const removeActivityFromState = (activityResponse, activities) => {
4692
4902
  return { changed: false, activities };
4693
4903
  }
4694
4904
  };
4905
+ const removePinnedActivityFromState = (activityResponse, pinnedActivities) => {
4906
+ const index = pinnedActivities?.findIndex((pinnedActivity) => pinnedActivity.activity.id === activityResponse.id) ?? -1;
4907
+ if (index !== -1) {
4908
+ const newActivities = [...pinnedActivities];
4909
+ newActivities.splice(index, 1);
4910
+ return { changed: true, activities: newActivities };
4911
+ }
4912
+ else {
4913
+ return { changed: false, pinned_activities: pinnedActivities };
4914
+ }
4915
+ };
4695
4916
  function handleActivityDeleted(event) {
4696
- const currentActivities = this.currentState.activities;
4697
- if (currentActivities) {
4698
- const result = removeActivityFromState(event.activity, currentActivities);
4699
- if (result.changed) {
4700
- this.state.partialNext({ activities: result.activities });
4701
- }
4917
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4918
+ const [result1, result2] = [
4919
+ removeActivityFromState(event.activity, currentActivities),
4920
+ removePinnedActivityFromState(event.activity, currentPinnedActivities),
4921
+ ];
4922
+ if (result1.changed || result2.changed) {
4923
+ this.state.partialNext({
4924
+ activities: result1.activities,
4925
+ pinned_activities: result2.pinned_activities,
4926
+ });
4702
4927
  }
4703
4928
  }
4704
4929
 
@@ -4712,233 +4937,242 @@ function handleActivityRemovedFromFeed(event) {
4712
4937
  }
4713
4938
  }
4714
4939
 
4715
- const updateActivityInState = (updatedActivityResponse, activities, replaceCompletely = false) => {
4716
- const index = activities.findIndex((a) => a.id === updatedActivityResponse.id);
4717
- if (index !== -1) {
4718
- const newActivities = [...activities];
4719
- const activity = activities[index];
4720
- if (replaceCompletely) {
4721
- newActivities[index] = updatedActivityResponse;
4722
- }
4723
- else {
4724
- newActivities[index] = {
4725
- ...updatedActivityResponse,
4726
- own_reactions: activity.own_reactions,
4727
- own_bookmarks: activity.own_bookmarks,
4728
- latest_reactions: activity.latest_reactions,
4729
- reaction_groups: activity.reaction_groups,
4730
- };
4940
+ const sharedUpdateActivity$2 = ({ currentActivity, event, }) => {
4941
+ return {
4942
+ ...event.activity,
4943
+ own_reactions: currentActivity.own_reactions,
4944
+ own_bookmarks: currentActivity.own_bookmarks,
4945
+ };
4946
+ };
4947
+ const updateActivityInState = (event, activities) => updateEntityInArray({
4948
+ entities: activities,
4949
+ matcher: (activity) => activity.id === event.activity.id,
4950
+ updater: (matchedActivity) => sharedUpdateActivity$2({
4951
+ currentActivity: matchedActivity,
4952
+ event,
4953
+ }),
4954
+ });
4955
+ const updatePinnedActivityInState = (event, pinnedActivities) => updateEntityInArray({
4956
+ entities: pinnedActivities,
4957
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.activity.id,
4958
+ updater: (matchedPinnedActivity) => {
4959
+ const newActivity = sharedUpdateActivity$2({
4960
+ currentActivity: matchedPinnedActivity.activity,
4961
+ event,
4962
+ });
4963
+ if (newActivity === matchedPinnedActivity.activity) {
4964
+ return matchedPinnedActivity;
4731
4965
  }
4732
- return { changed: true, activities: newActivities };
4733
- }
4734
- else {
4735
- return { changed: false, activities };
4736
- }
4737
- };
4966
+ return {
4967
+ ...matchedPinnedActivity,
4968
+ activity: newActivity,
4969
+ };
4970
+ },
4971
+ });
4738
4972
  function handleActivityUpdated(event) {
4739
- const currentActivities = this.currentState.activities;
4740
- if (currentActivities) {
4741
- const result = updateActivityInState(event.activity, currentActivities);
4742
- if (result.changed) {
4743
- this.client.hydratePollCache([event.activity]);
4744
- this.state.partialNext({ activities: result.activities });
4745
- }
4973
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4974
+ const [result1, result2] = [
4975
+ updateActivityInState(event, currentActivities),
4976
+ updatePinnedActivityInState(event, currentPinnedActivities),
4977
+ ];
4978
+ if (result1.changed || result2.changed) {
4979
+ this.client.hydratePollCache([event.activity]);
4980
+ this.state.partialNext({
4981
+ activities: result1.entities,
4982
+ pinned_activities: result2.entities,
4983
+ });
4746
4984
  }
4747
4985
  }
4748
4986
 
4749
- const addReactionToActivity = (event, activity, isCurrentUser) => {
4750
- // Update own_reactions if the reaction is from the current user
4751
- const ownReactions = [...(activity.own_reactions || [])];
4752
- if (isCurrentUser) {
4753
- ownReactions.push(event.reaction);
4987
+ // shared function to update the activity with the new reaction
4988
+ const sharedUpdateActivity$1 = ({ currentActivity, event, eventBelongsToCurrentUser, }) => {
4989
+ let newOwnReactions = currentActivity.own_reactions;
4990
+ if (eventBelongsToCurrentUser) {
4991
+ newOwnReactions = [...currentActivity.own_reactions, event.reaction];
4754
4992
  }
4755
4993
  return {
4756
- ...activity,
4757
- own_reactions: ownReactions,
4758
- latest_reactions: event.activity.latest_reactions,
4759
- reaction_groups: event.activity.reaction_groups,
4760
- changed: true,
4761
- };
4762
- };
4763
- const addReactionToActivities = (event, activities, isCurrentUser) => {
4764
- if (!activities) {
4765
- return { changed: false, activities: [] };
4766
- }
4767
- const activityIndex = activities.findIndex((a) => a.id === event.activity.id);
4768
- if (activityIndex === -1) {
4769
- return { changed: false, activities };
4770
- }
4771
- const activity = activities[activityIndex];
4772
- const updatedActivity = addReactionToActivity(event, activity, isCurrentUser);
4773
- return updateActivityInState(updatedActivity, activities, true);
4774
- };
4994
+ ...event.activity,
4995
+ own_reactions: newOwnReactions,
4996
+ own_bookmarks: currentActivity.own_bookmarks,
4997
+ };
4998
+ };
4999
+ const addReactionToActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
5000
+ entities: activities,
5001
+ matcher: (activity) => activity.id === event.activity.id,
5002
+ updater: (matchedActivity) => sharedUpdateActivity$1({
5003
+ currentActivity: matchedActivity,
5004
+ event,
5005
+ eventBelongsToCurrentUser,
5006
+ }),
5007
+ });
5008
+ const addReactionToPinnedActivities = (event, pinnedActivities, eventBelongsToCurrentUser) => updateEntityInArray({
5009
+ entities: pinnedActivities,
5010
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.activity.id,
5011
+ updater: (matchedPinnedActivity) => {
5012
+ const newActivity = sharedUpdateActivity$1({
5013
+ currentActivity: matchedPinnedActivity.activity,
5014
+ event,
5015
+ eventBelongsToCurrentUser,
5016
+ });
5017
+ // this should never happen, but just in case
5018
+ if (newActivity === matchedPinnedActivity.activity) {
5019
+ return matchedPinnedActivity;
5020
+ }
5021
+ return {
5022
+ ...matchedPinnedActivity,
5023
+ activity: newActivity,
5024
+ };
5025
+ },
5026
+ });
4775
5027
  function handleActivityReactionAdded(event) {
4776
- const currentActivities = this.currentState.activities;
5028
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4777
5029
  const connectedUser = this.client.state.getLatestValue().connected_user;
4778
- const isCurrentUser = Boolean(connectedUser && event.reaction.user.id === connectedUser.id);
4779
- const result = addReactionToActivities(event, currentActivities, isCurrentUser);
4780
- if (result.changed) {
4781
- this.state.partialNext({ activities: result.activities });
5030
+ const eventBelongsToCurrentUser = typeof connectedUser !== 'undefined' &&
5031
+ event.reaction.user.id === connectedUser.id;
5032
+ const [result1, result2] = [
5033
+ addReactionToActivities(event, currentActivities, eventBelongsToCurrentUser),
5034
+ addReactionToPinnedActivities(event, currentPinnedActivities, eventBelongsToCurrentUser),
5035
+ ];
5036
+ if (result1.changed || result2.changed) {
5037
+ this.state.partialNext({
5038
+ activities: result1.entities,
5039
+ pinned_activities: result2.entities,
5040
+ });
4782
5041
  }
4783
5042
  }
4784
5043
 
4785
- const removeReactionFromActivity = (event, activity, isCurrentUser) => {
4786
- // Update own_reactions if the reaction is from the current user
4787
- const ownReactions = isCurrentUser
4788
- ? (activity.own_reactions || []).filter((r) => !(r.type === event.reaction.type &&
4789
- r.user.id === event.reaction.user.id))
4790
- : activity.own_reactions;
4791
- return {
4792
- ...activity,
4793
- own_reactions: ownReactions,
4794
- latest_reactions: event.activity.latest_reactions,
4795
- reaction_groups: event.activity.reaction_groups,
4796
- changed: true,
4797
- };
4798
- };
4799
- const removeReactionFromActivities = (event, activities, isCurrentUser) => {
4800
- if (!activities) {
4801
- return { changed: false, activities: [] };
5044
+ const sharedUpdateActivity = ({ currentActivity, event, eventBelongsToCurrentUser, }) => {
5045
+ let newOwnReactions = currentActivity.own_reactions;
5046
+ if (eventBelongsToCurrentUser) {
5047
+ newOwnReactions = currentActivity.own_reactions.filter((reaction) => !(reaction.type === event.reaction.type &&
5048
+ reaction.user.id === event.reaction.user.id));
4802
5049
  }
4803
- const activityIndex = activities.findIndex((a) => a.id === event.activity.id);
4804
- if (activityIndex === -1) {
4805
- return { changed: false, activities };
4806
- }
4807
- const activity = activities[activityIndex];
4808
- const updatedActivity = removeReactionFromActivity(event, activity, isCurrentUser);
4809
- return updateActivityInState(updatedActivity, activities, true);
4810
- };
5050
+ return {
5051
+ ...event.activity,
5052
+ own_reactions: newOwnReactions,
5053
+ own_bookmarks: currentActivity.own_bookmarks,
5054
+ };
5055
+ };
5056
+ const removeReactionFromActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
5057
+ entities: activities,
5058
+ matcher: (activity) => activity.id === event.activity.id,
5059
+ updater: (matchedActivity) => sharedUpdateActivity({
5060
+ currentActivity: matchedActivity,
5061
+ event,
5062
+ eventBelongsToCurrentUser,
5063
+ }),
5064
+ });
5065
+ const removeReactionFromPinnedActivities = (event, activities, eventBelongsToCurrentUser) => updateEntityInArray({
5066
+ entities: activities,
5067
+ matcher: (pinnedActivity) => pinnedActivity.activity.id === event.activity.id,
5068
+ updater: (matchedPinnedActivity) => {
5069
+ const newActivity = sharedUpdateActivity({
5070
+ currentActivity: matchedPinnedActivity.activity,
5071
+ event,
5072
+ eventBelongsToCurrentUser,
5073
+ });
5074
+ if (newActivity === matchedPinnedActivity.activity) {
5075
+ return matchedPinnedActivity;
5076
+ }
5077
+ return {
5078
+ ...matchedPinnedActivity,
5079
+ activity: newActivity,
5080
+ };
5081
+ },
5082
+ });
4811
5083
  function handleActivityReactionDeleted(event) {
4812
- const currentActivities = this.currentState.activities;
5084
+ const { activities: currentActivities, pinned_activities: currentPinnedActivities, } = this.currentState;
4813
5085
  const connectedUser = this.client.state.getLatestValue().connected_user;
4814
- const isCurrentUser = Boolean(connectedUser && event.reaction.user.id === connectedUser.id);
4815
- const result = removeReactionFromActivities(event, currentActivities, isCurrentUser);
4816
- if (result.changed) {
4817
- this.state.partialNext({ activities: result.activities });
5086
+ const eventBelongsToCurrentUser = typeof connectedUser !== 'undefined' &&
5087
+ event.reaction.user.id === connectedUser.id;
5088
+ const [result1, result2] = [
5089
+ removeReactionFromActivities(event, currentActivities, eventBelongsToCurrentUser),
5090
+ removeReactionFromPinnedActivities(event, currentPinnedActivities, eventBelongsToCurrentUser),
5091
+ ];
5092
+ if (result1.changed || result2.changed) {
5093
+ this.state.partialNext({
5094
+ activities: result1.entities,
5095
+ pinned_activities: result2.entities,
5096
+ });
4818
5097
  }
4819
5098
  }
4820
5099
 
4821
- const addBookmarkToActivity = (event, activity, isCurrentUser) => {
4822
- // Update own_bookmarks if the bookmark is from the current user
4823
- const ownBookmarks = [...(activity.own_bookmarks || [])];
4824
- if (isCurrentUser) {
4825
- ownBookmarks.push(event.bookmark);
5100
+ const updateNotificationStatusFromActivityMarked = (event, currentNotificationStatus, aggregatedActivities = []) => {
5101
+ if (!currentNotificationStatus) {
5102
+ return {
5103
+ changed: false,
5104
+ };
4826
5105
  }
4827
- return {
4828
- ...activity,
4829
- own_bookmarks: ownBookmarks,
4830
- changed: true,
5106
+ const newState = {
5107
+ ...currentNotificationStatus,
4831
5108
  };
4832
- };
4833
- const addBookmarkToActivities = (event, activities, isCurrentUser) => {
4834
- if (!activities) {
4835
- return { changed: false, activities: [] };
4836
- }
4837
- const activityIndex = activities.findIndex((a) => a.id === event.bookmark.activity.id);
4838
- if (activityIndex === -1) {
4839
- return { changed: false, activities };
4840
- }
4841
- const activity = activities[activityIndex];
4842
- const updatedActivity = addBookmarkToActivity(event, activity, isCurrentUser);
4843
- return updateActivityInState(updatedActivity, activities, true);
4844
- };
4845
- function handleBookmarkAdded(event) {
4846
- const currentActivities = this.currentState.activities;
4847
- const { connected_user: connectedUser } = this.client.state.getLatestValue();
4848
- const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4849
- const result = addBookmarkToActivities(event, currentActivities, isCurrentUser);
4850
- if (result.changed) {
4851
- this.state.partialNext({ activities: result.activities });
5109
+ if (event.mark_all_read) {
5110
+ const allGroupIds = aggregatedActivities.map((activity) => activity.group);
5111
+ newState.read_activities = [
5112
+ ...new Set([
5113
+ ...(currentNotificationStatus.read_activities ?? []),
5114
+ ...allGroupIds,
5115
+ ]),
5116
+ ];
4852
5117
  }
4853
- }
4854
-
4855
- // Helper function to check if two bookmarks are the same
4856
- // A bookmark is identified by activity_id + folder_id + user_id
4857
- const isSameBookmark = (bookmark1, bookmark2) => {
4858
- return (bookmark1.user.id === bookmark2.user.id &&
4859
- bookmark1.activity.id === bookmark2.activity.id &&
4860
- bookmark1.folder?.id === bookmark2.folder?.id);
4861
- };
4862
- const removeBookmarkFromActivities = (event, activities, isCurrentUser) => {
4863
- if (!activities) {
4864
- return { changed: false, activities: [] };
5118
+ if (event.mark_read && event.mark_read.length > 0) {
5119
+ newState.read_activities = [
5120
+ ...new Set([
5121
+ ...(currentNotificationStatus?.read_activities ?? []),
5122
+ ...event.mark_read,
5123
+ ]),
5124
+ ];
4865
5125
  }
4866
- const activityIndex = activities.findIndex((a) => a.id === event.bookmark.activity.id);
4867
- if (activityIndex === -1) {
4868
- return { changed: false, activities };
5126
+ if (event.mark_all_seen) {
5127
+ newState.last_seen_at = new Date();
4869
5128
  }
4870
- const activity = activities[activityIndex];
4871
- const updatedActivity = removeBookmarkFromActivity(event, activity, isCurrentUser);
4872
- return updateActivityInState(updatedActivity, activities, true);
4873
- };
4874
- const removeBookmarkFromActivity = (event, activity, isCurrentUser) => {
4875
- // Update own_bookmarks if the bookmark is from the current user
4876
- const ownBookmarks = isCurrentUser
4877
- ? (activity.own_bookmarks || []).filter((bookmark) => !isSameBookmark(bookmark, event.bookmark))
4878
- : activity.own_bookmarks;
4879
5129
  return {
4880
- ...activity,
4881
- own_bookmarks: ownBookmarks,
4882
5130
  changed: true,
5131
+ data: { notification_status: newState },
4883
5132
  };
4884
5133
  };
4885
- function handleBookmarkDeleted(event) {
4886
- const currentActivities = this.currentState.activities;
4887
- const { connected_user: connectedUser } = this.client.state.getLatestValue();
4888
- const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4889
- const result = removeBookmarkFromActivities(event, currentActivities, isCurrentUser);
5134
+ function handleActivityMarked(event) {
5135
+ const result = updateNotificationStatusFromActivityMarked(event, this.currentState.notification_status, this.currentState.aggregated_activities);
4890
5136
  if (result.changed) {
4891
- this.state.partialNext({ activities: result.activities });
5137
+ this.state.partialNext({
5138
+ notification_status: result.data?.notification_status,
5139
+ });
4892
5140
  }
4893
5141
  }
4894
5142
 
4895
- const updateBookmarkInActivity = (event, activity, isCurrentUser) => {
4896
- // Update own_bookmarks if the bookmark is from the current user
4897
- let ownBookmarks = activity.own_bookmarks || [];
4898
- if (isCurrentUser) {
4899
- const bookmarkIndex = ownBookmarks.findIndex((bookmark) => isSameBookmark(bookmark, event.bookmark));
4900
- if (bookmarkIndex !== -1) {
4901
- ownBookmarks = [...ownBookmarks];
4902
- ownBookmarks[bookmarkIndex] = event.bookmark;
4903
- }
5143
+ function handleFeedUpdated(event) {
5144
+ this.state.partialNext({ ...event.feed });
5145
+ }
5146
+
5147
+ const updateNotificationFeedFromEvent = (event) => {
5148
+ const updates = {};
5149
+ if (event.notification_status) {
5150
+ updates.notification_status = event.notification_status;
4904
5151
  }
4905
- return {
4906
- ...activity,
4907
- own_bookmarks: ownBookmarks,
4908
- changed: true,
4909
- };
4910
- };
4911
- const updateBookmarkInActivities = (event, activities, isCurrentUser) => {
4912
- if (!activities) {
4913
- return { changed: false, activities: [] };
5152
+ if (event.aggregated_activities) {
5153
+ updates.aggregated_activities = event.aggregated_activities;
4914
5154
  }
4915
- const activityIndex = activities.findIndex((a) => a.id === event.bookmark.activity.id);
4916
- if (activityIndex === -1) {
4917
- return { changed: false, activities };
5155
+ // Only return changed if we have actual updates
5156
+ if (Object.keys(updates).length > 0) {
5157
+ return {
5158
+ changed: true,
5159
+ data: updates,
5160
+ };
4918
5161
  }
4919
- const activity = activities[activityIndex];
4920
- const updatedActivity = updateBookmarkInActivity(event, activity, isCurrentUser);
4921
- return updateActivityInState(updatedActivity, activities, true);
5162
+ return {
5163
+ changed: false,
5164
+ };
4922
5165
  };
4923
- function handleBookmarkUpdated(event) {
4924
- const currentActivities = this.currentState.activities;
4925
- const { connected_user: connectedUser } = this.client.state.getLatestValue();
4926
- const isCurrentUser = event.bookmark.user.id === connectedUser?.id;
4927
- const result = updateBookmarkInActivities(event, currentActivities, isCurrentUser);
5166
+ function handleNotificationFeedUpdated(event) {
5167
+ const result = updateNotificationFeedFromEvent(event);
4928
5168
  if (result.changed) {
4929
- this.state.partialNext({ activities: result.activities });
5169
+ this.state.partialNext({
5170
+ notification_status: result.data?.notification_status,
5171
+ aggregated_activities: result.data?.aggregated_activities,
5172
+ });
4930
5173
  }
4931
5174
  }
4932
5175
 
4933
- function handleFeedUpdated(event) {
4934
- this.state.partialNext({ ...event.feed });
4935
- }
4936
-
4937
- function handleNotificationFeedUpdated(event) {
4938
- console.info('notification feed updated', event);
4939
- // TODO: handle notification feed updates
4940
- }
4941
-
4942
5176
  function handleWatchStarted() {
4943
5177
  this.state.partialNext({ watch: true });
4944
5178
  }
@@ -4991,7 +5225,7 @@ class Feed extends FeedApi {
4991
5225
  'feeds.poll.vote_removed': Feed.noop,
4992
5226
  'feeds.activity.pinned': Feed.noop,
4993
5227
  'feeds.activity.unpinned': Feed.noop,
4994
- 'feeds.activity.marked': Feed.noop,
5228
+ 'feeds.activity.marked': handleActivityMarked.bind(this),
4995
5229
  'moderation.custom_action': Feed.noop,
4996
5230
  'moderation.flagged': Feed.noop,
4997
5231
  'moderation.mark_reviewed': Feed.noop,
@@ -6238,4 +6472,5 @@ exports.isVideoFile = isVideoFile;
6238
6472
  exports.isVoteAnswer = isVoteAnswer;
6239
6473
  exports.shouldUpdateState = shouldUpdateState;
6240
6474
  exports.uniqueArrayMerge = uniqueArrayMerge;
6475
+ exports.updateEntityInArray = updateEntityInArray;
6241
6476
  //# sourceMappingURL=index.browser.cjs.map