diff --git a/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/json-string-comparison.function.ts b/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/json-string-comparison.function.ts index 7f0a1a008b..43529e6190 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/json-string-comparison.function.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/json-string-comparison.function.ts @@ -8,5 +8,5 @@ * Meaning no class instances can take part in this data. */ export function jsonStringComparison(a: unknown, b: unknown): boolean { - return JSON.stringify(a) === JSON.stringify(b); + return (a === undefined && b === undefined) || JSON.stringify(a) === JSON.stringify(b); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts index e05e9ced72..028a3ed887 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-element-manager.ts @@ -119,6 +119,10 @@ export class UmbBlockElementManager { + const newUrl = e.detail.url; + + if (this.#allowNavigateAway) { + return true; + } + + if (this._checkWillNavigateAway(newUrl) && this.getHasUnpersistedChanges()) { + /* Since ours modals are async while events are synchronous, we need to prevent the default behavior of the event, even if the modal hasn’t been resolved yet. + Once the modal is resolved (the user accepted to discard the changes and navigate away from the route), we will push a new history state. + This push will make the "willchangestate" event happen again and due to this somewhat "backward" behavior, + we set an "allowNavigateAway"-flag to prevent the "discard-changes" functionality from running in a loop.*/ + e.preventDefault(); + const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT).catch(() => undefined); + const modal = modalManager?.open(this, UMB_DISCARD_CHANGES_MODAL); + if (modal) { + try { + // navigate to the new url when discarding changes + await modal.onSubmit(); + this.#allowNavigateAway = true; + history.pushState({}, '', e.detail.url); + return true; + } catch { + return false; + } + } else { + console.error('No modal manager found!'); + } + } + + return true; + }; + + /** + * Check if the workspace is about to navigate away. + * @protected + * @param {string} newUrl The new url that the workspace is navigating to. + * @returns { boolean} true if the workspace is navigating away. + * @memberof UmbEntityWorkspaceContextBase + */ + protected _checkWillNavigateAway(newUrl: string): boolean { + return !newUrl.includes(this.routes.getActiveLocalPath()); + } + setEditorSize(editorSize: UUIModalSidebarSize) { this.#modalContext?.setModalSize(editorSize); } @@ -236,6 +283,7 @@ export class UmbBlockWorkspaceContext