diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 2886a3bcef1d..44e4197f1c95 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -1237,6 +1237,19 @@ "react": ">=16" } }, + "node_modules/@microsoft/signalr": { + "version": "8.0.17", + "resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-8.0.17.tgz", + "integrity": "sha512-5pM6xPtKZNJLO0Tq5nQasVyPFwi/WBY3QB5uc/v3dIPTpS1JXQbaXAQAPxFoQ5rTBFE094w8bbqkp17F9ReQvA==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "eventsource": "^2.0.2", + "fetch-cookie": "^2.0.3", + "node-fetch": "^2.6.7", + "ws": "^7.5.10" + } + }, "node_modules/@mswjs/cookies": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-0.2.2.tgz", @@ -5448,6 +5461,18 @@ "license": "(Unlicense OR Apache-2.0)", "optional": true }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -8402,6 +8427,15 @@ "node": ">= 0.6" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -8412,6 +8446,15 @@ "node": ">=0.8.x" } }, + "node_modules/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -8602,6 +8645,16 @@ } } }, + "node_modules/fetch-cookie": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-2.2.0.tgz", + "integrity": "sha512-h9AgfjURuCgA2+2ISl8GbavpUdR+WGAM2McW/ovn4tVccegp8ZqCKWSBR8uRdM8dDNlx5WdKRWxBYUwteLDCNQ==", + "license": "Unlicense", + "dependencies": { + "set-cookie-parser": "^2.4.8", + "tough-cookie": "^4.0.0" + } + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -12175,7 +12228,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" @@ -12203,21 +12255,18 @@ "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true, "license": "MIT" }, "node_modules/node-fetch/node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true, "license": "BSD-2-Clause" }, "node_modules/node-fetch/node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, "license": "MIT", "dependencies": { "tr46": "~0.0.3", @@ -14137,6 +14186,18 @@ "dev": true, "license": "MIT" }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, "node_modules/pump": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", @@ -14152,7 +14213,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -14223,6 +14283,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" + }, "node_modules/queue-lit": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.5.2.tgz", @@ -14588,6 +14654,12 @@ "node": ">=10.13.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -14923,7 +14995,6 @@ "version": "2.7.1", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", - "dev": true, "license": "MIT" }, "node_modules/set-function-length": { @@ -15742,6 +15813,30 @@ "node": ">=0.6" } }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/tr46": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", @@ -16544,6 +16639,16 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", @@ -17091,7 +17196,6 @@ "version": "7.5.10", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.3.0" @@ -17318,6 +17422,7 @@ "name": "@umbraco-backoffice/core", "dependencies": { "@hey-api/client-fetch": "^0.12.0", + "@microsoft/signalr": "^8.0.7", "@types/diff": "^7.0.2", "diff": "^7.0.0", "uuid": "^11.1.0" diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/common/delete/delete.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/common/delete/delete.action.ts index bd6f2fd1587f..109490222bbb 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/common/delete/delete.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/common/delete/delete.action.ts @@ -1,11 +1,11 @@ import { UmbEntityActionBase } from '../../entity-action-base.js'; -import { UmbRequestReloadStructureForEntityEvent } from '../../request-reload-structure-for-entity.event.js'; import type { MetaEntityActionDeleteKind } from './types.js'; import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; import { umbConfirmModal } from '@umbraco-cms/backoffice/modal'; import type { UmbDetailRepository, UmbItemRepository } from '@umbraco-cms/backoffice/repository'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; +import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/entity-action'; export class UmbDeleteEntityAction< MetaKind extends MetaEntityActionDeleteKind = MetaEntityActionDeleteKind, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/common/duplicate/duplicate.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/common/duplicate/duplicate.action.ts index 720fc9251d13..acf847431ddd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/common/duplicate/duplicate.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/common/duplicate/duplicate.action.ts @@ -1,8 +1,8 @@ import { UmbEntityActionBase } from '../../entity-action-base.js'; -import { UmbRequestReloadStructureForEntityEvent } from '../../request-reload-structure-for-entity.event.js'; import type { UmbDuplicateRepository } from './duplicate-repository.interface.js'; import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; +import { UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/entity-action'; export class UmbDuplicateEntityAction extends UmbEntityActionBase { override async execute() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/entity-action.event.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/entity-action.event.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/entity-action/entity-action.event.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/entity-action.event.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/entity-created.event.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/entity-created.event.ts new file mode 100644 index 000000000000..156fd038be7f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/entity-created.event.ts @@ -0,0 +1,10 @@ +import type { UmbEntityActionEventArgs } from './entity-action.event.js'; +import { UmbEntityActionEvent } from './entity-action.event.js'; + +export class UmbEntityCreatedEvent extends UmbEntityActionEvent { + static readonly TYPE = 'entity-created'; + + constructor(args: UmbEntityActionEventArgs) { + super(UmbEntityCreatedEvent.TYPE, args); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/entity-deleted.event.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/entity-deleted.event.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/entity-action/entity-deleted.event.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/entity-deleted.event.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/entity-updated.event.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/entity-updated.event.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/entity-action/entity-updated.event.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/entity-updated.event.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/entity.event.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/entity.event.ts new file mode 100644 index 000000000000..15938b5c2586 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/entity.event.ts @@ -0,0 +1,12 @@ +import type { UmbEntityActionEventArgs } from './entity-action.event.js'; +import { UmbEntityActionEvent } from './entity-action.event.js'; + +interface UmbGenericEntityActionEventArgs extends UmbEntityActionEventArgs { + type: string; +} + +export class UmbEntityEvent extends UmbEntityActionEvent { + constructor(args: UmbGenericEntityActionEventArgs) { + super(args.type, args); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/index.ts new file mode 100644 index 000000000000..f2f2effc20a1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/index.ts @@ -0,0 +1,8 @@ +export * from './entity-action.event.js'; +export * from './entity-created.event.js'; +export * from './entity-deleted.event.js'; +export * from './entity-updated.event.js'; +export * from './entity.event.js'; + +export * from './request-reload-structure-for-entity.event.js'; +export * from './request-reload-children-of-entity.event.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/request-reload-children-of-entity.event.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/request-reload-children-of-entity.event.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/entity-action/request-reload-children-of-entity.event.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/request-reload-children-of-entity.event.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/request-reload-structure-for-entity.event.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/request-reload-structure-for-entity.event.ts similarity index 100% rename from src/Umbraco.Web.UI.Client/src/packages/core/entity-action/request-reload-structure-for-entity.event.ts rename to src/Umbraco.Web.UI.Client/src/packages/core/entity-action/event/request-reload-structure-for-entity.event.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/index.ts index 2104b5fe36f4..c1f8a4d4cb59 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/index.ts @@ -4,13 +4,9 @@ export * from './common/index.js'; 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 './has-children/index.js'; -export * from './entity-updated.event.js'; -export * from './entity-deleted.event.js'; +export * from './event/index.js'; export type * from './types.js'; -export { UmbRequestReloadStructureForEntityEvent } from './request-reload-structure-for-entity.event.js'; -export { UmbRequestReloadChildrenOfEntityEvent } from './request-reload-children-of-entity.event.js'; export { UMB_ENTITY_ACTION_DEFAULT_KIND_MANIFEST } from './default/default.action.kind.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entry-point.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entry-point.ts index d1236bc97bef..9b4a718eecdc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entry-point.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entry-point.ts @@ -2,6 +2,7 @@ import { UMB_AUTH_CONTEXT } from './auth/auth.context.token.js'; import { UmbBackofficeNotificationContainerElement, UmbBackofficeModalContainerElement } from './components/index.js'; import { UmbActionEventContext } from './action/action-event.context.js'; import { manifests as coreManifests } from './manifests.js'; +import { UmbServerEventManager } from './server/index.js'; import { UmbNotificationContext } from '@umbraco-cms/backoffice/notification'; import { UmbModalManagerContext } from '@umbraco-cms/backoffice/modal'; import { UmbExtensionsApiInitializer, type UmbEntryPointOnInit } from '@umbraco-cms/backoffice/extension-api'; @@ -20,6 +21,8 @@ export const onInit: UmbEntryPointOnInit = (host, extensionRegistry) => { new UmbExtensionsApiInitializer(host, extensionRegistry, 'treeStore', [host]); new UmbExtensionsApiInitializer(host, extensionRegistry, 'itemStore', [host]); + new UmbServerEventManager(host); + extensionRegistry.registerMany(coreManifests); const notificationContainerElement = new UmbBackofficeNotificationContainerElement(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/package.json b/src/Umbraco.Web.UI.Client/src/packages/core/package.json index d4907d423e88..8d37cd2c460c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/package.json +++ b/src/Umbraco.Web.UI.Client/src/packages/core/package.json @@ -7,6 +7,7 @@ "generate:server-api": "openapi-ts --file openapi-ts.config.js" }, "dependencies": { + "@microsoft/signalr": "^8.0.7", "@types/diff": "^7.0.2", "diff": "^7.0.0", "uuid": "^11.1.0", diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/server/event/constants.ts b/src/Umbraco.Web.UI.Client/src/packages/core/server/event/constants.ts new file mode 100644 index 000000000000..6cfd031852e4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/server/event/constants.ts @@ -0,0 +1 @@ +export { UMB_SERVER_EVENT_CONTEXT } from './server-event.context.token.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/server/event/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/server/event/index.ts new file mode 100644 index 000000000000..0abb8475e9cc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/server/event/index.ts @@ -0,0 +1 @@ +export * from './server-event.manager.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/server/event/server-event.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/core/server/event/server-event.manager.ts new file mode 100644 index 000000000000..96e4525cde18 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/server/event/server-event.manager.ts @@ -0,0 +1,105 @@ +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +// eslint-disable-next-line local-rules/enforce-umbraco-external-imports +import type { HubConnection } from '@microsoft/signalr'; +// eslint-disable-next-line local-rules/enforce-umbraco-external-imports +import { HubConnectionBuilder } from '@microsoft/signalr'; +import { UMB_AUTH_CONTEXT } from '@umbraco-cms/backoffice/auth'; +import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; +import { UmbEntityEvent } from '@umbraco-cms/backoffice/entity-action'; + +interface UmbServerEventModel { + eventSource: string; + eventType: string; + key: string; +} + +export class UmbServerEventManager extends UmbControllerBase { + #connection?: HubConnection; + #authContext?: typeof UMB_AUTH_CONTEXT.TYPE; + #actionEventContext?: typeof UMB_ACTION_EVENT_CONTEXT.TYPE; + + constructor(host: UmbControllerHost) { + super(host); + + this.consumeContext(UMB_AUTH_CONTEXT, (context) => { + this.#authContext = context; + this.#observeIsAuthorized(); + }); + + this.consumeContext(UMB_ACTION_EVENT_CONTEXT, (context) => { + this.#actionEventContext = context; + }); + } + + #observeIsAuthorized() { + this.observe(this.#authContext?.isAuthorized, async (isAuthorized) => { + if (isAuthorized) { + const token = await this.#authContext?.getLatestToken(); + if (token) { + this.#initHubConnection(token); + } else { + throw new Error('No auth token found'); + } + } else { + this.#connection?.stop(); + this.#connection = undefined; + } + }); + } + + #initHubConnection(token: string) { + this.#connection = new HubConnectionBuilder() + .withUrl('https://localhost:44339/umbraco/serverEventHub', { + accessTokenFactory: () => token, + }) + .build(); + + this.#connection.on('notify', (payload: UmbServerEventModel) => { + console.log('payloadReceived', payload); + + // TODO: Only for POC purposes, replace with a proper mapping + const eventSourceToEntity: Record = { + 'Umbraco:CMS:Document': 'document', + 'Umbraco:CMS:DocumentType': 'document-type', + 'Umbraco:CMS:Media': 'media', + 'Umbraco:CMS:MediaType': 'media-type', + 'Umbraco:CMS:Member': 'member', + 'Umbraco:CMS:MemberType': 'member-type', + 'Umbraco:CMS:MemberGroup': 'member-group', + 'Umbraco:CMS:DataType': 'data-type', + }; + + // TODO: Only for POC purposes, replace with a proper mapping + const eventTypeToAction: Record = { + Created: 'entity-created', + Updated: 'entity-updated', + Deleted: 'entity-deleted', + Trashed: 'entity-trashed', + }; + + const event = new UmbEntityEvent({ + type: eventTypeToAction[payload.eventType], + entityType: eventSourceToEntity[payload.eventSource], + unique: payload.key, + }); + + this.#actionEventContext?.dispatchEvent(event); + }); + + this.#connection + .start() + .then(function () { + console.log('Connected!'); + }) + .catch(function (err) { + console.log(err); + }); + + this.#connection.onclose((err?: Error) => { + if (err) { + console.error('Connection closed with error: ', err); + } + }); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/server/event/types.ts b/src/Umbraco.Web.UI.Client/src/packages/core/server/event/types.ts new file mode 100644 index 000000000000..03a219188ebd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/server/event/types.ts @@ -0,0 +1 @@ +export type * from './server-event.manager.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/server/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/server/index.ts index 22bc53a8f59c..981f44b8ff0a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/server/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/server/index.ts @@ -1,3 +1,4 @@ +export * from './event/index.js'; export * from './server-connection.js'; export * from './server.context-token.js'; export * from './server.context.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-base/tree-item-context-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-base/tree-item-context-base.ts index 6ddd369c0372..93bf481d41ad 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-base/tree-item-context-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/tree-item/tree-item-base/tree-item-context-base.ts @@ -12,6 +12,8 @@ import { UMB_SECTION_CONTEXT, UMB_SECTION_SIDEBAR_CONTEXT } from '@umbraco-cms/b import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { + UmbEntityCreatedEvent, + UmbEntityUpdatedEvent, UmbHasChildrenEntityContext, UmbRequestReloadChildrenOfEntityEvent, UmbRequestReloadStructureForEntityEvent, @@ -323,6 +325,11 @@ export abstract class UmbTreeItemContextBase< UmbRequestReloadStructureForEntityEvent.TYPE, this.#onReloadStructureRequest as unknown as EventListener, ); + + this.#actionEventContext?.addEventListener( + UmbEntityUpdatedEvent.TYPE, + this.#onReloadStructureRequest as unknown as EventListener, + ); }); } @@ -480,6 +487,11 @@ export abstract class UmbTreeItemContextBase< UmbRequestReloadStructureForEntityEvent.TYPE, this.#onReloadStructureRequest as unknown as EventListener, ); + + this.#actionEventContext?.removeEventListener( + UmbEntityUpdatedEvent.TYPE, + this.#onReloadStructureRequest as unknown as EventListener, + ); }; override destroy(): void {