@stream-io/feeds-client 1.0.0 → 1.1.0

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 (96) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/cjs/index.js +1 -1
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/react-bindings.js +5 -19
  5. package/dist/cjs/react-bindings.js.map +1 -1
  6. package/dist/es/index.mjs +2 -2
  7. package/dist/es/index.mjs.map +1 -1
  8. package/dist/es/react-bindings.mjs +5 -19
  9. package/dist/es/react-bindings.mjs.map +1 -1
  10. package/dist/{feeds-client-tw63OGrd.js → feeds-client-C1c6lcS3.js} +529 -155
  11. package/dist/feeds-client-C1c6lcS3.js.map +1 -0
  12. package/dist/{feeds-client-B03y08Kq.mjs → feeds-client-jtUTE4AC.mjs} +529 -155
  13. package/dist/feeds-client-jtUTE4AC.mjs.map +1 -0
  14. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  15. package/dist/types/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivityRead.d.ts +6 -2
  16. package/dist/types/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivityRead.d.ts.map +1 -1
  17. package/dist/types/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivitySeen.d.ts +6 -2
  18. package/dist/types/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivitySeen.d.ts.map +1 -1
  19. package/dist/types/feed/event-handlers/activity/handle-activity-deleted.d.ts +3 -2
  20. package/dist/types/feed/event-handlers/activity/handle-activity-deleted.d.ts.map +1 -1
  21. package/dist/types/feed/event-handlers/activity/handle-activity-feedback.d.ts +5 -0
  22. package/dist/types/feed/event-handlers/activity/handle-activity-feedback.d.ts.map +1 -1
  23. package/dist/types/feed/event-handlers/activity/handle-activity-pinned.d.ts +3 -2
  24. package/dist/types/feed/event-handlers/activity/handle-activity-pinned.d.ts.map +1 -1
  25. package/dist/types/feed/event-handlers/activity/handle-activity-unpinned.d.ts +3 -2
  26. package/dist/types/feed/event-handlers/activity/handle-activity-unpinned.d.ts.map +1 -1
  27. package/dist/types/feed/event-handlers/activity/index.d.ts +3 -1
  28. package/dist/types/feed/event-handlers/activity/index.d.ts.map +1 -1
  29. package/dist/types/feed/event-handlers/activity-updater.d.ts +1 -0
  30. package/dist/types/feed/event-handlers/activity-updater.d.ts.map +1 -1
  31. package/dist/types/feed/event-handlers/add-aggregated-activities-to-state.d.ts +1 -1
  32. package/dist/types/feed/event-handlers/add-aggregated-activities-to-state.d.ts.map +1 -1
  33. package/dist/types/feed/event-handlers/bookmark/handle-bookmark-added.d.ts +6 -5
  34. package/dist/types/feed/event-handlers/bookmark/handle-bookmark-added.d.ts.map +1 -1
  35. package/dist/types/feed/event-handlers/bookmark/handle-bookmark-deleted.d.ts +6 -5
  36. package/dist/types/feed/event-handlers/bookmark/handle-bookmark-deleted.d.ts.map +1 -1
  37. package/dist/types/feed/event-handlers/bookmark/handle-bookmark-updated.d.ts +6 -5
  38. package/dist/types/feed/event-handlers/bookmark/handle-bookmark-updated.d.ts.map +1 -1
  39. package/dist/types/feed/event-handlers/feed/handle-feed-deleted.d.ts +4 -0
  40. package/dist/types/feed/event-handlers/feed/handle-feed-deleted.d.ts.map +1 -0
  41. package/dist/types/feed/event-handlers/feed/handle-feed-updated.d.ts.map +1 -1
  42. package/dist/types/feed/event-handlers/feed/index.d.ts +1 -0
  43. package/dist/types/feed/event-handlers/feed/index.d.ts.map +1 -1
  44. package/dist/types/feed/event-handlers/feed-member/handle-feed-member-added.d.ts +3 -2
  45. package/dist/types/feed/event-handlers/feed-member/handle-feed-member-added.d.ts.map +1 -1
  46. package/dist/types/feed/event-handlers/feed-member/handle-feed-member-removed.d.ts +3 -2
  47. package/dist/types/feed/event-handlers/feed-member/handle-feed-member-removed.d.ts.map +1 -1
  48. package/dist/types/feed/event-handlers/feed-member/handle-feed-member-updated.d.ts +3 -2
  49. package/dist/types/feed/event-handlers/feed-member/handle-feed-member-updated.d.ts.map +1 -1
  50. package/dist/types/feed/event-handlers/follow/handle-follow-updated.d.ts.map +1 -1
  51. package/dist/types/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts +3 -2
  52. package/dist/types/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts.map +1 -1
  53. package/dist/types/feed/feed.d.ts.map +1 -1
  54. package/dist/types/feeds-client/feeds-client.d.ts +24 -1
  55. package/dist/types/feeds-client/feeds-client.d.ts.map +1 -1
  56. package/dist/types/gen/feeds/FeedsApi.d.ts +6 -4
  57. package/dist/types/gen/feeds/FeedsApi.d.ts.map +1 -1
  58. package/dist/types/gen/models/index.d.ts +40 -2
  59. package/dist/types/gen/models/index.d.ts.map +1 -1
  60. package/dist/types/utils/state-update-queue.d.ts +11 -2
  61. package/dist/types/utils/state-update-queue.d.ts.map +1 -1
  62. package/dist/types/utils/unique-array-merge.d.ts +1 -1
  63. package/dist/types/utils/unique-array-merge.d.ts.map +1 -1
  64. package/package.json +1 -1
  65. package/src/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivityRead.ts +6 -20
  66. package/src/bindings/react/hooks/feed-state-hooks/useIsAggregatedActivitySeen.ts +6 -20
  67. package/src/feed/event-handlers/activity/handle-activity-deleted.ts +28 -2
  68. package/src/feed/event-handlers/activity/handle-activity-feedback.ts +17 -7
  69. package/src/feed/event-handlers/activity/handle-activity-pinned.ts +25 -3
  70. package/src/feed/event-handlers/activity/handle-activity-unpinned.ts +25 -2
  71. package/src/feed/event-handlers/activity/index.ts +3 -1
  72. package/src/feed/event-handlers/add-aggregated-activities-to-state.ts +11 -2
  73. package/src/feed/event-handlers/bookmark/handle-bookmark-added.ts +20 -11
  74. package/src/feed/event-handlers/bookmark/handle-bookmark-deleted.ts +21 -11
  75. package/src/feed/event-handlers/bookmark/handle-bookmark-updated.ts +24 -10
  76. package/src/feed/event-handlers/feed/handle-feed-deleted.ts +12 -0
  77. package/src/feed/event-handlers/feed/handle-feed-updated.ts +8 -0
  78. package/src/feed/event-handlers/feed/index.ts +1 -0
  79. package/src/feed/event-handlers/feed-member/handle-feed-member-added.ts +25 -2
  80. package/src/feed/event-handlers/feed-member/handle-feed-member-removed.ts +25 -2
  81. package/src/feed/event-handlers/feed-member/handle-feed-member-updated.ts +25 -2
  82. package/src/feed/event-handlers/follow/handle-follow-updated.ts +14 -0
  83. package/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.ts +68 -2
  84. package/src/feed/event-handlers/story-feeds/handle-story-feeds-updated.ts +1 -1
  85. package/src/feed/feed.ts +7 -5
  86. package/src/feeds-client/feeds-client.ts +255 -0
  87. package/src/gen/feeds/FeedsApi.ts +79 -12
  88. package/src/gen/model-decoders/decoders.ts +7 -0
  89. package/src/gen/models/index.ts +66 -4
  90. package/src/utils/state-update-queue.ts +42 -28
  91. package/src/utils/unique-array-merge.ts +11 -3
  92. package/dist/feeds-client-B03y08Kq.mjs.map +0 -1
  93. package/dist/feeds-client-tw63OGrd.js.map +0 -1
  94. package/dist/types/feed/event-handlers/activity/handle-activity-marked.d.ts +0 -12
  95. package/dist/types/feed/event-handlers/activity/handle-activity-marked.d.ts.map +0 -1
  96. package/src/feed/event-handlers/activity/handle-activity-marked.ts +0 -68
@@ -1,5 +1,6 @@
1
1
  import type { Feed } from '../..';
2
2
  import type {
3
+ ActivityResponse,
3
4
  AggregatedActivityResponse,
4
5
  NotificationFeedUpdatedEvent,
5
6
  NotificationStatusResponse,
@@ -35,15 +36,18 @@ export const updateNotificationFeedFromEvent = (
35
36
  event: NotificationFeedUpdatedEvent,
36
37
  currentAggregatedActivities?: AggregatedActivityResponse[],
37
38
  currentNotificationStatus?: NotificationStatusResponse,
39
+ currentActivities?: ActivityResponse[],
38
40
  ): UpdateStateResult<{
39
41
  data?: {
40
42
  notification_status?: NotificationStatusResponse;
41
43
  aggregated_activities?: AggregatedActivityResponse[];
44
+ activities?: ActivityResponse[];
42
45
  };
43
46
  }> => {
44
47
  const updates: {
45
48
  notification_status?: NotificationStatusResponse;
46
49
  aggregated_activities?: AggregatedActivityResponse[];
50
+ activities?: ActivityResponse[];
47
51
  } = {};
48
52
 
49
53
  if (event.notification_status) {
@@ -58,11 +62,71 @@ export const updateNotificationFeedFromEvent = (
58
62
  }
59
63
  }
60
64
 
65
+ // Determine effective notification status (prefer new from event, fall back to current)
66
+ const effectiveStatus =
67
+ event.notification_status ?? currentNotificationStatus;
68
+ const lastReadAt = effectiveStatus?.last_read_at;
69
+ const lastSeenAt = effectiveStatus?.last_seen_at;
70
+ const readActivities = effectiveStatus?.read_activities ?? [];
71
+ const seenActivities = effectiveStatus?.seen_activities ?? [];
72
+
73
+ // For flat feeds — update currentActivities with is_read/is_seen
74
+ if (
75
+ currentActivities?.length &&
76
+ !currentAggregatedActivities?.length &&
77
+ effectiveStatus
78
+ ) {
79
+ let anyChanged = false;
80
+ const updatedActivities = currentActivities.map((activity) => {
81
+ const isRead =
82
+ (lastReadAt != null &&
83
+ activity.updated_at.getTime() < lastReadAt.getTime()) ||
84
+ readActivities.includes(activity.id);
85
+ const isSeen =
86
+ (lastSeenAt != null &&
87
+ activity.updated_at.getTime() < lastSeenAt.getTime()) ||
88
+ seenActivities.includes(activity.id);
89
+ if (activity.is_read !== isRead || activity.is_seen !== isSeen) {
90
+ anyChanged = true;
91
+ return { ...activity, is_read: isRead, is_seen: isSeen };
92
+ }
93
+ return activity;
94
+ });
95
+ if (anyChanged) {
96
+ updates.activities = updatedActivities;
97
+ }
98
+ }
99
+
100
+ // For aggregated feeds — update aggregated_activities with is_read/is_seen
101
+ if (currentAggregatedActivities?.length && effectiveStatus) {
102
+ const baseAggregated = currentAggregatedActivities;
103
+ let anyChanged = false;
104
+ const updatedAggregated = baseAggregated.map((group) => {
105
+ const isRead =
106
+ (lastReadAt != null &&
107
+ group.updated_at.getTime() < lastReadAt.getTime()) ||
108
+ readActivities.includes(group.group);
109
+ const isSeen =
110
+ (lastSeenAt != null &&
111
+ group.updated_at.getTime() < lastSeenAt.getTime()) ||
112
+ seenActivities.includes(group.group);
113
+ if (group.is_read !== isRead || group.is_seen !== isSeen) {
114
+ anyChanged = true;
115
+ return { ...group, is_read: isRead, is_seen: isSeen };
116
+ }
117
+ return group;
118
+ });
119
+ if (anyChanged) {
120
+ updates.aggregated_activities = updatedAggregated;
121
+ }
122
+ }
123
+
124
+ // Leave this to the end, because notification_status may not be 100% accurate (only includes last 100 activities)
61
125
  if (event.aggregated_activities && currentAggregatedActivities) {
62
126
  const aggregatedActivitiesResult = addAggregatedActivitiesToState(
63
127
  event.aggregated_activities,
64
- currentAggregatedActivities,
65
- 'start',
128
+ updates.aggregated_activities ?? currentAggregatedActivities,
129
+ 'replace-then-start',
66
130
  );
67
131
 
68
132
  if (aggregatedActivitiesResult.changed) {
@@ -91,11 +155,13 @@ export function handleNotificationFeedUpdated(
91
155
  event,
92
156
  this.currentState.aggregated_activities,
93
157
  this.currentState.notification_status,
158
+ this.currentState.activities,
94
159
  );
95
160
  if (result.changed) {
96
161
  this.state.partialNext({
97
162
  notification_status: result.data?.notification_status,
98
163
  aggregated_activities: result.data?.aggregated_activities,
164
+ activities: result.data?.activities ?? this.currentState.activities,
99
165
  });
100
166
  }
101
167
  }
@@ -79,7 +79,7 @@ export function updateStoriesFeedFromEvent(
79
79
  const aggregatedActivitiesResult = addAggregatedActivitiesToState(
80
80
  event.aggregated_activities,
81
81
  aggregatedActivities,
82
- 'replace',
82
+ 'replace-then-end',
83
83
  );
84
84
 
85
85
  if (aggregatedActivitiesResult.changed) {
package/src/feed/feed.ts CHANGED
@@ -38,9 +38,9 @@ import {
38
38
  handleBookmarkAdded,
39
39
  handleActivityDeleted,
40
40
  handleActivityRemovedFromFeed,
41
+ handleFeedDeleted,
41
42
  handleFeedUpdated,
42
43
  handleNotificationFeedUpdated,
43
- handleActivityMarked,
44
44
  handleActivityReactionAdded,
45
45
  handleActivityReactionDeleted,
46
46
  handleActivityReactionUpdated,
@@ -50,6 +50,8 @@ import {
50
50
  addAggregatedActivitiesToState,
51
51
  updateNotificationStatus,
52
52
  handleStoriesFeedUpdated,
53
+ handleActivityPinned,
54
+ handleActivityUnpinned,
53
55
  } from './event-handlers';
54
56
  import { capitalize } from '../common/utils';
55
57
  import type {
@@ -181,7 +183,7 @@ export class Feed extends FeedApi {
181
183
  'feeds.comment.deleted': handleCommentDeleted.bind(this),
182
184
  'feeds.comment.updated': handleCommentUpdated.bind(this),
183
185
  'feeds.feed.created': Feed.noop,
184
- 'feeds.feed.deleted': Feed.noop,
186
+ 'feeds.feed.deleted': handleFeedDeleted.bind(this),
185
187
  'feeds.feed.updated': handleFeedUpdated.bind(this),
186
188
  'feeds.feed_group.changed': Feed.noop,
187
189
  'feeds.feed_group.deleted': Feed.noop,
@@ -204,9 +206,9 @@ export class Feed extends FeedApi {
204
206
  'feeds.poll.vote_casted': Feed.noop,
205
207
  'feeds.poll.vote_changed': Feed.noop,
206
208
  'feeds.poll.vote_removed': Feed.noop,
207
- 'feeds.activity.pinned': Feed.noop,
208
- 'feeds.activity.unpinned': Feed.noop,
209
- 'feeds.activity.marked': handleActivityMarked.bind(this),
209
+ 'feeds.activity.pinned': handleActivityPinned.bind(this),
210
+ 'feeds.activity.unpinned': handleActivityUnpinned.bind(this),
211
+ 'feeds.activity.marked': Feed.noop,
210
212
  'moderation.custom_action': Feed.noop,
211
213
  'moderation.flagged': Feed.noop,
212
214
  'moderation.mark_reviewed': Feed.noop,
@@ -1,8 +1,13 @@
1
1
  import { FeedsApi } from '../gen/feeds/FeedsApi';
2
2
  import type {
3
+ AcceptFeedMemberInviteResponse,
4
+ ActivityFeedbackRequest,
5
+ ActivityFeedbackResponse,
3
6
  ActivityResponse,
4
7
  AddActivityRequest,
5
8
  AddActivityResponse,
9
+ AddBookmarkRequest,
10
+ AddBookmarkResponse,
6
11
  AddCommentReactionRequest,
7
12
  AddCommentReactionResponse,
8
13
  AddCommentRequest,
@@ -11,8 +16,11 @@ import type {
11
16
  CastPollVoteRequest,
12
17
  CreateGuestResponse,
13
18
  DeleteActivityReactionResponse,
19
+ DeleteActivityResponse,
20
+ DeleteBookmarkResponse,
14
21
  DeleteCommentReactionResponse,
15
22
  DeleteCommentResponse,
23
+ DeleteFeedResponse,
16
24
  FeedResponse,
17
25
  FileUploadRequest,
18
26
  FollowBatchRequest,
@@ -23,16 +31,24 @@ import type {
23
31
  ImageSize,
24
32
  ImageUploadRequest,
25
33
  OwnBatchRequest,
34
+ PinActivityResponse,
26
35
  PollResponse,
27
36
  PollVoteResponse,
28
37
  PollVotesResponse,
29
38
  QueryFeedsRequest,
30
39
  QueryPollVotesRequest,
40
+ RejectFeedMemberInviteResponse,
31
41
  UnfollowBatchRequest,
42
+ UnpinActivityResponse,
43
+ UpdateActivityPartialResponse,
32
44
  UpdateActivityRequest,
33
45
  UpdateActivityResponse,
46
+ UpdateBookmarkRequest,
47
+ UpdateBookmarkResponse,
34
48
  UpdateCommentRequest,
35
49
  UpdateCommentResponse,
50
+ UpdateFeedMembersResponse,
51
+ UpdateFeedResponse,
36
52
  UpdateFollowRequest,
37
53
  UpdatePollPartialRequest,
38
54
  UpdatePollRequest,
@@ -68,21 +84,32 @@ import { StreamPoll } from '../common/Poll';
68
84
  import {
69
85
  Feed,
70
86
  type FeedState,
87
+ handleActivityDeleted,
71
88
  handleActivityReactionAdded,
72
89
  handleActivityReactionDeleted,
73
90
  handleActivityReactionUpdated,
74
91
  handleActivityUpdated,
92
+ handleActivityPinned,
93
+ handleActivityUnpinned,
94
+ handleBookmarkAdded,
95
+ handleBookmarkDeleted,
96
+ handleBookmarkUpdated,
75
97
  handleCommentAdded,
76
98
  handleCommentDeleted,
77
99
  handleCommentReactionAdded,
78
100
  handleCommentReactionDeleted,
79
101
  handleCommentUpdated,
102
+ handleFeedDeleted,
103
+ handleFeedMemberAdded,
104
+ handleFeedMemberRemoved,
105
+ handleFeedMemberUpdated,
80
106
  handleFeedUpdated,
81
107
  handleFollowCreated,
82
108
  handleFollowDeleted,
83
109
  handleFollowUpdated,
84
110
  handleWatchStarted,
85
111
  handleWatchStopped,
112
+ updateActivityFromFeedback,
86
113
  } from '../feed';
87
114
  import { applyNewActivityToActiveFeeds } from './apply-new-activity-to-active-feeds';
88
115
  import { handleUserUpdated } from './event-handlers';
@@ -564,6 +591,66 @@ export class FeedsClient extends FeedsApi {
564
591
  return response;
565
592
  };
566
593
 
594
+ updateActivityPartial = async (
595
+ ...args: Parameters<FeedsApi['updateActivityPartial']>
596
+ ): Promise<StreamResponse<UpdateActivityPartialResponse>> => {
597
+ const response = await super.updateActivityPartial(...args);
598
+ for (const feed of this.allActiveFeeds) {
599
+ handleActivityUpdated.bind(feed)(response, false);
600
+ }
601
+ return response;
602
+ };
603
+
604
+ deleteActivity = async (
605
+ ...args: Parameters<FeedsApi['deleteActivity']>
606
+ ): Promise<StreamResponse<DeleteActivityResponse>> => {
607
+ const response = await super.deleteActivity(...args);
608
+ const activityId = args[0].id;
609
+ for (const feed of this.allActiveFeeds) {
610
+ handleActivityDeleted.bind(feed)(
611
+ { activity: { id: activityId } } as Parameters<
612
+ typeof handleActivityDeleted
613
+ >[0],
614
+ false,
615
+ );
616
+ }
617
+ this.activeActivities = this.activeActivities.filter(
618
+ (activity) => activity.id !== activityId,
619
+ );
620
+ return response;
621
+ };
622
+
623
+ activityFeedback = async (
624
+ request: ActivityFeedbackRequest & { activity_id: string },
625
+ ): Promise<StreamResponse<ActivityFeedbackResponse>> => {
626
+ const response = await super.activityFeedback(request);
627
+ if (request.hide !== undefined) {
628
+ const feedback = {
629
+ activity_id: request.activity_id,
630
+ action: 'hide' as const,
631
+ value: request.hide ? 'true' : 'false',
632
+ };
633
+ for (const feed of this.allActiveFeeds) {
634
+ const {
635
+ activities: currentActivities,
636
+ pinned_activities: currentPinnedActivities,
637
+ } = feed.currentState;
638
+ const [result1, result2] = [
639
+ updateActivityFromFeedback(feedback, currentActivities),
640
+ updateActivityFromFeedback(feedback, currentPinnedActivities),
641
+ ];
642
+
643
+ if (result1.changed || result2.changed) {
644
+ feed.state.partialNext({
645
+ activities: result1.entities,
646
+ pinned_activities: result2.entities,
647
+ });
648
+ }
649
+ }
650
+ }
651
+ return response;
652
+ };
653
+
567
654
  addComment = async (
568
655
  request: AddCommentRequest,
569
656
  ): Promise<StreamResponse<AddCommentResponse>> => {
@@ -693,6 +780,160 @@ export class FeedsClient extends FeedsApi {
693
780
  return response;
694
781
  };
695
782
 
783
+ addBookmark = async (
784
+ request: AddBookmarkRequest & { activity_id: string },
785
+ ): Promise<StreamResponse<AddBookmarkResponse>> => {
786
+ const response = await super.addBookmark(request);
787
+ for (const feed of this.allActiveFeeds) {
788
+ handleBookmarkAdded.bind(feed)(response);
789
+ }
790
+ return response;
791
+ };
792
+
793
+ updateBookmark = async (
794
+ request: UpdateBookmarkRequest & { activity_id: string },
795
+ ): Promise<StreamResponse<UpdateBookmarkResponse>> => {
796
+ const response = await super.updateBookmark(request);
797
+ for (const feed of this.allActiveFeeds) {
798
+ handleBookmarkUpdated.bind(feed)(response);
799
+ }
800
+ return response;
801
+ };
802
+
803
+ deleteBookmark = async (request: {
804
+ activity_id: string;
805
+ folder_id?: string;
806
+ }): Promise<StreamResponse<DeleteBookmarkResponse>> => {
807
+ const response = await super.deleteBookmark(request);
808
+ for (const feed of this.allActiveFeeds) {
809
+ handleBookmarkDeleted.bind(feed)(response);
810
+ }
811
+ return response;
812
+ };
813
+
814
+ pinActivity = async (
815
+ ...args: Parameters<FeedsApi['pinActivity']>
816
+ ): Promise<StreamResponse<PinActivityResponse>> => {
817
+ const response = await super.pinActivity(...args);
818
+ const feedIds =
819
+ response.activity?.feeds ?? (response.feed ? [response.feed] : []);
820
+ for (const fid of feedIds) {
821
+ const feed = this.activeFeeds[fid];
822
+ if (feed) {
823
+ handleActivityPinned.bind(feed)(
824
+ { pinned_activity: response } as Parameters<
825
+ typeof handleActivityPinned
826
+ >[0],
827
+ false,
828
+ );
829
+ }
830
+ }
831
+ return response;
832
+ };
833
+
834
+ unpinActivity = async (
835
+ ...args: Parameters<FeedsApi['unpinActivity']>
836
+ ): Promise<StreamResponse<UnpinActivityResponse>> => {
837
+ const response = await super.unpinActivity(...args);
838
+ const feedIds =
839
+ response.activity?.feeds ?? (response.feed ? [response.feed] : []);
840
+ for (const fid of feedIds) {
841
+ const feed = this.activeFeeds[fid];
842
+ if (feed) {
843
+ handleActivityUnpinned.bind(feed)(
844
+ {
845
+ pinned_activity: {
846
+ ...response,
847
+ created_at: new Date(),
848
+ },
849
+ } as Parameters<typeof handleActivityUnpinned>[0],
850
+ false,
851
+ );
852
+ }
853
+ }
854
+ return response;
855
+ };
856
+
857
+ updateFeed = async (
858
+ ...args: Parameters<FeedsApi['updateFeed']>
859
+ ): Promise<StreamResponse<UpdateFeedResponse>> => {
860
+ const response = await super.updateFeed(...args);
861
+ const fid = `${args[0].feed_group_id}:${args[0].feed_id}`;
862
+ const feed = this.activeFeeds[fid];
863
+ if (feed) {
864
+ handleFeedUpdated.call(feed, { feed: response.feed } as Parameters<
865
+ typeof handleFeedUpdated
866
+ >[0]);
867
+ }
868
+ return response;
869
+ };
870
+
871
+ deleteFeed = async (
872
+ ...args: Parameters<FeedsApi['deleteFeed']>
873
+ ): Promise<StreamResponse<DeleteFeedResponse>> => {
874
+ const response = await super.deleteFeed(...args);
875
+ const fid = `${args[0].feed_group_id}:${args[0].feed_id}`;
876
+ const feed = this.activeFeeds[fid];
877
+ if (feed) {
878
+ handleFeedDeleted.call(feed, {
879
+ created_at: new Date(),
880
+ } as Parameters<typeof handleFeedDeleted>[0]);
881
+ // If the feed is not watched, clean up immediately (no WS event will follow).
882
+ // If watched, the WS handler will clean up after dispatching the event.
883
+ if (!feed.currentState.watch) {
884
+ delete this.activeFeeds[fid];
885
+ this.activeActivities = this.activeActivities.filter(
886
+ (activity) => getFeed.call(activity)?.feed !== fid,
887
+ );
888
+ }
889
+ }
890
+ return response;
891
+ };
892
+
893
+ updateFeedMembers = async (
894
+ ...args: Parameters<FeedsApi['updateFeedMembers']>
895
+ ): Promise<StreamResponse<UpdateFeedMembersResponse>> => {
896
+ const response = await super.updateFeedMembers(...args);
897
+ const fid = `${args[0].feed_group_id}:${args[0].feed_id}`;
898
+ const feed = this.activeFeeds[fid];
899
+ if (feed) {
900
+ for (const member of response.added) {
901
+ handleFeedMemberAdded.call(feed, { member }, false);
902
+ }
903
+ for (const member of response.updated) {
904
+ handleFeedMemberUpdated.call(feed, { member }, false);
905
+ }
906
+ for (const memberId of response.removed_ids) {
907
+ handleFeedMemberRemoved.call(feed, { member_id: memberId }, false);
908
+ }
909
+ }
910
+ return response;
911
+ };
912
+
913
+ acceptFeedMemberInvite = async (
914
+ ...args: Parameters<FeedsApi['acceptFeedMemberInvite']>
915
+ ): Promise<StreamResponse<AcceptFeedMemberInviteResponse>> => {
916
+ const response = await super.acceptFeedMemberInvite(...args);
917
+ const fid = `${args[0].feed_group_id}:${args[0].feed_id}`;
918
+ const feed = this.activeFeeds[fid];
919
+ if (feed) {
920
+ handleFeedMemberUpdated.call(feed, { member: response.member }, false);
921
+ }
922
+ return response;
923
+ };
924
+
925
+ rejectFeedMemberInvite = async (
926
+ ...args: Parameters<FeedsApi['rejectFeedMemberInvite']>
927
+ ): Promise<StreamResponse<RejectFeedMemberInviteResponse>> => {
928
+ const response = await super.rejectFeedMemberInvite(...args);
929
+ const fid = `${args[0].feed_group_id}:${args[0].feed_id}`;
930
+ const feed = this.activeFeeds[fid];
931
+ if (feed) {
932
+ handleFeedMemberUpdated.call(feed, { member: response.member }, false);
933
+ }
934
+ return response;
935
+ };
936
+
696
937
  queryPollAnswers = async (
697
938
  request: QueryPollVotesRequest & { poll_id: string; user_id?: string },
698
939
  ): Promise<StreamResponse<PollVotesResponse>> => {
@@ -856,6 +1097,20 @@ export class FeedsClient extends FeedsApi {
856
1097
  return response;
857
1098
  }
858
1099
 
1100
+ async acceptFollow(...args: Parameters<FeedsApi['acceptFollow']>) {
1101
+ const response = await super.acceptFollow(...args);
1102
+
1103
+ [
1104
+ response.follow.source_feed.feed,
1105
+ response.follow.target_feed.feed,
1106
+ ].forEach((fid) => {
1107
+ const feeds = this.findAllActiveFeedsByFid(fid);
1108
+ feeds.forEach((f) => handleFollowUpdated.bind(f)(response, false));
1109
+ });
1110
+
1111
+ return response;
1112
+ }
1113
+
859
1114
  // For follow API endpoints we update the state after HTTP response to allow queryFeeds with watch: false
860
1115
  async follow(request: FollowRequest) {
861
1116
  const response = await super.follow(request);
@@ -84,6 +84,8 @@ import type {
84
84
  QueryBookmarkFoldersResponse,
85
85
  QueryBookmarksRequest,
86
86
  QueryBookmarksResponse,
87
+ QueryCollectionsRequest,
88
+ QueryCollectionsResponse,
87
89
  QueryCommentReactionsRequest,
88
90
  QueryCommentReactionsResponse,
89
91
  QueryCommentsRequest,
@@ -106,6 +108,7 @@ import type {
106
108
  RejectFeedMemberInviteResponse,
107
109
  RejectFollowRequest,
108
110
  RejectFollowResponse,
111
+ RemoveUserGroupMembersRequest,
109
112
  RemoveUserGroupMembersResponse,
110
113
  Response,
111
114
  RestoreActivityRequest,
@@ -114,6 +117,8 @@ import type {
114
117
  SharedLocationResponse,
115
118
  SharedLocationsResponse,
116
119
  SingleFollowResponse,
120
+ TrackActivityMetricsRequest,
121
+ TrackActivityMetricsResponse,
117
122
  UnblockUsersRequest,
118
123
  UnblockUsersResponse,
119
124
  UnfollowBatchRequest,
@@ -413,6 +418,29 @@ export class FeedsApi {
413
418
  return { ...response.body, metadata: response.metadata };
414
419
  }
415
420
 
421
+ async trackActivityMetrics(
422
+ request: TrackActivityMetricsRequest,
423
+ ): Promise<StreamResponse<TrackActivityMetricsResponse>> {
424
+ const body = {
425
+ events: request?.events,
426
+ };
427
+
428
+ const response = await this.apiClient.sendRequest<
429
+ StreamResponse<TrackActivityMetricsResponse>
430
+ >(
431
+ 'POST',
432
+ '/api/v2/feeds/activities/metrics/track',
433
+ undefined,
434
+ undefined,
435
+ body,
436
+ 'application/json',
437
+ );
438
+
439
+ decoders.TrackActivityMetricsResponse?.(response.body);
440
+
441
+ return { ...response.body, metadata: response.metadata };
442
+ }
443
+
416
444
  async queryActivities(
417
445
  request?: QueryActivitiesRequest,
418
446
  ): Promise<StreamResponse<QueryActivitiesResponse>> {
@@ -1020,6 +1048,33 @@ export class FeedsApi {
1020
1048
  return { ...response.body, metadata: response.metadata };
1021
1049
  }
1022
1050
 
1051
+ async queryCollections(
1052
+ request?: QueryCollectionsRequest,
1053
+ ): Promise<StreamResponse<QueryCollectionsResponse>> {
1054
+ const body = {
1055
+ limit: request?.limit,
1056
+ next: request?.next,
1057
+ prev: request?.prev,
1058
+ sort: request?.sort,
1059
+ filter: request?.filter,
1060
+ };
1061
+
1062
+ const response = await this.apiClient.sendRequest<
1063
+ StreamResponse<QueryCollectionsResponse>
1064
+ >(
1065
+ 'POST',
1066
+ '/api/v2/feeds/collections/query',
1067
+ undefined,
1068
+ undefined,
1069
+ body,
1070
+ 'application/json',
1071
+ );
1072
+
1073
+ decoders.QueryCollectionsResponse?.(response.body);
1074
+
1075
+ return { ...response.body, metadata: response.metadata };
1076
+ }
1077
+
1023
1078
  async getComments(request: {
1024
1079
  object_id: string;
1025
1080
  object_type: string;
@@ -1112,6 +1167,7 @@ export class FeedsApi {
1112
1167
  ): Promise<StreamResponse<QueryCommentsResponse>> {
1113
1168
  const body = {
1114
1169
  filter: request?.filter,
1170
+ id_around: request?.id_around,
1115
1171
  limit: request?.limit,
1116
1172
  next: request?.next,
1117
1173
  prev: request?.prev,
@@ -2649,25 +2705,36 @@ export class FeedsApi {
2649
2705
  return { ...response.body, metadata: response.metadata };
2650
2706
  }
2651
2707
 
2652
- async removeUserGroupMembers(request: {
2653
- id: string;
2654
- }): Promise<StreamResponse<RemoveUserGroupMembersResponse>> {
2708
+ async addUserGroupMembers(
2709
+ request: AddUserGroupMembersRequest & { id: string },
2710
+ ): Promise<StreamResponse<AddUserGroupMembersResponse>> {
2655
2711
  const pathParams = {
2656
2712
  id: request?.id,
2657
2713
  };
2714
+ const body = {
2715
+ member_ids: request?.member_ids,
2716
+ team_id: request?.team_id,
2717
+ };
2658
2718
 
2659
2719
  const response = await this.apiClient.sendRequest<
2660
- StreamResponse<RemoveUserGroupMembersResponse>
2661
- >('DELETE', '/api/v2/usergroups/{id}/members', pathParams, undefined);
2720
+ StreamResponse<AddUserGroupMembersResponse>
2721
+ >(
2722
+ 'POST',
2723
+ '/api/v2/usergroups/{id}/members',
2724
+ pathParams,
2725
+ undefined,
2726
+ body,
2727
+ 'application/json',
2728
+ );
2662
2729
 
2663
- decoders.RemoveUserGroupMembersResponse?.(response.body);
2730
+ decoders.AddUserGroupMembersResponse?.(response.body);
2664
2731
 
2665
2732
  return { ...response.body, metadata: response.metadata };
2666
2733
  }
2667
2734
 
2668
- async addUserGroupMembers(
2669
- request: AddUserGroupMembersRequest & { id: string },
2670
- ): Promise<StreamResponse<AddUserGroupMembersResponse>> {
2735
+ async removeUserGroupMembers(
2736
+ request: RemoveUserGroupMembersRequest & { id: string },
2737
+ ): Promise<StreamResponse<RemoveUserGroupMembersResponse>> {
2671
2738
  const pathParams = {
2672
2739
  id: request?.id,
2673
2740
  };
@@ -2677,17 +2744,17 @@ export class FeedsApi {
2677
2744
  };
2678
2745
 
2679
2746
  const response = await this.apiClient.sendRequest<
2680
- StreamResponse<AddUserGroupMembersResponse>
2747
+ StreamResponse<RemoveUserGroupMembersResponse>
2681
2748
  >(
2682
2749
  'POST',
2683
- '/api/v2/usergroups/{id}/members',
2750
+ '/api/v2/usergroups/{id}/members/delete',
2684
2751
  pathParams,
2685
2752
  undefined,
2686
2753
  body,
2687
2754
  'application/json',
2688
2755
  );
2689
2756
 
2690
- decoders.AddUserGroupMembersResponse?.(response.body);
2757
+ decoders.RemoveUserGroupMembersResponse?.(response.body);
2691
2758
 
2692
2759
  return { ...response.body, metadata: response.metadata };
2693
2760
  }
@@ -1554,6 +1554,13 @@ decoders.QueryBookmarksResponse = (input?: Record<string, any>) => {
1554
1554
  return decode(typeMappings, input);
1555
1555
  };
1556
1556
 
1557
+ decoders.QueryCollectionsResponse = (input?: Record<string, any>) => {
1558
+ const typeMappings: TypeMapping = {
1559
+ collections: { type: 'CollectionResponse', isSingle: false },
1560
+ };
1561
+ return decode(typeMappings, input);
1562
+ };
1563
+
1557
1564
  decoders.QueryCommentReactionsResponse = (input?: Record<string, any>) => {
1558
1565
  const typeMappings: TypeMapping = {
1559
1566
  reactions: { type: 'FeedsReactionResponse', isSingle: false },