@stream-io/feeds-client 0.2.1 → 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 (54) hide show
  1. package/@react-bindings/hooks/feed-state-hooks/index.ts +4 -0
  2. package/CHANGELOG.md +8 -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 +154 -29
  10. package/dist/index-react-bindings.browser.cjs.map +1 -1
  11. package/dist/index-react-bindings.browser.js +151 -30
  12. package/dist/index-react-bindings.browser.js.map +1 -1
  13. package/dist/index-react-bindings.node.cjs +154 -29
  14. package/dist/index-react-bindings.node.cjs.map +1 -1
  15. package/dist/index-react-bindings.node.js +151 -30
  16. package/dist/index-react-bindings.node.js.map +1 -1
  17. package/dist/index.browser.cjs +88 -12
  18. package/dist/index.browser.cjs.map +1 -1
  19. package/dist/index.browser.js +88 -12
  20. package/dist/index.browser.js.map +1 -1
  21. package/dist/index.node.cjs +88 -12
  22. package/dist/index.node.cjs.map +1 -1
  23. package/dist/index.node.js +88 -12
  24. package/dist/index.node.js.map +1 -1
  25. package/dist/src/feed/event-handlers/activity/handle-activity-marked.d.ts +11 -0
  26. package/dist/src/feed/event-handlers/activity/index.d.ts +1 -0
  27. package/dist/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts +8 -1
  28. package/dist/src/feed/feed.d.ts +2 -2
  29. package/dist/src/test-utils/response-generators.d.ts +21 -1
  30. package/dist/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +1 -1
  32. package/src/feed/event-handlers/activity/activity-marked-utils.test.ts +208 -0
  33. package/src/feed/event-handlers/activity/handle-activity-marked.ts +68 -0
  34. package/src/feed/event-handlers/activity/handle-activity-reaction-added.test.ts +15 -15
  35. package/src/feed/event-handlers/activity/handle-activity-reaction-deleted.test.ts +14 -14
  36. package/src/feed/event-handlers/activity/handle-activity-unpinned.test.ts +4 -3
  37. package/src/feed/event-handlers/activity/handle-activity-updated.test.ts +4 -4
  38. package/src/feed/event-handlers/activity/index.ts +2 -1
  39. package/src/feed/event-handlers/bookmark/handle-bookmark-added.test.ts +14 -14
  40. package/src/feed/event-handlers/bookmark/handle-bookmark-deleted.test.ts +14 -14
  41. package/src/feed/event-handlers/bookmark/handle-bookmark-updated.test.ts +16 -16
  42. package/src/feed/event-handlers/comment/handle-comment-added.test.ts +147 -0
  43. package/src/feed/event-handlers/comment/handle-comment-deleted.test.ts +133 -0
  44. package/src/feed/event-handlers/comment/handle-comment-deleted.ts +24 -10
  45. package/src/feed/event-handlers/comment/handle-comment-reaction.test.ts +315 -0
  46. package/src/feed/event-handlers/comment/handle-comment-updated.test.ts +131 -0
  47. package/src/feed/event-handlers/follow/handle-follow-created.test.ts +7 -7
  48. package/src/feed/event-handlers/follow/handle-follow-deleted.test.ts +2 -2
  49. package/src/feed/event-handlers/follow/handle-follow-updated.test.ts +1 -1
  50. package/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.test.ts +120 -0
  51. package/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.ts +47 -3
  52. package/src/feed/feed.ts +4 -2
  53. package/src/gen/model-decoders/decoders.ts +1 -1
  54. package/src/test-utils/response-generators.ts +123 -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) => {
@@ -4503,20 +4503,29 @@ function handleCommentAdded(event) {
4503
4503
  function handleCommentDeleted({ comment }) {
4504
4504
  const entityId = comment.parent_id ?? comment.object_id;
4505
4505
  this.state.next((currentState) => {
4506
- const newCommentsByEntityId = {
4507
- ...currentState.comments_by_entity_id,
4508
- [entityId]: {
4509
- ...currentState.comments_by_entity_id[entityId],
4510
- },
4511
- };
4506
+ let newCommentsByEntityId;
4512
4507
  const index = this.getCommentIndex(comment, currentState);
4513
- 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
+ });
4514
4515
  newCommentsByEntityId[entityId].comments = [
4515
4516
  ...newCommentsByEntityId[entityId].comments,
4516
4517
  ];
4517
4518
  newCommentsByEntityId[entityId]?.comments?.splice(index, 1);
4518
4519
  }
4519
- 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
+ }
4520
4529
  return {
4521
4530
  ...currentState,
4522
4531
  comments_by_entity_id: newCommentsByEntityId,
@@ -5071,13 +5080,80 @@ function handleActivityReactionDeleted(event) {
5071
5080
  }
5072
5081
  }
5073
5082
 
5083
+ const updateNotificationStatusFromActivityMarked = (event, currentNotificationStatus, aggregatedActivities = []) => {
5084
+ if (!currentNotificationStatus) {
5085
+ return {
5086
+ changed: false,
5087
+ };
5088
+ }
5089
+ const newState = {
5090
+ ...currentNotificationStatus,
5091
+ };
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
+ ];
5100
+ }
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
+ ];
5108
+ }
5109
+ if (event.mark_all_seen) {
5110
+ newState.last_seen_at = new Date();
5111
+ }
5112
+ return {
5113
+ changed: true,
5114
+ data: { notification_status: newState },
5115
+ };
5116
+ };
5117
+ function handleActivityMarked(event) {
5118
+ const result = updateNotificationStatusFromActivityMarked(event, this.currentState.notification_status, this.currentState.aggregated_activities);
5119
+ if (result.changed) {
5120
+ this.state.partialNext({
5121
+ notification_status: result.data?.notification_status,
5122
+ });
5123
+ }
5124
+ }
5125
+
5074
5126
  function handleFeedUpdated(event) {
5075
5127
  this.state.partialNext({ ...event.feed });
5076
5128
  }
5077
5129
 
5130
+ const updateNotificationFeedFromEvent = (event) => {
5131
+ const updates = {};
5132
+ if (event.notification_status) {
5133
+ updates.notification_status = event.notification_status;
5134
+ }
5135
+ if (event.aggregated_activities) {
5136
+ updates.aggregated_activities = event.aggregated_activities;
5137
+ }
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
+ };
5144
+ }
5145
+ return {
5146
+ changed: false,
5147
+ };
5148
+ };
5078
5149
  function handleNotificationFeedUpdated(event) {
5079
- console.info('notification feed updated', event);
5080
- // TODO: handle notification feed updates
5150
+ const result = updateNotificationFeedFromEvent(event);
5151
+ if (result.changed) {
5152
+ this.state.partialNext({
5153
+ notification_status: result.data?.notification_status,
5154
+ aggregated_activities: result.data?.aggregated_activities,
5155
+ });
5156
+ }
5081
5157
  }
5082
5158
 
5083
5159
  function handleWatchStarted() {
@@ -5132,7 +5208,7 @@ class Feed extends FeedApi {
5132
5208
  'feeds.poll.vote_removed': Feed.noop,
5133
5209
  'feeds.activity.pinned': Feed.noop,
5134
5210
  'feeds.activity.unpinned': Feed.noop,
5135
- 'feeds.activity.marked': Feed.noop,
5211
+ 'feeds.activity.marked': handleActivityMarked.bind(this),
5136
5212
  'moderation.custom_action': Feed.noop,
5137
5213
  'moderation.flagged': Feed.noop,
5138
5214
  'moderation.mark_reviewed': Feed.noop,
@@ -6004,10 +6080,10 @@ const useFeedsClient = () => {
6004
6080
  */
6005
6081
  const useClientConnectedUser = () => {
6006
6082
  const client = useFeedsClient();
6007
- const { user } = useStateStore(client?.state, selector$a) ?? {};
6083
+ const { user } = useStateStore(client?.state, selector$c) ?? {};
6008
6084
  return user;
6009
6085
  };
6010
- const selector$a = (nextState) => ({
6086
+ const selector$c = (nextState) => ({
6011
6087
  user: nextState.connected_user,
6012
6088
  });
6013
6089
 
@@ -6016,10 +6092,10 @@ const selector$a = (nextState) => ({
6016
6092
  */
6017
6093
  const useWsConnectionState = () => {
6018
6094
  const client = useFeedsClient();
6019
- const { is_healthy } = useStateStore(client?.state, selector$9) ?? {};
6095
+ const { is_healthy } = useStateStore(client?.state, selector$b) ?? {};
6020
6096
  return { is_healthy };
6021
6097
  };
6022
- const selector$9 = (nextState) => ({
6098
+ const selector$b = (nextState) => ({
6023
6099
  is_healthy: nextState.is_ws_connection_healthy,
6024
6100
  });
6025
6101
 
@@ -6069,7 +6145,7 @@ const useStableCallback = (callback) => {
6069
6145
  const useFeedActivities = (feedFromProps) => {
6070
6146
  const feedFromContext = useFeedContext();
6071
6147
  const feed = feedFromProps ?? feedFromContext;
6072
- const data = useStateStore(feed?.state, selector$8);
6148
+ const data = useStateStore(feed?.state, selector$a);
6073
6149
  const loadNextPage = useStableCallback(async () => {
6074
6150
  if (!feed || !data?.has_next_page || data?.is_loading) {
6075
6151
  return;
@@ -6078,7 +6154,7 @@ const useFeedActivities = (feedFromProps) => {
6078
6154
  });
6079
6155
  return useMemo(() => ({ ...data, loadNextPage }), [data, loadNextPage]);
6080
6156
  };
6081
- const selector$8 = ({ is_loading_activities, next, activities = [] }) => ({
6157
+ const selector$a = ({ is_loading_activities, next, activities = [] }) => ({
6082
6158
  is_loading: is_loading_activities,
6083
6159
  has_next_page: typeof next !== 'undefined',
6084
6160
  activities,
@@ -6151,13 +6227,13 @@ const FeedOwnCapability = {
6151
6227
  };
6152
6228
 
6153
6229
  const stableEmptyArray = [];
6154
- const selector$7 = (currentState) => ({
6230
+ const selector$9 = (currentState) => ({
6155
6231
  oc: currentState.own_capabilities ?? stableEmptyArray,
6156
6232
  });
6157
6233
  const useOwnCapabilities = (feedFromProps) => {
6158
6234
  const feedFromContext = useFeedContext();
6159
6235
  const feed = feedFromProps ?? feedFromContext;
6160
- const { oc = stableEmptyArray } = useStateStore(feed?.state, selector$7) ?? {};
6236
+ const { oc = stableEmptyArray } = useStateStore(feed?.state, selector$9) ?? {};
6161
6237
  return useMemo(() => ({
6162
6238
  can_add_activity: oc.indexOf(FeedOwnCapability.ADD_ACTIVITY) > -1,
6163
6239
  can_add_activity_reaction: oc.indexOf(FeedOwnCapability.ADD_ACTIVITY_REACTION) > -1,
@@ -6192,7 +6268,7 @@ const useOwnCapabilities = (feedFromProps) => {
6192
6268
  }), [oc]);
6193
6269
  };
6194
6270
 
6195
- const selector$6 = ({ follower_count, followers, followers_pagination, }) => ({
6271
+ const selector$8 = ({ follower_count, followers, followers_pagination, }) => ({
6196
6272
  follower_count,
6197
6273
  followers,
6198
6274
  followers_pagination,
@@ -6200,7 +6276,7 @@ const selector$6 = ({ follower_count, followers, followers_pagination, }) => ({
6200
6276
  function useFollowers(feedFromProps) {
6201
6277
  const feedFromContext = useFeedContext();
6202
6278
  const feed = feedFromProps ?? feedFromContext;
6203
- const data = useStateStore(feed?.state, selector$6);
6279
+ const data = useStateStore(feed?.state, selector$8);
6204
6280
  const loadNextPage = useCallback((...options) => feed?.loadNextPageFollowers(...options), [feed]);
6205
6281
  return useMemo(() => {
6206
6282
  if (!data) {
@@ -6215,7 +6291,7 @@ function useFollowers(feedFromProps) {
6215
6291
  }, [data, loadNextPage]);
6216
6292
  }
6217
6293
 
6218
- const selector$5 = ({ following_count, following, following_pagination, }) => ({
6294
+ const selector$7 = ({ following_count, following, following_pagination, }) => ({
6219
6295
  following_count,
6220
6296
  following,
6221
6297
  following_pagination,
@@ -6223,7 +6299,7 @@ const selector$5 = ({ following_count, following, following_pagination, }) => ({
6223
6299
  function useFollowing(feedFromProps) {
6224
6300
  const feedFromContext = useFeedContext();
6225
6301
  const feed = feedFromProps ?? feedFromContext;
6226
- const data = useStateStore(feed?.state, selector$5);
6302
+ const data = useStateStore(feed?.state, selector$7);
6227
6303
  const loadNextPage = useCallback((...options) => feed?.loadNextPageFollowing(...options), [feed]);
6228
6304
  return useMemo(() => {
6229
6305
  if (!data) {
@@ -6245,9 +6321,9 @@ function useFollowing(feedFromProps) {
6245
6321
  const useFeedMetadata = (feedFromProps) => {
6246
6322
  const feedFromContext = useFeedContext();
6247
6323
  const feed = feedFromProps ?? feedFromContext;
6248
- return useStateStore(feed?.state, selector$4);
6324
+ return useStateStore(feed?.state, selector$6);
6249
6325
  };
6250
- 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, }) => ({
6251
6327
  created_by,
6252
6328
  follower_count,
6253
6329
  following_count,
@@ -6262,12 +6338,57 @@ const selector$4 = ({ follower_count = 0, following_count = 0, created_by, creat
6262
6338
  const useOwnFollows = (feedFromProps) => {
6263
6339
  const feedFromContext = useFeedContext();
6264
6340
  const feed = feedFromProps ?? feedFromContext;
6265
- return useStateStore(feed?.state, selector$3);
6341
+ return useStateStore(feed?.state, selector$5);
6266
6342
  };
6267
- const selector$3 = ({ own_follows }) => ({
6343
+ const selector$5 = ({ own_follows }) => ({
6268
6344
  own_follows,
6269
6345
  });
6270
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
+
6271
6392
  const StreamSearchResultsContext = createContext(undefined);
6272
6393
  /**
6273
6394
  * Hook to access the nearest SearchSource instance.
@@ -6384,7 +6505,7 @@ const StreamFeeds = ({ client, children }) => {
6384
6505
  };
6385
6506
  StreamFeeds.displayName = 'StreamFeeds';
6386
6507
 
6387
- const StreamFeed = ({ feed, children }) => {
6508
+ const StreamFeed = ({ feed, children, }) => {
6388
6509
  return (jsx(StreamFeedContext.Provider, { value: feed, children: children }));
6389
6510
  };
6390
6511
  StreamFeed.displayName = 'StreamFeed';
@@ -6399,5 +6520,5 @@ const StreamSearchResults = ({ source, children, }) => {
6399
6520
  };
6400
6521
  StreamSearchResults.displayName = 'StreamSearchResults';
6401
6522
 
6402
- 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 };
6403
6524
  //# sourceMappingURL=index-react-bindings.browser.js.map