@internetarchive/collection-browser 1.14.17-alpha.2 → 1.14.17-alpha.21
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.
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/src/app-root.d.ts +1 -0
- package/dist/src/app-root.js +38 -0
- package/dist/src/app-root.js.map +1 -1
- package/dist/src/collection-browser.d.ts +2 -15
- package/dist/src/collection-browser.js +22 -29
- package/dist/src/collection-browser.js.map +1 -1
- package/dist/src/data-source/collection-browser-data-source.d.ts +55 -32
- package/dist/src/data-source/collection-browser-data-source.js +155 -162
- package/dist/src/data-source/collection-browser-data-source.js.map +1 -1
- package/dist/src/data-source/models.d.ts +6 -3
- package/dist/src/data-source/models.js.map +1 -1
- package/dist/src/manage/manage-bar.d.ts +1 -1
- package/dist/src/manage/manage-bar.js.map +1 -1
- package/dist/src/models.d.ts +20 -4
- package/dist/src/models.js +105 -0
- package/dist/src/models.js.map +1 -1
- package/dist/src/sort-filter-bar/sort-filter-bar.js +25 -16
- package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -1
- package/dist/src/tiles/grid/item-tile.d.ts +1 -0
- package/dist/src/tiles/grid/item-tile.js +28 -1
- package/dist/src/tiles/grid/item-tile.js.map +1 -1
- package/dist/src/tiles/grid/tile-stats.js +13 -8
- package/dist/src/tiles/grid/tile-stats.js.map +1 -1
- package/dist/src/tiles/list/tile-list-compact.js +1 -1
- package/dist/src/tiles/list/tile-list-compact.js.map +1 -1
- package/dist/src/tiles/list/tile-list.d.ts +1 -0
- package/dist/src/tiles/list/tile-list.js +32 -1
- package/dist/src/tiles/list/tile-list.js.map +1 -1
- package/dist/src/tiles/tile-dispatcher.js +3 -2
- package/dist/src/tiles/tile-dispatcher.js.map +1 -1
- package/dist/src/tiles/tile-display-value-provider.d.ts +6 -2
- package/dist/src/tiles/tile-display-value-provider.js +15 -1
- package/dist/src/tiles/tile-display-value-provider.js.map +1 -1
- package/dist/src/utils/collapse-repeated-quotes.d.ts +11 -0
- package/dist/src/utils/collapse-repeated-quotes.js +14 -0
- package/dist/src/utils/collapse-repeated-quotes.js.map +1 -0
- package/dist/src/utils/resolve-mediatype.d.ts +8 -0
- package/dist/src/utils/resolve-mediatype.js +24 -0
- package/dist/src/utils/resolve-mediatype.js.map +1 -0
- package/dist/test/collection-browser.test.js +39 -19
- package/dist/test/collection-browser.test.js.map +1 -1
- package/dist/test/collection-facets/more-facets-content.test.js +2 -2
- package/dist/test/collection-facets/more-facets-content.test.js.map +1 -1
- package/dist/test/collection-facets.test.js +5 -0
- package/dist/test/collection-facets.test.js.map +1 -1
- package/dist/test/item-image.test.js +33 -34
- package/dist/test/item-image.test.js.map +1 -1
- package/dist/test/mocks/mock-search-responses.d.ts +1 -0
- package/dist/test/mocks/mock-search-responses.js +62 -0
- package/dist/test/mocks/mock-search-responses.js.map +1 -1
- package/dist/test/sort-filter-bar/sort-filter-bar.test.js +41 -4
- package/dist/test/sort-filter-bar/sort-filter-bar.test.js.map +1 -1
- package/dist/test/tiles/hover/hover-pane-controller.test.js +18 -17
- package/dist/test/tiles/hover/hover-pane-controller.test.js.map +1 -1
- package/package.json +2 -2
- package/src/app-root.ts +39 -0
- package/src/collection-browser.ts +24 -40
- package/src/data-source/collection-browser-data-source.ts +160 -132
- package/src/data-source/models.ts +6 -2
- package/src/manage/manage-bar.ts +1 -1
- package/src/models.ts +154 -3
- package/src/sort-filter-bar/sort-filter-bar.ts +26 -16
- package/src/tiles/grid/item-tile.ts +36 -1
- package/src/tiles/grid/tile-stats.ts +12 -7
- package/src/tiles/list/tile-list-compact.ts +1 -1
- package/src/tiles/list/tile-list.ts +43 -5
- package/src/tiles/tile-dispatcher.ts +2 -1
- package/src/tiles/tile-display-value-provider.ts +20 -2
- package/src/utils/collapse-repeated-quotes.ts +13 -0
- package/src/utils/resolve-mediatype.ts +26 -0
- package/test/collection-browser.test.ts +74 -19
- package/test/collection-facets/more-facets-content.test.ts +4 -2
- package/test/collection-facets.test.ts +5 -0
- package/test/item-image.test.ts +34 -36
- package/test/mocks/mock-search-responses.ts +66 -0
- package/test/sort-filter-bar/sort-filter-bar.test.ts +50 -4
- package/test/tiles/hover/hover-pane-controller.test.ts +19 -17
- package/dist/src/data-source/data-source-fetch-provider.d.ts +0 -13
- package/dist/src/data-source/data-source-fetch-provider.js +0 -61
- package/dist/src/data-source/data-source-fetch-provider.js.map +0 -1
- package/dist/src/selected-facets.d.ts +0 -67
- package/dist/src/selected-facets.js +0 -149
- package/dist/src/selected-facets.js.map +0 -1
- package/src/data-source/data-source-fetch-provider.ts +0 -79
- package/src/selected-facets.ts +0 -216
package/src/models.ts
CHANGED
|
@@ -1,42 +1,193 @@
|
|
|
1
1
|
import type { MediaType } from '@internetarchive/field-parsers';
|
|
2
2
|
import {
|
|
3
3
|
AggregationSortType,
|
|
4
|
+
SearchResult,
|
|
4
5
|
SortDirection,
|
|
5
6
|
} from '@internetarchive/search-service';
|
|
7
|
+
import { collapseRepeatedQuotes } from './utils/collapse-repeated-quotes';
|
|
8
|
+
import { resolveMediatype } from './utils/resolve-mediatype';
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Flags that can affect the visibility of content on a tile
|
|
12
|
+
*/
|
|
13
|
+
interface TileFlags {
|
|
14
|
+
loginRequired: boolean;
|
|
15
|
+
contentWarning: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Class for converting & storing raw search results in the correct format for UI tiles.
|
|
20
|
+
*/
|
|
21
|
+
export class TileModel {
|
|
8
22
|
averageRating?: number;
|
|
23
|
+
|
|
24
|
+
captureDates?: Date[]; // List of capture dates for a URL, used on profile Web Archives tiles
|
|
25
|
+
|
|
9
26
|
checked: boolean; // Whether this tile is currently checked for item management functions
|
|
27
|
+
|
|
10
28
|
collectionIdentifier?: string;
|
|
29
|
+
|
|
11
30
|
collectionName?: string;
|
|
31
|
+
|
|
12
32
|
collectionFilesCount: number;
|
|
33
|
+
|
|
13
34
|
collections: string[];
|
|
35
|
+
|
|
14
36
|
collectionSize: number;
|
|
37
|
+
|
|
15
38
|
commentCount: number;
|
|
39
|
+
|
|
16
40
|
creator?: string;
|
|
41
|
+
|
|
17
42
|
creators: string[];
|
|
43
|
+
|
|
44
|
+
dateStr?: string; // A string representation of the publication date, used strictly for passing preformatted dates to the parent
|
|
45
|
+
|
|
18
46
|
dateAdded?: Date; // Date added to public search (software-defined) [from: addeddate]
|
|
47
|
+
|
|
19
48
|
dateArchived?: Date; // Date archived (software-defined) item created on archive.org [from: publicdate]
|
|
49
|
+
|
|
20
50
|
datePublished?: Date; // Date work published in the world (user-defined) [from: date]
|
|
51
|
+
|
|
21
52
|
dateReviewed?: Date; // Date reviewed (user-created) most recent review [from: reviewdate]
|
|
53
|
+
|
|
22
54
|
description?: string;
|
|
55
|
+
|
|
23
56
|
favCount: number;
|
|
57
|
+
|
|
24
58
|
href?: string;
|
|
25
|
-
|
|
59
|
+
|
|
60
|
+
identifier?: string;
|
|
61
|
+
|
|
26
62
|
issue?: string;
|
|
63
|
+
|
|
27
64
|
itemCount: number;
|
|
65
|
+
|
|
28
66
|
mediatype: MediaType;
|
|
67
|
+
|
|
29
68
|
source?: string;
|
|
69
|
+
|
|
30
70
|
snippets?: string[];
|
|
71
|
+
|
|
31
72
|
subjects: string[];
|
|
73
|
+
|
|
32
74
|
title: string;
|
|
33
|
-
|
|
75
|
+
|
|
76
|
+
viewCount?: number;
|
|
77
|
+
|
|
34
78
|
volume?: string;
|
|
79
|
+
|
|
35
80
|
weeklyViewCount?: number;
|
|
81
|
+
|
|
36
82
|
loginRequired: boolean;
|
|
83
|
+
|
|
37
84
|
contentWarning: boolean;
|
|
85
|
+
|
|
86
|
+
constructor(result: SearchResult) {
|
|
87
|
+
const flags = this.getFlags(result);
|
|
88
|
+
|
|
89
|
+
this.averageRating = result.avg_rating?.value;
|
|
90
|
+
this.captureDates = result.capture_dates?.values;
|
|
91
|
+
this.checked = false;
|
|
92
|
+
this.collections = result.collection?.values ?? [];
|
|
93
|
+
this.collectionFilesCount = result.collection_files_count?.value ?? 0;
|
|
94
|
+
this.collectionSize = result.collection_size?.value ?? 0;
|
|
95
|
+
this.commentCount = result.num_reviews?.value ?? 0;
|
|
96
|
+
this.creator = result.creator?.value;
|
|
97
|
+
this.creators = result.creator?.values ?? [];
|
|
98
|
+
this.dateAdded = result.addeddate?.value;
|
|
99
|
+
this.dateArchived = result.publicdate?.value;
|
|
100
|
+
this.datePublished = result.date?.value;
|
|
101
|
+
this.dateReviewed = result.reviewdate?.value;
|
|
102
|
+
this.description = result.description?.values.join('\n');
|
|
103
|
+
this.favCount = result.num_favorites?.value ?? 0;
|
|
104
|
+
this.href = collapseRepeatedQuotes(result.__href__?.value);
|
|
105
|
+
this.identifier = result.identifier;
|
|
106
|
+
this.issue = result.issue?.value;
|
|
107
|
+
this.itemCount = result.item_count?.value ?? 0;
|
|
108
|
+
this.mediatype = resolveMediatype(result);
|
|
109
|
+
this.snippets = result.highlight?.values ?? [];
|
|
110
|
+
this.source = result.source?.value;
|
|
111
|
+
this.subjects = result.subject?.values ?? [];
|
|
112
|
+
this.title = result.title?.value ?? '';
|
|
113
|
+
this.volume = result.volume?.value;
|
|
114
|
+
this.viewCount = result.downloads?.value;
|
|
115
|
+
this.weeklyViewCount = result.week?.value;
|
|
116
|
+
this.loginRequired = flags.loginRequired;
|
|
117
|
+
this.contentWarning = flags.contentWarning;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Copies the contents of this TileModel onto a new instance
|
|
122
|
+
*/
|
|
123
|
+
clone(): TileModel {
|
|
124
|
+
const cloned = new TileModel({});
|
|
125
|
+
cloned.averageRating = this.averageRating;
|
|
126
|
+
cloned.captureDates = this.captureDates;
|
|
127
|
+
cloned.checked = this.checked;
|
|
128
|
+
cloned.collections = this.collections;
|
|
129
|
+
cloned.collectionFilesCount = this.collectionFilesCount;
|
|
130
|
+
cloned.collectionSize = this.collectionSize;
|
|
131
|
+
cloned.commentCount = this.commentCount;
|
|
132
|
+
cloned.creator = this.creator;
|
|
133
|
+
cloned.creators = this.creators;
|
|
134
|
+
cloned.dateStr = this.dateStr;
|
|
135
|
+
cloned.dateAdded = this.dateAdded;
|
|
136
|
+
cloned.dateArchived = this.dateArchived;
|
|
137
|
+
cloned.datePublished = this.datePublished;
|
|
138
|
+
cloned.dateReviewed = this.dateReviewed;
|
|
139
|
+
cloned.description = this.description;
|
|
140
|
+
cloned.favCount = this.favCount;
|
|
141
|
+
cloned.href = this.href;
|
|
142
|
+
cloned.identifier = this.identifier;
|
|
143
|
+
cloned.issue = this.issue;
|
|
144
|
+
cloned.itemCount = this.itemCount;
|
|
145
|
+
cloned.mediatype = this.mediatype;
|
|
146
|
+
cloned.snippets = this.snippets;
|
|
147
|
+
cloned.source = this.source;
|
|
148
|
+
cloned.subjects = this.subjects;
|
|
149
|
+
cloned.title = this.title;
|
|
150
|
+
cloned.volume = this.volume;
|
|
151
|
+
cloned.viewCount = this.viewCount;
|
|
152
|
+
cloned.weeklyViewCount = this.weeklyViewCount;
|
|
153
|
+
cloned.loginRequired = this.loginRequired;
|
|
154
|
+
cloned.contentWarning = this.contentWarning;
|
|
155
|
+
return cloned;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Determines the appropriate tile flags for the given search result
|
|
160
|
+
* (login required and/or content warning)
|
|
161
|
+
*/
|
|
162
|
+
private getFlags(result: SearchResult): TileFlags {
|
|
163
|
+
const flags: TileFlags = {
|
|
164
|
+
loginRequired: false,
|
|
165
|
+
contentWarning: false,
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// Check if item and item in "modifying" collection, setting above flags
|
|
169
|
+
if (
|
|
170
|
+
result.collection?.values.length &&
|
|
171
|
+
result.mediatype?.value !== 'collection'
|
|
172
|
+
) {
|
|
173
|
+
for (const collection of result.collection?.values ?? []) {
|
|
174
|
+
if (collection === 'loggedin') {
|
|
175
|
+
flags.loginRequired = true;
|
|
176
|
+
if (flags.contentWarning) break;
|
|
177
|
+
}
|
|
178
|
+
if (collection === 'no-preview') {
|
|
179
|
+
flags.contentWarning = true;
|
|
180
|
+
if (flags.loginRequired) break;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return flags;
|
|
186
|
+
}
|
|
38
187
|
}
|
|
39
188
|
|
|
189
|
+
export type RequestKind = 'full' | 'hits' | 'aggregations';
|
|
190
|
+
|
|
40
191
|
export type CollectionDisplayMode = 'grid' | 'list-compact' | 'list-detail';
|
|
41
192
|
|
|
42
193
|
export type TileDisplayMode =
|
|
@@ -216,28 +216,37 @@ export class SortFilterBar
|
|
|
216
216
|
private disconnectResizeObserver(
|
|
217
217
|
resizeObserver: SharedResizeObserverInterface
|
|
218
218
|
) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
219
|
+
if (this.sortSelectorContainer) {
|
|
220
|
+
resizeObserver.removeObserver({
|
|
221
|
+
target: this.sortSelectorContainer,
|
|
222
|
+
handler: this,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
223
225
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
226
|
+
if (this.desktopSortContainer) {
|
|
227
|
+
resizeObserver.removeObserver({
|
|
228
|
+
target: this.desktopSortContainer,
|
|
229
|
+
handler: this,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
228
232
|
}
|
|
229
233
|
|
|
230
234
|
private setupResizeObserver() {
|
|
231
235
|
if (!this.resizeObserver) return;
|
|
232
|
-
this.resizeObserver.addObserver({
|
|
233
|
-
target: this.sortSelectorContainer,
|
|
234
|
-
handler: this,
|
|
235
|
-
});
|
|
236
236
|
|
|
237
|
-
this.
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
if (this.sortSelectorContainer) {
|
|
238
|
+
this.resizeObserver.addObserver({
|
|
239
|
+
target: this.sortSelectorContainer,
|
|
240
|
+
handler: this,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (this.desktopSortContainer) {
|
|
245
|
+
this.resizeObserver.addObserver({
|
|
246
|
+
target: this.desktopSortContainer,
|
|
247
|
+
handler: this,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
241
250
|
}
|
|
242
251
|
|
|
243
252
|
handleResize(entry: ResizeObserverEntry): void {
|
|
@@ -1107,6 +1116,7 @@ export class SortFilterBar
|
|
|
1107
1116
|
height: 100%;
|
|
1108
1117
|
padding-left: 5px;
|
|
1109
1118
|
font-size: 1.4rem;
|
|
1119
|
+
font-family: var(--ia-theme-base-font-family);
|
|
1110
1120
|
line-height: 2;
|
|
1111
1121
|
color: var(--ia-theme-primary-text-color, #2c2c2c);
|
|
1112
1122
|
white-space: nowrap;
|
|
@@ -4,6 +4,7 @@ import { customElement, property } from 'lit/decorators.js';
|
|
|
4
4
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
5
5
|
import { msg } from '@lit/localize';
|
|
6
6
|
|
|
7
|
+
import { map } from 'lit/directives/map';
|
|
7
8
|
import { DateFormat, formatDate } from '../../utils/format-date';
|
|
8
9
|
import { isFirstMillisecondOfUTCYear } from '../../utils/local-date-from-utc';
|
|
9
10
|
import { BaseTileComponent } from '../base-tile-component';
|
|
@@ -57,7 +58,7 @@ export class ItemTile extends BaseTileComponent {
|
|
|
57
58
|
${this.isSortedByDate
|
|
58
59
|
? this.sortedDateInfoTemplate
|
|
59
60
|
: this.creatorTemplate}
|
|
60
|
-
${this.textSnippetsTemplate}
|
|
61
|
+
${this.webArchivesCaptureDatesTemplate} ${this.textSnippetsTemplate}
|
|
61
62
|
</div>
|
|
62
63
|
|
|
63
64
|
<tile-stats
|
|
@@ -172,6 +173,26 @@ export class ItemTile extends BaseTileComponent {
|
|
|
172
173
|
`;
|
|
173
174
|
}
|
|
174
175
|
|
|
176
|
+
private get webArchivesCaptureDatesTemplate():
|
|
177
|
+
| TemplateResult
|
|
178
|
+
| typeof nothing {
|
|
179
|
+
if (!this.model?.captureDates || !this.model?.title) return nothing;
|
|
180
|
+
|
|
181
|
+
return html`
|
|
182
|
+
<ul class="capture-dates">
|
|
183
|
+
${map(
|
|
184
|
+
this.model.captureDates,
|
|
185
|
+
date => html`<li>
|
|
186
|
+
${this.displayValueProvider.webArchivesCaptureLink(
|
|
187
|
+
this.model!.title,
|
|
188
|
+
date
|
|
189
|
+
)}
|
|
190
|
+
</li>`
|
|
191
|
+
)}
|
|
192
|
+
</ul>
|
|
193
|
+
`;
|
|
194
|
+
}
|
|
195
|
+
|
|
175
196
|
private get isSortedByDate(): boolean {
|
|
176
197
|
return ['date', 'reviewdate', 'addeddate', 'publicdate'].includes(
|
|
177
198
|
this.sortParam?.field as string
|
|
@@ -200,10 +221,24 @@ export class ItemTile extends BaseTileComponent {
|
|
|
200
221
|
return [
|
|
201
222
|
baseTileStyles,
|
|
202
223
|
css`
|
|
224
|
+
a:link {
|
|
225
|
+
text-decoration: none;
|
|
226
|
+
color: var(--ia-theme-link-color, #4b64ff);
|
|
227
|
+
}
|
|
228
|
+
a:hover {
|
|
229
|
+
text-decoration: underline;
|
|
230
|
+
}
|
|
231
|
+
|
|
203
232
|
.container {
|
|
204
233
|
border: 1px solid ${tileBorderColor};
|
|
205
234
|
}
|
|
206
235
|
|
|
236
|
+
.capture-dates {
|
|
237
|
+
margin: 0;
|
|
238
|
+
padding: 0 5px;
|
|
239
|
+
list-style-type: none;
|
|
240
|
+
}
|
|
241
|
+
|
|
207
242
|
text-snippet-block {
|
|
208
243
|
--containerLeftMargin: 5px;
|
|
209
244
|
--containerTopMargin: 5px;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { css, CSSResultGroup, html, LitElement } from 'lit';
|
|
2
2
|
import { customElement, property } from 'lit/decorators.js';
|
|
3
3
|
|
|
4
|
+
import { msg } from '@lit/localize';
|
|
4
5
|
import { favoriteFilledIcon } from '../../assets/img/icons/favorite-filled';
|
|
5
6
|
import { reviewsIcon } from '../../assets/img/icons/reviews';
|
|
6
7
|
import { uploadIcon } from '../../assets/img/icons/upload';
|
|
@@ -33,8 +34,8 @@ export class TileStats extends LitElement {
|
|
|
33
34
|
|
|
34
35
|
const uploadsOrViewsTitle =
|
|
35
36
|
this.mediatype === 'account'
|
|
36
|
-
? `${this.itemCount} uploads`
|
|
37
|
-
: `${this.viewCount} ${this.viewLabel ?? 'all-time views'}`;
|
|
37
|
+
? `${this.itemCount ?? 0} uploads`
|
|
38
|
+
: `${this.viewCount ?? 0} ${this.viewLabel ?? 'all-time views'}`;
|
|
38
39
|
|
|
39
40
|
return html`
|
|
40
41
|
<div class="item-stats">
|
|
@@ -43,17 +44,21 @@ export class TileStats extends LitElement {
|
|
|
43
44
|
</p>
|
|
44
45
|
<ul id="stats-row">
|
|
45
46
|
<li class="col">
|
|
46
|
-
<p class="sr-only"
|
|
47
|
+
<p class="sr-only">${msg('Mediatype:')}</p>
|
|
47
48
|
<mediatype-icon .mediatype=${this.mediatype}></mediatype-icon>
|
|
48
49
|
</li>
|
|
49
50
|
<li class="col" title="${uploadsOrViewsTitle}">
|
|
50
51
|
${this.mediatype === 'account' ? uploadIcon : viewsIcon}
|
|
51
52
|
<p class="status-text">
|
|
52
53
|
<span class="sr-only">
|
|
53
|
-
${this.mediatype === 'account'
|
|
54
|
+
${this.mediatype === 'account'
|
|
55
|
+
? msg('Uploads:')
|
|
56
|
+
: msg('Views:')}
|
|
54
57
|
</span>
|
|
55
58
|
${formatCount(
|
|
56
|
-
this.mediatype === 'account'
|
|
59
|
+
this.mediatype === 'account'
|
|
60
|
+
? this.itemCount ?? 0
|
|
61
|
+
: this.viewCount ?? 0,
|
|
57
62
|
'short',
|
|
58
63
|
'short'
|
|
59
64
|
)}
|
|
@@ -62,14 +67,14 @@ export class TileStats extends LitElement {
|
|
|
62
67
|
<li class="col" title="${formattedFavCount} favorites">
|
|
63
68
|
${favoriteFilledIcon}
|
|
64
69
|
<p class="status-text">
|
|
65
|
-
<span class="sr-only"
|
|
70
|
+
<span class="sr-only">${msg('Favorites:')}</span>
|
|
66
71
|
${formattedFavCount}
|
|
67
72
|
</p>
|
|
68
73
|
</li>
|
|
69
74
|
<li class="col reviews" title="${formattedReviewCount} reviews">
|
|
70
75
|
${reviewsIcon}
|
|
71
76
|
<p class="status-text">
|
|
72
|
-
<span class="sr-only"
|
|
77
|
+
<span class="sr-only">${msg('Reviews:')}</span>
|
|
73
78
|
${formattedReviewCount}
|
|
74
79
|
</p>
|
|
75
80
|
</li>
|
|
@@ -37,7 +37,7 @@ export class TileList extends BaseTileComponent {
|
|
|
37
37
|
@property({ type: Object })
|
|
38
38
|
collectionTitles?: CollectionTitles;
|
|
39
39
|
|
|
40
|
-
@state() private collectionLinks: TemplateResult[] = [];
|
|
40
|
+
@state() private collectionLinks: (TemplateResult | typeof nothing)[] = [];
|
|
41
41
|
|
|
42
42
|
render() {
|
|
43
43
|
return html`
|
|
@@ -107,6 +107,7 @@ export class TileList extends BaseTileComponent {
|
|
|
107
107
|
${this.itemLineTemplate} ${this.creatorTemplate}
|
|
108
108
|
<div id="dates-line">
|
|
109
109
|
${this.datePublishedTemplate} ${this.dateSortByTemplate}
|
|
110
|
+
${this.webArchivesCaptureDatesTemplate}
|
|
110
111
|
</div>
|
|
111
112
|
<div id="views-line">
|
|
112
113
|
${this.viewsTemplate} ${this.ratingTemplate} ${this.reviewsTemplate}
|
|
@@ -238,6 +239,7 @@ export class TileList extends BaseTileComponent {
|
|
|
238
239
|
this.sortParam?.field === 'week'
|
|
239
240
|
? this.model?.weeklyViewCount // weekly views
|
|
240
241
|
: this.model?.viewCount; // all-time views
|
|
242
|
+
if (viewCount == null) return nothing;
|
|
241
243
|
|
|
242
244
|
// when its a search-tile, we don't have any stats to show
|
|
243
245
|
if (this.model?.mediatype === 'search') {
|
|
@@ -245,7 +247,7 @@ export class TileList extends BaseTileComponent {
|
|
|
245
247
|
}
|
|
246
248
|
|
|
247
249
|
return this.metadataTemplate(
|
|
248
|
-
`${formatCount(viewCount
|
|
250
|
+
`${formatCount(viewCount, this.formatSize)}`,
|
|
249
251
|
msg('Views')
|
|
250
252
|
);
|
|
251
253
|
}
|
|
@@ -309,6 +311,26 @@ export class TileList extends BaseTileComponent {
|
|
|
309
311
|
return !!this.model?.snippets?.length;
|
|
310
312
|
}
|
|
311
313
|
|
|
314
|
+
private get webArchivesCaptureDatesTemplate():
|
|
315
|
+
| TemplateResult
|
|
316
|
+
| typeof nothing {
|
|
317
|
+
if (!this.model?.captureDates || !this.model?.title) return nothing;
|
|
318
|
+
|
|
319
|
+
return html`
|
|
320
|
+
<ul class="capture-dates">
|
|
321
|
+
${map(
|
|
322
|
+
this.model.captureDates,
|
|
323
|
+
date => html`<li>
|
|
324
|
+
${this.displayValueProvider.webArchivesCaptureLink(
|
|
325
|
+
this.model!.title,
|
|
326
|
+
date
|
|
327
|
+
)}
|
|
328
|
+
</li>`
|
|
329
|
+
)}
|
|
330
|
+
</ul>
|
|
331
|
+
`;
|
|
332
|
+
}
|
|
333
|
+
|
|
312
334
|
// Utility functions
|
|
313
335
|
// eslint-disable-next-line default-param-last
|
|
314
336
|
private metadataTemplate(text: any, label = '', id?: string) {
|
|
@@ -345,10 +367,12 @@ export class TileList extends BaseTileComponent {
|
|
|
345
367
|
}
|
|
346
368
|
|
|
347
369
|
private detailsLink(
|
|
348
|
-
identifier
|
|
370
|
+
identifier?: string,
|
|
349
371
|
text?: string,
|
|
350
372
|
isCollection = false
|
|
351
|
-
): TemplateResult {
|
|
373
|
+
): TemplateResult | typeof nothing {
|
|
374
|
+
if (!identifier) return nothing;
|
|
375
|
+
|
|
352
376
|
const linkText = text ?? identifier;
|
|
353
377
|
const linkHref = this.displayValueProvider.itemPageUrl(
|
|
354
378
|
identifier,
|
|
@@ -392,7 +416,7 @@ export class TileList extends BaseTileComponent {
|
|
|
392
416
|
// Note: quirk of Lit: need to replace collectionLinks array,
|
|
393
417
|
// otherwise it will not re-render. Can't simply alter the array.
|
|
394
418
|
this.collectionLinks = [];
|
|
395
|
-
const newCollectionLinks: TemplateResult[] = [];
|
|
419
|
+
const newCollectionLinks: (TemplateResult | typeof nothing)[] = [];
|
|
396
420
|
for (const collection of this.model.collections) {
|
|
397
421
|
// Don't include favorites or collections that are meant to be suppressed
|
|
398
422
|
if (
|
|
@@ -622,6 +646,20 @@ export class TileList extends BaseTileComponent {
|
|
|
622
646
|
#views-line {
|
|
623
647
|
flex-wrap: wrap;
|
|
624
648
|
}
|
|
649
|
+
|
|
650
|
+
.capture-dates {
|
|
651
|
+
margin: 0;
|
|
652
|
+
padding: 0;
|
|
653
|
+
list-style-type: none;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
.capture-dates a:link {
|
|
657
|
+
text-decoration: none;
|
|
658
|
+
color: var(--ia-theme-link-color, #4b64ff);
|
|
659
|
+
}
|
|
660
|
+
.capture-dates a:hover {
|
|
661
|
+
text-decoration: underline;
|
|
662
|
+
}
|
|
625
663
|
`;
|
|
626
664
|
}
|
|
627
665
|
}
|
|
@@ -184,7 +184,8 @@ export class TileDispatcher
|
|
|
184
184
|
this.enableHoverPane &&
|
|
185
185
|
!!this.tileDisplayMode &&
|
|
186
186
|
TileDispatcher.HOVER_PANE_DISPLAY_MODES[this.tileDisplayMode] &&
|
|
187
|
-
this.model?.mediatype !== 'search' // don't show hover panes on search tiles
|
|
187
|
+
this.model?.mediatype !== 'search' && // don't show hover panes on search tiles
|
|
188
|
+
!this.model?.captureDates // don't show hover panes on web archive tiles
|
|
188
189
|
);
|
|
189
190
|
}
|
|
190
191
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { nothing } from 'lit';
|
|
1
|
+
import { TemplateResult, html, nothing } from 'lit';
|
|
2
2
|
import { msg, str } from '@lit/localize';
|
|
3
3
|
import type { SortParam } from '@internetarchive/search-service';
|
|
4
4
|
import type { TileModel } from '../models';
|
|
5
|
+
import { formatDate } from '../utils/format-date';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* A class encapsulating shared logic for converting model values into display values
|
|
@@ -96,11 +97,28 @@ export class TileDisplayValueProvider {
|
|
|
96
97
|
* item is specified to be a collection (default false).
|
|
97
98
|
*/
|
|
98
99
|
itemPageUrl(
|
|
99
|
-
identifier
|
|
100
|
+
identifier?: string,
|
|
100
101
|
isCollection = false
|
|
101
102
|
): string | typeof nothing {
|
|
102
103
|
if (!identifier || this.baseNavigationUrl == null) return nothing;
|
|
103
104
|
const basePath = isCollection ? this.collectionPagePath : '/details/';
|
|
104
105
|
return `${this.baseNavigationUrl}${basePath}${identifier}`;
|
|
105
106
|
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Produces a template for a link to a single web capture of the given URL and date
|
|
110
|
+
*/
|
|
111
|
+
webArchivesCaptureLink(url: string, date: Date): TemplateResult {
|
|
112
|
+
// Convert the date into the format used to identify wayback captures (e.g., '20150102124550')
|
|
113
|
+
const captureDateStr = date
|
|
114
|
+
.toISOString()
|
|
115
|
+
.replace(/[TZ:-]/g, '')
|
|
116
|
+
.replace(/\..*/, '');
|
|
117
|
+
const captureHref = `https://web.archive.org/web/${captureDateStr}/${encodeURIComponent(
|
|
118
|
+
url
|
|
119
|
+
)}`;
|
|
120
|
+
const captureText = formatDate(date, 'long');
|
|
121
|
+
|
|
122
|
+
return html` <a href=${captureHref}> ${captureText} </a> `;
|
|
123
|
+
}
|
|
106
124
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the input string, but removing one set of quotes from all instances of
|
|
3
|
+
* ""clauses wrapped in two sets of quotes"". This assumes the quotes are already
|
|
4
|
+
* URL-encoded.
|
|
5
|
+
*
|
|
6
|
+
* This should be a temporary measure to address the fact that the __href__ field
|
|
7
|
+
* sometimes acquires extra quotation marks during query rewriting. Once there is a
|
|
8
|
+
* full Lucene parser in place that handles quoted queries correctly, this can likely
|
|
9
|
+
* be removed.
|
|
10
|
+
*/
|
|
11
|
+
export function collapseRepeatedQuotes(str?: string): string | undefined {
|
|
12
|
+
return str?.replace(/%22%22(?!%22%22)(.+?)%22%22/g, '%22$1%22');
|
|
13
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { MediaType } from '@internetarchive/field-parsers';
|
|
2
|
+
import type { SearchResult } from '@internetarchive/search-service';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns the mediatype string for the given search result, taking into account
|
|
6
|
+
* the special `favorited_search` hit type.
|
|
7
|
+
* @param result The search result to extract a mediatype from
|
|
8
|
+
*/
|
|
9
|
+
export function resolveMediatype(result: SearchResult): MediaType {
|
|
10
|
+
/**
|
|
11
|
+
* hit_type == 'favorited_search' is basically a new hit_type
|
|
12
|
+
* - we are getting from PPS.
|
|
13
|
+
* - which gives response for fav- collection
|
|
14
|
+
* - having favorited items like account/collection/item etc..
|
|
15
|
+
* - as user can also favorite a search result (a search page)
|
|
16
|
+
* - so we need to have response (having fav- items and fav- search results)
|
|
17
|
+
*
|
|
18
|
+
* if backend hit_type == 'favorited_search'
|
|
19
|
+
* - let's assume a "search" as new mediatype
|
|
20
|
+
*/
|
|
21
|
+
if (result?.rawMetadata?.hit_type === 'favorited_search') {
|
|
22
|
+
return 'search';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return result.mediatype?.value ?? 'data';
|
|
26
|
+
}
|