@stream-io/feeds-client 0.1.1 → 0.1.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 (42) hide show
  1. package/@react-bindings/index.ts +13 -1
  2. package/CHANGELOG.md +18 -0
  3. package/dist/@react-bindings/contexts/StreamFeedsContext.d.ts +15 -0
  4. package/dist/@react-bindings/hooks/clientStateHooks.d.ts +10 -0
  5. package/dist/@react-bindings/hooks/useCreateFeedsClient.d.ts +13 -0
  6. package/dist/@react-bindings/hooks/useStateStore.d.ts +1 -1
  7. package/dist/@react-bindings/index.d.ts +4 -0
  8. package/dist/@react-bindings/wrappers/StreamFeeds.d.ts +6 -0
  9. package/dist/index-react-bindings.browser.cjs +5388 -12
  10. package/dist/index-react-bindings.browser.cjs.map +1 -1
  11. package/dist/index-react-bindings.browser.js +5384 -14
  12. package/dist/index-react-bindings.browser.js.map +1 -1
  13. package/dist/index-react-bindings.node.cjs +5388 -12
  14. package/dist/index-react-bindings.node.cjs.map +1 -1
  15. package/dist/index-react-bindings.node.js +5384 -14
  16. package/dist/index-react-bindings.node.js.map +1 -1
  17. package/dist/index.browser.cjs +97 -31
  18. package/dist/index.browser.cjs.map +1 -1
  19. package/dist/index.browser.js +97 -31
  20. package/dist/index.browser.js.map +1 -1
  21. package/dist/index.node.cjs +97 -31
  22. package/dist/index.node.cjs.map +1 -1
  23. package/dist/index.node.js +97 -31
  24. package/dist/index.node.js.map +1 -1
  25. package/dist/src/Feed.d.ts +2 -2
  26. package/dist/src/FeedsClient.d.ts +8 -5
  27. package/dist/src/common/StateStore.d.ts +1 -0
  28. package/dist/src/common/real-time/StableWSConnection.d.ts +3 -3
  29. package/dist/src/gen/feeds/FeedsApi.d.ts +7 -1
  30. package/dist/src/gen/models/index.d.ts +41 -0
  31. package/dist/src/types.d.ts +2 -0
  32. package/dist/tsconfig.tsbuildinfo +1 -1
  33. package/package.json +2 -6
  34. package/src/Feed.ts +19 -12
  35. package/src/FeedsClient.ts +24 -17
  36. package/src/common/StateStore.ts +30 -12
  37. package/src/common/utils.ts +1 -1
  38. package/src/gen/feeds/FeedsApi.ts +51 -0
  39. package/src/gen/model-decoders/decoders.ts +29 -0
  40. package/src/gen/model-decoders/event-decoder-mapping.ts +6 -0
  41. package/src/gen/models/index.ts +60 -0
  42. package/src/types.ts +4 -0
@@ -261,6 +261,14 @@ decoders.BookmarkDeletedEvent = (input) => {
261
261
  };
262
262
  return decode(typeMappings, input);
263
263
  };
264
+ decoders.BookmarkFolderDeletedEvent = (input) => {
265
+ const typeMappings = {
266
+ created_at: { type: 'DatetimeType', isSingle: true },
267
+ bookmark_folder: { type: 'BookmarkFolderResponse', isSingle: true },
268
+ received_at: { type: 'DatetimeType', isSingle: true },
269
+ };
270
+ return decode(typeMappings, input);
271
+ };
264
272
  decoders.BookmarkFolderResponse = (input) => {
265
273
  const typeMappings = {
266
274
  created_at: { type: 'DatetimeType', isSingle: true },
@@ -268,6 +276,14 @@ decoders.BookmarkFolderResponse = (input) => {
268
276
  };
269
277
  return decode(typeMappings, input);
270
278
  };
279
+ decoders.BookmarkFolderUpdatedEvent = (input) => {
280
+ const typeMappings = {
281
+ created_at: { type: 'DatetimeType', isSingle: true },
282
+ bookmark_folder: { type: 'BookmarkFolderResponse', isSingle: true },
283
+ received_at: { type: 'DatetimeType', isSingle: true },
284
+ };
285
+ return decode(typeMappings, input);
286
+ };
271
287
  decoders.BookmarkResponse = (input) => {
272
288
  const typeMappings = {
273
289
  created_at: { type: 'DatetimeType', isSingle: true },
@@ -1227,6 +1243,12 @@ decoders.UpdateBlockListResponse = (input) => {
1227
1243
  };
1228
1244
  return decode(typeMappings, input);
1229
1245
  };
1246
+ decoders.UpdateBookmarkFolderResponse = (input) => {
1247
+ const typeMappings = {
1248
+ bookmark_folder: { type: 'BookmarkFolderResponse', isSingle: true },
1249
+ };
1250
+ return decode(typeMappings, input);
1251
+ };
1230
1252
  decoders.UpdateBookmarkResponse = (input) => {
1231
1253
  const typeMappings = {
1232
1254
  bookmark: { type: 'BookmarkResponse', isSingle: true },
@@ -1647,6 +1669,26 @@ class FeedsApi {
1647
1669
  decoders.QueryBookmarkFoldersResponse?.(response.body);
1648
1670
  return { ...response.body, metadata: response.metadata };
1649
1671
  }
1672
+ async deleteBookmarkFolder(request) {
1673
+ const pathParams = {
1674
+ folder_id: request?.folder_id,
1675
+ };
1676
+ const response = await this.apiClient.sendRequest('DELETE', '/api/v2/feeds/bookmark_folders/{folder_id}', pathParams, undefined);
1677
+ decoders.DeleteBookmarkFolderResponse?.(response.body);
1678
+ return { ...response.body, metadata: response.metadata };
1679
+ }
1680
+ async updateBookmarkFolder(request) {
1681
+ const pathParams = {
1682
+ folder_id: request?.folder_id,
1683
+ };
1684
+ const body = {
1685
+ name: request?.name,
1686
+ custom: request?.custom,
1687
+ };
1688
+ const response = await this.apiClient.sendRequest('PATCH', '/api/v2/feeds/bookmark_folders/{folder_id}', pathParams, undefined, body, 'application/json');
1689
+ decoders.UpdateBookmarkFolderResponse?.(response.body);
1690
+ return { ...response.body, metadata: response.metadata };
1691
+ }
1650
1692
  async queryBookmarks(request) {
1651
1693
  const body = {
1652
1694
  limit: request?.limit,
@@ -2323,14 +2365,9 @@ class StateStore {
2323
2365
  let previouslySelectedValues;
2324
2366
  const wrappedHandler = (nextValue) => {
2325
2367
  const newlySelectedValues = selector(nextValue);
2326
- let hasUpdatedValues = typeof previouslySelectedValues === 'undefined';
2327
- for (const key in previouslySelectedValues) {
2328
- if (previouslySelectedValues[key] === newlySelectedValues[key])
2329
- continue;
2330
- hasUpdatedValues = true;
2331
- break;
2332
- }
2333
- if (!hasUpdatedValues)
2368
+ // shallow comparison of previouslySelectedValues and newlySelectedValues
2369
+ const selectionsAreEqual = StateStore.doSelectionsEqual(previouslySelectedValues, newlySelectedValues);
2370
+ if (selectionsAreEqual)
2334
2371
  return;
2335
2372
  // save a copy of previouslySelectedValues before running
2336
2373
  // handler - if previouslySelectedValues are set to
@@ -2361,7 +2398,7 @@ class StateStore {
2361
2398
  ? newValueOrPatch(this.value)
2362
2399
  : newValueOrPatch;
2363
2400
  // do not notify subscribers if the value hasn't changed
2364
- if (newValue === this.value)
2401
+ if (Object.is(newValue, this.value))
2365
2402
  return;
2366
2403
  this.preprocessors.forEach((preprocessor) => preprocessor(newValue, this.value));
2367
2404
  const oldValue = this.value;
@@ -2378,6 +2415,19 @@ class StateStore {
2378
2415
  this.handlers.delete(handler);
2379
2416
  };
2380
2417
  }
2418
+ static doSelectionsEqual(previouslySelectedValues, newlySelectedValues) {
2419
+ let selectionsAreEqual;
2420
+ if ((selectionsAreEqual = typeof previouslySelectedValues !== 'undefined')) {
2421
+ for (const key in newlySelectedValues) {
2422
+ if (Object.is(previouslySelectedValues[key], newlySelectedValues[key])) {
2423
+ continue;
2424
+ }
2425
+ selectionsAreEqual = false;
2426
+ break;
2427
+ }
2428
+ }
2429
+ return selectionsAreEqual;
2430
+ }
2381
2431
  /**
2382
2432
  * Registers a preprocessor function that will be called before the state is updated.
2383
2433
  *
@@ -2603,7 +2653,7 @@ function getRandomValuesWithMathRandom(bytes) {
2603
2653
  }
2604
2654
  }
2605
2655
  const getRandomValues = (() => {
2606
- if (typeof crypto?.getRandomValues !== 'undefined') {
2656
+ if (typeof crypto !== 'undefined' && typeof crypto?.getRandomValues !== 'undefined') {
2607
2657
  return crypto.getRandomValues.bind(crypto);
2608
2658
  }
2609
2659
  else if (typeof msCrypto !== 'undefined') {
@@ -3612,6 +3662,8 @@ const eventDecoderMapping = {
3612
3662
  'feeds.bookmark.added': (data) => decoders.BookmarkAddedEvent(data),
3613
3663
  'feeds.bookmark.deleted': (data) => decoders.BookmarkDeletedEvent(data),
3614
3664
  'feeds.bookmark.updated': (data) => decoders.BookmarkUpdatedEvent(data),
3665
+ 'feeds.bookmark_folder.deleted': (data) => decoders.BookmarkFolderDeletedEvent(data),
3666
+ 'feeds.bookmark_folder.updated': (data) => decoders.BookmarkFolderUpdatedEvent(data),
3615
3667
  'feeds.comment.added': (data) => decoders.CommentAddedEvent(data),
3616
3668
  'feeds.comment.deleted': (data) => decoders.CommentDeletedEvent(data),
3617
3669
  'feeds.comment.reaction.added': (data) => decoders.CommentReactionAddedEvent(data),
@@ -4014,6 +4066,8 @@ class Feed extends FeedApi {
4014
4066
  'feeds.bookmark.added': this.handleBookmarkAdded.bind(this),
4015
4067
  'feeds.bookmark.deleted': this.handleBookmarkDeleted.bind(this),
4016
4068
  'feeds.bookmark.updated': this.handleBookmarkUpdated.bind(this),
4069
+ 'feeds.bookmark_folder.deleted': Feed.noop,
4070
+ 'feeds.bookmark_folder.updated': Feed.noop,
4017
4071
  'feeds.comment.added': (event) => {
4018
4072
  const { comment } = event;
4019
4073
  const forId = comment.parent_id ?? comment.object_id;
@@ -4103,16 +4157,19 @@ class Feed extends FeedApi {
4103
4157
  return;
4104
4158
  // this feed followed someone
4105
4159
  if (event.follow.source_feed.fid === this.fid) {
4106
- if (this.currentState.following_pagination?.next === END_OF_LIST) {
4107
- this.state.next((currentState) => ({
4160
+ this.state.next((currentState) => {
4161
+ const newState = {
4108
4162
  ...currentState,
4109
4163
  ...event.follow.source_feed,
4164
+ };
4165
+ if (currentState.following_pagination?.next === END_OF_LIST) {
4110
4166
  // TODO: respect sort
4111
- following: currentState.following
4167
+ newState.following = currentState.following
4112
4168
  ? currentState.following.concat(event.follow)
4113
- : [event.follow],
4114
- }));
4115
- }
4169
+ : [event.follow];
4170
+ }
4171
+ return newState;
4172
+ });
4116
4173
  }
4117
4174
  else if (
4118
4175
  // someone followed this feed
@@ -4122,14 +4179,14 @@ class Feed extends FeedApi {
4122
4179
  this.state.next((currentState) => {
4123
4180
  const newState = { ...currentState, ...event.follow.target_feed };
4124
4181
  if (source.created_by.id === connectedUser?.id) {
4125
- newState.own_follows = newState.own_follows
4126
- ? newState.own_follows.concat(event.follow)
4182
+ newState.own_follows = currentState.own_follows
4183
+ ? currentState.own_follows.concat(event.follow)
4127
4184
  : [event.follow];
4128
4185
  }
4129
4186
  if (currentState.followers_pagination?.next === END_OF_LIST) {
4130
4187
  // TODO: respect sort
4131
- newState.followers = newState.followers
4132
- ? newState.followers.concat(event.follow)
4188
+ newState.followers = currentState.followers
4189
+ ? currentState.followers.concat(event.follow)
4133
4190
  : [event.follow];
4134
4191
  }
4135
4192
  return newState;
@@ -4155,9 +4212,9 @@ class Feed extends FeedApi {
4155
4212
  this.state.next((currentState) => {
4156
4213
  const newState = { ...currentState, ...event.follow.target_feed };
4157
4214
  if (source.created_by.id === connectedUser?.id) {
4158
- newState.own_follows = newState.own_follows?.filter((follow) => follow.source_feed.fid !== event.follow.source_feed.fid);
4215
+ newState.own_follows = currentState.own_follows?.filter((follow) => follow.source_feed.fid !== event.follow.source_feed.fid);
4159
4216
  }
4160
- newState.followers = newState.followers?.filter((follow) => follow.source_feed.fid !== event.follow.source_feed.fid);
4217
+ newState.followers = currentState.followers?.filter((follow) => follow.source_feed.fid !== event.follow.source_feed.fid);
4161
4218
  return newState;
4162
4219
  });
4163
4220
  }
@@ -5065,7 +5122,10 @@ class FeedsClient extends FeedsApi {
5065
5122
  }, this.tokenManager, this.connectionIdManager, [decodeWSEvent]);
5066
5123
  this.wsConnection.on('all', (event) => this.eventDispatcher.dispatch(event));
5067
5124
  const connectedEvent = await this.wsConnection.connect();
5068
- this.state.partialNext({ connectedUser: connectedEvent?.me });
5125
+ this.state.partialNext({
5126
+ connectedUser: connectedEvent?.me,
5127
+ isWsConnectionHealthy: this.wsConnection.isHealthy,
5128
+ });
5069
5129
  }
5070
5130
  catch (err) {
5071
5131
  await this.disconnectUser();
@@ -5120,13 +5180,20 @@ class FeedsClient extends FeedsApi {
5120
5180
  removeConnectionEventListeners(this.updateNetworkConnectionStatus);
5121
5181
  this.connectionIdManager.reset();
5122
5182
  this.tokenManager.reset();
5123
- this.state.partialNext({ connectedUser: undefined });
5183
+ this.state.partialNext({ connectedUser: undefined, isWsConnectionHealthy: false });
5124
5184
  };
5125
5185
  this.on = this.eventDispatcher.on;
5126
5186
  this.off = this.eventDispatcher.off;
5127
5187
  this.feed = (groupId, id) => {
5128
5188
  return this.getOrCreateActiveFeed(groupId, id);
5129
5189
  };
5190
+ this.updateNetworkConnectionStatus = (event) => {
5191
+ const networkEvent = {
5192
+ type: 'network.changed',
5193
+ online: event.type === 'online',
5194
+ };
5195
+ this.eventDispatcher.dispatch(networkEvent);
5196
+ };
5130
5197
  this.getOrCreateActiveFeed = (group, id, data) => {
5131
5198
  const fid = `${group}:${id}`;
5132
5199
  if (this.activeFeeds[fid]) {
@@ -5138,15 +5205,9 @@ class FeedsClient extends FeedsApi {
5138
5205
  return feed;
5139
5206
  }
5140
5207
  };
5141
- this.updateNetworkConnectionStatus = (event) => {
5142
- const networkEvent = {
5143
- type: 'network.changed',
5144
- online: event.type === 'online',
5145
- };
5146
- this.eventDispatcher.dispatch(networkEvent);
5147
- };
5148
5208
  this.state = new StateStore({
5149
5209
  connectedUser: undefined,
5210
+ isWsConnectionHealthy: false,
5150
5211
  });
5151
5212
  this.moderation = new ModerationClient(apiClient);
5152
5213
  this.tokenManager = tokenManager;
@@ -5156,6 +5217,11 @@ class FeedsClient extends FeedsApi {
5156
5217
  const fid = event.fid;
5157
5218
  const feed = typeof fid === 'string' ? this.activeFeeds[fid] : undefined;
5158
5219
  switch (event.type) {
5220
+ case 'connection.changed': {
5221
+ const { online } = event;
5222
+ this.state.partialNext({ isWsConnectionHealthy: online });
5223
+ break;
5224
+ }
5159
5225
  case 'feeds.feed.created': {
5160
5226
  if (feed)
5161
5227
  break;