Skip to content

Commit a139717

Browse files
committed
Address review feedback
- Extract shared supervisor update config plumbing into a reactive controller to deduplicate logic between the settings and the new app-update-backups pages. - Don't show the misleading "local only" headline on the overview card before the supervisor config has loaded; surface fetch errors via ha-alert instead of swallowing them silently. - Redirect the app-update-backups sub-page back to the overview when hassio isn't loaded, so direct navigation can't reach a non-functional form. - Drop the duplicate settings.schedule.error_load/error_save translation keys and use the canonical settings.app_update_backup.* ones. - Align overview card title with the sub-page header (plural) and add a proper plural-aware translation for the backup-before-update summary.
1 parent 5e62989 commit a139717

6 files changed

Lines changed: 133 additions & 167 deletions

File tree

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import type { ReactiveController, ReactiveControllerHost } from "lit";
2+
import { debounce } from "../../common/util/debounce";
3+
import type { HomeAssistant } from "../../types";
4+
import {
5+
getSupervisorUpdateConfig,
6+
updateSupervisorUpdateConfig,
7+
type SupervisorUpdateConfig,
8+
} from "./update";
9+
10+
export interface SupervisorUpdateConfigHost extends ReactiveControllerHost {
11+
hass: HomeAssistant;
12+
}
13+
14+
export class SupervisorUpdateConfigController implements ReactiveController {
15+
public config?: SupervisorUpdateConfig;
16+
17+
public error?: string;
18+
19+
private _host: SupervisorUpdateConfigHost;
20+
21+
constructor(host: SupervisorUpdateConfigHost) {
22+
this._host = host;
23+
host.addController(this);
24+
}
25+
26+
hostConnected() {
27+
// Callers invoke `fetch()` explicitly to gate on prerequisites (e.g. hassio loaded).
28+
}
29+
30+
public async fetch() {
31+
try {
32+
this.config = await getSupervisorUpdateConfig(this._host.hass);
33+
this.error = undefined;
34+
} catch (err: any) {
35+
// eslint-disable-next-line no-console
36+
console.error(err);
37+
this.error = this._host.hass.localize(
38+
"ui.panel.config.backup.settings.app_update_backup.error_load",
39+
{ error: err?.message || err }
40+
);
41+
}
42+
this._host.requestUpdate();
43+
}
44+
45+
public update(partial: Partial<SupervisorUpdateConfig>) {
46+
this.config = { ...this.config, ...partial } as SupervisorUpdateConfig;
47+
this._host.requestUpdate();
48+
this._debouncedSave();
49+
}
50+
51+
private _debouncedSave = debounce(() => this._save(), 500);
52+
53+
private async _save() {
54+
if (!this.config) {
55+
return;
56+
}
57+
try {
58+
await updateSupervisorUpdateConfig(this._host.hass, this.config);
59+
this.error = undefined;
60+
} catch (err: any) {
61+
// eslint-disable-next-line no-console
62+
console.error(err);
63+
this.error = this._host.hass.localize(
64+
"ui.panel.config.backup.settings.app_update_backup.error_save",
65+
{ error: err?.message || err?.toString() }
66+
);
67+
}
68+
this._host.requestUpdate();
69+
}
70+
}

src/panels/config/backup/components/overview/ha-backup-overview-app-update-backup.ts

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,50 @@
11
import { mdiPuzzle } from "@mdi/js";
22
import type { CSSResultGroup } from "lit";
3-
import { css, html, LitElement } from "lit";
4-
import { customElement, property, state } from "lit/decorators";
3+
import { css, html, LitElement, nothing } from "lit";
4+
import { customElement, property } from "lit/decorators";
5+
import "../../../../../components/ha-alert";
56
import "../../../../../components/ha-card";
67
import "../../../../../components/ha-icon-next";
78
import "../../../../../components/ha-md-list";
89
import "../../../../../components/ha-md-list-item";
910
import "../../../../../components/ha-svg-icon";
10-
import {
11-
getSupervisorUpdateConfig,
12-
type SupervisorUpdateConfig,
13-
} from "../../../../../data/supervisor/update";
11+
import { SupervisorUpdateConfigController } from "../../../../../data/supervisor/update-controller";
1412
import { haStyle } from "../../../../../resources/styles";
1513
import type { HomeAssistant } from "../../../../../types";
1614

1715
@customElement("ha-backup-overview-app-update-backup")
1816
class HaBackupOverviewAppUpdateBackup extends LitElement {
1917
@property({ attribute: false }) public hass!: HomeAssistant;
2018

21-
@state() private _supervisorUpdateConfig?: SupervisorUpdateConfig;
19+
private _supervisorUpdateConfig = new SupervisorUpdateConfigController(this);
2220

2321
protected firstUpdated() {
24-
this._fetchSupervisorUpdateConfig();
22+
this._supervisorUpdateConfig.fetch();
2523
}
2624

27-
private async _fetchSupervisorUpdateConfig() {
28-
try {
29-
this._supervisorUpdateConfig = await getSupervisorUpdateConfig(this.hass);
30-
} catch (err) {
31-
// eslint-disable-next-line no-console
32-
console.error(err);
33-
}
34-
}
35-
36-
private _appUpdateBackupDescription() {
37-
if (!this._supervisorUpdateConfig) {
38-
return this.hass.localize(
39-
"ui.panel.config.backup.settings.app_update_backup.local_only"
40-
);
25+
private _appUpdateBackupHeadline() {
26+
const config = this._supervisorUpdateConfig.config;
27+
if (!config) {
28+
return nothing;
4129
}
4230

43-
if (!this._supervisorUpdateConfig.add_on_backup_before_update) {
31+
if (!config.add_on_backup_before_update) {
4432
return this.hass.localize(
4533
"ui.panel.config.backup.schedule.update_preference.skip_backups"
4634
);
4735
}
4836

49-
const copies =
50-
this._supervisorUpdateConfig.add_on_backup_retain_copies || 1;
37+
const copies = config.add_on_backup_retain_copies || 1;
5138

52-
return `${this.hass.localize(
53-
"ui.panel.config.backup.schedule.update_preference.backup_before_update"
54-
)} ${this.hass.localize(
55-
"ui.panel.config.backup.overview.settings.schedule_copies_backups",
39+
return this.hass.localize(
40+
"ui.panel.config.backup.overview.app_update_backup.backup_before_update_summary",
5641
{ count: copies }
57-
)}`;
42+
);
5843
}
5944

6045
protected render() {
46+
const error = this._supervisorUpdateConfig.error;
47+
6148
return html`
6249
<ha-card>
6350
<div class="card-header">
@@ -66,13 +53,16 @@ class HaBackupOverviewAppUpdateBackup extends LitElement {
6653
)}
6754
</div>
6855
<div class="card-content">
56+
${error
57+
? html`<ha-alert alert-type="error">${error}</ha-alert>`
58+
: nothing}
6959
<ha-md-list>
7060
<ha-md-list-item
7161
type="link"
7262
href="/config/backup/app-update-backups"
7363
>
7464
<ha-svg-icon slot="start" .path=${mdiPuzzle}></ha-svg-icon>
75-
<div slot="headline">${this._appUpdateBackupDescription()}</div>
65+
<div slot="headline">${this._appUpdateBackupHeadline()}</div>
7666
<div slot="supporting-text">
7767
${this.hass.localize(
7868
"ui.panel.config.backup.overview.app_update_backup.description"
@@ -100,6 +90,11 @@ class HaBackupOverviewAppUpdateBackup extends LitElement {
10090
padding-top: 0;
10191
}
10292
93+
ha-alert {
94+
display: block;
95+
margin: 0 16px 8px;
96+
}
97+
10398
ha-md-list {
10499
padding-top: 0;
105100
padding-bottom: 0;

src/panels/config/backup/ha-config-backup-app-update-backups.ts

Lines changed: 20 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import { css, html, LitElement, nothing } from "lit";
22
import type { PropertyValues } from "lit";
3-
import { customElement, property, state } from "lit/decorators";
3+
import { customElement, property } from "lit/decorators";
44
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
5-
import { debounce } from "../../../common/util/debounce";
5+
import { navigate } from "../../../common/navigate";
66
import "../../../components/ha-alert";
77
import "../../../components/ha-card";
8-
import {
9-
getSupervisorUpdateConfig,
10-
updateSupervisorUpdateConfig,
11-
type SupervisorUpdateConfig,
12-
} from "../../../data/supervisor/update";
8+
import { SupervisorUpdateConfigController } from "../../../data/supervisor/update-controller";
9+
import type { SupervisorUpdateConfig } from "../../../data/supervisor/update";
1310
import "../../../layouts/hass-subpage";
1411
import type { HomeAssistant } from "../../../types";
1512
import "./components/config/ha-backup-config-addon";
@@ -20,19 +17,17 @@ class HaConfigBackupAppUpdateBackups extends LitElement {
2017

2118
@property({ type: Boolean }) public narrow = false;
2219

23-
@state() private _supervisorUpdateConfig?: SupervisorUpdateConfig;
24-
25-
@state() private _error?: string;
20+
private _supervisorUpdateConfig = new SupervisorUpdateConfigController(this);
2621

2722
protected willUpdate(changedProps: PropertyValues<this>): void {
2823
super.willUpdate(changedProps);
2924

30-
if (
31-
!this.hasUpdated &&
32-
this.hass &&
33-
isComponentLoaded(this.hass.config, "hassio")
34-
) {
35-
this._getSupervisorUpdateConfig();
25+
if (!this.hasUpdated && this.hass) {
26+
if (isComponentLoaded(this.hass.config, "hassio")) {
27+
this._supervisorUpdateConfig.fetch();
28+
} else {
29+
navigate("/config/backup/overview", { replace: true });
30+
}
3631
}
3732
}
3833

@@ -59,12 +54,14 @@ class HaConfigBackupAppUpdateBackups extends LitElement {
5954
"ui.panel.config.backup.settings.app_update_backup.local_only"
6055
)}
6156
</p>
62-
${this._error
63-
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
57+
${this._supervisorUpdateConfig.error
58+
? html`<ha-alert alert-type="error"
59+
>${this._supervisorUpdateConfig.error}</ha-alert
60+
>`
6461
: nothing}
6562
<ha-backup-config-addon
6663
.hass=${this.hass}
67-
.supervisorUpdateConfig=${this._supervisorUpdateConfig}
64+
.supervisorUpdateConfig=${this._supervisorUpdateConfig.config}
6865
@update-config-changed=${this._supervisorUpdateConfigChanged}
6966
></ha-backup-config-addon>
7067
</div>
@@ -74,56 +71,10 @@ class HaConfigBackupAppUpdateBackups extends LitElement {
7471
`;
7572
}
7673

77-
private async _getSupervisorUpdateConfig() {
78-
try {
79-
this._supervisorUpdateConfig = await getSupervisorUpdateConfig(this.hass);
80-
this._error = undefined;
81-
} catch (err: any) {
82-
// eslint-disable-next-line no-console
83-
console.error(err);
84-
this._error = this.hass.localize(
85-
"ui.panel.config.backup.settings.app_update_backup.error_load",
86-
{
87-
error: err?.message || err,
88-
}
89-
);
90-
}
91-
}
92-
93-
private async _supervisorUpdateConfigChanged(ev) {
94-
const config = ev.detail.value as SupervisorUpdateConfig;
95-
this._supervisorUpdateConfig = {
96-
...this._supervisorUpdateConfig,
97-
...config,
98-
} as SupervisorUpdateConfig;
99-
this._debounceSaveSupervisorUpdateConfig();
100-
}
101-
102-
private _debounceSaveSupervisorUpdateConfig = debounce(
103-
() => this._saveSupervisorUpdateConfig(),
104-
500
105-
);
106-
107-
private async _saveSupervisorUpdateConfig() {
108-
if (!this._supervisorUpdateConfig) {
109-
return;
110-
}
111-
try {
112-
await updateSupervisorUpdateConfig(
113-
this.hass,
114-
this._supervisorUpdateConfig
115-
);
116-
this._error = undefined;
117-
} catch (err: any) {
118-
// eslint-disable-next-line no-console
119-
console.error(err);
120-
this._error = this.hass.localize(
121-
"ui.panel.config.backup.settings.app_update_backup.error_save",
122-
{
123-
error: err?.message || err?.toString(),
124-
}
125-
);
126-
}
74+
private _supervisorUpdateConfigChanged(
75+
ev: CustomEvent<{ value: SupervisorUpdateConfig }>
76+
) {
77+
this._supervisorUpdateConfig.update(ev.detail.value);
12778
}
12879

12980
static styles = css`

src/panels/config/backup/ha-config-backup-overview.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ import "../../../layouts/hass-tabs-subpage-data-table";
3333
import { haStyle } from "../../../resources/styles";
3434
import type { HomeAssistant, Route } from "../../../types";
3535
import { showAlertDialog } from "../../lovelace/custom-card-helpers";
36-
import "./components/overview/ha-backup-overview-backups";
36+
import "./components/config/ha-backup-config-encryption-key";
3737
import "./components/overview/ha-backup-overview-app-update-backup";
38+
import "./components/overview/ha-backup-overview-backups";
3839
import "./components/overview/ha-backup-overview-onboarding";
3940
import "./components/overview/ha-backup-overview-progress";
4041
import "./components/overview/ha-backup-overview-settings";
4142
import "./components/overview/ha-backup-overview-summary";
42-
import "./components/config/ha-backup-config-encryption-key";
4343
import { showBackupOnboardingDialog } from "./dialogs/show-dialog-backup_onboarding";
4444
import { showGenerateBackupDialog } from "./dialogs/show-dialog-generate-backup";
4545
import { showNewBackupDialog } from "./dialogs/show-dialog-new-backup";

0 commit comments

Comments
 (0)