Compare commits

...

1 Commits

Author SHA1 Message Date
Aidan Timson 10bf185d2f Migrate 4th set to dirty state provider 2026-06-09 14:56:48 +01:00
7 changed files with 108 additions and 40 deletions
@@ -10,13 +10,21 @@ import "../../components/ha-button";
import type { HaSwitch } from "../../components/ha-switch";
import type { ConfigEntryMutableParams } from "../../data/config_entries";
import { updateConfigEntry } from "../../data/config_entries";
import { DirtyStateProviderMixin } from "../../mixins/dirty-state-provider-mixin";
import { haStyleDialog } from "../../resources/styles";
import type { HomeAssistant } from "../../types";
import { showAlertDialog } from "../generic/show-dialog-box";
import type { ConfigEntrySystemOptionsDialogParams } from "./show-dialog-config-entry-system-options";
interface SystemOptionsState {
disableNewEntities: boolean;
disablePolling: boolean;
}
@customElement("dialog-config-entry-system-options")
class DialogConfigEntrySystemOptions extends LitElement {
class DialogConfigEntrySystemOptions extends DirtyStateProviderMixin<SystemOptionsState>()(
LitElement
) {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _disableNewEntities!: boolean;
@@ -38,6 +46,13 @@ class DialogConfigEntrySystemOptions extends LitElement {
this._error = undefined;
this._disableNewEntities = params.entry.pref_disable_new_entities;
this._disablePolling = params.entry.pref_disable_polling;
this._initDirtyTracking(
{ type: "shallow" },
{
disableNewEntities: this._disableNewEntities,
disablePolling: this._disablePolling,
}
);
this._open = true;
}
@@ -68,7 +83,7 @@ class DialogConfigEntrySystemOptions extends LitElement {
) || this._params.entry.domain,
}
)}
prevent-scrim-close
.preventScrimClose=${this.isDirtyState}
@closed=${this._dialogClosed}
>
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
@@ -135,7 +150,7 @@ class DialogConfigEntrySystemOptions extends LitElement {
<ha-button
slot="primaryAction"
@click=${this._updateEntry}
.disabled=${this._submitting}
.disabled=${this._submitting || !this.isDirtyState}
>
${this.hass.localize(
"ui.dialogs.config_entry_system_options.update"
@@ -149,11 +164,19 @@ class DialogConfigEntrySystemOptions extends LitElement {
private _disableNewEntitiesChanged(ev: Event): void {
this._error = undefined;
this._disableNewEntities = !(ev.target as HaSwitch).checked;
this._updateDirtyState({
disableNewEntities: this._disableNewEntities,
disablePolling: this._disablePolling,
});
}
private _disablePollingChanged(ev: Event): void {
this._error = undefined;
this._disablePolling = !(ev.target as HaSwitch).checked;
this._updateDirtyState({
disableNewEntities: this._disableNewEntities,
disablePolling: this._disablePolling,
});
}
private async _updateEntry(): Promise<void> {
@@ -23,18 +23,28 @@ import {
} from "../../../data/application_credential";
import type { IntegrationManifest } from "../../../data/integration";
import { domainToName } from "../../../data/integration";
import { DirtyStateProviderMixin } from "../../../mixins/dirty-state-provider-mixin";
import { haStyleDialog } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
import type { AddApplicationCredentialDialogParams } from "./show-dialog-add-application-credential";
interface CredentialFormState {
domain: string;
name: string;
clientId: string;
clientSecret: string;
}
interface Domain {
id: string;
name: string;
}
@customElement("dialog-add-application-credential")
export class DialogAddApplicationCredential extends LitElement {
export class DialogAddApplicationCredential extends DirtyStateProviderMixin<CredentialFormState>()(
LitElement
) {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _loading = false;
@@ -76,6 +86,7 @@ export class DialogAddApplicationCredential extends LitElement {
this._error = undefined;
this._loading = false;
this._open = true;
this._initDirtyTracking({ type: "shallow" }, this._currentState());
this._fetchConfig();
}
@@ -100,10 +111,7 @@ export class DialogAddApplicationCredential extends LitElement {
<ha-dialog
.open=${this._open}
@closed=${this._abortDialog}
.preventScrimClose=${!!this._domain ||
!!this._name ||
!!this._clientId ||
!!this._clientSecret}
.preventScrimClose=${this.isDirtyState}
.headerTitle=${this.hass.localize(
"ui.panel.config.application_credentials.editor.caption"
)}
@@ -284,6 +292,7 @@ export class DialogAddApplicationCredential extends LitElement {
ev.stopPropagation();
this._domain = ev.detail.value;
this._updateDescription();
this._updateDirtyState(this._currentState());
}
private async _updateDescription() {
@@ -307,6 +316,16 @@ export class DialogAddApplicationCredential extends LitElement {
const name = (ev.target as any).name;
const value = (ev.target as any).value;
this[`_${name}`] = value;
this._updateDirtyState(this._currentState());
}
private _currentState(): CredentialFormState {
return {
domain: this._domain || "",
name: this._name || "",
clientId: this._clientId || "",
clientSecret: this._clientSecret || "",
};
}
private _abortDialog() {
@@ -32,6 +32,7 @@ import {
import { extractApiErrorMessage } from "../../../../../data/hassio/common";
import type { ObjectSelector, Selector } from "../../../../../data/selector";
import { showConfirmationDialog } from "../../../../../dialogs/generic/show-dialog-box";
import { DirtyStateProviderMixin } from "../../../../../mixins/dirty-state-provider-mixin";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant } from "../../../../../types";
import { supervisorAppsStyle } from "../../resources/supervisor-apps-style";
@@ -56,15 +57,15 @@ const ADDON_YAML_SCHEMA = DEFAULT_SCHEMA.extend([
const MASKED_FIELDS = ["password", "secret", "token"];
@customElement("supervisor-app-config")
class SupervisorAppConfig extends LitElement {
class SupervisorAppConfig extends DirtyStateProviderMixin<
Record<string, unknown>
>()(LitElement) {
@property({ attribute: false }) public addon!: HassioAddonDetails;
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Boolean }) public disabled = false;
@state() private _configHasChanged = false;
@state() private _valid = true;
@state() private _canShowSchema = false;
@@ -351,9 +352,7 @@ class SupervisorAppConfig extends LitElement {
<div class="card-actions right">
<ha-progress-button
@click=${this._saveTapped}
.disabled=${this.disabled ||
!this._configHasChanged ||
!this._valid}
.disabled=${this.disabled || !this.isDirtyState || !this._valid}
>
${this.hass.localize("ui.common.save")}
</ha-progress-button>
@@ -377,6 +376,7 @@ class SupervisorAppConfig extends LitElement {
protected updated(changedProperties: PropertyValues): void {
if (changedProperties.has("addon")) {
this._options = { ...this.addon.options };
this._initDirtyTracking({ type: "deep" }, this.addon.options);
}
super.updated(changedProperties);
if (
@@ -415,11 +415,13 @@ class SupervisorAppConfig extends LitElement {
private _configChanged(ev): void {
if (this.addon.schema && this._canShowSchema && !this._yamlMode) {
this._valid = true;
this._configHasChanged = true;
this._options = ev.detail.value;
this._updateDirtyState(ev.detail.value);
} else {
this._configHasChanged = true;
this._valid = ev.detail.isValid;
if (ev.detail.isValid) {
this._updateDirtyState(ev.detail.value);
}
}
}
@@ -450,7 +452,7 @@ class SupervisorAppConfig extends LitElement {
};
try {
await setHassioAddonOption(this.hass.callWS, this.addon.slug, data);
this._configHasChanged = false;
this._markDirtyStateClean();
const eventdata = {
success: true,
response: undefined,
@@ -469,7 +471,7 @@ class SupervisorAppConfig extends LitElement {
}
private async _saveTapped(ev: CustomEvent): Promise<void> {
if (this.disabled || !this._configHasChanged || !this._valid) {
if (this.disabled || !this.isDirtyState || !this._valid) {
return;
}
@@ -499,7 +501,7 @@ class SupervisorAppConfig extends LitElement {
options,
});
this._configHasChanged = false;
this._markDirtyStateClean();
if (this.addon?.state === "started") {
await suggestSupervisorAppRestart(this, this.hass, this.addon);
}
@@ -15,13 +15,16 @@ import type {
} from "../../../../../data/hassio/addon";
import { setHassioAddonOption } from "../../../../../data/hassio/addon";
import { extractApiErrorMessage } from "../../../../../data/hassio/common";
import { DirtyStateProviderMixin } from "../../../../../mixins/dirty-state-provider-mixin";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant } from "../../../../../types";
import { supervisorAppsStyle } from "../../resources/supervisor-apps-style";
import { suggestSupervisorAppRestart } from "../dialogs/suggestSupervisorAppRestart";
@customElement("supervisor-app-network")
class SupervisorAppNetwork extends LitElement {
class SupervisorAppNetwork extends DirtyStateProviderMixin<
Record<string, number | null>
>()(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public addon!: HassioAddonDetails;
@@ -30,19 +33,19 @@ class SupervisorAppNetwork extends LitElement {
@state() private _showOptional = false;
@state() private _configHasChanged = false;
@state() private _error?: string;
@state() private _config?: Record<string, any>;
@state() private _config?: Record<string, number | null>;
protected render() {
if (!this._config) {
return nothing;
}
const hasHiddenOptions = Object.keys(this._config).find(
(entry) => this._config![entry] === null
const config = this._config;
const hasHiddenOptions = Object.keys(config).find(
(entry) => config[entry] === null
);
return html`
@@ -98,7 +101,7 @@ class SupervisorAppNetwork extends LitElement {
</ha-progress-button>
<ha-progress-button
@click=${this._saveTapped}
.disabled=${!this._configHasChanged || this.disabled}
.disabled=${!this.isDirtyState || this.disabled}
>
${this.hass.localize("ui.common.save")}
</ha-progress-button>
@@ -115,7 +118,10 @@ class SupervisorAppNetwork extends LitElement {
}
private _createSchema = memoizeOne(
(config: Record<string, number>, showOptional: boolean): HaFormSchema[] =>
(
config: Record<string, number | null>,
showOptional: boolean
): HaFormSchema[] =>
(showOptional
? Object.keys(config)
: Object.keys(config).filter((entry) => config[entry] !== null)
@@ -141,12 +147,14 @@ class SupervisorAppNetwork extends LitElement {
item.name;
private _setNetworkConfig(): void {
this._config = this.addon.network || {};
const config = this.addon.network || {};
this._config = config;
this._initDirtyTracking({ type: "shallow" }, config);
}
private async _configChanged(ev: CustomEvent): Promise<void> {
this._configHasChanged = true;
private _configChanged(ev: CustomEvent): void {
this._config = ev.detail.value;
this._updateDirtyState(ev.detail.value);
}
private async _resetTapped(ev: CustomEvent): Promise<void> {
@@ -161,7 +169,7 @@ class SupervisorAppNetwork extends LitElement {
try {
await setHassioAddonOption(this.hass.callWS, this.addon.slug, data);
this._configHasChanged = false;
this._markDirtyStateClean();
const eventdata = {
success: true,
response: undefined,
@@ -188,14 +196,14 @@ class SupervisorAppNetwork extends LitElement {
}
private async _saveTapped(ev: CustomEvent): Promise<void> {
if (!this._configHasChanged || this.disabled) {
if (!this.isDirtyState || this.disabled) {
return;
}
const button = ev.currentTarget as any;
this._error = undefined;
const networkconfiguration = {};
const networkconfiguration: Record<string, number | null> = {};
Object.entries(this._config!).forEach(([key, value]) => {
networkconfiguration[key] = value ?? null;
});
@@ -206,7 +214,7 @@ class SupervisorAppNetwork extends LitElement {
try {
await setHassioAddonOption(this.hass.callWS, this.addon.slug, data);
this._configHasChanged = false;
this._markDirtyStateClean();
const eventdata = {
success: true,
response: undefined,
@@ -16,6 +16,7 @@ import type {
AssistPipelineMutableParams,
} from "../../../data/assist_pipeline";
import { fetchAssistPipelineLanguages } from "../../../data/assist_pipeline";
import { DirtyStateProviderMixin } from "../../../mixins/dirty-state-provider-mixin";
import { haStyleDialog } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import "./assist-pipeline-detail/assist-pipeline-detail-config";
@@ -28,7 +29,9 @@ import type { VoiceAssistantPipelineDetailsDialogParams } from "./show-dialog-vo
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
@customElement("dialog-voice-assistant-pipeline-detail")
export class DialogVoiceAssistantPipelineDetail extends LitElement {
export class DialogVoiceAssistantPipelineDetail extends DirtyStateProviderMixin<
Partial<AssistPipeline>
>()(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _params?: VoiceAssistantPipelineDetailsDialogParams;
@@ -62,6 +65,7 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
this._hideWakeWord =
this._params.hideWakeWord || !this._data.wake_word_entity;
this._initDirtyTracking({ type: "deep" }, this._data);
return;
}
@@ -98,6 +102,7 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
stt_engine: this._params.pipeline?.stt_engine || sstDefault,
tts_engine: this._params.pipeline?.tts_engine || ttsDefault,
};
this._initDirtyTracking({ type: "deep" }, this._data);
}
public closeDialog(): void {
@@ -145,7 +150,7 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
<ha-dialog
.open=${this._open}
header-title=${title}
prevent-scrim-close
.preventScrimClose=${this.isDirtyState}
@closed=${this._dialogClosed}
>
${!this._hideWakeWord ||
@@ -266,6 +271,7 @@ export class DialogVoiceAssistantPipelineDetail extends LitElement {
value[key] = ev.detail.value[key];
});
this._data = { ...this._data, ...value };
this._updateDirtyState(this._data);
}
private async _updatePipeline() {
@@ -8,6 +8,7 @@ import "../../../components/ha-dialog";
import "../../../components/ha-form/ha-form";
import "../../../components/ha-button";
import type { HomeZoneMutableParams } from "../../../data/zone";
import { DirtyStateProviderMixin } from "../../../mixins/dirty-state-provider-mixin";
import { haStyleDialog } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import type { HomeZoneDetailDialogParams } from "./show-dialog-home-zone-detail";
@@ -21,7 +22,9 @@ const SCHEMA = [
];
@customElement("dialog-home-zone-detail")
class DialogHomeZoneDetail extends LitElement {
class DialogHomeZoneDetail extends DirtyStateProviderMixin<HomeZoneMutableParams>()(
LitElement
) {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _error?: Record<string, string>;
@@ -43,6 +46,7 @@ class DialogHomeZoneDetail extends LitElement {
longitude: this.hass.config.longitude,
radius: this.hass.config.radius,
};
this._initDirtyTracking({ type: "deep" }, this._data);
this._open = true;
}
@@ -71,7 +75,7 @@ class DialogHomeZoneDetail extends LitElement {
header-title=${this.hass!.localize("ui.common.edit_item", {
name: this._data.name,
})}
prevent-scrim-close
.preventScrimClose=${this.isDirtyState}
@closed=${this._dialogClosed}
>
<ha-form
@@ -120,6 +124,7 @@ class DialogHomeZoneDetail extends LitElement {
value.radius = value.location.radius;
delete value.location;
this._data = value;
this._updateDirtyState(value);
}
private _computeLabel = (): string => "";
+7 -2
View File
@@ -11,12 +11,15 @@ import "../../../components/ha-button";
import type { SchemaUnion } from "../../../components/ha-form/types";
import type { ZoneMutableParams } from "../../../data/zone";
import { getZoneEditorInitData } from "../../../data/zone";
import { DirtyStateProviderMixin } from "../../../mixins/dirty-state-provider-mixin";
import { haStyleDialog } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import type { ZoneDetailDialogParams } from "./show-dialog-zone-detail";
@customElement("dialog-zone-detail")
class DialogZoneDetail extends LitElement {
class DialogZoneDetail extends DirtyStateProviderMixin<ZoneMutableParams>()(
LitElement
) {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _error?: Record<string, string>;
@@ -53,6 +56,7 @@ class DialogZoneDetail extends LitElement {
radius: 100,
};
}
this._initDirtyTracking({ type: "deep" }, this._data);
this._open = true;
}
@@ -93,7 +97,7 @@ class DialogZoneDetail extends LitElement {
name: this._params.entry.name,
})
: this.hass!.localize("ui.panel.config.zone.detail.new_zone")}
prevent-scrim-close
.preventScrimClose=${this.isDirtyState}
@closed=${this._dialogClosed}
>
<ha-form
@@ -189,6 +193,7 @@ class DialogZoneDetail extends LitElement {
delete value.icon;
}
this._data = value;
this._updateDirtyState(value);
}
private _computeLabel = (