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