Skip to content

Commit

Permalink
Simplify dialog navigation to fix back btn
Browse files Browse the repository at this point in the history
  • Loading branch information
MindFreeze committed Dec 9, 2024
1 parent 1393a3a commit d993b31
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 127 deletions.
6 changes: 0 additions & 6 deletions src/common/navigate.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { historyPromise } from "../state/url-sync-mixin";
import { fireEvent } from "./dom/fire_event";
import { mainWindow } from "./dom/get_main_window";

Expand All @@ -17,11 +16,6 @@ export interface NavigateOptions {
export const navigate = (path: string, options?: NavigateOptions) => {
const replace = options?.replace || false;

if (historyPromise) {
historyPromise.then(() => navigate(path, options));
return;
}

if (__DEMO__) {
if (replace) {
mainWindow.history.replaceState(
Expand Down
5 changes: 4 additions & 1 deletion src/dialogs/generic/show-dialog-box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const showDialogHelper = (
extra?: {
confirmation?: DialogBoxParams["confirmation"];
prompt?: DialogBoxParams["prompt"];
alert?: boolean;
}
) =>
new Promise((resolve) => {
Expand All @@ -71,13 +72,15 @@ const showDialogHelper = (
}
},
},
// simple alerts don't need to be added to history
addHistory: extra?.alert ? false : undefined,
});
});

export const showAlertDialog = (
element: HTMLElement,
dialogParams: AlertDialogParams
) => showDialogHelper(element, dialogParams);
) => showDialogHelper(element, dialogParams, { alert: true });

export const showConfirmationDialog = (
element: HTMLElement,
Expand Down
53 changes: 12 additions & 41 deletions src/dialogs/make-dialog-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export interface DialogClosedParams {
export interface DialogState {
dialog: string;
open: boolean;
oldState: null | DialogState;
dialogParams?: unknown;
nextState?: DialogState;
}

interface LoadedDialogInfo {
Expand Down Expand Up @@ -82,42 +82,21 @@ export const showDialog = async (
};
}

// Get the focus targets after the dialog closes, but keep the original if dialog is being replaced
if (mainWindow.history.state?.replaced) {
LOADED[dialogTag].closedFocusTargets =
LOADED[mainWindow.history.state.dialog].closedFocusTargets;
delete LOADED[mainWindow.history.state.dialog].closedFocusTargets;
} else {
LOADED[dialogTag].closedFocusTargets = ancestorsWithProperty(
deepActiveElement(),
FOCUS_TARGET
);
}
LOADED[dialogTag].closedFocusTargets = ancestorsWithProperty(
deepActiveElement(),
FOCUS_TARGET
);

if (addHistory) {
mainWindow.history.replaceState(
{
dialog: dialogTag,
open: false,
oldState:
mainWindow.history.state?.open &&
mainWindow.history.state?.dialog !== dialogTag
? mainWindow.history.state
: null,
},
""
);
const { state } = mainWindow.history;
// if the same dialog is already open, don't push state
if (addHistory && state?.dialog !== dialogTag) {
const nextState = { dialog: dialogTag, dialogParams, open: true };
mainWindow.history.replaceState({ ...state, nextState }, "");
try {
mainWindow.history.pushState(
{ dialog: dialogTag, dialogParams: dialogParams, open: true },
""
);
mainWindow.history.pushState(nextState, "");
} catch (err: any) {
// dialogParams could not be cloned, probably contains callback
mainWindow.history.pushState(
{ dialog: dialogTag, dialogParams: null, open: true },
""
);
mainWindow.history.pushState({ ...nextState, dialogParams: null }, "");
}
}

Expand All @@ -132,14 +111,6 @@ export const showDialog = async (
return true;
};

export const replaceDialog = (dialogElement: HassDialog) => {
mainWindow.history.replaceState(
{ ...mainWindow.history.state, replaced: true },
""
);
dialogElement.removeEventListener("dialog-closed", _handleClosedFocus);
};

export const closeDialog = async (dialogTag: string): Promise<boolean> => {
if (!(dialogTag in LOADED)) {
return true;
Expand Down
103 changes: 24 additions & 79 deletions src/state/url-sync-mixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ import type { Constructor } from "../types";

const DEBUG = false;

// eslint-disable-next-line import/no-mutable-exports
export let historyPromise: Promise<void> | undefined;

let historyResolve: undefined | (() => void);

export const urlSyncMixin = <
T extends Constructor<ReactiveElement & ProvideHassElement>,
>(
Expand All @@ -26,8 +21,6 @@ export const urlSyncMixin = <
__DEMO__
? superClass
: class extends superClass {
private _ignoreNextPopState = false;

public connectedCallback(): void {
super.connectedCallback();
if (mainWindow.history.length === 1) {
Expand Down Expand Up @@ -72,105 +65,57 @@ export const urlSyncMixin = <
}
// If not closed by navigating back, and not a new dialog is open, remove the open state from history
if (
mainWindow.history.length > 1 &&
mainWindow.history.state?.open &&
mainWindow.history.state?.dialog === ev.detail.dialog
) {
if (DEBUG) {
console.log("remove state", ev.detail.dialog);
}
if (mainWindow.history.length) {
this._ignoreNextPopState = true;
historyPromise = new Promise((resolve) => {
historyResolve = () => {
resolve();
historyResolve = undefined;
historyPromise = undefined;
};
mainWindow.history.back();
});
}
mainWindow.history.back();
}
};

private _popstateChangeListener = (ev: PopStateEvent) => {
if (this._ignoreNextPopState) {
if (
history.length &&
(ev.state?.oldState?.replaced ||
ev.state?.oldState?.dialogParams === null)
) {
// if the previous dialog was replaced, or we could not copy the params, and the current dialog is closed, we should also remove the previous dialog from history
if (DEBUG) {
console.log("remove old state", ev.state.oldState);
}
mainWindow.history.back();
return;
}
if (ev.state) {
if (DEBUG) {
console.log("ignore popstate");
console.log("popstate", ev);
}
this._ignoreNextPopState = false;
if (historyResolve) {
historyResolve();
if ("nextState" in ev.state) {
// coming back from a dialog
this._closeDialog(ev.state.nextState);
}
return;
}
if (ev.state && "dialog" in ev.state) {
if (DEBUG) {
console.log("popstate", ev);
if ("dialog" in ev.state) {
// coming to a dialog
this._handleDialogStateChange(ev.state);
}
this._handleDialogStateChange(ev.state);
}
if (historyResolve) {
historyResolve();
}
};

private async _handleDialogStateChange(state: DialogState) {
if (DEBUG) {
console.log("handle state", state);
}
if (!state.open) {
const closed = await closeDialog(state.dialog);
if (!closed) {
if (DEBUG) {
console.log("dialog could not be closed");
}
// dialog could not be closed, push state again
mainWindow.history.pushState(
{
dialog: state.dialog,
open: true,
dialogParams: null,
oldState: null,
},
""
);
return;
}
if (state.oldState) {
if (DEBUG) {
console.log("handle old state");
}
this._handleDialogStateChange(state.oldState);
}
return;
}
let shown = false;
if (state.open && state.dialogParams !== null) {
shown = await showDialog(
await showDialog(
this,
this.shadowRoot!,
state.dialog,
state.dialogParams
state.dialogParams,
undefined,
false
);
}
if (!shown) {
// can't open dialog, update state
mainWindow.history.replaceState(
{ ...mainWindow.history.state, open: false },
""
);
}

private async _closeDialog(dialogState: DialogState) {
const closed = await closeDialog(dialogState.dialog);
if (!closed) {
if (DEBUG) {
console.log("dialog could not be closed");
}
// dialog could not be closed, push state again
mainWindow.history.pushState(dialogState, "");
}
}
};

0 comments on commit d993b31

Please sign in to comment.