load errors: detect non-200/206 response and return an error
local file loading support: fix blob reader, add UI for file chooser and url entry ui: choose default tab after loading completed remove some logs
This commit is contained in:
+165
-13
@@ -17,6 +17,7 @@ import farListAlt from '@fortawesome/fontawesome-free/svgs/solid/list-alt.svg';
|
|||||||
import fasSearch from '@fortawesome/fontawesome-free/svgs/solid/search.svg';
|
import fasSearch from '@fortawesome/fontawesome-free/svgs/solid/search.svg';
|
||||||
import farPlayCircle from '@fortawesome/fontawesome-free/svgs/regular/play-circle.svg';
|
import farPlayCircle from '@fortawesome/fontawesome-free/svgs/regular/play-circle.svg';
|
||||||
import fabGoogleDrive from '@fortawesome/fontawesome-free/svgs/brands/google-drive.svg';
|
import fabGoogleDrive from '@fortawesome/fontawesome-free/svgs/brands/google-drive.svg';
|
||||||
|
import fasUpload from '@fortawesome/fontawesome-free/svgs/solid/upload.svg'
|
||||||
|
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
@@ -66,8 +67,13 @@ class AppMain extends LitElement
|
|||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
${this.sourceUrl ? html`
|
${this.sourceUrl ? html`
|
||||||
<wr-coll sourceUrl="${this.sourceUrl}"></wr-coll>
|
<wr-coll .loadInfo="${this.loadInfo}"
|
||||||
` : this.sourceUrl === "" ? html`<wr-index></wr-index>` : html``}
|
sourceUrl="${this.sourceUrl}"
|
||||||
|
@coll-loaded=${this.onCollLoaded}></wr-coll>
|
||||||
|
` : this.sourceUrl === "" ? html`
|
||||||
|
<wr-index @load-start=${this.onStartLoad}></wr-index>` :
|
||||||
|
|
||||||
|
html``}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +102,28 @@ class AppMain extends LitElement
|
|||||||
|
|
||||||
this.sourceUrl = this.pageParams.get("source") || "";
|
this.sourceUrl = this.pageParams.get("source") || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onStartLoad(event) {
|
||||||
|
// just redirect right away?
|
||||||
|
if (!event.detail.blobUrl) {
|
||||||
|
this.pageParams.set("source", event.detail.sourceUrl);
|
||||||
|
window.location.search = this.pageParams.toString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sourceUrl = event.detail.sourceUrl;
|
||||||
|
this.loadInfo = event.detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
onCollLoaded(event) {
|
||||||
|
this.loadInfo = null;
|
||||||
|
this.initRoute();
|
||||||
|
|
||||||
|
if (event.detail.sourceUrl !== this.sourceUrl) {
|
||||||
|
this.pageParams.set("source", event.detail.sourceUrl);
|
||||||
|
window.location.search = this.pageParams.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
@@ -108,6 +136,7 @@ class WrLoader extends LitElement
|
|||||||
this.percent = 0;
|
this.percent = 0;
|
||||||
this.coll = "";
|
this.coll = "";
|
||||||
this.state = "waiting";
|
this.state = "waiting";
|
||||||
|
this.loadInfo = null;
|
||||||
|
|
||||||
this.worker = new Worker("./dist/dbworker.js");
|
this.worker = new Worker("./dist/dbworker.js");
|
||||||
}
|
}
|
||||||
@@ -115,9 +144,11 @@ class WrLoader extends LitElement
|
|||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
sourceUrl: { type: String },
|
sourceUrl: { type: String },
|
||||||
|
loadInfo: { type: Object },
|
||||||
state: { type: String },
|
state: { type: String },
|
||||||
progress: { type: Number },
|
progress: { type: Number },
|
||||||
percent: { type: Number },
|
percent: { type: Number },
|
||||||
|
error: { type: String},
|
||||||
total: { type: Number },
|
total: { type: Number },
|
||||||
status: { type: String },
|
status: { type: String },
|
||||||
coll: { type: String },
|
coll: { type: String },
|
||||||
@@ -135,6 +166,10 @@ class WrLoader extends LitElement
|
|||||||
case "collProgress":
|
case "collProgress":
|
||||||
if (event.data.name === this.coll) {
|
if (event.data.name === this.coll) {
|
||||||
this.percent = event.data.percent;
|
this.percent = event.data.percent;
|
||||||
|
if (event.data.error) {
|
||||||
|
this.error = event.data.error;
|
||||||
|
this.state = "errored";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -182,6 +217,20 @@ class WrLoader extends LitElement
|
|||||||
name: this.sourceUrl};
|
name: this.sourceUrl};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "file:":
|
||||||
|
if (!this.loadInfo) {
|
||||||
|
this.state = "errored";
|
||||||
|
this.error = "File URLs are local and can not be shared. You can choose a file to upload from the main page.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
source = {
|
||||||
|
sourceUrl: this.loadInfo.blobUrl,
|
||||||
|
sourceId: this.loadInfo.name,
|
||||||
|
name: this.loadInfo.name
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
@@ -202,7 +251,6 @@ class WrLoader extends LitElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
async onLoadReady(event) {
|
async onLoadReady(event) {
|
||||||
console.log(event);
|
|
||||||
if (this._gdResolve) {
|
if (this._gdResolve) {
|
||||||
//const digest = await digestMessage(url, 'SHA-256');
|
//const digest = await digestMessage(url, 'SHA-256');
|
||||||
//this.coll = "id-" + digest.slice(0, 12);
|
//this.coll = "id-" + digest.slice(0, 12);
|
||||||
@@ -242,9 +290,13 @@ class WrLoader extends LitElement
|
|||||||
return html`
|
return html`
|
||||||
<progress class="progress is-primary is-large" data-percent="${this.percent}" value="${this.percent}" max="100" style="max-width: 400px"/>`;
|
<progress class="progress is-primary is-large" data-percent="${this.percent}" value="${this.percent}" max="100" style="max-width: 400px"/>`;
|
||||||
|
|
||||||
|
case "errored":
|
||||||
|
return html`<div class="has-text-danger">${this.error}</div>`;
|
||||||
|
|
||||||
case "waiting":
|
case "waiting":
|
||||||
default:
|
default:
|
||||||
return html`<progress class="progress is-primary is-large" style="max-width: 400px"/>`;
|
return html`<progress class="progress is-primary is-large" style="max-width: 400px"/>`;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -255,11 +307,15 @@ class WrIndex extends LitElement
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.colls = [];
|
this.colls = [];
|
||||||
|
|
||||||
|
this.fileDisplayName = "";
|
||||||
|
this.file = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
colls: { type: Array },
|
colls: { type: Array },
|
||||||
|
fileDisplayName: { type: String }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,20 +347,100 @@ class WrIndex extends LitElement
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onChooseFile(event) {
|
||||||
|
if (event.currentTarget.files.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.file = event.currentTarget.files[0];
|
||||||
|
this.fileDisplayName = "file://" + this.file.name;
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
onStartLoad(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const detail = {sourceUrl: this.fileDisplayName};
|
||||||
|
|
||||||
|
if (this.file) {
|
||||||
|
detail.blobUrl = URL.createObjectURL(this.file);
|
||||||
|
detail.name = this.fileDisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dispatchEvent(new CustomEvent("load-start", {detail}));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onInput(event) {
|
||||||
|
this.fileDisplayName = event.currentTarget.value;
|
||||||
|
|
||||||
|
if (this.file && this.fileDisplayName && this.fileDisplayName.startsWith("file://")) {
|
||||||
|
this.file = null;
|
||||||
|
this.fileDisplayName = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return css`
|
||||||
.size {
|
.size {
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
}
|
}
|
||||||
|
.extra-padding {
|
||||||
|
padding: 2em;
|
||||||
|
}
|
||||||
|
.no-top-padding {
|
||||||
|
padding-top: 1.0em;
|
||||||
|
}
|
||||||
|
div.field.has-addons {
|
||||||
|
flex: auto;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<link href="./dist/frontend.css" rel="stylesheet"/>
|
<link href="./dist/frontend.css" rel="stylesheet"/>
|
||||||
|
<section class="section no-top-padding">
|
||||||
|
<div class="container">
|
||||||
|
<nav class="panel is-info">
|
||||||
|
<p class="panel-heading">Add Web Archive</p>
|
||||||
|
<div class="extra-padding panel-block file has-name">
|
||||||
|
<form class="container" @submit="${this.onStartLoad}">
|
||||||
|
<label class="file-label">
|
||||||
|
<input class="file-input"
|
||||||
|
@click="${(e) => e.currentTarget.value = null}"
|
||||||
|
@change=${this.onChooseFile} type="file" id="fileupload" name="fileupload">
|
||||||
|
<span class="file-cta">
|
||||||
|
<span class="file-icon">
|
||||||
|
<fa-icon size="0.9em" .svg=${fasUpload}></fa-icon>
|
||||||
|
</span>
|
||||||
|
<span class="file-label">
|
||||||
|
Choose File...
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<div class="field has-addons">
|
||||||
|
<p class="control is-expanded">
|
||||||
|
<input style="max-width: 100%" class="file-name input" type="text"
|
||||||
|
name="filename"
|
||||||
|
.value="${this.fileDisplayName}"
|
||||||
|
@input="${this.onInput}"
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="Choose a local file or enter a URL for a (WARC, HAR, WBN, or WAZ) archive">
|
||||||
|
</p>
|
||||||
|
<div class="control">
|
||||||
|
<button type="submit" class="button is-primary">Replay!</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
<section class="container">
|
<section class="container">
|
||||||
<nav class="panel">
|
<nav class="panel">
|
||||||
<p class="panel-heading">Local Archive Collections</p>
|
<p class="panel-heading">Replayable Archive Collections</p>
|
||||||
${this.colls.map((coll, i) => html`
|
${this.colls.map((coll, i) => html`
|
||||||
<div class="panel-block">
|
<div class="panel-block">
|
||||||
<div class="level" style="width: 100%">
|
<div class="level" style="width: 100%">
|
||||||
@@ -350,6 +486,7 @@ class WrColl extends LitElement
|
|||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
sourceUrl: { type: String },
|
sourceUrl: { type: String },
|
||||||
|
loadInfo: { type: Object },
|
||||||
|
|
||||||
collInfo: { type: Object },
|
collInfo: { type: Object },
|
||||||
coll: { type: String },
|
coll: { type: String },
|
||||||
@@ -361,9 +498,8 @@ class WrColl extends LitElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated() {
|
firstUpdated() {
|
||||||
|
this.doUpdateInfo();
|
||||||
window.addEventListener("hashchange", (event) => this.onHashChange(event));
|
window.addEventListener("hashchange", (event) => this.onHashChange(event));
|
||||||
|
|
||||||
this.doUpdateInfo().then(() => this.onHashChange());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async sourceToId(url) {
|
async sourceToId(url) {
|
||||||
@@ -382,6 +518,11 @@ class WrColl extends LitElement
|
|||||||
}
|
}
|
||||||
if (changedProperties.get("tabData")) {
|
if (changedProperties.get("tabData")) {
|
||||||
this._manualHash = true;
|
this._manualHash = true;
|
||||||
|
|
||||||
|
if (this.collInfo && !this.collInfo.coll) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// don't add empty params to shorten query
|
// don't add empty params to shorten query
|
||||||
Object.keys(this.tabData).
|
Object.keys(this.tabData).
|
||||||
forEach(key => !this.tabData[key] && delete this.tabData[key]);
|
forEach(key => !this.tabData[key] && delete this.tabData[key]);
|
||||||
@@ -414,10 +555,21 @@ class WrColl extends LitElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.hasCurated = (this.collInfo.lists && this.collInfo.lists.length);
|
this.hasCurated = (this.collInfo.lists && this.collInfo.lists.length);
|
||||||
|
|
||||||
|
const hash = window.location.hash;
|
||||||
|
if (hash) {
|
||||||
|
this.tabData = Object.fromEntries(new URLSearchParams(hash.slice(1)).entries());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.collInfo.coll && !this.tabData.currTab) {
|
||||||
|
this.tabData = {...this.tabData, currTab: this.hasCurated ? "curated" : "resources"};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onCollLoaded(event) {
|
onCollLoaded(event) {
|
||||||
this.doUpdateInfo();
|
this.doUpdateInfo();
|
||||||
|
this.loadInfo = null;
|
||||||
|
this.dispatchEvent(new CustomEvent("coll-loaded", {detail: {sourceUrl: this.sourceUrl}}));
|
||||||
}
|
}
|
||||||
|
|
||||||
onHashChange(event) {
|
onHashChange(event) {
|
||||||
@@ -429,9 +581,6 @@ class WrColl extends LitElement
|
|||||||
if (hash) {
|
if (hash) {
|
||||||
this.tabData = Object.fromEntries(new URLSearchParams(hash.slice(1)).entries());
|
this.tabData = Object.fromEntries(new URLSearchParams(hash.slice(1)).entries());
|
||||||
}
|
}
|
||||||
if (!this.tabData.currTab) {
|
|
||||||
this.tabData = {...this.tabData, currTab: this.hasCurated ? "curated" : "resources"};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onTabClick(event) {
|
onTabClick(event) {
|
||||||
@@ -442,7 +591,6 @@ class WrColl extends LitElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
onCollTabNav(event) {
|
onCollTabNav(event) {
|
||||||
console.log(event.currentTarget);
|
|
||||||
this.tabData = {...this.tabData, ...event.detail};
|
this.tabData = {...this.tabData, ...event.detail};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -476,7 +624,7 @@ class WrColl extends LitElement
|
|||||||
|
|
||||||
renderColl() {
|
renderColl() {
|
||||||
if (this.collInfo && !this.collInfo.coll) {
|
if (this.collInfo && !this.collInfo.coll) {
|
||||||
return html`<wr-loader .coll="${this.coll}" .sourceUrl="${this.sourceUrl}" @coll-loaded=${this.onCollLoaded}></wr-loader>`;
|
return html`<wr-loader .loadInfo="${this.loadInfo}" .coll="${this.coll}" .sourceUrl="${this.sourceUrl}" @coll-loaded=${this.onCollLoaded}></wr-loader>`;
|
||||||
} else if (this.collInfo) {
|
} else if (this.collInfo) {
|
||||||
return html`
|
return html`
|
||||||
<nav class="panel is-light">
|
<nav class="panel is-light">
|
||||||
@@ -774,8 +922,13 @@ class WrResources extends LitElement
|
|||||||
|
|
||||||
async doLoadResources() {
|
async doLoadResources() {
|
||||||
const count = 100;
|
const count = 100;
|
||||||
const url = (this.urlSearchType !== "contains" ? this.urlSearch : "");
|
let url = (this.urlSearchType !== "contains" ? this.urlSearch : "");
|
||||||
const prefix = url && this.urlSearchType === "prefix" ? 1 : 0;
|
const prefix = url && this.urlSearchType === "prefix" ? 1 : 0;
|
||||||
|
// optimization: if not starting with http, likely won't have a match here, so just add https://
|
||||||
|
|
||||||
|
if (url && !url.startsWith("http")) {
|
||||||
|
url = "https://" + url;
|
||||||
|
}
|
||||||
|
|
||||||
const detail = {urlSearch: this.urlSearch,
|
const detail = {urlSearch: this.urlSearch,
|
||||||
currMime: this.currMime,
|
currMime: this.currMime,
|
||||||
@@ -825,7 +978,6 @@ class WrResources extends LitElement
|
|||||||
let resp = await fetch(`${this.collInfo.apiPrefix}/urls?${params}`);
|
let resp = await fetch(`${this.collInfo.apiPrefix}/urls?${params}`);
|
||||||
resp = await resp.json();
|
resp = await resp.json();
|
||||||
this.results = this.results.concat(resp.urls);
|
this.results = this.results.concat(resp.urls);
|
||||||
console.log(resp.urls.length);
|
|
||||||
this.tryMore = (resp.urls.length === count);
|
this.tryMore = (resp.urls.length === count);
|
||||||
this.filter();
|
this.filter();
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user