@stream-io/feeds-client 0.2.1 → 0.2.3

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 (92) hide show
  1. package/@react-bindings/hooks/feed-state-hooks/index.ts +4 -0
  2. package/CHANGELOG.md +16 -0
  3. package/dist/@react-bindings/contexts/StreamSearchContext.d.ts +1 -1
  4. package/dist/@react-bindings/contexts/StreamSearchResultsContext.d.ts +1 -1
  5. package/dist/@react-bindings/hooks/feed-state-hooks/index.d.ts +4 -0
  6. package/dist/@react-bindings/hooks/feed-state-hooks/useAggregatedActivities.d.ts +11 -0
  7. package/dist/@react-bindings/hooks/feed-state-hooks/useIsAggregatedActivityRead.d.ts +6 -0
  8. package/dist/@react-bindings/hooks/feed-state-hooks/useIsAggregatedActivitySeen.d.ts +6 -0
  9. package/dist/@react-bindings/hooks/feed-state-hooks/useNotificationStatus.d.ts +13 -0
  10. package/dist/@react-bindings/hooks/search-state-hooks/useSearchQuery.d.ts +1 -1
  11. package/dist/@react-bindings/hooks/search-state-hooks/useSearchResult.d.ts +1 -1
  12. package/dist/@react-bindings/hooks/search-state-hooks/useSearchSources.d.ts +2 -2
  13. package/dist/@react-bindings/wrappers/StreamFeed.d.ts +1 -1
  14. package/dist/@react-bindings/wrappers/StreamSearch.d.ts +1 -1
  15. package/dist/@react-bindings/wrappers/StreamSearchResults.d.ts +1 -1
  16. package/dist/index-react-bindings.browser.cjs +178 -35
  17. package/dist/index-react-bindings.browser.cjs.map +1 -1
  18. package/dist/index-react-bindings.browser.js +175 -36
  19. package/dist/index-react-bindings.browser.js.map +1 -1
  20. package/dist/index-react-bindings.node.cjs +178 -35
  21. package/dist/index-react-bindings.node.cjs.map +1 -1
  22. package/dist/index-react-bindings.node.js +175 -36
  23. package/dist/index-react-bindings.node.js.map +1 -1
  24. package/dist/index.browser.cjs +328 -180
  25. package/dist/index.browser.cjs.map +1 -1
  26. package/dist/index.browser.js +328 -181
  27. package/dist/index.browser.js.map +1 -1
  28. package/dist/index.d.ts +1 -5
  29. package/dist/index.node.cjs +328 -180
  30. package/dist/index.node.cjs.map +1 -1
  31. package/dist/index.node.js +328 -181
  32. package/dist/index.node.js.map +1 -1
  33. package/dist/src/common/{ActivitySearchSource.d.ts → search/ActivitySearchSource.d.ts} +3 -3
  34. package/dist/src/common/{BaseSearchSource.d.ts → search/BaseSearchSource.d.ts} +41 -35
  35. package/dist/src/common/{FeedSearchSource.d.ts → search/FeedSearchSource.d.ts} +3 -3
  36. package/dist/src/common/{SearchController.d.ts → search/SearchController.d.ts} +1 -3
  37. package/dist/src/common/{UserSearchSource.d.ts → search/UserSearchSource.d.ts} +4 -4
  38. package/dist/src/common/search/index.d.ts +6 -0
  39. package/dist/src/common/search/types.d.ts +22 -0
  40. package/dist/src/common/types.d.ts +1 -0
  41. package/dist/src/feed/event-handlers/activity/handle-activity-deleted.d.ts +5 -12
  42. package/dist/src/feed/event-handlers/activity/handle-activity-marked.d.ts +11 -0
  43. package/dist/src/feed/event-handlers/activity/index.d.ts +1 -0
  44. package/dist/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.d.ts +8 -1
  45. package/dist/src/feed/feed.d.ts +2 -2
  46. package/dist/src/gen/models/index.d.ts +58 -26
  47. package/dist/tsconfig.tsbuildinfo +1 -1
  48. package/index.ts +1 -5
  49. package/package.json +1 -1
  50. package/src/common/{ActivitySearchSource.ts → search/ActivitySearchSource.ts} +3 -3
  51. package/src/common/{BaseSearchSource.ts → search/BaseSearchSource.ts} +137 -69
  52. package/src/common/{FeedSearchSource.ts → search/FeedSearchSource.ts} +3 -3
  53. package/src/common/{SearchController.ts → search/SearchController.ts} +2 -7
  54. package/src/common/{UserSearchSource.ts → search/UserSearchSource.ts} +3 -3
  55. package/src/common/search/index.ts +6 -0
  56. package/src/common/search/types.ts +21 -0
  57. package/src/common/types.ts +2 -0
  58. package/src/feed/event-handlers/activity/activity-marked-utils.test.ts +208 -0
  59. package/src/feed/event-handlers/activity/activity-utils.test.ts +2 -2
  60. package/src/feed/event-handlers/activity/handle-activity-added.test.ts +86 -0
  61. package/src/feed/event-handlers/activity/handle-activity-deleted.test.ts +117 -0
  62. package/src/feed/event-handlers/activity/handle-activity-deleted.ts +8 -4
  63. package/src/feed/event-handlers/activity/handle-activity-marked.ts +68 -0
  64. package/src/feed/event-handlers/activity/handle-activity-reaction-added.test.ts +15 -15
  65. package/src/feed/event-handlers/activity/handle-activity-reaction-deleted.test.ts +14 -14
  66. package/src/feed/event-handlers/activity/handle-activity-unpinned.test.ts +4 -3
  67. package/src/feed/event-handlers/activity/handle-activity-updated.test.ts +4 -4
  68. package/src/feed/event-handlers/activity/index.ts +2 -1
  69. package/src/feed/event-handlers/bookmark/handle-bookmark-added.test.ts +14 -14
  70. package/src/feed/event-handlers/bookmark/handle-bookmark-deleted.test.ts +14 -14
  71. package/src/feed/event-handlers/bookmark/handle-bookmark-updated.test.ts +16 -16
  72. package/src/feed/event-handlers/comment/handle-comment-added.test.ts +147 -0
  73. package/src/feed/event-handlers/comment/handle-comment-deleted.test.ts +133 -0
  74. package/src/feed/event-handlers/comment/handle-comment-deleted.ts +24 -10
  75. package/src/feed/event-handlers/comment/handle-comment-reaction.test.ts +315 -0
  76. package/src/feed/event-handlers/comment/handle-comment-updated.test.ts +131 -0
  77. package/src/feed/event-handlers/feed-member/handle-feed-member-added.test.ts +75 -0
  78. package/src/feed/event-handlers/feed-member/handle-feed-member-removed.test.ts +82 -0
  79. package/src/feed/event-handlers/feed-member/handle-feed-member-removed.ts +19 -9
  80. package/src/feed/event-handlers/feed-member/handle-feed-member-updated.test.ts +84 -0
  81. package/src/feed/event-handlers/follow/handle-follow-created.test.ts +7 -7
  82. package/src/feed/event-handlers/follow/handle-follow-deleted.test.ts +2 -2
  83. package/src/feed/event-handlers/follow/handle-follow-updated.test.ts +1 -1
  84. package/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.test.ts +120 -0
  85. package/src/feed/event-handlers/notification-feed/handle-notification-feed-updated.ts +47 -3
  86. package/src/feed/feed.ts +4 -2
  87. package/src/gen/feeds/FeedsApi.ts +6 -0
  88. package/src/gen/model-decoders/decoders.ts +12 -0
  89. package/src/gen/models/index.ts +90 -34
  90. package/src/test-utils/response-generators.ts +230 -0
  91. package/dist/src/test-utils/index.d.ts +0 -1
  92. package/dist/src/test-utils/response-generators.d.ts +0 -54
@@ -413,6 +413,7 @@ decoders.Command = (input) => {
413
413
  decoders.CommentAddedEvent = (input) => {
414
414
  const typeMappings = {
415
415
  created_at: { type: 'DatetimeType', isSingle: true },
416
+ activity: { type: 'ActivityResponse', isSingle: true },
416
417
  comment: { type: 'CommentResponse', isSingle: true },
417
418
  received_at: { type: 'DatetimeType', isSingle: true },
418
419
  };
@@ -429,6 +430,7 @@ decoders.CommentDeletedEvent = (input) => {
429
430
  decoders.CommentReactionAddedEvent = (input) => {
430
431
  const typeMappings = {
431
432
  created_at: { type: 'DatetimeType', isSingle: true },
433
+ activity: { type: 'ActivityResponse', isSingle: true },
432
434
  comment: { type: 'CommentResponse', isSingle: true },
433
435
  reaction: { type: 'FeedsReactionResponse', isSingle: true },
434
436
  received_at: { type: 'DatetimeType', isSingle: true },
@@ -447,6 +449,7 @@ decoders.CommentReactionDeletedEvent = (input) => {
447
449
  decoders.CommentReactionUpdatedEvent = (input) => {
448
450
  const typeMappings = {
449
451
  created_at: { type: 'DatetimeType', isSingle: true },
452
+ activity: { type: 'ActivityResponse', isSingle: true },
450
453
  comment: { type: 'CommentResponse', isSingle: true },
451
454
  reaction: { type: 'FeedsReactionResponse', isSingle: true },
452
455
  received_at: { type: 'DatetimeType', isSingle: true },
@@ -809,6 +812,7 @@ decoders.Message = (input) => {
809
812
  pin_expires: { type: 'DatetimeType', isSingle: true },
810
813
  pinned_at: { type: 'DatetimeType', isSingle: true },
811
814
  thread_participants: { type: 'User', isSingle: false },
815
+ member: { type: 'ChannelMember', isSingle: true },
812
816
  pinned_by: { type: 'User', isSingle: true },
813
817
  poll: { type: 'Poll', isSingle: true },
814
818
  quoted_message: { type: 'Message', isSingle: true },
@@ -862,6 +866,8 @@ decoders.ModerationCustomActionEvent = (input) => {
862
866
  };
863
867
  decoders.ModerationFlagResponse = (input) => {
864
868
  const typeMappings = {
869
+ created_at: { type: 'DatetimeType', isSingle: true },
870
+ updated_at: { type: 'DatetimeType', isSingle: true },
865
871
  review_queue_item: { type: 'ReviewQueueItemResponse', isSingle: true },
866
872
  user: { type: 'UserResponse', isSingle: true },
867
873
  };
@@ -1628,6 +1634,7 @@ class FeedsApi {
1628
1634
  const body = {
1629
1635
  type: request?.type,
1630
1636
  create_notification_activity: request?.create_notification_activity,
1637
+ skip_push: request?.skip_push,
1631
1638
  custom: request?.custom,
1632
1639
  };
1633
1640
  const response = await this.apiClient.sendRequest('POST', '/api/v2/feeds/activities/{activity_id}/reactions', pathParams, undefined, body, 'application/json');
@@ -1774,6 +1781,7 @@ class FeedsApi {
1774
1781
  object_type: request?.object_type,
1775
1782
  create_notification_activity: request?.create_notification_activity,
1776
1783
  parent_id: request?.parent_id,
1784
+ skip_push: request?.skip_push,
1777
1785
  attachments: request?.attachments,
1778
1786
  mentioned_user_ids: request?.mentioned_user_ids,
1779
1787
  custom: request?.custom,
@@ -1827,6 +1835,7 @@ class FeedsApi {
1827
1835
  };
1828
1836
  const body = {
1829
1837
  comment: request?.comment,
1838
+ skip_push: request?.skip_push,
1830
1839
  custom: request?.custom,
1831
1840
  };
1832
1841
  const response = await this.apiClient.sendRequest('PATCH', '/api/v2/feeds/comments/{id}', pathParams, undefined, body, 'application/json');
@@ -1840,6 +1849,7 @@ class FeedsApi {
1840
1849
  const body = {
1841
1850
  type: request?.type,
1842
1851
  create_notification_activity: request?.create_notification_activity,
1852
+ skip_push: request?.skip_push,
1843
1853
  custom: request?.custom,
1844
1854
  };
1845
1855
  const response = await this.apiClient.sendRequest('POST', '/api/v2/feeds/comments/{id}/reactions', pathParams, undefined, body, 'application/json');
@@ -2080,6 +2090,7 @@ class FeedsApi {
2080
2090
  create_notification_activity: request?.create_notification_activity,
2081
2091
  follower_role: request?.follower_role,
2082
2092
  push_preference: request?.push_preference,
2093
+ skip_push: request?.skip_push,
2083
2094
  custom: request?.custom,
2084
2095
  };
2085
2096
  const response = await this.apiClient.sendRequest('PATCH', '/api/v2/feeds/follows', undefined, undefined, body, 'application/json');
@@ -2092,6 +2103,7 @@ class FeedsApi {
2092
2103
  target: request?.target,
2093
2104
  create_notification_activity: request?.create_notification_activity,
2094
2105
  push_preference: request?.push_preference,
2106
+ skip_push: request?.skip_push,
2095
2107
  custom: request?.custom,
2096
2108
  };
2097
2109
  const response = await this.apiClient.sendRequest('POST', '/api/v2/feeds/follows', undefined, undefined, body, 'application/json');
@@ -4518,20 +4530,29 @@ function handleCommentAdded(event) {
4518
4530
  function handleCommentDeleted({ comment }) {
4519
4531
  const entityId = comment.parent_id ?? comment.object_id;
4520
4532
  this.state.next((currentState) => {
4521
- const newCommentsByEntityId = {
4522
- ...currentState.comments_by_entity_id,
4523
- [entityId]: {
4524
- ...currentState.comments_by_entity_id[entityId],
4525
- },
4526
- };
4533
+ let newCommentsByEntityId;
4527
4534
  const index = this.getCommentIndex(comment, currentState);
4528
- if (newCommentsByEntityId?.[entityId]?.comments?.length && index !== -1) {
4535
+ if (index !== -1) {
4536
+ newCommentsByEntityId ?? (newCommentsByEntityId = {
4537
+ ...currentState.comments_by_entity_id,
4538
+ [entityId]: {
4539
+ ...currentState.comments_by_entity_id[entityId],
4540
+ },
4541
+ });
4529
4542
  newCommentsByEntityId[entityId].comments = [
4530
4543
  ...newCommentsByEntityId[entityId].comments,
4531
4544
  ];
4532
4545
  newCommentsByEntityId[entityId]?.comments?.splice(index, 1);
4533
4546
  }
4534
- delete newCommentsByEntityId[comment.id];
4547
+ if (typeof currentState.comments_by_entity_id[comment.id] !== 'undefined') {
4548
+ newCommentsByEntityId ?? (newCommentsByEntityId = {
4549
+ ...currentState.comments_by_entity_id,
4550
+ });
4551
+ delete newCommentsByEntityId[comment.id];
4552
+ }
4553
+ if (!newCommentsByEntityId) {
4554
+ return currentState;
4555
+ }
4535
4556
  return {
4536
4557
  ...currentState,
4537
4558
  comments_by_entity_id: newCommentsByEntityId,
@@ -4652,14 +4673,20 @@ function handleFeedMemberUpdated(event) {
4652
4673
  function handleFeedMemberRemoved(event) {
4653
4674
  const { connected_user: connectedUser } = this.client.state.getLatestValue();
4654
4675
  this.state.next((currentState) => {
4655
- const newState = {
4656
- ...currentState,
4657
- members: currentState.members?.filter((member) => member.user.id !== event.user?.id),
4658
- };
4659
- if (connectedUser?.id === event.member_id) {
4676
+ let newState;
4677
+ if (typeof currentState.members !== 'undefined') {
4678
+ const filtered = currentState.members.filter((member) => member.user.id !== event.member_id);
4679
+ if (filtered.length !== currentState.members.length) {
4680
+ newState ?? (newState = { ...currentState });
4681
+ newState.members = filtered;
4682
+ }
4683
+ }
4684
+ if (connectedUser?.id === event.member_id &&
4685
+ typeof currentState.own_membership !== 'undefined') {
4686
+ newState ?? (newState = { ...currentState });
4660
4687
  delete newState.own_membership;
4661
4688
  }
4662
- return newState;
4689
+ return newState ?? currentState;
4663
4690
  });
4664
4691
  }
4665
4692
 
@@ -4896,7 +4923,7 @@ const removePinnedActivityFromState = (activityResponse, pinnedActivities) => {
4896
4923
  if (index !== -1) {
4897
4924
  const newActivities = [...pinnedActivities];
4898
4925
  newActivities.splice(index, 1);
4899
- return { changed: true, activities: newActivities };
4926
+ return { changed: true, pinned_activities: newActivities };
4900
4927
  }
4901
4928
  else {
4902
4929
  return { changed: false, pinned_activities: pinnedActivities };
@@ -5086,13 +5113,80 @@ function handleActivityReactionDeleted(event) {
5086
5113
  }
5087
5114
  }
5088
5115
 
5116
+ const updateNotificationStatusFromActivityMarked = (event, currentNotificationStatus, aggregatedActivities = []) => {
5117
+ if (!currentNotificationStatus) {
5118
+ return {
5119
+ changed: false,
5120
+ };
5121
+ }
5122
+ const newState = {
5123
+ ...currentNotificationStatus,
5124
+ };
5125
+ if (event.mark_all_read) {
5126
+ const allGroupIds = aggregatedActivities.map((activity) => activity.group);
5127
+ newState.read_activities = [
5128
+ ...new Set([
5129
+ ...(currentNotificationStatus.read_activities ?? []),
5130
+ ...allGroupIds,
5131
+ ]),
5132
+ ];
5133
+ }
5134
+ if (event.mark_read && event.mark_read.length > 0) {
5135
+ newState.read_activities = [
5136
+ ...new Set([
5137
+ ...(currentNotificationStatus?.read_activities ?? []),
5138
+ ...event.mark_read,
5139
+ ]),
5140
+ ];
5141
+ }
5142
+ if (event.mark_all_seen) {
5143
+ newState.last_seen_at = new Date();
5144
+ }
5145
+ return {
5146
+ changed: true,
5147
+ data: { notification_status: newState },
5148
+ };
5149
+ };
5150
+ function handleActivityMarked(event) {
5151
+ const result = updateNotificationStatusFromActivityMarked(event, this.currentState.notification_status, this.currentState.aggregated_activities);
5152
+ if (result.changed) {
5153
+ this.state.partialNext({
5154
+ notification_status: result.data?.notification_status,
5155
+ });
5156
+ }
5157
+ }
5158
+
5089
5159
  function handleFeedUpdated(event) {
5090
5160
  this.state.partialNext({ ...event.feed });
5091
5161
  }
5092
5162
 
5163
+ const updateNotificationFeedFromEvent = (event) => {
5164
+ const updates = {};
5165
+ if (event.notification_status) {
5166
+ updates.notification_status = event.notification_status;
5167
+ }
5168
+ if (event.aggregated_activities) {
5169
+ updates.aggregated_activities = event.aggregated_activities;
5170
+ }
5171
+ // Only return changed if we have actual updates
5172
+ if (Object.keys(updates).length > 0) {
5173
+ return {
5174
+ changed: true,
5175
+ data: updates,
5176
+ };
5177
+ }
5178
+ return {
5179
+ changed: false,
5180
+ };
5181
+ };
5093
5182
  function handleNotificationFeedUpdated(event) {
5094
- console.info('notification feed updated', event);
5095
- // TODO: handle notification feed updates
5183
+ const result = updateNotificationFeedFromEvent(event);
5184
+ if (result.changed) {
5185
+ this.state.partialNext({
5186
+ notification_status: result.data?.notification_status,
5187
+ aggregated_activities: result.data?.aggregated_activities,
5188
+ });
5189
+ }
5096
5190
  }
5097
5191
 
5098
5192
  function handleWatchStarted() {
@@ -5147,7 +5241,7 @@ class Feed extends FeedApi {
5147
5241
  'feeds.poll.vote_removed': Feed.noop,
5148
5242
  'feeds.activity.pinned': Feed.noop,
5149
5243
  'feeds.activity.unpinned': Feed.noop,
5150
- 'feeds.activity.marked': Feed.noop,
5244
+ 'feeds.activity.marked': handleActivityMarked.bind(this),
5151
5245
  'moderation.custom_action': Feed.noop,
5152
5246
  'moderation.flagged': Feed.noop,
5153
5247
  'moderation.mark_reviewed': Feed.noop,
@@ -6033,16 +6127,120 @@ const FeedOwnCapability = {
6033
6127
  UPDATE_FEED_MEMBERS: 'update-feed-members',
6034
6128
  };
6035
6129
 
6130
+ class SearchController {
6131
+ constructor({ config, sources } = {}) {
6132
+ this.addSource = (source) => {
6133
+ this.state.partialNext({
6134
+ sources: [...this.sources, source],
6135
+ });
6136
+ };
6137
+ this.getSource = (sourceType) => this.sources.find((s) => s.type === sourceType);
6138
+ this.removeSource = (sourceType) => {
6139
+ const newSources = this.sources.filter((s) => s.type !== sourceType);
6140
+ if (newSources.length === this.sources.length)
6141
+ return;
6142
+ this.state.partialNext({ sources: newSources });
6143
+ };
6144
+ this.activateSource = (sourceType) => {
6145
+ const source = this.getSource(sourceType);
6146
+ if (!source || source.isActive)
6147
+ return;
6148
+ if (this.config.keepSingleActiveSource) {
6149
+ this.sources.forEach((s) => {
6150
+ if (s.type !== sourceType) {
6151
+ s.deactivate();
6152
+ }
6153
+ });
6154
+ }
6155
+ source.activate();
6156
+ this.state.partialNext({ sources: [...this.sources] });
6157
+ };
6158
+ this.deactivateSource = (sourceType) => {
6159
+ const source = this.getSource(sourceType);
6160
+ if (!source?.isActive)
6161
+ return;
6162
+ if (this.activeSources.length === 1)
6163
+ return;
6164
+ source.deactivate();
6165
+ this.state.partialNext({ sources: [...this.sources] });
6166
+ };
6167
+ this.activate = () => {
6168
+ if (!this.activeSources.length) {
6169
+ const sourcesToActivate = this.config.keepSingleActiveSource
6170
+ ? this.sources.slice(0, 1)
6171
+ : this.sources;
6172
+ sourcesToActivate.forEach((s) => s.activate());
6173
+ }
6174
+ if (this.isActive)
6175
+ return;
6176
+ this.state.partialNext({ isActive: true });
6177
+ };
6178
+ this.search = async (searchQuery) => {
6179
+ const searchedSources = this.activeSources;
6180
+ this.state.partialNext({
6181
+ searchQuery,
6182
+ });
6183
+ await Promise.all(searchedSources.map((source) => source.search(searchQuery)));
6184
+ };
6185
+ this.cancelSearchQueries = () => {
6186
+ this.activeSources.forEach((s) => s.cancelScheduledQuery());
6187
+ };
6188
+ this.clear = () => {
6189
+ this.cancelSearchQueries();
6190
+ this.sources.forEach((source) => source.state.next({ ...source.initialState, isActive: source.isActive }));
6191
+ this.state.next((current) => ({
6192
+ ...current,
6193
+ isActive: true,
6194
+ queriesInProgress: [],
6195
+ searchQuery: '',
6196
+ }));
6197
+ };
6198
+ this.exit = () => {
6199
+ this.cancelSearchQueries();
6200
+ this.sources.forEach((source) => source.state.next({ ...source.initialState, isActive: source.isActive }));
6201
+ this.state.next((current) => ({
6202
+ ...current,
6203
+ isActive: false,
6204
+ queriesInProgress: [],
6205
+ searchQuery: '',
6206
+ }));
6207
+ };
6208
+ this.state = new StateStore({
6209
+ isActive: false,
6210
+ searchQuery: '',
6211
+ sources: sources ?? [],
6212
+ });
6213
+ this._internalState = new StateStore({});
6214
+ this.config = { keepSingleActiveSource: true, ...config };
6215
+ }
6216
+ get hasNext() {
6217
+ return this.sources.some((source) => source.hasNext);
6218
+ }
6219
+ get sources() {
6220
+ return this.state.getLatestValue().sources;
6221
+ }
6222
+ get activeSources() {
6223
+ return this.state.getLatestValue().sources.filter((s) => s.isActive);
6224
+ }
6225
+ get isActive() {
6226
+ return this.state.getLatestValue().isActive;
6227
+ }
6228
+ get searchQuery() {
6229
+ return this.state.getLatestValue().searchQuery;
6230
+ }
6231
+ get searchSourceTypes() {
6232
+ return this.sources.map((s) => s.type);
6233
+ }
6234
+ }
6235
+
6036
6236
  const DEFAULT_SEARCH_SOURCE_OPTIONS = {
6037
6237
  debounceMs: 300,
6038
6238
  pageSize: 10,
6039
6239
  allowEmptySearchString: false,
6240
+ resetOnNewSearchQuery: true,
6040
6241
  };
6041
- class BaseSearchSource {
6242
+ class BaseSearchSourceBase {
6042
6243
  constructor(options) {
6043
- this.setDebounceOptions = ({ debounceMs }) => {
6044
- this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
6045
- };
6046
6244
  this.activate = () => {
6047
6245
  if (this.isActive)
6048
6246
  return;
@@ -6061,15 +6259,11 @@ class BaseSearchSource {
6061
6259
  (this.hasNext || hasNewSearchQuery) &&
6062
6260
  (this.allowEmptySearchString || searchString));
6063
6261
  };
6064
- this.search = (searchQuery) => this.searchDebounced(searchQuery);
6065
- const { debounceMs, pageSize, allowEmptySearchString } = {
6066
- ...DEFAULT_SEARCH_SOURCE_OPTIONS,
6067
- ...options,
6068
- };
6262
+ const { pageSize, allowEmptySearchString, resetOnNewSearchQuery } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
6069
6263
  this.pageSize = pageSize;
6070
6264
  this.allowEmptySearchString = allowEmptySearchString;
6265
+ this.resetOnNewSearchQuery = resetOnNewSearchQuery;
6071
6266
  this.state = new StateStore(this.initialState);
6072
- this.setDebounceOptions({ debounceMs });
6073
6267
  }
6074
6268
  get lastQueryError() {
6075
6269
  return this.state.getLatestValue().lastQueryError;
@@ -6111,10 +6305,14 @@ class BaseSearchSource {
6111
6305
  return this.state.getLatestValue().searchQuery;
6112
6306
  }
6113
6307
  getStateBeforeFirstQuery(newSearchString) {
6308
+ const initialState = this.initialState;
6309
+ const oldItems = this.items;
6310
+ const items = this.resetOnNewSearchQuery ? initialState.items : oldItems;
6114
6311
  return {
6115
6312
  ...this.initialState,
6313
+ items,
6116
6314
  isActive: this.isActive,
6117
- isLoading: true,
6315
+ isLoading: this.resetOnNewSearchQuery ? true : !oldItems,
6118
6316
  searchQuery: newSearchString,
6119
6317
  };
6120
6318
  }
@@ -6127,12 +6325,10 @@ class BaseSearchSource {
6127
6325
  isLoading: false,
6128
6326
  items: isFirstPage
6129
6327
  ? stateUpdate.items
6130
- : [...(this.items ?? []), ...(stateUpdate.items ?? [])],
6328
+ : [...(this.items ?? []), ...(stateUpdate.items || [])],
6131
6329
  };
6132
6330
  }
6133
- async executeQuery(newSearchString) {
6134
- if (!this.canExecuteQuery(newSearchString))
6135
- return;
6331
+ prepareStateForQuery(newSearchString) {
6136
6332
  const hasNewSearchQuery = typeof newSearchString !== 'undefined';
6137
6333
  const searchString = newSearchString ?? this.searchQuery;
6138
6334
  if (hasNewSearchQuery) {
@@ -6141,20 +6337,50 @@ class BaseSearchSource {
6141
6337
  else {
6142
6338
  this.state.partialNext({ isLoading: true });
6143
6339
  }
6340
+ return { searchString, hasNewSearchQuery };
6341
+ }
6342
+ updatePaginationStateFromQuery(result) {
6343
+ const { items, next } = result;
6144
6344
  const stateUpdate = {};
6345
+ if (next || next === null) {
6346
+ stateUpdate.next = next;
6347
+ stateUpdate.hasNext = !!next;
6348
+ }
6349
+ else {
6350
+ stateUpdate.offset = (this.offset ?? 0) + items.length;
6351
+ stateUpdate.hasNext = items.length === this.pageSize;
6352
+ }
6353
+ return stateUpdate;
6354
+ }
6355
+ resetState() {
6356
+ this.state.next(this.initialState);
6357
+ }
6358
+ resetStateAndActivate() {
6359
+ this.resetState();
6360
+ this.activate();
6361
+ }
6362
+ }
6363
+ class BaseSearchSource extends BaseSearchSourceBase {
6364
+ constructor(options) {
6365
+ const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
6366
+ super(options);
6367
+ this.setDebounceOptions = ({ debounceMs }) => {
6368
+ this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
6369
+ };
6370
+ this.search = (searchQuery) => this.searchDebounced(searchQuery);
6371
+ this.setDebounceOptions({ debounceMs });
6372
+ }
6373
+ async executeQuery(newSearchString) {
6374
+ if (!this.canExecuteQuery(newSearchString))
6375
+ return;
6376
+ const { hasNewSearchQuery, searchString } = this.prepareStateForQuery(newSearchString);
6377
+ let stateUpdate = {};
6145
6378
  try {
6146
6379
  const results = await this.query(searchString);
6147
6380
  if (!results)
6148
6381
  return;
6149
- const { items, next } = results;
6150
- if (typeof next === 'string' || next === null) {
6151
- stateUpdate.next = next;
6152
- stateUpdate.hasNext = !!next;
6153
- }
6154
- else {
6155
- stateUpdate.offset = (this.offset ?? 0) + items.length;
6156
- stateUpdate.hasNext = items.length === this.pageSize;
6157
- }
6382
+ const { items } = results;
6383
+ stateUpdate = this.updatePaginationStateFromQuery(results);
6158
6384
  stateUpdate.items = await this.filterQueryResults(items);
6159
6385
  }
6160
6386
  catch (e) {
@@ -6167,118 +6393,39 @@ class BaseSearchSource {
6167
6393
  cancelScheduledQuery() {
6168
6394
  this.searchDebounced.cancel();
6169
6395
  }
6170
- resetState() {
6171
- this.state.next(this.initialState);
6172
- }
6173
- resetStateAndActivate() {
6174
- this.resetState();
6175
- this.activate();
6176
- }
6177
6396
  }
6178
-
6179
- class SearchController {
6180
- constructor({ config, sources } = {}) {
6181
- this.addSource = (source) => {
6182
- this.state.partialNext({
6183
- sources: [...this.sources, source],
6184
- });
6185
- };
6186
- this.getSource = (sourceType) => this.sources.find((s) => s.type === sourceType);
6187
- this.removeSource = (sourceType) => {
6188
- const newSources = this.sources.filter((s) => s.type !== sourceType);
6189
- if (newSources.length === this.sources.length)
6190
- return;
6191
- this.state.partialNext({ sources: newSources });
6192
- };
6193
- this.activateSource = (sourceType) => {
6194
- const source = this.getSource(sourceType);
6195
- if (!source || source.isActive)
6196
- return;
6197
- if (this.config.keepSingleActiveSource) {
6198
- this.sources.forEach((s) => {
6199
- if (s.type !== sourceType) {
6200
- s.deactivate();
6201
- }
6202
- });
6203
- }
6204
- source.activate();
6205
- this.state.partialNext({ sources: [...this.sources] });
6206
- };
6207
- this.deactivateSource = (sourceType) => {
6208
- const source = this.getSource(sourceType);
6209
- if (!source?.isActive)
6210
- return;
6211
- if (this.activeSources.length === 1)
6212
- return;
6213
- source.deactivate();
6214
- this.state.partialNext({ sources: [...this.sources] });
6215
- };
6216
- this.activate = () => {
6217
- if (!this.activeSources.length) {
6218
- const sourcesToActivate = this.config.keepSingleActiveSource
6219
- ? this.sources.slice(0, 1)
6220
- : this.sources;
6221
- sourcesToActivate.forEach((s) => s.activate());
6222
- }
6223
- if (this.isActive)
6224
- return;
6225
- this.state.partialNext({ isActive: true });
6226
- };
6227
- this.search = async (searchQuery) => {
6228
- const searchedSources = this.activeSources;
6229
- this.state.partialNext({
6230
- searchQuery,
6231
- });
6232
- await Promise.all(searchedSources.map((source) => source.search(searchQuery)));
6233
- };
6234
- this.cancelSearchQueries = () => {
6235
- this.activeSources.forEach((s) => s.cancelScheduledQuery());
6236
- };
6237
- this.clear = () => {
6238
- this.cancelSearchQueries();
6239
- this.sources.forEach((source) => source.state.next({ ...source.initialState, isActive: source.isActive }));
6240
- this.state.next((current) => ({
6241
- ...current,
6242
- isActive: true,
6243
- queriesInProgress: [],
6244
- searchQuery: '',
6245
- }));
6246
- };
6247
- this.exit = () => {
6248
- this.cancelSearchQueries();
6249
- this.sources.forEach((source) => source.state.next({ ...source.initialState, isActive: source.isActive }));
6250
- this.state.next((current) => ({
6251
- ...current,
6252
- isActive: false,
6253
- queriesInProgress: [],
6254
- searchQuery: '',
6255
- }));
6397
+ class BaseSearchSourceSync extends BaseSearchSourceBase {
6398
+ constructor(options) {
6399
+ const { debounceMs } = { ...DEFAULT_SEARCH_SOURCE_OPTIONS, ...options };
6400
+ super(options);
6401
+ this.setDebounceOptions = ({ debounceMs }) => {
6402
+ this.searchDebounced = debounce(this.executeQuery.bind(this), debounceMs);
6256
6403
  };
6257
- this.state = new StateStore({
6258
- isActive: false,
6259
- searchQuery: '',
6260
- sources: sources ?? [],
6261
- });
6262
- this._internalState = new StateStore({});
6263
- this.config = { keepSingleActiveSource: true, ...config };
6264
- }
6265
- get hasNext() {
6266
- return this.sources.some((source) => source.hasNext);
6267
- }
6268
- get sources() {
6269
- return this.state.getLatestValue().sources;
6270
- }
6271
- get activeSources() {
6272
- return this.state.getLatestValue().sources.filter((s) => s.isActive);
6273
- }
6274
- get isActive() {
6275
- return this.state.getLatestValue().isActive;
6404
+ this.search = (searchQuery) => this.searchDebounced(searchQuery);
6405
+ this.setDebounceOptions({ debounceMs });
6276
6406
  }
6277
- get searchQuery() {
6278
- return this.state.getLatestValue().searchQuery;
6407
+ executeQuery(newSearchString) {
6408
+ if (!this.canExecuteQuery(newSearchString))
6409
+ return;
6410
+ const { hasNewSearchQuery, searchString } = this.prepareStateForQuery(newSearchString);
6411
+ let stateUpdate = {};
6412
+ try {
6413
+ const results = this.query(searchString);
6414
+ if (!results)
6415
+ return;
6416
+ const { items } = results;
6417
+ stateUpdate = this.updatePaginationStateFromQuery(results);
6418
+ stateUpdate.items = this.filterQueryResults(items);
6419
+ }
6420
+ catch (e) {
6421
+ stateUpdate.lastQueryError = e;
6422
+ }
6423
+ finally {
6424
+ this.state.next(this.getStateAfterQuery(stateUpdate, hasNewSearchQuery));
6425
+ }
6279
6426
  }
6280
- get searchSourceTypes() {
6281
- return this.sources.map((s) => s.type);
6427
+ cancelScheduledQuery() {
6428
+ this.searchDebounced.cancel();
6282
6429
  }
6283
6430
  }
6284
6431
 
@@ -6307,36 +6454,6 @@ class ActivitySearchSource extends BaseSearchSource {
6307
6454
  }
6308
6455
  }
6309
6456
 
6310
- class UserSearchSource extends BaseSearchSource {
6311
- constructor(client, options) {
6312
- super(options);
6313
- this.type = 'user';
6314
- this.client = client;
6315
- }
6316
- async query(searchQuery) {
6317
- const { connected_user: connectedUser } = this.client.state.getLatestValue();
6318
- if (!connectedUser)
6319
- return { items: [] };
6320
- const { users: items } = await this.client.queryUsers({
6321
- payload: {
6322
- filter_conditions: {
6323
- ...(!this.allowEmptySearchString || searchQuery.length > 0
6324
- ? {
6325
- name: {
6326
- $autocomplete: searchQuery,
6327
- },
6328
- }
6329
- : {}),
6330
- },
6331
- },
6332
- });
6333
- return { items, next: undefined };
6334
- }
6335
- filterQueryResults(items) {
6336
- return items;
6337
- }
6338
- }
6339
-
6340
6457
  class FeedSearchSource extends BaseSearchSource {
6341
6458
  constructor(client, options) {
6342
6459
  super(options);
@@ -6370,5 +6487,35 @@ class FeedSearchSource extends BaseSearchSource {
6370
6487
  }
6371
6488
  }
6372
6489
 
6373
- export { ActivitySearchSource, BaseSearchSource, ChannelOwnCapability, Constants, Feed, FeedOwnCapability, FeedSearchSource, FeedsClient, MergedStateStore, SearchController, StateStore, StreamApiError, StreamPoll, UserSearchSource, checkHasAnotherPage, getStateUpdateQueueId, isCommentResponse, isFollowResponse, isImageFile, isPatch, isVideoFile, isVoteAnswer, shouldUpdateState, uniqueArrayMerge, updateEntityInArray };
6490
+ class UserSearchSource extends BaseSearchSource {
6491
+ constructor(client, options) {
6492
+ super(options);
6493
+ this.type = 'user';
6494
+ this.client = client;
6495
+ }
6496
+ async query(searchQuery) {
6497
+ const { connected_user: connectedUser } = this.client.state.getLatestValue();
6498
+ if (!connectedUser)
6499
+ return { items: [] };
6500
+ const { users: items } = await this.client.queryUsers({
6501
+ payload: {
6502
+ filter_conditions: {
6503
+ ...(!this.allowEmptySearchString || searchQuery.length > 0
6504
+ ? {
6505
+ name: {
6506
+ $autocomplete: searchQuery,
6507
+ },
6508
+ }
6509
+ : {}),
6510
+ },
6511
+ },
6512
+ });
6513
+ return { items, next: undefined };
6514
+ }
6515
+ filterQueryResults(items) {
6516
+ return items;
6517
+ }
6518
+ }
6519
+
6520
+ export { ActivitySearchSource, BaseSearchSource, BaseSearchSourceSync, ChannelOwnCapability, Constants, Feed, FeedOwnCapability, FeedSearchSource, FeedsClient, MergedStateStore, SearchController, StateStore, StreamApiError, StreamPoll, UserSearchSource, checkHasAnotherPage, getStateUpdateQueueId, isCommentResponse, isFollowResponse, isImageFile, isPatch, isVideoFile, isVoteAnswer, shouldUpdateState, uniqueArrayMerge, updateEntityInArray };
6374
6521
  //# sourceMappingURL=index.browser.js.map