IPFS Support + Loading Improvements (Better local file handling) + Streamlined Collection View (#25)

* support ipfs:// urls!
misc: better error message for loading from http://
tabs: don't render pages/story/urls if hidden

* ipfs work: remove ww only use sw

* electron + ipfs: add ipfs support to electron app, using ipfs-core
- route ipfs requests to files proxy with 'ipfs=' query param

* file loading:
- add support for new Chrome native file system api with file handles, which allows persistent access to files vial file handles
- if window.showOpenFilePicker() is available use that, include file handle in load message
- support asking permissions for file handles, using similar workflow as google drive auth
- attempt to ask for file permission directly, show auth button if fails (user gestured required)
- replace auth modal with iframe replacement insert to ask for auth for google drive and local file

* ui overhaul for 1.3.0 / condense collection view:
- don't show navbar in collection view
- add logo to back to home page to location bar
- condense location bar buttons
- convert terms to general about dialog, available from collection context menu
- add info tab to collection view in place of drop-down info
- make <wr-coll-info> component, used in both collection list and info tab with additional details
- update to wabac.js 2.2.0

* update CHANGES.md
This commit is contained in:
Ilya Kreymer
2020-11-16 18:31:15 -08:00
committed by GitHub
parent e839400017
commit 228e9bdff6
15 changed files with 4533 additions and 614 deletions
+82 -80
View File
@@ -1,14 +1,11 @@
import { LitElement, html, css } from 'lit-element';
import { wrapCss, rwpLogo, IS_APP, clickOnSpacebarPress } from './misc';
import { registerSW, initDBWorker } from './pageutils';
import prettyBytes from 'pretty-bytes';
import { registerSW } from './pageutils';
import fasHelp from '@fortawesome/fontawesome-free/svgs/solid/question-circle.svg';
import fasArrowLeft from '@fortawesome/fontawesome-free/svgs/solid/arrow-left.svg';
import fasArrowRight from '@fortawesome/fontawesome-free/svgs/solid/arrow-right.svg';
import fasInfoIcon from '@fortawesome/fontawesome-free/svgs/solid/info-circle.svg';
// ===========================================================================
@@ -19,14 +16,13 @@ class App extends LitElement
super();
this.sourceUrl = null;
this.collInfo = null;
this.showTerms = false;
this.showAbout = false;
this.pageParams = {};
this.inited = false;
this.navMenuShown = false;
registerSW(__SW_NAME__);
initDBWorker();
this.safariKeyframes();
}
@@ -37,7 +33,7 @@ class App extends LitElement
pageParams: { type: Object },
sourceUrl: { type: String },
navMenuShown: { type: Boolean },
showTerms: { type: Boolean },
showAbout: { type: Boolean },
collInfo: { type: Object },
loadInfo: { type: Object },
embed: { type: String },
@@ -112,12 +108,19 @@ class App extends LitElement
`);
}
renderNavBar() {
}
render() {
if (!this.inited) {
return html``;
}
const showNavBar = !this.collInfo && (!this.embed || this.embed === "full");
return html`
${!this.embed || this.embed === "full" ? html`
${showNavBar ? html`
<a href="#skip-main-target" @click=${this.skipMenu} class="skip-link">Skip main navigation</a>
<nav class="navbar has-background-info" aria-label="main">
<div class="navbar-brand">
@@ -158,37 +161,6 @@ class App extends LitElement
<fa-icon .svg="${fasArrowRight}" aria-hidden="true"></fa-icon><span class="menu-only">&nbsp;Go Forward</span>
</a>
` : ``}
${this.sourceUrl && this.collInfo ? html`
<div class="navbar-item has-dropdown is-hoverable">
<span tabindex="0" class="info-menu navbar-link is-arrowless" aria-labelledby="about-this-archive">
<fa-icon size="1.2em" .svg="${fasInfoIcon}" aria-hidden="true"></fa-icon>
<span id="about-this-archive" class="menu-only">About this Archive</span>
</span>
<div class="navbar-dropdown">
<div class="navbar-item wide-only"><i>About this Archive</i></div>
<hr class="navbar-divider">
<div class="navbar-item">
Source:&nbsp;<b>${this.sourceUrl}</b>
</div>
<div class="navbar-item">
Filename:&nbsp;<b>${this.collInfo.filename}</b>
</div>
<div class="navbar-item">
Total Size:&nbsp;<b>${prettyBytes(Number(this.collInfo.size || 0))}</b>
</div>
<div class="navbar-item">
Loading Mode:&nbsp;<b>${this.collInfo.onDemand ? "Download On-Demand" : "Fully Local"}</b>
</div>
<div class="navbar-item">
Collection Id:&nbsp;<b>${this.collInfo.coll}</b>
</div>
<hr class="navbar-divider">
<div class="navbar-item">
ReplayWeb.page Version: ${__VERSION__}
</div>
</div>
</div>
` : ``}
</div>
${!this.embed ? html`
<div class="navbar-end">
@@ -196,10 +168,10 @@ class App extends LitElement
<fa-icon .svg="${fasHelp}" aria-hidden="true"></fa-icon><span>&nbsp;User Docs</span>
</a>
<!--
-- NB: the Terms modal is currently inaccessible to people using keyboards or screen readers.
-- Should all the JS and infrastructure for accessible modals be added, or should Terms be a normal page?
-- NB: the About modal is currently inaccessible to people using keyboards or screen readers.
-- Should all the JS and infrastructure for accessible modals be added, or should About be a normal page?
-->
<a href="?terms" @click="${(e) => { e.preventDefault(); this.showTerms = true} }"class="navbar-item">Terms</a>
<a href="?terms" @click="${(e) => { e.preventDefault(); this.showAbout = true} }"class="navbar-item">About</a>
</div>` : html``}
</div>
</nav><p id="skip-main-target" tabindex="-1" class="is-sr-only">Skipped</p>
@@ -211,7 +183,8 @@ class App extends LitElement
sourceUrl="${this.sourceUrl}"
embed="${this.embed}"
@replay-favicons=${this.onFavIcons}
@coll-loaded=${this.onCollLoaded}></wr-coll>
@coll-loaded=${this.onCollLoaded}
@about-show=${(e) => this.showAbout = true}></wr-coll>
` : html`
<wr-coll-index>
@@ -222,42 +195,8 @@ class App extends LitElement
</wr-coll-index>
`}
${this.showTerms ? html`
<div class="modal is-active">
<div class="modal-background" @click="${(e) => this.showTerms = false}"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">Terms and Privacy</p>
<button class="delete" aria-label="close" @click="${(e) => this.showTerms = false}"></button>
</header>
<section class="modal-card-body">
<div class="container">
<div class="content">
<p>This site is a static browser-based application that loads web archive files provided by the user
and renders them for replay in the browser.</p>
<p>The site is operated by the <a href="https://webrecorder.net/">Webrecorder Project</a></p>
<p>See the <a target="_blank" href="./docs">Docs</a> for more info on how it works.</p>
<h3>Privacy</h3>
<p><b>No data is uploaded anywhere and no information is collected.</b></p>
<p>All content rendered stays directly in your browser. When loading an archive from Google Drive,
the site may ask for account authorization to download the specified file only.</p>
<h4>Disclaimer of Warranties</h4>
<p>The application may not always be available. No guarantees!</p>
<p>Some legalese:</p>
<p style="font-size: 0.8rem">DISCLAIMER OF SOFTWARE WARRANTY. WEBRECORDER SOFTWARE PROVIDES THIS SOFTWARE TO YOU "AS AVAILABLE"
AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
INCLUDING WITHOUT LIMITATION ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
</p>
<a class="button is-warning" href="#" @click="${(e) => this.showTerms = false}">Close</a>
</div>
</div>
</section>
</div>
</div>
</div>
` : ``} `
${this.showAbout ? this.renderAbout() : ``}
`;
}
firstUpdated() {
@@ -344,7 +283,7 @@ class App extends LitElement
this.embed = this.pageParams.get("embed") || "";
if (this.pageParams.has("terms")) {
this.showTerms = true;
this.showAbout = true;
}
if (this.pageParams.get("config")) {
@@ -438,6 +377,69 @@ class App extends LitElement
document.head.appendChild(style);
style.appendChild(document.createTextNode(css));
}
renderAbout() {
return html`
<div class="modal is-active">
<div class="modal-background" @click="${this.onAboutClose}"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">About ReplayWeb.page ${IS_APP ? 'App' : ''}</p>
<button class="delete" aria-label="close" @click="${this.onAboutClose}"></button>
</header>
<section class="modal-card-body">
<div class="container">
<div class="content">
<div style="display: flex">
<div class="has-text-centered" style="width: 220px">
<wr-anim-logo class="logo" size="48px"></wr-anim-logo>
<div style="font-size: smaller; margin-bottom: 1em">${IS_APP ? 'App' : ''} v${__VERSION__}</div>
</div>
${IS_APP ? html`
<p>ReplayWeb.page App is a standalone app for Mac, Windows and Linux that loads web archive files provided by the user
and renders them for replay.</p>
` : html`
<p><a href="https://replayweb.page" target="_blank">ReplayWeb.page</a> is a browser-based viewer that loads web archive files provided by the user
and renders them for replay in the browser.</p>`}
</div>
<p>Full source code is available at:
<a href="https://github.com/webrecorder/replayweb.page" target="_blank">https://github.com/webrecorder/replayweb.page</a>
</p>
<p>See the <a target="_blank" href="./docs">User Docs</a> or the GitHub README for more info on how it works.</p>
<p>ReplayWeb.page is part of the <a href="https://webrecorder.net/" target="_blank">Webrecorder Project</a>.</p>
<h3>Privacy</h3>
<p><b>No data is uploaded anywhere and no information is collected.</b></p>
<p>All content rendered stays directly in your browser.<br/>When loading an archive from Google Drive,
the site may ask for account authorization to download the specified file only.</p>
<h4>Disclaimer of Warranties</h4>
<p>The application is provided "as is" without any guarantees.</p>
<details>
<summary>Legalese:</summary>
<p style="font-size: 0.8rem">DISCLAIMER OF SOFTWARE WARRANTY. WEBRECORDER SOFTWARE PROVIDES THIS SOFTWARE TO YOU "AS AVAILABLE"
AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
INCLUDING WITHOUT LIMITATION ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
</details>
<div class="has-text-centered">
<a class="button is-warning" href="#" @click="${this.onAboutClose}">Close</a>
</div>
</div>
</div>
</section>
</div>
</div>
</div>`;
}
onAboutClose() {
this.showAbout = false;
}
}
customElements.define("replay-app-main", App);