@internetarchive/collection-browser 1.14.17-alpha.3 → 1.14.17-alpha.30

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 (69) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/index.js.map +1 -1
  3. package/dist/src/collection-browser.d.ts +6 -13
  4. package/dist/src/collection-browser.js +54 -25
  5. package/dist/src/collection-browser.js.map +1 -1
  6. package/dist/src/data-source/collection-browser-data-source.d.ts +48 -30
  7. package/dist/src/data-source/collection-browser-data-source.js +106 -116
  8. package/dist/src/data-source/collection-browser-data-source.js.map +1 -1
  9. package/dist/src/data-source/models.d.ts +7 -3
  10. package/dist/src/data-source/models.js.map +1 -1
  11. package/dist/src/manage/manage-bar.d.ts +1 -1
  12. package/dist/src/manage/manage-bar.js.map +1 -1
  13. package/dist/src/models.d.ts +20 -4
  14. package/dist/src/models.js +105 -0
  15. package/dist/src/models.js.map +1 -1
  16. package/dist/src/sort-filter-bar/sort-filter-bar.js +1 -0
  17. package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -1
  18. package/dist/src/tiles/grid/item-tile.d.ts +1 -0
  19. package/dist/src/tiles/grid/item-tile.js +28 -1
  20. package/dist/src/tiles/grid/item-tile.js.map +1 -1
  21. package/dist/src/tiles/grid/tile-stats.js +13 -8
  22. package/dist/src/tiles/grid/tile-stats.js.map +1 -1
  23. package/dist/src/tiles/list/tile-list.d.ts +1 -0
  24. package/dist/src/tiles/list/tile-list.js +32 -1
  25. package/dist/src/tiles/list/tile-list.js.map +1 -1
  26. package/dist/src/tiles/tile-dispatcher.js +3 -2
  27. package/dist/src/tiles/tile-dispatcher.js.map +1 -1
  28. package/dist/src/tiles/tile-display-value-provider.d.ts +6 -2
  29. package/dist/src/tiles/tile-display-value-provider.js +15 -1
  30. package/dist/src/tiles/tile-display-value-provider.js.map +1 -1
  31. package/dist/src/utils/collapse-repeated-quotes.d.ts +11 -0
  32. package/dist/src/utils/collapse-repeated-quotes.js +14 -0
  33. package/dist/src/utils/collapse-repeated-quotes.js.map +1 -0
  34. package/dist/src/utils/resolve-mediatype.d.ts +8 -0
  35. package/dist/src/utils/resolve-mediatype.js +24 -0
  36. package/dist/src/utils/resolve-mediatype.js.map +1 -0
  37. package/dist/test/collection-browser.test.js +37 -25
  38. package/dist/test/collection-browser.test.js.map +1 -1
  39. package/dist/test/collection-facets/more-facets-content.test.js +2 -2
  40. package/dist/test/collection-facets/more-facets-content.test.js.map +1 -1
  41. package/dist/test/item-image.test.js +33 -34
  42. package/dist/test/item-image.test.js.map +1 -1
  43. package/dist/test/mocks/mock-search-responses.d.ts +1 -0
  44. package/dist/test/mocks/mock-search-responses.js +62 -0
  45. package/dist/test/mocks/mock-search-responses.js.map +1 -1
  46. package/dist/test/sort-filter-bar/sort-filter-bar.test.js +41 -4
  47. package/dist/test/sort-filter-bar/sort-filter-bar.test.js.map +1 -1
  48. package/dist/test/tiles/hover/hover-pane-controller.test.js +18 -17
  49. package/dist/test/tiles/hover/hover-pane-controller.test.js.map +1 -1
  50. package/package.json +2 -2
  51. package/src/collection-browser.ts +62 -42
  52. package/src/data-source/collection-browser-data-source.ts +170 -140
  53. package/src/data-source/models.ts +7 -2
  54. package/src/manage/manage-bar.ts +1 -1
  55. package/src/models.ts +154 -3
  56. package/src/sort-filter-bar/sort-filter-bar.ts +1 -0
  57. package/src/tiles/grid/item-tile.ts +36 -1
  58. package/src/tiles/grid/tile-stats.ts +12 -7
  59. package/src/tiles/list/tile-list.ts +43 -5
  60. package/src/tiles/tile-dispatcher.ts +2 -1
  61. package/src/tiles/tile-display-value-provider.ts +20 -2
  62. package/src/utils/collapse-repeated-quotes.ts +13 -0
  63. package/src/utils/resolve-mediatype.ts +26 -0
  64. package/test/collection-browser.test.ts +72 -27
  65. package/test/collection-facets/more-facets-content.test.ts +4 -2
  66. package/test/item-image.test.ts +34 -36
  67. package/test/mocks/mock-search-responses.ts +66 -0
  68. package/test/sort-filter-bar/sort-filter-bar.test.ts +50 -4
  69. package/test/tiles/hover/hover-pane-controller.test.ts +19 -17
@@ -12,13 +12,13 @@ import {
12
12
  SearchResult,
13
13
  SearchType,
14
14
  } from '@internetarchive/search-service';
15
- import type { MediaType } from '@internetarchive/field-parsers';
16
15
  import {
17
16
  prefixFilterAggregationKeys,
18
17
  type FacetBucket,
19
18
  type PrefixFilterType,
20
- type TileModel,
19
+ TileModel,
21
20
  PrefixFilterCounts,
21
+ RequestKind,
22
22
  } from '../models';
23
23
  import type {
24
24
  CollectionBrowserSearchInterface,
@@ -27,8 +27,6 @@ import type {
27
27
  } from './models';
28
28
  import { sha1 } from '../utils/sha1';
29
29
 
30
- type RequestKind = 'full' | 'hits' | 'aggregations';
31
-
32
30
  export interface CollectionBrowserDataSourceInterface
33
31
  extends ReactiveController {
34
32
  /**
@@ -50,6 +48,12 @@ export interface CollectionBrowserDataSourceInterface
50
48
  */
51
49
  readonly endOfDataReached: boolean;
52
50
 
51
+ /**
52
+ * True if the initial work for a new query state has been completed (i.e., firing initial
53
+ * page/facet requests). False otherwise.
54
+ */
55
+ readonly queryInitialized: boolean;
56
+
53
57
  /**
54
58
  * A string key compactly representing the current full search state, which can
55
59
  * be used to determine, e.g., when a new search is required or whether an arriving
@@ -134,6 +138,20 @@ export interface CollectionBrowserDataSourceInterface
134
138
  */
135
139
  readonly uncheckedTileModels: TileModel[];
136
140
 
141
+ /**
142
+ * A Promise which, after each query change, resolves once the fetches for the initial
143
+ * search have completed. Waits for *both* the hits and aggregations fetches to finish.
144
+ *
145
+ * Ensure you await this component's `updateComplete` promise before awaiting this
146
+ * one, to ensure you do not await an obsolete promise from the previous update.
147
+ */
148
+ readonly initialSearchComplete: Promise<boolean>;
149
+
150
+ /**
151
+ * Resets the data source to its empty state, with no result pages, aggregations, etc.
152
+ */
153
+ reset(): void;
154
+
137
155
  /**
138
156
  * Adds the given page of tile models to the data source.
139
157
  * If the given page number already exists, that page will be overwritten.
@@ -197,11 +215,6 @@ export interface CollectionBrowserDataSourceInterface
197
215
  */
198
216
  setPageSize(pageSize: number): void;
199
217
 
200
- /**
201
- * Resets the data source to its empty state, with no result pages, aggregations, etc.
202
- */
203
- reset(): void;
204
-
205
218
  /**
206
219
  * Notifies the data source that a query change has occurred, which may trigger a data
207
220
  * reset & new fetches.
@@ -248,6 +261,14 @@ export class CollectionBrowserDataSource
248
261
  */
249
262
  private pageFetchesInProgress: Record<string, Set<number>> = {};
250
263
 
264
+ /**
265
+ * A record of the query key used for the last search.
266
+ * If this changes, we need to load new results.
267
+ */
268
+ private previousQueryKey: string = '';
269
+
270
+ private id = Math.random();
271
+
251
272
  /**
252
273
  * @inheritdoc
253
274
  */
@@ -258,6 +279,11 @@ export class CollectionBrowserDataSource
258
279
  */
259
280
  endOfDataReached = false;
260
281
 
282
+ /**
283
+ * @inheritdoc
284
+ */
285
+ queryInitialized = false;
286
+
261
287
  /**
262
288
  * @inheritdoc
263
289
  */
@@ -299,6 +325,26 @@ export class CollectionBrowserDataSource
299
325
  prefixFilterCountMap: Partial<Record<PrefixFilterType, PrefixFilterCounts>> =
300
326
  {};
301
327
 
328
+ /**
329
+ * Internal property to store the `resolve` function for the most recent
330
+ * `initialSearchComplete` promise, allowing us to resolve it at the appropriate time.
331
+ */
332
+ private _initialSearchCompleteResolver!: (val: boolean) => void;
333
+
334
+ /**
335
+ * Internal property to store the private value backing the `initialSearchComplete` getter.
336
+ */
337
+ private _initialSearchCompletePromise: Promise<boolean> = new Promise(res => {
338
+ this._initialSearchCompleteResolver = res;
339
+ });
340
+
341
+ /**
342
+ * @inheritdoc
343
+ */
344
+ get initialSearchComplete(): Promise<boolean> {
345
+ return this._initialSearchCompletePromise;
346
+ }
347
+
302
348
  constructor(
303
349
  /** The host element to which this controller should attach listeners */
304
350
  private readonly host: ReactiveControllerHost &
@@ -306,7 +352,35 @@ export class CollectionBrowserDataSource
306
352
  /** Default size of result pages */
307
353
  private pageSize: number
308
354
  ) {
309
- this.host.addController(this as CollectionBrowserDataSourceInterface);
355
+ // console.log('adding controller', this.id);
356
+ // this.host.addController(this as CollectionBrowserDataSourceInterface);
357
+ }
358
+
359
+ hostUpdate(): void {
360
+ console.log('hostUpdate', this.id, this.previousQueryKey, this.pageFetchQueryKey);
361
+ // This reactive controller hook is run whenever the host component (collection-browser) performs an update.
362
+ // We check whether the host's state has changed in a way which should trigger a reset & new results fetch.
363
+
364
+ // Only the currently-installed data source should react to the update
365
+ if (this.host.dataSource !== this) return;
366
+
367
+ // Can't perform searches without a search service
368
+ if (!this.host.searchService) return;
369
+
370
+ // We should only reset if part of the full query state has changed
371
+ const queryKeyChanged = this.pageFetchQueryKey !== this.previousQueryKey;
372
+ console.log('query keys', this.pageFetchQueryKey, this.previousQueryKey);
373
+ if (!queryKeyChanged) return;
374
+
375
+ // We should only reset if either:
376
+ // (a) our state permits a valid search, or
377
+ // (b) we have a blank query that we want to show empty results for
378
+ const shouldShowEmptyQueryResults =
379
+ this.host.clearResultsOnEmptyQuery && this.host.baseQuery === '';
380
+ if (!(this.canPerformSearch || shouldShowEmptyQueryResults)) return;
381
+
382
+ this.host.emitQueryStateChanged();
383
+ this.handleQueryChange();
310
384
  }
311
385
 
312
386
  /**
@@ -316,6 +390,29 @@ export class CollectionBrowserDataSource
316
390
  return this.numTileModels;
317
391
  }
318
392
 
393
+ /**
394
+ * @inheritdoc
395
+ */
396
+ reset(): void {
397
+ this.pages = {};
398
+ this.aggregations = {};
399
+ this.yearHistogramAggregation = undefined;
400
+ this.pageFetchesInProgress = {};
401
+ this.pageElements = undefined;
402
+ this.parentCollections = [];
403
+ this.prefixFilterCountMap = {};
404
+
405
+ this.offset = 0;
406
+ this.numTileModels = 0;
407
+ this.totalResults = 0;
408
+ this.endOfDataReached = false;
409
+ this.queryInitialized = false;
410
+
411
+ this.host.setTotalResultCount(0);
412
+
413
+ this.host.requestUpdate();
414
+ }
415
+
319
416
  /**
320
417
  * @inheritdoc
321
418
  */
@@ -370,35 +467,26 @@ export class CollectionBrowserDataSource
370
467
  this.pageSize = pageSize;
371
468
  }
372
469
 
373
- /**
374
- * @inheritdoc
375
- */
376
- reset(): void {
377
- this.pages = {};
378
- this.aggregations = {};
379
- this.yearHistogramAggregation = undefined;
380
- this.pageFetchesInProgress = {};
381
- this.pageElements = undefined;
382
- this.parentCollections = [];
383
- this.prefixFilterCountMap = {};
384
-
385
- this.offset = 0;
386
- this.numTileModels = 0;
387
- this.totalResults = 0;
388
- this.endOfDataReached = false;
389
-
390
- this.host.requestUpdate();
391
- }
392
-
393
470
  /**
394
471
  * @inheritdoc
395
472
  */
396
473
  async handleQueryChange(): Promise<void> {
397
474
  this.reset();
475
+
476
+ // Reset the `initialSearchComplete` promise with a new value for the imminent search
477
+ this._initialSearchCompletePromise = new Promise(res => {
478
+ this._initialSearchCompleteResolver = res;
479
+ });
480
+
481
+ // Fire the initial page & facet requests
482
+ this.queryInitialized = true;
398
483
  await Promise.all([
399
484
  this.doInitialPageFetch(),
400
485
  this.host.suppressFacets ? null : this.fetchFacets(),
401
486
  ]);
487
+
488
+ // Resolve the `initialSearchComplete` promise for this search
489
+ this._initialSearchCompleteResolver(true);
402
490
  }
403
491
 
404
492
  /**
@@ -423,14 +511,22 @@ export class CollectionBrowserDataSource
423
511
  * @inheritdoc
424
512
  */
425
513
  checkAllTiles = (): void => {
426
- this.map(model => ({ ...model, checked: true }));
514
+ this.map(model => {
515
+ const cloned = model.clone();
516
+ cloned.checked = true;
517
+ return cloned;
518
+ });
427
519
  };
428
520
 
429
521
  /**
430
522
  * @inheritdoc
431
523
  */
432
524
  uncheckAllTiles = (): void => {
433
- this.map(model => ({ ...model, checked: false }));
525
+ this.map(model => {
526
+ const cloned = model.clone();
527
+ cloned.checked = false;
528
+ return cloned;
529
+ });
434
530
  };
435
531
 
436
532
  /**
@@ -474,6 +570,7 @@ export class CollectionBrowserDataSource
474
570
  this.pages = newPages;
475
571
  this.numTileModels -= numChecked;
476
572
  this.host.requestUpdate();
573
+ this.host.refreshVisibleResults();
477
574
  };
478
575
 
479
576
  /**
@@ -542,15 +639,15 @@ export class CollectionBrowserDataSource
542
639
  * - Any currently-applied prefix filters
543
640
  * - The current sort options
544
641
  *
545
- * This lets us keep track of queries so we don't persist data that's
546
- * no longer relevant.
642
+ * This lets us internally keep track of queries so we don't persist data that's
643
+ * no longer relevant. Not meant to be human-readable.
547
644
  */
548
645
  get pageFetchQueryKey(): string {
549
- const profileKey = `${this.host.withinProfile}--${this.host.profileElement}`;
646
+ const profileKey = `pf;${this.host.withinProfile}--pe;${this.host.profileElement}`;
550
647
  const pageTarget = this.host.withinCollection ?? profileKey;
551
648
  const sortField = this.host.sortParam?.field ?? 'none';
552
649
  const sortDirection = this.host.sortParam?.direction ?? 'none';
553
- return `${this.fullQuery}-${pageTarget}-${this.host.searchType}-${sortField}-${sortDirection}`;
650
+ return `fq:${this.fullQuery}-pt:${pageTarget}-st:${this.host.searchType}-sf:${sortField}-sd:${sortDirection}`;
554
651
  }
555
652
 
556
653
  /**
@@ -826,8 +923,10 @@ export class CollectionBrowserDataSource
826
923
  * the current search state.
827
924
  */
828
925
  private async fetchFacets(): Promise<void> {
926
+ console.log('fetchFacets', this.id, this.host.profileElement);
829
927
  const trimmedQuery = this.host.baseQuery?.trim();
830
928
  if (!this.canPerformSearch) return;
929
+ console.log('will actually fetch facets');
831
930
 
832
931
  const { facetFetchQueryKey } = this;
833
932
 
@@ -859,7 +958,14 @@ export class CollectionBrowserDataSource
859
958
  // likely no longer valid for the newer query.
860
959
  const queryChangedSinceFetch =
861
960
  facetFetchQueryKey !== this.facetFetchQueryKey;
862
- if (queryChangedSinceFetch) return;
961
+ if (queryChangedSinceFetch) {
962
+ console.log(
963
+ 'facet query has changed since fetch, returning. new/old:',
964
+ this.facetFetchQueryKey,
965
+ facetFetchQueryKey
966
+ );
967
+ return;
968
+ }
863
969
 
864
970
  if (!success) {
865
971
  const errorMsg = searchResponse?.error?.message;
@@ -873,6 +979,7 @@ export class CollectionBrowserDataSource
873
979
  );
874
980
  }
875
981
 
982
+ this.host.setFacetsLoading(false);
876
983
  return;
877
984
  }
878
985
 
@@ -911,14 +1018,6 @@ export class CollectionBrowserDataSource
911
1018
  * if `pageNumber != 1`, defaulting to a single page.
912
1019
  */
913
1020
  async fetchPage(pageNumber: number, numInitialPages = 1): Promise<void> {
914
- console.log(
915
- `fetchPage(${pageNumber})`,
916
- this.canPerformSearch,
917
- this.hasPage(pageNumber),
918
- this.endOfDataReached,
919
- this.host.baseQuery,
920
- JSON.stringify(this.host.selectedFacets)
921
- );
922
1021
  const trimmedQuery = this.host.baseQuery?.trim();
923
1022
  if (!this.canPerformSearch) return;
924
1023
 
@@ -936,16 +1035,23 @@ export class CollectionBrowserDataSource
936
1035
  const { pageFetchQueryKey } = this;
937
1036
  const pageFetches =
938
1037
  this.pageFetchesInProgress[pageFetchQueryKey] ?? new Set();
939
- if (pageFetches.has(pageNumber)) {
940
- console.log(
941
- `Skipping fetch for page ${pageNumber} because one is already in progress`
942
- );
943
- return;
944
- }
1038
+ if (pageFetches.has(pageNumber)) return;
1039
+
945
1040
  for (let i = 0; i < numPages; i += 1) {
946
1041
  pageFetches.add(pageNumber + i);
947
1042
  }
1043
+ console.log(
1044
+ `fetchPage(${pageNumber})`,
1045
+ this.id,
1046
+ this.canPerformSearch,
1047
+ this.hasPage(pageNumber),
1048
+ this.endOfDataReached,
1049
+ this.host.baseQuery,
1050
+ JSON.stringify(this.host.selectedFacets),
1051
+ this.pageFetchQueryKey
1052
+ );
948
1053
  this.pageFetchesInProgress[pageFetchQueryKey] = pageFetches;
1054
+ this.previousQueryKey = pageFetchQueryKey;
949
1055
 
950
1056
  const sortParams = this.host.sortParam ? [this.host.sortParam] : [];
951
1057
  const params: SearchParams = {
@@ -1006,6 +1112,10 @@ export class CollectionBrowserDataSource
1006
1112
  }
1007
1113
 
1008
1114
  if (this.host.withinCollection) {
1115
+ console.log(
1116
+ 'host is within collection, setting collection info:',
1117
+ success.response.collectionExtraInfo
1118
+ );
1009
1119
  this.collectionExtraInfo = success.response.collectionExtraInfo;
1010
1120
 
1011
1121
  // For collections, we want the UI to respect the default sort option
@@ -1020,10 +1130,17 @@ export class CollectionBrowserDataSource
1020
1130
  } else if (this.host.withinProfile) {
1021
1131
  console.log(
1022
1132
  'host is within profile, setting acct info:',
1023
- success.response.accountExtraInfo
1133
+ success.response.accountExtraInfo,
1134
+ success.response.pageElements
1024
1135
  );
1025
1136
  this.accountExtraInfo = success.response.accountExtraInfo;
1026
1137
  this.pageElements = success.response.pageElements;
1138
+ } else {
1139
+ console.log(
1140
+ 'not within profile/collxn',
1141
+ this.host.withinCollection,
1142
+ this.host.withinProfile
1143
+ );
1027
1144
  }
1028
1145
 
1029
1146
  const { results, collectionTitles } = success.response;
@@ -1080,56 +1197,7 @@ export class CollectionBrowserDataSource
1080
1197
  const tiles: TileModel[] = [];
1081
1198
  results?.forEach(result => {
1082
1199
  if (!result.identifier) return;
1083
-
1084
- let loginRequired = false;
1085
- let contentWarning = false;
1086
- // Check if item and item in "modifying" collection, setting above flags
1087
- if (
1088
- result.collection?.values.length &&
1089
- result.mediatype?.value !== 'collection'
1090
- ) {
1091
- for (const collection of result.collection?.values ?? []) {
1092
- if (collection === 'loggedin') {
1093
- loginRequired = true;
1094
- if (contentWarning) break;
1095
- }
1096
- if (collection === 'no-preview') {
1097
- contentWarning = true;
1098
- if (loginRequired) break;
1099
- }
1100
- }
1101
- }
1102
-
1103
- tiles.push({
1104
- averageRating: result.avg_rating?.value,
1105
- checked: false,
1106
- collections: result.collection?.values ?? [],
1107
- collectionFilesCount: result.collection_files_count?.value ?? 0,
1108
- collectionSize: result.collection_size?.value ?? 0,
1109
- commentCount: result.num_reviews?.value ?? 0,
1110
- creator: result.creator?.value,
1111
- creators: result.creator?.values ?? [],
1112
- dateAdded: result.addeddate?.value,
1113
- dateArchived: result.publicdate?.value,
1114
- datePublished: result.date?.value,
1115
- dateReviewed: result.reviewdate?.value,
1116
- description: result.description?.values.join('\n'),
1117
- favCount: result.num_favorites?.value ?? 0,
1118
- href: this.collapseRepeatedQuotes(result.__href__?.value),
1119
- identifier: result.identifier,
1120
- issue: result.issue?.value,
1121
- itemCount: result.item_count?.value ?? 0,
1122
- mediatype: this.getMediatype(result),
1123
- snippets: result.highlight?.values ?? [],
1124
- source: result.source?.value,
1125
- subjects: result.subject?.values ?? [],
1126
- title: result.title?.value ?? '',
1127
- volume: result.volume?.value,
1128
- viewCount: result.downloads?.value ?? 0,
1129
- weeklyViewCount: result.week?.value,
1130
- loginRequired,
1131
- contentWarning,
1132
- });
1200
+ tiles.push(new TileModel(result));
1133
1201
  });
1134
1202
  this.addPage(pageNumber, tiles);
1135
1203
  const visiblePages = this.host.currentVisiblePageNumbers;
@@ -1139,44 +1207,6 @@ export class CollectionBrowserDataSource
1139
1207
  }
1140
1208
  }
1141
1209
 
1142
- /**
1143
- * Returns the mediatype string for the given search result, taking into account
1144
- * the special `favorited_search` hit type.
1145
- * @param result The search result to extract a mediatype from
1146
- */
1147
- private getMediatype(result: SearchResult): MediaType {
1148
- /**
1149
- * hit_type == 'favorited_search' is basically a new hit_type
1150
- * - we are getting from PPS.
1151
- * - which gives response for fav- collection
1152
- * - having favorited items like account/collection/item etc..
1153
- * - as user can also favorite a search result (a search page)
1154
- * - so we need to have response (having fav- items and fav- search results)
1155
- *
1156
- * if backend hit_type == 'favorited_search'
1157
- * - let's assume a "search" as new mediatype
1158
- */
1159
- if (result?.rawMetadata?.hit_type === 'favorited_search') {
1160
- return 'search';
1161
- }
1162
-
1163
- return result.mediatype?.value ?? 'data';
1164
- }
1165
-
1166
- /**
1167
- * Returns the input string, but removing one set of quotes from all instances of
1168
- * ""clauses wrapped in two sets of quotes"". This assumes the quotes are already
1169
- * URL-encoded.
1170
- *
1171
- * This should be a temporary measure to address the fact that the __href__ field
1172
- * sometimes acquires extra quotation marks during query rewriting. Once there is a
1173
- * full Lucene parser in place that handles quoted queries correctly, this can likely
1174
- * be removed.
1175
- */
1176
- private collapseRepeatedQuotes(str?: string): string | undefined {
1177
- return str?.replace(/%22%22(?!%22%22)(.+?)%22%22/g, '%22$1%22');
1178
- }
1179
-
1180
1210
  /**
1181
1211
  * Fetches the aggregation buckets for the given prefix filter type.
1182
1212
  */
@@ -1,5 +1,6 @@
1
1
  import type {
2
2
  CollectionExtraInfo,
3
+ PageElementName,
3
4
  PageType,
4
5
  SearchServiceInterface,
5
6
  SearchType,
@@ -7,6 +8,7 @@ import type {
7
8
  SortParam,
8
9
  } from '@internetarchive/search-service';
9
10
  import type { SelectedFacets, SortField } from '../models';
11
+ import type { CollectionBrowserDataSourceInterface } from './collection-browser-data-source';
10
12
 
11
13
  /**
12
14
  * A Map from collection identifiers to their corresponding collection titles.
@@ -30,7 +32,7 @@ export type PageSpecifierParams = {
30
32
  * Which specific elements of a profile page to fetch. Corresponds to individual tab data
31
33
  * (e.g., "uploads", "reviews", ...)
32
34
  */
33
- pageElements?: string[];
35
+ pageElements?: PageElementName[];
34
36
  };
35
37
 
36
38
  /**
@@ -40,7 +42,7 @@ export interface CollectionBrowserQueryState {
40
42
  baseQuery?: string;
41
43
  withinCollection?: string;
42
44
  withinProfile?: string;
43
- profileElement?: string;
45
+ profileElement?: PageElementName;
44
46
  searchType: SearchType;
45
47
  selectedFacets?: SelectedFacets;
46
48
  minSelectedDate?: string;
@@ -63,6 +65,8 @@ export interface CollectionBrowserSearchInterface
63
65
  readonly suppressFacets?: boolean;
64
66
  readonly initialPageNumber: number;
65
67
  readonly currentVisiblePageNumbers: number[];
68
+ readonly clearResultsOnEmptyQuery?: boolean;
69
+ readonly dataSource?: CollectionBrowserDataSourceInterface;
66
70
 
67
71
  getSessionId(): Promise<string>;
68
72
  setSearchResultsLoading(loading: boolean): void;
@@ -71,5 +75,6 @@ export interface CollectionBrowserSearchInterface
71
75
  setTileCount(count: number): void;
72
76
  applyDefaultCollectionSort(collectionInfo?: CollectionExtraInfo): void;
73
77
  emitEmptyResults(): void;
78
+ emitQueryStateChanged(): void;
74
79
  refreshVisibleResults(): void;
75
80
  }
@@ -6,7 +6,7 @@ import { when } from 'lit/directives/when.js';
6
6
  export interface ManageableItem {
7
7
  identifier: string;
8
8
  title?: string;
9
- date?: string;
9
+ dateStr?: string;
10
10
  }
11
11
 
12
12
  @customElement('manage-bar')