@internetarchive/collection-browser 1.14.17-alpha.4 → 1.14.17-alpha.41

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 (56) hide show
  1. package/dist/index.d.ts +3 -2
  2. package/dist/index.js +3 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/src/collection-browser.d.ts +6 -13
  5. package/dist/src/collection-browser.js +56 -30
  6. package/dist/src/collection-browser.js.map +1 -1
  7. package/dist/src/data-source/collection-browser-data-source-interface.d.ts +194 -0
  8. package/dist/src/data-source/collection-browser-data-source-interface.js +2 -0
  9. package/dist/src/data-source/collection-browser-data-source-interface.js.map +1 -0
  10. package/dist/src/data-source/collection-browser-data-source.d.ts +62 -174
  11. package/dist/src/data-source/collection-browser-data-source.js +185 -64
  12. package/dist/src/data-source/collection-browser-data-source.js.map +1 -1
  13. package/dist/src/data-source/collection-browser-query-state.d.ts +42 -0
  14. package/dist/src/data-source/collection-browser-query-state.js +2 -0
  15. package/dist/src/data-source/collection-browser-query-state.js.map +1 -0
  16. package/dist/src/data-source/models.d.ts +1 -39
  17. package/dist/src/data-source/models.js.map +1 -1
  18. package/dist/src/models.d.ts +3 -2
  19. package/dist/src/models.js +24 -22
  20. package/dist/src/models.js.map +1 -1
  21. package/dist/src/sort-filter-bar/sort-filter-bar.js +1 -0
  22. package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -1
  23. package/dist/src/tiles/grid/item-tile.d.ts +1 -0
  24. package/dist/src/tiles/grid/item-tile.js +28 -1
  25. package/dist/src/tiles/grid/item-tile.js.map +1 -1
  26. package/dist/src/tiles/grid/tile-stats.js +13 -8
  27. package/dist/src/tiles/grid/tile-stats.js.map +1 -1
  28. package/dist/src/tiles/item-image.d.ts +3 -0
  29. package/dist/src/tiles/item-image.js +30 -2
  30. package/dist/src/tiles/item-image.js.map +1 -1
  31. package/dist/src/tiles/list/tile-list.d.ts +1 -0
  32. package/dist/src/tiles/list/tile-list.js +30 -1
  33. package/dist/src/tiles/list/tile-list.js.map +1 -1
  34. package/dist/src/tiles/tile-dispatcher.js +3 -2
  35. package/dist/src/tiles/tile-dispatcher.js.map +1 -1
  36. package/dist/src/tiles/tile-display-value-provider.d.ts +5 -1
  37. package/dist/src/tiles/tile-display-value-provider.js +15 -1
  38. package/dist/src/tiles/tile-display-value-provider.js.map +1 -1
  39. package/dist/test/collection-browser.test.js +2 -6
  40. package/dist/test/collection-browser.test.js.map +1 -1
  41. package/index.ts +3 -5
  42. package/package.json +2 -2
  43. package/src/collection-browser.ts +70 -49
  44. package/src/data-source/collection-browser-data-source-interface.ts +245 -0
  45. package/src/data-source/collection-browser-data-source.ts +237 -281
  46. package/src/data-source/collection-browser-query-state.ts +53 -0
  47. package/src/data-source/models.ts +0 -47
  48. package/src/models.ts +7 -3
  49. package/src/sort-filter-bar/sort-filter-bar.ts +1 -0
  50. package/src/tiles/grid/item-tile.ts +36 -1
  51. package/src/tiles/grid/tile-stats.ts +12 -7
  52. package/src/tiles/item-image.ts +28 -0
  53. package/src/tiles/list/tile-list.ts +37 -1
  54. package/src/tiles/tile-dispatcher.ts +2 -1
  55. package/src/tiles/tile-display-value-provider.ts +19 -1
  56. package/test/collection-browser.test.ts +2 -8
@@ -1,4 +1,4 @@
1
- import type { ReactiveController, ReactiveControllerHost } from 'lit';
1
+ import type { ReactiveControllerHost } from 'lit';
2
2
  import {
3
3
  AccountExtraInfo,
4
4
  Aggregation,
@@ -20,283 +20,120 @@ import {
20
20
  PrefixFilterCounts,
21
21
  RequestKind,
22
22
  } from '../models';
23
- import type {
24
- CollectionBrowserSearchInterface,
25
- CollectionTitles,
26
- PageSpecifierParams,
27
- } from './models';
23
+ import type { PageSpecifierParams } from './models';
24
+ import type { CollectionBrowserDataSourceInterface } from './collection-browser-data-source-interface';
25
+ import type { CollectionBrowserSearchInterface } from './collection-browser-query-state';
28
26
  import { sha1 } from '../utils/sha1';
29
27
 
30
- export interface CollectionBrowserDataSourceInterface
31
- extends ReactiveController {
32
- /**
33
- * How many tile models are present in this data source
34
- */
35
- readonly size: number;
36
-
37
- /**
38
- * Whether the host has a valid set of properties for performing a search.
39
- * For instance, on the search page this requires a valid search service and a
40
- * non-empty query, while collection pages allow searching with an empty query
41
- * for MDS but not FTS.
42
- */
43
- readonly canPerformSearch: boolean;
44
-
45
- /**
46
- * Whether the end of the set of results for the current query state has been
47
- * encountered (i.e., the last page of results).
48
- */
49
- readonly endOfDataReached: boolean;
50
-
51
- /**
52
- * A string key compactly representing the current full search state, which can
53
- * be used to determine, e.g., when a new search is required or whether an arriving
54
- * response is outdated.
55
- */
56
- readonly pageFetchQueryKey: string;
57
-
58
- /**
59
- * Similar to `pageFetchQueryKey`, but excluding properties that do not affect
60
- * the validity of a set of facets (e.g., sort).
61
- */
62
- readonly facetFetchQueryKey: string;
63
-
64
- /**
65
- * An object representing any collection- or profile-specific properties to be passed along
66
- * to the search service, specifying the exact page/tab to fetch results for.
67
- */
68
- readonly pageSpecifierParams: PageSpecifierParams | null;
69
-
70
- /**
71
- * A FilterMap object representing all filters applied to the current search,
72
- * including any facets, letter filters, and date ranges.
73
- */
74
- readonly filterMap: FilterMap;
75
-
76
- /**
77
- * The full set of aggregations retrieved for the current search.
78
- */
79
- readonly aggregations?: Record<string, Aggregation>;
80
-
81
- /**
82
- * The `year_histogram` aggregation retrieved for the current search.
83
- */
84
- readonly yearHistogramAggregation?: Aggregation;
85
-
86
- /**
87
- * A map from collection identifiers that appear on hits or aggregations for the
88
- * current search, to their human-readable collection titles.
89
- */
90
- readonly collectionTitles: CollectionTitles;
91
-
92
- /**
93
- * The "extra info" package provided by the PPS for collection pages, including details
94
- * used to populate the target collection header & About tab content.
95
- */
96
- readonly collectionExtraInfo?: CollectionExtraInfo;
97
-
98
- /**
99
- * The "extra info" package provided by the PPS for profile pages, including details
100
- * used to populate the profile header.
101
- */
102
- readonly accountExtraInfo?: AccountExtraInfo;
103
-
104
- /**
105
- * The set of requested page elements for profile pages, if applicable. These represent
106
- * any content specific to the current profile tab.
107
- */
108
- readonly pageElements?: PageElementMap;
109
-
110
- /**
111
- * An array of the current target collection's parent collections. Should include *all*
112
- * ancestors in the collection hierarchy, not just the immediate parent.
113
- */
114
- readonly parentCollections?: string[];
115
-
116
- /**
117
- * An object storing result counts for the current search bucketed by letter prefix.
118
- * Keys are the result field on which the prefixes are considered (e.g., title/creator)
119
- * and values are a Record mapping letters to their counts.
120
- */
121
- readonly prefixFilterCountMap: Partial<
122
- Record<PrefixFilterType, PrefixFilterCounts>
123
- >;
124
-
125
- /**
126
- * An array of all the tile models whose management checkboxes are checked
127
- */
128
- readonly checkedTileModels: TileModel[];
129
-
130
- /**
131
- * An array of all the tile models whose management checkboxes are unchecked
132
- */
133
- readonly uncheckedTileModels: TileModel[];
134
-
135
- /**
136
- * Resets the data source to its empty state, with no result pages, aggregations, etc.
137
- */
138
- reset(): void;
139
-
140
- /**
141
- * Adds the given page of tile models to the data source.
142
- * If the given page number already exists, that page will be overwritten.
143
- * @param pageNum Which page number to add (indexed starting from 1)
144
- * @param pageTiles The array of tile models for the new page
145
- */
146
- addPage(pageNum: number, pageTiles: TileModel[]): void;
147
-
148
- /**
149
- * Returns the given page of tile models from the data source.
150
- * @param pageNum Which page number to get (indexed starting from 1)
151
- */
152
- getPage(pageNum: number): TileModel[];
153
-
154
- /**
155
- * Returns the full set of paged tile models stored in this data source.
156
- */
157
- getAllPages(): Record<string, TileModel[]>;
28
+ export class CollectionBrowserDataSource
29
+ implements CollectionBrowserDataSourceInterface
30
+ {
31
+ private pages: Record<string, TileModel[]> = {};
158
32
 
159
- /**
160
- * Whether the data source contains any tiles for the given page number.
161
- * @param pageNum Which page number to query (indexed starting from 1)
162
- */
163
- hasPage(pageNum: number): boolean;
33
+ private offset = 0;
164
34
 
165
- /**
166
- * Returns the single tile model appearing at the given index in the
167
- * data source, with respect to the current page size. Returns `undefined` if
168
- * the corresponding page is not present on the data source or if it does not
169
- * contain a tile model at the corresponding index.
170
- * @param index The 0-based index (within the full data source) of the tile to get
171
- */
172
- getTileModelAt(index: number): TileModel | undefined;
35
+ private numTileModels = 0;
173
36
 
174
37
  /**
175
- * Returns the first numeric tile index corresponding to the given tile model object,
176
- * or -1 if the given tile model is not present.
177
- * @param tile The tile model to search for in the data source
38
+ * A set of fetch IDs that are valid for the current query state
178
39
  */
179
- indexOf(tile: TileModel): number;
40
+ private fetchesInProgress = new Set<string>();
180
41
 
181
42
  /**
182
- * Requests that the data source fire a backend request for the given page of results.
183
- * @param pageNum Which page number to fetch results for
184
- * @param numInitialPages How many pages should be batched together on an initial fetch
43
+ * A record of the query key used for the last search.
44
+ * If this changes, we need to load new results.
185
45
  */
186
- fetchPage(pageNum: number, numInitialPages?: number): Promise<void>;
46
+ private previousQueryKey: string = '';
187
47
 
188
- /**
189
- * Requests that the data source update its prefix bucket result counts for the given
190
- * type of prefix filter.
191
- * @param filterType Which prefixable field to update the buckets for (e.g., title/creator)
192
- */
193
- updatePrefixFilterCounts(filterType: PrefixFilterType): Promise<void>;
48
+ private searchResultsLoading = false;
194
49
 
195
- /**
196
- * Changes the page size used by the data source, discarding any previously-fetched pages.
197
- *
198
- * **Note: this operation will reset any data stored in the data source!**
199
- * @param pageSize
200
- */
201
- setPageSize(pageSize: number): void;
50
+ private facetsLoading = false;
202
51
 
203
- /**
204
- * Notifies the data source that a query change has occurred, which may trigger a data
205
- * reset & new fetches.
206
- */
207
- handleQueryChange(): void;
52
+ // TEMP for ease of debugging
53
+ private id = Math.random();
208
54
 
209
55
  /**
210
- * Applies the given map function to all of the tile models in every page of the data
211
- * source.
212
- * @param callback A callback function to apply on each tile model, as with Array.map
56
+ * @inheritdoc
213
57
  */
214
- map(
215
- callback: (model: TileModel, index: number, array: TileModel[]) => TileModel
216
- ): void;
58
+ totalResults = 0;
217
59
 
218
60
  /**
219
- * Checks every tile's management checkbox
61
+ * @inheritdoc
220
62
  */
221
- checkAllTiles(): void;
63
+ endOfDataReached = false;
222
64
 
223
65
  /**
224
- * Unchecks every tile's management checkbox
66
+ * @inheritdoc
225
67
  */
226
- uncheckAllTiles(): void;
68
+ queryInitialized = false;
227
69
 
228
70
  /**
229
- * Removes all tile models that are currently checked & adjusts the paging
230
- * of the data source to account for any new gaps in the data.
71
+ * @inheritdoc
231
72
  */
232
- removeCheckedTiles(): void;
233
- }
234
-
235
- export class CollectionBrowserDataSource
236
- implements CollectionBrowserDataSourceInterface
237
- {
238
- private pages: Record<string, TileModel[]> = {};
239
-
240
- private offset = 0;
241
-
242
- private numTileModels = 0;
73
+ aggregations?: Record<string, Aggregation>;
243
74
 
244
75
  /**
245
- * Maps the full query key to the pages being fetched for that query
76
+ * @inheritdoc
246
77
  */
247
- private pageFetchesInProgress: Record<string, Set<number>> = {};
78
+ yearHistogramAggregation?: Aggregation;
248
79
 
249
80
  /**
250
81
  * @inheritdoc
251
82
  */
252
- totalResults = 0;
83
+ collectionTitles = new Map<string, string>();
253
84
 
254
85
  /**
255
86
  * @inheritdoc
256
87
  */
257
- endOfDataReached = false;
88
+ collectionExtraInfo?: CollectionExtraInfo;
258
89
 
259
90
  /**
260
91
  * @inheritdoc
261
92
  */
262
- aggregations?: Record<string, Aggregation>;
93
+ accountExtraInfo?: AccountExtraInfo;
263
94
 
264
95
  /**
265
96
  * @inheritdoc
266
97
  */
267
- yearHistogramAggregation?: Aggregation;
98
+ pageElements?: PageElementMap;
268
99
 
269
100
  /**
270
101
  * @inheritdoc
271
102
  */
272
- collectionTitles = new Map<string, string>();
103
+ parentCollections?: string[] = [];
273
104
 
274
105
  /**
275
106
  * @inheritdoc
276
107
  */
277
- collectionExtraInfo?: CollectionExtraInfo;
108
+ prefixFilterCountMap: Partial<Record<PrefixFilterType, PrefixFilterCounts>> =
109
+ {};
278
110
 
279
111
  /**
280
112
  * @inheritdoc
281
113
  */
282
- accountExtraInfo?: AccountExtraInfo;
114
+ queryErrorMessage?: string;
283
115
 
284
116
  /**
285
- * @inheritdoc
117
+ * Internal property to store the `resolve` function for the most recent
118
+ * `initialSearchComplete` promise, allowing us to resolve it at the appropriate time.
286
119
  */
287
- pageElements?: PageElementMap;
120
+ private _initialSearchCompleteResolver!: (val: boolean) => void;
288
121
 
289
122
  /**
290
- * @inheritdoc
123
+ * Internal property to store the private value backing the `initialSearchComplete` getter.
291
124
  */
292
- parentCollections?: string[] = [];
125
+ private _initialSearchCompletePromise: Promise<boolean> = new Promise(res => {
126
+ this._initialSearchCompleteResolver = res;
127
+ });
293
128
 
294
129
  /**
295
130
  * @inheritdoc
296
131
  */
297
- prefixFilterCountMap: Partial<Record<PrefixFilterType, PrefixFilterCounts>> =
298
- {};
132
+ get initialSearchComplete(): Promise<boolean> {
133
+ return this._initialSearchCompletePromise;
134
+ }
299
135
 
136
+ // eslint-disable-next-line no-useless-constructor
300
137
  constructor(
301
138
  /** The host element to which this controller should attach listeners */
302
139
  private readonly host: ReactiveControllerHost &
@@ -304,7 +141,56 @@ export class CollectionBrowserDataSource
304
141
  /** Default size of result pages */
305
142
  private pageSize: number
306
143
  ) {
307
- this.host.addController(this as CollectionBrowserDataSourceInterface);
144
+ // Just setting some property values
145
+ }
146
+
147
+ hostConnected(): void {
148
+ console.log('hostConnected', this.id);
149
+ this.setSearchResultsLoading(this.searchResultsLoading);
150
+ this.setFacetsLoading(this.facetsLoading);
151
+ }
152
+
153
+ hostUpdate(): void {
154
+ console.log(
155
+ 'hostUpdate',
156
+ this.id,
157
+ this.previousQueryKey,
158
+ this.pageFetchQueryKey
159
+ );
160
+ // This reactive controller hook is run whenever the host component (collection-browser) performs an update.
161
+ // We check whether the host's state has changed in a way which should trigger a reset & new results fetch.
162
+
163
+ // Only the currently-installed data source should react to the update
164
+ if (!this.activeOnHost) return;
165
+
166
+ // Copy loading states onto the host
167
+ this.setSearchResultsLoading(this.searchResultsLoading);
168
+ this.setFacetsLoading(this.facetsLoading);
169
+
170
+ // Can't perform searches without a search service
171
+ if (!this.host.searchService) return;
172
+
173
+ // We should only reset if part of the full query state has changed
174
+ const queryKeyChanged = this.pageFetchQueryKey !== this.previousQueryKey;
175
+ console.log('query keys', this.pageFetchQueryKey, this.previousQueryKey);
176
+ if (!queryKeyChanged) return;
177
+
178
+ // We should only reset if either:
179
+ // (a) our state permits a valid search, or
180
+ // (b) we have a blank query that we want to show empty results for
181
+ const shouldShowEmptyQueryResults =
182
+ this.host.clearResultsOnEmptyQuery && this.host.baseQuery === '';
183
+ if (!(this.canPerformSearch || shouldShowEmptyQueryResults)) return;
184
+
185
+ if (this.activeOnHost) this.host.emitQueryStateChanged();
186
+ this.handleQueryChange();
187
+ }
188
+
189
+ /**
190
+ * Returns whether this data source is the one currently installed on the host component.
191
+ */
192
+ private get activeOnHost(): boolean {
193
+ return this.host.dataSource === this;
308
194
  }
309
195
 
310
196
  /**
@@ -318,20 +204,27 @@ export class CollectionBrowserDataSource
318
204
  * @inheritdoc
319
205
  */
320
206
  reset(): void {
207
+ console.log('resetting data source', this.id);
321
208
  this.pages = {};
322
209
  this.aggregations = {};
323
210
  this.yearHistogramAggregation = undefined;
324
- this.pageFetchesInProgress = {};
325
211
  this.pageElements = undefined;
326
212
  this.parentCollections = [];
327
213
  this.prefixFilterCountMap = {};
214
+ this.queryErrorMessage = undefined;
328
215
 
329
216
  this.offset = 0;
330
217
  this.numTileModels = 0;
331
218
  this.totalResults = 0;
332
219
  this.endOfDataReached = false;
220
+ this.queryInitialized = false;
333
221
 
334
- this.host.requestUpdate();
222
+ // Invalidate any fetches in progress
223
+ this.fetchesInProgress.clear();
224
+ console.log('fetches cleared - size =', this.fetchesInProgress.size);
225
+
226
+ if (this.activeOnHost) this.host.setTotalResultCount(0);
227
+ this.requestHostUpdate();
335
228
  }
336
229
 
337
230
  /**
@@ -340,7 +233,7 @@ export class CollectionBrowserDataSource
340
233
  addPage(pageNum: number, pageTiles: TileModel[]): void {
341
234
  this.pages[pageNum] = pageTiles;
342
235
  this.numTileModels += pageTiles.length;
343
- this.host.requestUpdate();
236
+ this.requestHostUpdate();
344
237
  }
345
238
 
346
239
  /**
@@ -393,10 +286,21 @@ export class CollectionBrowserDataSource
393
286
  */
394
287
  async handleQueryChange(): Promise<void> {
395
288
  this.reset();
289
+
290
+ // Reset the `initialSearchComplete` promise with a new value for the imminent search
291
+ this._initialSearchCompletePromise = new Promise(res => {
292
+ this._initialSearchCompleteResolver = res;
293
+ });
294
+
295
+ // Fire the initial page & facet requests
296
+ this.queryInitialized = true;
396
297
  await Promise.all([
397
298
  this.doInitialPageFetch(),
398
299
  this.host.suppressFacets ? null : this.fetchFacets(),
399
300
  ]);
301
+
302
+ // Resolve the `initialSearchComplete` promise for this search
303
+ this._initialSearchCompleteResolver(true);
400
304
  }
401
305
 
402
306
  /**
@@ -413,8 +317,8 @@ export class CollectionBrowserDataSource
413
317
  ),
414
318
  ])
415
319
  );
416
- this.host.requestUpdate();
417
- this.host.refreshVisibleResults();
320
+ this.requestHostUpdate();
321
+ this.refreshVisibleResults();
418
322
  }
419
323
 
420
324
  /**
@@ -479,8 +383,8 @@ export class CollectionBrowserDataSource
479
383
  // Swap in the new pages
480
384
  this.pages = newPages;
481
385
  this.numTileModels -= numChecked;
482
- this.host.requestUpdate();
483
- this.host.refreshVisibleResults();
386
+ this.requestHostUpdate();
387
+ this.refreshVisibleResults();
484
388
  };
485
389
 
486
390
  /**
@@ -514,8 +418,6 @@ export class CollectionBrowserDataSource
514
418
  );
515
419
  }
516
420
 
517
- // DATA FETCHES
518
-
519
421
  /**
520
422
  * @inheritdoc
521
423
  */
@@ -538,6 +440,47 @@ export class CollectionBrowserDataSource
538
440
  );
539
441
  }
540
442
 
443
+ /**
444
+ * Sets the state for whether the initial set of search results for the
445
+ * current query is loading
446
+ */
447
+ private setSearchResultsLoading(loading: boolean): void {
448
+ this.searchResultsLoading = loading;
449
+ if (this.activeOnHost) {
450
+ this.host.setSearchResultsLoading(loading);
451
+ }
452
+ }
453
+
454
+ /**
455
+ * Sets the state for whether the facets for a query is loading
456
+ */
457
+ private setFacetsLoading(loading: boolean): void {
458
+ this.facetsLoading = loading;
459
+ if (this.activeOnHost) {
460
+ this.host.setFacetsLoading(loading);
461
+ }
462
+ }
463
+
464
+ /**
465
+ * Requests that the host perform an update, provided this data
466
+ * source is actively installed on it.
467
+ */
468
+ private requestHostUpdate(): void {
469
+ if (this.activeOnHost) {
470
+ this.host.requestUpdate();
471
+ }
472
+ }
473
+
474
+ /**
475
+ * Requests that the host refresh its visible tiles, provided this
476
+ * data source is actively installed on it.
477
+ */
478
+ private refreshVisibleResults(): void {
479
+ if (this.activeOnHost) {
480
+ this.host.refreshVisibleResults();
481
+ }
482
+ }
483
+
541
484
  /**
542
485
  * The query key is a string that uniquely identifies the current search.
543
486
  * It consists of:
@@ -549,15 +492,15 @@ export class CollectionBrowserDataSource
549
492
  * - Any currently-applied prefix filters
550
493
  * - The current sort options
551
494
  *
552
- * This lets us keep track of queries so we don't persist data that's
553
- * no longer relevant.
495
+ * This lets us internally keep track of queries so we don't persist data that's
496
+ * no longer relevant. Not meant to be human-readable.
554
497
  */
555
498
  get pageFetchQueryKey(): string {
556
- const profileKey = `${this.host.withinProfile}--${this.host.profileElement}`;
499
+ const profileKey = `pf;${this.host.withinProfile}--pe;${this.host.profileElement}`;
557
500
  const pageTarget = this.host.withinCollection ?? profileKey;
558
501
  const sortField = this.host.sortParam?.field ?? 'none';
559
502
  const sortDirection = this.host.sortParam?.direction ?? 'none';
560
- return `${this.fullQuery}-${pageTarget}-${this.host.searchType}-${sortField}-${sortDirection}`;
503
+ return `fq:${this.fullQuery}-pt:${pageTarget}-st:${this.host.searchType}-sf:${sortField}-sd:${sortDirection}`;
561
504
  }
562
505
 
563
506
  /**
@@ -833,10 +776,14 @@ export class CollectionBrowserDataSource
833
776
  * the current search state.
834
777
  */
835
778
  private async fetchFacets(): Promise<void> {
779
+ console.log('fetchFacets', this.id, this.host.profileElement);
836
780
  const trimmedQuery = this.host.baseQuery?.trim();
837
781
  if (!this.canPerformSearch) return;
782
+ console.log('will actually fetch facets');
838
783
 
839
784
  const { facetFetchQueryKey } = this;
785
+ if (this.fetchesInProgress.has(facetFetchQueryKey)) return;
786
+ this.fetchesInProgress.add(facetFetchQueryKey);
840
787
 
841
788
  const sortParams = this.host.sortParam ? [this.host.sortParam] : [];
842
789
  const params: SearchParams = {
@@ -854,7 +801,7 @@ export class CollectionBrowserDataSource
854
801
  'aggregations'
855
802
  );
856
803
 
857
- this.host.setFacetsLoading(true);
804
+ this.setFacetsLoading(true);
858
805
  const searchResponse = await this.host.searchService?.search(
859
806
  params,
860
807
  this.host.searchType
@@ -865,8 +812,16 @@ export class CollectionBrowserDataSource
865
812
  // If so, we just want to discard this set of aggregations because they are
866
813
  // likely no longer valid for the newer query.
867
814
  const queryChangedSinceFetch =
868
- facetFetchQueryKey !== this.facetFetchQueryKey;
869
- if (queryChangedSinceFetch) return;
815
+ !this.fetchesInProgress.has(facetFetchQueryKey);
816
+ this.fetchesInProgress.delete(facetFetchQueryKey);
817
+ if (queryChangedSinceFetch) {
818
+ console.log(
819
+ 'facet query has changed since fetch, returning. new/old:',
820
+ this.facetFetchQueryKey,
821
+ facetFetchQueryKey
822
+ );
823
+ return;
824
+ }
870
825
 
871
826
  if (!success) {
872
827
  const errorMsg = searchResponse?.error?.message;
@@ -880,6 +835,7 @@ export class CollectionBrowserDataSource
880
835
  );
881
836
  }
882
837
 
838
+ this.setFacetsLoading(false);
883
839
  return;
884
840
  }
885
841
 
@@ -895,18 +851,17 @@ export class CollectionBrowserDataSource
895
851
  this.yearHistogramAggregation =
896
852
  success?.response?.aggregations?.year_histogram;
897
853
 
898
- this.host.setFacetsLoading(false);
899
- this.host.requestUpdate();
854
+ this.setFacetsLoading(false);
855
+ this.requestHostUpdate();
900
856
  }
901
857
 
902
858
  /**
903
859
  * Performs the initial page fetch(es) for the current search state.
904
860
  */
905
861
  private async doInitialPageFetch(): Promise<void> {
906
- this.host.setSearchResultsLoading(true);
862
+ this.setSearchResultsLoading(true);
907
863
  // Try to batch 2 initial page requests when possible
908
864
  await this.fetchPage(this.host.initialPageNumber, 2);
909
- this.host.setSearchResultsLoading(false);
910
865
  }
911
866
 
912
867
  /**
@@ -918,14 +873,6 @@ export class CollectionBrowserDataSource
918
873
  * if `pageNumber != 1`, defaulting to a single page.
919
874
  */
920
875
  async fetchPage(pageNumber: number, numInitialPages = 1): Promise<void> {
921
- console.log(
922
- `fetchPage(${pageNumber})`,
923
- this.canPerformSearch,
924
- this.hasPage(pageNumber),
925
- this.endOfDataReached,
926
- this.host.baseQuery,
927
- JSON.stringify(this.host.selectedFacets)
928
- );
929
876
  const trimmedQuery = this.host.baseQuery?.trim();
930
877
  if (!this.canPerformSearch) return;
931
878
 
@@ -941,18 +888,31 @@ export class CollectionBrowserDataSource
941
888
 
942
889
  // if a fetch is already in progress for this query and page, don't fetch again
943
890
  const { pageFetchQueryKey } = this;
944
- const pageFetches =
945
- this.pageFetchesInProgress[pageFetchQueryKey] ?? new Set();
946
- if (pageFetches.has(pageNumber)) {
947
- console.log(
948
- `Skipping fetch for page ${pageNumber} because one is already in progress`
949
- );
950
- return;
951
- }
891
+ const currentPageKey = `${pageFetchQueryKey}-p:${pageNumber}`;
892
+ console.log(
893
+ 'page fetch key',
894
+ currentPageKey,
895
+ this.fetchesInProgress.has(currentPageKey)
896
+ );
897
+ if (this.fetchesInProgress.has(currentPageKey)) return;
898
+ // const pageFetches =
899
+ // this.fetchesInProgress[pageFetchQueryKey] ?? new Set();
900
+ // if (pageFetches.has(pageNumber)) return;
901
+
952
902
  for (let i = 0; i < numPages; i += 1) {
953
- pageFetches.add(pageNumber + i);
903
+ this.fetchesInProgress.add(`${pageFetchQueryKey}-p:${pageNumber + i}`);
954
904
  }
955
- this.pageFetchesInProgress[pageFetchQueryKey] = pageFetches;
905
+ console.log(
906
+ `fetchPage(${pageNumber})`,
907
+ this.id,
908
+ this.canPerformSearch,
909
+ this.hasPage(pageNumber),
910
+ this.endOfDataReached,
911
+ this.host.baseQuery,
912
+ JSON.stringify(this.host.selectedFacets),
913
+ this.pageFetchQueryKey
914
+ );
915
+ this.previousQueryKey = pageFetchQueryKey;
956
916
 
957
917
  const sortParams = this.host.sortParam ? [this.host.sortParam] : [];
958
918
  const params: SearchParams = {
@@ -974,42 +934,45 @@ export class CollectionBrowserDataSource
974
934
  console.log('=== RECEIVED PAGE RESPONSE IN CB === ');
975
935
  const success = searchResponse?.success;
976
936
 
977
- // This is checking to see if the query has changed since the data was fetched.
978
- // If so, we just want to discard the data since there should be a new query
979
- // right behind it.
980
- const queryChangedSinceFetch = pageFetchQueryKey !== this.pageFetchQueryKey;
981
- if (queryChangedSinceFetch) return;
937
+ // This is checking to see if the fetch has been invalidated since it was fired off.
938
+ // If so, we just want to discard the response since it is for an obsolete query state.
939
+ console.log(
940
+ 'after page fetch key:',
941
+ currentPageKey,
942
+ this.fetchesInProgress.has(currentPageKey)
943
+ );
944
+ if (!this.fetchesInProgress.has(currentPageKey)) return;
945
+ for (let i = 0; i < numPages; i += 1) {
946
+ this.fetchesInProgress.delete(`${pageFetchQueryKey}-p:${pageNumber + i}`);
947
+ }
982
948
 
983
949
  if (!success) {
984
950
  const errorMsg = searchResponse?.error?.message;
985
951
  const detailMsg = searchResponse?.error?.details?.message;
986
952
 
987
- this.host.queryErrorMessage = `${errorMsg ?? ''}${
953
+ this.queryErrorMessage = `${errorMsg ?? ''}${
988
954
  detailMsg ? `; ${detailMsg}` : ''
989
955
  }`;
990
956
 
991
- if (!this.host.queryErrorMessage) {
992
- this.host.queryErrorMessage =
993
- 'Missing or malformed response from backend';
957
+ if (!this.queryErrorMessage) {
958
+ this.queryErrorMessage = 'Missing or malformed response from backend';
994
959
  // @ts-ignore: Property 'Sentry' does not exist on type 'Window & typeof globalThis'
995
960
  window?.Sentry?.captureMessage?.(this.queryErrorMessage, 'error');
996
961
  }
997
962
 
998
- for (let i = 0; i < numPages; i += 1) {
999
- this.pageFetchesInProgress[pageFetchQueryKey]?.delete(pageNumber + i);
1000
- }
1001
-
1002
- this.host.setSearchResultsLoading(false);
1003
- this.host.requestUpdate();
963
+ this.setSearchResultsLoading(false);
964
+ this.requestHostUpdate();
1004
965
  return;
1005
966
  }
1006
967
 
1007
968
  this.totalResults = success.response.totalResults - this.offset;
1008
- this.host.setTotalResultCount(this.totalResults);
969
+ if (this.activeOnHost) {
970
+ this.host.setTotalResultCount(this.totalResults);
1009
971
 
1010
- // display event to offshoot when result count is zero.
1011
- if (this.totalResults === 0) {
1012
- this.host.emitEmptyResults();
972
+ // display event to offshoot when result count is zero.
973
+ if (this.totalResults === 0) {
974
+ this.host.emitEmptyResults();
975
+ }
1013
976
  }
1014
977
 
1015
978
  if (this.host.withinCollection) {
@@ -1071,14 +1034,11 @@ export class CollectionBrowserDataSource
1071
1034
  if (resultCountDiscrepancy > 0) {
1072
1035
  console.log('End of data reached');
1073
1036
  this.endOfDataReached = true;
1074
- this.host.setTileCount(this.totalResults);
1075
- }
1076
-
1077
- for (let i = 0; i < numPages; i += 1) {
1078
- this.pageFetchesInProgress[pageFetchQueryKey]?.delete(pageNumber + i);
1037
+ if (this.activeOnHost) this.host.setTileCount(this.totalResults);
1079
1038
  }
1080
1039
 
1081
- this.host.requestUpdate();
1040
+ this.setSearchResultsLoading(false);
1041
+ this.requestHostUpdate();
1082
1042
  }
1083
1043
 
1084
1044
  /**
@@ -1091,10 +1051,6 @@ export class CollectionBrowserDataSource
1091
1051
  pageNumber: number,
1092
1052
  results: SearchResult[]
1093
1053
  ): void {
1094
- // copy our existing datasource so when we set it below, it gets set
1095
- // instead of modifying the existing dataSource since object changes
1096
- // don't trigger a re-render
1097
- // const datasource = { ...this.dataSource };
1098
1054
  const tiles: TileModel[] = [];
1099
1055
  results?.forEach(result => {
1100
1056
  if (!result.identifier) return;
@@ -1104,7 +1060,7 @@ export class CollectionBrowserDataSource
1104
1060
  const visiblePages = this.host.currentVisiblePageNumbers;
1105
1061
  const needsReload = visiblePages.includes(pageNumber);
1106
1062
  if (needsReload) {
1107
- this.host.refreshVisibleResults();
1063
+ this.refreshVisibleResults();
1108
1064
  }
1109
1065
  }
1110
1066
 
@@ -1168,6 +1124,6 @@ export class CollectionBrowserDataSource
1168
1124
  {}
1169
1125
  );
1170
1126
 
1171
- this.host.requestUpdate();
1127
+ this.requestHostUpdate();
1172
1128
  }
1173
1129
  }