diff --git a/hassio/src/components/supervisor-formfield-label.ts b/hassio/src/components/supervisor-formfield-label.ts new file mode 100644 index 0000000000..e173c0c44c --- /dev/null +++ b/hassio/src/components/supervisor-formfield-label.ts @@ -0,0 +1,55 @@ +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import "../../../src/components/ha-svg-icon"; + +@customElement("supervisor-formfield-label") +class SupervisorFormfieldLabel extends LitElement { + @property({ type: String }) public label!: string; + + @property({ type: String }) public imageUrl?: string; + + @property({ type: String }) public iconPath?: string; + + @property({ type: String }) public version?: string; + + protected render(): TemplateResult { + return html` + ${this.imageUrl + ? html`` + : this.iconPath + ? html`` + : ""} + ${this.label} + ${this.version + ? html`(${this.version})` + : ""} + `; + } + + static get styles(): CSSResultGroup { + return css` + :host { + cursor: pointer; + display: flex; + align-items: center; + } + .label { + margin-right: 4px; + } + .version { + color: var(--secondary-text-color); + } + .icon { + max-height: 22px; + max-width: 22px; + margin-right: 8px; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "supervisor-formfield-label": SupervisorFormfieldLabel; + } +} diff --git a/hassio/src/components/supervisor-snapshot-content.ts b/hassio/src/components/supervisor-snapshot-content.ts new file mode 100644 index 0000000000..8c87976329 --- /dev/null +++ b/hassio/src/components/supervisor-snapshot-content.ts @@ -0,0 +1,418 @@ +import { mdiFolder, mdiHomeAssistant, mdiPuzzle } from "@mdi/js"; +import { PaperInputElement } from "@polymer/paper-input/paper-input"; +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import { atLeastVersion } from "../../../src/common/config/version"; +import { formatDate } from "../../../src/common/datetime/format_date"; +import { formatDateTime } from "../../../src/common/datetime/format_date_time"; +import "../../../src/components/ha-checkbox"; +import "../../../src/components/ha-formfield"; +import "../../../src/components/ha-radio"; +import type { HaRadio } from "../../../src/components/ha-radio"; +import { + HassioFullSnapshotCreateParams, + HassioPartialSnapshotCreateParams, + HassioSnapshotDetail, +} from "../../../src/data/hassio/snapshot"; +import { Supervisor } from "../../../src/data/supervisor/supervisor"; +import { PolymerChangedEvent } from "../../../src/polymer-types"; +import { HomeAssistant } from "../../../src/types"; +import "./supervisor-formfield-label"; + +interface CheckboxItem { + slug: string; + checked: boolean; + name: string; +} + +interface AddonCheckboxItem extends CheckboxItem { + version: string; +} + +const _computeFolders = (folders): CheckboxItem[] => { + const list: CheckboxItem[] = []; + if (folders.includes("homeassistant")) { + list.push({ + slug: "homeassistant", + name: "Home Assistant configuration", + checked: false, + }); + } + if (folders.includes("ssl")) { + list.push({ slug: "ssl", name: "SSL", checked: false }); + } + if (folders.includes("share")) { + list.push({ slug: "share", name: "Share", checked: false }); + } + if (folders.includes("addons/local")) { + list.push({ slug: "addons/local", name: "Local add-ons", checked: false }); + } + return list.sort((a, b) => (a.name > b.name ? 1 : -1)); +}; + +const _computeAddons = (addons): AddonCheckboxItem[] => + addons + .map((addon) => ({ + slug: addon.slug, + name: addon.name, + version: addon.version, + checked: false, + })) + .sort((a, b) => (a.name > b.name ? 1 : -1)); + +@customElement("supervisor-snapshot-content") +export class SupervisorSnapshotContent extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public supervisor?: Supervisor; + + @property({ attribute: false }) public snapshot?: HassioSnapshotDetail; + + @property() public snapshotType: HassioSnapshotDetail["type"] = "full"; + + @property({ attribute: false }) public folders?: CheckboxItem[]; + + @property({ attribute: false }) public addons?: AddonCheckboxItem[]; + + @property({ type: Boolean }) public homeAssistant = false; + + @property({ type: Boolean }) public snapshotHasPassword = false; + + @property() public snapshotName = ""; + + @property() public snapshotPassword = ""; + + public willUpdate(changedProps) { + super.willUpdate(changedProps); + if (!this.hasUpdated) { + this.folders = _computeFolders( + this.snapshot + ? this.snapshot.folders + : ["homeassistant", "ssl", "share", "media", "addons/local"] + ); + this.addons = _computeAddons( + this.snapshot + ? this.snapshot.addons + : this.supervisor?.supervisor.addons + ); + this.snapshotType = this.snapshot?.type || "full"; + this.snapshotName = this.snapshot?.name || ""; + this.snapshotHasPassword = this.snapshot?.protected || false; + } + } + + protected render(): TemplateResult { + if (!this.supervisor) { + return html``; + } + const foldersSection = + this.snapshotType === "partial" ? this._getSection("folders") : undefined; + const addonsSection = + this.snapshotType === "partial" ? this._getSection("addons") : undefined; + + return html` + ${this.snapshot + ? html`
+ ${this.snapshot.type === "full" + ? this.supervisor.localize("snapshot.full_snapshot") + : this.supervisor.localize("snapshot.partial_snapshot")} + (${Math.ceil(this.snapshot.size * 10) / 10 + " MB"})
+ ${formatDateTime(new Date(this.snapshot.date), this.hass.locale)} +
` + : html` + `} + ${!this.snapshot || this.snapshot.type === "full" + ? html`
+ ${!this.snapshot + ? this.supervisor.localize("snapshot.type") + : this.supervisor.localize("snapshot.select_type")} +
+
+ + + + + + + + +
` + : ""} + ${this.snapshot && this.snapshotType === "partial" + ? html` + ${this.snapshot.homeassistant + ? html` + + `} + > + { + this.homeAssistant = !this.homeAssistant; + }} + > + + + ` + : ""} + ` + : ""} + ${this.snapshotType === "partial" + ? html` + ${foldersSection?.templates.length + ? html` + + `} + > + + + +
${foldersSection.templates}
+ ` + : ""} + ${addonsSection?.templates.length + ? html` + + `} + > + + + +
${addonsSection.templates}
+ ` + : ""} + ` + : ""} + ${!this.snapshot + ? html` + + ` + : ""} + ${this.snapshotHasPassword + ? html` + + + ` + : ""} + `; + } + + static get styles(): CSSResultGroup { + return css` + ha-checkbox { + --mdc-checkbox-touch-target-size: 16px; + display: block; + margin: 4px 12px 8px 0; + } + ha-formfield { + display: contents; + } + supervisor-formfield-label { + display: inline-flex; + align-items: center; + } + paper-input[type="password"] { + display: block; + margin: 4px 0 4px 16px; + } + .details { + color: var(--secondary-text-color); + } + .section-content { + display: flex; + flex-direction: column; + margin-left: 16px; + } + .security { + margin-top: 16px; + } + .snapshot-types { + display: flex; + } + .sub-header { + margin-top: 8px; + } + `; + } + + public snapshotDetails(): + | HassioPartialSnapshotCreateParams + | HassioFullSnapshotCreateParams { + const data: any = {}; + + if (!this.snapshot) { + data.name = this.snapshotName || formatDate(new Date(), this.hass.locale); + } + + if (this.snapshotHasPassword) { + data.password = this.snapshotPassword; + } + + if (this.snapshotType === "full") { + return data; + } + + const addons = this.addons + ?.filter((addon) => addon.checked) + .map((addon) => addon.slug); + const folders = this.folders + ?.filter((folder) => folder.checked) + .map((folder) => folder.slug); + + if (addons?.length) { + data.addons = addons; + } + if (folders?.length) { + data.folders = folders; + } + if (this.homeAssistant) { + data.homeassistant = this.homeAssistant; + } + + return data; + } + + private _getSection(section: string) { + const templates: TemplateResult[] = []; + const addons = + section === "addons" + ? new Map( + this.supervisor!.addon.addons.map((item) => [item.slug, item]) + ) + : undefined; + let checkedItems = 0; + this[section].forEach((item) => { + templates.push(html` + `} + > + + + `); + + if (item.checked) { + checkedItems++; + } + }); + + const checked = checkedItems === this[section].length; + + return { + templates, + checked, + indeterminate: !checked && checkedItems !== 0, + }; + } + + private _handleRadioValueChanged(ev: CustomEvent) { + const input = ev.currentTarget as HaRadio; + this[input.name] = input.value; + } + + private _handleTextValueChanged(ev: PolymerChangedEvent) { + const input = ev.currentTarget as PaperInputElement; + this[input.name!] = ev.detail.value; + } + + private _toggleHasPassword(): void { + this.snapshotHasPassword = !this.snapshotHasPassword; + } + + private _toggleSection(ev): void { + const section = ev.currentTarget.section; + + this[section] = (section === "addons" ? this.addons : this.folders)!.map( + (item) => ({ + ...item, + checked: ev.currentTarget.checked, + }) + ); + } + + private _updateSectionEntry(ev): void { + const item = ev.currentTarget.item; + const section = ev.currentTarget.section; + this[section] = this[section].map((entry) => + entry.slug === item.slug + ? { + ...entry, + checked: ev.currentTarget.checked, + } + : entry + ); + } +} + +declare global { + interface HTMLElementTagNameMap { + "supervisor-snapshot-content": SupervisorSnapshotContent; + } +} diff --git a/hassio/src/dialogs/snapshot/dialog-hassio-create-snapshot.ts b/hassio/src/dialogs/snapshot/dialog-hassio-create-snapshot.ts index 4afbf0acae..2bc817fe2b 100755 --- a/hassio/src/dialogs/snapshot/dialog-hassio-create-snapshot.ts +++ b/hassio/src/dialogs/snapshot/dialog-hassio-create-snapshot.ts @@ -1,91 +1,43 @@ import "@material/mwc-button"; -import "@polymer/paper-input/paper-input"; -import type { PaperInputElement } from "@polymer/paper-input/paper-input"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import { formatDate } from "../../../../src/common/datetime/format_date"; +import { customElement, property, query, state } from "lit/decorators"; import { fireEvent } from "../../../../src/common/dom/fire_event"; -import { compare } from "../../../../src/common/string/compare"; import "../../../../src/components/buttons/ha-progress-button"; -import "../../../../src/components/ha-checkbox"; -import type { HaCheckbox } from "../../../../src/components/ha-checkbox"; import { createCloseHeading } from "../../../../src/components/ha-dialog"; -import "../../../../src/components/ha-formfield"; -import "../../../../src/components/ha-radio"; -import type { HaRadio } from "../../../../src/components/ha-radio"; -import "../../../../src/components/ha-settings-row"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; import { createHassioFullSnapshot, createHassioPartialSnapshot, - HassioFullSnapshotCreateParams, - HassioPartialSnapshotCreateParams, - HassioSnapshot, } from "../../../../src/data/hassio/snapshot"; import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box"; -import { PolymerChangedEvent } from "../../../../src/polymer-types"; import { haStyle, haStyleDialog } from "../../../../src/resources/styles"; import { HomeAssistant } from "../../../../src/types"; +import "../../components/supervisor-snapshot-content"; +import type { SupervisorSnapshotContent } from "../../components/supervisor-snapshot-content"; import { HassioCreateSnapshotDialogParams } from "./show-dialog-hassio-create-snapshot"; -interface CheckboxItem { - slug: string; - checked: boolean; - name?: string; - version?: string; -} - -const folderList = () => [ - { - slug: "homeassistant", - checked: true, - }, - { slug: "ssl", checked: true }, - { slug: "share", checked: true }, - { slug: "media", checked: true }, - { slug: "addons/local", checked: true }, -]; - @customElement("dialog-hassio-create-snapshot") class HassioCreateSnapshotDialog extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @state() private _snapshotName = ""; - - @state() private _snapshotPassword = ""; - - @state() private _snapshotHasPassword = false; - - @state() private _snapshotType: HassioSnapshot["type"] = "full"; - @state() private _dialogParams?: HassioCreateSnapshotDialogParams; - @state() private _addonList: CheckboxItem[] = []; + @state() private _error?: string; - @state() private _folderList: CheckboxItem[] = folderList(); + @state() private _creatingSnapshot = false; - @state() private _error = ""; + @query("supervisor-snapshot-content") + private _snapshotContent!: SupervisorSnapshotContent; public showDialog(params: HassioCreateSnapshotDialogParams) { this._dialogParams = params; - this._addonList = this._dialogParams.supervisor.supervisor.addons - .map((addon) => ({ - slug: addon.slug, - name: addon.name, - version: addon.version, - checked: true, - })) - .sort((a, b) => compare(a.name, b.name)); - this._snapshotType = "full"; - this._error = ""; - this._folderList = folderList(); - this._snapshotHasPassword = false; - this._snapshotPassword = ""; - this._snapshotName = ""; + this._creatingSnapshot = false; } public closeDialog() { this._dialogParams = undefined; + this._creatingSnapshot = false; + this._error = undefined; fireEvent(this, "dialog-closed", { dialog: this.localName }); } @@ -102,173 +54,29 @@ class HassioCreateSnapshotDialog extends LitElement { this._dialogParams.supervisor.localize("snapshot.create_snapshot") )} > - - -
-
- ${this._dialogParams.supervisor.localize("snapshot.type")}: -
- ` + : html` - - - - - - - -
- - ${ - this._snapshotType === "full" - ? undefined - : html` - ${this._dialogParams.supervisor.localize("snapshot.folders")}: -
- ${this._folderList.map( - (folder, idx) => html` -
- - - - ${this._dialogParams!.supervisor.localize( - `snapshot.folder.${folder.slug}` - )} - -
- ` - )} -
- - ${this._dialogParams.supervisor.localize("snapshot.addons")}: -
- ${this._addonList.map( - (addon, idx) => html` -
- - - - ${addon.name} - (${addon.version}) - - -
- ` - )} -
- ` - } - ${this._dialogParams.supervisor.localize("snapshot.security")}: -
-
- - - - ${this._dialogParams.supervisor.localize( - "snapshot.password_protection" - )} - - -
-
- - ${ - this._snapshotHasPassword - ? html` - - - ` - : undefined - } - ${ - this._error !== "" - ? html`

${this._error}

` - : undefined - } + `} + ${this._error ? html`

Error: ${this._error}

` : ""} ${this._dialogParams.supervisor.localize("common.close")} - + ${this._dialogParams.supervisor.localize("snapshot.create")} - + `; } - private _handleTextValueChanged(ev: PolymerChangedEvent) { - const input = ev.currentTarget as PaperInputElement; - this[`_${input.name}`] = ev.detail.value; - } - - private _handleCheckboxValueChanged(ev: CustomEvent) { - const input = ev.currentTarget as HaCheckbox; - this._snapshotHasPassword = input.checked; - } - - private _handleRadioValueChanged(ev: CustomEvent) { - const input = ev.currentTarget as HaRadio; - this[`_${input.name}`] = input.value; - } - - private _folderChecked(ev) { - const { idx, checked } = ev.currentTarget!; - this._folderList = this._folderList.map((folder, curIdx) => - curIdx === idx ? { ...folder, checked } : folder - ); - } - - private _addonChecked(ev) { - const { idx, checked } = ev.currentTarget!; - this._addonList = this._addonList.map((addon, curIdx) => - curIdx === idx ? { ...addon, checked } : addon - ); - } - - private async _createSnapshot(ev: CustomEvent): Promise { + private async _createSnapshot(): Promise { if (this._dialogParams!.supervisor.info.state !== "running") { showAlertDialog(this, { title: this._dialogParams!.supervisor.localize( @@ -282,40 +90,26 @@ class HassioCreateSnapshotDialog extends LitElement { }); return; } - const button = ev.currentTarget as any; - button.progress = true; + const snapshotDetails = this._snapshotContent.snapshotDetails(); + this._creatingSnapshot = true; this._error = ""; - if (this._snapshotHasPassword && !this._snapshotPassword.length) { + if ( + this._snapshotContent.snapshotHasPassword && + !this._snapshotContent.snapshotPassword.length + ) { this._error = this._dialogParams!.supervisor.localize( "snapshot.enter_password" ); - button.progress = false; + this._creatingSnapshot = false; return; } - const name = this._snapshotName || formatDate(new Date(), this.hass.locale); try { - if (this._snapshotType === "full") { - const data: HassioFullSnapshotCreateParams = { name }; - if (this._snapshotHasPassword) { - data.password = this._snapshotPassword; - } - await createHassioFullSnapshot(this.hass, data); + if (this._snapshotContent.snapshotType === "full") { + await createHassioFullSnapshot(this.hass, snapshotDetails); } else { - const data: HassioPartialSnapshotCreateParams = { - name, - folders: this._folderList - .filter((folder) => folder.checked) - .map((folder) => folder.slug), - addons: this._addonList - .filter((addon) => addon.checked) - .map((addon) => addon.slug), - }; - if (this._snapshotHasPassword) { - data.password = this._snapshotPassword; - } - await createHassioPartialSnapshot(this.hass, data); + await createHassioPartialSnapshot(this.hass, snapshotDetails); } this._dialogParams!.onCreate(); @@ -323,7 +117,7 @@ class HassioCreateSnapshotDialog extends LitElement { } catch (err) { this._error = extractApiErrorMessage(err); } - button.progress = false; + this._creatingSnapshot = false; } static get styles(): CSSResultGroup { @@ -331,22 +125,9 @@ class HassioCreateSnapshotDialog extends LitElement { haStyle, haStyleDialog, css` - .error { - color: var(--error-color); - } - paper-input[type="password"] { + ha-circular-progress { display: block; - margin: 4px 0 4px 16px; - } - span.version { - color: var(--secondary-text-color); - } - .checkbox-section { - display: grid; - } - .checkbox-line { - display: inline-flex; - align-items: center; + text-align: center; } `, ]; diff --git a/hassio/src/dialogs/snapshot/dialog-hassio-snapshot.ts b/hassio/src/dialogs/snapshot/dialog-hassio-snapshot.ts index 81f21bb908..3ffbfcd53d 100755 --- a/hassio/src/dialogs/snapshot/dialog-hassio-snapshot.ts +++ b/hassio/src/dialogs/snapshot/dialog-hassio-snapshot.ts @@ -1,13 +1,12 @@ -import "@material/mwc-button"; -import { mdiClose, mdiDelete, mdiDownload, mdiHistory } from "@mdi/js"; -import "@polymer/paper-checkbox/paper-checkbox"; -import type { PaperCheckboxElement } from "@polymer/paper-checkbox/paper-checkbox"; -import "@polymer/paper-input/paper-input"; +import { ActionDetail } from "@material/mwc-list"; +import "@material/mwc-list/mwc-list-item"; +import { mdiDotsVertical } from "@mdi/js"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; -import { customElement, property, state } from "lit/decorators"; -import { formatDateTime } from "../../../../src/common/datetime/format_date_time"; +import { customElement, property, query, state } from "lit/decorators"; import { fireEvent } from "../../../../src/common/dom/fire_event"; -import "../../../../src/components/ha-header-bar"; +import "../../../../src/components/buttons/ha-progress-button"; +import "../../../../src/components/ha-button-menu"; +import { createCloseHeading } from "../../../../src/components/ha-dialog"; import "../../../../src/components/ha-svg-icon"; import { getSignedPath } from "../../../../src/data/auth"; import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; @@ -15,106 +14,45 @@ import { fetchHassioSnapshotInfo, HassioSnapshotDetail, } from "../../../../src/data/hassio/snapshot"; -import { Supervisor } from "../../../../src/data/supervisor/supervisor"; import { showAlertDialog, showConfirmationDialog, } from "../../../../src/dialogs/generic/show-dialog-box"; import { HassDialog } from "../../../../src/dialogs/make-dialog-manager"; -import { PolymerChangedEvent } from "../../../../src/polymer-types"; import { haStyle, haStyleDialog } from "../../../../src/resources/styles"; import { HomeAssistant } from "../../../../src/types"; +import "../../components/supervisor-snapshot-content"; +import type { SupervisorSnapshotContent } from "../../components/supervisor-snapshot-content"; import { HassioSnapshotDialogParams } from "./show-dialog-hassio-snapshot"; -const _computeFolders = (folders) => { - const list: Array<{ slug: string; name: string; checked: boolean }> = []; - if (folders.includes("homeassistant")) { - list.push({ - slug: "homeassistant", - name: "Home Assistant configuration", - checked: true, - }); - } - if (folders.includes("ssl")) { - list.push({ slug: "ssl", name: "SSL", checked: true }); - } - if (folders.includes("share")) { - list.push({ slug: "share", name: "Share", checked: true }); - } - if (folders.includes("addons/local")) { - list.push({ slug: "addons/local", name: "Local add-ons", checked: true }); - } - return list; -}; - -const _computeAddons = (addons) => - addons.map((addon) => ({ - slug: addon.slug, - name: addon.name, - version: addon.version, - checked: true, - })); - -interface AddonItem { - slug: string; - name: string; - version: string; - checked: boolean | null | undefined; -} - -interface FolderItem { - slug: string; - name: string; - checked: boolean | null | undefined; -} - @customElement("dialog-hassio-snapshot") class HassioSnapshotDialog extends LitElement implements HassDialog { @property({ attribute: false }) public hass!: HomeAssistant; - @property({ attribute: false }) public supervisor?: Supervisor; - @state() private _error?: string; - @state() private _onboarding = false; - @state() private _snapshot?: HassioSnapshotDetail; - @state() private _folders!: FolderItem[]; - - @state() private _addons!: AddonItem[]; - @state() private _dialogParams?: HassioSnapshotDialogParams; - @state() private _snapshotPassword!: string; + @state() private _restoringSnapshot = false; - @state() private _restoreHass = true; + @query("supervisor-snapshot-content") + private _snapshotContent!: SupervisorSnapshotContent; public async showDialog(params: HassioSnapshotDialogParams) { this._snapshot = await fetchHassioSnapshotInfo(this.hass, params.slug); - this._folders = _computeFolders( - this._snapshot?.folders - ).sort((a: FolderItem, b: FolderItem) => (a.name > b.name ? 1 : -1)); - this._addons = _computeAddons( - this._snapshot?.addons - ).sort((a: AddonItem, b: AddonItem) => (a.name > b.name ? 1 : -1)); - this._dialogParams = params; - this._onboarding = params.onboarding ?? false; - this.supervisor = params.supervisor; - if (!this._snapshot.homeassistant) { - this._restoreHass = false; - } + this._restoringSnapshot = false; } public closeDialog() { - this._dialogParams = undefined; this._snapshot = undefined; - this._snapshotPassword = ""; - this._folders = []; - this._addons = []; + this._dialogParams = undefined; + this._restoringSnapshot = false; + this._error = undefined; fireEvent(this, "dialog-closed", { dialog: this.localName }); } @@ -123,121 +61,41 @@ class HassioSnapshotDialog return html``; } return html` - -
- - ${this._computeName} - - - - -
-
- ${this._snapshot.type === "full" - ? "Full snapshot" - : "Partial snapshot"} - (${this._computeSize})
- ${formatDateTime(new Date(this._snapshot.date), this.hass.locale)} -
- ${this._snapshot.homeassistant - ? html`
Home Assistant:
- - Home Assistant - (${this._snapshot.homeassistant}) - ` - : ""} - ${this._folders.length - ? html` -
Folders:
- - ${this._folders.map( - (item) => html` - - ${item.name} - - ` - )} - - ` - : ""} - ${this._addons.length - ? html` -
Add-on:
- - ${this._addons.map( - (item) => html` - - ${item.name} - (${item.version}) - - ` - )} - - ` - : ""} - ${this._snapshot.protected - ? html` - - ` - : ""} - ${this._error ? html`

Error: ${this._error}

` : ""} + + ${this._restoringSnapshot + ? html` ` + : html` + `} + ${this._error ? html`

Error: ${this._error}

` : ""} -
- - - Restore Selected - - ${!this._onboarding - ? html` - - - - Delete Snapshot - - ` - : ""} -
-
- ${this._snapshot.type === "full" - ? html` - - - Restore Everything - - ` - : ""} - ${!this._onboarding - ? html` - - Download Snapshot - ` - : ""} -
+ + Restore + + + ev.stopPropagation()} + > + + + + Download Snapshot + Delete Snapshot +
`; } @@ -247,83 +105,47 @@ class HassioSnapshotDialog haStyle, haStyleDialog, css` - paper-checkbox { + ha-svg-icon { + color: var(--primary-text-color); + } + ha-circular-progress { display: block; - margin: 4px; - } - mwc-button ha-svg-icon { - margin-right: 4px; - } - .button-row { - display: grid; - gap: 8px; - margin-right: 8px; - } - .details { - color: var(--secondary-text-color); - } - .warning, - .error { - color: var(--error-color); - } - .buttons li { - list-style-type: none; - } - .buttons .icon { - margin-right: 16px; - } - .no-margin-top { - margin-top: 0; - } - span.version { - color: var(--secondary-text-color); - } - ha-header-bar { - --mdc-theme-on-primary: var(--primary-text-color); - --mdc-theme-primary: var(--mdc-theme-surface); - flex-shrink: 0; - } - /* overrule the ha-style-dialog max-height on small screens */ - @media all and (max-width: 450px), all and (max-height: 500px) { - ha-header-bar { - --mdc-theme-primary: var(--app-header-background-color); - --mdc-theme-on-primary: var(--app-header-text-color, white); - } + text-align: center; } `, ]; } - private _updateFolders(item: FolderItem, value: boolean | null | undefined) { - this._folders = this._folders.map((folder) => { - if (folder.slug === item.slug) { - folder.checked = value; - } - return folder; - }); + private _handleMenuAction(ev: CustomEvent) { + switch (ev.detail.index) { + case 0: + this._downloadClicked(); + break; + case 1: + this._deleteClicked(); + break; + } } - private _updateAddons(item: AddonItem, value: boolean | null | undefined) { - this._addons = this._addons.map((addon) => { - if (addon.slug === item.slug) { - addon.checked = value; - } - return addon; - }); + private async _restoreClicked() { + const snapshotDetails = this._snapshotContent.snapshotDetails(); + this._restoringSnapshot = true; + if (this._snapshotContent.snapshotType === "full") { + await this._fullRestoreClicked(snapshotDetails); + } else { + await this._partialRestoreClicked(snapshotDetails); + } + this._restoringSnapshot = false; } - private _passwordInput(ev: PolymerChangedEvent) { - this._snapshotPassword = ev.detail.value; - } - - private async _partialRestoreClicked() { + private async _partialRestoreClicked(snapshotDetails) { if ( - this.supervisor !== undefined && - this.supervisor.info.state !== "running" + this._dialogParams?.supervisor !== undefined && + this._dialogParams?.supervisor.info.state !== "running" ) { await showAlertDialog(this, { title: "Could not restore snapshot", - text: `Restoring a snapshot is not possible right now because the system is in ${this.supervisor.info.state} state.`, + text: `Restoring a snapshot is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`, }); return; } @@ -337,40 +159,16 @@ class HassioSnapshotDialog return; } - const addons = this._addons - .filter((addon) => addon.checked) - .map((addon) => addon.slug); - - const folders = this._folders - .filter((folder) => folder.checked) - .map((folder) => folder.slug); - - const data: { - homeassistant: boolean; - addons: any; - folders: any; - password?: string; - } = { - homeassistant: this._restoreHass, - addons, - folders, - }; - - if (this._snapshot!.protected) { - data.password = this._snapshotPassword; - } - - if (!this._onboarding) { + if (!this._dialogParams?.onboarding) { this.hass .callApi( "POST", `hassio/snapshots/${this._snapshot!.slug}/restore/partial`, - data + snapshotDetails ) .then( () => { - alert("Snapshot restored!"); this.closeDialog(); }, (error) => { @@ -381,20 +179,20 @@ class HassioSnapshotDialog fireEvent(this, "restoring"); fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/partial`, { method: "POST", - body: JSON.stringify(data), + body: JSON.stringify(snapshotDetails), }); this.closeDialog(); } } - private async _fullRestoreClicked() { + private async _fullRestoreClicked(snapshotDetails) { if ( - this.supervisor !== undefined && - this.supervisor.info.state !== "running" + this._dialogParams?.supervisor !== undefined && + this._dialogParams?.supervisor.info.state !== "running" ) { await showAlertDialog(this, { title: "Could not restore snapshot", - text: `Restoring a snapshot is not possible right now because the system is in ${this.supervisor.info.state} state.`, + text: `Restoring a snapshot is not possible right now because the system is in ${this._dialogParams?.supervisor.info.state} state.`, }); return; } @@ -409,19 +207,15 @@ class HassioSnapshotDialog return; } - const data = this._snapshot!.protected - ? { password: this._snapshotPassword } - : undefined; - if (!this._onboarding) { + if (!this._dialogParams?.onboarding) { this.hass .callApi( "POST", `hassio/snapshots/${this._snapshot!.slug}/restore/full`, - data + snapshotDetails ) .then( () => { - alert("Snapshot restored!"); this.closeDialog(); }, (error) => { @@ -432,7 +226,7 @@ class HassioSnapshotDialog fireEvent(this, "restoring"); fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/full`, { method: "POST", - body: JSON.stringify(data), + body: JSON.stringify(snapshotDetails), }); this.closeDialog(); } @@ -473,7 +267,9 @@ class HassioSnapshotDialog `/api/hassio/snapshots/${this._snapshot!.slug}/download` ); } catch (err) { - alert(`Error: ${extractApiErrorMessage(err)}`); + await showAlertDialog(this, { + text: extractApiErrorMessage(err), + }); return; } @@ -504,10 +300,6 @@ class HassioSnapshotDialog ? this._snapshot.name || this._snapshot.slug : "Unnamed snapshot"; } - - private get _computeSize() { - return Math.ceil(this._snapshot!.size * 10) / 10 + " MB"; - } } declare global { diff --git a/src/components/ha-button-menu.ts b/src/components/ha-button-menu.ts index 52e27af49e..8223b68d1c 100644 --- a/src/components/ha-button-menu.ts +++ b/src/components/ha-button-menu.ts @@ -12,6 +12,8 @@ export class HaButtonMenu extends LitElement { @property({ type: Boolean }) public disabled = false; + @property({ type: Boolean }) public fixed = false; + @query("mwc-menu", true) private _menu?: Menu; public get items() { @@ -29,6 +31,7 @@ export class HaButtonMenu extends LitElement { diff --git a/src/data/hassio/snapshot.ts b/src/data/hassio/snapshot.ts index 0281f0d9a4..ba34776228 100644 --- a/src/data/hassio/snapshot.ts +++ b/src/data/hassio/snapshot.ts @@ -42,11 +42,10 @@ export interface HassioFullSnapshotCreateParams { name: string; password?: string; } -export interface HassioPartialSnapshotCreateParams { - name: string; +export interface HassioPartialSnapshotCreateParams + extends HassioFullSnapshotCreateParams { folders?: string[]; addons?: string[]; - password?: string; homeassistant?: boolean; } diff --git a/src/translations/en.json b/src/translations/en.json index f25ae07695..79f039cfe5 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3901,14 +3901,15 @@ "create_snapshot": "Create snapshot", "create": "Create", "created": "Created", - "name": "Name", - "type": "Type", + "name": "Snapshot name", + "type": "Snapshot type", + "select_type": "Select what to restore", "security": "Security", "full_snapshot": "Full snapshot", "partial_snapshot": "Partial snapshot", "addons": "Add-ons", "folders": "Folders", - "password": "Password", + "password": "Snapshot password", "password_protection": "Password protection", "password_protected": "password protected", "enter_password": "Please enter a password.",