diff --git a/frontend/src/components/index.ts b/frontend/src/components/index.ts index 39270c58..4bd36c70 100644 --- a/frontend/src/components/index.ts +++ b/frontend/src/components/index.ts @@ -24,6 +24,9 @@ import("./sign-up-form").then(({ SignUpForm }) => { import("./not-found").then(({ NotFound }) => { customElements.define("btrix-not-found", NotFound); }); +import("./screencast").then(({ Screencast: Screencast }) => { + customElements.define("btrix-screencast", Screencast); +}); customElements.define("btrix-alert", Alert); customElements.define("btrix-input", Input); diff --git a/frontend/src/components/screencast.ts b/frontend/src/components/screencast.ts new file mode 100644 index 00000000..6d7a29b3 --- /dev/null +++ b/frontend/src/components/screencast.ts @@ -0,0 +1,316 @@ +import { LitElement, html, css } from "lit"; +import { property, state } from "lit/decorators.js"; + +type Message = { + id: string; // page ID +}; + +type InitMessage = Message & { + msg: "init"; + browsers: number; + width: number; + height: number; +}; + +type ScreencastMessage = Message & { + msg: "screencast"; + url: string; // page URL + data: string; // base64 PNG data +}; + +type CloseMessage = Message & { + msg: "close"; +}; + +/** + * Watch page crawl + * + * Usage example: + * ```ts + * + * ``` + */ +export class Screencast extends LitElement { + static styles = css` + .wrapper { + position: relative; + } + + .spinner { + text-align: center; + font-size: 2rem; + } + + .container { + display: grid; + gap: 0.5rem; + } + + .screen { + border: 1px solid var(--sl-color-neutral-100); + border-radius: var(--sl-border-radius-medium); + cursor: pointer; + transition: opacity 0.1s border-color 0.1s; + overflow: hidden; + } + + .screen:hover { + opacity: 0.8; + border-color: var(--sl-color-neutral-300); + } + + figure { + margin: 0; + } + + figcaption { + flex: 1; + border-bottom-width: 1px; + border-bottom-color: var(--sl-panel-border-color); + color: var(--sl-color-neutral-600); + font-size: var(--sl-font-size-small); + padding: var(--sl-spacing-x-small); + } + + figcaption, + .dialog-label { + display: block; + font-size: var(--sl-font-size-small); + line-height: 1; + /* Truncate: */ + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .dialog-label { + max-width: 40em; + } + + img { + display: block; + width: 100%; + height: auto; + box-shadow: 0; + outline: 0; + border: 0; + } + `; + + @property({ type: String }) + authToken?: string; + + @property({ type: String }) + archiveId?: string; + + @property({ type: String }) + crawlId?: string; + + @property({ type: Array }) + watchIPs: string[] = []; + + @state() + private dataList: Array = []; + + @state() + private isConnecting: boolean = false; + + @state() + private focusedScreenData?: ScreencastMessage; + + // Websocket connections + private wsMap: Map = new Map(); + + // Page image data + private imageDataMap: Map = new Map(); + + private screenCount = 1; + private screenWidth = 640; + + shouldUpdate(changedProperties: Map) { + if (changedProperties.size === 1 && changedProperties.has("watchIPs")) { + // Check stringified value of IP list + return ( + this.watchIPs.toString() !== + changedProperties.get("watchIPs").toString() + ); + } + + return true; + } + + protected firstUpdated() { + this.isConnecting = true; + + // Connect to websocket server + this.connectWs(); + } + + async updated(changedProperties: Map) { + if ( + changedProperties.get("archiveId") || + changedProperties.get("crawlId") || + changedProperties.get("watchIPs") || + changedProperties.get("authToken") + ) { + // Reconnect + this.disconnectWs(); + this.connectWs(); + } + } + + disconnectedCallback() { + this.disconnectWs(); + super.disconnectedCallback(); + } + + render() { + return html` +
+ ${this.isConnecting + ? html`
+ +
` + : ""} +
+ ${this.dataList.map( + (pageData) => html`
(this.focusedScreenData = pageData)} + > +
${pageData.url}
+ +
` + )} +
+
+ + + + ${this.focusedScreenData?.url} + + + ${this.focusedScreenData + ? html` + + ` + : ""} + + `; + } + + private connectWs() { + if (!this.archiveId || !this.crawlId) { + return; + } + + if (!this.watchIPs?.length) { + console.warn("No watch IPs to connect to"); + return; + } + + const baseURL = `${window.location.protocol === "https:" ? "wss" : "ws"}:${ + process.env.API_HOST + }/watch/${this.archiveId}/${this.crawlId}`; + + this.watchIPs.forEach((ip: string) => { + const ws = new WebSocket( + `${baseURL}/${ip}/ws?auth_bearer=${this.authToken || ""}` + ); + + ws.addEventListener("open", () => { + if (this.wsMap.size === this.watchIPs.length) { + this.isConnecting = false; + } + }); + ws.addEventListener("close", () => { + this.wsMap.delete(ip); + }); + ws.addEventListener("error", () => { + this.isConnecting = false; + }); + ws.addEventListener("message", ({ data }) => { + this.handleMessage(JSON.parse(data)); + }); + + this.wsMap.set(ip, ws); + }); + } + + private disconnectWs() { + this.isConnecting = false; + + this.wsMap.forEach((ws) => { + ws.close(); + }); + } + + private handleMessage( + message: InitMessage | ScreencastMessage | CloseMessage + ) { + if (message.msg === "init") { + this.screenCount = message.browsers; + this.screenWidth = message.width; + } else { + const { id } = message; + + if (message.msg === "screencast") { + if (message.url === "about:blank") { + // Skip blank pages + return; + } + + if (this.isConnecting) { + this.isConnecting = false; + } + + this.imageDataMap.set(id, message); + + if (this.focusedScreenData?.id === id) { + this.focusedScreenData = message; + } + + this.updateDataList(); + } else if (message.msg === "close") { + this.imageDataMap.delete(id); + this.updateDataList(); + } + } + } + + updateDataList() { + // keep same number of data entries (probably should only decrease if scale is reduced) + this.dataList = [ + ...this.imageDataMap.values(), + ...this.dataList.slice(this.imageDataMap.size), + ]; + } + + unfocusScreen() { + this.updateDataList(); + this.focusedScreenData = undefined; + } +} diff --git a/frontend/src/components/temp.ts b/frontend/src/components/temp.ts new file mode 100644 index 00000000..10ef869c --- /dev/null +++ b/frontend/src/components/temp.ts @@ -0,0 +1 @@ +export default "iVBORw0KGgoAAAANSUhEUgAAA5QAAAMACAYAAABbwSgdAAARBUlEQVR4nO3XMQ2AMAAAwVJTHTCEfwNUQ38hJHcKfv3rftY7AAAA4ND8OgAAAIB/MpQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASAwlAAAAiaEEAAAgMZQAAAAkhhIAAIDEUAIAAJAYSgAAABJDCQAAQGIoAQAASDYemQfZxiZcRgAAAABJRU5ErkJggg=="; diff --git a/frontend/src/index.ts b/frontend/src/index.ts index 6988dee7..cc7f4176 100644 --- a/frontend/src/index.ts +++ b/frontend/src/index.ts @@ -147,11 +147,19 @@ export class App extends LiteElement { } navigate(newViewPath: string, state?: object) { + let url; + if (newViewPath.startsWith("http")) { - const url = new URL(newViewPath); - newViewPath = `${url.pathname}${url.search}`; + url = new URL(newViewPath); + } else { + url = new URL( + `${window.location.origin}/${newViewPath.replace(/^\//, "")}` + ); } + // Remove hash from path for matching + newViewPath = `${url.pathname}${url.search}`; + if (newViewPath === "/log-in" && this.authService.authState) { // Redirect to logged in home page this.viewState = this.router.match(DASHBOARD_ROUTE); @@ -161,7 +169,11 @@ export class App extends LiteElement { this.viewState.data = state; - window.history.pushState(this.viewState, "", this.viewState.pathname); + window.history.pushState( + this.viewState, + "", + `${this.viewState.pathname}${url.hash}${url.search}` + ); } navLink(event: Event) { diff --git a/frontend/src/pages/archive/crawl-detail.ts b/frontend/src/pages/archive/crawl-detail.ts index 81c2552d..76f2bb48 100644 --- a/frontend/src/pages/archive/crawl-detail.ts +++ b/frontend/src/pages/archive/crawl-detail.ts @@ -65,13 +65,6 @@ export class CrawlDetail extends LiteElement { async firstUpdated() { this.fetchCrawl(); - - // try { - // this.watchUrl = await this.watchCrawl(); - // console.log(this.watchUrl); - // } catch (e) { - // console.error(e); - // } } connectedCallback(): void { @@ -92,9 +85,21 @@ export class CrawlDetail extends LiteElement { let sectionContent: string | TemplateResult = ""; switch (this.sectionName) { - case "watch": - sectionContent = this.renderWatch(); + case "watch": { + if (this.crawl) { + if (this.crawl.state === "running") { + sectionContent = this.renderWatch(); + } else { + sectionContent = this.renderReplay(); + } + } else { + // TODO loading indicator? + return ""; + } + break; + } + case "download": sectionContent = this.renderFiles(); break; @@ -384,6 +389,41 @@ export class CrawlDetail extends LiteElement { } private renderWatch() { + if (!this.authState) return ""; + + const authToken = this.authState.headers.Authorization.split(" ")[1]; + + return html` +
+

${msg("Watch Crawl")}

+ ${document.fullscreenEnabled + ? html` + this.enterFullscreen("screencast-crawl")} + > + ` + : ""} +
+ + ${this.crawl + ? html` +
+ +
` + : ""} + `; + } + + private renderReplay() { + const isRunning = this.crawl?.state === "running"; + const bearer = this.authState?.headers?.Authorization?.split(" ", 2)[1]; // for now, just use the first file until multi-wacz support is fully implemented @@ -391,10 +431,22 @@ export class CrawlDetail extends LiteElement { const replaySource = this.crawl?.resources?.[0]?.path; return html` -

${msg("Watch or Replay Crawl")}

+
+

${msg("Replay Crawl")}

+ ${document.fullscreenEnabled + ? html` + this.enterFullscreen("replay-crawl")} + > + ` + : ""} +
@@ -408,38 +460,6 @@ export class CrawlDetail extends LiteElement { >` : ``}
-
- ${this.isWatchExpanded - ? html` - (this.isWatchExpanded = false)} - > - ` - : html` - (this.isWatchExpanded = true)} - > - `} - ${this.watchUrl - ? html` - - ` - : ""} -
`; } @@ -664,18 +684,6 @@ export class CrawlDetail extends LiteElement { return data; } - private async watchCrawl(): Promise { - const data = await this.apiFetch( - `/archives/${this.archiveId}/crawls/${this.crawlId}/watch`, - this.authState!, - { - method: "POST", - } - ); - - return data.watch_url; - } - private async cancel() { if (window.confirm(msg("Are you sure you want to cancel the crawl?"))) { const data = await this.apiFetch( @@ -761,6 +769,21 @@ export class CrawlDetail extends LiteElement { private stopPollTimer() { window.clearTimeout(this.timerId); } + + /** + * Enter fullscreen mode + * @param id ID of element to fullscreen + */ + private async enterFullscreen(id: string) { + try { + document.getElementById(id)!.requestFullscreen({ + // Show browser navigation controls + navigationUI: "show", + }); + } catch (err) { + console.error(err); + } + } } customElements.define("btrix-crawl-detail", CrawlDetail); diff --git a/frontend/src/pages/archive/crawl-templates-detail.ts b/frontend/src/pages/archive/crawl-templates-detail.ts index decaab7d..48bc98e8 100644 --- a/frontend/src/pages/archive/crawl-templates-detail.ts +++ b/frontend/src/pages/archive/crawl-templates-detail.ts @@ -759,7 +759,7 @@ export class CrawlTemplatesDetail extends LiteElement { ${this.crawlTemplate.currCrawlId ? html` ${msg("View crawl")}` @@ -787,7 +787,7 @@ export class CrawlTemplatesDetail extends LiteElement { ${this.crawlTemplate?.lastCrawlId ? html`${msg("View crawl")} @@ -1240,7 +1240,8 @@ export class CrawlTemplatesDetail extends LiteElement {
View crawl` diff --git a/frontend/src/pages/archive/crawl-templates-list.ts b/frontend/src/pages/archive/crawl-templates-list.ts index 3daf4872..2f108565 100644 --- a/frontend/src/pages/archive/crawl-templates-list.ts +++ b/frontend/src/pages/archive/crawl-templates-list.ts @@ -364,7 +364,7 @@ export class CrawlTemplatesList extends LiteElement { ? this.navTo( `/archives/${this.archiveId}/crawls/crawl/${ this.runningCrawlsMap[t.id] - }` + }#watch` ) : this.runNow(t); }} @@ -502,7 +502,8 @@ export class CrawlTemplatesList extends LiteElement { html`Started crawl from ${template.name}.
View crawl` diff --git a/frontend/src/pages/archive/crawl-templates.ts b/frontend/src/pages/archive/crawl-templates.ts index 5e6192a5..41e726a5 100644 --- a/frontend/src/pages/archive/crawl-templates.ts +++ b/frontend/src/pages/archive/crawl-templates.ts @@ -157,7 +157,8 @@ export class CrawlTemplatesList extends LiteElement { html`Started crawl from ${template.name}.
View crawl` diff --git a/frontend/src/pages/archive/index.ts b/frontend/src/pages/archive/index.ts index a238448a..dda7cc67 100644 --- a/frontend/src/pages/archive/index.ts +++ b/frontend/src/pages/archive/index.ts @@ -53,7 +53,7 @@ export class Archive extends LiteElement { isNewResourceTab: boolean = false; @state() - private archive?: ArchiveData; + private archive?: ArchiveData | null; @state() private successfullyInvitedEmail?: string; @@ -61,14 +61,22 @@ export class Archive extends LiteElement { async firstUpdated() { if (!this.archiveId) return; - const archive = await this.getArchive(this.archiveId); + try { + const archive = await this.getArchive(this.archiveId); - if (!archive) { - this.navTo("/archives"); - } else { - this.archive = archive; + if (!archive) { + this.navTo("/archives"); + } else { + this.archive = archive; + } + } catch { + this.archive = null; - // TODO get archive members + this.notify({ + message: msg("Sorry, couldn't retrieve archive at this time."), + type: "danger", + icon: "exclamation-octagon", + }); } } @@ -79,6 +87,11 @@ export class Archive extends LiteElement { } render() { + if (this.archive === null) { + // TODO handle 404 and 500s + return ""; + } + if (!this.archive) { return html`
; }; type SeedConfig = { diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 99472002..f1915f3d 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -47,6 +47,9 @@ function makeTheme() { lg: `var(--sl-shadow-large)`, xl: `var(--sl-shadow-x-large)`, }, + aspectRatio: { + "4/3": "4 / 3", // For Browsertrix watch/replay + }, }; } diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js index 66e54d64..be6a91ea 100644 --- a/frontend/webpack.config.js +++ b/frontend/webpack.config.js @@ -1,9 +1,9 @@ // webpack.config.js const path = require("path"); +const webpack = require("webpack"); const ESLintPlugin = require("eslint-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const CopyPlugin = require("copy-webpack-plugin"); -const Dotenv = require("dotenv-webpack"); const childProcess = require("child_process"); const isDevServer = process.env.WEBPACK_SERVE; @@ -23,14 +23,16 @@ const execCommand = (cmd, defValue) => { } catch (e) { return defValue; } -} +}; // Local dev only // Git branch and commit hash is used to add build info to error reporter when running locally -const gitBranch = process.env.GIT_BRANCH_NAME || +const gitBranch = + process.env.GIT_BRANCH_NAME || execCommand("git rev-parse --abbrev-ref HEAD", "unknown"); -const commitHash = process.env.GIT_COMMIT_HASH || +const commitHash = + process.env.GIT_COMMIT_HASH || execCommand("git rev-parse --short HEAD", "unknown"); require("dotenv").config({ @@ -106,6 +108,7 @@ module.exports = { Host: backendUrl.host, }, pathRewrite: { "^/api": "" }, + ws: true, }, }, // Serve replay service worker file @@ -119,7 +122,9 @@ module.exports = { }, plugins: [ - new Dotenv({ path: dotEnvPath }), + new webpack.DefinePlugin({ + "process.env.API_HOST": JSON.stringify(backendUrl.host), + }), new HtmlWebpackPlugin({ template: "src/index.ejs",