mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-19 18:28:42 +00:00
Use history to manage back button click in automations add TCA (#28289)
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import type { HASSDomEvent, ValidHassDomEvent } from "../common/dom/fire_event";
|
||||
import { mainWindow } from "../common/dom/get_main_window";
|
||||
import type { ProvideHassElement } from "../mixins/provide-hass-lit-mixin";
|
||||
import { ancestorsWithProperty } from "../common/dom/ancestors-with-property";
|
||||
import { deepActiveElement } from "../common/dom/deep-active-element";
|
||||
import type { HASSDomEvent, ValidHassDomEvent } from "../common/dom/fire_event";
|
||||
import { mainWindow } from "../common/dom/get_main_window";
|
||||
import { nextRender } from "../common/util/render-status";
|
||||
import type { ProvideHassElement } from "../mixins/provide-hass-lit-mixin";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
@@ -23,7 +23,7 @@ export interface HassDialog<
|
||||
T = HASSDomEvents[ValidHassDomEvent],
|
||||
> extends HTMLElement {
|
||||
showDialog(params: T);
|
||||
closeDialog?: () => boolean;
|
||||
closeDialog?: (historyState?: any) => boolean;
|
||||
}
|
||||
|
||||
interface ShowDialogParams<T> {
|
||||
@@ -144,27 +144,32 @@ export const showDialog = async (
|
||||
return true;
|
||||
};
|
||||
|
||||
export const closeDialog = async (dialogTag: string): Promise<boolean> => {
|
||||
export const closeDialog = async (
|
||||
dialogTag: string,
|
||||
historyState?: any
|
||||
): Promise<boolean> => {
|
||||
if (!(dialogTag in LOADED)) {
|
||||
return true;
|
||||
}
|
||||
const dialogElement = await LOADED[dialogTag].element;
|
||||
if (dialogElement.closeDialog) {
|
||||
return dialogElement.closeDialog() !== false;
|
||||
return dialogElement.closeDialog(historyState) !== false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// called on back()
|
||||
export const closeLastDialog = async () => {
|
||||
export const closeLastDialog = async (historyState?: any) => {
|
||||
if (OPEN_DIALOG_STACK.length) {
|
||||
const lastDialog = OPEN_DIALOG_STACK.pop();
|
||||
const closed = await closeDialog(lastDialog!.dialogTag);
|
||||
const lastDialog = OPEN_DIALOG_STACK.pop() as DialogState;
|
||||
const closed = await closeDialog(lastDialog.dialogTag, historyState);
|
||||
if (!closed) {
|
||||
// if the dialog was not closed, put it back on the stack
|
||||
OPEN_DIALOG_STACK.push(lastDialog!);
|
||||
}
|
||||
if (OPEN_DIALOG_STACK.length && mainWindow.history.state?.opensDialog) {
|
||||
OPEN_DIALOG_STACK.push(lastDialog);
|
||||
} else if (
|
||||
OPEN_DIALOG_STACK.length &&
|
||||
mainWindow.history.state?.opensDialog
|
||||
) {
|
||||
// if there are more dialogs open, push a new state so back() will close the next top dialog
|
||||
mainWindow.history.pushState(
|
||||
{ dialog: OPEN_DIALOG_STACK[OPEN_DIALOG_STACK.length - 1].dialogTag },
|
||||
|
||||
@@ -16,6 +16,7 @@ import { classMap } from "lit/directives/class-map";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { mainWindow } from "../../../common/dom/get_main_window";
|
||||
import { computeAreaName } from "../../../common/entity/compute_area_name";
|
||||
import { computeDeviceName } from "../../../common/entity/compute_device_name";
|
||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
@@ -118,7 +119,6 @@ import type { HomeAssistant } from "../../../types";
|
||||
import { isMac } from "../../../util/is_mac";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import "./add-automation-element/ha-automation-add-from-target";
|
||||
import type HaAutomationAddFromTarget from "./add-automation-element/ha-automation-add-from-target";
|
||||
import "./add-automation-element/ha-automation-add-items";
|
||||
import "./add-automation-element/ha-automation-add-search";
|
||||
import type { AddAutomationElementDialogParams } from "./show-add-automation-element-dialog";
|
||||
@@ -216,10 +216,6 @@ class DialogAddAutomationElement
|
||||
// #endregion state
|
||||
|
||||
// #region queries
|
||||
|
||||
@query("ha-automation-add-from-target")
|
||||
private _targetPickerElement?: HaAutomationAddFromTarget;
|
||||
|
||||
@query("ha-automation-add-items")
|
||||
private _itemsListElement?: HTMLDivElement;
|
||||
|
||||
@@ -298,6 +294,14 @@ class DialogAddAutomationElement
|
||||
}
|
||||
);
|
||||
|
||||
// add initial dialog view state to history
|
||||
mainWindow.history.pushState(
|
||||
{
|
||||
dialogData: {},
|
||||
},
|
||||
""
|
||||
);
|
||||
|
||||
if (this._params?.type === "action") {
|
||||
this.hass.loadBackendTranslation("services");
|
||||
getServiceIcons(this.hass);
|
||||
@@ -318,7 +322,41 @@ class DialogAddAutomationElement
|
||||
this._bottomSheetMode = this._narrow;
|
||||
}
|
||||
|
||||
public closeDialog() {
|
||||
public closeDialog(historyState?: any) {
|
||||
// prevent closing when come from popstate event and root level isn't active
|
||||
if (
|
||||
this._open &&
|
||||
historyState &&
|
||||
(this._selectedTarget || this._selectedGroup)
|
||||
) {
|
||||
if (historyState.dialogData?.target) {
|
||||
this._selectedTarget = historyState.dialogData.target;
|
||||
this._getItemsByTarget();
|
||||
this._tab = "targets";
|
||||
return false;
|
||||
}
|
||||
if (historyState.dialogData?.group) {
|
||||
this._selectedCollectionIndex = historyState.dialogData.collectionIndex;
|
||||
this._selectedGroup = historyState.dialogData.group;
|
||||
this._tab = "groups";
|
||||
return false;
|
||||
}
|
||||
|
||||
// return to home on mobile
|
||||
if (this._narrow) {
|
||||
this._selectedTarget = undefined;
|
||||
this._selectedGroup = undefined;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// if dialog is closed, but root level isn't active, clean up history state
|
||||
if (mainWindow.history.state?.dialogData) {
|
||||
this._open = false;
|
||||
mainWindow.history.back();
|
||||
return false;
|
||||
}
|
||||
|
||||
this.removeKeyboardShortcuts();
|
||||
this._unsubscribe();
|
||||
if (this._params) {
|
||||
@@ -405,7 +443,7 @@ class DialogAddAutomationElement
|
||||
return html`
|
||||
<ha-bottom-sheet
|
||||
.open=${this._open}
|
||||
@closed=${this.closeDialog}
|
||||
@closed=${this._handleClosed}
|
||||
flexcontent
|
||||
>
|
||||
${this._renderContent()}
|
||||
@@ -417,7 +455,7 @@ class DialogAddAutomationElement
|
||||
<ha-wa-dialog
|
||||
width="large"
|
||||
.open=${this._open}
|
||||
@closed=${this.closeDialog}
|
||||
@closed=${this._handleClosed}
|
||||
flexcontent
|
||||
>
|
||||
${this._renderContent()}
|
||||
@@ -1659,11 +1697,7 @@ class DialogAddAutomationElement
|
||||
}
|
||||
|
||||
private _back() {
|
||||
if (this._selectedTarget) {
|
||||
this._targetPickerElement?.navigateBack();
|
||||
return;
|
||||
}
|
||||
this._selectedGroup = undefined;
|
||||
mainWindow.history.back();
|
||||
}
|
||||
|
||||
private _groupSelected(ev) {
|
||||
@@ -1675,6 +1709,16 @@ class DialogAddAutomationElement
|
||||
}
|
||||
this._selectedGroup = group.value;
|
||||
this._selectedCollectionIndex = ev.currentTarget.index;
|
||||
|
||||
mainWindow.history.pushState(
|
||||
{
|
||||
dialogData: {
|
||||
group: this._selectedGroup,
|
||||
collectionIndex: this._selectedCollectionIndex,
|
||||
},
|
||||
},
|
||||
""
|
||||
);
|
||||
requestAnimationFrame(() => {
|
||||
this._itemsListElement?.scrollTo(0, 0);
|
||||
});
|
||||
@@ -1704,6 +1748,14 @@ class DialogAddAutomationElement
|
||||
this._targetItems = undefined;
|
||||
this._loadItemsError = false;
|
||||
this._selectedTarget = ev.detail.value;
|
||||
mainWindow.history.pushState(
|
||||
{
|
||||
dialogData: {
|
||||
target: this._selectedTarget,
|
||||
},
|
||||
},
|
||||
""
|
||||
);
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
if (this._narrow) {
|
||||
@@ -1823,6 +1875,10 @@ class DialogAddAutomationElement
|
||||
this._tab = "targets";
|
||||
}
|
||||
|
||||
private _handleClosed() {
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
// #region interaction
|
||||
|
||||
// #region render helpers
|
||||
|
||||
@@ -1383,92 +1383,6 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
);
|
||||
}
|
||||
|
||||
public navigateBack() {
|
||||
if (!this.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const valueType = Object.keys(this.value)[0].replace("_id", "");
|
||||
const valueId = this.value[`${valueType}_id`];
|
||||
|
||||
if (
|
||||
valueType === "floor" ||
|
||||
valueType === "label" ||
|
||||
(!valueId &&
|
||||
(valueType === "device" ||
|
||||
valueType === "helper" ||
|
||||
valueType === "service" ||
|
||||
valueType === "area"))
|
||||
) {
|
||||
fireEvent(this, "value-changed", { value: undefined });
|
||||
return;
|
||||
}
|
||||
|
||||
if (valueType === "area") {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: { floor_id: this.areas[valueId].floor_id || undefined },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (valueType === "device") {
|
||||
if (
|
||||
!this.devices[valueId].area_id &&
|
||||
this.devices[valueId].entry_type === "service"
|
||||
) {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: { service_id: undefined },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
value: { area_id: this.devices[valueId].area_id || undefined },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (valueType === "entity" && valueId) {
|
||||
const deviceId = this.entities[valueId].device_id;
|
||||
if (deviceId) {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: { device_id: deviceId },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const areaId = this.entities[valueId].area_id;
|
||||
if (areaId) {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: { area_id: areaId },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const domain = valueId.split(".", 2)[0];
|
||||
const manifest = this.manifests ? this.manifests[domain] : undefined;
|
||||
if (manifest?.integration_type === "helper") {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: { [`helper_${domain}_id`]: undefined },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
fireEvent(this, "value-changed", {
|
||||
value: { [`entity_${domain}_id`]: undefined },
|
||||
});
|
||||
}
|
||||
|
||||
if (valueType.startsWith("helper_") || valueType.startsWith("entity_")) {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: {
|
||||
[`${valueType.startsWith("helper_") ? "helper" : "device"}_id`]:
|
||||
undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _expandHeight() {
|
||||
this._fullHeight = true;
|
||||
this.style.setProperty("--max-height", "none");
|
||||
|
||||
@@ -56,7 +56,11 @@ export const urlSyncMixin = <
|
||||
// if we are instead navigating forward, the dialogs are already closed
|
||||
closeLastDialog();
|
||||
}
|
||||
if ("dialog" in ev.state) {
|
||||
if ("dialogData" in ev.state) {
|
||||
// if we have dialog data we are closing a dialog with appended state
|
||||
// so dialog has the change to navigate back to the previous state
|
||||
closeLastDialog(ev.state);
|
||||
} else if ("dialog" in ev.state) {
|
||||
// coming to a dialog
|
||||
// the dialog stack must be empty in this case so this state should be cleaned up
|
||||
mainWindow.history.back();
|
||||
|
||||
Reference in New Issue
Block a user