Migrate ReplayWebApp, Coll and Replay to TS (#233)

This commit is contained in:
sua yoo
2023-09-18 10:43:29 -07:00
committed by GitHub
parent 79d2f1585c
commit 4d55efa341
6 changed files with 220 additions and 165 deletions
+79 -56
View File
@@ -1,4 +1,6 @@
import { LitElement, html, css } from "lit"; import { LitElement, html, css } from "lit";
import { property, state } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { import {
wrapCss, wrapCss,
rwpLogo, rwpLogo,
@@ -14,24 +16,60 @@ import { SWManager } from "./swmanager";
// =========================================================================== // ===========================================================================
class ReplayWebApp extends LitElement { class ReplayWebApp extends LitElement {
@property({ type: Boolean })
inited = false;
@property({ type: Object })
pageParams: URLSearchParams = new URLSearchParams();
@property({ type: String })
sourceUrl: string | null = null;
@property({ type: Boolean })
navMenuShown = false;
@property({ type: Boolean })
showAbout = false;
@property({ type: Boolean })
showFileDropOverlay = false;
@property({ type: String })
collTitle: string | null = null;
@property({ type: Object })
loadInfo: any = null;
@property({ type: String })
embed: any = null;
@property({ type: String })
collPageUrl = "";
@property({ type: String })
pageTitle = "";
@property({ type: Boolean })
pageReplay = false;
@property({ type: String })
source: any = null;
@property({ type: Boolean })
skipRuffle = false;
@property({ type: Object })
swErrorMsg: any = null;
private swName?: string;
private swmanager: SWManager | null;
private useRuffle = false;
private droppedFile: File | null = null;
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
constructor(swName = __SW_NAME__) { constructor(swName = __SW_NAME__) {
super(); super();
this.sourceUrl = null;
this.collTitle = null;
this.showAbout = false;
this.showFileDropOverlay = false;
this.pageParams = new URLSearchParams();
this.inited = false;
this.navMenuShown = false;
this.collPageUrl = "";
this.pageTitle = "";
this.pageReplay = false;
this.loadInfo = null;
this.swName = swName; this.swName = swName;
this.swmanager = null; this.swmanager = null;
@@ -58,21 +96,21 @@ class ReplayWebApp extends LitElement {
}); });
this.addEventListener("drop", (dragEvent) => { this.addEventListener("drop", (dragEvent) => {
this.droppedFile = dragEvent.dataTransfer.files[0]; this.droppedFile = dragEvent.dataTransfer?.files[0] || null;
this.showFileDropOverlay = false; this.showFileDropOverlay = false;
dragEvent.preventDefault(); dragEvent.preventDefault();
}); });
this.maybeStartFileDrop = (dragEvent) => {
if (this.sourceUrl) {
// A source is already loaded. Don't allow dropping a file.
return;
}
this.showFileDropOverlay = true;
dragEvent.preventDefault();
};
} }
private maybeStartFileDrop = (dragEvent) => {
if (this.sourceUrl) {
// A source is already loaded. Don't allow dropping a file.
return;
}
this.showFileDropOverlay = true;
dragEvent.preventDefault();
};
get appName() { get appName() {
return "ReplayWeb.page"; return "ReplayWeb.page";
} }
@@ -81,27 +119,6 @@ class ReplayWebApp extends LitElement {
return window.location.pathname; return window.location.pathname;
} }
static get properties() {
return {
inited: { type: Boolean },
pageParams: { type: Object },
sourceUrl: { type: String },
navMenuShown: { type: Boolean },
showAbout: { type: Boolean },
showFileDropOverlay: { type: Boolean },
collTitle: { type: String },
loadInfo: { type: Object },
embed: { type: String },
collPageUrl: { type: String },
pageTitle: { type: String },
pageReplay: { type: Boolean },
source: { type: String },
skipRuffle: { type: Boolean },
swErrorMsg: { type: Object },
};
}
static get styles() { static get styles() {
return wrapCss(ReplayWebApp.appStyles); return wrapCss(ReplayWebApp.appStyles);
} }
@@ -364,10 +381,10 @@ class ReplayWebApp extends LitElement {
renderColl() { renderColl() {
return html` <wr-coll return html` <wr-coll
.loadInfo="${this.loadInfo}" .loadInfo="${this.loadInfo}"
sourceUrl="${this.sourceUrl}" sourceUrl="${this.sourceUrl || ""}"
embed="${this.embed}" embed="${this.embed}"
appName="${this.appName}" appName="${this.appName}"
swName="${this.swName}" swName="${ifDefined(this.swName)}"
.appLogo="${this.mainLogo}" .appLogo="${this.mainLogo}"
@replay-favicons=${this.onFavIcons} @replay-favicons=${this.onFavIcons}
@update-title=${this.onTitle} @update-title=${this.onTitle}
@@ -436,7 +453,7 @@ class ReplayWebApp extends LitElement {
.register() .register()
.catch( .catch(
() => () =>
(this.swErrorMsg = this.swmanager.renderErrorReport(this.mainLogo)), (this.swErrorMsg = this.swmanager?.renderErrorReport(this.mainLogo)),
); );
window.addEventListener("popstate", () => { window.addEventListener("popstate", () => {
@@ -451,7 +468,7 @@ class ReplayWebApp extends LitElement {
} }
onFavIcons(event) { onFavIcons(event) {
const head = document.querySelector("head"); const head = document.querySelector("head")!;
const oldLinks = document.querySelectorAll("link[rel*='icon']"); const oldLinks = document.querySelectorAll("link[rel*='icon']");
for (const link of oldLinks) { for (const link of oldLinks) {
@@ -470,7 +487,7 @@ class ReplayWebApp extends LitElement {
// This is a workaround, since this app's routing doesn't permit normal // This is a workaround, since this app's routing doesn't permit normal
// following of in-page anchors. // following of in-page anchors.
event.preventDefault(); event.preventDefault();
this.renderRoot.querySelector("#skip-main-target").focus(); (this.renderRoot.querySelector("#skip-main-target") as HTMLElement).focus();
} }
onNavMenu(event) { onNavMenu(event) {
@@ -487,7 +504,9 @@ class ReplayWebApp extends LitElement {
(event) => { (event) => {
event.preventDefault(); event.preventDefault();
this.navMenuShown = false; this.navMenuShown = false;
this.renderRoot.querySelector("#menu-button").focus(); (
this.renderRoot.querySelector("#menu-button") as HTMLElement
).focus();
}, },
{ once: true }, { once: true },
); );
@@ -497,7 +516,9 @@ class ReplayWebApp extends LitElement {
if (event.key == "Escape") { if (event.key == "Escape") {
event.preventDefault(); event.preventDefault();
this.navMenuShown = false; this.navMenuShown = false;
this.renderRoot.querySelector("#menu-button").focus(); (
this.renderRoot.querySelector("#menu-button") as HTMLElement
).focus();
} }
}, },
{ once: true }, { once: true },
@@ -510,7 +531,7 @@ class ReplayWebApp extends LitElement {
this.pageParams = new URLSearchParams(window.location.search); this.pageParams = new URLSearchParams(window.location.search);
// Google Drive // Google Drive
let state = this.pageParams.get("state"); let state: any = this.pageParams.get("state");
if (state) { if (state) {
try { try {
state = JSON.parse(state); state = JSON.parse(state);
@@ -557,7 +578,9 @@ class ReplayWebApp extends LitElement {
if (this.pageParams.get("config")) { if (this.pageParams.get("config")) {
try { try {
this.loadInfo.extraConfig = JSON.parse(this.pageParams.get("config")); this.loadInfo.extraConfig = JSON.parse(
this.pageParams.get("config") || "",
);
} catch (e) { } catch (e) {
console.log("invalid config: " + e); console.log("invalid config: " + e);
} }
@@ -596,7 +619,7 @@ class ReplayWebApp extends LitElement {
} }
if (this.pageParams.get("swName")) { if (this.pageParams.get("swName")) {
this.swName = this.pageParams.get("swName"); this.swName = this.pageParams.get("swName") || undefined;
} }
if (IS_APP && this.sourceUrl.startsWith("file://")) { if (IS_APP && this.sourceUrl.startsWith("file://")) {
+38 -46
View File
@@ -1,4 +1,5 @@
import { LitElement, html, css } from "lit"; import { LitElement, html, css } from "lit";
import { property, state } from "lit/decorators.js";
import { wrapCss, apiPrefix } from "./misc"; import { wrapCss, apiPrefix } from "./misc";
import { map } from "lit/directives/map.js"; import { map } from "lit/directives/map.js";
@@ -11,26 +12,41 @@ import fasArrowDown from "@fortawesome/fontawesome-free/svgs/solid/angle-double-
import fasSearch from "@fortawesome/fontawesome-free/svgs/solid/search.svg"; import fasSearch from "@fortawesome/fontawesome-free/svgs/solid/search.svg";
import type { Coll } from "./types";
// =========================================================================== // ===========================================================================
class CollIndex extends LitElement { class CollIndex extends LitElement {
@property({ type: Array })
colls: Coll[] = [];
@property({ type: String })
query = "";
@property({ type: Array })
filteredColls: any[] = [];
@property({ type: Array })
sortedColls: any[] | null = null;
@property({ type: Boolean })
hideHeader: any = null;
@property({ type: String })
dateName = "Date Loaded";
@property({ type: String })
headerName = "Loaded Archives";
@state()
private _deleting: any = {};
private typeFilter = "";
private indexParams = "";
constructor() { constructor() {
super(); super();
this.colls = [];
this.filteredColls = [];
this.sortedColls = null;
this.query = "";
this.hideHeader = localStorage.getItem("index:hideHeader") === "1"; this.hideHeader = localStorage.getItem("index:hideHeader") === "1";
this._deleting = {};
this.dateName = "Date Loaded";
this.headerName = "Loaded Archives";
this.typeFilter = "";
this.indexParams = "";
} }
get sortKeys() { get sortKeys() {
@@ -45,25 +61,6 @@ class CollIndex extends LitElement {
]; ];
} }
static get properties() {
return {
colls: { type: Array },
query: { type: String },
filteredColls: { type: Array },
sortedColls: { type: Array },
hideHeader: { type: Boolean },
_deleting: { type: Object },
dateName: { type: String },
headerName: { type: String },
};
}
firstUpdated() { firstUpdated() {
this.loadColls(); this.loadColls();
} }
@@ -363,19 +360,14 @@ class CollIndex extends LitElement {
// =========================================================================== // ===========================================================================
class CollInfo extends LitElement { class CollInfo extends LitElement {
constructor() { @property({ type: Object })
super(); coll!: Coll | Record<string, never>;
this.detailed = false;
this.canDelete = false;
}
static get properties() { @property({ type: Boolean })
return { detailed = false;
coll: { type: Object },
detailed: { type: Boolean }, @property({ type: Boolean })
canDelete: { type: Boolean }, canDelete = false;
};
}
static get styles() { static get styles() {
return wrapCss(CollInfo.compStyles); return wrapCss(CollInfo.compStyles);
@@ -526,7 +518,7 @@ class CollInfo extends LitElement {
<ol style="padding: revert"> <ol style="padding: revert">
${map( ${map(
coll.resources, coll.resources,
(resource) => (resource: any) =>
html`<li> html`<li>
<a href="${resource.path}">${resource.name + "\n"}</a> <a href="${resource.path}">${resource.name + "\n"}</a>
</li>`, </li>`,
+42 -26
View File
@@ -1,6 +1,5 @@
import { LitElement, html, css } from "lit"; import { LitElement, html, css } from "lit";
import { property, state } from "lit/decorators.js"; import { property, state } from "lit/decorators.js";
import { map } from "lit/directives/map.js";
import type { import type {
SlDropdown, SlDropdown,
SlMenu, SlMenu,
@@ -47,6 +46,8 @@ import fasCaretDown from "@fortawesome/fontawesome-free/svgs/solid/caret-down.sv
import { RWPEmbedReceipt } from "./embed-receipt.js"; import { RWPEmbedReceipt } from "./embed-receipt.js";
import Split from "split.js"; import Split from "split.js";
import type { Coll as CollInfo } from "./types";
import type { Replay } from "./replay"; import type { Replay } from "./replay";
const RWP_SCHEME = "search://"; const RWP_SCHEME = "search://";
@@ -66,7 +67,7 @@ class Coll extends LitElement {
showSidebar: boolean | null = null; showSidebar: boolean | null = null;
@property({ type: Object, attribute: false }) @property({ type: Object, attribute: false })
collInfo: any = null; collInfo: CollInfo | Record<string, never> | null = null;
@property({ type: String }) @property({ type: String })
coll = ""; coll = "";
@@ -96,13 +97,13 @@ class Coll extends LitElement {
ts = ""; ts = "";
@property({ type: Boolean }) @property({ type: Boolean })
isFullscreen: any; isFullscreen: boolean | null = null;
@property({ type: Boolean }) @property({ type: Boolean })
menuActive = false; menuActive = false;
@property({ type: String }) @property({ type: String })
embed: any; embed: string | null = null;
@property({ type: Boolean }) @property({ type: Boolean })
embedDropdownActive = false; embedDropdownActive = false;
@@ -135,24 +136,30 @@ class Coll extends LitElement {
autoUpdateInterval = 10; autoUpdateInterval = 10;
@property({ type: String }) @property({ type: String })
swName: any; swName: any = null;
private tabNames = ["pages", "story", "resources", "info"]; private splitter: any = null;
private tabLabels = {
private _replaceLoc = false;
private _locUpdateNeeded = false;
private _locationHash = "";
private _autoUpdater: null | Promise<void> = null;
private observer?: IntersectionObserver;
private readonly tabNames = ["pages", "story", "resources", "info"];
private readonly tabLabels = {
pages: "Pages", pages: "Pages",
story: "Story", story: "Story",
resources: "URLs", resources: "URLs",
info: "Archive Info", info: "Archive Info",
}; };
private splitter: any = null;
private _replaceLoc = false;
private _locUpdateNeeded = false;
private _locationHash = "";
private _autoUpdater: Promise<void> | null = null;
private observer?: IntersectionObserver;
constructor() { constructor() {
super(); super();
this.showSidebar = localStorage.getItem("pages:showSidebar") === "1"; this.showSidebar = localStorage.getItem("pages:showSidebar") === "1";
} }
@@ -301,7 +308,9 @@ class Coll extends LitElement {
configureSplitter() { configureSplitter() {
if (this.tabData.url && this.showSidebar) { if (this.tabData.url && this.showSidebar) {
const contents = this.renderRoot.querySelector("#contents"); const contents = this.renderRoot.querySelector(
"#contents",
) as HTMLElement;
const replay = this.renderRoot.querySelector("wr-coll-replay") as Replay; const replay = this.renderRoot.querySelector("wr-coll-replay") as Replay;
if (contents && replay && !this.splitter) { if (contents && replay && !this.splitter) {
@@ -379,15 +388,17 @@ class Coll extends LitElement {
}); });
} }
if (!this.collInfo.title) { if (!this.collInfo!.title) {
this.collInfo.title = this.collInfo.filename; this.collInfo!.title = this.collInfo!.filename;
} }
if (this.embed === "replayonly" || this.embed === "replay-with-info") { if (this.embed === "replayonly" || this.embed === "replay-with-info") {
this.showSidebar = false; this.showSidebar = false;
} }
this.hasStory = this.collInfo.desc || this.collInfo.lists.length; this.hasStory = Boolean(
this.collInfo!.desc || this.collInfo!.lists?.length,
);
this.dispatchEvent( this.dispatchEvent(
new CustomEvent("coll-loaded", { new CustomEvent("coll-loaded", {
@@ -435,12 +446,12 @@ class Coll extends LitElement {
} }
if ( if (
this.collInfo.coll && this.collInfo?.coll &&
(!this.tabData.view || !this.tabNames.includes(this.tabData.view)) (!this.tabData.view || !this.tabNames.includes(this.tabData.view))
) { ) {
const view = this.hasStory const view = this.hasStory
? "story" ? "story"
: this.editable || this.collInfo.pages.length : this.editable || this.collInfo.pages?.length
? "pages" ? "pages"
: "resources"; : "resources";
@@ -452,7 +463,11 @@ class Coll extends LitElement {
if (this.tabData.url && this.tabData.url.startsWith("page:")) { if (this.tabData.url && this.tabData.url.startsWith("page:")) {
const pageIndex = Number(this.tabData.url.slice("page:".length)); const pageIndex = Number(this.tabData.url.slice("page:".length));
if (!isNaN(pageIndex) && pageIndex < this.collInfo.pages.length) { if (
this.collInfo?.pages &&
!isNaN(pageIndex) &&
pageIndex < this.collInfo.pages.length
) {
const page = this.collInfo.pages[pageIndex]; const page = this.collInfo.pages[pageIndex];
this.tabData.url = page.url; this.tabData.url = page.url;
this.tabData.ts = getPageDateTS(page).timestamp; this.tabData.ts = getPageDateTS(page).timestamp;
@@ -852,7 +867,7 @@ class Coll extends LitElement {
if (this.collInfo && !this.collInfo.coll) { if (this.collInfo && !this.collInfo.coll) {
return html` <wr-loader return html` <wr-loader
.loadInfo="${this.loadInfo}" .loadInfo="${this.loadInfo}"
embed="${this.embed}" embed="${this.embed || ""}"
swName="${this.swName}" swName="${this.swName}"
.coll="${this.coll}" .coll="${this.coll}"
sourceUrl="${this.sourceUrl || ""}" sourceUrl="${this.sourceUrl || ""}"
@@ -1442,7 +1457,7 @@ class Coll extends LitElement {
} }
return html`<rwp-embed-receipt return html`<rwp-embed-receipt
.collInfo=${this.collInfo} .collInfo=${this.collInfo || {}}
url=${this.url} url=${this.url}
ts=${this.ts} ts=${this.ts}
.appLogo=${this.appLogo} .appLogo=${this.appLogo}
@@ -1465,6 +1480,7 @@ class Coll extends LitElement {
} }
renderCollInfo() { renderCollInfo() {
if (!this.collInfo) return;
return html` <div class="info-bg"> return html` <div class="info-bg">
<wr-coll-info <wr-coll-info
class="is-list" class="is-list"
@@ -1490,7 +1506,7 @@ class Coll extends LitElement {
${isInfo ? this.renderCollInfo() : html``} ${isInfo ? this.renderCollInfo() : html``}
${isStory ${isStory
? html` <wr-coll-story ? html` <wr-coll-story
.collInfo="${this.collInfo}" .collInfo="${this.collInfo || {}}"
.active="${isStory}" .active="${isStory}"
currList="${this.tabData.currList || 0}" currList="${this.tabData.currList || 0}"
@coll-tab-nav="${this.onCollTabNav}" @coll-tab-nav="${this.onCollTabNav}"
@@ -1503,7 +1519,7 @@ class Coll extends LitElement {
: ""} : ""}
${isResources ${isResources
? html` <wr-coll-resources ? html` <wr-coll-resources
.collInfo="${this.collInfo}" .collInfo="${this.collInfo || {}}"
.active="${isResources}" .active="${isResources}"
query="${this.tabData.query || ""}" query="${this.tabData.query || ""}"
urlSearchType="${this.tabData.urlSearchType || ""}" urlSearchType="${this.tabData.urlSearchType || ""}"
@@ -1642,7 +1658,7 @@ class Coll extends LitElement {
} }
async deleteFully(reload = false) { async deleteFully(reload = false) {
const deleteURL = this.collInfo.apiPrefix + (reload ? "?reload=1" : ""); const deleteURL = this.collInfo!.apiPrefix + (reload ? "?reload=1" : "");
const resp = await fetch(deleteURL, { const resp = await fetch(deleteURL, {
method: "DELETE", method: "DELETE",
@@ -1663,7 +1679,7 @@ class Coll extends LitElement {
onSubmit(event) { onSubmit(event) {
event.preventDefault(); event.preventDefault();
const input = this.renderRoot.querySelector("input")!; const input = this.renderRoot.querySelector("input") as HTMLInputElement;
if (input.value) { if (input.value) {
this.navigateTo(input.value); this.navigateTo(input.value);
} else { } else {
+1
View File
@@ -1 +1,2 @@
declare module "*.svg"; declare module "*.svg";
declare var __SW_NAME__: string;
+43 -37
View File
@@ -1,44 +1,49 @@
import { LitElement, html, css } from "lit"; import { LitElement, html, css } from "lit";
import { property, state } from "lit/decorators.js";
import { wrapCss, rwpLogo } from "./misc"; import { wrapCss, rwpLogo } from "./misc";
import type { Coll } from "./types";
// =========================================================================== // ===========================================================================
class Replay extends LitElement { class Replay extends LitElement {
constructor() { @property({ type: Object })
super(); collInfo: Coll | Record<string, never> | null = null;
this.replayUrl = "";
this.replayTS = "";
this.actualTS = "";
this.url = "";
this.ts = "";
this.title = "";
this.collInfo = null;
this.showAuth = false; @property({ type: String })
this.reauthWait = null; sourceUrl: any = null;
this.authFileHandle = null;
}
static get properties() { // external url set from parent
return { @property({ type: String })
collInfo: { type: Object }, url = "";
sourceUrl: { type: String },
// external url set from parent @property({ type: String })
url: { type: String }, ts = "";
ts: { type: String },
// actual replay url // actual replay url
replayUrl: { type: String }, @property({ type: String })
replayTS: { type: String }, replayUrl = "";
actualTS: { type: String },
title: { type: String },
iframeUrl: { type: String }, @property({ type: String })
replayTS = "";
showAuth: { type: Boolean }, @property({ type: String })
authFileHandle: { type: Object }, actualTS = "";
};
} @property({ type: String })
title = "";
@property({ type: String })
iframeUrl: any = null;
@property({ type: Boolean })
showAuth = false;
@property({ type: Object })
authFileHandle: any = null;
private reauthWait: null | Promise<void> = null;
private _loadPoll: null | number = null;
firstUpdated() { firstUpdated() {
window.addEventListener("message", (event) => this.onReplayMessage(event)); window.addEventListener("message", (event) => this.onReplayMessage(event));
@@ -81,9 +86,10 @@ class Replay extends LitElement {
} }
doSetIframeUrl() { doSetIframeUrl() {
this.iframeUrl = this.url this.iframeUrl =
? `${this.collInfo.replayPrefix}/${this.ts || ""}mp_/${this.url}` this.url && this.collInfo
: ""; ? `${this.collInfo.replayPrefix}/${this.ts || ""}mp_/${this.url}`
: "";
} }
updated(changedProperties) { updated(changedProperties) {
@@ -199,7 +205,7 @@ class Replay extends LitElement {
// google drive reauth // google drive reauth
const headers = event.detail.headers; const headers = event.detail.headers;
await fetch(`${this.collInfo.apiPrefix}/updateAuth`, { await fetch(`${this.collInfo!.apiPrefix}/updateAuth`, {
method: "POST", method: "POST",
body: JSON.stringify({ headers }), body: JSON.stringify({ headers }),
}); });
@@ -231,7 +237,7 @@ class Replay extends LitElement {
!iframe.contentDocument || !iframe.contentDocument ||
!iframe.contentWindow || !iframe.contentWindow ||
(iframe.contentDocument.readyState === "complete" && (iframe.contentDocument.readyState === "complete" &&
!iframe.contentWindow._WBWombat) !(iframe.contentWindow as Window & { _WBWombat: any })._WBWombat)
) { ) {
this.clearLoading(iframe && iframe.contentWindow); this.clearLoading(iframe && iframe.contentWindow);
} }
@@ -273,7 +279,7 @@ class Replay extends LitElement {
this.doSetIframeUrl(); this.doSetIframeUrl();
if (oldIframeUrl === this.iframeUrl || this.url === this.replayUrl) { if (oldIframeUrl === this.iframeUrl || this.url === this.replayUrl) {
this.waitForLoad(); this.waitForLoad();
iframe.contentWindow.location.reload(); iframe.contentWindow?.location.reload();
} }
} }
@@ -394,7 +400,7 @@ class Replay extends LitElement {
` `
: html` <wr-gdrive : html` <wr-gdrive
.sourceUrl="${this.sourceUrl}" .sourceUrl="${this.sourceUrl}"
.state="trymanual" state="trymanual"
.reauth="${true}" .reauth="${true}"
@load-ready="${this.onReAuthed}" @load-ready="${this.onReAuthed}"
></wr-gdrive>`} ></wr-gdrive>`}
+17
View File
@@ -0,0 +1,17 @@
export interface Coll {
filename: string;
sourceUrl: string;
replayPrefix: string;
apiPrefix: string;
coll?: string;
name?: string;
title?: string;
loadUrl?: string;
desc?: string;
description?: string;
resources?: { path: string; name: string }[];
verify?: any;
onDemand?: any;
pages?: any[];
lists?: any[];
}