diff --git a/src/panels/config/integrations/ha-config-flow-card.ts b/src/panels/config/integrations/ha-config-flow-card.ts index e717f81bd2..7f9e2f9cd5 100644 --- a/src/panels/config/integrations/ha-config-flow-card.ts +++ b/src/panels/config/integrations/ha-config-flow-card.ts @@ -1,4 +1,10 @@ -import { mdiBookshelf, mdiCog, mdiDotsVertical, mdiOpenInNew } from "@mdi/js"; +import { + mdiBookshelf, + mdiCog, + mdiDelete, + mdiDotsVertical, + mdiOpenInNew, +} from "@mdi/js"; import type { TemplateResult } from "lit"; import { LitElement, css, html } from "lit"; import { customElement, property } from "lit/decorators"; @@ -7,6 +13,11 @@ import { fireEvent } from "../../../common/dom/fire_event"; import "../../../components/ha-button"; import "../../../components/ha-button-menu"; import "../../../components/ha-list-item"; +import { + deleteApplicationCredential, + fetchApplicationCredentialsConfigEntry, +} from "../../../data/application_credential"; +import { deleteConfigEntry } from "../../../data/config_entries"; import { ATTENTION_SOURCES, DISCOVERY_SOURCES, @@ -15,7 +26,10 @@ import { } from "../../../data/config_flow"; import type { IntegrationManifest } from "../../../data/integration"; import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow"; -import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; +import { + showAlertDialog, + showConfirmationDialog, +} from "../../../dialogs/generic/show-dialog-box"; import type { HomeAssistant } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; import type { DataEntryFlowProgressExtended } from "./ha-config-integrations"; @@ -60,7 +74,7 @@ export class HaConfigFlowCard extends LitElement { : "ui.common.add" )} - ${this.flow.context.configuration_url || this.manifest + ${this.flow.context.configuration_url || this.manifest || attention ? html` ` : ""} + ${attention + ? html` + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.delete" + )} + ` + : ""} ` : ""} @@ -175,6 +205,109 @@ export class HaConfigFlowCard extends LitElement { }); } + // Return an application credentials id for this config entry to prompt the + // user for removal. This is best effort so we don't stop overall removal + // if the integration isn't loaded or there is some other error. + private async _fetchApplicationCredentials(entryId: string) { + try { + return (await fetchApplicationCredentialsConfigEntry(this.hass, entryId)) + .application_credentials_id; + } catch (_err: any) { + // We won't prompt the user to remove credentials + return null; + } + } + + private async _removeApplicationCredential(applicationCredentialsId: string) { + const confirmed = await showConfirmationDialog(this, { + title: this.hass.localize( + "ui.panel.config.integrations.config_entry.application_credentials.delete_title" + ), + text: html`${this.hass.localize( + "ui.panel.config.integrations.config_entry.application_credentials.delete_prompt" + )}, +
+
+ ${this.hass.localize( + "ui.panel.config.integrations.config_entry.application_credentials.delete_detail" + )} +
+
+ + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.application_credentials.learn_more" + )} + `, + confirmText: this.hass.localize("ui.common.delete"), + dismissText: this.hass.localize("ui.common.cancel"), + destructive: true, + }); + + if (!confirmed) { + return; + } + + try { + await deleteApplicationCredential(this.hass, applicationCredentialsId); + } catch (err: any) { + showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.config.integrations.config_entry.application_credentials.delete_error_title" + ), + text: err.message, + }); + } + } + + private async _handleDelete() { + const entryId = this.flow.context.entry_id; + + if (!entryId) { + // This shouldn't happen for reauth flows, but handle gracefully + return; + } + + const applicationCredentialsId = + await this._fetchApplicationCredentials(entryId); + + const confirmed = await showConfirmationDialog(this, { + title: this.hass.localize( + "ui.panel.config.integrations.config_entry.delete_confirm_title", + { title: localizeConfigFlowTitle(this.hass.localize, this.flow) } + ), + text: this.hass.localize( + "ui.panel.config.integrations.config_entry.delete_confirm_text" + ), + confirmText: this.hass!.localize("ui.common.delete"), + dismissText: this.hass!.localize("ui.common.cancel"), + destructive: true, + }); + + if (!confirmed) { + return; + } + + const result = await deleteConfigEntry(this.hass, entryId); + + if (result.require_restart) { + showAlertDialog(this, { + text: this.hass.localize( + "ui.panel.config.integrations.config_entry.restart_confirm" + ), + }); + } + + if (applicationCredentialsId) { + this._removeApplicationCredential(applicationCredentialsId); + } + + this._handleFlowUpdated(); + } + static styles = css` a { text-decoration: none; @@ -191,6 +324,9 @@ export class HaConfigFlowCard extends LitElement { --mdc-theme-primary: var(--error-color); --ha-card-border-color: var(--error-color); } + .warning { + --mdc-theme-text-primary-on-background: var(--error-color); + } `; }