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:
+108
-31
@@ -8,6 +8,7 @@ import fasBook from '@fortawesome/fontawesome-free/svgs/solid/book.svg';
|
||||
import farListAlt from '@fortawesome/fontawesome-free/svgs/regular/list-alt.svg';
|
||||
import farResources from '@fortawesome/fontawesome-free/svgs/solid/puzzle-piece.svg';
|
||||
import farPages from '@fortawesome/fontawesome-free/svgs/regular/file-image.svg';
|
||||
import fasInfoIcon from '@fortawesome/fontawesome-free/svgs/solid/info-circle.svg';
|
||||
|
||||
import fasRefresh from '@fortawesome/fontawesome-free/svgs/solid/redo-alt.svg';
|
||||
import fasFullscreen from '@fortawesome/fontawesome-free/svgs/solid/desktop.svg';
|
||||
@@ -20,6 +21,8 @@ import fasMenuV from '@fortawesome/fontawesome-free/svgs/solid/ellipsis-v.svg';
|
||||
import fasAngleLeft from '@fortawesome/fontawesome-free/svgs/solid/angle-left.svg';
|
||||
import fasAngleRight from '@fortawesome/fontawesome-free/svgs/solid/angle-right.svg';
|
||||
|
||||
import prettyBytes from 'pretty-bytes';
|
||||
|
||||
import Split from 'split.js';
|
||||
|
||||
|
||||
@@ -53,7 +56,7 @@ class Coll extends LitElement
|
||||
this.url = "";
|
||||
this.ts = "";
|
||||
|
||||
this.tabNames = ["pages", "story", "resources"];
|
||||
this.tabNames = ["pages", "story", "resources", "info"];
|
||||
|
||||
this.menuActive = false;
|
||||
|
||||
@@ -97,7 +100,7 @@ class Coll extends LitElement
|
||||
|
||||
isVisible: { type: Boolean },
|
||||
|
||||
favIconUrl: {type: String }
|
||||
favIconUrl: {type: String },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,7 +428,7 @@ class Coll extends LitElement
|
||||
static get replayBarStyles() {
|
||||
return css`
|
||||
.replay-bar {
|
||||
padding: 1em 0em 1em 1em;
|
||||
padding: 0.5em 0em 0.5em 0.5em;
|
||||
max-width: none;
|
||||
border-bottom: solid .1rem #97989A;
|
||||
width: 100%;
|
||||
@@ -439,7 +442,8 @@ class Coll extends LitElement
|
||||
.favicon img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
filter: drop-shadow(1px 1px 2px grey);
|
||||
margin: 8px;
|
||||
/*filter: drop-shadow(1px 1px 2px grey);*/
|
||||
}
|
||||
|
||||
#datetime {
|
||||
@@ -484,12 +488,13 @@ class Coll extends LitElement
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.modal {
|
||||
top: 174px;
|
||||
.narrow {
|
||||
padding: calc(0.5em - 1px) 0.8em;
|
||||
}
|
||||
|
||||
form {
|
||||
width: 100%;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.gutter.gutter-horizontal {
|
||||
@@ -506,6 +511,18 @@ class Coll extends LitElement
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.info-bg {
|
||||
background-color: whitesmoke;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.is-list {
|
||||
margin: 1.0em;
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
#contents.full-pages {
|
||||
width: 100% !important;
|
||||
}
|
||||
@@ -581,7 +598,7 @@ class Coll extends LitElement
|
||||
<div id="contents" class="is-light ${isSidebar ? 'sidebar' : (isReplay ? 'is-hidden' : 'full-pages')}"
|
||||
role="${isSidebar ? "complementary" : ""}" aria-label="${isSidebar ? "Browse Contents" : ""}">
|
||||
${this.renderTabHeader(isSidebar)}
|
||||
${this.renderCollTabs(isSidebar)}
|
||||
${isSidebar || !isReplay ? this.renderCollTabs(isSidebar) : html``}
|
||||
</div>
|
||||
|
||||
${isReplay && this.isVisible ? html`
|
||||
@@ -644,6 +661,13 @@ class Coll extends LitElement
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="${this.tabData.view === 'info' ? 'is-active' : ''}">
|
||||
<a @click="${this.onTabClick}" href="#info" class="is-size-6" aria-label="Archive Info" aria-current="${this.tabData.view === 'info' ? 'location' : ''}">
|
||||
<span class="icon"><fa-icon .svg="${fasInfoIcon}" aria-hidden="true" title="Archive Info"></fa-icon></span>
|
||||
<span class="tab-label ${isSidebar ? 'is-hidden' : ''}" title="Archive Info">Info</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
${isSidebar ? html`
|
||||
<li class="sidebar-nav right">
|
||||
<a role="button" href="#" @click="${this.onFullPageView}" @keyup="${clickOnSpacebarPress}" class="is-marginless is-size-6 is-paddingless">
|
||||
@@ -671,25 +695,34 @@ class Coll extends LitElement
|
||||
<a class="skip-link" href="#skip-replay-target" @click="${this.skipMenu}">Skip replay navigation</a>
|
||||
<nav class="replay-bar" aria-label="replay">
|
||||
<div class="field has-addons">
|
||||
<a href="#" role="button" id="fullscreen" class="button is-borderless is-hidden-touch" @click="${this.onFullscreenToggle}" @keyup="${clickOnSpacebarPress}"
|
||||
|
||||
${!this.embed ? html`
|
||||
<a href="/" role="button" class="button is-borderless is-hidden-mobile" aria-labelledby="home" @keyup="${clickOnSpacebarPress}">
|
||||
<span class="icon is-small">
|
||||
<fa-icon id="wrlogo" size="1.5rem" .svg=${rwpLogo} aria-hidden="true"></fa-icon>
|
||||
<span class="is-sr-only">Home</span>
|
||||
</span>
|
||||
</a>` : ``}
|
||||
|
||||
<a href="#" role="button" class="button narrow is-borderless is-hidden-touch" id="fullscreen" @click="${this.onFullscreenToggle}" @keyup="${clickOnSpacebarPress}"
|
||||
title="${this.isFullscreen ? "Exit Full Screen" : "Full Screen"}" aria-label="${this.isFullscreen ? "Exit Fullscreen" : "Fullscreen"}">
|
||||
<span class="icon is-small">
|
||||
<fa-icon size="1.0em" class="has-text-grey" aria-hidden="true" .svg="${this.isFullscreen ? fasUnfullscreen : fasFullscreen}"></fa-icon>
|
||||
</span>
|
||||
</a>
|
||||
<a href="#" role="button" class="button is-borderless is-hidden-mobile" @click="${this.onGoBack}" @keyup="${clickOnSpacebarPress}"
|
||||
<a href="#" role="button" class="button narrow is-borderless is-hidden-mobile" @click="${this.onGoBack}" @keyup="${clickOnSpacebarPress}"
|
||||
title="Back" aria-label="Back">
|
||||
<span class="icon is-small">
|
||||
<fa-icon size="1.0em" class="has-text-grey" aria-hidden="true" .svg="${fasLeft}"></fa-icon>
|
||||
</span>
|
||||
</a>
|
||||
<a href="#" role="button" class="button is-borderless is-hidden-mobile" @click="${this.onGoForward}" @keyup="${clickOnSpacebarPress}"
|
||||
<a href="#" role="button" class="button narrow is-borderless is-hidden-mobile" @click="${this.onGoForward}" @keyup="${clickOnSpacebarPress}"
|
||||
title="Forward" aria-label="Forward">
|
||||
<span class="icon is-small">
|
||||
<fa-icon size="1.0em" class="has-text-grey" aria-hidden="true" .svg="${fasRight}"></fa-icon>
|
||||
</span>
|
||||
</a>
|
||||
<a href="#" role="button" id="refresh" class="button is-borderless ${this.isLoading ? 'is-loading' : 'is-hidden-mobile'}" @click="${this.onRefresh}" @keyup="${clickOnSpacebarPress}"
|
||||
<a href="#" role="button" class="button narrow is-borderless ${this.isLoading ? 'is-loading' : 'is-hidden-mobile'}" id="refresh" @click="${this.onRefresh}" @keyup="${clickOnSpacebarPress}"
|
||||
title="Reload" aria-label="Reload">
|
||||
<span class="icon is-small">
|
||||
${!this.isLoading ? html`
|
||||
@@ -697,7 +730,7 @@ class Coll extends LitElement
|
||||
` : ``}
|
||||
</span>
|
||||
</a>
|
||||
<a href="#" role="button" class="button is-borderless is-hidden-touch ${!isReplay ? 'grey-disabled' : ''}" @click="${this.onShowPages}" @keyup="${clickOnSpacebarPress}"
|
||||
<a href="#" role="button" class="button narrow is-borderless is-hidden-touch ${!isReplay ? 'grey-disabled' : ''}" @click="${this.onShowPages}" @keyup="${clickOnSpacebarPress}"
|
||||
?disabled="${!isReplay}" title="Browse Contents" aria-label="Browse Contents" aria-controls="contents">
|
||||
<span class="icon is-small">
|
||||
<fa-icon size="1.0em" class="has-text-grey" aria-hidden="true" .svg="${farListAlt}"></fa-icon>
|
||||
@@ -725,6 +758,15 @@ class Coll extends LitElement
|
||||
</div>
|
||||
<div class="dropdown-menu" id="menu-dropdown">
|
||||
<div class="dropdown-content">
|
||||
${!this.embed ? html`
|
||||
<a href="/" role="button" class="dropdown-item is-hidden-tablet" @keyup="${clickOnSpacebarPress}">
|
||||
<span class="icon is-small">
|
||||
<fa-icon size="1.0em" class="has-text-grey" aria-hidden="true" .svg="${rwpLogo}"></fa-icon>
|
||||
</span>
|
||||
<span>Home</span>
|
||||
</a>
|
||||
<hr class="dropdown-divider is-hidden-tablet">
|
||||
` : ``}
|
||||
<a href="#" role="button" class="dropdown-item is-hidden-desktop" @click="${this.onFullscreenToggle}" @keyup="${clickOnSpacebarPress}">
|
||||
<span class="icon is-small">
|
||||
<fa-icon size="1.0em" class="has-text-grey" aria-hidden="true" .svg="${this.isFullscreen ? fasUnfullscreen : fasFullscreen}"></fa-icon>
|
||||
@@ -764,14 +806,11 @@ class Coll extends LitElement
|
||||
<div class="dropdown-item info is-hidden-desktop">
|
||||
<span class="menu-head">Capture Date</span>${dateStr}
|
||||
</div>` : ``}
|
||||
${this.embed ? html`
|
||||
<hr class="dropdown-divider">
|
||||
<a href="https://replayweb.page/" target="_blank" class="dropdown-item info">
|
||||
<fa-icon class="menu-logo" size="1.0rem" aria-hidden="true" .svg=${rwpLogo}></fa-icon>
|
||||
<a href="#" role="button" class="dropdown-item" @click="${this.onAbout}">
|
||||
<span> About ReplayWeb.page</span>
|
||||
<span class="menu-version">(${__VERSION__})</span>
|
||||
</a>
|
||||
` : ``}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -796,12 +835,26 @@ class Coll extends LitElement
|
||||
}
|
||||
|
||||
renderCollTabs(isSidebar) {
|
||||
const isStory = this.tabData.view === 'story';
|
||||
const isStory = this.hasStory && this.tabData.view === 'story';
|
||||
const isPages = this.tabData.view === 'pages';
|
||||
const isResources = this.tabData.view === 'resources';
|
||||
const isInfo = this.tabData.view === 'info';
|
||||
|
||||
return html`
|
||||
${this.hasStory ? html`
|
||||
|
||||
${isInfo ? html`
|
||||
<div class="info-bg">
|
||||
<wr-coll-info
|
||||
class="is-list"
|
||||
.coll="${this.collInfo}"
|
||||
?detailed="${true}"
|
||||
?canDelete="${!this.embed}"
|
||||
@coll-purge="${this.onPurgeCache}"
|
||||
></wr-coll-info>
|
||||
</div>
|
||||
`: html``}
|
||||
|
||||
${isStory ? html`
|
||||
<wr-coll-story .collInfo="${this.collInfo}"
|
||||
.active="${isStory}"
|
||||
currList="${this.tabData.currList || 0}"
|
||||
@@ -812,6 +865,7 @@ class Coll extends LitElement
|
||||
>
|
||||
</wr-coll-story>` : ''}
|
||||
|
||||
${isResources ? html`
|
||||
<wr-coll-resources .collInfo="${this.collInfo}"
|
||||
.active="${isResources}"
|
||||
query="${this.tabData.query || ""}"
|
||||
@@ -822,8 +876,9 @@ class Coll extends LitElement
|
||||
class="is-paddingless ${isResources ? '' : 'is-hidden'} ${isSidebar ? 'sidebar' : ''}"
|
||||
role="${isSidebar ? '' : 'main'}"
|
||||
>
|
||||
</wr-coll-resources>
|
||||
</wr-coll-resources>` : ``}
|
||||
|
||||
${isPages ? html`
|
||||
<wr-page-view
|
||||
.collInfo="${this.collInfo}"
|
||||
.active="${isPages}"
|
||||
@@ -838,7 +893,7 @@ class Coll extends LitElement
|
||||
class="${isPages ? '' : 'is-hidden'} ${isSidebar ? 'sidebar' : ''}"
|
||||
role="${isSidebar ? '' : 'main'}"
|
||||
>
|
||||
</wr-page-view>
|
||||
</wr-page-view>` : ``}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -911,28 +966,46 @@ class Coll extends LitElement
|
||||
this.showSidebar = false;
|
||||
}
|
||||
|
||||
onFavIcons(event) {
|
||||
this.favIconUrl = "";
|
||||
event.detail.icons.map(async (icon) => {
|
||||
const res = await fetch(icon.href);
|
||||
if (res.status === 200) {
|
||||
this.favIconUrl = icon.href;
|
||||
async onFavIcons(event) {
|
||||
for (const icon of event.detail.icons) {
|
||||
const resp = await fetch(icon.href);
|
||||
if (resp.status === 200) {
|
||||
const ct = resp.headers.get("Content-Type");
|
||||
if (ct && !ct.startsWith("text/")) {
|
||||
this.favIconUrl = icon.href;
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// not found
|
||||
this.favIconUrl = "";
|
||||
}
|
||||
|
||||
async onPurgeCache(event) {
|
||||
onPurgeCache(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const reload = event.detail && event.detail.reload !== undefined ? event.detail.reload : true;
|
||||
|
||||
this.deleteFully(reload);
|
||||
}
|
||||
|
||||
async deleteFully(reload = false) {
|
||||
const resp = await fetch(`${this.collInfo.apiPrefix}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
|
||||
if (resp.status === 200 && window.parent) {
|
||||
window.parent.location.reload();
|
||||
} else {
|
||||
if (resp.status !== 200) {
|
||||
console.warn("purge failed: " + resp.status);
|
||||
}
|
||||
|
||||
if (!reload && !this.embed) {
|
||||
window.location.search = "";
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.hash = "";
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
onSubmit(event) {
|
||||
@@ -1019,6 +1092,10 @@ class Coll extends LitElement
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
onAbout() {
|
||||
this.dispatchEvent(new CustomEvent("about-show"));
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("wr-coll", Coll);
|
||||
|
||||
Reference in New Issue
Block a user