@stream-io/feeds-client 0.3.32 → 0.3.33

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 (52) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/cjs/index.js +3 -2
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/react-bindings.js +39 -33
  5. package/dist/cjs/react-bindings.js.map +1 -1
  6. package/dist/es/index.mjs +4 -3
  7. package/dist/es/index.mjs.map +1 -1
  8. package/dist/es/react-bindings.mjs +39 -33
  9. package/dist/es/react-bindings.mjs.map +1 -1
  10. package/dist/{feeds-client-BNhvggk2.mjs → feeds-client-C-6NrDBy.mjs} +196 -182
  11. package/dist/feeds-client-C-6NrDBy.mjs.map +1 -0
  12. package/dist/{feeds-client-B9rwEWH3.js → feeds-client-CyaHg6lu.js} +196 -182
  13. package/dist/feeds-client-CyaHg6lu.js.map +1 -0
  14. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  15. package/dist/types/activity-with-state-updates/activity-with-state-updates.d.ts +1 -1
  16. package/dist/types/activity-with-state-updates/activity-with-state-updates.d.ts.map +1 -1
  17. package/dist/types/bindings/react/hooks/feed-state-hooks/index.d.ts +1 -0
  18. package/dist/types/bindings/react/hooks/feed-state-hooks/index.d.ts.map +1 -1
  19. package/dist/types/bindings/react/hooks/feed-state-hooks/useOwnCapabilities.d.ts.map +1 -1
  20. package/dist/types/bindings/react/hooks/feed-state-hooks/useOwnFollowings.d.ts +8 -0
  21. package/dist/types/bindings/react/hooks/feed-state-hooks/useOwnFollowings.d.ts.map +1 -0
  22. package/dist/types/common/types.d.ts +1 -1
  23. package/dist/types/common/types.d.ts.map +1 -1
  24. package/dist/types/feed/event-handlers/activity/handle-activity-added.d.ts.map +1 -1
  25. package/dist/types/feed/event-handlers/activity/handle-activity-updated.d.ts.map +1 -1
  26. package/dist/types/feed/feed.d.ts +2 -1
  27. package/dist/types/feed/feed.d.ts.map +1 -1
  28. package/dist/types/feeds-client/feeds-client.d.ts +7 -9
  29. package/dist/types/feeds-client/feeds-client.d.ts.map +1 -1
  30. package/dist/types/utils/check-own-fields-equality.d.ts +2 -0
  31. package/dist/types/utils/check-own-fields-equality.d.ts.map +1 -1
  32. package/dist/types/utils/throttling/index.d.ts +1 -1
  33. package/dist/types/utils/throttling/index.d.ts.map +1 -1
  34. package/dist/types/utils/throttling/throttled-get-batched-own-fields.d.ts +14 -0
  35. package/dist/types/utils/throttling/throttled-get-batched-own-fields.d.ts.map +1 -0
  36. package/package.json +1 -1
  37. package/src/activity-with-state-updates/activity-with-state-updates.ts +7 -2
  38. package/src/bindings/react/hooks/feed-state-hooks/index.ts +1 -0
  39. package/src/bindings/react/hooks/feed-state-hooks/useOwnCapabilities.ts +14 -23
  40. package/src/bindings/react/hooks/feed-state-hooks/useOwnFollowings.ts +18 -0
  41. package/src/common/types.ts +1 -1
  42. package/src/feed/event-handlers/activity/handle-activity-added.ts +0 -6
  43. package/src/feed/event-handlers/activity/handle-activity-updated.ts +0 -4
  44. package/src/feed/feed.ts +51 -39
  45. package/src/feeds-client/feeds-client.ts +88 -91
  46. package/src/utils/check-own-fields-equality.ts +37 -10
  47. package/src/utils/throttling/index.ts +1 -1
  48. package/src/utils/throttling/{throttled-get-batched-own-capabilities.ts → throttled-get-batched-own-fields.ts} +10 -10
  49. package/dist/feeds-client-B9rwEWH3.js.map +0 -1
  50. package/dist/feeds-client-BNhvggk2.mjs.map +0 -1
  51. package/dist/types/utils/throttling/throttled-get-batched-own-capabilities.d.ts +0 -14
  52. package/dist/types/utils/throttling/throttled-get-batched-own-capabilities.d.ts.map +0 -1
@@ -60,6 +60,7 @@ import { ModerationClient } from '../moderation-client';
60
60
  import { StreamPoll } from '../common/Poll';
61
61
  import {
62
62
  Feed,
63
+ type FeedState,
63
64
  handleActivityReactionAdded,
64
65
  handleActivityReactionDeleted,
65
66
  handleActivityReactionUpdated,
@@ -86,15 +87,16 @@ import { feedsLoggerSystem } from '../utils';
86
87
  import { handleCommentReactionUpdated } from '../feed/event-handlers/comment/handle-comment-reaction-updated';
87
88
  import {
88
89
  throttle,
89
- DEFAULT_BATCH_OWN_CAPABILITIES_THROTTLING_INTERVAL,
90
- type GetBatchedOwnCapabilitiesThrottledCallback,
91
- queueBatchedOwnCapabilities,
92
- type ThrottledGetBatchedOwnCapabilities,
93
90
  clearQueuedFeeds,
91
+ type ThrottledGetBatchedOwnFields,
92
+ type GetBatchedOwnFieldsThrottledCallback,
93
+ DEFAULT_BATCH_OWN_FIELDS_THROTTLING_INTERVAL,
94
94
  } from '../utils/throttling';
95
95
  import { ActivityWithStateUpdates } from '../activity-with-state-updates/activity-with-state-updates';
96
96
  import { getFeed } from '../activity-with-state-updates/get-feed';
97
97
  import {
98
+ isOwnCapabilitiesEqual,
99
+ isOwnFollowingsEqual,
98
100
  isOwnFollowsEqual,
99
101
  isOwnMembershipEqual,
100
102
  } from '../utils/check-own-fields-equality';
@@ -102,7 +104,6 @@ import {
102
104
  export type FeedsClientState = {
103
105
  connected_user: ConnectedUser | undefined;
104
106
  is_ws_connection_healthy: boolean;
105
- own_capabilities_by_fid: Record<string, FeedResponse['own_capabilities']>;
106
107
  };
107
108
 
108
109
  type FID = string;
@@ -128,9 +129,9 @@ export class FeedsClient extends FeedsApi {
128
129
 
129
130
  private healthyConnectionChangedEventCount = 0;
130
131
 
131
- protected throttledGetBatchOwnCapabilities!: ThrottledGetBatchedOwnCapabilities;
132
- private cancelGetBatchOwnCapabilitiesTimer!: () => void;
133
- private query_batch_own_capabilties_throttling_interval!: number;
132
+ protected throttledGetBatchOwnFields!: ThrottledGetBatchedOwnFields;
133
+ private cancelGetBatchOwnFieldsTimer!: () => void;
134
+ private query_batch_own_fields_throttling_interval!: number;
134
135
 
135
136
  constructor(apiKey: string, options?: FeedsClientOptions) {
136
137
  const tokenManager = new TokenManager();
@@ -145,16 +146,15 @@ export class FeedsClient extends FeedsApi {
145
146
  this.state = new StateStore<FeedsClientState>({
146
147
  connected_user: undefined,
147
148
  is_ws_connection_healthy: false,
148
- own_capabilities_by_fid: {},
149
149
  });
150
150
  this.moderation = new ModerationClient(apiClient);
151
151
  this.tokenManager = tokenManager;
152
152
  this.connectionIdManager = connectionIdManager;
153
153
  this.polls_by_id = new Map();
154
154
 
155
- this.query_batch_own_capabilties_throttling_interval =
156
- options?.query_batch_own_capabilties_throttling_interval ??
157
- DEFAULT_BATCH_OWN_CAPABILITIES_THROTTLING_INTERVAL;
155
+ this.query_batch_own_fields_throttling_interval =
156
+ options?.query_batch_own_fields_throttling_interval ??
157
+ DEFAULT_BATCH_OWN_FIELDS_THROTTLING_INTERVAL;
158
158
 
159
159
  feedsLoggerSystem.configureLoggers(options?.configure_loggers_options);
160
160
 
@@ -184,6 +184,7 @@ export class FeedsClient extends FeedsApi {
184
184
  group: event.feed.group_id,
185
185
  id: event.feed.id,
186
186
  data: event.feed,
187
+ fieldsToUpdate: [],
187
188
  });
188
189
 
189
190
  break;
@@ -284,30 +285,27 @@ export class FeedsClient extends FeedsApi {
284
285
  });
285
286
  }
286
287
 
287
- private setGetBatchOwnCapabilitiesThrottlingInterval = (
288
- throttlingMs: number,
289
- ) => {
290
- const {
291
- throttledFn: throttledGetBatchOwnCapabilities,
292
- cancelTimer: cancel,
293
- } = throttle<GetBatchedOwnCapabilitiesThrottledCallback>(
294
- (feeds, callback) => {
295
- this.ownBatch({
296
- feeds,
297
- }).catch((error) => {
298
- this.eventDispatcher.dispatch({
299
- type: 'errors.unhandled',
300
- error_type: UnhandledErrorType.FetchingOwnCapabilitiesOnNewActivity,
301
- error,
288
+ private setGetBatchOwnFieldsThrottlingInterval = (throttlingMs: number) => {
289
+ const { throttledFn: throttledGetBatchOwnFields, cancelTimer: cancel } =
290
+ throttle<GetBatchedOwnFieldsThrottledCallback>(
291
+ (feeds, callback) => {
292
+ this.ownBatch({
293
+ feeds,
294
+ }).catch((error) => {
295
+ this.eventDispatcher.dispatch({
296
+ type: 'errors.unhandled',
297
+ error_type:
298
+ UnhandledErrorType.FetchingOwnCapabilitiesOnNewActivity,
299
+ error,
300
+ });
302
301
  });
303
- });
304
- callback(feeds);
305
- },
306
- throttlingMs,
307
- { trailing: true },
308
- );
309
- this.throttledGetBatchOwnCapabilities = throttledGetBatchOwnCapabilities;
310
- this.cancelGetBatchOwnCapabilitiesTimer = cancel;
302
+ callback(feeds);
303
+ },
304
+ throttlingMs,
305
+ { trailing: true },
306
+ );
307
+ this.throttledGetBatchOwnFields = throttledGetBatchOwnFields;
308
+ this.cancelGetBatchOwnFieldsTimer = cancel;
311
309
  };
312
310
 
313
311
  private recoverOnReconnect = async () => {
@@ -369,34 +367,6 @@ export class FeedsClient extends FeedsApi {
369
367
  }
370
368
  }
371
369
 
372
- public hydrateCapabilitiesCache(
373
- feedResponses: Array<Pick<FeedResponse, 'feed' | 'own_capabilities'>>,
374
- ) {
375
- let ownCapabilitiesCache =
376
- this.state.getLatestValue().own_capabilities_by_fid;
377
-
378
- const capabilitiesToFetchQueue: string[] = [];
379
-
380
- for (const feedResponse of feedResponses) {
381
- const { feed, own_capabilities } = feedResponse;
382
-
383
- if (!Object.prototype.hasOwnProperty.call(ownCapabilitiesCache, feed)) {
384
- if (own_capabilities) {
385
- ownCapabilitiesCache = {
386
- ...ownCapabilitiesCache,
387
- [feed]: own_capabilities,
388
- };
389
- } else {
390
- capabilitiesToFetchQueue.push(feed);
391
- }
392
- }
393
- }
394
-
395
- queueBatchedOwnCapabilities.bind(this)({ feeds: capabilitiesToFetchQueue });
396
-
397
- this.state.partialNext({ own_capabilities_by_fid: ownCapabilitiesCache });
398
- }
399
-
400
370
  connectUser = async (user: UserRequest, tokenProvider?: TokenOrProvider) => {
401
371
  if (
402
372
  this.state.getLatestValue().connected_user !== undefined ||
@@ -407,8 +377,8 @@ export class FeedsClient extends FeedsApi {
407
377
 
408
378
  this.tokenManager.setTokenOrProvider(tokenProvider);
409
379
 
410
- this.setGetBatchOwnCapabilitiesThrottlingInterval(
411
- this.query_batch_own_capabilties_throttling_interval,
380
+ this.setGetBatchOwnFieldsThrottlingInterval(
381
+ this.query_batch_own_fields_throttling_interval,
412
382
  );
413
383
 
414
384
  try {
@@ -649,10 +619,9 @@ export class FeedsClient extends FeedsApi {
649
619
  this.state.partialNext({
650
620
  connected_user: undefined,
651
621
  is_ws_connection_healthy: false,
652
- own_capabilities_by_fid: {},
653
622
  });
654
623
 
655
- this.cancelGetBatchOwnCapabilitiesTimer();
624
+ this.cancelGetBatchOwnFieldsTimer();
656
625
  clearQueuedFeeds();
657
626
  };
658
627
 
@@ -680,6 +649,7 @@ export class FeedsClient extends FeedsApi {
680
649
  group: groupId,
681
650
  id,
682
651
  options,
652
+ fieldsToUpdate: [],
683
653
  });
684
654
  };
685
655
 
@@ -711,11 +681,15 @@ export class FeedsClient extends FeedsApi {
711
681
  id: feedResponse.id,
712
682
  data: feedResponse,
713
683
  watch: request?.watch,
684
+ fieldsToUpdate: [
685
+ 'own_capabilities',
686
+ 'own_follows',
687
+ 'own_membership',
688
+ 'own_followings',
689
+ ],
714
690
  }),
715
691
  );
716
692
 
717
- this.hydrateCapabilitiesCache(feedResponses);
718
-
719
693
  return {
720
694
  feeds,
721
695
  next: response.next,
@@ -727,13 +701,12 @@ export class FeedsClient extends FeedsApi {
727
701
 
728
702
  async ownBatch(request: OwnBatchRequest) {
729
703
  const response = await super.ownBatch(request);
730
- const feedResponses = Object.entries(response.data).map(
731
- ([feed, ownFields]) => ({
732
- feed,
733
- own_capabilities: ownFields.own_capabilities,
734
- }),
735
- );
736
- this.hydrateCapabilitiesCache(feedResponses);
704
+ Object.entries(response.data).forEach(([fid, ownFields]) => {
705
+ const feed = this.activeFeeds[fid];
706
+ if (feed) {
707
+ feed.state.partialNext(ownFields);
708
+ }
709
+ });
737
710
  return response;
738
711
  }
739
712
 
@@ -826,7 +799,6 @@ export class FeedsClient extends FeedsApi {
826
799
  },
827
800
  ) {
828
801
  const response = await super.getOrCreateFeed(request);
829
- this.hydrateCapabilitiesCache([response.feed]);
830
802
 
831
803
  if (request.watch) {
832
804
  const feeds = this.findAllActiveFeedsByFid(
@@ -848,6 +820,12 @@ export class FeedsClient extends FeedsApi {
848
820
  group: suggestion.group_id,
849
821
  id: suggestion.id,
850
822
  data: suggestion,
823
+ fieldsToUpdate: [
824
+ 'own_capabilities',
825
+ 'own_follows',
826
+ 'own_membership',
827
+ 'own_followings',
828
+ ],
851
829
  });
852
830
  });
853
831
 
@@ -861,7 +839,7 @@ export class FeedsClient extends FeedsApi {
861
839
  data,
862
840
  watch,
863
841
  options,
864
- fromWebSocket = false,
842
+ fieldsToUpdate,
865
843
  }: {
866
844
  group: string;
867
845
  id: string;
@@ -871,7 +849,9 @@ export class FeedsClient extends FeedsApi {
871
849
  addNewActivitiesTo?: 'start' | 'end';
872
850
  activityAddedEventFilter?: (event: ActivityAddedEvent) => boolean;
873
851
  };
874
- fromWebSocket?: boolean;
852
+ fieldsToUpdate: Array<
853
+ 'own_capabilities' | 'own_follows' | 'own_followings' | 'own_membership'
854
+ >;
875
855
  }) => {
876
856
  const fid = `${group}:${id}`;
877
857
  let isCreated = false;
@@ -909,18 +889,35 @@ export class FeedsClient extends FeedsApi {
909
889
  handleFeedUpdated.call(feed, { feed: data });
910
890
  } else if (
911
891
  (feed.currentState.updated_at?.getTime() ?? 0) ===
912
- data.updated_at.getTime() &&
913
- !fromWebSocket
892
+ data.updated_at.getTime()
914
893
  ) {
915
- const fieldsToUpdate: Array<keyof FeedResponse> = [];
916
- if (!isOwnFollowsEqual(feed.currentState, data)) {
917
- fieldsToUpdate.push('own_follows');
918
- }
919
- if (!isOwnMembershipEqual(feed.currentState, data)) {
920
- fieldsToUpdate.push('own_membership');
921
- }
922
- if (fieldsToUpdate.length > 0) {
923
- const fieldsToUpdateData = fieldsToUpdate.reduce(
894
+ const fieldsToUpdateData: Array<keyof FeedResponse> = [];
895
+ const fieldChecks: Array<
896
+ [
897
+ (
898
+ | 'own_capabilities'
899
+ | 'own_follows'
900
+ | 'own_membership'
901
+ | 'own_followings'
902
+ ),
903
+ (currentState: FeedState, newState: FeedResponse) => boolean,
904
+ ]
905
+ > = [
906
+ ['own_capabilities', isOwnCapabilitiesEqual],
907
+ ['own_follows', isOwnFollowsEqual],
908
+ ['own_membership', isOwnMembershipEqual],
909
+ ['own_followings', isOwnFollowingsEqual],
910
+ ];
911
+ fieldChecks.forEach(([field, isEqual]) => {
912
+ if (
913
+ fieldsToUpdate.includes(field) &&
914
+ !isEqual(feed.currentState, data)
915
+ ) {
916
+ fieldsToUpdateData.push(field);
917
+ }
918
+ });
919
+ if (fieldsToUpdateData.length > 0) {
920
+ const fieldsToUpdatePayload = fieldsToUpdateData.reduce(
924
921
  (acc: Partial<FeedResponse>, field) => {
925
922
  // @ts-expect-error TODO: fix this
926
923
  acc[field] = data[field];
@@ -928,7 +925,7 @@ export class FeedsClient extends FeedsApi {
928
925
  },
929
926
  {},
930
927
  );
931
- feed.state.partialNext(fieldsToUpdateData);
928
+ feed.state.partialNext(fieldsToUpdatePayload);
932
929
  }
933
930
  }
934
931
  }
@@ -1,25 +1,25 @@
1
1
  import type { FeedState } from '../feed';
2
- import type { FeedResponse } from '../gen/models';
2
+ import type { FeedResponse, FollowResponse } from '../gen/models';
3
3
 
4
- export const isOwnFollowsEqual = (
5
- currentState: FeedState,
6
- newState: FeedResponse,
7
- ) => {
4
+ const areFollowArraysEqual = (
5
+ currentFollows: FollowResponse[] | undefined,
6
+ newFollows: FollowResponse[] | undefined,
7
+ ): boolean => {
8
8
  const existingFollows = new Set(
9
- currentState.own_follows?.map(
9
+ currentFollows?.map(
10
10
  (f) =>
11
11
  `${f.source_feed.feed}:${f.target_feed.feed}:${f.updated_at.getTime()}`,
12
12
  ),
13
13
  );
14
- const newFollows = new Set(
15
- newState.own_follows?.map(
14
+ const newFollowsSet = new Set(
15
+ newFollows?.map(
16
16
  (f) =>
17
17
  `${f.source_feed.feed}:${f.target_feed.feed}:${f.updated_at.getTime()}`,
18
18
  ),
19
19
  );
20
- if (existingFollows.size === newFollows.size) {
20
+ if (existingFollows.size === newFollowsSet.size) {
21
21
  const areEqual = Array.from(existingFollows).every((f) =>
22
- newFollows.has(f),
22
+ newFollowsSet.has(f),
23
23
  );
24
24
  if (areEqual) {
25
25
  return true;
@@ -29,6 +29,13 @@ export const isOwnFollowsEqual = (
29
29
  return false;
30
30
  };
31
31
 
32
+ export const isOwnFollowsEqual = (
33
+ currentState: FeedState,
34
+ newState: FeedResponse,
35
+ ) => {
36
+ return areFollowArraysEqual(currentState.own_follows, newState.own_follows);
37
+ };
38
+
32
39
  export const isOwnMembershipEqual = (
33
40
  currentState: FeedState,
34
41
  newState: FeedResponse,
@@ -38,3 +45,23 @@ export const isOwnMembershipEqual = (
38
45
  (newState.own_membership?.updated_at.getTime() ?? 0)
39
46
  );
40
47
  };
48
+
49
+ export const isOwnCapabilitiesEqual = (
50
+ currentState: FeedState,
51
+ newState: FeedResponse,
52
+ ) => {
53
+ return (
54
+ [...(currentState.own_capabilities ?? [])].sort().join(',') ===
55
+ [...(newState.own_capabilities ?? [])].sort().join(',')
56
+ );
57
+ };
58
+
59
+ export const isOwnFollowingsEqual = (
60
+ currentState: FeedState,
61
+ newState: FeedResponse,
62
+ ) => {
63
+ return areFollowArraysEqual(
64
+ currentState.own_followings,
65
+ newState.own_followings,
66
+ );
67
+ };
@@ -1,2 +1,2 @@
1
1
  export * from './throttle';
2
- export * from './throttled-get-batched-own-capabilities'
2
+ export * from './throttled-get-batched-own-fields';
@@ -1,35 +1,35 @@
1
1
  import type { FeedsClient } from '../../feeds-client';
2
2
  import type { ThrottledFunction } from './throttle';
3
3
 
4
- const BATCH_OWN_CAPABILITIES_API_LIMIT = 100;
4
+ const BATCH_OWN_FIELDS_API_LIMIT = 100;
5
5
 
6
- export type GetBatchedOwnCapabilities = {
6
+ export type GetBatchedOwnFields = {
7
7
  feeds: string[];
8
8
  };
9
9
 
10
- export type GetBatchedOwnCapabilitiesThrottledCallback = [
10
+ export type GetBatchedOwnFieldsThrottledCallback = [
11
11
  feeds: string[],
12
12
  callback: (feedsToClear: string[]) => void | Promise<void>,
13
13
  ];
14
14
 
15
- export type ThrottledGetBatchedOwnCapabilities =
16
- ThrottledFunction<GetBatchedOwnCapabilitiesThrottledCallback>;
15
+ export type ThrottledGetBatchedOwnFields =
16
+ ThrottledFunction<GetBatchedOwnFieldsThrottledCallback>;
17
17
 
18
- export const DEFAULT_BATCH_OWN_CAPABILITIES_THROTTLING_INTERVAL = 2000;
18
+ export const DEFAULT_BATCH_OWN_FIELDS_THROTTLING_INTERVAL = 2000;
19
19
 
20
20
  const queuedFeeds: Set<string> = new Set();
21
21
 
22
- export function queueBatchedOwnCapabilities(
22
+ export function queueBatchedOwnFields(
23
23
  this: FeedsClient,
24
- { feeds }: GetBatchedOwnCapabilities,
24
+ { feeds }: GetBatchedOwnFields,
25
25
  ) {
26
26
  for (const feed of feeds) {
27
27
  queuedFeeds.add(feed);
28
28
  }
29
29
 
30
30
  if (queuedFeeds.size > 0) {
31
- this.throttledGetBatchOwnCapabilities(
32
- [...queuedFeeds].slice(0, BATCH_OWN_CAPABILITIES_API_LIMIT),
31
+ this.throttledGetBatchOwnFields(
32
+ [...queuedFeeds].slice(0, BATCH_OWN_FIELDS_API_LIMIT),
33
33
  (feedsToClear: string[]) => {
34
34
  for (const feed of feedsToClear) {
35
35
  queuedFeeds.delete(feed);