Collection share dialog + copy updates (#1056)

- Always shows primary "Share" action button in Collection detail page.
- Enables toggling shareable status and share info from dialog. Difference from mockups: I made the "Done" button neutral do differentiate from our submit action buttons in the dialog, since toggling will apply changes immediately.
- Menu item: "Go to Public View"/"Go to Shareable View" -> "Visit Shareable URL". 
- Toggle label: "Make Collection Shareable" -> "Collection is Shareable".
- Additional dialog copy: adds "This collection can be viewed by anyone with the link." under "Link to Share" and "Share this collection by embedding it into an existing webpage." under "Embed Collection".
- Moves share status icon to its own column in list view.
- Adds new syntax-highlighted code component that supports js and html.

Co-authored-by: Henry Wilkinson <henry@wilkinson.graphics>
This commit is contained in:
sua yoo 2023-08-09 10:12:46 -07:00 committed by GitHub
parent de3e5907a7
commit b494070e43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 184 additions and 71 deletions

View File

@ -32,6 +32,7 @@
"eslint-webpack-plugin": "^3.1.1",
"fuse.js": "^6.5.3",
"glob": "^8.1.0",
"highlight.js": "^11.8.0",
"html-loader": "^3.0.1",
"html-webpack-plugin": "^5.5.0",
"immutable": "^4.1.0",

View File

@ -0,0 +1,56 @@
import { LitElement, html, css } from "lit";
import { property } from "lit/decorators.js";
import { html as staticHtml, unsafeStatic } from "lit/static-html.js";
import hljs from "highlight.js/lib/core";
import javascript from "highlight.js/lib/languages/javascript";
import xml from "highlight.js/lib/languages/xml";
/**
* Syntax highlighting for javascript and HTML (XML)
*/
export class Code extends LitElement {
static styles = [
css`
pre {
white-space: pre-wrap;
font-family: var(--sl-font-mono);
font-size: 0.9em;
color: #24292e;
margin: 0;
}
.hljs-name {
color: #22863a;
}
.hljs-attr {
color: #6f42c1;
}
.hljs-string {
color: #032f62;
}
`,
];
@property({ type: String })
value: string = "";
@property({ type: String })
language: "javascript" | "xml" = "xml";
constructor() {
super();
hljs.registerLanguage("javascript", javascript);
hljs.registerLanguage("xml", xml);
}
render() {
const htmlStr = hljs.highlight(this.value, {
language: this.language,
}).value;
return html`<pre><code>${staticHtml`${unsafeStatic(
htmlStr
)}`}</code></pre>`;
}
}

View File

@ -150,6 +150,9 @@ import("./file-list").then(({ FileList, FileListItem }) => {
import("./collections-add").then(({ CollectionsAdd }) => {
customElements.define("btrix-collections-add", CollectionsAdd);
});
import("./code").then(({ Code }) => {
customElements.define("btrix-code", Code);
});
customElements.define("btrix-alert", Alert);
customElements.define("btrix-input", Input);

View File

@ -5,6 +5,7 @@ import { when } from "lit/directives/when.js";
import { guard } from "lit/directives/guard.js";
import queryString from "query-string";
import type { TemplateResult } from "lit";
import type { SlCheckbox } from "@shoelace-style/shoelace";
import type { AuthState } from "../../utils/AuthService";
import LiteElement, { html } from "../../utils/LiteElement";
@ -52,7 +53,7 @@ export class CollectionDetail extends LiteElement {
private isDescriptionExpanded = false;
@state()
private showEmbedInfo = false;
private showShareInfo = false;
// Use to cancel requests
private getArchivedItemsController: AbortController | null = null;
@ -109,15 +110,14 @@ export class CollectionDetail extends LiteElement {
html`<sl-skeleton class="w-96"></sl-skeleton>`}
</h1>
</div>
${when(
this.collection?.isPublic,
() => html`
<sl-button size="small" @click=${() => (this.showEmbedInfo = true)}>
<sl-icon name="code-slash"></sl-icon>
View Embed Code
</sl-button>
`
)}
<sl-button
variant="primary"
size="small"
@click=${() => (this.showShareInfo = true)}
>
<sl-icon name="box-arrow-up" slot="prefix"></sl-icon>
Share
</sl-button>
${when(this.isCrawler, this.renderActions)}
</header>
<div class="border rounded-lg py-2 mb-3">${this.renderInfoBar()}</div>
@ -159,7 +159,7 @@ export class CollectionDetail extends LiteElement {
>
</div>
</btrix-dialog>
${this.renderShareInfo()}`;
${this.renderShareDialog()}`;
}
private getPublicReplayURL() {
@ -169,34 +169,83 @@ export class CollectionDetail extends LiteElement {
).href;
}
private renderShareInfo = () => {
if (!this.collection?.isPublic) {
return;
}
private renderShareDialog() {
return html`
<btrix-dialog
label=${msg("Share Collection")}
?open=${this.showShareInfo}
@sl-request-close=${() => (this.showShareInfo = false)}
style="--width: 32rem;"
>
${this.collection?.isPublic
? ""
: html`<p class="mb-3">
${msg(
"Make this collection shareable to enable a public viewing link."
)}
</p>`}
<div class="mb-5">
<sl-switch
?checked=${this.collection?.isPublic}
@sl-change=${(e: CustomEvent) =>
this.onTogglePublic((e.target as SlCheckbox).checked)}
>${msg("Collection is Shareable")}</sl-switch
>
</div>
${when(this.collection?.isPublic, this.renderShareInfo)}
<div slot="footer" class="flex justify-end">
<sl-button size="small" @click=${() => (this.showShareInfo = false)}
>${msg("Done")}</sl-button
>
</div>
</btrix-dialog>
`;
}
const embedCode = `<replay-web-page source="${this.getPublicReplayURL()}"></replay-web-page>`;
private renderShareInfo = () => {
const replaySrc = this.getPublicReplayURL();
const publicReplayUrl = `https://replayweb.page?source=${replaySrc}`;
const embedCode = `<replay-web-page source="${replaySrc}"></replay-web-page>`;
const importCode = `importScripts("https://replayweb.page/sw.js");`;
return html` <btrix-dialog
label=${msg(str`Embed Code for “${this.collection?.name}`)}
?open=${this.showEmbedInfo}
@sl-request-close=${() => (this.showEmbedInfo = false)}
>
<div class="text-left">
<p class="mb-5">
return html` <btrix-section-heading
>${msg("Link to Share")}</btrix-section-heading
>
<section class="mt-3 mb-5">
<p class="mb-3">
${msg("This collection can be viewed by anyone with the link.")}
</p>
<div class="flex items-center rounded border">
<div class="text-base">
<btrix-copy-button
.getValue=${() => publicReplayUrl}
content=${msg("Copy Public URL")}
></btrix-copy-button>
</div>
<div class="flex-1 min-w-0 truncate">${publicReplayUrl}</div>
<div class="text-base">
<sl-icon-button
href=${publicReplayUrl}
name="box-arrow-up-right"
target="_blank"
>
</sl-icon-button>
</div>
</div>
</section>
<btrix-section-heading>${msg("Embed Collection")}</btrix-section-heading>
<section class="mt-3">
<p class="mb-3">
${msg(
html`Embed this collection in another site using these
<strong class="font-medium">ReplayWeb.page</strong> code snippets.`
html`Share this collection by embedding it into an existing webpage.`
)}
</p>
<p class="mb-3">
${msg(html`Add the following embed code to your HTML page:`)}
</p>
<div class="relative">
<pre
class="whitespace-pre-wrap mb-5 rounded p-4 bg-slate-50 text-slate-600 text-[0.9em]"
><code>${embedCode}</code></pre>
<div class="absolute top-0 right-0">
<div class="relative mb-5 border rounded p-3 pr-9 bg-slate-50">
<btrix-code value=${embedCode}></btrix-code>
<div class="absolute top-1.5 right-1.5 border rounded bg-white shadow-sm">
<btrix-copy-button
.getValue=${() => embedCode}
content=${msg("Copy Embed Code")}
@ -205,15 +254,13 @@ export class CollectionDetail extends LiteElement {
</div>
<p class="mb-3">
${msg(
html`Add the following JavaScript to
<code class="text-[0.9em]">./replay/sw.js</code>:`
html`Add the following JavaScript to your
<code class="text-[0.9em]">/replay/sw.js</code>:`
)}
</p>
<div class="relative">
<pre
class="whitespace-pre-wrap mb-5 rounded p-4 bg-slate-50 text-slate-600 text-[0.9em]"
><code>${importCode}</code></pre>
<div class="absolute top-0 right-0">
<div class="relative mb-5 border rounded p-3 pr-9 bg-slate-50">
<btrix-code language="javascript" value=${importCode}></btrix-code>
<div class="absolute top-1.5 right-1.5 border rounded bg-white shadow-sm">
<btrix-copy-button
.getValue=${() => importCode}
content=${msg("Copy JS")}
@ -233,8 +280,7 @@ export class CollectionDetail extends LiteElement {
for more details.`
)}
</p>
</div>
</btrix-dialog>`;
</section>`;
};
private renderHeader = () => html`
@ -314,7 +360,7 @@ export class CollectionDetail extends LiteElement {
slot="prefix"
href="https://replayweb.page?source=${this.getPublicReplayURL()}"
>
Go to Public View
Visit Shareable URL
</a>
</sl-menu-item>
<sl-menu-item

View File

@ -369,10 +369,16 @@ export class CollectionsList extends LiteElement {
return html`
<header class="py-2 text-neutral-600 leading-none">
<div
class="hidden md:grid md:grid-cols-[repeat(2,1fr)_repeat(3,12ch)_16ch_2.5rem] gap-3"
class="hidden md:grid md:grid-cols-[2rem_1fr_repeat(3,12ch)_18ch_2.5rem] gap-3"
>
<div class="col-span-1 text-xs pl-3">${msg("Collection Name")}</div>
<div class="col-span-1 text-xs">${msg("Top 3 Tags")}</div>
<div class="col-span-1 pl-3 text-center">
<sl-icon
class="block text-[15px]"
name="eye"
label=${msg("Collection share access")}
></sl-icon>
</div>
<div class="col-span-1 text-xs">${msg("Name")}</div>
<div class="col-span-1 text-xs">${msg("Archived Items")}</div>
<div class="col-span-1 text-xs">${msg("Total Size")}</div>
<div class="col-span-1 text-xs">${msg("Total Pages")}</div>
@ -446,42 +452,38 @@ export class CollectionsList extends LiteElement {
html`<li class="mb-2 last:mb-0">
<div class="block border rounded leading-none">
<div
class="relative p-3 md:p-0 grid grid-cols-1 md:grid-cols-[repeat(2,1fr)_repeat(3,12ch)_16ch_2.5rem] gap-3 lg:h-10 items-center"
class="relative p-3 md:p-0 grid grid-cols-1 md:grid-cols-[2rem_1fr_repeat(3,12ch)_18ch_2.5rem] gap-3 lg:h-10 items-center"
>
<div class="col-span-1 md:pl-3 truncate font-semibold">
<div class="col-span-1 md:pl-3 text-base text-neutral-500">
${col?.isPublic
? html`
<sl-tooltip content=${msg("Shareable")}>
<sl-icon
class="inline-block align-middle"
name="people-fill"
label=${msg("Shareable Collection")}
></sl-icon>
</sl-tooltip>
`
: html`
<sl-tooltip content=${msg("Private")}>
<sl-icon
class="inline-block align-middle"
name="eye-slash-fill"
label=${msg("Private Collection")}
></sl-icon>
</sl-tooltip>
`}
</div>
<div class="col-span-1 truncate font-semibold">
<a
href=${`/orgs/${this.orgId}/collections/view/${col.id}`}
class="block text-primary hover:text-indigo-500"
@click=${this.navLink}
>
${col?.isPublic
? html`
<sl-tooltip content=${msg("Shareable")}>
<sl-icon
style="margin-right: 4px; vertical-align: bottom; font-size: 14px;"
name="people-fill"
></sl-icon>
</sl-tooltip>
`
: html`
<sl-tooltip content=${msg("Private")}>
<sl-icon
style="margin-right: 4px; vertical-align: bottom; font-size: 14px;"
name="eye-slash-fill"
></sl-icon>
</sl-tooltip>
`}
${col.name}
</a>
</div>
<div class="col-span-1 order-last md:order-none truncate">
${col.tags
.slice(0, 3)
.map(
(tag) =>
html`<btrix-tag class="mr-1" size="small">${tag}</btrix-tag>`
)}
</div>
<div
class="col-span-1 truncate text-xs text-neutral-500 font-monostyle"
>
@ -560,7 +562,7 @@ export class CollectionsList extends LiteElement {
col
)}"
>
Go to Shared View
Visit Shareable URL
</a>
</sl-menu-item>
<sl-menu-item

View File

@ -3720,6 +3720,11 @@ he@^1.2.0:
resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
highlight.js@^11.8.0:
version "11.8.0"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.8.0.tgz#966518ea83257bae2e7c9a48596231856555bb65"
integrity sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==
hoist-non-react-statics@^3.3.1:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"