Add new backup dialog to choose between automatic and manual (#22895)

Co-authored-by: Simon Lamon <32477463+silamon@users.noreply.github.com>
This commit is contained in:
Paul Bottein 2024-11-20 12:32:25 +01:00 committed by GitHub
parent 258a19028b
commit 918fca4d0a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 209 additions and 7 deletions

View File

@ -76,6 +76,9 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
}
private _dialogClosed() {
if (this._params!.cancel) {
this._params!.cancel();
}
this._step = undefined;
this._formData = undefined;
this._agents = [];
@ -307,7 +310,7 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
// TODO: Fetch all addons
const ALL_ADDONS = [];
const { slug } = await generateBackup(this.hass, {
const { backup_id } = await generateBackup(this.hass, {
name,
agent_ids:
agents_mode === "all"
@ -318,7 +321,7 @@ class DialogGenerateBackup extends LitElement implements HassDialog {
addons_included: addons_mode === "all" ? ALL_ADDONS : addons,
});
this._params!.submit?.({ slug });
this._params!.submit?.({ backup_id });
this.closeDialog();
}

View File

@ -0,0 +1,147 @@
import { mdiBackupRestore, mdiClose, mdiCogs } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-dialog-header";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-icon-next";
import "../../../../components/ha-md-dialog";
import "../../../../components/ha-md-list";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-svg-icon";
import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
import { haStyle, haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import type { NewBackupDialogParams } from "./show-dialog-new-backup";
@customElement("ha-dialog-new-backup")
class DialogNewBackup extends LitElement implements HassDialog {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _opened = false;
@state() private _params?: NewBackupDialogParams;
public showDialog(params: NewBackupDialogParams): void {
this._opened = true;
this._params = params;
}
public closeDialog(): void {
if (this._params!.cancel) {
this._params!.cancel();
}
if (this._opened) {
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
this._opened = false;
this._params = undefined;
}
protected render() {
if (!this._opened || !this._params) {
return nothing;
}
const heading = "New backup";
return html`
<ha-md-dialog
open
@closed=${this.closeDialog}
aria-labelledby="dialog-box-title"
aria-describedby="dialog-box-description"
>
<ha-dialog-header slot="headline">
<ha-icon-button
slot="navigationIcon"
@click=${this.closeDialog}
.label=${this.hass.localize("ui.common.close")}
.path=${mdiClose}
></ha-icon-button>
<span slot="title" id="dialog-light-color-favorite-title">
${heading}
</span>
</ha-dialog-header>
<div slot="content">
<ha-md-list
innerRole="listbox"
itemRoles="option"
innerAriaLabel=${heading}
rootTabbable
dialogInitialFocus
>
<ha-md-list-item @click=${this._automatic} type="button">
<ha-svg-icon slot="start" .path=${mdiBackupRestore}></ha-svg-icon>
<span slot="headline">Use automatic backup settings</span>
<span slot="supporting-text">
Trigger a backup using the configured settings for automatic backups
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
<ha-md-list-item @click=${this._manual} type="button">
<ha-svg-icon slot="start" .path=${mdiCogs}></ha-svg-icon>
<span slot="headline"> Create a manual backup</span>
<span slot="supporting-text">
Create a backup with custom settings (e.g. specific add-ons,
database, etc.)
</span>
<ha-icon-next slot="end"></ha-icon-next>
</ha-md-list-item>
</ha-md-list>
</div>
</ha-md-dialog>
`;
}
private async _manual() {
this._params!.submit?.("manual");
this.closeDialog();
}
private async _automatic() {
this._params!.submit?.("automatic");
this.closeDialog();
}
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
css`
ha-md-dialog {
--dialog-content-padding: 0;
max-width: 500px;
}
div[slot="content"] {
margin-top: -16px;
}
@media all and (max-width: 450px), all and (max-height: 500px) {
ha-md-dialog {
max-width: none;
}
div[slot="content"] {
margin-top: 0;
}
}
ha-md-list {
background: none;
}
ha-md-list-item {
}
ha-icon-next {
width: 24px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-dialog-new-backup": DialogNewBackup;
}
}

View File

@ -1,7 +1,7 @@
import { fireEvent } from "../../../../common/dom/fire_event";
export interface GenerateBackupDialogParams {
submit?: (response: { slug: string }) => void;
submit?: (response: { backup_id: string }) => void;
cancel?: () => void;
}
@ -12,7 +12,7 @@ export const showGenerateBackupDialog = (
element: HTMLElement,
params: GenerateBackupDialogParams
) =>
new Promise<{ slug: string } | null>((resolve) => {
new Promise<{ backup_id: string } | null>((resolve) => {
const origCancel = params.cancel;
const origSubmit = params.submit;
fireEvent(element, "show-dialog", {

View File

@ -0,0 +1,37 @@
import { fireEvent } from "../../../../common/dom/fire_event";
export type NewBackupType = "automatic" | "manual";
export interface NewBackupDialogParams {
submit?: (type: NewBackupType) => void;
cancel?: () => void;
}
export const loadNewBackupDialog = () => import("./dialog-new-backup");
export const showNewBackupDialog = (
element: HTMLElement,
params: NewBackupDialogParams
) =>
new Promise<NewBackupType | null>((resolve) => {
const origCancel = params.cancel;
const origSubmit = params.submit;
fireEvent(element, "show-dialog", {
dialogTag: "ha-dialog-new-backup",
dialogImport: loadNewBackupDialog,
dialogParams: {
...params,
cancel: () => {
resolve(null);
if (origCancel) {
origCancel();
}
},
submit: (response) => {
resolve(response);
if (origSubmit) {
origSubmit(response);
}
},
},
});
});

View File

@ -20,6 +20,7 @@ import "../../../components/ha-icon";
import "../../../components/ha-icon-next";
import "../../../components/ha-icon-overflow-menu";
import "../../../components/ha-svg-icon";
import { getSignedPath } from "../../../data/auth";
import {
fetchBackupInfo,
getBackupDownloadUrl,
@ -37,10 +38,10 @@ import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant, Route } from "../../../types";
import { brandsUrl } from "../../../util/brands-url";
import { fileDownload } from "../../../util/file_download";
import "./components/ha-backup-summary-card";
import { showGenerateBackupDialog } from "./dialogs/show-dialog-generate-backup";
import { getSignedPath } from "../../../data/auth";
import { fileDownload } from "../../../util/file_download";
import { showNewBackupDialog } from "./dialogs/show-dialog-new-backup";
@customElement("ha-config-backup-dashboard")
class HaConfigBackupDashboard extends SubscribeMixin(LitElement) {
@ -232,7 +233,7 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) {
?disabled=${this._backingUp}
.label=${this.hass.localize("ui.panel.config.backup.create_backup")}
extended
@click=${this._generateBackup}
@click=${this._newBackup}
>
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
</ha-fab>
@ -251,6 +252,20 @@ class HaConfigBackupDashboard extends SubscribeMixin(LitElement) {
this._backingUp = info.backing_up;
}
private async _newBackup(): Promise<void> {
const type = await showNewBackupDialog(this, {});
if (!type) {
return;
}
if (type === "manual") {
await this._generateBackup();
} else {
// Todo: implement trigger automatic backup
}
}
private async _generateBackup(): Promise<void> {
const response = await showGenerateBackupDialog(this, {});