@stream-io/feeds-client 0.2.17 → 0.2.19

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 (117) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/cjs/index.js +94 -25
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/react-bindings.js +26 -55
  5. package/dist/cjs/react-bindings.js.map +1 -1
  6. package/dist/es/index.mjs +86 -17
  7. package/dist/es/index.mjs.map +1 -1
  8. package/dist/es/react-bindings.mjs +19 -48
  9. package/dist/es/react-bindings.mjs.map +1 -1
  10. package/dist/{index-nq6SDtbt.js → feeds-client-C09giTf1.js} +322 -133
  11. package/dist/feeds-client-C09giTf1.js.map +1 -0
  12. package/dist/{index-BZL77zNq.mjs → feeds-client-CFadXO-B.mjs} +335 -146
  13. package/dist/feeds-client-CFadXO-B.mjs.map +1 -0
  14. package/dist/tsconfig.tsbuildinfo +1 -1
  15. package/dist/types/bindings/react/hooks/feed-state-hooks/useOwnCapabilities.d.ts +2 -32
  16. package/dist/types/bindings/react/hooks/feed-state-hooks/useOwnCapabilities.d.ts.map +1 -1
  17. package/dist/types/common/real-time/StableWSConnection.d.ts +3 -3
  18. package/dist/types/common/real-time/event-models.d.ts +7 -2
  19. package/dist/types/common/real-time/event-models.d.ts.map +1 -1
  20. package/dist/types/common/types.d.ts +1 -0
  21. package/dist/types/common/types.d.ts.map +1 -1
  22. package/dist/types/feed/event-handlers/activity/handle-activity-added.d.ts +4 -3
  23. package/dist/types/feed/event-handlers/activity/handle-activity-added.d.ts.map +1 -1
  24. package/dist/types/feed/event-handlers/activity/handle-activity-updated.d.ts.map +1 -1
  25. package/dist/types/feed/event-handlers/activity-updater.d.ts +44 -0
  26. package/dist/types/feed/event-handlers/activity-updater.d.ts.map +1 -0
  27. package/dist/types/feed/event-handlers/add-aggregated-activities-to-state.d.ts +6 -0
  28. package/dist/types/feed/event-handlers/add-aggregated-activities-to-state.d.ts.map +1 -0
  29. package/dist/types/feed/event-handlers/index.d.ts +3 -1
  30. package/dist/types/feed/event-handlers/index.d.ts.map +1 -1
  31. package/dist/types/feed/event-handlers/{aggregated-feed/handle-aggregated-feed-updated.d.ts → notification-feed/handle-notification-feed-updated.d.ts} +2 -11
  32. package/dist/types/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts.map +1 -0
  33. package/dist/types/feed/event-handlers/notification-feed/index.d.ts +2 -0
  34. package/dist/types/feed/event-handlers/notification-feed/index.d.ts.map +1 -0
  35. package/dist/types/feed/event-handlers/story-feeds/handle-story-feeds-updated.d.ts +15 -0
  36. package/dist/types/feed/event-handlers/story-feeds/handle-story-feeds-updated.d.ts.map +1 -0
  37. package/dist/types/feed/event-handlers/story-feeds/index.d.ts +2 -0
  38. package/dist/types/feed/event-handlers/story-feeds/index.d.ts.map +1 -0
  39. package/dist/types/feed/feed.d.ts +10 -4
  40. package/dist/types/feed/feed.d.ts.map +1 -1
  41. package/dist/types/feeds-client/feeds-client.d.ts +14 -4
  42. package/dist/types/feeds-client/feeds-client.d.ts.map +1 -1
  43. package/dist/types/gen/feeds/FeedsApi.d.ts.map +1 -1
  44. package/dist/types/gen/models/index.d.ts +42 -451
  45. package/dist/types/gen/models/index.d.ts.map +1 -1
  46. package/dist/types/utils/throttling/index.d.ts +3 -0
  47. package/dist/types/utils/throttling/index.d.ts.map +1 -0
  48. package/dist/types/utils/throttling/throttle.d.ts +34 -0
  49. package/dist/types/utils/throttling/throttle.d.ts.map +1 -0
  50. package/dist/types/utils/throttling/throttled-get-batched-own-capabilities.d.ts +14 -0
  51. package/dist/types/utils/throttling/throttled-get-batched-own-capabilities.d.ts.map +1 -0
  52. package/package.json +7 -3
  53. package/react-bindings.d.ts +11 -0
  54. package/react-bindings.js +7 -0
  55. package/react-bindings.mjs +11 -0
  56. package/src/bindings/react/hooks/feed-state-hooks/useOwnCapabilities.ts +21 -73
  57. package/src/common/real-time/event-models.ts +8 -2
  58. package/src/common/types.ts +1 -0
  59. package/src/feed/event-handlers/activity/handle-activity-added.ts +18 -12
  60. package/src/feed/event-handlers/activity/handle-activity-updated.ts +12 -16
  61. package/src/feed/event-handlers/activity-updater.ts +15 -0
  62. package/src/feed/event-handlers/add-aggregated-activities-to-state.ts +72 -0
  63. package/src/feed/event-handlers/index.ts +3 -1
  64. package/src/feed/event-handlers/{aggregated-feed/handle-aggregated-feed-updated.ts → notification-feed/handle-notification-feed-updated.ts} +2 -94
  65. package/src/feed/event-handlers/notification-feed/index.ts +1 -0
  66. package/src/feed/event-handlers/story-feeds/handle-story-feeds-updated.ts +122 -0
  67. package/src/feed/event-handlers/story-feeds/index.ts +1 -0
  68. package/src/feed/feed.ts +30 -3
  69. package/src/feeds-client/feeds-client.ts +127 -6
  70. package/src/gen/feeds/FeedsApi.ts +5 -0
  71. package/src/gen/model-decoders/decoders.ts +10 -4
  72. package/src/gen/models/index.ts +75 -834
  73. package/src/test-utils/response-generators.ts +37 -1
  74. package/src/utils/throttling/index.ts +2 -0
  75. package/src/utils/throttling/throttle.ts +123 -0
  76. package/src/utils/throttling/throttled-get-batched-own-capabilities.ts +42 -0
  77. package/dist/index-BZL77zNq.mjs.map +0 -1
  78. package/dist/index-nq6SDtbt.js.map +0 -1
  79. package/dist/types/feed/event-handlers/aggregated-feed/handle-aggregated-feed-updated.d.ts.map +0 -1
  80. package/dist/types/feed/event-handlers/aggregated-feed/index.d.ts +0 -2
  81. package/dist/types/feed/event-handlers/aggregated-feed/index.d.ts.map +0 -1
  82. package/src/feed/event-handlers/activity/activity-marked-utils.test.ts +0 -208
  83. package/src/feed/event-handlers/activity/activity-reaction-utils.test.ts +0 -371
  84. package/src/feed/event-handlers/activity/activity-utils.test.ts +0 -252
  85. package/src/feed/event-handlers/activity/handle-activity-added.test.ts +0 -86
  86. package/src/feed/event-handlers/activity/handle-activity-deleted.test.ts +0 -117
  87. package/src/feed/event-handlers/activity/handle-activity-pinned.test.ts +0 -60
  88. package/src/feed/event-handlers/activity/handle-activity-reaction-added.test.ts +0 -257
  89. package/src/feed/event-handlers/activity/handle-activity-reaction-deleted.test.ts +0 -317
  90. package/src/feed/event-handlers/activity/handle-activity-reaction-updated.test.ts +0 -282
  91. package/src/feed/event-handlers/activity/handle-activity-unpinned.test.ts +0 -95
  92. package/src/feed/event-handlers/activity/handle-activity-updated.test.ts +0 -245
  93. package/src/feed/event-handlers/aggregated-feed/handle-aggregated-feed-updated.test.ts +0 -644
  94. package/src/feed/event-handlers/aggregated-feed/index.ts +0 -1
  95. package/src/feed/event-handlers/bookmark/bookmark-utils.test.ts +0 -521
  96. package/src/feed/event-handlers/bookmark/handle-bookmark-added.test.ts +0 -178
  97. package/src/feed/event-handlers/bookmark/handle-bookmark-deleted.test.ts +0 -188
  98. package/src/feed/event-handlers/bookmark/handle-bookmark-updated.test.ts +0 -196
  99. package/src/feed/event-handlers/comment/handle-comment-added.test.ts +0 -271
  100. package/src/feed/event-handlers/comment/handle-comment-deleted.test.ts +0 -255
  101. package/src/feed/event-handlers/comment/handle-comment-reaction-added.test.ts +0 -329
  102. package/src/feed/event-handlers/comment/handle-comment-reaction-deleted.test.ts +0 -343
  103. package/src/feed/event-handlers/comment/handle-comment-reaction-updated.test.ts +0 -350
  104. package/src/feed/event-handlers/comment/handle-comment-updated.test.ts +0 -267
  105. package/src/feed/event-handlers/comment/utils/update-comment-count.test.ts +0 -322
  106. package/src/feed/event-handlers/feed-member/handle-feed-member-added.test.ts +0 -75
  107. package/src/feed/event-handlers/feed-member/handle-feed-member-removed.test.ts +0 -82
  108. package/src/feed/event-handlers/feed-member/handle-feed-member-updated.test.ts +0 -84
  109. package/src/feed/event-handlers/follow/follow-state-update-queue.test.ts +0 -219
  110. package/src/feed/event-handlers/follow/handle-follow-created.test.ts +0 -250
  111. package/src/feed/event-handlers/follow/handle-follow-deleted.test.ts +0 -268
  112. package/src/feed/event-handlers/follow/handle-follow-updated.test.ts +0 -131
  113. package/src/feed/feed.test.ts +0 -90
  114. package/src/feeds-client/event-handlers/user/handle-user-updated.test.ts +0 -53
  115. package/src/utils/event-triggered-by-connected-user.test.ts +0 -73
  116. package/src/utils/state-update-queue.test.ts +0 -129
  117. package/src/utils/unique-array-merge.test.ts +0 -179
@@ -14,6 +14,7 @@ import type {
14
14
  FollowBatchRequest,
15
15
  FollowRequest,
16
16
  ImageUploadRequest,
17
+ OwnCapabilitiesBatchRequest,
17
18
  OwnUser,
18
19
  PollResponse,
19
20
  PollVotesResponse,
@@ -66,15 +67,26 @@ import {
66
67
  handleWatchStopped,
67
68
  } from '../feed';
68
69
  import { handleUserUpdated } from './event-handlers';
69
- import type { SyncFailure } from '../common/real-time/event-models';
70
- import { UnhandledErrorType } from '../common/real-time/event-models';
70
+ import {
71
+ type SyncFailure,
72
+ UnhandledErrorType,
73
+ } from '../common/real-time/event-models';
71
74
  import { updateCommentCount } from '../feed/event-handlers/comment/utils';
72
75
  import { configureLoggers } from '../utils';
73
76
  import { handleCommentReactionUpdated } from '../feed/event-handlers/comment/handle-comment-reaction-updated';
77
+ import {
78
+ throttle,
79
+ DEFAULT_BATCH_OWN_CAPABILITIES_THROTTLING_INTERVAL,
80
+ type GetBatchedOwnCapabilitiesThrottledCallback,
81
+ queueBatchedOwnCapabilities,
82
+ type ThrottledGetBatchedOwnCapabilities,
83
+ clearQueuedFeeds,
84
+ } from '../utils/throttling';
74
85
 
75
86
  export type FeedsClientState = {
76
87
  connected_user: OwnUser | undefined;
77
88
  is_ws_connection_healthy: boolean;
89
+ own_capabilities_by_fid: Record<string, FeedResponse['own_capabilities']>;
78
90
  };
79
91
 
80
92
  type FID = string;
@@ -97,6 +109,10 @@ export class FeedsClient extends FeedsApi {
97
109
 
98
110
  private healthyConnectionChangedEventCount = 0;
99
111
 
112
+ protected throttledGetBatchOwnCapabilities!: ThrottledGetBatchedOwnCapabilities;
113
+ private cancelGetBatchOwnCapabilitiesTimer!: () => void;
114
+ private query_batch_own_capabilties_throttling_interval!: number;
115
+
100
116
  constructor(apiKey: string, options?: FeedsClientOptions) {
101
117
  const tokenManager = new TokenManager();
102
118
  const connectionIdManager = new ConnectionIdManager();
@@ -110,12 +126,17 @@ export class FeedsClient extends FeedsApi {
110
126
  this.state = new StateStore<FeedsClientState>({
111
127
  connected_user: undefined,
112
128
  is_ws_connection_healthy: false,
129
+ own_capabilities_by_fid: {},
113
130
  });
114
131
  this.moderation = new ModerationClient(apiClient);
115
132
  this.tokenManager = tokenManager;
116
133
  this.connectionIdManager = connectionIdManager;
117
134
  this.polls_by_id = new Map();
118
135
 
136
+ this.query_batch_own_capabilties_throttling_interval =
137
+ options?.query_batch_own_capabilties_throttling_interval ??
138
+ DEFAULT_BATCH_OWN_CAPABILITIES_THROTTLING_INTERVAL;
139
+
119
140
  configureLoggers(options?.configure_loggers_options);
120
141
 
121
142
  this.on('all', (event) => {
@@ -231,6 +252,32 @@ export class FeedsClient extends FeedsApi {
231
252
  });
232
253
  }
233
254
 
255
+ private setGetBatchOwnCapabilitiesThrottlingInterval = (
256
+ throttlingMs: number,
257
+ ) => {
258
+ const {
259
+ throttledFn: throttledGetBatchOwnCapabilities,
260
+ cancelTimer: cancel,
261
+ } = throttle<GetBatchedOwnCapabilitiesThrottledCallback>(
262
+ (feeds, callback) => {
263
+ this.ownCapabilitiesBatch({
264
+ feeds,
265
+ }).catch((error) => {
266
+ this.eventDispatcher.dispatch({
267
+ type: 'errors.unhandled',
268
+ error_type: UnhandledErrorType.FetchingOwnCapabilitiesOnNewActivity,
269
+ error,
270
+ });
271
+ });
272
+ callback(feeds);
273
+ },
274
+ throttlingMs,
275
+ { trailing: true },
276
+ );
277
+ this.throttledGetBatchOwnCapabilities = throttledGetBatchOwnCapabilities;
278
+ this.cancelGetBatchOwnCapabilitiesTimer = cancel;
279
+ };
280
+
234
281
  private recoverOnReconnect = async () => {
235
282
  this.healthyConnectionChangedEventCount++;
236
283
 
@@ -276,6 +323,34 @@ export class FeedsClient extends FeedsApi {
276
323
  }
277
324
  }
278
325
 
326
+ public hydrateCapabilitiesCache(
327
+ feedResponses: Array<Pick<FeedResponse, 'feed' | 'own_capabilities'>>,
328
+ ) {
329
+ let ownCapabilitiesCache =
330
+ this.state.getLatestValue().own_capabilities_by_fid;
331
+
332
+ const capabilitiesToFetchQueue: string[] = [];
333
+
334
+ for (const feedResponse of feedResponses) {
335
+ const { feed, own_capabilities } = feedResponse;
336
+
337
+ if (!Object.prototype.hasOwnProperty.call(ownCapabilitiesCache, feed)) {
338
+ if (own_capabilities) {
339
+ ownCapabilitiesCache = {
340
+ ...ownCapabilitiesCache,
341
+ [feed]: own_capabilities,
342
+ };
343
+ } else {
344
+ capabilitiesToFetchQueue.push(feed);
345
+ }
346
+ }
347
+ }
348
+
349
+ queueBatchedOwnCapabilities.bind(this)({ feeds: capabilitiesToFetchQueue });
350
+
351
+ this.state.partialNext({ own_capabilities_by_fid: ownCapabilitiesCache });
352
+ }
353
+
279
354
  connectUser = async (user: UserRequest, tokenProvider: TokenOrProvider) => {
280
355
  if (
281
356
  this.state.getLatestValue().connected_user !== undefined ||
@@ -286,6 +361,10 @@ export class FeedsClient extends FeedsApi {
286
361
 
287
362
  this.tokenManager.setTokenOrProvider(tokenProvider);
288
363
 
364
+ this.setGetBatchOwnCapabilitiesThrottlingInterval(
365
+ this.query_batch_own_capabilties_throttling_interval,
366
+ );
367
+
289
368
  try {
290
369
  addConnectionEventListeners(this.updateNetworkConnectionStatus);
291
370
  this.wsConnection = new StableWSConnection(
@@ -516,23 +595,43 @@ export class FeedsClient extends FeedsApi {
516
595
 
517
596
  this.connectionIdManager.reset();
518
597
  this.tokenManager.reset();
598
+
599
+ // clear all caches
600
+ this.polls_by_id.clear();
601
+
519
602
  this.state.partialNext({
520
603
  connected_user: undefined,
521
604
  is_ws_connection_healthy: false,
605
+ own_capabilities_by_fid: {},
522
606
  });
607
+
608
+ this.cancelGetBatchOwnCapabilitiesTimer();
609
+ clearQueuedFeeds();
523
610
  };
524
611
 
525
612
  on = this.eventDispatcher.on;
526
613
  off = this.eventDispatcher.off;
527
614
 
528
- feed = (groupId: string, id: string) => {
529
- return this.getOrCreateActiveFeed(groupId, id);
615
+ feed = (
616
+ groupId: string,
617
+ id: string,
618
+ options?: { addNewActivitiesTo?: 'start' | 'end' },
619
+ ) => {
620
+ return this.getOrCreateActiveFeed(
621
+ groupId,
622
+ id,
623
+ undefined,
624
+ undefined,
625
+ options?.addNewActivitiesTo,
626
+ );
530
627
  };
531
628
 
532
629
  async queryFeeds(request?: QueryFeedsRequest) {
533
630
  const response = await this._queryFeeds(request);
534
631
 
535
- const feeds = response.feeds.map((feedResponse) =>
632
+ const feedResponses = response.feeds;
633
+
634
+ const feeds = feedResponses.map((feedResponse) =>
536
635
  this.getOrCreateActiveFeed(
537
636
  feedResponse.group_id,
538
637
  feedResponse.id,
@@ -541,6 +640,8 @@ export class FeedsClient extends FeedsApi {
541
640
  ),
542
641
  );
543
642
 
643
+ this.hydrateCapabilitiesCache(feedResponses);
644
+
544
645
  return {
545
646
  feeds,
546
647
  next: response.next,
@@ -550,6 +651,18 @@ export class FeedsClient extends FeedsApi {
550
651
  };
551
652
  }
552
653
 
654
+ async ownCapabilitiesBatch(request: OwnCapabilitiesBatchRequest) {
655
+ const response = await super.ownCapabilitiesBatch(request);
656
+ const feedResponses = Object.entries(response.capabilities).map(
657
+ ([feed, own_capabilities]) => ({
658
+ feed,
659
+ own_capabilities,
660
+ }),
661
+ );
662
+ this.hydrateCapabilitiesCache(feedResponses);
663
+ return response;
664
+ }
665
+
553
666
  updateNetworkConnectionStatus = (
554
667
  event: { type: 'online' | 'offline' } | Event,
555
668
  ) => {
@@ -640,11 +753,19 @@ export class FeedsClient extends FeedsApi {
640
753
  id: string,
641
754
  data?: FeedResponse,
642
755
  watch?: boolean,
756
+ addNewActivitiesTo?: 'start' | 'end',
643
757
  ) => {
644
758
  const fid = `${group}:${id}`;
645
759
 
646
760
  if (!this.activeFeeds[fid]) {
647
- this.activeFeeds[fid] = new Feed(this, group, id, data, watch);
761
+ this.activeFeeds[fid] = new Feed(
762
+ this,
763
+ group,
764
+ id,
765
+ data,
766
+ watch,
767
+ addNewActivitiesTo,
768
+ );
648
769
  }
649
770
 
650
771
  const feed = this.activeFeeds[fid];
@@ -170,6 +170,8 @@ export class FeedsApi {
170
170
  const body = {
171
171
  name: request?.name,
172
172
  words: request?.words,
173
+ is_leet_check_enabled: request?.is_leet_check_enabled,
174
+ is_plural_check_enabled: request?.is_plural_check_enabled,
173
175
  team: request?.team,
174
176
  type: request?.type,
175
177
  };
@@ -220,6 +222,8 @@ export class FeedsApi {
220
222
  name: request?.name,
221
223
  };
222
224
  const body = {
225
+ is_leet_check_enabled: request?.is_leet_check_enabled,
226
+ is_plural_check_enabled: request?.is_plural_check_enabled,
223
227
  team: request?.team,
224
228
  words: request?.words,
225
229
  };
@@ -499,6 +503,7 @@ export class FeedsApi {
499
503
  reason: request?.reason,
500
504
  report: request?.report,
501
505
  show_less: request?.show_less,
506
+ show_more: request?.show_more,
502
507
  };
503
508
 
504
509
  const response = await this.apiClient.sendRequest<
@@ -1233,9 +1233,11 @@ decoders.ModerationCustomActionEvent = (input?: Record<string, any>) => {
1233
1233
  const typeMappings: TypeMapping = {
1234
1234
  created_at: { type: 'DatetimeType', isSingle: true },
1235
1235
 
1236
- message: { type: 'Message', isSingle: true },
1236
+ review_queue_item: { type: 'ReviewQueueItemResponse', isSingle: true },
1237
1237
 
1238
- user: { type: 'User', isSingle: true },
1238
+ received_at: { type: 'DatetimeType', isSingle: true },
1239
+
1240
+ message: { type: 'MessageResponse', isSingle: true },
1239
1241
  };
1240
1242
  return decode(typeMappings, input);
1241
1243
  };
@@ -1266,9 +1268,11 @@ decoders.ModerationMarkReviewedEvent = (input?: Record<string, any>) => {
1266
1268
  const typeMappings: TypeMapping = {
1267
1269
  created_at: { type: 'DatetimeType', isSingle: true },
1268
1270
 
1269
- message: { type: 'Message', isSingle: true },
1271
+ item: { type: 'ReviewQueueItemResponse', isSingle: true },
1270
1272
 
1271
- user: { type: 'User', isSingle: true },
1273
+ received_at: { type: 'DatetimeType', isSingle: true },
1274
+
1275
+ message: { type: 'MessageResponse', isSingle: true },
1272
1276
  };
1273
1277
  return decode(typeMappings, input);
1274
1278
  };
@@ -1745,6 +1749,8 @@ decoders.StoriesFeedUpdatedEvent = (input?: Record<string, any>) => {
1745
1749
 
1746
1750
  received_at: { type: 'DatetimeType', isSingle: true },
1747
1751
 
1752
+ activities: { type: 'ActivityResponse', isSingle: false },
1753
+
1748
1754
  aggregated_activities: {
1749
1755
  type: 'AggregatedActivityResponse',
1750
1756
  isSingle: false,