Handle errors in multi select (#20494)

This commit is contained in:
Bram Kragten 2024-04-12 11:15:15 +02:00 committed by GitHub
parent 8dc2797b16
commit be2f2c6271
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 235 additions and 19 deletions

View File

@ -0,0 +1,9 @@
export const hasRejectedItems = <T = any>(results: PromiseSettledResult<T>[]) =>
results.some((result) => result.status === "rejected");
export const rejectedItems = <T = any>(
results: PromiseSettledResult<T>[]
): PromiseRejectedResult[] =>
results.filter(
(result) => result.status === "rejected"
) as PromiseRejectedResult[];

View File

@ -105,6 +105,10 @@ import { showCategoryRegistryDetailDialog } from "../category/show-dialog-catego
import { configSections } from "../ha-panel-config";
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
import { showNewAutomationDialog } from "./show-dialog-new-automation";
import {
hasRejectedItems,
rejectedItems,
} from "../../../common/util/promise-all-settled-results";
type AutomationItem = AutomationEntity & {
name: string;
@ -196,6 +200,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
labels: (labels || []).map(
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
),
selectable: entityRegEntry !== undefined,
};
});
}
@ -1112,7 +1117,20 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
})
);
});
await Promise.all(promises);
const result = await Promise.allSettled(promises);
if (hasRejectedItems(result)) {
const rejected = rejectedItems(result);
showAlertDialog(this, {
title: this.hass.localize("ui.panel.config.common.multiselect.failed", {
number: rejected.length,
}),
text: html`<pre>
${rejected
.map((r) => r.reason.message || r.reason.code || r.reason)
.join("\r\n")}</pre
>`,
});
}
}
private async _handleBulkLabel(ev) {
@ -1135,7 +1153,20 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
})
);
});
await Promise.all(promises);
const result = await Promise.allSettled(promises);
if (hasRejectedItems(result)) {
const rejected = rejectedItems(result);
showAlertDialog(this, {
title: this.hass.localize("ui.panel.config.common.multiselect.failed", {
number: rejected.length,
}),
text: html`<pre>
${rejected
.map((r) => r.reason.message || r.reason.code || r.reason)
.join("\r\n")}</pre
>`,
});
}
}
private async _handleBulkEnable() {
@ -1143,7 +1174,20 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
this._selected.forEach((entityId) => {
promises.push(turnOnOffEntity(this.hass, entityId, true));
});
await Promise.all(promises);
const result = await Promise.allSettled(promises);
if (hasRejectedItems(result)) {
const rejected = rejectedItems(result);
showAlertDialog(this, {
title: this.hass.localize("ui.panel.config.common.multiselect.failed", {
number: rejected.length,
}),
text: html`<pre>
${rejected
.map((r) => r.reason.message || r.reason.code || r.reason)
.join("\r\n")}</pre
>`,
});
}
}
private async _handleBulkDisable() {
@ -1151,7 +1195,20 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
this._selected.forEach((entityId) => {
promises.push(turnOnOffEntity(this.hass, entityId, false));
});
await Promise.all(promises);
const result = await Promise.allSettled(promises);
if (hasRejectedItems(result)) {
const rejected = rejectedItems(result);
showAlertDialog(this, {
title: this.hass.localize("ui.panel.config.common.multiselect.failed", {
number: rejected.length,
}),
text: html`<pre>
${rejected
.map((r) => r.reason.message || r.reason.code || r.reason)
.join("\r\n")}</pre
>`,
});
}
}
private async _bulkCreateCategory() {

View File

@ -69,6 +69,11 @@ import { configSections } from "../ha-panel-config";
import "../integrations/ha-integration-overflow-menu";
import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog";
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
import {
hasRejectedItems,
rejectedItems,
} from "../../../common/util/promise-all-settled-results";
import { showAlertDialog } from "../../lovelace/custom-card-helpers";
interface DeviceRowData extends DeviceRegistryEntry {
device?: DeviceRowData;
@ -824,7 +829,20 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
})
);
});
await Promise.all(promises);
const result = await Promise.allSettled(promises);
if (hasRejectedItems(result)) {
const rejected = rejectedItems(result);
showAlertDialog(this, {
title: this.hass.localize("ui.panel.config.common.multiselect.failed", {
number: rejected.length,
}),
text: html`<pre>
${rejected
.map((r) => r.reason.message || r.reason.code || r.reason)
.join("\r\n")}</pre
>`,
});
}
}
private _bulkCreateLabel() {

View File

@ -90,6 +90,10 @@ import {
EntitySources,
fetchEntitySourcesWithCache,
} from "../../../data/entity_sources";
import {
hasRejectedItems,
rejectedItems,
} from "../../../common/util/promise-all-settled-results";
export interface StateEntity
extends Omit<EntityRegistryEntry, "id" | "unique_id"> {
@ -957,19 +961,41 @@ ${
confirm: async () => {
let require_restart = false;
let reload_delay = 0;
await Promise.all(
const result = await Promise.allSettled(
this._selected.map(async (entity) => {
const result = await updateEntityRegistryEntry(this.hass, entity, {
disabled_by: null,
});
if (result.require_restart) {
const updateResult = await updateEntityRegistryEntry(
this.hass,
entity,
{
disabled_by: null,
}
);
if (updateResult.require_restart) {
require_restart = true;
}
if (result.reload_delay) {
reload_delay = Math.max(reload_delay, result.reload_delay);
if (updateResult.reload_delay) {
reload_delay = Math.max(reload_delay, updateResult.reload_delay);
}
})
);
if (hasRejectedItems(result)) {
const rejected = rejectedItems(result);
showAlertDialog(this, {
title: this.hass.localize(
"ui.panel.config.common.multiselect.failed",
{
number: rejected.length,
}
),
text: html`<pre>
${rejected
.map((r) => r.reason.message || r.reason.code || r.reason)
.join("\r\n")}</pre
>`,
});
}
this._clearSelection();
// If restart is required by any entity, show a dialog.
// Otherwise, show a dialog explaining that some patience is needed
@ -1068,7 +1094,20 @@ ${
})
);
});
await Promise.all(promises);
const result = await Promise.allSettled(promises);
if (hasRejectedItems(result)) {
const rejected = rejectedItems(result);
showAlertDialog(this, {
title: this.hass.localize("ui.panel.config.common.multiselect.failed", {
number: rejected.length,
}),
text: html`<pre>
${rejected
.map((r) => r.reason.message || r.reason.code || r.reason)
.join("\r\n")}</pre
>`,
});
}
}
private _bulkCreateLabel() {

View File

@ -32,6 +32,10 @@ import {
LocalizeKeys,
} from "../../../common/translations/localize";
import { extractSearchParam } from "../../../common/url/search-params";
import {
hasRejectedItems,
rejectedItems,
} from "../../../common/util/promise-all-settled-results";
import {
DataTableColumnContainer,
RowClickedEvent,
@ -801,7 +805,20 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
})
);
});
await Promise.all(promises);
const result = await Promise.allSettled(promises);
if (hasRejectedItems(result)) {
const rejected = rejectedItems(result);
showAlertDialog(this, {
title: this.hass.localize("ui.panel.config.common.multiselect.failed", {
number: rejected.length,
}),
text: html`<pre>
${rejected
.map((r) => r.reason.message || r.reason.code || r.reason)
.join("\r\n")}</pre
>`,
});
}
}
private async _handleBulkLabel(ev) {
@ -824,7 +841,20 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
})
);
});
await Promise.all(promises);
const result = await Promise.allSettled(promises);
if (hasRejectedItems(result)) {
const rejected = rejectedItems(result);
showAlertDialog(this, {
title: this.hass.localize("ui.panel.config.common.multiselect.failed", {
number: rejected.length,
}),
text: html`<pre>
${rejected
.map((r) => r.reason.message || r.reason.code || r.reason)
.join("\r\n")}</pre
>`,
});
}
}
private _handleSelectionChanged(

View File

@ -95,6 +95,10 @@ import { showAssignCategoryDialog } from "../category/show-dialog-assign-categor
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
import { configSections } from "../ha-panel-config";
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
import {
hasRejectedItems,
rejectedItems,
} from "../../../common/util/promise-all-settled-results";
type SceneItem = SceneEntity & {
name: string;
@ -178,6 +182,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
labels: (labels || []).map(
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
),
selectable: entityRegEntry !== undefined,
};
});
}
@ -798,7 +803,20 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
})
);
});
await Promise.all(promises);
const result = await Promise.allSettled(promises);
if (hasRejectedItems(result)) {
const rejected = rejectedItems(result);
showAlertDialog(this, {
title: this.hass.localize("ui.panel.config.common.multiselect.failed", {
number: rejected.length,
}),
text: html`<pre>
${rejected
.map((r) => r.reason.message || r.reason.code || r.reason)
.join("\r\n")}</pre
>`,
});
}
}
private async _handleBulkLabel(ev) {
@ -821,7 +839,20 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
})
);
});
await Promise.all(promises);
const result = await Promise.allSettled(promises);
if (hasRejectedItems(result)) {
const rejected = rejectedItems(result);
showAlertDialog(this, {
title: this.hass.localize("ui.panel.config.common.multiselect.failed", {
number: rejected.length,
}),
text: html`<pre>
${rejected
.map((r) => r.reason.message || r.reason.code || r.reason)
.join("\r\n")}</pre
>`,
});
}
}
private _editCategory(scene: any) {

View File

@ -97,6 +97,10 @@ import { showAssignCategoryDialog } from "../category/show-dialog-assign-categor
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
import { configSections } from "../ha-panel-config";
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
import {
hasRejectedItems,
rejectedItems,
} from "../../../common/util/promise-all-settled-results";
type ScriptItem = ScriptEntity & {
name: string;
@ -185,6 +189,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
labels: (labels || []).map(
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
),
selectable: entityRegEntry !== undefined,
};
});
}
@ -867,7 +872,20 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
})
);
});
await Promise.all(promises);
const result = await Promise.allSettled(promises);
if (hasRejectedItems(result)) {
const rejected = rejectedItems(result);
showAlertDialog(this, {
title: this.hass.localize("ui.panel.config.common.multiselect.failed", {
number: rejected.length,
}),
text: html`<pre>
${rejected
.map((r) => r.reason.message || r.reason.code || r.reason)
.join("\r\n")}</pre
>`,
});
}
}
private async _handleBulkLabel(ev) {
@ -890,7 +908,20 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
})
);
});
await Promise.all(promises);
const result = await Promise.allSettled(promises);
if (hasRejectedItems(result)) {
const rejected = rejectedItems(result);
showAlertDialog(this, {
title: this.hass.localize("ui.panel.config.common.multiselect.failed", {
number: rejected.length,
}),
text: html`<pre>
${rejected
.map((r) => r.reason.message || r.reason.code || r.reason)
.join("\r\n")}</pre
>`,
});
}
}
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {

View File

@ -1867,6 +1867,7 @@
"editor": {
"confirm_unsaved": "You have unsaved changes. Are you sure you want to leave?"
},
"multiselect": { "failed": "Failed to update {number} items." },
"learn_more": "Learn more"
},
"updates": {