misc ui tweaks, mostly pages:

- add download menu
- add selection hilite
- tweak colors on chooser
disable notarize for now
This commit is contained in:
Ilya Kreymer
2020-06-08 21:53:04 -07:00
parent 149558f647
commit 101c4b3319
7 changed files with 355 additions and 95 deletions
-1
View File
@@ -48,7 +48,6 @@
"all": "yarn run build && yarn run docs && yarn run pack" "all": "yarn run build && yarn run docs && yarn run pack"
}, },
"build": { "build": {
"afterSign": "build/notarize.js",
"productName": "ReplayWeb.page", "productName": "ReplayWeb.page",
"asar": true, "asar": true,
"appId": "net.webrecorder.replaywebpage", "appId": "net.webrecorder.replaywebpage",
+4 -1
View File
@@ -64,6 +64,9 @@ class Chooser extends LitElement
div.field.has-addons { div.field.has-addons {
flex: auto; flex: auto;
} }
.panel-heading {
background-color: #cff3ff;
}
form { form {
flex-grow: 1; flex-grow: 1;
flex-shrink: 0; flex-shrink: 0;
@@ -94,7 +97,7 @@ class Chooser extends LitElement
render() { render() {
return html` return html`
<section class="section less-padding"> <section class="section less-padding">
<nav class="panel is-warning"> <nav class="panel">
<p class="panel-heading">Load Web Archive</p> <p class="panel-heading">Load Web Archive</p>
<div class="extra-padding panel-block file has-name"> <div class="extra-padding panel-block file has-name">
<form class="content is-flex" @submit="${this.onStartLoad}"> <form class="content is-flex" @submit="${this.onStartLoad}">
+2 -1
View File
@@ -56,6 +56,7 @@ class Coll extends LitElement
replayTS: { type: String }, replayTS: { type: String },
embed: { type: String }, embed: { type: String },
editable: { type: Boolean }
} }
} }
@@ -173,7 +174,6 @@ class Coll extends LitElement
this.tabData = {view: this.tabData.view, ...event.detail.data}; this.tabData = {view: this.tabData.view, ...event.detail.data};
this._replaceLoc = !this._locUpdateNeeded && event.detail.replaceLoc; this._replaceLoc = !this._locUpdateNeeded && event.detail.replaceLoc;
this._locUpdateNeeded = true; this._locUpdateNeeded = true;
console.log(this.tabData);
} }
} }
@@ -307,6 +307,7 @@ class Coll extends LitElement
<wr-page-view .collInfo="${this.collInfo}" <wr-page-view .collInfo="${this.collInfo}"
.active="${isPages}" .active="${isPages}"
.editable="${this.editable}"
currList="${this.tabData.currList || 0}" currList="${this.tabData.currList || 0}"
query="${this.tabData.query || ""}" query="${this.tabData.query || ""}"
@coll-tab-nav="${this.onCollTabNav}" id="pages" @coll-tab-nav="${this.onCollTabNav}" id="pages"
+19 -3
View File
@@ -15,7 +15,7 @@ function wrapCss(custom) {
return [allCss, custom]; return [allCss, custom];
} }
const IS_APP = window.electron && window.electron.IS_APP || window.matchMedia('(display-mode: standalone)').matches; const IS_APP = window.IS_APP || window.electron && window.electron.IS_APP || window.matchMedia('(display-mode: standalone)').matches;
// =========================================================================== // ===========================================================================
@@ -24,12 +24,16 @@ class FaIcon extends LitElement
constructor() { constructor() {
super(); super();
this.size = "1.1em"; this.size = "1.1em";
this.width = null;
this.height = null;
} }
static get properties() { static get properties() {
return { return {
svg: { type: String }, svg: { type: String },
size: { type: String } size: { type: String },
width: { type: String },
height: { type: String }
} }
} }
@@ -54,7 +58,19 @@ class FaIcon extends LitElement
return html``; return html``;
} }
const styles = {width: this.size, height: this.size}; const styles = {};
if (this.size) {
styles.width = this.size;
styles.height = this.size;
} else {
if (this.width) {
styles.width = this.width;
}
if (this.height) {
styles.height = this.height;
}
}
return html`<svg style="${styleMap(styles)}"><g>${unsafeSVG(this.svg)}</g></svg>`; return html`<svg style="${styleMap(styles)}"><g>${unsafeSVG(this.svg)}</g></svg>`;
} }
+190 -30
View File
@@ -11,6 +11,7 @@ import "keyword-mark-element/lib/keyword-mark.js";
import { getTS } from './pageutils'; import { getTS } from './pageutils';
import fasSearch from '@fortawesome/fontawesome-free/svgs/solid/search.svg'; import fasSearch from '@fortawesome/fontawesome-free/svgs/solid/search.svg';
import fasAngleDown from '@fortawesome/fontawesome-free/svgs/solid/angle-down.svg';
// =========================================================================== // ===========================================================================
@@ -28,7 +29,10 @@ class Pages extends LitElement
this.currList = 0; this.currList = 0;
this.active = false; this.active = false;
this.editable = false;
this.changeNeeded = false; this.changeNeeded = false;
this.selectedPages = new Set();
} }
static get sortKeys() { static get sortKeys() {
@@ -55,6 +59,10 @@ class Pages extends LitElement
query: { type: String }, query: { type: String },
loading: { type: Boolean }, loading: { type: Boolean },
editable: { type: Boolean },
selectedPages: { type: Set },
allSelected: { type: Boolean }
} }
} }
@@ -219,7 +227,9 @@ class Pages extends LitElement
flex: auto; flex: auto;
height: 100%; height: 100%;
} }
.selected {
background-color: ghostwhite;
}
.panel-block:last-child { .panel-block:last-child {
border-bottom: 1px solid rgb(237, 237, 237); border-bottom: 1px solid rgb(237, 237, 237);
} }
@@ -227,8 +237,8 @@ class Pages extends LitElement
border-left: 1px solid rgb(237, 237, 237); border-left: 1px solid rgb(237, 237, 237);
border-right: 1px solid rgb(237, 237, 237); border-right: 1px solid rgb(237, 237, 237);
} }
.panel-block.status { .status.level {
justify-content: space-between; width: 100%;
} }
nav.panel { nav.panel {
box-shadow: none; box-shadow: none;
@@ -291,24 +301,67 @@ class Pages extends LitElement
<span class="icon is-left"><fa-icon .svg="${fasSearch}"/></span> <span class="icon is-left"><fa-icon .svg="${fasSearch}"/></span>
</div> </div>
</div> </div>
<div class="panel-block status"> <div class="panel-block">
<span class="num-results">${this.formatResults()}</span> <div class="status level is-mobile">
<div class="level-left">
${this.editable ? html`
<div class="check-select level-item">
<label class="checkbox">
<input @change=${this.onSelectAll} type="checkbox" .checked="${this.allSelected}">
</label>
</div>` : ``}
${this.currList === 0 ? html` <span class="num-results level-item">${this.formatResults()}</span>
<wr-sorter id="pages"
defaultKey="ts"
?defaultDesc="true"
.sortKeys="${Pages.sortKeys}"
.data="${this.filteredPages}"
@sort-changed="${(e) => this.sortedPages = e.detail.sortedData}"
class="${this.filteredPages.length ? '' : 'is-hidden'}">
</wr-sorter>` : ``}
<div class="level-item dropdown is-hoverable">
<div class="dropdown-trigger">
<button class="button is-small" aria-haspopup="true" aria-controls="dropdown-menu">
<span>Download</span>
<span class="icon is-small">
<fa-icon .svg="${fasAngleDown}"/>
</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu" role="menu">
<div class="dropdown-content">
${this.editable ? html`
<a @click="${(e) => this.onDownload(e, "wacz", true)}" class="dropdown-item">
Download Selected as WACZ (Web Archive Collection)
</a>
<a @click="${(e) => this.onDownload(e, "warc", true)}" class="dropdown-item">
Download Selected as WARC Only
</a>
<hr class="dropdown-divider">
` : ``}
<a @click="${(e) => this.onDownload(e, "wacz", false)}" class="dropdown-item">
Download All as WACZ (Web Archive Collection)
</a>
<a @click="${(e) => this.onDownload(e, "warc", false)}" class="dropdown-item">
Download All as WARC Only
</a>
</div>
</div>
</div>
</div>
${this.currList === 0 ? html`
<div class="level-right">
<wr-sorter id="pages"
defaultKey="ts"
?defaultDesc="true"
.sortKeys="${Pages.sortKeys}"
.data="${this.filteredPages}"
@sort-changed="${(e) => this.sortedPages = e.detail.sortedData}"
class="${this.filteredPages.length ? '' : 'is-hidden'} level-item">
</wr-sorter>` : ``}
</div>
</div>
</div> </div>
<div class="scroller" @scroll="${this.onScroll}"> <div class="scroller" @scroll="${this.onScroll}">
${this.sortedPages.map((p) => html` ${this.sortedPages.map((p) => html`
<div class="panel-block"> <div class="panel-block ${this.selectedPages.has(p.id) ? 'selected' : ''}">
<wr-page-entry @delete-page="${this.onDeletePage}" replayPrefix="${this.collInfo.replayPrefix}" query="${this.query}" .page="${p}"> <wr-page-entry .editable="${this.editable}" .selected="${this.selectedPages.has(p.id)}" @sel-page="${this.onSelectToggle}" @delete-page="${this.onDeletePage}" replayPrefix="${this.collInfo.replayPrefix}" query="${this.query}" .page="${p}">
</wr-page-entry> </wr-page-entry>
</div>`)} </div>`)}
</div> </div>
@@ -316,6 +369,44 @@ class Pages extends LitElement
`; `;
} }
onSelectToggle(event) {
const {page, selected} = event.detail;
if (selected) {
this.selectedPages.add(page);
} else {
this.selectedPages.delete(page);
}
this.allSelected = (this.selectedPages.size === this.sortedPages.length);
this.requestUpdate();
}
onSelectAll(event) {
this.allSelected = event.currentTarget.checked;
if (!this.allSelected) {
this.selectedPages.clear();
} else {
//this.selectedPages = new Set();
this.sortedPages.forEach(p => {
this.selectedPages.add(p.id);
});
}
//this.allSelected = (this.selectedPages.size === this.sortedPages.length);
this.requestUpdate();
}
async onDownload(event, format, selected) {
event.preventDefault();
const params = new URLSearchParams();
params.set("pages", selected ? Array.from(this.selectedPages.keys()).join(",") : "all");
params.set("format", format);
if (this.collInfo.filename) {
params.set("filename", this.collInfo.filename);
}
window.location.href = `${this.collInfo.apiPrefix}/dl?` + params.toString();
}
async onDeletePage(event) { async onDeletePage(event) {
const page = event.currentTarget.page; const page = event.currentTarget.page;
const resp = await fetch(`${this.collInfo.apiPrefix}/page/${page.id}`, {method: 'DELETE'}); const resp = await fetch(`${this.collInfo.apiPrefix}/page/${page.id}`, {method: 'DELETE'});
@@ -339,7 +430,7 @@ class Pages extends LitElement
formatResults() { formatResults() {
if (!this.collInfo || !this.collInfo.pages.length) { if (!this.collInfo || !this.collInfo.pages.length) {
return html`No Pages defined this archive. Check out <a href="#view=resources">Page Resources</a> to search by URL.`; return html`No Pages defined this archive. Check out&nbsp;<a href="#view=resources">Page Resources</a>&nbsp;to search by URL.`;
} }
if (this.loading) { if (this.loading) {
@@ -377,6 +468,8 @@ class PageEntry extends LitElement
this.page = null; this.page = null;
this.replayPrefix = ""; this.replayPrefix = "";
this.deleting = false; this.deleting = false;
this.editable = false;
this.iconValid = false;
} }
static get properties() { static get properties() {
@@ -385,7 +478,10 @@ class PageEntry extends LitElement
textSnippet: { type: String }, textSnippet: { type: String },
page: { type: Object }, page: { type: Object },
replayPrefix: { type: String }, replayPrefix: { type: String },
deleting: { type: Boolean } deleting: { type: Boolean },
selected: { type: Boolean },
editable: { type: Boolean },
iconValid: { type: Boolean }
} }
} }
@@ -396,6 +492,19 @@ class PageEntry extends LitElement
width: 100%; width: 100%;
word-break: break-all; word-break: break-all;
position: relative; position: relative;
display: flex;
flex-direction: row;
background: transparent;
}
.check-select {
padding-right: 1.0em;
height: 100%;
margin: auto 0 auto 0;
}
.columns {
width: 100%;
} }
.favicon { .favicon {
@@ -423,10 +532,12 @@ class PageEntry extends LitElement
} }
@media screen and (max-width: 767px) { @media screen and (max-width: 767px) {
.col-date {
margin-left: calc(24px + 1rem);
}
.col-date div { .col-date div {
display: inline; display: inline;
} }
.columns { .columns {
display: flex; display: flex;
flex-direction: column-reverse; flex-direction: column-reverse;
@@ -438,24 +549,39 @@ class PageEntry extends LitElement
updated(changedProperties) { updated(changedProperties) {
if (changedProperties.has("page") || changedProperties.has("query")) { if (changedProperties.has("page") || changedProperties.has("query")) {
this.updateSnippet(); this.updateSnippet();
this.iconValid = !!this.page.favIconUrl;
//this.updateFavIcon();
this.deleting = false; this.deleting = false;
} }
} }
render() { getDateTS() {
const p = this.page;
let date = null; let date = null;
let ts = p.ts; let ts = this.page.ts;
try { try {
date = new Date(p.ts || p.date); date = new Date(this.page.ts || this.page.date);
} catch (e) { } catch (e) {
} }
const timestamp = (date && !isNaN(date)) ? getTS(date.toISOString()) : ""; const timestamp = (date && !isNaN(date)) ? getTS(date.toISOString()) : "";
return {date, timestamp};
}
render() {
const p = this.page;
const {date, timestamp} = this.getDateTS();
const hasSize = typeof(p.size) === "number"; const hasSize = typeof(p.size) === "number";
return html` return html`
${this.editable ? html`
<div class="check-select">
<label class="checkbox">
<input @change=${this.onSendSelToggle} type="checkbox" .checked="${this.selected}">
</label>
</div>` : ``}
<div class="columns"> <div class="columns">
<div class="column col-date is-2"> <div class="column col-date is-2">
<div>${date ? date.toLocaleDateString() : ""}</div> <div>${date ? date.toLocaleDateString() : ""}</div>
@@ -465,8 +591,8 @@ class PageEntry extends LitElement
<article class="media"> <article class="media">
<figure class="media-left"> <figure class="media-left">
<p class=""> <p class="">
${p.favIconUrl ? html` ${this.iconValid ? html`
<img class="favicon" src="${this.replayPrefix}/id_/${p.favIconUrl}"/>` : html` <img class="favicon" @error="${(e) => this.iconValid = false}" src="${this.replayPrefix}/${timestamp}id_/${p.favIconUrl}"/>` : html`
<span class="favicon"></span>`} <span class="favicon"></span>`}
</p> </p>
</figure> </figure>
@@ -487,11 +613,39 @@ class PageEntry extends LitElement
</article> </article>
</div> </div>
</div> </div>
${hasSize ? html`
${!this.deleting ? html` ${this.editable ? html`
<button @click="${this.onSendDeletePage}" class="delete"></button>` : html` ${!this.deleting ? html`
<button class="button delete is-loading is-static"></button> <button @click="${this.onSendDeletePage}" class="delete"></button>` : html`
`}` : ''}`; <button class="button delete is-loading is-static"></button>
`}` : ''}
`;
}
async updateFavIcon() {
if (!this.page.favIconUrl) {
this.favIconData = null;
return;
}
const {timestamp} = this.getDateTS();
const resp = await fetch(`${this.replayPrefix}/${timestamp}id_/${this.page.favIconUrl}`);
if (resp.status != 200) {
this.favIconData = null;
return;
}
const payload = await resp.arrayBuffer();
const mime = resp.headers.get("content-type");
try {
this.favIconData = `data:${mime};base64,${btoa(String.fromCharCode.apply(null, payload))}`;
} catch (e) {
console.log(e);
this.favIconData = null;
}
} }
updateSnippet() { updateSnippet() {
@@ -558,6 +712,12 @@ class PageEntry extends LitElement
this.deleting = true; this.deleting = true;
this.dispatchEvent(new CustomEvent("delete-page", {detail: {page}})); this.dispatchEvent(new CustomEvent("delete-page", {detail: {page}}));
} }
onSendSelToggle(event) {
const page = this.page.id;
const selected = event.currentTarget.checked;
this.dispatchEvent(new CustomEvent("sel-page", {detail: {page, selected}}));
}
} }
customElements.define("wr-page-view", Pages); customElements.define("wr-page-view", Pages);
+12 -4
View File
File diff suppressed because one or more lines are too long
+128 -55
View File
File diff suppressed because one or more lines are too long