diff --git a/src/appmain.ts b/src/appmain.ts index af8ec971..7074df6f 100644 --- a/src/appmain.ts +++ b/src/appmain.ts @@ -14,6 +14,7 @@ import { IS_APP, VERSION, clickOnSpacebarPress, + updateFaviconLinks, } from "./misc"; import fasHelp from "@fortawesome/fontawesome-free/svgs/solid/question-circle.svg"; @@ -24,6 +25,7 @@ import "./item"; import "./item-index"; import "./chooser"; import { type LoadInfo } from "./item"; +import type { FavIconEventDetail } from "./types"; // =========================================================================== @customElement("replay-app-main") @@ -480,21 +482,8 @@ export class ReplayWebApp extends LitElement { } } - // @ts-expect-error [// TODO: Fix this the next time the file is edited.] - TS7006 - Parameter 'event' implicitly has an 'any' type. - onFavIcons(event) { - const head = document.querySelector("head")!; - const oldLinks = document.querySelectorAll("link[rel*='icon']"); - - for (const link of oldLinks) { - head.removeChild(link); - } - - for (const icon of event.detail.icons) { - const link = document.createElement("link"); - link.rel = icon.rel; - link.href = icon.href; - head.appendChild(link); - } + onFavIcons(event: CustomEvent) { + updateFaviconLinks(event.detail); } // @ts-expect-error [// TODO: Fix this the next time the file is edited.] - TS7006 - Parameter 'event' implicitly has an 'any' type. diff --git a/src/embed.ts b/src/embed.ts index 50c61410..e39179f1 100644 --- a/src/embed.ts +++ b/src/embed.ts @@ -7,9 +7,20 @@ import { } from "lit"; import { ifDefined } from "lit/directives/if-defined.js"; -import { wrapCss, rwpLogo } from "./misc"; +import { wrapCss, rwpLogo, updateFaviconLinks } from "./misc"; import { SWManager } from "./swmanager"; import { property } from "lit/decorators.js"; +import type { FavIconEventDetail } from "./types"; +import type { EmbedReplayData } from "./item"; + +type IframeMessage = MessageEvent< + | ({ + type: "urlchange"; + } & EmbedReplayData) + | ({ + type: "favicons"; + } & FavIconEventDetail) +>; const scriptSrc = document.currentScript && (document.currentScript as HTMLScriptElement).src; @@ -46,6 +57,7 @@ class Embed extends LitElement { @property({ type: String }) hashString: string | undefined; @property({ type: Boolean }) deepLink = false; + @property({ type: Boolean }) updateFavicons = false; @property({ type: Boolean }) sandbox = false; @property({ type: Boolean }) noSandbox: boolean | null = null; @property({ type: Boolean }) noWebWorker = false; @@ -113,30 +125,53 @@ class Embed extends LitElement { } } - // @ts-expect-error [// TODO: Fix this the next time the file is edited.] - TS7006 - Parameter 'event' implicitly has an 'any' type. - handleMessage(event) { + handleMessage(event: IframeMessage) { const iframe = this.renderRoot.querySelector("iframe"); if (iframe && event.source === iframe.contentWindow) { - if (!event.data.view) { - return; + switch (event.data.type) { + case "urlchange": + if (this.deepLink) { + this.handleUrlChangeMessage(event.data); + } + break; + + case "favicons": + if (this.updateFavicons) { + updateFaviconLinks(event.data); + } + break; } + } + } - if (event.data.title) { - this.title = event.data.title; - } + handleUrlChangeMessage(data: EmbedReplayData) { + const { url, ts, view, query, title } = data; - if (!this.deepLink) { - return; - } + if (title) { + this.title = title; + } - // TODO: Fix this the next time the file is edited. - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - const currHash = new URLSearchParams(event.data); - const url = new URL(window.location.href); - url.hash = "#" + currHash.toString(); - window.history.replaceState({}, "", url); + const params: Record = {}; + + if (url) { + params.url = url; } + if (ts) { + params.ts = ts; + } + if (query) { + params.query = query; + } + if (view && !url) { + params.view = view; + } + + const currHash = new URLSearchParams(params); + + const fullUrl = new URL(window.location.href); + fullUrl.hash = "#" + currHash.toString(); + window.history.replaceState({}, "", fullUrl); } firstUpdated() { @@ -149,7 +184,9 @@ class Embed extends LitElement { // eslint-disable-next-line @typescript-eslint/no-floating-promises this.doRegister(); - window.addEventListener("message", (event) => this.handleMessage(event)); + window.addEventListener("message", (event: IframeMessage) => + this.handleMessage(event), + ); if (this.deepLink) { this.updateFromHash(); @@ -295,12 +332,17 @@ class Embed extends LitElement { params as unknown as Record, ).toString(); - this.hashString = new URLSearchParams({ + const hashParams: Record = { url: this.url, ts: this.ts, query: this.query, - view: this.view, - }).toString(); + }; + + if (!this.url) { + hashParams.view = this.view; + } + + this.hashString = new URLSearchParams(hashParams).toString(); } } diff --git a/src/item.ts b/src/item.ts index 33590df9..21f3b4be 100644 --- a/src/item.ts +++ b/src/item.ts @@ -57,7 +57,7 @@ import fasCaretDown from "@fortawesome/fontawesome-free/svgs/solid/caret-down.sv import { RWPEmbedReceipt } from "./embed-receipt"; import Split from "split.js"; -import type { ItemType, URLResource } from "./types"; +import type { FavIconEventDetail, ItemType, URLResource } from "./types"; import type { Replay } from "./replay"; import { ifDefined } from "lit/directives/if-defined.js"; @@ -85,6 +85,21 @@ export type LoadInfo = { importCollId?: string; }; +export type EmbedReplayData = { + view?: "story" | "pages" | "resources"; + url?: string; + ts?: string; + title?: string; + query?: string; +}; + +export type TabData = EmbedReplayData & { + multiTs?: string[]; + currList?: number; + urlSearchType?: string; + currMime?: string; +}; + // =========================================================================== class Item extends LitElement { @property({ type: Boolean }) @@ -112,16 +127,7 @@ class Item extends LitElement { isLoading = false; @property({ type: Object, attribute: false }) - tabData: { - view?: "story" | "pages" | "resources"; - url?: string; - ts?: string; - multiTs?: string[]; - currList?: number; - query?: string; - urlSearchType?: string; - currMime?: string; - } = {}; + tabData: TabData = {}; @property({ type: String }) url = ""; @@ -328,7 +334,11 @@ class Item extends LitElement { } } if (this.embed && window.parent !== window) { - window.parent.postMessage(this.tabData, "*"); + const { url, ts, view, query, title }: EmbedReplayData = this.tabData; + window.parent.postMessage( + { type: "urlchange", url, ts, view, query, title }, + "*", + ); } } this._locUpdateNeeded = false; @@ -1698,11 +1708,12 @@ class Item extends LitElement { this.showSidebar = false; } - // @ts-expect-error [// TODO: Fix this the next time the file is edited.] - TS7006 - Parameter 'event' implicitly has an 'any' type. - async onFavIcons(event) { + async onFavIcons(event: CustomEvent) { + if (this.embed && window.parent !== window) { + window.parent.postMessage({ type: "favicons", ...event.detail }, "*"); + } + for (const icon of event.detail.icons) { - // TODO: Fix this the next time the file is edited. - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const resp = await fetch(icon.href); if (resp.status === 200) { const ct = resp.headers.get("Content-Type"); diff --git a/src/misc.ts b/src/misc.ts index 3cb969f0..6acca46d 100644 --- a/src/misc.ts +++ b/src/misc.ts @@ -7,6 +7,7 @@ import { styleMap } from "lit/directives/style-map.js"; import allCssRaw from "../assets/main.scss"; import rwpLogo from "../assets/logo.svg"; +import type { FavIconEventDetail } from "./types"; const apiPrefix = "./w/api"; const replayPrefix = "./w"; @@ -37,6 +38,25 @@ function clickOnSpacebarPress(event) { } } +// Update favicon links from an array of {rel, href} objects +// remove all existing icon links +// used by both embed and main app +export function updateFaviconLinks(data: FavIconEventDetail) { + const head = document.querySelector("head")!; + const oldLinks = document.querySelectorAll("link[rel*='icon']"); + + for (const link of oldLinks) { + head.removeChild(link); + } + + for (const icon of data.icons) { + const link = document.createElement("link"); + link.rel = icon.rel; + link.href = icon.href; + head.appendChild(link); + } +} + // =========================================================================== class FaIcon extends LitElement { constructor() { diff --git a/src/types.ts b/src/types.ts index a4697b73..b6e2e335 100644 --- a/src/types.ts +++ b/src/types.ts @@ -50,3 +50,10 @@ export type ItemType = { totalSize?: unknown; size?: number | string; }; + +export type FavIconEventDetail = { + icons: { + rel: string; + href: string; + }[]; +};