Frontend browser profile editor enhancements (#288)

- add button to duplicate profile from main view
- add save / cancel button when editing
- change location of 'full screen' button
This commit is contained in:
sua yoo 2022-08-10 15:51:34 -07:00 committed by GitHub
parent b11a5f136a
commit ee6161ad43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 172 additions and 125 deletions

View File

@ -21,6 +21,8 @@ const POLL_INTERVAL_SECONDS = 2;
* origins=${origins}
* ></btrix-profile-browser>
* ```
*
* @event load Event on iframe load, with src URL
*/
@localized()
export class ProfileBrowser extends LiteElement {
@ -49,6 +51,9 @@ export class ProfileBrowser extends LiteElement {
@state()
private iframeSrc?: string;
@state()
private isIframeLoaded = false;
@state()
private hasFetchError = false;
@ -74,9 +79,11 @@ export class ProfileBrowser extends LiteElement {
updated(changedProperties: Map<string, any>) {
if (changedProperties.has("browserId")) {
if (this.browserId) {
window.clearTimeout(this.pollTimerId);
this.fetchBrowser();
} else if (changedProperties.get("browserId")) {
this.iframeSrc = undefined;
this.isIframeLoaded = false;
window.clearTimeout(this.pollTimerId);
}
@ -96,8 +103,8 @@ export class ProfileBrowser extends LiteElement {
<div
class="rounded-b lg:rounded-b-none lg:rounded-r border w-72 bg-white absolute h-full top-0 right-0"
>
${this.renderFullscreenButton()} ${this.renderOrigins()}
${this.renderNewOrigins()}
${document.fullscreenEnabled ? this.renderFullscreenButton() : ""}
${this.renderOrigins()} ${this.renderNewOrigins()}
</div>
</div>
</div>
@ -135,11 +142,12 @@ export class ProfileBrowser extends LiteElement {
class="w-full h-full"
title=${msg("Interactive browser for creating browser profile")}
src=${this.iframeSrc}
@load=${this.onIframeLoad}
${ref((el) => this.onIframeRef(el as HTMLIFrameElement))}
></iframe>`;
}
if (this.browserId && !this.iframeSrc) {
if (this.browserId && !this.isIframeLoaded) {
return html`
<div
class="w-full h-full flex items-center justify-center text-4xl"
@ -154,37 +162,37 @@ export class ProfileBrowser extends LiteElement {
}
private renderFullscreenButton() {
return html`${document.fullscreenEnabled
? html`
<div class="p-2 text-right">
<sl-button
size="small"
@click=${() =>
this.isFullscreen
? document.exitFullscreen()
: this.enterFullscreen("interactive-browser")}
>
${this.isFullscreen
? html`
<sl-icon
slot="prefix"
name="fullscreen-exit"
label=${msg("Exit fullscreen")}
></sl-icon>
${msg("Exit")}
`
: html`
<sl-icon
slot="prefix"
name="arrows-fullscreen"
label=${msg("Enter fullscreen")}
></sl-icon>
${msg("Fullscreen")}
`}
</sl-button>
</div>
`
: ""}`;
if (!this.browserId) return;
return html`
<div class="p-2 text-right">
<sl-button
size="small"
@click=${() =>
this.isFullscreen
? document.exitFullscreen()
: this.enterFullscreen("interactive-browser")}
>
${this.isFullscreen
? html`
<sl-icon
slot="prefix"
name="fullscreen-exit"
label=${msg("Exit fullscreen")}
></sl-icon>
${msg("Exit")}
`
: html`
<sl-icon
slot="prefix"
name="arrows-fullscreen"
label=${msg("Enter fullscreen")}
></sl-icon>
${msg("Fullscreen")}
`}
</sl-button>
</div>
`;
}
private renderOrigins() {
@ -244,9 +252,14 @@ export class ProfileBrowser extends LiteElement {
}
/**
* Fetch browser profiles and update internal state
* Fetch browser profile and update internal state
*/
private async fetchBrowser(): Promise<void> {
await this.updateComplete;
this.iframeSrc = undefined;
this.isIframeLoaded = false;
try {
await this.checkBrowserStatus();
} catch (e) {
@ -280,6 +293,10 @@ export class ProfileBrowser extends LiteElement {
this.iframeSrc = result.url;
await this.updateComplete;
this.dispatchEvent(new CustomEvent("load", { detail: result.url }));
this.pingBrowser();
} else {
console.debug("Unknown checkBrowserStatus state");
@ -333,7 +350,7 @@ export class ProfileBrowser extends LiteElement {
if (!this.origins) {
this.origins = data.origins;
} else {
this.newOrigins = data.origins.filter(
this.newOrigins = data.origins?.filter(
(url: string) => !this.origins?.includes(url)
);
}
@ -359,6 +376,12 @@ export class ProfileBrowser extends LiteElement {
}
}
private onIframeLoad() {
this.isIframeLoaded = true;
this.dispatchEvent(new CustomEvent("load", { detail: this.iframeSrc }));
}
private onFullscreenChange = () => {
if (document.fullscreenElement) {
this.isFullscreen = true;

View File

@ -33,7 +33,10 @@ export class BrowserProfilesDetail extends LiteElement {
private profile?: Profile;
@state()
private isLoading = false;
private isBrowserLoading = false;
@state()
private isBrowserLoaded = false;
@state()
private isSubmittingBrowserChange = false;
@ -41,9 +44,6 @@ export class BrowserProfilesDetail extends LiteElement {
@state()
private isSubmittingProfileChange = false;
@state()
private showSaveButton = false;
@state()
private browserId?: string;
@ -53,10 +53,10 @@ export class BrowserProfilesDetail extends LiteElement {
@state()
private isEditDialogContentVisible = false;
private showSaveButtonTimerId?: number;
disconnectedCallback() {
window.clearTimeout(this.showSaveButtonTimerId);
if (this.browserId) {
this.deleteBrowser(this.browserId);
}
}
firstUpdated() {
@ -172,72 +172,85 @@ export class BrowserProfilesDetail extends LiteElement {
<section>
<header>
<h3 class="text-lg font-medium mb-2">
${msg("Browser Profile Editor")}
</h3>
<h3 class="text-lg font-medium">${msg("Browser Profile")}</h3>
</header>
<main class="relative">
<btrix-profile-browser
.authState=${this.authState}
archiveId=${this.archiveId}
browserId=${ifDefined(this.browserId)}
.origins=${this.profile?.origins}
></btrix-profile-browser>
${this.browserId && !this.isLoading
? html`
<!-- Hide browser area with overlay -->
<!-- TODO remove when browser no longer shows dev tools -->
${this.isSubmittingBrowserChange
? html`<div
class="absolute top-0 left-0 h-full flex items-center justify-center text-4xl bg-slate-50 lg:rounded-l-lg border border-r-0"
style="right: ${ProfileBrowser.SIDE_BAR_WIDTH}px;"
>
<sl-spinner></sl-spinner>
</div>`
: ""}
${this.showSaveButton
? html`<div
class="absolute top-0 p-2"
style="right: ${ProfileBrowser.SIDE_BAR_WIDTH}px;"
>
<sl-button
class="shadow"
type="primary"
size="small"
@click=${this.saveBrowser}
>${msg("Done Editing")}</sl-button
>
</div>`
: ""}
`
: html`
<div
class="absolute top-0 left-0 h-full flex flex-col items-center justify-center"
style="right: ${ProfileBrowser.SIDE_BAR_WIDTH}px;"
>
<p class="mb-4 text-neutral-600 max-w-prose">
<div class="rounded p-2 bg-slate-50">
<div class="mb-2 flex justify-between items-center">
<div class="text-sm text-neutral-500 mx-1">
${this.browserId
? html`
${msg(
"Load browser to view or edit websites in the profile."
"Interact with the browsing tool to make changes to your browser profile."
)}
</p>
<sl-button
type="primary"
outline
?disabled=${this.isLoading ||
!ProfileBrowser.isBrowserCompatible}
?loading=${this.isLoading}
@click=${this.startBrowserPreview}
><sl-icon
slot="prefix"
name="collection-play-fill"
></sl-icon>
${msg("Load Browser")}</sl-button
`
: ""}
</div>
<div>
${this.browserId && !this.isBrowserLoading
? html`
<sl-button size="small" @click=${this.cancelEditBrowser}
>${msg("Cancel")}</sl-button
>
<sl-button
type="primary"
size="small"
?loading=${this.isSubmittingBrowserChange}
?disabled=${this.isSubmittingBrowserChange ||
!this.isBrowserLoaded}
@click=${this.saveBrowser}
>${msg("Save")}</sl-button
>
`
: html`
<sl-button
size="small"
?loading=${this.isBrowserLoading}
?disabled=${this.isBrowserLoading}
@click=${this.duplicateProfile}
>${msg("Duplicate")}</sl-button
>
`}
</div>
</div>
<main class="relative">
<btrix-profile-browser
.authState=${this.authState}
archiveId=${this.archiveId}
browserId=${ifDefined(this.browserId)}
.origins=${this.profile?.origins}
@load=${() => (this.isBrowserLoaded = true)}
></btrix-profile-browser>
${this.browserId || this.isBrowserLoading
? ""
: html`
<div
class="absolute top-0 left-0 h-full flex flex-col items-center justify-center"
style="right: ${ProfileBrowser.SIDE_BAR_WIDTH}px;"
>
</div>
`}
</main>
<p class="mb-4 text-neutral-600 max-w-prose">
${msg(
"Load browser to view or edit websites in the profile."
)}
</p>
<sl-button
type="primary"
outline
?disabled=${!ProfileBrowser.isBrowserCompatible}
@click=${this.startBrowserPreview}
><sl-icon
slot="prefix"
name="collection-play-fill"
></sl-icon>
${msg("Load Browser")}</sl-button
>
</div>
`}
</main>
</div>
</section>
<sl-dialog
@ -254,7 +267,7 @@ export class BrowserProfilesDetail extends LiteElement {
private renderMenu() {
return html`
<sl-dropdown placement="bottom-end" distance="4">
<sl-button slot="trigger" type="primary" size="small" caret
<sl-button slot="trigger" size="small" caret
>${msg("Actions")}</sl-button
>
@ -351,27 +364,17 @@ export class BrowserProfilesDetail extends LiteElement {
private async startBrowserPreview() {
if (!this.profile) return;
this.isLoading = true;
this.isBrowserLoading = true;
const url = this.profile.origins[0];
try {
const data = await this.createBrowser({ url });
this.notify({
message: msg("Starting up browser..."),
type: "success",
icon: "check2-circle",
});
this.browserId = data.browserid;
// Slightly delay showing the save button while browser loads
this.showSaveButtonTimerId = window.setTimeout(() => {
this.showSaveButton = true;
}, 3 * 1000);
this.isBrowserLoading = false;
} catch (e) {
this.isLoading = false;
this.isBrowserLoading = false;
this.notify({
message: msg("Sorry, couldn't preview browser profile at this time."),
@ -379,14 +382,28 @@ export class BrowserProfilesDetail extends LiteElement {
icon: "exclamation-octagon",
});
}
}
this.isLoading = false;
private async cancelEditBrowser() {
const prevBrowserId = this.browserId;
this.isBrowserLoaded = false;
this.browserId = undefined;
if (prevBrowserId) {
try {
await this.deleteBrowser(prevBrowserId);
} catch (e) {
// TODO Investigate DELETE is returning 404
console.debug(e);
}
}
}
private async duplicateProfile() {
if (!this.profile) return;
this.isLoading = true;
this.isBrowserLoading = true;
const url = this.profile.origins[0];
@ -409,7 +426,7 @@ export class BrowserProfilesDetail extends LiteElement {
)}&profileId=${window.encodeURIComponent(this.profile.id)}&navigateUrl=`
);
} catch (e) {
this.isLoading = false;
this.isBrowserLoading = false;
this.notify({
message: msg("Sorry, couldn't create browser profile at this time."),
@ -479,6 +496,16 @@ export class BrowserProfilesDetail extends LiteElement {
);
}
private deleteBrowser(id: string) {
return this.apiFetch(
`/archives/${this.archiveId}/profiles/browser/${id}`,
this.authState!,
{
method: "DELETE",
}
);
}
/**
* Fetch browser profile and update internal state
*/
@ -511,7 +538,7 @@ export class BrowserProfilesDetail extends LiteElement {
if (
!window.confirm(
msg(
"Save browser changes to profile? You will need to reload the editor to make additional changes."
"Save browser changes to profile? You will need to re-load the browsing tool to make additional changes."
)
)
) {
@ -519,7 +546,6 @@ export class BrowserProfilesDetail extends LiteElement {
}
this.isSubmittingBrowserChange = true;
this.showSaveButton = false;
const params = {
name: this.profile!.name,
@ -553,8 +579,6 @@ export class BrowserProfilesDetail extends LiteElement {
type: "danger",
icon: "exclamation-octagon",
});
this.showSaveButton = true;
}
this.isSubmittingBrowserChange = false;