Skip to content

Commit

Permalink
Merge pull request #18154 from umbraco/v15/feature/enable-document-ro…
Browse files Browse the repository at this point in the history
…llback-as-entity-action

Rollback as entity action + Picker data updates
  • Loading branch information
nielslyngsoe authored Jan 31, 2025
2 parents 0480a32 + 9896119 commit 7c5c3fd
Show file tree
Hide file tree
Showing 8 changed files with 393 additions and 166 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import type { UmbModalToken } from '@umbraco-cms/backoffice/modal';
import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import {
UmbEntityUpdatedEvent,
UmbRequestReloadChildrenOfEntityEvent,
UmbRequestReloadStructureForEntityEvent,
} from '@umbraco-cms/backoffice/entity-action';
Expand Down Expand Up @@ -709,13 +710,21 @@ export abstract class UmbContentDetailWorkspaceContextBase<
);
this._data.setCurrent(newCurrentData);

const unique = this.getUnique()!;
const entityType = this.getEntityType();

const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
const event = new UmbRequestReloadStructureForEntityEvent({
entityType: this.getEntityType(),
unique: this.getUnique()!,
const structureEvent = new UmbRequestReloadStructureForEntityEvent({ unique, entityType });
eventContext.dispatchEvent(structureEvent);

const updatedEvent = new UmbEntityUpdatedEvent({
unique,
entityType,
eventUnique: this._workspaceEventUnique,
});

eventContext.dispatchEvent(event);
eventContext.dispatchEvent(updatedEvent);

this._closeModal();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { UmbControllerEvent } from '@umbraco-cms/backoffice/controller-api';
import type { UmbEntityModel } from '@umbraco-cms/backoffice/entity';

// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface UmbEntityActionEventArgs extends UmbEntityModel {}
export interface UmbEntityActionEventArgs extends UmbEntityModel {
eventUnique?: string;
}

export class UmbEntityActionEvent<
ArgsType extends UmbEntityActionEventArgs = UmbEntityActionEventArgs,
Expand All @@ -21,4 +23,8 @@ export class UmbEntityActionEvent<
getUnique(): string | null {
return this._args.unique;
}

getEventUnique(): string | undefined {
return this._args.eventUnique;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { UmbEntityActionEventArgs } from './entity-action.event.js';
import { UmbEntityActionEvent } from './entity-action.event.js';

export class UmbEntityUpdatedEvent extends UmbEntityActionEvent {
static readonly TYPE = 'entity-updated';

constructor(args: UmbEntityActionEventArgs) {
super(UmbEntityUpdatedEvent.TYPE, args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export * from './constants.js';
export * from './entity-action-base.js';
export * from './entity-action-list.element.js';
export * from './entity-action.event.js';
export * from './entity-updated.event.js';
export type * from './types.js';

export { UmbRequestReloadStructureForEntityEvent } from './request-reload-structure-for-entity.event.js';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { type ManifestRepository, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { UmbExtensionApiInitializer } from '@umbraco-cms/backoffice/extension-api';
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
import { UmbEntityUpdatedEvent } from '@umbraco-cms/backoffice/entity-action';

const ObserveRepositoryAlias = Symbol();

Expand All @@ -14,6 +16,7 @@ export class UmbRepositoryItemsManager<ItemType extends { unique: string }> exte

#init: Promise<unknown>;
#currentRequest?: Promise<unknown>;
#eventContext?: typeof UMB_ACTION_EVENT_CONTEXT.TYPE;

// the init promise is used externally for recognizing when the manager is ready.
public get init() {
Expand Down Expand Up @@ -70,6 +73,20 @@ export class UmbRepositoryItemsManager<ItemType extends { unique: string }> exte
},
null,
);

this.consumeContext(UMB_ACTION_EVENT_CONTEXT, (context) => {
this.#eventContext = context;

this.#eventContext.removeEventListener(
UmbEntityUpdatedEvent.TYPE,
this.#onEntityUpdatedEvent as unknown as EventListener,
);

this.#eventContext.addEventListener(
UmbEntityUpdatedEvent.TYPE,
this.#onEntityUpdatedEvent as unknown as EventListener,
);
});
}

getUniques(): Array<string> {
Expand Down Expand Up @@ -122,6 +139,25 @@ export class UmbRepositoryItemsManager<ItemType extends { unique: string }> exte
}
}

async #reloadItem(unique: string): Promise<void> {
await this.#init;
if (!this.repository) throw new Error('Repository is not initialized');

const { data } = await this.repository.requestItems([unique]);

if (data) {
const items = this.getItems();
const item = items.find((item) => this.#getUnique(item) === unique);

if (item) {
const index = items.indexOf(item);
const newItems = [...items];
newItems[index] = data[0];
this.#items.setValue(this.#sortByUniques(newItems));
}
}
}

#sortByUniques(data: Array<ItemType>): Array<ItemType> {
const uniques = this.getUniques();
return [...data].sort((a, b) => {
Expand All @@ -130,4 +166,25 @@ export class UmbRepositoryItemsManager<ItemType extends { unique: string }> exte
return aIndex - bIndex;
});
}

#onEntityUpdatedEvent = (event: UmbEntityUpdatedEvent) => {
const eventUnique = event.getUnique();

const items = this.getItems();
if (items.length === 0) return;

// Ignore events if the entity is not in the list of items.
const item = items.find((item) => this.#getUnique(item) === eventUnique);
if (!item) return;

this.#reloadItem(item.unique);
};

override destroy(): void {
this.#eventContext?.removeEventListener(
UmbEntityUpdatedEvent.TYPE,
this.#onEntityUpdatedEvent as unknown as EventListener,
);
super.destroy();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { UmbEntityContext, type UmbEntityModel, type UmbEntityUnique } from '@um
import { UMB_DISCARD_CHANGES_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api';
import {
UmbEntityUpdatedEvent,
UmbRequestReloadChildrenOfEntityEvent,
UmbRequestReloadStructureForEntityEvent,
} from '@umbraco-cms/backoffice/entity-action';
Expand All @@ -15,6 +16,7 @@ import { umbExtensionsRegistry, type ManifestRepository } from '@umbraco-cms/bac
import type { UmbDetailRepository } from '@umbraco-cms/backoffice/repository';
import { UmbStateManager } from '@umbraco-cms/backoffice/utils';
import { UmbValidationContext } from '@umbraco-cms/backoffice/validation';
import { UmbId } from '@umbraco-cms/backoffice/id';

const LOADING_STATE_UNIQUE = 'umbLoadingEntityDetail';

Expand Down Expand Up @@ -45,6 +47,8 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
protected _getDataPromise?: Promise<any>;
protected _detailRepository?: DetailRepositoryType;

#eventContext?: typeof UMB_ACTION_EVENT_CONTEXT.TYPE;

#parent = new UmbObjectState<{ entityType: string; unique: UmbEntityUnique } | undefined>(undefined);
public readonly parentUnique = this.#parent.asObservablePart((parent) => (parent ? parent.unique : undefined));
public readonly parentEntityType = this.#parent.asObservablePart((parent) =>
Expand Down Expand Up @@ -85,6 +89,19 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
window.addEventListener('willchangestate', this.#onWillNavigate);
this.#observeRepository(args.detailRepositoryAlias);
this.addValidationContext(this.validationContext);

this.consumeContext(UMB_ACTION_EVENT_CONTEXT, (context) => {
this.#eventContext = context;

this.#eventContext.removeEventListener(
UmbEntityUpdatedEvent.TYPE,
this.#onEntityUpdatedEvent as unknown as EventListener,
);
this.#eventContext.addEventListener(
UmbEntityUpdatedEvent.TYPE,
this.#onEntityUpdatedEvent as unknown as EventListener,
);
});
}

/**
Expand Down Expand Up @@ -307,13 +324,21 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
this._data.setPersisted(data);
this._data.setCurrent(data);

const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
const event = new UmbRequestReloadStructureForEntityEvent({
unique: this.getUnique()!,
entityType: this.getEntityType(),
const unique = this.getUnique()!;
const entityType = this.getEntityType();

const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
const event = new UmbRequestReloadStructureForEntityEvent({ unique, entityType });

eventContext.dispatchEvent(event);

const updatedEvent = new UmbEntityUpdatedEvent({
unique,
entityType,
eventUnique: this._workspaceEventUnique,
});

actionEventContext.dispatchEvent(event);
eventContext.dispatchEvent(updatedEvent);
}

#allowNavigateAway = false;
Expand Down Expand Up @@ -396,8 +421,30 @@ export abstract class UmbEntityDetailWorkspaceContextBase<
}
}

// Discriminator to identify events from this workspace context
protected readonly _workspaceEventUnique = UmbId.new();

#onEntityUpdatedEvent = (event: UmbEntityUpdatedEvent) => {
const eventEntityUnique = event.getUnique();
const eventEntityType = event.getEntityType();
const eventDiscriminator = event.getEventUnique();

// Ignore events for other entities
if (eventEntityType !== this.getEntityType()) return;
if (eventEntityUnique !== this.getUnique()) return;

// Ignore events from this workspace so we don't reload the data twice. Ex saving this workspace
if (eventDiscriminator === this._workspaceEventUnique) return;

this.reload();
};

public override destroy(): void {
window.removeEventListener('willchangestate', this.#onWillNavigate);
this.#eventContext?.removeEventListener(
UmbEntityUpdatedEvent.TYPE,
this.#onEntityUpdatedEvent as unknown as EventListener,
);
this._detailRepository?.destroy();
this.#entityContext.destroy();
super.destroy();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
UMB_USER_PERMISSION_DOCUMENT_ROLLBACK,
UMB_DOCUMENT_ENTITY_TYPE,
UMB_DOCUMENT_WORKSPACE_ALIAS,
} from '../../constants.js';
import { UMB_WORKSPACE_CONDITION_ALIAS } from '@umbraco-cms/backoffice/workspace';
import { UMB_USER_PERMISSION_DOCUMENT_ROLLBACK, UMB_DOCUMENT_ENTITY_TYPE } from '../../constants.js';
import { UMB_ENTITY_IS_NOT_TRASHED_CONDITION_ALIAS } from '@umbraco-cms/backoffice/recycle-bin';

export const manifests: Array<UmbExtensionManifest> = [
Expand All @@ -12,7 +7,7 @@ export const manifests: Array<UmbExtensionManifest> = [
kind: 'default',
alias: 'Umb.EntityAction.Document.Rollback',
name: 'Rollback Document Entity Action',
weight: 500,
weight: 450,
api: () => import('./rollback.action.js'),
forEntityTypes: [UMB_DOCUMENT_ENTITY_TYPE],
meta: {
Expand All @@ -27,12 +22,6 @@ export const manifests: Array<UmbExtensionManifest> = [
{
alias: UMB_ENTITY_IS_NOT_TRASHED_CONDITION_ALIAS,
},
/* Currently the rollback is tightly coupled to the workspace contexts so we only allow it to show up
In the document workspace. */
{
alias: UMB_WORKSPACE_CONDITION_ALIAS,
match: UMB_DOCUMENT_WORKSPACE_ALIAS,
},
],
},
];
Loading

0 comments on commit 7c5c3fd

Please sign in to comment.