mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Refresh snapshot create/restore dialogs (#9223)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
c32a4546f3
commit
0dd3757df2
55
hassio/src/components/supervisor-formfield-label.ts
Normal file
55
hassio/src/components/supervisor-formfield-label.ts
Normal file
@ -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`<img loading="lazy" .src=${this.imageUrl} class="icon" />`
|
||||||
|
: this.iconPath
|
||||||
|
? html`<ha-svg-icon .path=${this.iconPath} class="icon"></ha-svg-icon>`
|
||||||
|
: ""}
|
||||||
|
<span class="label">${this.label}</span>
|
||||||
|
${this.version
|
||||||
|
? html`<span class="version">(${this.version})</span>`
|
||||||
|
: ""}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
418
hassio/src/components/supervisor-snapshot-content.ts
Normal file
418
hassio/src/components/supervisor-snapshot-content.ts
Normal file
@ -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`<div class="details">
|
||||||
|
${this.snapshot.type === "full"
|
||||||
|
? this.supervisor.localize("snapshot.full_snapshot")
|
||||||
|
: this.supervisor.localize("snapshot.partial_snapshot")}
|
||||||
|
(${Math.ceil(this.snapshot.size * 10) / 10 + " MB"})<br />
|
||||||
|
${formatDateTime(new Date(this.snapshot.date), this.hass.locale)}
|
||||||
|
</div>`
|
||||||
|
: html`<paper-input
|
||||||
|
name="snapshotName"
|
||||||
|
.label=${this.supervisor.localize("snapshot.name")}
|
||||||
|
.value=${this.snapshotName}
|
||||||
|
@value-changed=${this._handleTextValueChanged}
|
||||||
|
>
|
||||||
|
</paper-input>`}
|
||||||
|
${!this.snapshot || this.snapshot.type === "full"
|
||||||
|
? html`<div class="sub-header">
|
||||||
|
${!this.snapshot
|
||||||
|
? this.supervisor.localize("snapshot.type")
|
||||||
|
: this.supervisor.localize("snapshot.select_type")}
|
||||||
|
</div>
|
||||||
|
<div class="snapshot-types">
|
||||||
|
<ha-formfield
|
||||||
|
.label=${this.supervisor.localize("snapshot.full_snapshot")}
|
||||||
|
>
|
||||||
|
<ha-radio
|
||||||
|
@change=${this._handleRadioValueChanged}
|
||||||
|
value="full"
|
||||||
|
name="snapshotType"
|
||||||
|
.checked=${this.snapshotType === "full"}
|
||||||
|
>
|
||||||
|
</ha-radio>
|
||||||
|
</ha-formfield>
|
||||||
|
<ha-formfield
|
||||||
|
.label=${this.supervisor!.localize("snapshot.partial_snapshot")}
|
||||||
|
>
|
||||||
|
<ha-radio
|
||||||
|
@change=${this._handleRadioValueChanged}
|
||||||
|
value="partial"
|
||||||
|
name="snapshotType"
|
||||||
|
.checked=${this.snapshotType === "partial"}
|
||||||
|
>
|
||||||
|
</ha-radio>
|
||||||
|
</ha-formfield>
|
||||||
|
</div>`
|
||||||
|
: ""}
|
||||||
|
${this.snapshot && this.snapshotType === "partial"
|
||||||
|
? html`
|
||||||
|
${this.snapshot.homeassistant
|
||||||
|
? html`
|
||||||
|
<ha-formfield
|
||||||
|
.label=${html`<supervisor-formfield-label
|
||||||
|
label="Home Assistant"
|
||||||
|
.iconPath=${mdiHomeAssistant}
|
||||||
|
.version=${this.snapshot.homeassistant}
|
||||||
|
>
|
||||||
|
</supervisor-formfield-label>`}
|
||||||
|
>
|
||||||
|
<ha-checkbox
|
||||||
|
.checked=${this.homeAssistant}
|
||||||
|
@click=${() => {
|
||||||
|
this.homeAssistant = !this.homeAssistant;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
</ha-checkbox>
|
||||||
|
</ha-formfield>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${this.snapshotType === "partial"
|
||||||
|
? html`
|
||||||
|
${foldersSection?.templates.length
|
||||||
|
? html`
|
||||||
|
<ha-formfield
|
||||||
|
.label=${html`<supervisor-formfield-label
|
||||||
|
.label=${this.supervisor.localize("snapshot.folders")}
|
||||||
|
.iconPath=${mdiFolder}
|
||||||
|
>
|
||||||
|
</supervisor-formfield-label>`}
|
||||||
|
>
|
||||||
|
<ha-checkbox
|
||||||
|
@change=${this._toggleSection}
|
||||||
|
.checked=${foldersSection.checked}
|
||||||
|
.indeterminate=${foldersSection.indeterminate}
|
||||||
|
.section=${"folders"}
|
||||||
|
>
|
||||||
|
</ha-checkbox>
|
||||||
|
</ha-formfield>
|
||||||
|
<div class="section-content">${foldersSection.templates}</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${addonsSection?.templates.length
|
||||||
|
? html`
|
||||||
|
<ha-formfield
|
||||||
|
.label=${html`<supervisor-formfield-label
|
||||||
|
.label=${this.supervisor.localize("snapshot.addons")}
|
||||||
|
.iconPath=${mdiPuzzle}
|
||||||
|
>
|
||||||
|
</supervisor-formfield-label>`}
|
||||||
|
>
|
||||||
|
<ha-checkbox
|
||||||
|
@change=${this._toggleSection}
|
||||||
|
.checked=${addonsSection.checked}
|
||||||
|
.indeterminate=${addonsSection.indeterminate}
|
||||||
|
.section=${"addons"}
|
||||||
|
>
|
||||||
|
</ha-checkbox>
|
||||||
|
</ha-formfield>
|
||||||
|
<div class="section-content">${addonsSection.templates}</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${!this.snapshot
|
||||||
|
? html`<ha-formfield
|
||||||
|
.label=${this.supervisor.localize("snapshot.password_protection")}
|
||||||
|
>
|
||||||
|
<ha-checkbox
|
||||||
|
.checked=${this.snapshotHasPassword}
|
||||||
|
@change=${this._toggleHasPassword}
|
||||||
|
>
|
||||||
|
</ha-checkbox
|
||||||
|
></ha-formfield>`
|
||||||
|
: ""}
|
||||||
|
${this.snapshotHasPassword
|
||||||
|
? html`
|
||||||
|
<paper-input
|
||||||
|
.label=${this.supervisor.localize("snapshot.password")}
|
||||||
|
type="password"
|
||||||
|
name="snapshotPassword"
|
||||||
|
.value=${this.snapshotPassword}
|
||||||
|
@value-changed=${this._handleTextValueChanged}
|
||||||
|
>
|
||||||
|
</paper-input>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
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`<ha-formfield
|
||||||
|
.label=${html`<supervisor-formfield-label
|
||||||
|
.label=${item.name}
|
||||||
|
.iconPath=${section === "addons" ? mdiPuzzle : mdiFolder}
|
||||||
|
.imageUrl=${section === "addons" &&
|
||||||
|
atLeastVersion(this.hass.config.version, 0, 105) &&
|
||||||
|
addons?.get(item.slug)?.icon
|
||||||
|
? `/api/hassio/addons/${item.slug}/icon`
|
||||||
|
: undefined}
|
||||||
|
.version=${item.version}
|
||||||
|
>
|
||||||
|
</supervisor-formfield-label>`}
|
||||||
|
>
|
||||||
|
<ha-checkbox
|
||||||
|
.item=${item}
|
||||||
|
.checked=${item.checked}
|
||||||
|
.section=${section}
|
||||||
|
@change=${this._updateSectionEntry}
|
||||||
|
>
|
||||||
|
</ha-checkbox>
|
||||||
|
</ha-formfield>`);
|
||||||
|
|
||||||
|
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<string>) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,91 +1,43 @@
|
|||||||
import "@material/mwc-button";
|
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 { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { formatDate } from "../../../../src/common/datetime/format_date";
|
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
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/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 { 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 { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
import {
|
import {
|
||||||
createHassioFullSnapshot,
|
createHassioFullSnapshot,
|
||||||
createHassioPartialSnapshot,
|
createHassioPartialSnapshot,
|
||||||
HassioFullSnapshotCreateParams,
|
|
||||||
HassioPartialSnapshotCreateParams,
|
|
||||||
HassioSnapshot,
|
|
||||||
} from "../../../../src/data/hassio/snapshot";
|
} from "../../../../src/data/hassio/snapshot";
|
||||||
import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../../../src/dialogs/generic/show-dialog-box";
|
||||||
import { PolymerChangedEvent } from "../../../../src/polymer-types";
|
|
||||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
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";
|
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")
|
@customElement("dialog-hassio-create-snapshot")
|
||||||
class HassioCreateSnapshotDialog extends LitElement {
|
class HassioCreateSnapshotDialog extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@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 _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) {
|
public showDialog(params: HassioCreateSnapshotDialogParams) {
|
||||||
this._dialogParams = params;
|
this._dialogParams = params;
|
||||||
this._addonList = this._dialogParams.supervisor.supervisor.addons
|
this._creatingSnapshot = false;
|
||||||
.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 = "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public closeDialog() {
|
public closeDialog() {
|
||||||
this._dialogParams = undefined;
|
this._dialogParams = undefined;
|
||||||
|
this._creatingSnapshot = false;
|
||||||
|
this._error = undefined;
|
||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
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.create_snapshot")
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<paper-input
|
${this._creatingSnapshot
|
||||||
name="snapshotName"
|
? html` <ha-circular-progress active></ha-circular-progress>`
|
||||||
.label=${this._dialogParams.supervisor.localize("snapshot.name")}
|
: html`<supervisor-snapshot-content
|
||||||
.value=${this._snapshotName}
|
.hass=${this.hass}
|
||||||
@value-changed=${this._handleTextValueChanged}
|
.supervisor=${this._dialogParams.supervisor}
|
||||||
>
|
|
||||||
</paper-input>
|
|
||||||
<div class="snapshot-types">
|
|
||||||
<div>
|
|
||||||
${this._dialogParams.supervisor.localize("snapshot.type")}:
|
|
||||||
</div>
|
|
||||||
<ha-formfield
|
|
||||||
.label=${this._dialogParams.supervisor.localize(
|
|
||||||
"snapshot.full_snapshot"
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<ha-radio
|
</supervisor-snapshot-content>`}
|
||||||
@change=${this._handleRadioValueChanged}
|
${this._error ? html`<p class="error">Error: ${this._error}</p>` : ""}
|
||||||
value="full"
|
|
||||||
name="snapshotType"
|
|
||||||
.checked=${this._snapshotType === "full"}
|
|
||||||
>
|
|
||||||
</ha-radio>
|
|
||||||
</ha-formfield>
|
|
||||||
<ha-formfield
|
|
||||||
.label=${this._dialogParams.supervisor.localize(
|
|
||||||
"snapshot.partial_snapshot"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<ha-radio
|
|
||||||
@change=${this._handleRadioValueChanged}
|
|
||||||
value="partial"
|
|
||||||
name="snapshotType"
|
|
||||||
.checked=${this._snapshotType === "partial"}
|
|
||||||
>
|
|
||||||
</ha-radio>
|
|
||||||
</ha-formfield>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
${
|
|
||||||
this._snapshotType === "full"
|
|
||||||
? undefined
|
|
||||||
: html`
|
|
||||||
${this._dialogParams.supervisor.localize("snapshot.folders")}:
|
|
||||||
<div class="checkbox-section">
|
|
||||||
${this._folderList.map(
|
|
||||||
(folder, idx) => html`
|
|
||||||
<div class="checkbox-line">
|
|
||||||
<ha-checkbox
|
|
||||||
.idx=${idx}
|
|
||||||
.checked=${folder.checked}
|
|
||||||
@change=${this._folderChecked}
|
|
||||||
slot="prefix"
|
|
||||||
>
|
|
||||||
</ha-checkbox>
|
|
||||||
<span>
|
|
||||||
${this._dialogParams!.supervisor.localize(
|
|
||||||
`snapshot.folder.${folder.slug}`
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
${this._dialogParams.supervisor.localize("snapshot.addons")}:
|
|
||||||
<div class="checkbox-section">
|
|
||||||
${this._addonList.map(
|
|
||||||
(addon, idx) => html`
|
|
||||||
<div class="checkbox-line">
|
|
||||||
<ha-checkbox
|
|
||||||
.idx=${idx}
|
|
||||||
.checked=${addon.checked}
|
|
||||||
@change=${this._addonChecked}
|
|
||||||
slot="prefix"
|
|
||||||
>
|
|
||||||
</ha-checkbox>
|
|
||||||
<span>
|
|
||||||
${addon.name}<span class="version">
|
|
||||||
(${addon.version})
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
${this._dialogParams.supervisor.localize("snapshot.security")}:
|
|
||||||
<div class="checkbox-section">
|
|
||||||
<div class="checkbox-line">
|
|
||||||
<ha-checkbox
|
|
||||||
.checked=${this._snapshotHasPassword}
|
|
||||||
@change=${this._handleCheckboxValueChanged}
|
|
||||||
slot="prefix"
|
|
||||||
>
|
|
||||||
</ha-checkbox>
|
|
||||||
<span>
|
|
||||||
${this._dialogParams.supervisor.localize(
|
|
||||||
"snapshot.password_protection"
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
${
|
|
||||||
this._snapshotHasPassword
|
|
||||||
? html`
|
|
||||||
<paper-input
|
|
||||||
.label=${this._dialogParams.supervisor.localize(
|
|
||||||
"snapshot.password"
|
|
||||||
)}
|
|
||||||
type="password"
|
|
||||||
name="snapshotPassword"
|
|
||||||
.value=${this._snapshotPassword}
|
|
||||||
@value-changed=${this._handleTextValueChanged}
|
|
||||||
>
|
|
||||||
</paper-input>
|
|
||||||
`
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
${
|
|
||||||
this._error !== ""
|
|
||||||
? html` <p class="error">${this._error}</p> `
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
|
<mwc-button slot="secondaryAction" @click=${this.closeDialog}>
|
||||||
${this._dialogParams.supervisor.localize("common.close")}
|
${this._dialogParams.supervisor.localize("common.close")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
<ha-progress-button slot="primaryAction" @click=${this._createSnapshot}>
|
<mwc-button
|
||||||
|
.disabled=${this._creatingSnapshot}
|
||||||
|
slot="primaryAction"
|
||||||
|
@click=${this._createSnapshot}
|
||||||
|
>
|
||||||
${this._dialogParams.supervisor.localize("snapshot.create")}
|
${this._dialogParams.supervisor.localize("snapshot.create")}
|
||||||
</ha-progress-button>
|
</mwc-button>
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleTextValueChanged(ev: PolymerChangedEvent<string>) {
|
private async _createSnapshot(): Promise<void> {
|
||||||
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<void> {
|
|
||||||
if (this._dialogParams!.supervisor.info.state !== "running") {
|
if (this._dialogParams!.supervisor.info.state !== "running") {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: this._dialogParams!.supervisor.localize(
|
title: this._dialogParams!.supervisor.localize(
|
||||||
@ -282,40 +90,26 @@ class HassioCreateSnapshotDialog extends LitElement {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const button = ev.currentTarget as any;
|
const snapshotDetails = this._snapshotContent.snapshotDetails();
|
||||||
button.progress = true;
|
this._creatingSnapshot = true;
|
||||||
|
|
||||||
this._error = "";
|
this._error = "";
|
||||||
if (this._snapshotHasPassword && !this._snapshotPassword.length) {
|
if (
|
||||||
|
this._snapshotContent.snapshotHasPassword &&
|
||||||
|
!this._snapshotContent.snapshotPassword.length
|
||||||
|
) {
|
||||||
this._error = this._dialogParams!.supervisor.localize(
|
this._error = this._dialogParams!.supervisor.localize(
|
||||||
"snapshot.enter_password"
|
"snapshot.enter_password"
|
||||||
);
|
);
|
||||||
button.progress = false;
|
this._creatingSnapshot = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const name = this._snapshotName || formatDate(new Date(), this.hass.locale);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (this._snapshotType === "full") {
|
if (this._snapshotContent.snapshotType === "full") {
|
||||||
const data: HassioFullSnapshotCreateParams = { name };
|
await createHassioFullSnapshot(this.hass, snapshotDetails);
|
||||||
if (this._snapshotHasPassword) {
|
|
||||||
data.password = this._snapshotPassword;
|
|
||||||
}
|
|
||||||
await createHassioFullSnapshot(this.hass, data);
|
|
||||||
} else {
|
} else {
|
||||||
const data: HassioPartialSnapshotCreateParams = {
|
await createHassioPartialSnapshot(this.hass, snapshotDetails);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._dialogParams!.onCreate();
|
this._dialogParams!.onCreate();
|
||||||
@ -323,7 +117,7 @@ class HassioCreateSnapshotDialog extends LitElement {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._error = extractApiErrorMessage(err);
|
this._error = extractApiErrorMessage(err);
|
||||||
}
|
}
|
||||||
button.progress = false;
|
this._creatingSnapshot = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
@ -331,22 +125,9 @@ class HassioCreateSnapshotDialog extends LitElement {
|
|||||||
haStyle,
|
haStyle,
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
.error {
|
ha-circular-progress {
|
||||||
color: var(--error-color);
|
|
||||||
}
|
|
||||||
paper-input[type="password"] {
|
|
||||||
display: block;
|
display: block;
|
||||||
margin: 4px 0 4px 16px;
|
text-align: center;
|
||||||
}
|
|
||||||
span.version {
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
.checkbox-section {
|
|
||||||
display: grid;
|
|
||||||
}
|
|
||||||
.checkbox-line {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import "@material/mwc-button";
|
import { ActionDetail } from "@material/mwc-list";
|
||||||
import { mdiClose, mdiDelete, mdiDownload, mdiHistory } from "@mdi/js";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import "@polymer/paper-checkbox/paper-checkbox";
|
import { mdiDotsVertical } from "@mdi/js";
|
||||||
import type { PaperCheckboxElement } from "@polymer/paper-checkbox/paper-checkbox";
|
|
||||||
import "@polymer/paper-input/paper-input";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { formatDateTime } from "../../../../src/common/datetime/format_date_time";
|
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
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 "../../../../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";
|
||||||
@ -15,106 +14,45 @@ import {
|
|||||||
fetchHassioSnapshotInfo,
|
fetchHassioSnapshotInfo,
|
||||||
HassioSnapshotDetail,
|
HassioSnapshotDetail,
|
||||||
} from "../../../../src/data/hassio/snapshot";
|
} from "../../../../src/data/hassio/snapshot";
|
||||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
|
||||||
import {
|
import {
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
} from "../../../../src/dialogs/generic/show-dialog-box";
|
} from "../../../../src/dialogs/generic/show-dialog-box";
|
||||||
import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
|
import { HassDialog } from "../../../../src/dialogs/make-dialog-manager";
|
||||||
import { PolymerChangedEvent } from "../../../../src/polymer-types";
|
|
||||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
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";
|
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")
|
@customElement("dialog-hassio-snapshot")
|
||||||
class HassioSnapshotDialog
|
class HassioSnapshotDialog
|
||||||
extends LitElement
|
extends LitElement
|
||||||
implements HassDialog<HassioSnapshotDialogParams> {
|
implements HassDialog<HassioSnapshotDialogParams> {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public supervisor?: Supervisor;
|
|
||||||
|
|
||||||
@state() private _error?: string;
|
@state() private _error?: string;
|
||||||
|
|
||||||
@state() private _onboarding = false;
|
|
||||||
|
|
||||||
@state() private _snapshot?: HassioSnapshotDetail;
|
@state() private _snapshot?: HassioSnapshotDetail;
|
||||||
|
|
||||||
@state() private _folders!: FolderItem[];
|
|
||||||
|
|
||||||
@state() private _addons!: AddonItem[];
|
|
||||||
|
|
||||||
@state() private _dialogParams?: HassioSnapshotDialogParams;
|
@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) {
|
public async showDialog(params: HassioSnapshotDialogParams) {
|
||||||
this._snapshot = await fetchHassioSnapshotInfo(this.hass, params.slug);
|
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._dialogParams = params;
|
||||||
this._onboarding = params.onboarding ?? false;
|
this._restoringSnapshot = false;
|
||||||
this.supervisor = params.supervisor;
|
|
||||||
if (!this._snapshot.homeassistant) {
|
|
||||||
this._restoreHass = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public closeDialog() {
|
public closeDialog() {
|
||||||
this._dialogParams = undefined;
|
|
||||||
this._snapshot = undefined;
|
this._snapshot = undefined;
|
||||||
this._snapshotPassword = "";
|
this._dialogParams = undefined;
|
||||||
this._folders = [];
|
this._restoringSnapshot = false;
|
||||||
this._addons = [];
|
this._error = undefined;
|
||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,121 +61,41 @@ class HassioSnapshotDialog
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<ha-dialog open @closing=${this.closeDialog} .heading=${true}>
|
<ha-dialog
|
||||||
<div slot="heading">
|
open
|
||||||
<ha-header-bar>
|
@closing=${this.closeDialog}
|
||||||
<span slot="title"> ${this._computeName} </span>
|
.heading=${createCloseHeading(this.hass, this._computeName)}
|
||||||
<mwc-icon-button slot="actionItems" dialogAction="cancel">
|
>
|
||||||
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
${this._restoringSnapshot
|
||||||
</mwc-icon-button>
|
? html` <ha-circular-progress active></ha-circular-progress>`
|
||||||
</ha-header-bar>
|
: html`<supervisor-snapshot-content
|
||||||
</div>
|
.hass=${this.hass}
|
||||||
<div class="details">
|
.supervisor=${this._dialogParams.supervisor}
|
||||||
${this._snapshot.type === "full"
|
.snapshot=${this._snapshot}
|
||||||
? "Full snapshot"
|
>
|
||||||
: "Partial snapshot"}
|
</supervisor-snapshot-content>`}
|
||||||
(${this._computeSize})<br />
|
${this._error ? html`<p class="error">Error: ${this._error}</p>` : ""}
|
||||||
${formatDateTime(new Date(this._snapshot.date), this.hass.locale)}
|
|
||||||
</div>
|
|
||||||
${this._snapshot.homeassistant
|
|
||||||
? html`<div>Home Assistant:</div>
|
|
||||||
<paper-checkbox
|
|
||||||
.checked=${this._restoreHass}
|
|
||||||
@change="${(ev: Event) => {
|
|
||||||
this._restoreHass = (ev.target as PaperCheckboxElement).checked!;
|
|
||||||
}}"
|
|
||||||
>
|
|
||||||
Home Assistant
|
|
||||||
<span class="version">(${this._snapshot.homeassistant})</span>
|
|
||||||
</paper-checkbox>`
|
|
||||||
: ""}
|
|
||||||
${this._folders.length
|
|
||||||
? html`
|
|
||||||
<div>Folders:</div>
|
|
||||||
<paper-dialog-scrollable class="no-margin-top">
|
|
||||||
${this._folders.map(
|
|
||||||
(item) => html`
|
|
||||||
<paper-checkbox
|
|
||||||
.checked=${item.checked}
|
|
||||||
@change="${(ev: Event) =>
|
|
||||||
this._updateFolders(
|
|
||||||
item,
|
|
||||||
(ev.target as PaperCheckboxElement).checked
|
|
||||||
)}"
|
|
||||||
>
|
|
||||||
${item.name}
|
|
||||||
</paper-checkbox>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</paper-dialog-scrollable>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this._addons.length
|
|
||||||
? html`
|
|
||||||
<div>Add-on:</div>
|
|
||||||
<paper-dialog-scrollable class="no-margin-top">
|
|
||||||
${this._addons.map(
|
|
||||||
(item) => html`
|
|
||||||
<paper-checkbox
|
|
||||||
.checked=${item.checked}
|
|
||||||
@change="${(ev: Event) =>
|
|
||||||
this._updateAddons(
|
|
||||||
item,
|
|
||||||
(ev.target as PaperCheckboxElement).checked
|
|
||||||
)}"
|
|
||||||
>
|
|
||||||
${item.name}
|
|
||||||
<span class="version">(${item.version})</span>
|
|
||||||
</paper-checkbox>
|
|
||||||
`
|
|
||||||
)}
|
|
||||||
</paper-dialog-scrollable>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this._snapshot.protected
|
|
||||||
? html`
|
|
||||||
<paper-input
|
|
||||||
autofocus=""
|
|
||||||
label="Password"
|
|
||||||
type="password"
|
|
||||||
@value-changed=${this._passwordInput}
|
|
||||||
.value=${this._snapshotPassword}
|
|
||||||
></paper-input>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this._error ? html` <p class="error">Error: ${this._error}</p> ` : ""}
|
|
||||||
|
|
||||||
<div class="button-row" slot="primaryAction">
|
<mwc-button
|
||||||
<mwc-button @click=${this._partialRestoreClicked}>
|
.disabled=${this._restoringSnapshot}
|
||||||
<ha-svg-icon .path=${mdiHistory} class="icon"></ha-svg-icon>
|
slot="secondaryAction"
|
||||||
Restore Selected
|
@click=${this._restoreClicked}
|
||||||
</mwc-button>
|
>
|
||||||
${!this._onboarding
|
Restore
|
||||||
? html`
|
</mwc-button>
|
||||||
<mwc-button @click=${this._deleteClicked}>
|
|
||||||
<ha-svg-icon .path=${mdiDelete} class="icon warning">
|
<ha-button-menu
|
||||||
</ha-svg-icon>
|
fixed
|
||||||
<span class="warning">Delete Snapshot</span>
|
slot="primaryAction"
|
||||||
</mwc-button>
|
@action=${this._handleMenuAction}
|
||||||
`
|
@closing=${(ev: Event) => ev.stopPropagation()}
|
||||||
: ""}
|
>
|
||||||
</div>
|
<mwc-icon-button slot="trigger" alt="menu">
|
||||||
<div class="button-row" slot="secondaryAction">
|
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||||
${this._snapshot.type === "full"
|
</mwc-icon-button>
|
||||||
? html`
|
<mwc-list-item>Download Snapshot</mwc-list-item>
|
||||||
<mwc-button @click=${this._fullRestoreClicked}>
|
<mwc-list-item class="error">Delete Snapshot</mwc-list-item>
|
||||||
<ha-svg-icon .path=${mdiHistory} class="icon"></ha-svg-icon>
|
</ha-button-menu>
|
||||||
Restore Everything
|
|
||||||
</mwc-button>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${!this._onboarding
|
|
||||||
? html`<mwc-button @click=${this._downloadClicked}>
|
|
||||||
<ha-svg-icon .path=${mdiDownload} class="icon"></ha-svg-icon>
|
|
||||||
Download Snapshot
|
|
||||||
</mwc-button>`
|
|
||||||
: ""}
|
|
||||||
</div>
|
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -247,83 +105,47 @@ class HassioSnapshotDialog
|
|||||||
haStyle,
|
haStyle,
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
paper-checkbox {
|
ha-svg-icon {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
ha-circular-progress {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 4px;
|
text-align: center;
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private _updateFolders(item: FolderItem, value: boolean | null | undefined) {
|
private _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||||
this._folders = this._folders.map((folder) => {
|
switch (ev.detail.index) {
|
||||||
if (folder.slug === item.slug) {
|
case 0:
|
||||||
folder.checked = value;
|
this._downloadClicked();
|
||||||
}
|
break;
|
||||||
return folder;
|
case 1:
|
||||||
});
|
this._deleteClicked();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _updateAddons(item: AddonItem, value: boolean | null | undefined) {
|
private async _restoreClicked() {
|
||||||
this._addons = this._addons.map((addon) => {
|
const snapshotDetails = this._snapshotContent.snapshotDetails();
|
||||||
if (addon.slug === item.slug) {
|
this._restoringSnapshot = true;
|
||||||
addon.checked = value;
|
if (this._snapshotContent.snapshotType === "full") {
|
||||||
}
|
await this._fullRestoreClicked(snapshotDetails);
|
||||||
return addon;
|
} else {
|
||||||
});
|
await this._partialRestoreClicked(snapshotDetails);
|
||||||
|
}
|
||||||
|
this._restoringSnapshot = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _passwordInput(ev: PolymerChangedEvent<string>) {
|
private async _partialRestoreClicked(snapshotDetails) {
|
||||||
this._snapshotPassword = ev.detail.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _partialRestoreClicked() {
|
|
||||||
if (
|
if (
|
||||||
this.supervisor !== undefined &&
|
this._dialogParams?.supervisor !== undefined &&
|
||||||
this.supervisor.info.state !== "running"
|
this._dialogParams?.supervisor.info.state !== "running"
|
||||||
) {
|
) {
|
||||||
await showAlertDialog(this, {
|
await showAlertDialog(this, {
|
||||||
title: "Could not restore snapshot",
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@ -337,40 +159,16 @@ class HassioSnapshotDialog
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const addons = this._addons
|
if (!this._dialogParams?.onboarding) {
|
||||||
.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) {
|
|
||||||
this.hass
|
this.hass
|
||||||
.callApi(
|
.callApi(
|
||||||
"POST",
|
"POST",
|
||||||
|
|
||||||
`hassio/snapshots/${this._snapshot!.slug}/restore/partial`,
|
`hassio/snapshots/${this._snapshot!.slug}/restore/partial`,
|
||||||
data
|
snapshotDetails
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
() => {
|
() => {
|
||||||
alert("Snapshot restored!");
|
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
@ -381,20 +179,20 @@ class HassioSnapshotDialog
|
|||||||
fireEvent(this, "restoring");
|
fireEvent(this, "restoring");
|
||||||
fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/partial`, {
|
fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/partial`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(snapshotDetails),
|
||||||
});
|
});
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _fullRestoreClicked() {
|
private async _fullRestoreClicked(snapshotDetails) {
|
||||||
if (
|
if (
|
||||||
this.supervisor !== undefined &&
|
this._dialogParams?.supervisor !== undefined &&
|
||||||
this.supervisor.info.state !== "running"
|
this._dialogParams?.supervisor.info.state !== "running"
|
||||||
) {
|
) {
|
||||||
await showAlertDialog(this, {
|
await showAlertDialog(this, {
|
||||||
title: "Could not restore snapshot",
|
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;
|
return;
|
||||||
}
|
}
|
||||||
@ -409,19 +207,15 @@ class HassioSnapshotDialog
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = this._snapshot!.protected
|
if (!this._dialogParams?.onboarding) {
|
||||||
? { password: this._snapshotPassword }
|
|
||||||
: undefined;
|
|
||||||
if (!this._onboarding) {
|
|
||||||
this.hass
|
this.hass
|
||||||
.callApi(
|
.callApi(
|
||||||
"POST",
|
"POST",
|
||||||
`hassio/snapshots/${this._snapshot!.slug}/restore/full`,
|
`hassio/snapshots/${this._snapshot!.slug}/restore/full`,
|
||||||
data
|
snapshotDetails
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
() => {
|
() => {
|
||||||
alert("Snapshot restored!");
|
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
@ -432,7 +226,7 @@ class HassioSnapshotDialog
|
|||||||
fireEvent(this, "restoring");
|
fireEvent(this, "restoring");
|
||||||
fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/full`, {
|
fetch(`/api/hassio/snapshots/${this._snapshot!.slug}/restore/full`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(snapshotDetails),
|
||||||
});
|
});
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
@ -473,7 +267,9 @@ class HassioSnapshotDialog
|
|||||||
`/api/hassio/snapshots/${this._snapshot!.slug}/download`
|
`/api/hassio/snapshots/${this._snapshot!.slug}/download`
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
alert(`Error: ${extractApiErrorMessage(err)}`);
|
await showAlertDialog(this, {
|
||||||
|
text: extractApiErrorMessage(err),
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,10 +300,6 @@ class HassioSnapshotDialog
|
|||||||
? this._snapshot.name || this._snapshot.slug
|
? this._snapshot.name || this._snapshot.slug
|
||||||
: "Unnamed snapshot";
|
: "Unnamed snapshot";
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _computeSize() {
|
|
||||||
return Math.ceil(this._snapshot!.size * 10) / 10 + " MB";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -12,6 +12,8 @@ export class HaButtonMenu extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean }) public fixed = false;
|
||||||
|
|
||||||
@query("mwc-menu", true) private _menu?: Menu;
|
@query("mwc-menu", true) private _menu?: Menu;
|
||||||
|
|
||||||
public get items() {
|
public get items() {
|
||||||
@ -29,6 +31,7 @@ export class HaButtonMenu extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
<mwc-menu
|
<mwc-menu
|
||||||
.corner=${this.corner}
|
.corner=${this.corner}
|
||||||
|
.fixed=${this.fixed}
|
||||||
.multi=${this.multi}
|
.multi=${this.multi}
|
||||||
.activatable=${this.activatable}
|
.activatable=${this.activatable}
|
||||||
>
|
>
|
||||||
|
@ -42,11 +42,10 @@ export interface HassioFullSnapshotCreateParams {
|
|||||||
name: string;
|
name: string;
|
||||||
password?: string;
|
password?: string;
|
||||||
}
|
}
|
||||||
export interface HassioPartialSnapshotCreateParams {
|
export interface HassioPartialSnapshotCreateParams
|
||||||
name: string;
|
extends HassioFullSnapshotCreateParams {
|
||||||
folders?: string[];
|
folders?: string[];
|
||||||
addons?: string[];
|
addons?: string[];
|
||||||
password?: string;
|
|
||||||
homeassistant?: boolean;
|
homeassistant?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3901,14 +3901,15 @@
|
|||||||
"create_snapshot": "Create snapshot",
|
"create_snapshot": "Create snapshot",
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
"created": "Created",
|
"created": "Created",
|
||||||
"name": "Name",
|
"name": "Snapshot name",
|
||||||
"type": "Type",
|
"type": "Snapshot type",
|
||||||
|
"select_type": "Select what to restore",
|
||||||
"security": "Security",
|
"security": "Security",
|
||||||
"full_snapshot": "Full snapshot",
|
"full_snapshot": "Full snapshot",
|
||||||
"partial_snapshot": "Partial snapshot",
|
"partial_snapshot": "Partial snapshot",
|
||||||
"addons": "Add-ons",
|
"addons": "Add-ons",
|
||||||
"folders": "Folders",
|
"folders": "Folders",
|
||||||
"password": "Password",
|
"password": "Snapshot password",
|
||||||
"password_protection": "Password protection",
|
"password_protection": "Password protection",
|
||||||
"password_protected": "password protected",
|
"password_protected": "password protected",
|
||||||
"enter_password": "Please enter a password.",
|
"enter_password": "Please enter a password.",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user