Fix issues with restoring snapshot during onboarding (#9385)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Joakim Sørensen 2021-06-08 17:57:53 +02:00 committed by GitHub
parent 8a33d174d7
commit e9b1b3d853
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 95 additions and 46 deletions

View File

@ -5,6 +5,7 @@ import { customElement, property } from "lit/decorators";
import { atLeastVersion } from "../../../src/common/config/version"; import { atLeastVersion } from "../../../src/common/config/version";
import { formatDate } from "../../../src/common/datetime/format_date"; import { formatDate } from "../../../src/common/datetime/format_date";
import { formatDateTime } from "../../../src/common/datetime/format_date_time"; import { formatDateTime } from "../../../src/common/datetime/format_date_time";
import { LocalizeFunc } from "../../../src/common/translations/localize";
import "../../../src/components/ha-checkbox"; import "../../../src/components/ha-checkbox";
import "../../../src/components/ha-formfield"; import "../../../src/components/ha-formfield";
import "../../../src/components/ha-radio"; import "../../../src/components/ha-radio";
@ -67,6 +68,8 @@ const _computeAddons = (addons): AddonCheckboxItem[] =>
export class SupervisorSnapshotContent extends LitElement { export class SupervisorSnapshotContent extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property() public localize?: LocalizeFunc;
@property({ attribute: false }) public supervisor?: Supervisor; @property({ attribute: false }) public supervisor?: Supervisor;
@property({ attribute: false }) public snapshot?: HassioSnapshotDetail; @property({ attribute: false }) public snapshot?: HassioSnapshotDetail;
@ -81,6 +84,8 @@ export class SupervisorSnapshotContent extends LitElement {
@property({ type: Boolean }) public snapshotHasPassword = false; @property({ type: Boolean }) public snapshotHasPassword = false;
@property({ type: Boolean }) public onboarding = false;
@property() public snapshotName = ""; @property() public snapshotName = "";
@property() public snapshotPassword = ""; @property() public snapshotPassword = "";
@ -106,8 +111,12 @@ export class SupervisorSnapshotContent extends LitElement {
} }
} }
private _localize = (string: string) =>
this.supervisor?.localize(`snapshot.${string}`) ||
this.localize!(`ui.panel.page-onboarding.restore.${string}`);
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.supervisor) { if (!this.onboarding && !this.supervisor) {
return html``; return html``;
} }
const foldersSection = const foldersSection =
@ -119,14 +128,16 @@ export class SupervisorSnapshotContent extends LitElement {
${this.snapshot ${this.snapshot
? html`<div class="details"> ? html`<div class="details">
${this.snapshot.type === "full" ${this.snapshot.type === "full"
? this.supervisor.localize("snapshot.full_snapshot") ? this._localize("full_snapshot")
: this.supervisor.localize("snapshot.partial_snapshot")} : this._localize("partial_snapshot")}
(${Math.ceil(this.snapshot.size * 10) / 10 + " MB"})<br /> (${Math.ceil(this.snapshot.size * 10) / 10 + " MB"})<br />
${formatDateTime(new Date(this.snapshot.date), this.hass.locale)} ${this.hass
? formatDateTime(new Date(this.snapshot.date), this.hass.locale)
: this.snapshot.date}
</div>` </div>`
: html`<paper-input : html`<paper-input
name="snapshotName" name="snapshotName"
.label=${this.supervisor.localize("snapshot.name")} .label=${this.supervisor?.localize("snapshot.name") || "Name"}
.value=${this.snapshotName} .value=${this.snapshotName}
@value-changed=${this._handleTextValueChanged} @value-changed=${this._handleTextValueChanged}
> >
@ -134,13 +145,11 @@ export class SupervisorSnapshotContent extends LitElement {
${!this.snapshot || this.snapshot.type === "full" ${!this.snapshot || this.snapshot.type === "full"
? html`<div class="sub-header"> ? html`<div class="sub-header">
${!this.snapshot ${!this.snapshot
? this.supervisor.localize("snapshot.type") ? this._localize("type")
: this.supervisor.localize("snapshot.select_type")} : this._localize("select_type")}
</div> </div>
<div class="snapshot-types"> <div class="snapshot-types">
<ha-formfield <ha-formfield .label=${this._localize("full_snapshot")}>
.label=${this.supervisor.localize("snapshot.full_snapshot")}
>
<ha-radio <ha-radio
@change=${this._handleRadioValueChanged} @change=${this._handleRadioValueChanged}
value="full" value="full"
@ -149,9 +158,7 @@ export class SupervisorSnapshotContent extends LitElement {
> >
</ha-radio> </ha-radio>
</ha-formfield> </ha-formfield>
<ha-formfield <ha-formfield .label=${this._localize("partial_snapshot")}>
.label=${this.supervisor!.localize("snapshot.partial_snapshot")}
>
<ha-radio <ha-radio
@change=${this._handleRadioValueChanged} @change=${this._handleRadioValueChanged}
value="partial" value="partial"
@ -188,7 +195,7 @@ export class SupervisorSnapshotContent extends LitElement {
? html` ? html`
<ha-formfield <ha-formfield
.label=${html`<supervisor-formfield-label .label=${html`<supervisor-formfield-label
.label=${this.supervisor.localize("snapshot.folders")} .label=${this._localize("folders")}
.iconPath=${mdiFolder} .iconPath=${mdiFolder}
> >
</supervisor-formfield-label>`} </supervisor-formfield-label>`}
@ -208,7 +215,7 @@ export class SupervisorSnapshotContent extends LitElement {
? html` ? html`
<ha-formfield <ha-formfield
.label=${html`<supervisor-formfield-label .label=${html`<supervisor-formfield-label
.label=${this.supervisor.localize("snapshot.addons")} .label=${this._localize("addons")}
.iconPath=${mdiPuzzle} .iconPath=${mdiPuzzle}
> >
</supervisor-formfield-label>`} </supervisor-formfield-label>`}
@ -233,7 +240,7 @@ export class SupervisorSnapshotContent extends LitElement {
${!this.snapshot ${!this.snapshot
? html`<ha-formfield ? html`<ha-formfield
class="password" class="password"
.label=${this.supervisor.localize("snapshot.password_protection")} .label=${this._localize("password_protection")}
> >
<ha-checkbox <ha-checkbox
.checked=${this.snapshotHasPassword} .checked=${this.snapshotHasPassword}
@ -245,7 +252,7 @@ export class SupervisorSnapshotContent extends LitElement {
${this.snapshotHasPassword ${this.snapshotHasPassword
? html` ? html`
<paper-input <paper-input
.label=${this.supervisor.localize("snapshot.password")} .label=${this._localize("password")}
type="password" type="password"
name="snapshotPassword" name="snapshotPassword"
.value=${this.snapshotPassword} .value=${this.snapshotPassword}
@ -254,9 +261,7 @@ export class SupervisorSnapshotContent extends LitElement {
</paper-input> </paper-input>
${!this.snapshot ${!this.snapshot
? html` <paper-input ? html` <paper-input
.label=${this.supervisor.localize( .label=${this.supervisor?.localize("confirm_password")}
"snapshot.confirm_password"
)}
type="password" type="password"
name="confirmSnapshotPassword" name="confirmSnapshotPassword"
.value=${this.confirmSnapshotPassword} .value=${this.confirmSnapshotPassword}
@ -357,7 +362,7 @@ export class SupervisorSnapshotContent extends LitElement {
const addons = const addons =
section === "addons" section === "addons"
? new Map( ? new Map(
this.supervisor!.addon.addons.map((item) => [item.slug, item]) this.supervisor?.addon.addons.map((item) => [item.slug, item])
) )
: undefined; : undefined;
let checkedItems = 0; let checkedItems = 0;
@ -367,6 +372,7 @@ export class SupervisorSnapshotContent extends LitElement {
.label=${item.name} .label=${item.name}
.iconPath=${section === "addons" ? mdiPuzzle : mdiFolder} .iconPath=${section === "addons" ? mdiPuzzle : mdiFolder}
.imageUrl=${section === "addons" && .imageUrl=${section === "addons" &&
!this.onboarding &&
atLeastVersion(this.hass.config.version, 0, 105) && atLeastVersion(this.hass.config.version, 0, 105) &&
addons?.get(item.slug)?.icon addons?.get(item.slug)?.icon
? `/api/hassio/addons/${item.slug}/icon` ? `/api/hassio/addons/${item.slug}/icon`

View File

@ -1,13 +1,13 @@
import { ActionDetail } from "@material/mwc-list"; import { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item"; import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js"; import { mdiClose, mdiDotsVertical } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators"; import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
import { slugify } from "../../../../src/common/string/slugify"; import { slugify } from "../../../../src/common/string/slugify";
import "../../../../src/components/buttons/ha-progress-button"; import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-button-menu"; import "../../../../src/components/ha-button-menu";
import { createCloseHeading } from "../../../../src/components/ha-dialog"; import "../../../../src/components/ha-header-bar";
import "../../../../src/components/ha-svg-icon"; import "../../../../src/components/ha-svg-icon";
import { getSignedPath } from "../../../../src/data/auth"; import { getSignedPath } from "../../../../src/data/auth";
import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
@ -67,14 +67,24 @@ class HassioSnapshotDialog
open open
scrimClickAction scrimClickAction
@closed=${this.closeDialog} @closed=${this.closeDialog}
.heading=${createCloseHeading(this.hass, this._computeName)} .heading=${true}
> >
<div slot="heading">
<ha-header-bar>
<span slot="title">${this._snapshot.name}</span>
<mwc-icon-button slot="actionItems" dialogAction="cancel">
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
</mwc-icon-button>
</ha-header-bar>
</div>
${this._restoringSnapshot ${this._restoringSnapshot
? html` <ha-circular-progress active></ha-circular-progress>` ? html` <ha-circular-progress active></ha-circular-progress>`
: html`<supervisor-snapshot-content : html`<supervisor-snapshot-content
.hass=${this.hass} .hass=${this.hass}
.supervisor=${this._dialogParams.supervisor} .supervisor=${this._dialogParams.supervisor}
.snapshot=${this._snapshot} .snapshot=${this._snapshot}
.onboarding=${this._dialogParams.onboarding || false}
.localize=${this._dialogParams.localize}
> >
</supervisor-snapshot-content>`} </supervisor-snapshot-content>`}
${this._error ? html`<p class="error">Error: ${this._error}</p>` : ""} ${this._error ? html`<p class="error">Error: ${this._error}</p>` : ""}
@ -87,18 +97,20 @@ class HassioSnapshotDialog
Restore Restore
</mwc-button> </mwc-button>
<ha-button-menu ${!this._dialogParams.onboarding
fixed ? html`<ha-button-menu
slot="primaryAction" fixed
@action=${this._handleMenuAction} slot="primaryAction"
@closed=${(ev: Event) => ev.stopPropagation()} @action=${this._handleMenuAction}
> @closed=${(ev: Event) => ev.stopPropagation()}
<mwc-icon-button slot="trigger" alt="menu"> >
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon> <mwc-icon-button slot="trigger" alt="menu">
</mwc-icon-button> <ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
<mwc-list-item>Download Snapshot</mwc-list-item> </mwc-icon-button>
<mwc-list-item class="error">Delete Snapshot</mwc-list-item> <mwc-list-item>Download Snapshot</mwc-list-item>
</ha-button-menu> <mwc-list-item class="error">Delete Snapshot</mwc-list-item>
</ha-button-menu>`
: ""}
</ha-dialog> </ha-dialog>
`; `;
} }
@ -115,6 +127,12 @@ class HassioSnapshotDialog
display: block; display: block;
text-align: center; text-align: center;
} }
ha-header-bar {
--mdc-theme-on-primary: var(--primary-text-color);
--mdc-theme-primary: var(--mdc-theme-surface);
flex-shrink: 0;
display: block;
}
`, `,
]; ];
} }

View File

@ -1,4 +1,5 @@
import { fireEvent } from "../../../../src/common/dom/fire_event"; import { fireEvent } from "../../../../src/common/dom/fire_event";
import { LocalizeFunc } from "../../../../src/common/translations/localize";
import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { Supervisor } from "../../../../src/data/supervisor/supervisor";
export interface HassioSnapshotDialogParams { export interface HassioSnapshotDialogParams {
@ -6,6 +7,7 @@ export interface HassioSnapshotDialogParams {
onDelete?: () => void; onDelete?: () => void;
onboarding?: boolean; onboarding?: boolean;
supervisor?: Supervisor; supervisor?: Supervisor;
localize?: LocalizeFunc;
} }
export const showHassioSnapshotDialog = ( export const showHassioSnapshotDialog = (

View File

@ -12,7 +12,10 @@ import { HASSDomEvent } from "../common/dom/fire_event";
import { extractSearchParamsObject } from "../common/url/search-params"; import { extractSearchParamsObject } from "../common/url/search-params";
import { subscribeOne } from "../common/util/subscribe-one"; import { subscribeOne } from "../common/util/subscribe-one";
import { AuthUrlSearchParams, hassUrl } from "../data/auth"; import { AuthUrlSearchParams, hassUrl } from "../data/auth";
import { fetchDiscoveryInformation } from "../data/discovery"; import {
DiscoveryInformation,
fetchDiscoveryInformation,
} from "../data/discovery";
import { import {
fetchOnboardingOverview, fetchOnboardingOverview,
OnboardingResponses, OnboardingResponses,
@ -68,6 +71,8 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
@state() private _steps?: OnboardingStep[]; @state() private _steps?: OnboardingStep[];
@state() private _discoveryInformation?: DiscoveryInformation;
protected render(): TemplateResult { protected render(): TemplateResult {
const step = this._curStep()!; const step = this._curStep()!;
@ -87,6 +92,7 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
? html`<onboarding-restore-snapshot ? html`<onboarding-restore-snapshot
.localize=${this.localize} .localize=${this.localize}
.restoring=${this._restoring} .restoring=${this._restoring}
.discoveryInformation=${this._discoveryInformation}
@restoring=${this._restoringSnapshot} @restoring=${this._restoringSnapshot}
> >
</onboarding-restore-snapshot>` </onboarding-restore-snapshot>`

View File

@ -4,9 +4,12 @@ import { customElement, property } from "lit/decorators";
import "../../hassio/src/components/hassio-ansi-to-html"; import "../../hassio/src/components/hassio-ansi-to-html";
import { showHassioSnapshotDialog } from "../../hassio/src/dialogs/snapshot/show-dialog-hassio-snapshot"; import { showHassioSnapshotDialog } from "../../hassio/src/dialogs/snapshot/show-dialog-hassio-snapshot";
import { showSnapshotUploadDialog } from "../../hassio/src/dialogs/snapshot/show-dialog-snapshot-upload"; import { showSnapshotUploadDialog } from "../../hassio/src/dialogs/snapshot/show-dialog-snapshot-upload";
import { navigate } from "../common/navigate";
import type { LocalizeFunc } from "../common/translations/localize"; import type { LocalizeFunc } from "../common/translations/localize";
import "../components/ha-card"; import "../components/ha-card";
import {
DiscoveryInformation,
fetchDiscoveryInformation,
} from "../data/discovery";
import { makeDialogManager } from "../dialogs/make-dialog-manager"; import { makeDialogManager } from "../dialogs/make-dialog-manager";
import { ProvideHassLitMixin } from "../mixins/provide-hass-lit-mixin"; import { ProvideHassLitMixin } from "../mixins/provide-hass-lit-mixin";
import { haStyle } from "../resources/styles"; import { haStyle } from "../resources/styles";
@ -26,6 +29,9 @@ class OnboardingRestoreSnapshot extends ProvideHassLitMixin(LitElement) {
@property({ type: Boolean }) public restoring = false; @property({ type: Boolean }) public restoring = false;
@property({ attribute: false })
public discoveryInformation?: DiscoveryInformation;
protected render(): TemplateResult { protected render(): TemplateResult {
return this.restoring return this.restoring
? html`<ha-card ? html`<ha-card
@ -58,13 +64,14 @@ class OnboardingRestoreSnapshot extends ProvideHassLitMixin(LitElement) {
private async _checkRestoreStatus(): Promise<void> { private async _checkRestoreStatus(): Promise<void> {
if (this.restoring) { if (this.restoring) {
try { try {
const response = await fetch("/api/hassio/supervisor/info", { const response = await fetchDiscoveryInformation();
method: "GET",
}); if (
if (response.status === 401) { !this.discoveryInformation ||
// If we get a unauthorized response, the restore is done this.discoveryInformation.uuid !== response.uuid
navigate("/", { replace: true }); ) {
location.reload(); // When the UUID changes, the restore is complete
window.location.replace("/");
} }
} catch (err) { } catch (err) {
// We fully expected issues with fetching info untill restore is complete. // We fully expected issues with fetching info untill restore is complete.
@ -76,6 +83,7 @@ class OnboardingRestoreSnapshot extends ProvideHassLitMixin(LitElement) {
showHassioSnapshotDialog(this, { showHassioSnapshotDialog(this, {
slug, slug,
onboarding: true, onboarding: true,
localize: this.localize,
}); });
} }

View File

@ -3569,7 +3569,16 @@
"description": "Alternatively you can restore from a previous snapshot.", "description": "Alternatively you can restore from a previous snapshot.",
"in_progress": "Restore in progress", "in_progress": "Restore in progress",
"show_log": "Show full log", "show_log": "Show full log",
"hide_log": "Hide full log" "hide_log": "Hide full log",
"full_snapshot": "[%key:supervisor::snapshot::full_snapshot%]",
"partial_snapshot": "[%key:supervisor::snapshot::partial_snapshot%]",
"type": "[%key:supervisor::snapshot::type%]",
"select_type": "[%key:supervisor::snapshot::select_type%]",
"folders": "[%key:supervisor::snapshot::folders%]",
"addons": "[%key:supervisor::snapshot::addons%]",
"password_protection": "[%key:supervisor::snapshot::password_protection%]",
"password": "[%key:supervisor::snapshot::password%]",
"confirm_password": "[%key:supervisor::snapshot::confirm_password%]"
} }
}, },
"custom": { "custom": {