mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Handle errors in multi select (#20494)
This commit is contained in:
parent
c96968e476
commit
ab49aca815
9
src/common/util/promise-all-settled-results.ts
Normal file
9
src/common/util/promise-all-settled-results.ts
Normal 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[];
|
@ -105,6 +105,10 @@ import { showCategoryRegistryDetailDialog } from "../category/show-dialog-catego
|
|||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
||||||
import { showNewAutomationDialog } from "./show-dialog-new-automation";
|
import { showNewAutomationDialog } from "./show-dialog-new-automation";
|
||||||
|
import {
|
||||||
|
hasRejectedItems,
|
||||||
|
rejectedItems,
|
||||||
|
} from "../../../common/util/promise-all-settled-results";
|
||||||
|
|
||||||
type AutomationItem = AutomationEntity & {
|
type AutomationItem = AutomationEntity & {
|
||||||
name: string;
|
name: string;
|
||||||
@ -196,6 +200,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
labels: (labels || []).map(
|
labels: (labels || []).map(
|
||||||
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
|
(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) {
|
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() {
|
private async _handleBulkEnable() {
|
||||||
@ -1143,7 +1174,20 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
this._selected.forEach((entityId) => {
|
this._selected.forEach((entityId) => {
|
||||||
promises.push(turnOnOffEntity(this.hass, entityId, true));
|
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() {
|
private async _handleBulkDisable() {
|
||||||
@ -1151,7 +1195,20 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
this._selected.forEach((entityId) => {
|
this._selected.forEach((entityId) => {
|
||||||
promises.push(turnOnOffEntity(this.hass, entityId, false));
|
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() {
|
private async _bulkCreateCategory() {
|
||||||
|
@ -69,6 +69,11 @@ import { configSections } from "../ha-panel-config";
|
|||||||
import "../integrations/ha-integration-overflow-menu";
|
import "../integrations/ha-integration-overflow-menu";
|
||||||
import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog";
|
import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog";
|
||||||
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
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 {
|
interface DeviceRowData extends DeviceRegistryEntry {
|
||||||
device?: DeviceRowData;
|
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() {
|
private _bulkCreateLabel() {
|
||||||
|
@ -90,6 +90,10 @@ import {
|
|||||||
EntitySources,
|
EntitySources,
|
||||||
fetchEntitySourcesWithCache,
|
fetchEntitySourcesWithCache,
|
||||||
} from "../../../data/entity_sources";
|
} from "../../../data/entity_sources";
|
||||||
|
import {
|
||||||
|
hasRejectedItems,
|
||||||
|
rejectedItems,
|
||||||
|
} from "../../../common/util/promise-all-settled-results";
|
||||||
|
|
||||||
export interface StateEntity
|
export interface StateEntity
|
||||||
extends Omit<EntityRegistryEntry, "id" | "unique_id"> {
|
extends Omit<EntityRegistryEntry, "id" | "unique_id"> {
|
||||||
@ -957,19 +961,41 @@ ${
|
|||||||
confirm: async () => {
|
confirm: async () => {
|
||||||
let require_restart = false;
|
let require_restart = false;
|
||||||
let reload_delay = 0;
|
let reload_delay = 0;
|
||||||
await Promise.all(
|
const result = await Promise.allSettled(
|
||||||
this._selected.map(async (entity) => {
|
this._selected.map(async (entity) => {
|
||||||
const result = await updateEntityRegistryEntry(this.hass, entity, {
|
const updateResult = await updateEntityRegistryEntry(
|
||||||
disabled_by: null,
|
this.hass,
|
||||||
});
|
entity,
|
||||||
if (result.require_restart) {
|
{
|
||||||
|
disabled_by: null,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (updateResult.require_restart) {
|
||||||
require_restart = true;
|
require_restart = true;
|
||||||
}
|
}
|
||||||
if (result.reload_delay) {
|
if (updateResult.reload_delay) {
|
||||||
reload_delay = Math.max(reload_delay, result.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();
|
this._clearSelection();
|
||||||
// If restart is required by any entity, show a dialog.
|
// If restart is required by any entity, show a dialog.
|
||||||
// Otherwise, show a dialog explaining that some patience is needed
|
// 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() {
|
private _bulkCreateLabel() {
|
||||||
|
@ -32,6 +32,10 @@ import {
|
|||||||
LocalizeKeys,
|
LocalizeKeys,
|
||||||
} from "../../../common/translations/localize";
|
} from "../../../common/translations/localize";
|
||||||
import { extractSearchParam } from "../../../common/url/search-params";
|
import { extractSearchParam } from "../../../common/url/search-params";
|
||||||
|
import {
|
||||||
|
hasRejectedItems,
|
||||||
|
rejectedItems,
|
||||||
|
} from "../../../common/util/promise-all-settled-results";
|
||||||
import {
|
import {
|
||||||
DataTableColumnContainer,
|
DataTableColumnContainer,
|
||||||
RowClickedEvent,
|
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) {
|
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(
|
private _handleSelectionChanged(
|
||||||
|
@ -95,6 +95,10 @@ import { showAssignCategoryDialog } from "../category/show-dialog-assign-categor
|
|||||||
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
||||||
|
import {
|
||||||
|
hasRejectedItems,
|
||||||
|
rejectedItems,
|
||||||
|
} from "../../../common/util/promise-all-settled-results";
|
||||||
|
|
||||||
type SceneItem = SceneEntity & {
|
type SceneItem = SceneEntity & {
|
||||||
name: string;
|
name: string;
|
||||||
@ -178,6 +182,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
labels: (labels || []).map(
|
labels: (labels || []).map(
|
||||||
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
|
(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) {
|
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) {
|
private _editCategory(scene: any) {
|
||||||
|
@ -97,6 +97,10 @@ import { showAssignCategoryDialog } from "../category/show-dialog-assign-categor
|
|||||||
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
|
||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
||||||
|
import {
|
||||||
|
hasRejectedItems,
|
||||||
|
rejectedItems,
|
||||||
|
} from "../../../common/util/promise-all-settled-results";
|
||||||
|
|
||||||
type ScriptItem = ScriptEntity & {
|
type ScriptItem = ScriptEntity & {
|
||||||
name: string;
|
name: string;
|
||||||
@ -185,6 +189,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
labels: (labels || []).map(
|
labels: (labels || []).map(
|
||||||
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
|
(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) {
|
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>) {
|
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
|
||||||
|
@ -1867,6 +1867,7 @@
|
|||||||
"editor": {
|
"editor": {
|
||||||
"confirm_unsaved": "You have unsaved changes. Are you sure you want to leave?"
|
"confirm_unsaved": "You have unsaved changes. Are you sure you want to leave?"
|
||||||
},
|
},
|
||||||
|
"multiselect": { "failed": "Failed to update {number} items." },
|
||||||
"learn_more": "Learn more"
|
"learn_more": "Learn more"
|
||||||
},
|
},
|
||||||
"updates": {
|
"updates": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user