feat: UX improvements to collections with single URL (#2325)
Resolves https://github.com/webrecorder/browsertrix/issues/2322 ## Changes - Sets default start page if collection only contains one page - Removes status code from snapshot options
This commit is contained in:
parent
9363095d62
commit
84ae73df18
@ -14,6 +14,7 @@ import type { SelectSnapshotDetail } from "./select-collection-start-page";
|
|||||||
|
|
||||||
import { BtrixElement } from "@/classes/BtrixElement";
|
import { BtrixElement } from "@/classes/BtrixElement";
|
||||||
import type { Dialog } from "@/components/ui/dialog";
|
import type { Dialog } from "@/components/ui/dialog";
|
||||||
|
import type { Collection } from "@/types/collection";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fires btrix-change
|
* @fires btrix-change
|
||||||
@ -40,14 +41,8 @@ export class CollectionStartPageDialog extends BtrixElement {
|
|||||||
@property({ type: String })
|
@property({ type: String })
|
||||||
collectionId?: string;
|
collectionId?: string;
|
||||||
|
|
||||||
@property({ type: String })
|
@property({ type: Object })
|
||||||
homeUrl?: string | null = null;
|
collection?: Collection;
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
homePageId?: string | null = null;
|
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
homeTs?: string | null = null;
|
|
||||||
|
|
||||||
@property({ type: Boolean })
|
@property({ type: Boolean })
|
||||||
open = false;
|
open = false;
|
||||||
@ -77,8 +72,8 @@ export class CollectionStartPageDialog extends BtrixElement {
|
|||||||
private readonly thumbnailPreview?: CollectionSnapshotPreview | null;
|
private readonly thumbnailPreview?: CollectionSnapshotPreview | null;
|
||||||
|
|
||||||
willUpdate(changedProperties: PropertyValues<this>) {
|
willUpdate(changedProperties: PropertyValues<this>) {
|
||||||
if (changedProperties.has("homeUrl")) {
|
if (changedProperties.has("collection") && this.collection) {
|
||||||
this.homeView = this.homeUrl ? HomeView.URL : HomeView.Pages;
|
this.homeView = this.collection.homeUrl ? HomeView.URL : HomeView.Pages;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +87,12 @@ export class CollectionStartPageDialog extends BtrixElement {
|
|||||||
class="[--width:60rem]"
|
class="[--width:60rem]"
|
||||||
@sl-show=${() => (this.showContent = true)}
|
@sl-show=${() => (this.showContent = true)}
|
||||||
@sl-after-hide=${() => {
|
@sl-after-hide=${() => {
|
||||||
this.homeView = this.homeUrl ? HomeView.URL : HomeView.Pages;
|
if (this.collection) {
|
||||||
|
this.homeView = this.collection.homeUrl
|
||||||
|
? HomeView.URL
|
||||||
|
: HomeView.Pages;
|
||||||
|
}
|
||||||
|
|
||||||
this.isSubmitting = false;
|
this.isSubmitting = false;
|
||||||
this.selectedSnapshot = null;
|
this.selectedSnapshot = null;
|
||||||
this.showContent = false;
|
this.showContent = false;
|
||||||
@ -142,11 +142,11 @@ export class CollectionStartPageDialog extends BtrixElement {
|
|||||||
private renderPreview() {
|
private renderPreview() {
|
||||||
const snapshot =
|
const snapshot =
|
||||||
this.selectedSnapshot ||
|
this.selectedSnapshot ||
|
||||||
(this.homeUrl
|
(this.collection?.homeUrl
|
||||||
? {
|
? {
|
||||||
url: this.homeUrl,
|
url: this.collection.homeUrl,
|
||||||
ts: this.homeTs,
|
ts: this.collection.homeUrlTs,
|
||||||
pageId: this.homePageId,
|
pageId: this.collection.homeUrlPageId,
|
||||||
status: 200,
|
status: 200,
|
||||||
}
|
}
|
||||||
: null);
|
: null);
|
||||||
@ -207,8 +207,8 @@ export class CollectionStartPageDialog extends BtrixElement {
|
|||||||
|
|
||||||
if (this.homeView === HomeView.Pages) {
|
if (this.homeView === HomeView.Pages) {
|
||||||
if (
|
if (
|
||||||
!this.homePageId ||
|
!this.collection?.homeUrlPageId ||
|
||||||
this.homePageId !== this.selectedSnapshot?.pageId
|
this.collection.homeUrlPageId !== this.selectedSnapshot?.pageId
|
||||||
) {
|
) {
|
||||||
// Reset unsaved selected snapshot
|
// Reset unsaved selected snapshot
|
||||||
this.selectedSnapshot = null;
|
this.selectedSnapshot = null;
|
||||||
@ -244,8 +244,7 @@ export class CollectionStartPageDialog extends BtrixElement {
|
|||||||
<section>
|
<section>
|
||||||
<btrix-select-collection-start-page
|
<btrix-select-collection-start-page
|
||||||
.collectionId=${this.collectionId}
|
.collectionId=${this.collectionId}
|
||||||
.homeUrl=${this.homeUrl}
|
.collection=${this.collection}
|
||||||
.homeTs=${this.homeTs}
|
|
||||||
@btrix-select=${async (
|
@btrix-select=${async (
|
||||||
e: CustomEvent<SelectSnapshotDetail>,
|
e: CustomEvent<SelectSnapshotDetail>,
|
||||||
) => {
|
) => {
|
||||||
@ -280,10 +279,10 @@ export class CollectionStartPageDialog extends BtrixElement {
|
|||||||
const { homeView, useThumbnail } = serialize(form);
|
const { homeView, useThumbnail } = serialize(form);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(homeView === HomeView.Pages && !this.homePageId) ||
|
(homeView === HomeView.Pages && !this.collection?.homeUrlPageId) ||
|
||||||
(homeView === HomeView.URL &&
|
(homeView === HomeView.URL &&
|
||||||
this.selectedSnapshot &&
|
this.selectedSnapshot &&
|
||||||
this.homePageId === this.selectedSnapshot.pageId)
|
this.collection?.homeUrlPageId === this.selectedSnapshot.pageId)
|
||||||
) {
|
) {
|
||||||
// No changes to save
|
// No changes to save
|
||||||
this.open = false;
|
this.open = false;
|
||||||
@ -302,7 +301,7 @@ export class CollectionStartPageDialog extends BtrixElement {
|
|||||||
homeView === HomeView.URL &&
|
homeView === HomeView.URL &&
|
||||||
useThumbnail === "on" &&
|
useThumbnail === "on" &&
|
||||||
this.selectedSnapshot &&
|
this.selectedSnapshot &&
|
||||||
this.homePageId !== this.selectedSnapshot.pageId;
|
this.collection?.homeUrlPageId !== this.selectedSnapshot.pageId;
|
||||||
// TODO get filename from rwp?
|
// TODO get filename from rwp?
|
||||||
const fileName = `page-thumbnail_${this.selectedSnapshot?.pageId}.jpeg`;
|
const fileName = `page-thumbnail_${this.selectedSnapshot?.pageId}.jpeg`;
|
||||||
let file: File | undefined;
|
let file: File | undefined;
|
||||||
|
@ -10,12 +10,15 @@ import { html, type PropertyValues } from "lit";
|
|||||||
import { customElement, property, query, state } from "lit/decorators.js";
|
import { customElement, property, query, state } from "lit/decorators.js";
|
||||||
import { when } from "lit/directives/when.js";
|
import { when } from "lit/directives/when.js";
|
||||||
import debounce from "lodash/fp/debounce";
|
import debounce from "lodash/fp/debounce";
|
||||||
import sortBy from "lodash/fp/sortBy";
|
import filter from "lodash/fp/filter";
|
||||||
|
import flow from "lodash/fp/flow";
|
||||||
|
import orderBy from "lodash/fp/orderBy";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
|
|
||||||
import { BtrixElement } from "@/classes/BtrixElement";
|
import { BtrixElement } from "@/classes/BtrixElement";
|
||||||
import type { Combobox } from "@/components/ui/combobox";
|
import type { Combobox } from "@/components/ui/combobox";
|
||||||
import type { APIPaginatedList, APIPaginationQuery } from "@/types/api";
|
import type { APIPaginatedList, APIPaginationQuery } from "@/types/api";
|
||||||
|
import type { Collection } from "@/types/collection";
|
||||||
import type { UnderlyingFunction } from "@/types/utils";
|
import type { UnderlyingFunction } from "@/types/utils";
|
||||||
import { tw } from "@/utils/tailwind";
|
import { tw } from "@/utils/tailwind";
|
||||||
|
|
||||||
@ -39,7 +42,11 @@ export type SelectSnapshotDetail = {
|
|||||||
|
|
||||||
const DEFAULT_PROTOCOL = "http";
|
const DEFAULT_PROTOCOL = "http";
|
||||||
|
|
||||||
const sortByTs = sortBy<Snapshot>("ts");
|
// TODO Check if backend can sort and filter snapshots instead
|
||||||
|
const sortByTs = flow(
|
||||||
|
filter<Snapshot>(({ status }) => status < 300),
|
||||||
|
orderBy<Snapshot>("ts")("desc"),
|
||||||
|
) as (snapshots: Snapshot[]) => Snapshot[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fires btrix-select
|
* @fires btrix-select
|
||||||
@ -50,11 +57,8 @@ export class SelectCollectionStartPage extends BtrixElement {
|
|||||||
@property({ type: String })
|
@property({ type: String })
|
||||||
collectionId?: string;
|
collectionId?: string;
|
||||||
|
|
||||||
@property({ type: String })
|
@property({ type: Object })
|
||||||
homeUrl?: string | null = null;
|
collection?: Collection;
|
||||||
|
|
||||||
@property({ type: String })
|
|
||||||
homeTs?: string | null = null;
|
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
private searchQuery = "";
|
private searchQuery = "";
|
||||||
@ -82,14 +86,13 @@ export class SelectCollectionStartPage extends BtrixElement {
|
|||||||
return this.selectedSnapshot;
|
return this.selectedSnapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
updated(changedProperties: PropertyValues<this>) {
|
protected willUpdate(changedProperties: PropertyValues<this>) {
|
||||||
if (changedProperties.has("homeUrl") && this.homeUrl) {
|
if (changedProperties.has("collection") && this.collection) {
|
||||||
if (this.input) {
|
void this.initSelection(this.collection);
|
||||||
this.input.value = this.homeUrl;
|
|
||||||
}
|
|
||||||
this.searchQuery = this.homeUrl;
|
|
||||||
void this.initSelection();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updated(changedProperties: PropertyValues<this>) {
|
||||||
if (changedProperties.has("selectedSnapshot")) {
|
if (changedProperties.has("selectedSnapshot")) {
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent<SelectSnapshotDetail>("btrix-select", {
|
new CustomEvent<SelectSnapshotDetail>("btrix-select", {
|
||||||
@ -106,26 +109,50 @@ export class SelectCollectionStartPage extends BtrixElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async initSelection() {
|
private async initSelection(collection: Collection) {
|
||||||
await this.updateComplete;
|
if (!collection.homeUrl && collection.pageCount !== 1) {
|
||||||
await this.searchResults.taskComplete;
|
return;
|
||||||
|
|
||||||
if (this.homeUrl && this.searchResults.value) {
|
|
||||||
this.selectedPage = this.searchResults.value.items.find(
|
|
||||||
({ url }) => url === this.homeUrl,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.selectedPage && this.homeTs) {
|
|
||||||
this.selectedSnapshot = this.selectedPage.snapshots.find(
|
|
||||||
({ ts }) => ts === this.homeTs,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pageUrls = await this.getPageUrls({
|
||||||
|
id: collection.id,
|
||||||
|
urlPrefix: collection.homeUrl || "",
|
||||||
|
pageSize: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!pageUrls.total) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const startPage = pageUrls.items[0];
|
||||||
|
|
||||||
|
if (this.input) {
|
||||||
|
this.input.value = startPage.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectedPage = this.formatPage(startPage);
|
||||||
|
|
||||||
|
const homeTs = collection.homeUrlTs;
|
||||||
|
|
||||||
|
this.selectedSnapshot = homeTs
|
||||||
|
? this.selectedPage.snapshots.find(({ ts }) => ts === homeTs)
|
||||||
|
: this.selectedPage.snapshots[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format page for display
|
||||||
|
* @TODO Check if backend can sort and filter snapshots instead
|
||||||
|
*/
|
||||||
|
private formatPage(page: Page) {
|
||||||
|
return {
|
||||||
|
...page,
|
||||||
|
snapshots: sortByTs(page.snapshots),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly searchResults = new Task(this, {
|
private readonly searchResults = new Task(this, {
|
||||||
task: async ([searchValue], { signal }) => {
|
task: async ([searchValue], { signal }) => {
|
||||||
const searchResults = await this.getPageUrls(
|
const pageUrls = await this.getPageUrls(
|
||||||
{
|
{
|
||||||
id: this.collectionId!,
|
id: this.collectionId!,
|
||||||
urlPrefix: searchValue,
|
urlPrefix: searchValue,
|
||||||
@ -133,7 +160,7 @@ export class SelectCollectionStartPage extends BtrixElement {
|
|||||||
signal,
|
signal,
|
||||||
);
|
);
|
||||||
|
|
||||||
return searchResults;
|
return pageUrls;
|
||||||
},
|
},
|
||||||
args: () => [this.searchQuery] as const,
|
args: () => [this.searchQuery] as const,
|
||||||
});
|
});
|
||||||
@ -150,6 +177,7 @@ export class SelectCollectionStartPage extends BtrixElement {
|
|||||||
value=${this.selectedSnapshot?.pageId || ""}
|
value=${this.selectedSnapshot?.pageId || ""}
|
||||||
?required=${this.selectedPage && !this.selectedSnapshot}
|
?required=${this.selectedPage && !this.selectedSnapshot}
|
||||||
?disabled=${!this.selectedPage}
|
?disabled=${!this.selectedPage}
|
||||||
|
hoist
|
||||||
@sl-change=${async (e: SlChangeEvent) => {
|
@sl-change=${async (e: SlChangeEvent) => {
|
||||||
const { value } = e.currentTarget as SlSelect;
|
const { value } = e.currentTarget as SlSelect;
|
||||||
|
|
||||||
@ -160,35 +188,22 @@ export class SelectCollectionStartPage extends BtrixElement {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
${when(
|
${when(this.selectedPage, this.renderSnapshotOptions)}
|
||||||
this.selectedSnapshot,
|
|
||||||
(snapshot) => html`
|
|
||||||
<btrix-badge
|
|
||||||
slot="suffix"
|
|
||||||
variant=${snapshot.status < 300 ? "success" : "danger"}
|
|
||||||
>${snapshot.status}</btrix-badge
|
|
||||||
>
|
|
||||||
`,
|
|
||||||
)}
|
|
||||||
${when(this.selectedPage, (item) =>
|
|
||||||
item.snapshots.map(
|
|
||||||
({ pageId, ts, status }) => html`
|
|
||||||
<sl-option value=${pageId}>
|
|
||||||
${this.localize.date(ts)}
|
|
||||||
<btrix-badge
|
|
||||||
slot="suffix"
|
|
||||||
variant=${status < 300 ? "success" : "danger"}
|
|
||||||
>${status}</btrix-badge
|
|
||||||
>
|
|
||||||
</sl-option>
|
|
||||||
`,
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</sl-select>
|
</sl-select>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly renderSnapshotOptions = ({ snapshots }: Page) => {
|
||||||
|
return html`
|
||||||
|
${snapshots.map(
|
||||||
|
({ pageId, ts }) => html`
|
||||||
|
<sl-option value=${pageId}> ${this.localize.date(ts)} </sl-option>
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
private renderPageSearch() {
|
private renderPageSearch() {
|
||||||
let prefix: {
|
let prefix: {
|
||||||
icon: string;
|
icon: string;
|
||||||
@ -223,7 +238,7 @@ export class SelectCollectionStartPage extends BtrixElement {
|
|||||||
id="pageUrlInput"
|
id="pageUrlInput"
|
||||||
label=${msg("Page URL")}
|
label=${msg("Page URL")}
|
||||||
placeholder=${msg("Start typing a URL...")}
|
placeholder=${msg("Start typing a URL...")}
|
||||||
clearable
|
?clearable=${this.collection && this.collection.pageCount > 1}
|
||||||
@sl-focus=${() => {
|
@sl-focus=${() => {
|
||||||
this.resetInputValidity();
|
this.resetInputValidity();
|
||||||
this.combobox?.show();
|
this.combobox?.show();
|
||||||
@ -295,7 +310,7 @@ export class SelectCollectionStartPage extends BtrixElement {
|
|||||||
this.selectedSnapshot = undefined;
|
this.selectedSnapshot = undefined;
|
||||||
} else if (results.total === 1) {
|
} else if (results.total === 1) {
|
||||||
// Choose only option, e.g. for copy-paste
|
// Choose only option, e.g. for copy-paste
|
||||||
this.selectedPage = this.searchResults.value.items[0];
|
this.selectedPage = this.formatPage(this.searchResults.value.items[0]);
|
||||||
this.selectedSnapshot = this.selectedPage.snapshots[0];
|
this.selectedSnapshot = this.selectedPage.snapshots[0];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -326,11 +341,7 @@ export class SelectCollectionStartPage extends BtrixElement {
|
|||||||
this.input.value = item.url;
|
this.input.value = item.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.selectedPage = {
|
this.selectedPage = this.formatPage(item);
|
||||||
...item,
|
|
||||||
// TODO check if backend can sort
|
|
||||||
snapshots: sortByTs(item.snapshots).reverse(),
|
|
||||||
};
|
|
||||||
|
|
||||||
this.combobox?.hide();
|
this.combobox?.hide();
|
||||||
|
|
||||||
|
@ -265,9 +265,7 @@ export class CollectionDetail extends BtrixElement {
|
|||||||
}}
|
}}
|
||||||
@sl-hide=${async () => (this.openDialogName = undefined)}
|
@sl-hide=${async () => (this.openDialogName = undefined)}
|
||||||
collectionId=${this.collectionId}
|
collectionId=${this.collectionId}
|
||||||
.homeUrl=${this.collection?.homeUrl}
|
.collection=${this.collection}
|
||||||
.homePageId=${this.collection?.homeUrlPageId}
|
|
||||||
.homeTs=${this.collection?.homeUrlTs}
|
|
||||||
?replayLoaded=${this.isRwpLoaded}
|
?replayLoaded=${this.isRwpLoaded}
|
||||||
></btrix-collection-replay-dialog>
|
></btrix-collection-replay-dialog>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user