Skip to content

Commit

Permalink
Handle errors in multi select (#20494)
Browse files Browse the repository at this point in the history
  • Loading branch information
bramkragten committed Apr 12, 2024
1 parent 8dc2797 commit be2f2c6
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 19 deletions.
9 changes: 9 additions & 0 deletions src/common/util/promise-all-settled-results.ts
Original file line number Diff line number Diff line change
@@ -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[];
65 changes: 61 additions & 4 deletions src/panels/config/automation/ha-automation-picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -196,6 +200,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
labels: (labels || []).map(
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
),
selectable: entityRegEntry !== undefined,
};
});
}
Expand Down Expand Up @@ -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) {
Expand All @@ -1135,23 +1153,62 @@ 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() {
const promises: Promise<ServiceCallResponse>[] = [];
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() {
const promises: Promise<ServiceCallResponse>[] = [];
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() {
Expand Down
20 changes: 19 additions & 1 deletion src/panels/config/devices/ha-config-devices-dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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() {
Expand Down
55 changes: 47 additions & 8 deletions src/panels/config/entities/ha-config-entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"> {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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() {
Expand Down
34 changes: 32 additions & 2 deletions src/panels/config/helpers/ha-config-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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) {
Expand All @@ -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(
Expand Down
35 changes: 33 additions & 2 deletions src/panels/config/scene/ha-scene-dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -178,6 +182,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
labels: (labels || []).map(
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
),
selectable: entityRegEntry !== undefined,
};
});
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down

0 comments on commit be2f2c6

Please sign in to comment.