From ff36aeeac9c31e72ae06e60eb13f18b3fe04e187 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 15 Dec 2022 14:32:04 +0100 Subject: [PATCH 1/8] add resource mixin this mixin follows the Lit lifecycle and understands how to store and cancel running cancelable promises --- .../src/core/resource-api/resource.mixin.ts | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/core/resource-api/resource.mixin.ts diff --git a/src/Umbraco.Web.UI.Client/src/core/resource-api/resource.mixin.ts b/src/Umbraco.Web.UI.Client/src/core/resource-api/resource.mixin.ts new file mode 100644 index 0000000000..5f71d52bea --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/resource-api/resource.mixin.ts @@ -0,0 +1,107 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { ApiError, CancelablePromise, ProblemDetails } from '@umbraco-cms/backend-api'; +import type { HTMLElementConstructor } from '@umbraco-cms/models'; +import { UmbNotificationOptions, UmbNotificationService, UmbNotificationDefaultData } from '@umbraco-cms/services'; +import { UmbContextConsumerMixin } from '@umbraco-cms/context-api'; + +export declare class UmbResourceMixinInterface { + tryExecute(promise: CancelablePromise): Promise<[T | undefined, ProblemDetails | undefined]>; + executeAndNotify(promise: CancelablePromise, options?: UmbNotificationOptions): Promise; + addResource(promise: CancelablePromise): void; + cancelAllResources(): void; +} + +export const UmbResourceMixin = (superClass: T) => { + class UmbResourceMixinClass extends UmbContextConsumerMixin(superClass) implements UmbResourceMixinInterface { + #promises: CancelablePromise[] = []; + + private _notificationService?: UmbNotificationService; + + connectedCallback() { + super.connectedCallback?.(); + this.#promises.length = 0; + this.consumeContext('umbNotificationService', (notificationService) => { + this._notificationService = notificationService; + }); + } + + disconnectedCallback() { + super.disconnectedCallback?.(); + this.cancelAllResources(); + } + + addResource(promise: CancelablePromise): void { + this.#promises.push(promise); + } + + /** + * Execute a given function and get the result as a promise. + */ + execute(func: CancelablePromise): Promise { + this.addResource(func); + return func; + } + + /** + * Wrap the {execute} function in a try/catch block and return a tuple with the result and the error. + */ + async tryExecute(func: CancelablePromise): Promise<[T | undefined, ProblemDetails | undefined]> { + try { + return [await this.execute(func), undefined]; + } catch (e) { + return [undefined, this.#toProblemDetails(e)]; + } + } + + /** + * Wrap the {execute} function in a try/catch block and return the result. + * If the executor function throws an error, then show the details in a notification. + */ + async executeAndNotify( + func: CancelablePromise, + options?: UmbNotificationOptions + ): Promise { + try { + return await this.execute(func); + } catch (e) { + const error = this.#toProblemDetails(e); + if (error) { + const data: UmbNotificationDefaultData = { + headline: error.title ?? 'Server Error', + message: error.detail ?? 'Something went wrong', + }; + this._notificationService?.peek('danger', { data, ...options }); + } + } + + return undefined; + } + + /** + * Cancel all resources that are currently being executed. + */ + cancelAllResources() { + this.#promises.forEach((promise) => { + if (promise instanceof CancelablePromise) { + promise.cancel(); + } + }); + } + + /** + * Extract the ProblemDetails object from an ApiError. + * + * This assumes that all ApiErrors contain a ProblemDetails object in their body. + */ + #toProblemDetails(error: unknown): ProblemDetails | undefined { + if (error instanceof ApiError) { + const errorDetails = error.body as ProblemDetails; + return errorDetails; + } + + return undefined; + } + } + + return UmbResourceMixinClass as unknown as HTMLElementConstructor & T; +}; From 6fef04e2d55d49a998d031e65be0055c225fc3c7 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 15 Dec 2022 14:32:19 +0100 Subject: [PATCH 2/8] add import paths --- src/Umbraco.Web.UI.Client/src/core/resource-api/index.ts | 1 + src/Umbraco.Web.UI.Client/tsconfig.json | 1 + src/Umbraco.Web.UI.Client/web-test-runner.config.mjs | 1 + 3 files changed, 3 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/core/resource-api/index.ts diff --git a/src/Umbraco.Web.UI.Client/src/core/resource-api/index.ts b/src/Umbraco.Web.UI.Client/src/core/resource-api/index.ts new file mode 100644 index 0000000000..726e3e3ff0 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/resource-api/index.ts @@ -0,0 +1 @@ +export * from './resource.mixin'; diff --git a/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json index 7b360952af..393b77ad72 100644 --- a/src/Umbraco.Web.UI.Client/tsconfig.json +++ b/src/Umbraco.Web.UI.Client/tsconfig.json @@ -26,6 +26,7 @@ "@umbraco-cms/extensions-api": ["src/core/extensions-api"], "@umbraco-cms/extensions-registry": ["src/core/extensions-registry"], "@umbraco-cms/observable-api": ["src/core/observable-api"], + "@umbraco-cms/resource-api": ["src/core/resource-api"], "@umbraco-cms/utils": ["src/core/utils"], "@umbraco-cms/test-utils": ["src/core/test-utils"], "@umbraco-cms/services": ["src/core/services"], diff --git a/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs b/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs index 384fe2aa3f..165a904a63 100644 --- a/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs +++ b/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs @@ -16,6 +16,7 @@ export default { '@umbraco-cms/context-api': './src/core/context-api/index.ts', '@umbraco-cms/extensions-api': './src/core/extensions-api/index.ts', '@umbraco-cms/observable-api': './src/core/observable-api/index.ts', + '@umbraco-cms/resource-api': './src/core/resource-api', '@umbraco-cms/utils': './src/core/utils/index.ts', '@umbraco-cms/test-utils': './src/core/test-utils/index.ts', '@umbraco-cms/extensions-registry': './src/core/extensions-registry/index.ts', From 5c8fbff84dc6488e2d1c6d3f16e6a46849fbd09f Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 15 Dec 2022 14:32:30 +0100 Subject: [PATCH 3/8] export options from services/notifications --- src/Umbraco.Web.UI.Client/src/core/services/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/core/services/index.ts b/src/Umbraco.Web.UI.Client/src/core/services/index.ts index 9a44d0ea7a..4aae52e06b 100644 --- a/src/Umbraco.Web.UI.Client/src/core/services/index.ts +++ b/src/Umbraco.Web.UI.Client/src/core/services/index.ts @@ -3,5 +3,5 @@ /* eslint-disable */ export * from './modal'; -export { UmbNotificationService } from './notification'; +export * from './notification'; export type { UmbNotificationDefaultData } from './notification/layouts/default'; From e65b7d681b59b32637385569d826113c83ae1901 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 15 Dec 2022 14:32:52 +0100 Subject: [PATCH 4/8] use new resource mixin for profiling dashboard --- ...dashboard-performance-profiling.element.ts | 36 ++++++------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/dashboards/performance-profiling/dashboard-performance-profiling.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/dashboards/performance-profiling/dashboard-performance-profiling.element.ts index 78e8195064..afc692ae80 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/dashboards/performance-profiling/dashboard-performance-profiling.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/dashboards/performance-profiling/dashboard-performance-profiling.element.ts @@ -2,12 +2,11 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html, LitElement } from 'lit'; import { customElement, state } from 'lit/decorators.js'; -import { ApiError, ProblemDetails, ProfilingResource } from '@umbraco-cms/backend-api'; -import { UmbContextConsumerMixin } from '@umbraco-cms/context-api'; -import { UmbNotificationDefaultData, UmbNotificationService } from '@umbraco-cms/services'; +import { ProfilingResource } from '@umbraco-cms/backend-api'; +import { UmbResourceMixin } from '@umbraco-cms/resource-api'; @customElement('umb-dashboard-performance-profiling') -export class UmbDashboardPerformanceProfilingElement extends UmbContextConsumerMixin(LitElement) { +export class UmbDashboardPerformanceProfilingElement extends UmbResourceMixin(LitElement) { static styles = [ UUITextStyles, css` @@ -31,34 +30,19 @@ export class UmbDashboardPerformanceProfilingElement extends UmbContextConsumerM @state() private _profilingPerfomance = false; - private _notificationService?: UmbNotificationService; - - private async _getProfilingStatus() { - try { - const status = await ProfilingResource.getProfilingStatus(); - this._profilingStatus = status.enabled; - } catch (e) { - if (e instanceof ApiError) { - const error = e as ProblemDetails; - const data: UmbNotificationDefaultData = { message: error.message ?? 'Something went wrong' }; - this._notificationService?.peek('danger', { data }); - } - } - } - - constructor() { - super(); - this.consumeAllContexts(['umbNotificationService'], (instances) => { - this._notificationService = instances['umbNotificationService']; - }); - } - connectedCallback(): void { super.connectedCallback(); this._getProfilingStatus(); this._profilingPerfomance = localStorage.getItem('profilingPerformance') === 'true'; } + private async _getProfilingStatus() { + const profilingStatus = await this.executeAndNotify(ProfilingResource.getProfilingStatus()); + if (profilingStatus) { + this._profilingStatus = profilingStatus.enabled; + } + } + private _changeProfilingPerformance() { this._profilingPerfomance = !this._profilingPerfomance; localStorage.setItem('profilingPerformance', this._profilingPerfomance.toString()); From 77cd8633c1b5cb568c6249caaad68a2b116aa05f Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 20 Dec 2022 07:44:30 +0100 Subject: [PATCH 5/8] add path for @umbraco-cms/controllers --- src/Umbraco.Web.UI.Client/tsconfig.json | 2 +- src/Umbraco.Web.UI.Client/web-test-runner.config.mjs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/tsconfig.json b/src/Umbraco.Web.UI.Client/tsconfig.json index 393b77ad72..6763dcdfee 100644 --- a/src/Umbraco.Web.UI.Client/tsconfig.json +++ b/src/Umbraco.Web.UI.Client/tsconfig.json @@ -26,9 +26,9 @@ "@umbraco-cms/extensions-api": ["src/core/extensions-api"], "@umbraco-cms/extensions-registry": ["src/core/extensions-registry"], "@umbraco-cms/observable-api": ["src/core/observable-api"], - "@umbraco-cms/resource-api": ["src/core/resource-api"], "@umbraco-cms/utils": ["src/core/utils"], "@umbraco-cms/test-utils": ["src/core/test-utils"], + "@umbraco-cms/controllers": ["src/core/controllers"], "@umbraco-cms/services": ["src/core/services"], "@umbraco-cms/components/*": ["src/backoffice/components/*"], "@umbraco-cms/stores/*": ["src/core/stores/*"], diff --git a/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs b/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs index 165a904a63..3ffba5215a 100644 --- a/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs +++ b/src/Umbraco.Web.UI.Client/web-test-runner.config.mjs @@ -19,6 +19,8 @@ export default { '@umbraco-cms/resource-api': './src/core/resource-api', '@umbraco-cms/utils': './src/core/utils/index.ts', '@umbraco-cms/test-utils': './src/core/test-utils/index.ts', + '@umbraco-cms/controllers': './src/core/controllers', + '@umbraco-cms/services': './src/core/services', '@umbraco-cms/extensions-registry': './src/core/extensions-registry/index.ts', }, }, From 62a598118cd459a8c1b85d2520e3ae7303d20e8d Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 20 Dec 2022 07:44:45 +0100 Subject: [PATCH 6/8] convert resource mixin to controller --- .../src/core/controllers/index.ts | 1 + .../core/controllers/resource.controller.ts | 113 ++++++++++++++++++ .../src/core/resource-api/index.ts | 1 - .../src/core/resource-api/resource.mixin.ts | 107 ----------------- 4 files changed, 114 insertions(+), 108 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/core/controllers/index.ts create mode 100644 src/Umbraco.Web.UI.Client/src/core/controllers/resource.controller.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/core/resource-api/index.ts delete mode 100644 src/Umbraco.Web.UI.Client/src/core/resource-api/resource.mixin.ts diff --git a/src/Umbraco.Web.UI.Client/src/core/controllers/index.ts b/src/Umbraco.Web.UI.Client/src/core/controllers/index.ts new file mode 100644 index 0000000000..b4dfcb2097 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/controllers/index.ts @@ -0,0 +1 @@ +export * from './resource.controller'; diff --git a/src/Umbraco.Web.UI.Client/src/core/controllers/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/core/controllers/resource.controller.ts new file mode 100644 index 0000000000..ca9a34d44e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/core/controllers/resource.controller.ts @@ -0,0 +1,113 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { ReactiveController, ReactiveControllerHost } from 'lit'; +import { ApiError, CancelablePromise, ProblemDetails } from '@umbraco-cms/backend-api'; +import { UmbNotificationOptions, UmbNotificationDefaultData, UmbNotificationService } from '@umbraco-cms/services'; + +export class UmbResourceController implements ReactiveController { + host: ReactiveControllerHost; + + #promises: Promise[] = []; + + #notificationService?: UmbNotificationService; + + constructor(host: ReactiveControllerHost) { + (this.host = host).addController(this); + } + + hostConnected() { + this.#promises.length = 0; + } + + hostDisconnected() { + this.cancelAllResources(); + } + + addResource(promise: Promise): void { + this.#promises.push(promise); + } + + /** + * Execute a given function and get the result as a promise. + */ + execute(func: Promise): Promise { + this.addResource(func); + return func; + } + + /** + * Wrap the {execute} function in a try/catch block and return a tuple with the result and the error. + */ + async tryExecute(func: Promise): Promise<[T | undefined, ProblemDetails | undefined]> { + try { + return [await this.execute(func), undefined]; + } catch (e) { + return [undefined, this.#toProblemDetails(e)]; + } + } + + /** + * Wrap the {execute} function in a try/catch block and return the result. + * If the executor function throws an error, then show the details in a notification. + */ + async tryExecuteAndNotify( + func: Promise, + options?: UmbNotificationOptions + ): Promise<[T | undefined, ProblemDetails | undefined]> { + const [result, error] = await this.tryExecute(func); + + if (error) { + const data: UmbNotificationDefaultData = { + headline: error.title ?? 'Server Error', + message: error.detail ?? 'Something went wrong', + }; + + if (this.#notificationService) { + this.#notificationService?.peek('danger', { data, ...options }); + } else { + console.group('UmbResourceController'); + console.error(error); + console.groupEnd(); + } + } + + return [result, error]; + } + + /** + * Cancel all resources that are currently being executed by this controller if they are cancelable. + * + * This works by checking if the promise is a CancelablePromise and if so, it will call the cancel method. + * + * This is useful when the controller is being disconnected from the DOM. + * + * @see CancelablePromise + * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal + * @see https://developer.mozilla.org/en-US/docs/Web/API/AbortController + */ + cancelAllResources() { + this.#promises.forEach((promise) => { + if (promise instanceof CancelablePromise) { + promise.cancel(); + } + }); + } + + /** + * Extract the ProblemDetails object from an ApiError. + * + * This assumes that all ApiErrors contain a ProblemDetails object in their body. + */ + #toProblemDetails(error: unknown): ProblemDetails | undefined { + if (error instanceof ApiError) { + const errorDetails = error.body as ProblemDetails; + return errorDetails; + } else if (error instanceof Error) { + return { + title: error.name, + detail: error.message, + }; + } + + return undefined; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/core/resource-api/index.ts b/src/Umbraco.Web.UI.Client/src/core/resource-api/index.ts deleted file mode 100644 index 726e3e3ff0..0000000000 --- a/src/Umbraco.Web.UI.Client/src/core/resource-api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './resource.mixin'; diff --git a/src/Umbraco.Web.UI.Client/src/core/resource-api/resource.mixin.ts b/src/Umbraco.Web.UI.Client/src/core/resource-api/resource.mixin.ts deleted file mode 100644 index 5f71d52bea..0000000000 --- a/src/Umbraco.Web.UI.Client/src/core/resource-api/resource.mixin.ts +++ /dev/null @@ -1,107 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { ApiError, CancelablePromise, ProblemDetails } from '@umbraco-cms/backend-api'; -import type { HTMLElementConstructor } from '@umbraco-cms/models'; -import { UmbNotificationOptions, UmbNotificationService, UmbNotificationDefaultData } from '@umbraco-cms/services'; -import { UmbContextConsumerMixin } from '@umbraco-cms/context-api'; - -export declare class UmbResourceMixinInterface { - tryExecute(promise: CancelablePromise): Promise<[T | undefined, ProblemDetails | undefined]>; - executeAndNotify(promise: CancelablePromise, options?: UmbNotificationOptions): Promise; - addResource(promise: CancelablePromise): void; - cancelAllResources(): void; -} - -export const UmbResourceMixin = (superClass: T) => { - class UmbResourceMixinClass extends UmbContextConsumerMixin(superClass) implements UmbResourceMixinInterface { - #promises: CancelablePromise[] = []; - - private _notificationService?: UmbNotificationService; - - connectedCallback() { - super.connectedCallback?.(); - this.#promises.length = 0; - this.consumeContext('umbNotificationService', (notificationService) => { - this._notificationService = notificationService; - }); - } - - disconnectedCallback() { - super.disconnectedCallback?.(); - this.cancelAllResources(); - } - - addResource(promise: CancelablePromise): void { - this.#promises.push(promise); - } - - /** - * Execute a given function and get the result as a promise. - */ - execute(func: CancelablePromise): Promise { - this.addResource(func); - return func; - } - - /** - * Wrap the {execute} function in a try/catch block and return a tuple with the result and the error. - */ - async tryExecute(func: CancelablePromise): Promise<[T | undefined, ProblemDetails | undefined]> { - try { - return [await this.execute(func), undefined]; - } catch (e) { - return [undefined, this.#toProblemDetails(e)]; - } - } - - /** - * Wrap the {execute} function in a try/catch block and return the result. - * If the executor function throws an error, then show the details in a notification. - */ - async executeAndNotify( - func: CancelablePromise, - options?: UmbNotificationOptions - ): Promise { - try { - return await this.execute(func); - } catch (e) { - const error = this.#toProblemDetails(e); - if (error) { - const data: UmbNotificationDefaultData = { - headline: error.title ?? 'Server Error', - message: error.detail ?? 'Something went wrong', - }; - this._notificationService?.peek('danger', { data, ...options }); - } - } - - return undefined; - } - - /** - * Cancel all resources that are currently being executed. - */ - cancelAllResources() { - this.#promises.forEach((promise) => { - if (promise instanceof CancelablePromise) { - promise.cancel(); - } - }); - } - - /** - * Extract the ProblemDetails object from an ApiError. - * - * This assumes that all ApiErrors contain a ProblemDetails object in their body. - */ - #toProblemDetails(error: unknown): ProblemDetails | undefined { - if (error instanceof ApiError) { - const errorDetails = error.body as ProblemDetails; - return errorDetails; - } - - return undefined; - } - } - - return UmbResourceMixinClass as unknown as HTMLElementConstructor & T; -}; From ccd58c8be9f9fe916cb3048a4a7749ca1bdf6eea Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 20 Dec 2022 07:45:35 +0100 Subject: [PATCH 7/8] use ResourceController --- .../dashboard-performance-profiling.element.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/dashboards/performance-profiling/dashboard-performance-profiling.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/dashboards/performance-profiling/dashboard-performance-profiling.element.ts index afc692ae80..fa4835f52b 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/dashboards/performance-profiling/dashboard-performance-profiling.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/dashboards/performance-profiling/dashboard-performance-profiling.element.ts @@ -2,11 +2,11 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html, LitElement } from 'lit'; import { customElement, state } from 'lit/decorators.js'; +import { UmbResourceController } from '@umbraco-cms/controllers'; import { ProfilingResource } from '@umbraco-cms/backend-api'; -import { UmbResourceMixin } from '@umbraco-cms/resource-api'; @customElement('umb-dashboard-performance-profiling') -export class UmbDashboardPerformanceProfilingElement extends UmbResourceMixin(LitElement) { +export class UmbDashboardPerformanceProfilingElement extends LitElement { static styles = [ UUITextStyles, css` @@ -30,6 +30,8 @@ export class UmbDashboardPerformanceProfilingElement extends UmbResourceMixin(Li @state() private _profilingPerfomance = false; + private _resourceController = new UmbResourceController(this); + connectedCallback(): void { super.connectedCallback(); this._getProfilingStatus(); @@ -37,7 +39,9 @@ export class UmbDashboardPerformanceProfilingElement extends UmbResourceMixin(Li } private async _getProfilingStatus() { - const profilingStatus = await this.executeAndNotify(ProfilingResource.getProfilingStatus()); + const [profilingStatus] = await this._resourceController.tryExecuteAndNotify( + ProfilingResource.getProfilingStatus() + ); if (profilingStatus) { this._profilingStatus = profilingStatus.enabled; } From 8732dcf029c3767dcb56670e3644a41c0d8a0545 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Tue, 20 Dec 2022 14:27:44 +0100 Subject: [PATCH 8/8] add ContextConsumer to get instance of NotificationService --- .../src/core/controllers/resource.controller.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Umbraco.Web.UI.Client/src/core/controllers/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/core/controllers/resource.controller.ts index ca9a34d44e..bff3e44b7b 100644 --- a/src/Umbraco.Web.UI.Client/src/core/controllers/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/core/controllers/resource.controller.ts @@ -2,24 +2,37 @@ import { ReactiveController, ReactiveControllerHost } from 'lit'; import { ApiError, CancelablePromise, ProblemDetails } from '@umbraco-cms/backend-api'; import { UmbNotificationOptions, UmbNotificationDefaultData, UmbNotificationService } from '@umbraco-cms/services'; +import { UmbContextConsumer } from '@umbraco-cms/context-api'; export class UmbResourceController implements ReactiveController { host: ReactiveControllerHost; #promises: Promise[] = []; + #notificationConsumer: UmbContextConsumer; + #notificationService?: UmbNotificationService; constructor(host: ReactiveControllerHost) { (this.host = host).addController(this); + + this.#notificationConsumer = new UmbContextConsumer( + host as unknown as EventTarget, + 'umbNotificationService', + (_instance: UmbNotificationService) => { + this.#notificationService = _instance; + } + ); } hostConnected() { this.#promises.length = 0; + this.#notificationConsumer.attach(); } hostDisconnected() { this.cancelAllResources(); + this.#notificationConsumer.detach(); } addResource(promise: Promise): void {