feat: Add "not found" error detail on page load (#482)
Resolves https://github.com/webrecorder/replayweb.page/issues/481 with workaround Related to https://github.com/webrecorder/browsertrix/issues/2865 ## Changes - Includes `replayNotFoundError` in `"page-loading"` event when page is finished loading as a workaround for the actual `replayNotFoundError` value not being available on `urlchange`. - Minor refactor to more strongly typed events.
This commit is contained in:
@@ -23,3 +23,4 @@ mkdocs/site/ruffle
|
|||||||
mkdocs/site/adblock
|
mkdocs/site/adblock
|
||||||
ipfs-core.min.js
|
ipfs-core.min.js
|
||||||
_site
|
_site
|
||||||
|
test-results/
|
||||||
+6
-5
@@ -11,14 +11,15 @@ import { wrapCss, updateFaviconLinks, apiPrefix } from "./misc";
|
|||||||
import { SWManager } from "./swmanager";
|
import { SWManager } from "./swmanager";
|
||||||
import { property, query } from "lit/decorators.js";
|
import { property, query } from "lit/decorators.js";
|
||||||
import type { FavIconEventDetail } from "./types";
|
import type { FavIconEventDetail } from "./types";
|
||||||
import type { EmbedReplayData, EmbedReplayEvent } from "./item";
|
import type { EmbedReplayData } from "./item";
|
||||||
|
import type { RwpPageLoadingEvent, RwpUrlChangeEvent } from "./events";
|
||||||
|
|
||||||
type IframeMessage = MessageEvent<
|
type IframeMessage = MessageEvent<
|
||||||
| EmbedReplayEvent
|
| RwpUrlChangeEvent["detail"]
|
||||||
|
| RwpPageLoadingEvent["detail"]
|
||||||
| ({
|
| ({
|
||||||
type: "favicons";
|
type: "favicons";
|
||||||
} & FavIconEventDetail)
|
} & FavIconEventDetail)
|
||||||
| { loading: boolean; type: "page-loading" }
|
|
||||||
>;
|
>;
|
||||||
|
|
||||||
const scriptSrc =
|
const scriptSrc =
|
||||||
@@ -157,7 +158,7 @@ class Embed extends LitElement {
|
|||||||
this.handleUrlChangeMessage(event.data);
|
this.handleUrlChangeMessage(event.data);
|
||||||
}
|
}
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent<EmbedReplayEvent>("rwp-url-change", {
|
new CustomEvent<RwpUrlChangeEvent["detail"]>("rwp-url-change", {
|
||||||
detail: event.data,
|
detail: event.data,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -165,7 +166,7 @@ class Embed extends LitElement {
|
|||||||
|
|
||||||
case "page-loading":
|
case "page-loading":
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent<{ loading: boolean }>("rwp-page-loading", {
|
new CustomEvent<RwpPageLoadingEvent["detail"]>("rwp-page-loading", {
|
||||||
detail: event.data,
|
detail: event.data,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import type { EmbedReplayData, TabData } from "./item";
|
||||||
|
|
||||||
|
export type TabNavEvent = CustomEvent<{
|
||||||
|
data: TabData;
|
||||||
|
reload?: boolean;
|
||||||
|
replaceLoc?: boolean;
|
||||||
|
replayNotFoundError?: boolean;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type RwpUrlChangeEvent = CustomEvent<
|
||||||
|
EmbedReplayData & {
|
||||||
|
type: "urlchange";
|
||||||
|
replayNotFoundError: boolean;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type ReplayLoadingDetail =
|
||||||
|
| { loading: true }
|
||||||
|
| { loading: false; replayNotFoundError: boolean };
|
||||||
|
|
||||||
|
export type RwpPageLoadingEvent = CustomEvent<
|
||||||
|
Required<Pick<EmbedReplayData, "url" | "ts">> & {
|
||||||
|
type: "page-loading";
|
||||||
|
} & ReplayLoadingDetail
|
||||||
|
>;
|
||||||
+5
-1
@@ -58,7 +58,11 @@ export {
|
|||||||
|
|
||||||
export type { ItemType, URLResource } from "./types";
|
export type { ItemType, URLResource } from "./types";
|
||||||
|
|
||||||
export type { EmbedReplayEvent } from "./item";
|
export type {
|
||||||
|
RwpUrlChangeEvent as EmbedReplayEvent,
|
||||||
|
RwpUrlChangeEvent,
|
||||||
|
RwpPageLoadingEvent,
|
||||||
|
} from "./events";
|
||||||
|
|
||||||
export * from "./misc";
|
export * from "./misc";
|
||||||
|
|
||||||
|
|||||||
+40
-24
@@ -57,6 +57,12 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
|||||||
|
|
||||||
import "./item-info";
|
import "./item-info";
|
||||||
import { dateTimeFormatter } from "./utils/dateTimeFormatter";
|
import { dateTimeFormatter } from "./utils/dateTimeFormatter";
|
||||||
|
import type {
|
||||||
|
ReplayLoadingDetail,
|
||||||
|
RwpPageLoadingEvent,
|
||||||
|
RwpUrlChangeEvent,
|
||||||
|
TabNavEvent,
|
||||||
|
} from "./events";
|
||||||
|
|
||||||
const RWP_SCHEME = "search://";
|
const RWP_SCHEME = "search://";
|
||||||
|
|
||||||
@@ -90,25 +96,17 @@ export type EmbedReplayData = {
|
|||||||
waczhash?: string;
|
waczhash?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EmbedReplayEvent = EmbedReplayData & {
|
|
||||||
type: "urlchange";
|
|
||||||
replayNotFoundError: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TabData = EmbedReplayData & {
|
export type TabData = EmbedReplayData & {
|
||||||
currList?: number;
|
currList?: number;
|
||||||
urlSearchType?: string;
|
urlSearchType?: string;
|
||||||
currMime?: string;
|
currMime?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TabDataUpdate = {
|
/**
|
||||||
reload: boolean;
|
* @fires coll-loaded
|
||||||
data: TabData;
|
* @fires update-title
|
||||||
replaceLoc: boolean;
|
* @fires about-show
|
||||||
replayNotFoundError: boolean;
|
*/
|
||||||
};
|
|
||||||
|
|
||||||
// ===========================================================================
|
|
||||||
class Item extends LitElement {
|
class Item extends LitElement {
|
||||||
@property({ type: Boolean })
|
@property({ type: Boolean })
|
||||||
inited = false;
|
inited = false;
|
||||||
@@ -347,7 +345,7 @@ class Item extends LitElement {
|
|||||||
lastUpdate.query !== query ||
|
lastUpdate.query !== query ||
|
||||||
lastUpdate.title !== title
|
lastUpdate.title !== title
|
||||||
) {
|
) {
|
||||||
const newUpdate: EmbedReplayEvent = {
|
const newUpdate: RwpUrlChangeEvent["detail"] = {
|
||||||
type: "urlchange",
|
type: "urlchange",
|
||||||
url,
|
url,
|
||||||
ts,
|
ts,
|
||||||
@@ -573,7 +571,7 @@ class Item extends LitElement {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onItemTabNav(event: CustomEvent<TabDataUpdate>) {
|
onItemTabNav(event: TabNavEvent) {
|
||||||
if (event.detail.reload) {
|
if (event.detail.reload) {
|
||||||
this.onRefresh(null, true);
|
this.onRefresh(null, true);
|
||||||
return;
|
return;
|
||||||
@@ -583,7 +581,9 @@ class Item extends LitElement {
|
|||||||
|
|
||||||
const { data, replaceLoc, replayNotFoundError } = event.detail;
|
const { data, replaceLoc, replayNotFoundError } = event.detail;
|
||||||
|
|
||||||
this.replayNotFoundError = replayNotFoundError;
|
if (replayNotFoundError !== undefined) {
|
||||||
|
this.replayNotFoundError = replayNotFoundError;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
targetId === this.tabData.view ||
|
targetId === this.tabData.view ||
|
||||||
@@ -1778,16 +1778,32 @@ class Item extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async onReplayLoading(
|
async onReplayLoading(
|
||||||
event: CustomEvent<{ loading: boolean; url: string; ts: string }>,
|
event: CustomEvent<{ loading: boolean; replayNotFoundError?: boolean }>,
|
||||||
) {
|
) {
|
||||||
if (
|
const { loading, replayNotFoundError } = event.detail;
|
||||||
this.embed &&
|
if (this.embed && window.parent !== window && this.isLoading !== loading) {
|
||||||
window.parent !== window &&
|
let loadingDetail: ReplayLoadingDetail;
|
||||||
this.isLoading !== event.detail.loading
|
|
||||||
) {
|
if (loading) {
|
||||||
window.parent.postMessage({ type: "page-loading", ...event.detail }, "*");
|
loadingDetail = {
|
||||||
|
loading,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
loadingDetail = {
|
||||||
|
loading,
|
||||||
|
replayNotFoundError: replayNotFoundError ?? false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const msg = {
|
||||||
|
...loadingDetail,
|
||||||
|
type: "page-loading",
|
||||||
|
url: this.tabData.url ?? "",
|
||||||
|
ts: this.tabData.ts ?? "",
|
||||||
|
} satisfies RwpPageLoadingEvent["detail"];
|
||||||
|
|
||||||
|
window.parent.postMessage(msg, "*");
|
||||||
}
|
}
|
||||||
this.isLoading = event.detail.loading;
|
this.isLoading = loading;
|
||||||
}
|
}
|
||||||
|
|
||||||
async onFavIcons(event: CustomEvent<FavIconEventDetail>) {
|
async onFavIcons(event: CustomEvent<FavIconEventDetail>) {
|
||||||
|
|||||||
+3
-3
@@ -14,6 +14,7 @@ import {
|
|||||||
dateTimeFormatter,
|
dateTimeFormatter,
|
||||||
timeFormatter,
|
timeFormatter,
|
||||||
} from "./utils/dateTimeFormatter";
|
} from "./utils/dateTimeFormatter";
|
||||||
|
import type { TabNavEvent } from "./events";
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
class PageEntry extends LitElement {
|
class PageEntry extends LitElement {
|
||||||
@@ -384,10 +385,9 @@ class PageEntry extends LitElement {
|
|||||||
return this.onReplay(event, true);
|
return this.onReplay(event, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error [// TODO: Fix this the next time the file is edited.] - TS7006 - Parameter 'data' implicitly has an 'any' type.
|
sendChangeEvent(data: URLTsChange, reload: boolean) {
|
||||||
sendChangeEvent(data, reload: boolean) {
|
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent("coll-tab-nav", {
|
new CustomEvent<TabNavEvent["detail"]>("coll-tab-nav", {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
composed: true,
|
composed: true,
|
||||||
detail: { data, reload },
|
detail: { data, reload },
|
||||||
|
|||||||
+8
-4
@@ -18,7 +18,8 @@ import fasEdit from "@fortawesome/fontawesome-free/svgs/solid/edit.svg";
|
|||||||
import type { Sorter } from "./sorter";
|
import type { Sorter } from "./sorter";
|
||||||
import type { PageEntry } from "./pageentry";
|
import type { PageEntry } from "./pageentry";
|
||||||
import type { Id, Index } from "flexsearch";
|
import type { Id, Index } from "flexsearch";
|
||||||
import type { ItemType, Page } from "./types";
|
import type { ItemType, Page, URLTsChange } from "./types";
|
||||||
|
import type { TabNavEvent } from "./events";
|
||||||
|
|
||||||
const DYNAMIC_PAGE_SIZE = 25;
|
const DYNAMIC_PAGE_SIZE = 25;
|
||||||
|
|
||||||
@@ -360,9 +361,12 @@ class Pages extends LitElement {
|
|||||||
this.filteredPages = curated;
|
this.filteredPages = curated;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error [// TODO: Fix this the next time the file is edited.] - TS7006 - Parameter 'data' implicitly has an 'any' type.
|
sendChangeEvent(data: URLTsChange) {
|
||||||
sendChangeEvent(data) {
|
this.dispatchEvent(
|
||||||
this.dispatchEvent(new CustomEvent("coll-tab-nav", { detail: { data } }));
|
new CustomEvent<TabNavEvent["detail"]>("coll-tab-nav", {
|
||||||
|
detail: { data },
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error [// TODO: Fix this the next time the file is edited.] - TS7006 - Parameter 'pages' implicitly has an 'any' type.
|
// @ts-expect-error [// TODO: Fix this the next time the file is edited.] - TS7006 - Parameter 'pages' implicitly has an 'any' type.
|
||||||
|
|||||||
+24
-6
@@ -4,9 +4,18 @@ import { property } from "lit/decorators.js";
|
|||||||
|
|
||||||
import { wrapCss } from "./misc";
|
import { wrapCss } from "./misc";
|
||||||
import rwpLogo from "~assets/brand/replaywebpage-icon-color.svg";
|
import rwpLogo from "~assets/brand/replaywebpage-icon-color.svg";
|
||||||
import type { ItemType, URLTsChange } from "./types";
|
import type { ItemType } from "./types";
|
||||||
|
import type { ReplayLoadingDetail, TabNavEvent } from "./events";
|
||||||
|
|
||||||
// ===========================================================================
|
/**
|
||||||
|
* @fires update-title
|
||||||
|
* @fires coll-tab-nav
|
||||||
|
* @fires update-title
|
||||||
|
* @fires replay-favicons
|
||||||
|
* @fires replay-loading ReplayLoadingDetail
|
||||||
|
* @fires cancel-click-download
|
||||||
|
* @fires update-download-res-url
|
||||||
|
*/
|
||||||
class Replay extends LitElement {
|
class Replay extends LitElement {
|
||||||
@property({ type: Object })
|
@property({ type: Object })
|
||||||
collInfo: ItemType | Record<string, never> | null = null;
|
collInfo: ItemType | Record<string, never> | null = null;
|
||||||
@@ -149,14 +158,14 @@ class Replay extends LitElement {
|
|||||||
(this.replayUrl && changedProperties.has("replayUrl")) ||
|
(this.replayUrl && changedProperties.has("replayUrl")) ||
|
||||||
(this.replayTS && changedProperties.has("replayTS"))
|
(this.replayTS && changedProperties.has("replayTS"))
|
||||||
) {
|
) {
|
||||||
const data: URLTsChange = {
|
const data = {
|
||||||
url: this.replayUrl,
|
url: this.replayUrl,
|
||||||
ts: this.replayTS,
|
ts: this.replayTS,
|
||||||
waczhash: this.waczhash,
|
waczhash: this.waczhash,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent("coll-tab-nav", {
|
new CustomEvent<TabNavEvent["detail"]>("coll-tab-nav", {
|
||||||
detail: {
|
detail: {
|
||||||
replaceLoc: true,
|
replaceLoc: true,
|
||||||
data,
|
data,
|
||||||
@@ -279,7 +288,12 @@ class Replay extends LitElement {
|
|||||||
|
|
||||||
clearLoading(iframe: HTMLIFrameElement | null) {
|
clearLoading(iframe: HTMLIFrameElement | null) {
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent("replay-loading", { detail: { loading: false } }),
|
new CustomEvent<ReplayLoadingDetail>("replay-loading", {
|
||||||
|
detail: {
|
||||||
|
loading: false,
|
||||||
|
replayNotFoundError: this.replayNotFoundError,
|
||||||
|
},
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this._loadPoll) {
|
if (this._loadPoll) {
|
||||||
@@ -303,7 +317,11 @@ class Replay extends LitElement {
|
|||||||
setLoading() {
|
setLoading() {
|
||||||
this.clearHilite(true);
|
this.clearHilite(true);
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent("replay-loading", { detail: { loading: true } }),
|
new CustomEvent<ReplayLoadingDetail>("replay-loading", {
|
||||||
|
detail: {
|
||||||
|
loading: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-6
@@ -8,9 +8,10 @@ import { marked } from "marked";
|
|||||||
import { getTS, getReplayLink } from "./pageutils";
|
import { getTS, getReplayLink } from "./pageutils";
|
||||||
|
|
||||||
import Split from "split.js";
|
import Split from "split.js";
|
||||||
import { type ItemType } from "./types";
|
import type { URLTsChange, ItemType } from "./types";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { dateTimeFormatter } from "./utils/dateTimeFormatter";
|
import { dateTimeFormatter } from "./utils/dateTimeFormatter";
|
||||||
|
import type { TabNavEvent } from "./events";
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
@customElement("wr-coll-story")
|
@customElement("wr-coll-story")
|
||||||
@@ -375,16 +376,20 @@ class Story extends LitElement {
|
|||||||
onReplay(event: Event) {
|
onReplay(event: Event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const data = {
|
const data = {
|
||||||
url: (event.currentTarget as Element).getAttribute("data-url"),
|
url:
|
||||||
ts: (event.currentTarget as Element).getAttribute("data-ts"),
|
(event.currentTarget as Element).getAttribute("data-url") ?? undefined,
|
||||||
|
ts: (event.currentTarget as Element).getAttribute("data-ts") ?? undefined,
|
||||||
};
|
};
|
||||||
this.sendChangeEvent(data);
|
this.sendChangeEvent(data);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error [// TODO: Fix this the next time the file is edited.] - TS7006 - Parameter 'data' implicitly has an 'any' type.
|
sendChangeEvent(data: URLTsChange) {
|
||||||
sendChangeEvent(data) {
|
this.dispatchEvent(
|
||||||
this.dispatchEvent(new CustomEvent("coll-tab-nav", { detail: { data } }));
|
new CustomEvent<TabNavEvent["detail"]>("coll-tab-nav", {
|
||||||
|
detail: { data },
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error [// TODO: Fix this the next time the file is edited.] - TS7006 - Parameter 'event' implicitly has an 'any' type.
|
// @ts-expect-error [// TODO: Fix this the next time the file is edited.] - TS7006 - Parameter 'event' implicitly has an 'any' type.
|
||||||
|
|||||||
+3
-2
@@ -27,9 +27,10 @@ export type Page = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type URLTsChange = {
|
export type URLTsChange = {
|
||||||
url: string;
|
url?: string;
|
||||||
ts: string;
|
ts?: string;
|
||||||
waczhash?: string;
|
waczhash?: string;
|
||||||
|
currList?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ItemType = {
|
export type ItemType = {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import "keyword-mark-element/lib/keyword-mark.js";
|
|||||||
import { type ItemType } from "./types";
|
import { type ItemType } from "./types";
|
||||||
import { type URLResource } from "./types";
|
import { type URLResource } from "./types";
|
||||||
import { dateTimeFormatter } from "./utils/dateTimeFormatter";
|
import { dateTimeFormatter } from "./utils/dateTimeFormatter";
|
||||||
|
import type { TabNavEvent } from "./events";
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
/**
|
/**
|
||||||
@@ -145,7 +146,9 @@ class URLResources extends LitElement {
|
|||||||
!changedProperties.has("currMime") &&
|
!changedProperties.has("currMime") &&
|
||||||
!changedProperties.has("urlSearchType");
|
!changedProperties.has("urlSearchType");
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent("coll-tab-nav", { detail: { replaceLoc, data } }),
|
new CustomEvent<TabNavEvent["detail"]>("coll-tab-nav", {
|
||||||
|
detail: { replaceLoc, data },
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -613,7 +616,11 @@ class URLResources extends LitElement {
|
|||||||
ts: event.currentTarget.getAttribute("data-ts"),
|
ts: event.currentTarget.getAttribute("data-ts"),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dispatchEvent(new CustomEvent("coll-tab-nav", { detail: { data } }));
|
this.dispatchEvent(
|
||||||
|
new CustomEvent<TabNavEvent["detail"]>("coll-tab-nav", {
|
||||||
|
detail: { data },
|
||||||
|
}),
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user