Feature: discard changes for block workspace (#18930)

* make getHasUnpersistedChanges public

* Discard changes impl for Block Workspace
This commit is contained in:
Niels Lyngsø
2025-04-07 18:42:31 +02:00
committed by GitHub
parent 9c03422222
commit 4c9084238c
3 changed files with 59 additions and 2 deletions

View File

@@ -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);
}

View File

@@ -119,6 +119,10 @@ export class UmbBlockElementManager<LayoutDataType extends UmbBlockLayoutBaseMod
return this.#data.getCurrent();
}
setPersistedData(data: UmbBlockDataModel | undefined) {
this.#data.setPersisted(data);
}
/**
* Check if there are unpersisted changes.
* @returns { boolean } true if there are unpersisted changes.

View File

@@ -16,7 +16,7 @@ import {
observeMultiple,
} from '@umbraco-cms/backoffice/observable-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UMB_MODAL_CONTEXT } from '@umbraco-cms/backoffice/modal';
import { UMB_DISCARD_CHANGES_MODAL, UMB_MODAL_CONTEXT, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
import { decodeFilePath, UmbReadOnlyVariantStateManager } from '@umbraco-cms/backoffice/utils';
import {
UMB_BLOCK_ENTRIES_CONTEXT,
@@ -81,6 +81,8 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
const manifest = workspaceArgs.manifest;
this.#entityType = manifest.meta?.entityType;
window.addEventListener('willchangestate', this.#onWillNavigate);
this.addValidationContext(this.content.validation);
this.addValidationContext(this.settings.validation);
@@ -223,6 +225,51 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
);
}
#allowNavigateAway = false;
#onWillNavigate = async (e: CustomEvent) => {
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 hasnt 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<LayoutDataType extends UmbBlockLayoutBaseM
this.#initialSettings = undefined;
this.content.resetState();
this.settings.resetState();
this.#allowNavigateAway = false;
this.removeUmbControllerByAlias(UmbWorkspaceIsNewRedirectControllerAlias);
}
@@ -471,6 +519,10 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
}
const settingsData = this.settings.getData();
this.content.setPersistedData(contentData);
if (settingsData) {
this.settings.setPersistedData(settingsData);
}
if (!this.#liveEditingMode) {
if (this.getIsNew() === true) {
@@ -542,6 +594,7 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
public override destroy(): void {
super.destroy();
window.removeEventListener('willchangestate', this.#onWillNavigate);
this.#layout?.destroy();
this.#name?.destroy();
this.#layout = undefined as any;