Merge branch 'v15/feature/discard-changes' into v15/fix/extend-entity-detail-workspace-base

This commit is contained in:
Mads Rasmussen
2024-09-25 09:17:40 +02:00
6 changed files with 106 additions and 70 deletions

View File

@@ -2,8 +2,8 @@ import type { UmbController } from '@umbraco-cms/backoffice/controller-api';
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
export interface UmbWorkspaceDataManager<ModelType extends UmbEntityModel> extends UmbController {
getPersistedData(): ModelType | undefined;
getCurrentData(): ModelType | undefined;
setPersistedData(data: ModelType | undefined): void;
setCurrentData(data: ModelType | undefined): void;
getPersisted(): ModelType | undefined;
getCurrent(): ModelType | undefined;
setPersisted(data: ModelType | undefined): void;
setCurrent(data: ModelType | undefined): void;
}

View File

@@ -68,11 +68,11 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
}
getData() {
return this._data.getCurrentData();
return this._data.getCurrent();
}
getUnique() {
return this._data.getCurrentData()?.unique;
return this._data.getCurrent()?.unique;
}
async load(unique: string) {
@@ -85,8 +85,8 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
if (data) {
this.setIsNew(false);
this._data.setPersistedData(data);
this._data.setCurrentData(data);
this._data.setPersisted(data);
this._data.setCurrent(data);
}
return response;
@@ -98,7 +98,7 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
async submit() {
await this.#init;
const currentData = this._data.getCurrentData();
const currentData = this._data.getCurrent();
if (!currentData) {
throw new Error('Data is not set');
@@ -115,7 +115,7 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
throw error?.message ?? 'Repository did not return data after create.';
}
this._data.setPersistedData(data);
this._data.setPersisted(data);
// TODO: this might not be the right place to alert the tree, but it works for now
const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
@@ -131,7 +131,7 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
throw error?.message ?? 'Repository did not return data after create.';
}
this._data.setPersistedData(data);
this._data.setPersisted(data);
const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
const event = new UmbRequestReloadStructureForEntityEvent({
@@ -156,8 +156,8 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
data = { ...data, ...this.modalContext.data.preset };
}
this.setIsNew(true);
this._data.setPersistedData(data);
this._data.setCurrentData(data);
this._data.setPersisted(data);
this._data.setCurrent(data);
return data;
}
@@ -182,7 +182,7 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
#onWillNavigate = async (e: CustomEvent) => {
const newUrl = e.detail.url;
if (this._checkWillNavigateAway(newUrl) && this._data.hasUnpersistedChanges()) {
if (this._checkWillNavigateAway(newUrl) && this._data.getHasUnpersistedChanges()) {
e.preventDefault();
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
const modal = modalManager.open(this, UMB_DISCARD_CHANGES_MODAL);
@@ -191,7 +191,7 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
// navigate to the new url when discarding changes
await modal.onSubmit();
// Reset the current data so we don't end in a endless loop of asking to discard changes.
this._data.resetCurrentData();
this._data.resetCurrent();
history.pushState({}, '', e.detail.url);
return true;
} catch {
@@ -204,7 +204,7 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
override resetState() {
super.resetState();
this._data.clearData();
this._data.clear();
}
#checkIfInitialized() {

View File

@@ -2,8 +2,14 @@ import type { UmbWorkspaceDataManager } from '../data-manager/workspace-data-man
import { jsonStringComparison, UmbObjectState, type MappingFunction } from '@umbraco-cms/backoffice/observable-api';
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
/**
* Manages the workspace data for an entity.
* @class UmbEntityWorkspaceDataManager
* @augments {UmbControllerBase}
* @implements {UmbWorkspaceDataManager<ModelType>}
* @template ModelType
*/
export class UmbEntityWorkspaceDataManager<ModelType extends UmbEntityModel>
extends UmbControllerBase
implements UmbWorkspaceDataManager<ModelType>
@@ -11,22 +17,24 @@ export class UmbEntityWorkspaceDataManager<ModelType extends UmbEntityModel>
#persisted = new UmbObjectState<ModelType | undefined>(undefined);
#current = new UmbObjectState<ModelType | undefined>(undefined);
/**
* Observable of the persisted data
* @memberof UmbEntityWorkspaceDataManager
*/
public readonly persisted = this.#persisted.asObservable();
/**
* Observable of the current data
* @memberof UmbEntityWorkspaceDataManager
*/
public readonly current = this.#current.asObservable();
constructor(host: UmbControllerHost) {
super(host);
}
createObservablePart<ReturnType>(mappingFunction: MappingFunction<ModelType | undefined, ReturnType>) {
return this.#current.asObservablePart(mappingFunction);
}
/**
* Gets persisted data
* @returns {(ModelType | undefined)}
* @memberof UmbSubmittableWorkspaceDataManager
*/
getPersistedData() {
getPersisted() {
return this.#persisted.getValue();
}
@@ -35,7 +43,7 @@ export class UmbEntityWorkspaceDataManager<ModelType extends UmbEntityModel>
* @param {(ModelType | undefined)} data
* @memberof UmbSubmittableWorkspaceDataManager
*/
setPersistedData(data: ModelType | undefined) {
setPersisted(data: ModelType | undefined) {
this.#persisted.setValue(data);
}
@@ -44,16 +52,27 @@ export class UmbEntityWorkspaceDataManager<ModelType extends UmbEntityModel>
* @param {Partial<ModelType>} partialData
* @memberof UmbSubmittableWorkspaceDataManager
*/
updatePersistedData(partialData: Partial<ModelType>) {
updatePersisted(partialData: Partial<ModelType>) {
this.#persisted.update(partialData);
}
/**
* Creates an observable part of the persisted data
* @template ReturnType
* @param {(MappingFunction<ModelType | undefined, ReturnType>)} mappingFunction
* @returns {*}
* @memberof UmbEntityWorkspaceDataManager
*/
createObservablePartOfPersisted<ReturnType>(mappingFunction: MappingFunction<ModelType | undefined, ReturnType>) {
return this.#persisted.asObservablePart(mappingFunction);
}
/**
* Gets the current data
* @returns {(ModelType | undefined)}
* @memberof UmbSubmittableWorkspaceDataManager
*/
getCurrentData() {
getCurrent() {
return this.#current.getValue();
}
@@ -62,7 +81,7 @@ export class UmbEntityWorkspaceDataManager<ModelType extends UmbEntityModel>
* @param {(ModelType | undefined)} data
* @memberof UmbSubmittableWorkspaceDataManager
*/
setCurrentData(data: ModelType | undefined) {
setCurrent(data: ModelType | undefined) {
this.#current.setValue(data);
}
@@ -71,16 +90,27 @@ export class UmbEntityWorkspaceDataManager<ModelType extends UmbEntityModel>
* @param {Partial<ModelType>} partialData
* @memberof UmbSubmittableWorkspaceDataManager
*/
updateCurrentData(partialData: Partial<ModelType>) {
updateCurrent(partialData: Partial<ModelType>) {
this.#current.update(partialData);
}
/**
* Creates an observable part of the current data
* @template ReturnType
* @param {(MappingFunction<ModelType | undefined, ReturnType>)} mappingFunction
* @returns {*}
* @memberof UmbEntityWorkspaceDataManager
*/
createObservablePartOfCurrent<ReturnType>(mappingFunction: MappingFunction<ModelType | undefined, ReturnType>) {
return this.#current.asObservablePart(mappingFunction);
}
/**
* Checks if there are unpersisted changes
* @returns {*}
* @memberof UmbSubmittableWorkspaceDataManager
*/
hasUnpersistedChanges() {
getHasUnpersistedChanges() {
const persisted = this.#persisted.getValue();
const current = this.#current.getValue();
return jsonStringComparison(persisted, current) === false;
@@ -90,7 +120,7 @@ export class UmbEntityWorkspaceDataManager<ModelType extends UmbEntityModel>
* Resets the current data to the persisted data
* @memberof UmbSubmittableWorkspaceDataManager
*/
resetCurrentData() {
resetCurrent() {
this.#current.setValue(this.#persisted.getValue());
}
@@ -98,7 +128,7 @@ export class UmbEntityWorkspaceDataManager<ModelType extends UmbEntityModel>
* Clears the data
* @memberof UmbSubmittableWorkspaceDataManager
*/
clearData() {
clear() {
this.#persisted.setValue(undefined);
this.#current.setValue(undefined);
}

View File

@@ -45,12 +45,12 @@ export class UmbDataTypeWorkspaceContext
extends UmbEntityDetailWorkspaceContextBase<EntityType, UmbDataTypeDetailRepository>
implements UmbInvariantDatasetWorkspaceContext, UmbRoutableWorkspaceContext
{
readonly name = this._data.createObservablePart((data) => data?.name);
readonly unique = this._data.createObservablePart((data) => data?.unique);
readonly entityType = this._data.createObservablePart((data) => data?.entityType);
readonly name = this._data.createObservablePartOfCurrent((data) => data?.name);
readonly unique = this._data.createObservablePartOfCurrent((data) => data?.unique);
readonly entityType = this._data.createObservablePartOfCurrent((data) => data?.entityType);
readonly propertyEditorUiAlias = this._data.createObservablePart((data) => data?.editorUiAlias);
readonly propertyEditorSchemaAlias = this._data.createObservablePart((data) => data?.editorAlias);
readonly propertyEditorUiAlias = this._data.createObservablePartOfCurrent((data) => data?.editorUiAlias);
readonly propertyEditorSchemaAlias = this._data.createObservablePartOfCurrent((data) => data?.editorAlias);
#properties = new UmbArrayState<PropertyEditorSettingsProperty>([], (x) => x.alias).sortBy(
(a, b) => (a.weight || 0) - (b.weight || 0),
@@ -239,7 +239,7 @@ export class UmbDataTypeWorkspaceContext
#transferConfigDefaultData() {
if (!this.#propertyEditorSchemaSettingsDefaultData || !this.#propertyEditorUISettingsDefaultData) return;
const data = this._data.getCurrentData();
const data = this._data.getCurrent();
if (!data) return;
this.#settingsDefaultData = [
@@ -248,8 +248,8 @@ export class UmbDataTypeWorkspaceContext
] satisfies Array<UmbDataTypePropertyModel>;
// We check for satisfied type, because we will be directly transferring them to become value. Future note, if they are not satisfied, we need to transfer alias and value. [NL]
this._data.updatePersistedData({ values: this.#settingsDefaultData });
this._data.updateCurrentData({ values: this.#settingsDefaultData });
this._data.updatePersisted({ values: this.#settingsDefaultData });
this._data.updateCurrent({ values: this.#settingsDefaultData });
}
public getPropertyDefaultValue(alias: string) {
@@ -261,27 +261,27 @@ export class UmbDataTypeWorkspaceContext
}
getName() {
return this._data.getCurrentData()?.name;
return this._data.getCurrent()?.name;
}
setName(name: string | undefined) {
this._data.updateCurrentData({ name });
this._data.updateCurrent({ name });
}
getPropertyEditorSchemaAlias() {
return this._data.getCurrentData()?.editorAlias;
return this._data.getCurrent()?.editorAlias;
}
setPropertyEditorSchemaAlias(alias?: string) {
this._data.updateCurrentData({ editorAlias: alias });
this._data.updateCurrent({ editorAlias: alias });
}
getPropertyEditorUiAlias() {
return this._data.getCurrentData()?.editorUiAlias;
return this._data.getCurrent()?.editorUiAlias;
}
setPropertyEditorUiAlias(alias?: string) {
this._data.updateCurrentData({ editorUiAlias: alias });
this._data.updateCurrent({ editorUiAlias: alias });
}
/**
@@ -292,14 +292,14 @@ export class UmbDataTypeWorkspaceContext
*/
async propertyValueByAlias<ReturnType = unknown>(propertyAlias: string) {
await this._getDataPromise;
return this._data.createObservablePart(
return this._data.createObservablePartOfCurrent(
(data) => data?.values?.find((x) => x.alias === propertyAlias)?.value as ReturnType,
);
}
getPropertyValue<ReturnType = unknown>(propertyAlias: string) {
return (
(this._data.getCurrentData()?.values?.find((x) => x.alias === propertyAlias)?.value as ReturnType) ??
(this._data.getCurrent()?.values?.find((x) => x.alias === propertyAlias)?.value as ReturnType) ??
(this.getPropertyDefaultValue(propertyAlias) as ReturnType)
);
}
@@ -309,11 +309,11 @@ export class UmbDataTypeWorkspaceContext
await this._getDataPromise;
const entry = { alias: alias, value: value };
const currentData = this._data.getCurrentData();
const currentData = this._data.getCurrent();
if (currentData) {
// TODO: make a partial update method for array of data, (idea/concept, use if this case is getting common)
const newDataSet = appendToFrozenArray(currentData.values || [], entry, (x) => x.alias);
this._data.updateCurrentData({ values: newDataSet });
this._data.updateCurrent({ values: newDataSet });
}
}

View File

@@ -19,8 +19,8 @@ export class UmbLanguageWorkspaceContext
readonly data = this._data.current;
readonly unique = this._data.createObservablePart((data) => data?.unique);
readonly name = this._data.createObservablePart((data) => data?.name);
readonly unique = this._data.createObservablePartOfCurrent((data) => data?.unique);
readonly name = this._data.createObservablePartOfCurrent((data) => data?.name);
constructor(host: UmbControllerHost) {
super(host, {
@@ -55,23 +55,23 @@ export class UmbLanguageWorkspaceContext
}
setName(name: string) {
this._data.updateCurrentData({ name });
this._data.updateCurrent({ name });
}
setCulture(unique: string) {
this._data.updateCurrentData({ unique });
this._data.updateCurrent({ unique });
}
setMandatory(isMandatory: boolean) {
this._data.updateCurrentData({ isMandatory });
this._data.updateCurrent({ isMandatory });
}
setDefault(isDefault: boolean) {
this._data.updateCurrentData({ isDefault });
this._data.updateCurrent({ isDefault });
}
setFallbackCulture(unique: string) {
this._data.updateCurrentData({ fallbackIsoCode: unique });
this._data.updateCurrent({ fallbackIsoCode: unique });
}
}

View File

@@ -22,14 +22,20 @@ export class UmbUserWorkspaceContext
public readonly configRepository = new UmbUserConfigRepository(this);
readonly data = this._data.current;
readonly state = this._data.createObservablePart((x) => x?.state);
readonly unique = this._data.createObservablePart((x) => x?.unique);
readonly kind = this._data.createObservablePart((x) => x?.kind);
readonly userGroupUniques = this._data.createObservablePart((x) => x?.userGroupUniques || []);
readonly documentStartNodeUniques = this._data.createObservablePart((data) => data?.documentStartNodeUniques || []);
readonly hasDocumentRootAccess = this._data.createObservablePart((data) => data?.hasDocumentRootAccess || false);
readonly mediaStartNodeUniques = this._data.createObservablePart((data) => data?.mediaStartNodeUniques || []);
readonly hasMediaRootAccess = this._data.createObservablePart((data) => data?.hasMediaRootAccess || false);
readonly state = this._data.createObservablePartOfCurrent((x) => x?.state);
readonly unique = this._data.createObservablePartOfCurrent((x) => x?.unique);
readonly kind = this._data.createObservablePartOfCurrent((x) => x?.kind);
readonly userGroupUniques = this._data.createObservablePartOfCurrent((x) => x?.userGroupUniques || []);
readonly documentStartNodeUniques = this._data.createObservablePartOfCurrent(
(data) => data?.documentStartNodeUniques || [],
);
readonly hasDocumentRootAccess = this._data.createObservablePartOfCurrent(
(data) => data?.hasDocumentRootAccess || false,
);
readonly mediaStartNodeUniques = this._data.createObservablePartOfCurrent(
(data) => data?.mediaStartNodeUniques || [],
);
readonly hasMediaRootAccess = this._data.createObservablePartOfCurrent((data) => data?.hasMediaRootAccess || false);
#calculatedStartNodes = new UmbObjectState<UmbUserStartNodesModel | undefined>(undefined);
readonly calculatedStartNodes = this.#calculatedStartNodes.asObservable();
@@ -82,15 +88,15 @@ export class UmbUserWorkspaceContext
history.pushState(null, '', 'section/user-management');
return;
}
this._data.updateCurrentData({ state: user.state, avatarUrls: user.avatarUrls });
this._data.updateCurrent({ state: user.state, avatarUrls: user.avatarUrls });
}
getState(): UmbUserStateEnum | null | undefined {
return this._data.getCurrentData()?.state;
return this._data.getCurrent()?.state;
}
updateProperty<PropertyName extends keyof EntityType>(propertyName: PropertyName, value: EntityType[PropertyName]) {
this._data.updateCurrentData({ [propertyName]: value });
this._data.updateCurrent({ [propertyName]: value });
}
// TODO: implement upload progress