diff --git a/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/incrementor-workspace-action.ts b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/incrementor-workspace-action.ts index 940436c579..04bfb72cac 100644 --- a/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/incrementor-workspace-action.ts +++ b/src/Umbraco.Web.UI.Client/examples/workspace-context-counter/incrementor-workspace-action.ts @@ -1,11 +1,14 @@ +import { EXAMPLE_COUNTER_CONTEXT } from './counter-workspace-context.js'; import { UmbWorkspaceActionBase, type UmbWorkspaceAction } from '@umbraco-cms/backoffice/workspace'; -import { EXAMPLE_COUNTER_CONTEXT } from './counter-workspace-context'; // The Example Incrementor Workspace Action Controller: export class ExampleIncrementorWorkspaceAction extends UmbWorkspaceActionBase implements UmbWorkspaceAction { // This method is executed override async execute() { const context = await this.getContext(EXAMPLE_COUNTER_CONTEXT); + if (!context) { + throw new Error('Could not get the counter context'); + } context.increment(); } } diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/api-interceptor.controller.ts b/src/Umbraco.Web.UI.Client/src/apps/app/api-interceptor.controller.ts index 874c96c2b0..8c7bbcf6e8 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/api-interceptor.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/api-interceptor.controller.ts @@ -29,6 +29,9 @@ export class UmbApiInterceptorController extends UmbControllerBase { if (!isUmbNotifications(notifications)) return response; this.getContext(UMB_NOTIFICATION_CONTEXT).then((notificationContext) => { + if (notificationContext === undefined) { + throw new Error('Notification context is not available'); + } for (const notification of notifications) { notificationContext.peek(extractUmbNotificationColor(notification.type), { data: { headline: notification.category, message: notification.message }, diff --git a/src/Umbraco.Web.UI.Client/src/apps/app/app-auth.controller.ts b/src/Umbraco.Web.UI.Client/src/apps/app/app-auth.controller.ts index 2d7f492808..93f1f6de1d 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/app/app-auth.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/app/app-auth.controller.ts @@ -7,13 +7,14 @@ import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import { setStoredPath } from '@umbraco-cms/backoffice/utils'; export class UmbAppAuthController extends UmbControllerBase { + #retrievedModal: Promise; #authContext?: typeof UMB_AUTH_CONTEXT.TYPE; #isFirstCheck = true; constructor(host: UmbControllerHost) { super(host); - this.consumeContext(UMB_AUTH_CONTEXT, (context) => { + this.#retrievedModal = this.consumeContext(UMB_AUTH_CONTEXT, (context) => { this.#authContext = context; // Observe the user's authorization state and start the authorization flow if the user is not authorized @@ -24,7 +25,7 @@ export class UmbAppAuthController extends UmbControllerBase { }, '_authState', ); - }); + }).asPromise({ preventTimeout: true }); } /** @@ -32,6 +33,7 @@ export class UmbAppAuthController extends UmbControllerBase { * If not, the authorization flow is started. */ async isAuthorized(): Promise { + await this.#retrievedModal.catch(() => undefined); if (!this.#authContext) { throw new Error('[Fatal] Auth context is not available'); } @@ -63,6 +65,7 @@ export class UmbAppAuthController extends UmbControllerBase { * @param userLoginState */ async makeAuthorizationRequest(userLoginState: UmbUserLoginState = 'loggingIn'): Promise { + await this.#retrievedModal.catch(() => undefined); if (!this.#authContext) { throw new Error('[Fatal] Auth context is not available'); } @@ -111,6 +114,7 @@ export class UmbAppAuthController extends UmbControllerBase { } async #showLoginModal(userLoginState: UmbUserLoginState): Promise { + await this.#retrievedModal.catch(() => undefined); if (!this.#authContext) { throw new Error('[Fatal] Auth context is not available'); } @@ -118,6 +122,9 @@ export class UmbAppAuthController extends UmbControllerBase { // Show the provider selection screen const authModalKey = 'umbAuthModal'; const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + if (!modalManager) { + throw new Error('[Fatal] Modal manager is not available'); + } const selected = await modalManager .open(this._host, UMB_MODAL_APP_AUTH, { diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts index 13ce2e72f8..7cbe0f48ad 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/backoffice.context.ts @@ -34,29 +34,26 @@ export class UmbBackofficeContext extends UmbContextBase { }); }); - this.#init(); - } - - async #init() { - const userContext = await this.getContext(UMB_CURRENT_USER_CONTEXT); - this.observe( - userContext.allowedSections, - (allowedSections) => { - if (!allowedSections) return; - // TODO: Please be aware that we re-initialize this initializer based on user permissions. I suggest we should solve this specific case should be improved by the ability to change the filter [NL] - new UmbExtensionsManifestInitializer( - this, - umbExtensionsRegistry, - 'section', - (manifest) => allowedSections.includes(manifest.alias), - async (sections) => { - this.#allowedSections.setValue([...sections]); - }, - 'umbAllowedSectionsManifestInitializer', - ); - }, - 'umbAllowedSectionsObserver', - ); + this.consumeContext(UMB_CURRENT_USER_CONTEXT, (userContext) => { + this.observe( + userContext.allowedSections, + (allowedSections) => { + if (!allowedSections) return; + // TODO: Please be aware that we re-initialize this initializer based on user permissions. I suggest we should solve this specific case should be improved by the ability to change the filter [NL] + new UmbExtensionsManifestInitializer( + this, + umbExtensionsRegistry, + 'section', + (manifest) => allowedSections.includes(manifest.alias), + async (sections) => { + this.#allowedSections.setValue([...sections]); + }, + 'umbAllowedSectionsManifestInitializer', + ); + }, + 'umbAllowedSectionsObserver', + ); + }); } async #getVersion() { diff --git a/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header-logo.element.ts b/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header-logo.element.ts index 03af4fdac5..5c7cbe4484 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header-logo.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/backoffice/components/backoffice-header-logo.element.ts @@ -47,7 +47,7 @@ export class UmbBackofficeHeaderLogoElement extends UmbLitElement { }); } - protected override async firstUpdated() { + protected override firstUpdated() { this.#isAdmin(); } @@ -88,6 +88,10 @@ export class UmbBackofficeHeaderLogoElement extends UmbLitElement { async #openSystemInformation() { const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + if (!modalManager) { + throw new Error('Modal manager not found'); + } + modalManager .open(this, UMB_SYSINFO_MODAL) .onSubmit() @@ -97,6 +101,7 @@ export class UmbBackofficeHeaderLogoElement extends UmbLitElement { async #openNewVersion() { if (!this._serverUpgradeCheck) return; const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + if (!modalManager) return; modalManager .open(this, UMB_NEWVERSION_MODAL, { data: { diff --git a/src/Umbraco.Web.UI.Client/src/apps/preview/apps/preview-culture.element.ts b/src/Umbraco.Web.UI.Client/src/apps/preview/apps/preview-culture.element.ts index 81d425e2a0..3fd66910ed 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/preview/apps/preview-culture.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/preview/apps/preview-culture.element.ts @@ -36,7 +36,7 @@ export class UmbPreviewCultureElement extends UmbLitElement { this._culture = culture; const previewContext = await this.getContext(UMB_PREVIEW_CONTEXT); - previewContext.updateIFrame({ culture: culture.unique }); + previewContext?.updateIFrame({ culture: culture.unique }); } override render() { diff --git a/src/Umbraco.Web.UI.Client/src/apps/preview/apps/preview-exit.element.ts b/src/Umbraco.Web.UI.Client/src/apps/preview/apps/preview-exit.element.ts index e751e42ad3..95e907a38a 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/preview/apps/preview-exit.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/preview/apps/preview-exit.element.ts @@ -6,7 +6,7 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; export class UmbPreviewExitElement extends UmbLitElement { async #onClick() { const previewContext = await this.getContext(UMB_PREVIEW_CONTEXT); - previewContext.exitPreview(0); + previewContext?.exitPreview(0); } override render() { diff --git a/src/Umbraco.Web.UI.Client/src/apps/preview/apps/preview-open-website.element.ts b/src/Umbraco.Web.UI.Client/src/apps/preview/apps/preview-open-website.element.ts index 95dd68f917..aba28768e5 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/preview/apps/preview-open-website.element.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/preview/apps/preview-open-website.element.ts @@ -6,7 +6,7 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; export class UmbPreviewOpenWebsiteElement extends UmbLitElement { async #onClick() { const previewContext = await this.getContext(UMB_PREVIEW_CONTEXT); - previewContext.openWebsite(); + previewContext?.openWebsite(); } override render() { diff --git a/src/Umbraco.Web.UI.Client/src/apps/preview/preview.context.ts b/src/Umbraco.Web.UI.Client/src/apps/preview/preview.context.ts index 03e928fa82..0e8bc63bd7 100644 --- a/src/Umbraco.Web.UI.Client/src/apps/preview/preview.context.ts +++ b/src/Umbraco.Web.UI.Client/src/apps/preview/preview.context.ts @@ -24,24 +24,22 @@ export class UmbPreviewContext extends UmbContextBase { constructor(host: UmbControllerHost) { super(host, UMB_PREVIEW_CONTEXT); - this.#init(); - } - async #init() { - const appContext = await this.getContext(UMB_APP_CONTEXT); - this.#serverUrl = appContext.getServerUrl(); + this.consumeContext(UMB_APP_CONTEXT, (appContext) => { + this.#serverUrl = appContext.getServerUrl(); - const params = new URLSearchParams(window.location.search); + const params = new URLSearchParams(window.location.search); - this.#culture = params.get('culture'); - this.#unique = params.get('id'); + this.#culture = params.get('culture'); + this.#unique = params.get('id'); - if (!this.#unique) { - console.error('No unique ID found in query string.'); - return; - } + if (!this.#unique) { + console.error('No unique ID found in query string.'); + return; + } - this.#setPreviewUrl(); + this.#setPreviewUrl(); + }); } #configureWebSocket() { diff --git a/src/Umbraco.Web.UI.Client/src/libs/class-api/class.interface.ts b/src/Umbraco.Web.UI.Client/src/libs/class-api/class.interface.ts index 47aa54c406..a70f20916e 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/class-api/class.interface.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/class-api/class.interface.ts @@ -1,5 +1,6 @@ import type { UmbContextCallback, + UmbContextConsumerAsPromiseOptionsType, UmbContextConsumerController, UmbContextProviderController, UmbContextToken, @@ -64,5 +65,6 @@ export interface UmbClassInterface extends UmbControllerHost { */ getContext( alias: string | UmbContextToken, - ): Promise; + options?: UmbContextConsumerAsPromiseOptionsType, + ): Promise; } diff --git a/src/Umbraco.Web.UI.Client/src/libs/class-api/class.mixin.ts b/src/Umbraco.Web.UI.Client/src/libs/class-api/class.mixin.ts index 0b1c893bcd..607cca29c1 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/class-api/class.mixin.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/class-api/class.mixin.ts @@ -11,6 +11,7 @@ import { type UmbContextCallback, UmbContextConsumerController, UmbContextProviderController, + type UmbContextConsumerAsPromiseOptionsType, } from '@umbraco-cms/backoffice/context-api'; import { type ObserverCallback, UmbObserverController, simpleHashCode } from '@umbraco-cms/backoffice/observable-api'; @@ -98,12 +99,19 @@ export const UmbClassMixin = >(superClas async getContext( contextAlias: string | UmbContextToken, - ): Promise { + options?: UmbContextConsumerAsPromiseOptionsType, + ): Promise { const controller = new UmbContextConsumerController(this, contextAlias); - const promise = controller.asPromise().then((result) => { - controller.destroy(); - return result; - }); + const promise = controller + .asPromise(options) + .then((result) => { + controller.destroy(); + return result; + }) + .catch(() => { + controller.destroy(); + return undefined; + }); return promise; } diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.test.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.test.ts index 5e110b74ae..57f9f0339b 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.test.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.test.ts @@ -52,44 +52,108 @@ describe('UmbContextConsumer', () => { }); describe('Simple implementation', () => { + let element: HTMLElement; + beforeEach(() => { + element = document.createElement('div'); + document.body.appendChild(element); + }); + afterEach(() => { + document.body.removeChild(element); + }); + it('works with UmbContextProvider', (done) => { const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass()); provider.hostConnected(); - const element = document.createElement('div'); - document.body.appendChild(element); - - const localConsumer = new UmbContextConsumer( + const localConsumer = new UmbContextConsumer( element, testContextAlias, - (_instance: UmbTestContextConsumerClass | undefined) => { + (_instance) => { if (_instance) { expect(_instance.prop).to.eq('value from provider'); - done(); localConsumer.hostDisconnected(); provider.hostDisconnected(); + done(); } }, ); localConsumer.hostConnected(); }); + it('works with asPromise for UmbContextProvider', (done) => { + const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass()); + + const localConsumer = new UmbContextConsumer(element, testContextAlias); + localConsumer.hostConnected(); + localConsumer + .asPromise() + .then((instance) => { + expect(instance?.prop).to.eq('value from provider'); + localConsumer.hostDisconnected(); + provider.hostDisconnected(); + done(); + }) + .catch(() => { + expect.fail('Promise should not reject'); + }); + + provider.hostConnected(); + }); + + it('gets rejected when using asPromise that does not resolve', (done) => { + const localConsumer = new UmbContextConsumer(element, testContextAlias); + + localConsumer + .asPromise() + .then((instance) => { + expect.fail('Promise should reject'); + }) + .catch(() => { + localConsumer.hostDisconnected(); + localConsumer.destroy(); + done(); + }); + localConsumer.hostConnected(); + }); + + it('never gets rejected when using asPromise that is set not to timeout and never will resolve', (done) => { + const localConsumer = new UmbContextConsumer(element, testContextAlias); + localConsumer.hostConnected(); + + const timeout = setTimeout(() => { + localConsumer.hostDisconnected(); + done(); + }, 200); + + try { + localConsumer + .asPromise({ preventTimeout: true }) + .then((instance) => { + clearTimeout(timeout); + expect.fail('Promise should not resolve'); + }) + .catch(() => { + clearTimeout(timeout); + expect.fail('Promise should not reject'); + }); + } catch (e) { + console.log('e', e); + } + }); + it('works with host as a method', (done) => { const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass()); provider.hostConnected(); - const element = document.createElement('div'); - document.body.appendChild(element); - const localConsumer = new UmbContextConsumer( () => element, testContextAlias, (_instance: UmbTestContextConsumerClass | undefined) => { if (_instance) { expect(_instance.prop).to.eq('value from provider'); - done(); localConsumer.hostDisconnected(); provider.hostDisconnected(); + done(); } }, ); @@ -97,12 +161,12 @@ describe('UmbContextConsumer', () => { }); it('works with host method returning undefined', async () => { - const element = undefined; + const notExistingElement = undefined; - const localConsumer = new UmbContextConsumer( - () => element, + const localConsumer = new UmbContextConsumer( + () => notExistingElement, testContextAlias, - (_instance: UmbTestContextConsumerClass | undefined) => { + (_instance) => { if (_instance) { expect.fail('Callback should not be called when never permitted'); } @@ -147,6 +211,15 @@ describe('UmbContextConsumer', () => { }); describe('Implementation with Api Alias', () => { + let element: HTMLElement; + beforeEach(() => { + element = document.createElement('div'); + document.body.appendChild(element); + }); + afterEach(() => { + document.body.removeChild(element); + }); + it('responds when api alias matches', (done) => { const provider = new UmbContextProvider( document.body, @@ -155,17 +228,18 @@ describe('UmbContextConsumer', () => { ); provider.hostConnected(); - const element = document.createElement('div'); - document.body.appendChild(element); - - const localConsumer = new UmbContextConsumer(element, testContextAliasAndApiAlias, (_instance) => { - if (_instance) { - expect((_instance as UmbTestContextConsumerClass).prop).to.eq('value from provider'); - localConsumer.hostDisconnected(); - provider.hostDisconnected(); - done(); - } - }); + const localConsumer = new UmbContextConsumer( + element, + testContextAliasAndApiAlias, + (_instance) => { + if (_instance) { + expect(_instance.prop).to.eq('value from provider'); + localConsumer.hostDisconnected(); + provider.hostDisconnected(); + done(); + } + }, + ); localConsumer.hostConnected(); }); @@ -177,9 +251,6 @@ describe('UmbContextConsumer', () => { ); provider.hostConnected(); - const element = document.createElement('div'); - document.body.appendChild(element); - const localConsumer = new UmbContextConsumer(element, testContextAliasAndNotExistingApiAlias, () => { expect(false).to.be.true; }); @@ -195,6 +266,15 @@ describe('UmbContextConsumer', () => { }); describe('Implementation with discriminator method', () => { + let element: HTMLElement; + beforeEach(() => { + element = document.createElement('div'); + document.body.appendChild(element); + }); + afterEach(() => { + document.body.removeChild(element); + }); + type A = { prop: string }; function discriminator(instance: unknown): instance is A { @@ -208,16 +288,20 @@ describe('UmbContextConsumer', () => { } it('discriminator determines the instance type', (done) => { + const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass()); + const localConsumer = new UmbContextConsumer( - document.body, + element, new UmbContextToken(testContextAlias, undefined, discriminator), (instance: A) => { expect(instance.prop).to.eq('value from provider'); - done(); + provider.destroy(); localConsumer.destroy(); + done(); }, ); localConsumer.hostConnected(); + provider.hostConnected(); // This bit of code is not really a test but it serves as a TypeScript type test, making sure the given type is matches the one given from the Discriminator method. type TestType = Exclude extends A ? true : never; @@ -229,17 +313,14 @@ describe('UmbContextConsumer', () => { const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass()); provider.hostConnected(); - const element = document.createElement('div'); - document.body.appendChild(element); - const localConsumer = new UmbContextConsumer( element, new UmbContextToken(testContextAlias, undefined, discriminator), (_instance) => { expect(_instance.prop).to.eq('value from provider'); - done(); localConsumer.hostDisconnected(); provider.hostDisconnected(); + done(); }, ); localConsumer.hostConnected(); @@ -249,9 +330,6 @@ describe('UmbContextConsumer', () => { const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass()); provider.hostConnected(); - const element = document.createElement('div'); - document.body.appendChild(element); - const localConsumer = new UmbContextConsumer( element, new UmbContextToken(testContextAlias, undefined, badDiscriminator), @@ -263,9 +341,9 @@ describe('UmbContextConsumer', () => { // Wait for to ensure the above request didn't succeed: Promise.resolve().then(() => { - done(); localConsumer.hostDisconnected(); provider.hostDisconnected(); + done(); }); }); @@ -273,9 +351,6 @@ describe('UmbContextConsumer', () => { const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass()); provider.hostConnected(); - const element = document.createElement('div'); - document.body.appendChild(element); - const alternativeProvider = new UmbContextProvider( element, testContextAlias, @@ -294,9 +369,9 @@ describe('UmbContextConsumer', () => { // Wait for to ensure the above request didn't succeed: Promise.resolve().then(() => { - done(); localConsumer.hostDisconnected(); provider.hostDisconnected(); + done(); }); }); @@ -304,9 +379,6 @@ describe('UmbContextConsumer', () => { const provider = new UmbContextProvider(document.body, testContextAlias, new UmbTestContextConsumerClass()); provider.hostConnected(); - const element = document.createElement('div'); - document.body.appendChild(element); - const alternativeProvider = new UmbContextProvider( element, testContextAlias, @@ -319,9 +391,9 @@ describe('UmbContextConsumer', () => { new UmbContextToken(testContextAlias, undefined, discriminator), (_instance) => { expect(_instance.prop).to.eq('value from provider'); - done(); localConsumer.hostDisconnected(); provider.hostDisconnected(); + done(); }, ); localConsumer.passContextAliasMatches(); diff --git a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.ts b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.ts index 0230a3f1fd..6fd55915e1 100644 --- a/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.ts +++ b/src/Umbraco.Web.UI.Client/src/libs/context-api/consume/context-consumer.ts @@ -5,17 +5,26 @@ import { UmbContextRequestEventImplementation } from './context-request.event.js type HostElementMethod = () => Element | undefined; +export type UmbContextConsumerAsPromiseOptionsType = { + preventTimeout?: boolean; +}; + /** * @class UmbContextConsumer + * @description The context consumer class, used to consume a context from a host element. + * Notice it is not recommended to use this class directly, but rather use the `consumeContext` method from a `UmbElement` or `UmbElementMixin` or `UmbControllerBase` or `UmbClassMixin`. + * Alternatively, you can use the `UmbContextConsumerController` to consume a context from a host element. But this does require that you can implement one of the Class Mixins mentioned above. */ export class UmbContextConsumer { protected _retrieveHost: HostElementMethod; + #raf?: number; #skipHost?: boolean; #stopAtContextMatch = true; #callback?: UmbContextCallback; - #promise?: Promise; + #promise?: Promise; #promiseResolver?: (instance: ResultType) => void; + #promiseRejecter?: (reason: string) => void; #instance?: ResultType; get instance() { @@ -83,7 +92,7 @@ export class UmbContextConsumer { + public asPromise(options?: UmbContextConsumerAsPromiseOptionsType): Promise { return ( this.#promise ?? - (this.#promise = new Promise((resolve) => { + (this.#promise = new Promise((resolve, reject) => { if (this.#instance) { + this.#promiseResolver = undefined; + this.#promiseRejecter = undefined; resolve(this.#instance); } else { this.#promiseResolver = resolve; + this.#promiseRejecter = options?.preventTimeout ? undefined : reject; } })) ); @@ -137,6 +152,20 @@ export class UmbContextConsumer 0 && this.#promiseRejecter) { + await new Promise((resolve) => requestAnimationFrame(resolve)); + } + */ + this.#raf = requestAnimationFrame(() => { + const hostElement = this._retrieveHost(); + // If we still have the rejecter, it means that the context was not found immediately, so lets reject the promise. [NL] + this.#promiseRejecter?.( + `Context could not be found. (Context Alias: ${this.#contextAlias} with API Alias: ${this.#apiAlias}). Controller is hosted on ${hostElement?.parentNode?.nodeName ?? 'Not attached node'} > ${hostElement?.nodeName}`, + ); + }); } public hostConnected(): void { @@ -147,6 +176,10 @@ export class UmbContextConsumer { // Does seem a bit unnecessary, we could just assume the type via type casting... @@ -186,6 +219,7 @@ export class UmbContextConsumer(superClass: T) async getContext( contextAlias: string | UmbContextToken, - ): Promise { + options?: UmbContextConsumerAsPromiseOptionsType, + ): Promise { const controller = new UmbContextConsumerController(this, contextAlias); - const promise = controller.asPromise().then((result) => { - controller.destroy(); - return result; - }); + const promise = controller + .asPromise(options) + .then((result) => { + controller.destroy(); + return result; + }) + .catch(() => { + controller.destroy(); + return undefined; + }); return promise; } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/block-grid-area-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/block-grid-area-type-workspace.context.ts index 192ba4d846..9cb21959c3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/block-grid-area-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-area-config-entry/workspace/block-grid-area-type-workspace.context.ts @@ -80,17 +80,22 @@ export class UmbBlockGridAreaTypeWorkspaceContext async load(unique: string) { this.resetState(); - const context = await this.getContext(UMB_PROPERTY_CONTEXT); - this.observe(context.value, (value) => { - if (value) { - const blockTypeData = value.find((x: UmbBlockGridTypeAreaType) => x.key === unique); - if (blockTypeData) { - this.#data.setValue(blockTypeData); - return; - } - } - // Fallback to undefined: - this.#data.setValue(undefined); + this.consumeContext(UMB_PROPERTY_CONTEXT, (context) => { + this.observe( + context.value, + (value) => { + if (value) { + const blockTypeData = value.find((x: UmbBlockGridTypeAreaType) => x.key === unique); + if (blockTypeData) { + this.#data.setValue(blockTypeData); + return; + } + } + // Fallback to undefined: + this.#data.setValue(undefined); + }, + 'observePropertyValue', + ); }); } @@ -171,6 +176,9 @@ export class UmbBlockGridAreaTypeWorkspaceContext } const context = await this.getContext(UMB_PROPERTY_CONTEXT); + if (!context) { + throw new Error('Property context not found.'); + } // TODO: We should most likely consume already, in this way I avoid having the reset this consumption. context.setValue(appendToFrozenArray(context.getValue() ?? [], this.#data.getValue(), (x) => x?.key)); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts index e410f894d0..d7eb3d41a2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entries.context.ts @@ -165,12 +165,18 @@ export class UmbBlockGridEntriesContext // Idea: Maybe on setup should be async, so it can retrieve the values when needed? [NL] const index = routingInfo.index ? parseInt(routingInfo.index) : -1; const clipboardContext = await this.getContext(UMB_CLIPBOARD_PROPERTY_CONTEXT); + if (!clipboardContext) { + throw new Error('Clipboard context not available'); + } const pasteTranslatorManifests = clipboardContext.getPasteTranslatorManifests( UMB_BLOCK_GRID_PROPERTY_EDITOR_UI_ALIAS, ); // TODO: consider moving some of this logic to the clipboard property context const propertyContext = await this.getContext(UMB_PROPERTY_CONTEXT); + if (!propertyContext) { + throw new Error('Property context not available'); + } const config = propertyContext.getConfig() as UmbBlockGridPropertyEditorConfig; const valueResolver = new UmbClipboardPastePropertyValueTranslatorValueResolver(this); @@ -234,7 +240,9 @@ export class UmbBlockGridEntriesContext } } else if (value?.clipboard && value.clipboard.selection?.length && data) { const clipboardContext = await this.getContext(UMB_CLIPBOARD_PROPERTY_CONTEXT); - + if (!clipboardContext) { + throw new Error('Clipboard context not available'); + } const propertyValues = await clipboardContext.readMultiple( value.clipboard.selection, UMB_BLOCK_GRID_PROPERTY_EDITOR_UI_ALIAS, diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entry.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entry.context.ts index de3b11d474..94ccd89e69 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entry.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-entry.context.ts @@ -293,6 +293,9 @@ export class UmbBlockGridEntryContext const propertyDatasetContext = await this.getContext(UMB_PROPERTY_DATASET_CONTEXT); const propertyContext = await this.getContext(UMB_PROPERTY_CONTEXT); const clipboardContext = await this.getContext(UMB_CLIPBOARD_PROPERTY_CONTEXT); + if (!clipboardContext) { + throw new Error('No clipboard context found'); + } const workspaceName = propertyDatasetContext?.getName(); const propertyLabel = propertyContext?.getLabel(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts index 7fb16cd573..c7ed061375 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/context/block-grid-manager.context.ts @@ -33,7 +33,7 @@ export class UmbBlockGridManagerContext< return this.#inlineEditingMode.getValue(); } - #initAppUrl: Promise; + #initAppUrl: Promise; #serverUrl?: string; @@ -87,9 +87,9 @@ export class UmbBlockGridManagerContext< constructor(host: UmbControllerHost) { super(host); - this.#initAppUrl = this.getContext(UMB_APP_CONTEXT).then((appContext) => { + this.#initAppUrl = this.consumeContext(UMB_APP_CONTEXT, (appContext) => { this.#serverUrl = appContext.getServerUrl(); - }); + }).asPromise({ preventTimeout: true }); } /** * @deprecated Use createWithPresets instead. Will be removed in v.17. diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts index af584330ef..15e9592308 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/components/block-list-entry/block-list-entry.element.ts @@ -292,6 +292,9 @@ export class UmbBlockListEntryElement extends UmbLitElement implements UmbProper const propertyDatasetContext = await this.getContext(UMB_PROPERTY_DATASET_CONTEXT); const propertyContext = await this.getContext(UMB_PROPERTY_CONTEXT); const clipboardContext = await this.getContext(UMB_CLIPBOARD_PROPERTY_CONTEXT); + if (!propertyDatasetContext || !propertyContext || !clipboardContext) { + throw new Error('Could not get required contexts to copy.'); + } const workspaceName = propertyDatasetContext?.getName(); const propertyLabel = propertyContext?.getLabel(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-entries.context.ts index e625f4d298..c7fb204505 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/context/block-list-entries.context.ts @@ -39,6 +39,9 @@ export class UmbBlockListEntriesContext extends UmbBlockEntriesContext< if (!this._manager) return false; const index = routingInfo.index ? parseInt(routingInfo.index) : -1; const clipboardContext = await this.getContext(UMB_CLIPBOARD_PROPERTY_CONTEXT); + if (!clipboardContext) { + throw new Error('Clipboard context not found'); + } const pasteTranslatorManifests = clipboardContext.getPasteTranslatorManifests( UMB_BLOCK_LIST_PROPERTY_EDITOR_UI_ALIAS, @@ -46,6 +49,9 @@ export class UmbBlockListEntriesContext extends UmbBlockEntriesContext< // TODO: consider moving some of this logic to the clipboard property context const propertyContext = await this.getContext(UMB_PROPERTY_CONTEXT); + if (!propertyContext) { + throw new Error('Property context not found'); + } const config = propertyContext.getConfig(); const valueResolver = new UmbClipboardPastePropertyValueTranslatorValueResolver(this); @@ -103,7 +109,9 @@ export class UmbBlockListEntriesContext extends UmbBlockEntriesContext< } } else if (value?.clipboard && value.clipboard.selection?.length && data) { const clipboardContext = await this.getContext(UMB_CLIPBOARD_PROPERTY_CONTEXT); - + if (!clipboardContext) { + throw new Error('Clipboard context not found'); + } const propertyValues = await clipboardContext.readMultiple( value.clipboard.selection, UMB_BLOCK_LIST_PROPERTY_EDITOR_UI_ALIAS, diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts index 9e9160deaa..d6c8a1f16d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-card/block-type-card.element.ts @@ -12,7 +12,7 @@ import { UUICardEvent } from '@umbraco-cms/backoffice/external/uui'; @customElement('umb-block-type-card') export class UmbBlockTypeCardElement extends UmbLitElement { // - #init: Promise; + #init: Promise; #serverUrl: string = ''; readonly #itemManager = new UmbRepositoryItemsManager( @@ -76,9 +76,9 @@ export class UmbBlockTypeCardElement extends UmbLitElement { constructor() { super(); - this.#init = this.getContext(UMB_APP_CONTEXT).then((appContext) => { + this.#init = this.consumeContext(UMB_APP_CONTEXT, (appContext) => { this.#serverUrl = appContext.getServerUrl(); - }); + }).asPromise({ preventTimeout: true }); this.observe(this.#itemManager.statuses, async (statuses) => { const status = statuses[0]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-custom-view-guide/block-type-custom-view-guide.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-custom-view-guide/block-type-custom-view-guide.element.ts index 923339fc71..5d54f25b41 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-custom-view-guide/block-type-custom-view-guide.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/block-type-custom-view-guide/block-type-custom-view-guide.element.ts @@ -6,7 +6,7 @@ import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registr import { stringOrStringArrayContains } from '@umbraco-cms/backoffice/utils'; import { UmbExtensionsManifestInitializer } from '@umbraco-cms/backoffice/extension-api'; import { UmbDocumentTypeDetailRepository } from '@umbraco-cms/backoffice/document-type'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import type { ManifestBlockEditorCustomView } from '@umbraco-cms/backoffice/block-custom-view'; @customElement('umb-block-type-custom-view-guide') @@ -82,13 +82,10 @@ export class UmbBlockTypeCustomViewGuideElement extends UmbLitElement { }; async #viewManifest(manifest: ManifestBlockEditorCustomView) { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - modalManager.open(this, UMB_MANIFEST_VIEWER_MODAL, { data: manifest }); + umbOpenModal(this, UMB_MANIFEST_VIEWER_MODAL, { data: manifest }); } async #generateManifest() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const manifest: UmbExtensionManifest = { type: 'blockEditorCustomView', alias: 'Local.blockEditorCustomView.' + this.#contentTypeAlias, @@ -97,7 +94,7 @@ export class UmbBlockTypeCustomViewGuideElement extends UmbLitElement { forContentTypeAlias: this.#contentTypeAlias, forBlockEditor: this.#blockEditorType, }; - modalManager.open(this, UMB_MANIFEST_VIEWER_MODAL, { data: manifest }); + umbOpenModal(this, UMB_MANIFEST_VIEWER_MODAL, { data: manifest }); } override render() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/input-block-type/input-block-type.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/input-block-type/input-block-type.element.ts index 5664722449..b3aeda0d3f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/input-block-type/input-block-type.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/components/input-block-type/input-block-type.element.ts @@ -140,6 +140,9 @@ export class UmbInputBlockTypeElement< async #onRequestDelete(item: BlockType) { const store = await this.getContext(UMB_DOCUMENT_TYPE_ITEM_STORE_CONTEXT); + if (!store) { + return; + } const contentType = store.getItems([item.contentElementTypeKey]); await umbConfirmModal(this, { color: 'danger', diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context.ts index e9e6d295af..22bc74416d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-type/workspace/block-type-workspace.context.ts @@ -25,6 +25,9 @@ export class UmbBlockTypeWorkspaceContext; + #propertyContext?: typeof UMB_PROPERTY_CONTEXT.TYPE; + #entityType: string; #data = new UmbObjectState(undefined); readonly data = this.#data.asObservable(); @@ -44,6 +47,10 @@ export class UmbBlockTypeWorkspaceContext { + this.#propertyContext = context; + }).asPromise({ preventTimeout: true }); + this.routes.setRoutes([ { // Would it make more sense to have groupKey before elementTypeKey? @@ -84,18 +91,23 @@ export class UmbBlockTypeWorkspaceContext { - if (value) { - const blockTypeData = value.find((x: UmbBlockTypeBaseModel) => x.contentElementTypeKey === unique); - if (blockTypeData) { - this.#data.setValue(blockTypeData); - return; + await this.#gotPropertyContext; + + this.observe( + this.#propertyContext?.value, + (value) => { + if (value) { + const blockTypeData = value.find((x: UmbBlockTypeBaseModel) => x.contentElementTypeKey === unique); + if (blockTypeData) { + this.#data.setValue(blockTypeData); + return; + } } - } - // Fallback to undefined: - this.#data.setValue(undefined); - }); + // Fallback to undefined: + this.#data.setValue(undefined); + }, + 'observePropertyValue', + ); } async create(contentElementTypeId: string, groupKey?: string | null) { @@ -174,10 +186,16 @@ export class UmbBlockTypeWorkspaceContext x?.contentElementTypeKey), + await this.#gotPropertyContext; + if (!this.#propertyContext) { + throw new Error('Property context is not available.'); + } + this.#propertyContext.setValue( + appendToFrozenArray( + this.#propertyContext.getValue() ?? [], + this.#data.getValue(), + (x) => x?.contentElementTypeKey, + ), ); this.setIsNew(false); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts index 99e7a726df..5d959e088a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-entries.context.ts @@ -53,7 +53,7 @@ export abstract class UmbBlockEntriesContext< this._retrieveManager = this.consumeContext(blockManagerContextToken, (blockGridManager) => { this._manager = blockGridManager; this._gotBlockManager(); - }).asPromise(); + }).asPromise({ preventTimeout: true }); } async getManager() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts index f851d49ee5..da15247980 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/context/block-manager.context.ts @@ -363,6 +363,9 @@ export abstract class UmbBlockManagerContext< protected async _createBlockElementData(key: string, contentTypeKey: string) { // const appLanguage = await this.getContext(UMB_APP_LANGUAGE_CONTEXT); + if (!appLanguage) { + throw new Error('Could not retrieve app language context.'); + } const contentStructure = this.getStructure(contentTypeKey); if (!contentStructure) { @@ -501,6 +504,9 @@ export abstract class UmbBlockManagerContext< if (varyByCulture) { // get all mandatory cultures: const appLanguageContext = await this.getContext(UMB_APP_LANGUAGE_CONTEXT); + if (!appLanguageContext) { + throw new Error('Could not retrieve app language context.'); + } const mandatoryLanguages = await appLanguageContext.getMandatoryLanguages(); mandatoryLanguages.forEach((x) => { // No need to insert the same expose twice: diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts index b565d4de48..8fc6a90af8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block/workspace/block-workspace.context.ts @@ -88,7 +88,7 @@ export class UmbBlockWorkspaceContext { this.#blockManager = manager; @@ -97,7 +97,7 @@ export class UmbBlockWorkspaceContext { this.#blockEntries = context; - }).asPromise(); + }).asPromise({ preventTimeout: true }); this.consumeContext(UMB_BLOCK_ENTRY_CONTEXT, (context) => { this.#name.setValue(context.getName()); diff --git a/src/Umbraco.Web.UI.Client/src/packages/clipboard/clipboard-local-storage.manager.ts b/src/Umbraco.Web.UI.Client/src/packages/clipboard/clipboard-local-storage.manager.ts index 434d3bbb0a..1e5e49f1b9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/clipboard/clipboard-local-storage.manager.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/clipboard/clipboard-local-storage.manager.ts @@ -94,6 +94,9 @@ export class UmbClipboardLocalStorageManager extends UmbControllerBase { } const context = await this.getContext(UMB_CURRENT_USER_CONTEXT); + if (!context) { + throw new Error('Could not get current user context'); + } this.#currentUserUnique = context.getUnique(); return this.#currentUserUnique; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/clipboard/property/actions/paste/paste-from-clipboard.property-action.ts b/src/Umbraco.Web.UI.Client/src/packages/clipboard/property/actions/paste/paste-from-clipboard.property-action.ts index e4c54117e8..6af9a0d14a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/clipboard/property/actions/paste/paste-from-clipboard.property-action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/clipboard/property/actions/paste/paste-from-clipboard.property-action.ts @@ -73,7 +73,7 @@ export class UmbPasteFromClipboardPropertyAction extends UmbPropertyActionBase { #init?: Promise; - #modalManagerContext?: typeof UMB_MODAL_MANAGER_CONTEXT.TYPE; - constructor(host: UmbControllerHost) { super(host, UMB_CLIPBOARD_PROPERTY_CONTEXT); - - this.#init = Promise.all([ - this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (context) => { - this.#modalManagerContext = context; - }).asPromise(), - ]); } /** @@ -99,6 +91,9 @@ export class UmbClipboardPropertyContext extends UmbContextBase { const clipboardContext = await this.getContext(UMB_CLIPBOARD_CONTEXT); + if (!clipboardContext) { + throw new Error('Clipboard context is required'); + } const copyValueResolver = new UmbClipboardCopyPropertyValueTranslatorValueResolver(this); const values = await copyValueResolver.resolve(args.propertyValue, args.propertyEditorUiAlias); @@ -129,11 +124,15 @@ export class UmbClipboardPropertyContext extends UmbContextBase { const hasSupportedPasteTranslator = this.hasSupportedPasteTranslator( @@ -164,7 +163,6 @@ export class UmbClipboardPropertyContext extends UmbContextBase {}); } constructor() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts index 8400d72aa5..e314529537 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/collection/default/collection-default.context.ts @@ -292,6 +292,7 @@ export class UmbDefaultCollectionContext< #onReloadChildrenRequest = async (event: UmbRequestReloadChildrenOfEntityEvent) => { // check if the collection is in the same context as the entity from the event const entityContext = await this.getContext(UMB_ENTITY_CONTEXT); + if (!entityContext) return; const unique = entityContext.getUnique(); const entityType = entityContext.getEntityType(); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/entity-actions-bundle/entity-actions-bundle.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/entity-actions-bundle/entity-actions-bundle.element.ts index b873cd7771..50f0b0cfee 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/entity-actions-bundle/entity-actions-bundle.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/entity-actions-bundle/entity-actions-bundle.element.ts @@ -104,7 +104,7 @@ export class UmbEntityActionsBundleElement extends UmbLitElement { } event.stopPropagation(); - await this._firstActionApi?.execute(); + await this._firstActionApi?.execute().catch(() => {}); } #onActionExecuted() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-manifest/input-manifest.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-manifest/input-manifest.element.ts index 9a32e98752..271dfe993c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/input-manifest/input-manifest.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/input-manifest/input-manifest.element.ts @@ -33,6 +33,9 @@ export class UmbInputManifestElement extends UmbLitElement { async #onClick() { const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + if (!modalManager) { + throw new Error('Modal manager not found.'); + } const modalContext = modalManager.open(this, UMB_ITEM_PICKER_MODAL, { data: { headline: `${this.localize.term('general_choose')}...`, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/global-components/content-type-workspace-editor-header.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/global-components/content-type-workspace-editor-header.element.ts index fc3180ee1d..16d1524652 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/global-components/content-type-workspace-editor-header.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/global-components/content-type-workspace-editor-header.element.ts @@ -51,6 +51,9 @@ export class UmbContentTypeWorkspaceEditorHeaderElement extends UmbLitElement { private async _handleIconClick() { const [alias, color] = this._icon?.replace('color-', '')?.split(' ') ?? []; const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + if (!modalManager) { + throw new Error('Modal manager not found.'); + } const modalContext = modalManager.open(this, UMB_ICON_PICKER_MODAL, { value: { icon: alias, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context-base.ts index 42fd994550..030b2cefc2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/content-type-workspace-context-base.ts @@ -153,6 +153,9 @@ export abstract class UmbContentTypeWorkspaceContextBase< this._data.setPersisted(this.structure.getOwnerContentType()); const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!eventContext) { + throw new Error('Could not get the action event context'); + } const event = new UmbRequestReloadChildrenOfEntityEvent({ entityType: parent.entityType, unique: parent.unique, @@ -176,6 +179,9 @@ export abstract class UmbContentTypeWorkspaceContextBase< this._data.setPersisted(this.structure.getOwnerContentType()); const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!actionEventContext) { + throw new Error('Could not get the action event context'); + } const event = new UmbRequestReloadStructureForEntityEvent({ unique: this.getUnique()!, entityType: this.getEntityType(), diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts index 7ca1e9a1cb..2da0806586 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content-type/workspace/views/design/content-type-design-editor.element.ts @@ -18,7 +18,7 @@ import type { UmbWorkspaceViewElement, } from '@umbraco-cms/backoffice/workspace'; import type { UmbConfirmModalData } from '@umbraco-cms/backoffice/modal'; -import { UMB_MODAL_MANAGER_CONTEXT, umbConfirmModal } from '@umbraco-cms/backoffice/modal'; +import { umbConfirmModal, umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; @@ -367,15 +367,13 @@ export class UmbContentTypeDesignEditorElement extends UmbLitElement implements isNew: this.#workspaceContext.getIsNew()!, }; - const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManagerContext.open(this, UMB_COMPOSITION_PICKER_MODAL, { + const value = await umbOpenModal(this, UMB_COMPOSITION_PICKER_MODAL, { data: compositionConfiguration, - }); - await modalContext?.onSubmit(); + }).catch(() => undefined); - if (!modalContext?.value) return; + if (!value) return; - const compositionIds = modalContext.getValue().selection; + const compositionIds = value.selection; this.#workspaceContext?.setCompositions( compositionIds.map((unique) => ({ contentType: { unique }, compositionType: CompositionTypeModel.COMPOSITION })), diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/content-detail-workspace-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/content-detail-workspace-base.ts index 363a73bdf3..91b9d2f5bc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/content-detail-workspace-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/content/workspace/content-detail-workspace-base.ts @@ -39,7 +39,7 @@ import { UmbVariantValuesValidationPathTranslator, } from '@umbraco-cms/backoffice/validation'; import type { UmbModalToken } from '@umbraco-cms/backoffice/modal'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UmbEntityUpdatedEvent, @@ -578,6 +578,9 @@ export abstract class UmbContentDetailWorkspaceContextBase< const variantsWithoutAName = saveData.variants.filter((x) => !x.name); if (variantsWithoutAName.length > 0) { const validationContext = await this.getContext(UMB_VALIDATION_CONTEXT); + if (!validationContext) { + throw new Error('Validation context is missing'); + } variantsWithoutAName.forEach((variant) => { validationContext.messages.addMessage( 'client', @@ -657,17 +660,13 @@ export abstract class UmbContentDetailWorkspaceContextBase< variantIds.push(UmbVariantId.Create(options[0])); } else if (this.#saveModalToken) { // If there are multiple variants, we will open the modal to let the user pick which variants to save. - const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const result = await modalManagerContext - .open(this, this.#saveModalToken, { - data: { - options, - pickableFilter: this._saveableVariantsFilter, - }, - value: { selection: selected }, - }) - .onSubmit() - .catch(() => undefined); + const result = await umbOpenModal(this, this.#saveModalToken, { + data: { + options, + pickableFilter: this._saveableVariantsFilter, + }, + value: { selection: selected }, + }).catch(() => undefined); if (!result?.selection.length) return; @@ -753,6 +752,9 @@ export abstract class UmbContentDetailWorkspaceContextBase< this._data.setCurrent(newCurrentData); const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!eventContext) { + throw new Error('Event context is missing'); + } const event = new UmbRequestReloadChildrenOfEntityEvent({ entityType: parent.entityType, unique: parent.unique, @@ -797,6 +799,9 @@ export abstract class UmbContentDetailWorkspaceContextBase< const entityType = this.getEntityType(); const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!eventContext) { + throw new Error('Event context is missing'); + } const structureEvent = new UmbRequestReloadStructureForEntityEvent({ unique, entityType }); eventContext.dispatchEvent(structureEvent); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/common/create/create.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/common/create/create.action.ts index 9d869b0b97..8f893b31ea 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/common/create/create.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/common/create/create.action.ts @@ -2,7 +2,7 @@ import { UmbEntityActionBase } from '../../entity-action-base.js'; import type { UmbEntityActionArgs } from '../../types.js'; import type { MetaEntityActionCreateKind } from './types.js'; import { UMB_ENTITY_CREATE_OPTION_ACTION_LIST_MODAL } from './modal/constants.js'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { @@ -24,9 +24,9 @@ export class UmbCreateEntityAction extends UmbEntityActionBase) { super(host, args); - /* This is wrapped in a promise to confirm whether only one option exists and to ensure - that the API for this option has been created. We both need to wait for any options to - be returned from the registry and for the API to be created. This is a custom promise implementation, + /* This is wrapped in a promise to confirm whether only one option exists and to ensure + that the API for this option has been created. We both need to wait for any options to + be returned from the registry and for the API to be created. This is a custom promise implementation, because using .asPromise() on the initializer does not wait for the async API creation in the callback.*/ this.#optionsInit = new Promise((resolve) => { new UmbExtensionsManifestInitializer( @@ -61,15 +61,12 @@ export class UmbCreateEntityAction extends UmbEntityActionBase { + this._submitModal(); + }) + .catch(() => {}); } async #onNavigate(event: Event, href: string | undefined) { @@ -135,7 +138,7 @@ export class UmbEntityCreateOptionActionListModalElement extends UmbModalBaseEle icon=${manifest.meta.icon} href=${ifDefined(href)} target=${this.#getTarget(href)} - @open=${(event: Event) => this.#onOpen(event, controller)} + @open=${async (event: Event) => await this.#onOpen(event, controller).catch(() => undefined)} @click=${(event: Event) => this.#onNavigate(event, href)}> `; 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 4fa014b073..50186d74d7 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 @@ -60,6 +60,9 @@ export class UmbDeleteEntityAction< async #notify() { const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!actionEventContext) { + throw new Error('Action event context not found.'); + } const event = new UmbRequestReloadStructureForEntityEvent({ unique: this.args.unique, 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 cc0d2710e6..392930b313 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 @@ -30,6 +30,9 @@ export class UmbDuplicateEntityAction extends UmbEntityActionBase { async #reloadMenu() { const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!actionEventContext) { + throw new Error('Action event context is not available'); + } const event = new UmbRequestReloadStructureForEntityEvent({ unique: this.args.unique, entityType: this.args.entityType, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/default/entity-action.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/default/entity-action.element.ts index 2389940612..789b50c601 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/default/entity-action.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-action/default/entity-action.element.ts @@ -49,7 +49,7 @@ export class UmbEntityActionDefaultElement< async #onClickLabel(event: UUIMenuItemEvent) { if (!this._href) { event.stopPropagation(); - await this.#api?.execute(); + await this.#api?.execute().catch(() => {}); } this.dispatchEvent(new UmbActionExecutedEvent()); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/duplicate-to/duplicate-to.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/duplicate-to/duplicate-to.action.ts index d865a17688..3ed6feda54 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/duplicate-to/duplicate-to.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/duplicate-to/duplicate-to.action.ts @@ -7,7 +7,7 @@ import { } from '@umbraco-cms/backoffice/entity-action'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UMB_ENTITY_CONTEXT } from '@umbraco-cms/backoffice/entity'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_TREE_PICKER_MODAL } from '@umbraco-cms/backoffice/tree'; import type { MetaEntityBulkActionDuplicateToKind } from '@umbraco-cms/backoffice/extension-registry'; @@ -15,9 +15,7 @@ export class UmbMediaDuplicateEntityBulkAction extends UmbEntityBulkActionBase undefined); if (!value?.selection?.length) return; const destinationUnique = value.selection[0]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/move-to/move-to.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/move-to/move-to.action.ts index f248afe4e8..38d6b6cf03 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/move-to/move-to.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/common/move-to/move-to.action.ts @@ -7,7 +7,7 @@ import { } from '@umbraco-cms/backoffice/entity-action'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UMB_ENTITY_CONTEXT } from '@umbraco-cms/backoffice/entity'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_TREE_PICKER_MODAL } from '@umbraco-cms/backoffice/tree'; import type { MetaEntityBulkActionMoveToKind } from '@umbraco-cms/backoffice/extension-registry'; @@ -15,9 +15,7 @@ export class UmbMediaMoveEntityBulkAction extends UmbEntityBulkActionBase undefined); if (!value?.selection?.length) return; const destinationUnique = value.selection[0]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/entity-bulk-action.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/entity-bulk-action.element.ts index 0478595f71..66513f528e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/entity-bulk-action.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/entity-bulk-action/entity-bulk-action.element.ts @@ -26,7 +26,7 @@ export class UmbEntityBulkActionDefaultElement< async #onClick(event: PointerEvent) { if (!this.api) return; event.stopPropagation(); - await this.api.execute(); + await this.api.execute().catch(() => {}); this.dispatchEvent(new UmbActionExecutedEvent()); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/extension-element-and-api-slot-element-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/extension-element-and-api-slot-element-base.ts index 7ef30b8a22..8837c0ea38 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/extension-element-and-api-slot-element-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/extension-element-and-api-slot-element-base.ts @@ -4,8 +4,6 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import type { ManifestElementAndApi } from '@umbraco-cms/backoffice/extension-api'; import { UmbExtensionElementAndApiInitializer } from '@umbraco-cms/backoffice/extension-api'; -// TODO: Eslint: allow abstract element class to end with "ElementBase" instead of "Element" - export abstract class UmbExtensionElementAndApiSlotElementBase< ManifestType extends ManifestElementAndApi, > extends UmbLitElement { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.controller.ts index 8deb91202b..7e2b9d6bbf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/common/confirm/confirm-modal.controller.ts @@ -1,38 +1,17 @@ -import { UMB_MODAL_MANAGER_CONTEXT } from '../../context/index.js'; +import { UmbOpenModalController } from '../../index.js'; import { UMB_CONFIRM_MODAL, type UmbConfirmModalData } from './confirm-modal.token.js'; -import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; +/** @deprecated use `UmbConfirmModalData`, will be removed in v.17 */ // eslint-disable-next-line @typescript-eslint/no-empty-object-type export interface UmbConfirmModalArgs extends UmbConfirmModalData {} -export class UmbConfirmModalController extends UmbControllerBase { - async open(args: UmbConfirmModalArgs): Promise { - const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - - const modalContext = modalManagerContext.open(this, UMB_CONFIRM_MODAL, { - data: args, - }); - - const p = modalContext.onSubmit(); - p.catch(() => { - this.destroy(); - }); - await p; - - // This is a one time off, so we can destroy our selfs. - this.destroy(); - - return; - } -} - /** * * @param host {UmbControllerHost} - The host controller - * @param args {UmbConfirmModalArgs} - The data to pass to the modal - * @returns {UmbConfirmModalController} The modal controller instance + * @param args {UmbConfirmModalData} - The data to pass to the modal + * @returns {UmbOpenModalController} The modal controller instance */ -export function umbConfirmModal(host: UmbControllerHost, args: UmbConfirmModalArgs) { - return new UmbConfirmModalController(host).open(args); +export function umbConfirmModal(host: UmbControllerHost, data: UmbConfirmModalData) { + return new UmbOpenModalController(host).open(UMB_CONFIRM_MODAL, { data }); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/context/modal.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/context/modal.context.ts index aa468f7dd6..c8af3838db 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/context/modal.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/context/modal.context.ts @@ -116,7 +116,7 @@ export class UmbModalContext< async _internal_removeCurrentModal() { const routeContext = await this.getContext(UMB_ROUTE_CONTEXT); - routeContext._internal_removeModalPath(this.#activeModalPath); + routeContext?._internal_removeModalPath(this.#activeModalPath); } forceResolve() { @@ -151,8 +151,9 @@ export class UmbModalContext< this._internal_removeCurrentModal(); return; } - this.#submitResolver?.(this.getValue()); + const resolver = this.#submitResolver; this.#markAsResolved(); + resolver?.(this.getValue()); // TODO: Could we clean up this class here? (Example destroy the value state, and other things?) } @@ -171,8 +172,9 @@ export class UmbModalContext< this._internal_removeCurrentModal(); return; } - this.#submitRejecter?.(reason); + const resolver = this.#submitRejecter; this.#markAsResolved(); + resolver?.(reason); // TODO: Could we clean up this class here? (Example destroy the value state, and other things?) } @@ -182,8 +184,8 @@ export class UmbModalContext< * @public * @memberof UmbModalContext */ - public onSubmit() { - return this.#submitPromise; + public async onSubmit() { + return await this.#submitPromise; } /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/controller/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/controller/index.ts new file mode 100644 index 0000000000..c924e43b5c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/controller/index.ts @@ -0,0 +1 @@ +export * from './open-modal.controller.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/controller/open-modal.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/controller/open-modal.controller.ts new file mode 100644 index 0000000000..55ada1c8dc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/controller/open-modal.controller.ts @@ -0,0 +1,45 @@ +import { UMB_MODAL_MANAGER_CONTEXT, type UmbModalContextClassArgs } from '../index.js'; +import type { UmbModalToken } from '../token/modal-token.js'; +import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; +import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; + +export class UmbOpenModalController extends UmbControllerBase { + async open< + ModalData extends { [key: string]: any } = { [key: string]: any }, + ModalValue = unknown, + ModalAliasTypeAsToken extends UmbModalToken = UmbModalToken, + >( + modalAlias: UmbModalToken | string, + args: UmbModalContextClassArgs = {}, + ): Promise { + const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + if (!modalManagerContext) { + this.destroy(); + throw new Error('Modal manager not found.'); + } + + const modalContext = modalManagerContext.open(this, modalAlias, args); + + return await modalContext.onSubmit().finally(() => { + this.destroy(); + }); + } +} + +/** + * + * @param host {UmbControllerHost} - The host controller + * @param args {UmbConfirmModalArgs} - The data to pass to the modal + * @returns {UmbConfirmModalController} The modal controller instance + */ +export function umbOpenModal< + ModalData extends { [key: string]: any } = { [key: string]: any }, + ModalValue = unknown, + ModalAliasTypeAsToken extends UmbModalToken = UmbModalToken, +>( + host: UmbControllerHost, + modalAlias: UmbModalToken | string, + args: UmbModalContextClassArgs = {}, +): Promise { + return new UmbOpenModalController(host).open(modalAlias, args); +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/modal/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/modal/index.ts index 5403700cff..128bf4886b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/modal/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/modal/index.ts @@ -1,6 +1,7 @@ import './component/modal.element.js'; export * from './common/index.js'; +export * from './controller/index.js'; export * from './component/modal-base.element.js'; export * from './component/modal.element.js'; export * from './context/index.js'; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/notification/controllers/peek-error/peek-error-notification.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/notification/controllers/peek-error/peek-error-notification.element.ts index 30c920b6be..768581b6b6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/notification/controllers/peek-error/peek-error-notification.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/notification/controllers/peek-error/peek-error-notification.element.ts @@ -13,6 +13,9 @@ export class UmbPeekErrorNotificationElement extends UmbLitElement { async #onClick() { const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + if (!modalManager) { + throw new Error('Modal manager not found.'); + } modalManager.open(this, UMB_ERROR_VIEWER_MODAL, { data: this.data?.details }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/notification/controllers/peek-error/peek-error.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/notification/controllers/peek-error/peek-error.controller.ts index 80dc9638a1..342f48291f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/notification/controllers/peek-error/peek-error.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/notification/controllers/peek-error/peek-error.controller.ts @@ -8,6 +8,9 @@ import './peek-error-notification.element.js'; export class UmbPeekErrorController extends UmbControllerBase { async open(args: UmbPeekErrorArgs): Promise { const context = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!context) { + throw new Error('Could not get notification context'); + } context.peek('danger', { elementName: 'umb-peek-error-notification', diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/picker-input/picker-input.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/picker-input/picker-input.context.ts index 057ba23d72..535b109e5e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/picker-input/picker-input.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/picker-input/picker-input.context.ts @@ -2,7 +2,7 @@ import { UMB_PICKER_INPUT_CONTEXT } from './picker-input.context-token.js'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; import { UmbRepositoryItemsManager } from '@umbraco-cms/backoffice/repository'; -import { UMB_MODAL_MANAGER_CONTEXT, umbConfirmModal } from '@umbraco-cms/backoffice/modal'; +import { umbConfirmModal, umbOpenModal } from '@umbraco-cms/backoffice/modal'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository'; import type { UmbModalToken, UmbPickerModalData, UmbPickerModalValue } from '@umbraco-cms/backoffice/modal'; @@ -91,8 +91,7 @@ export class UmbPickerInputContext< async openPicker(pickerData?: Partial) { await this.#itemManager.init; - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, this.modalAlias, { + const modalValue = await umbOpenModal(this, this.modalAlias, { data: { multiple: this._max === 1 ? false : true, ...pickerData, @@ -102,7 +101,6 @@ export class UmbPickerInputContext< } as PickerModalValueType, }); - const modalValue = await modalContext?.onSubmit(); this.setSelection(modalValue.selection); this.getHostElement().dispatchEvent(new UmbChangeEvent()); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/common/clear/property-action-clear.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/common/clear/property-action-clear.controller.ts index b4ce1068eb..9cc85c43ef 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/common/clear/property-action-clear.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/common/clear/property-action-clear.controller.ts @@ -4,6 +4,9 @@ import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property'; export class UmbClearPropertyAction extends UmbPropertyActionBase { override async execute() { const propertyContext = await this.getContext(UMB_PROPERTY_CONTEXT); + if (!propertyContext) { + return; + } propertyContext.clearValue(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/kinds/default/property-action.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/kinds/default/property-action.element.ts index 55d2da24bf..d57abc563d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-action/kinds/default/property-action.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-action/kinds/default/property-action.element.ts @@ -34,7 +34,7 @@ export class UmbPropertyActionElement< async #onClickLabel(event: UUIMenuItemEvent) { if (!this._href) { event.stopPropagation(); - await this.#api?.execute(); + await this.#api?.execute().catch(() => {}); } this.dispatchEvent(new UmbActionExecutedEvent()); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/property-type-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/property-type-workspace.context.ts index 01224eb0f5..219b1eeb3f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/property-type-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/property-type-workspace.context.ts @@ -57,7 +57,7 @@ export class UmbPropertyTypeWorkspaceContext 0) { const unique = selection[0]; this.setDestination(unique); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-action/restore-from-recycle-bin/restore-from-recycle-bin.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-action/restore-from-recycle-bin/restore-from-recycle-bin.action.ts index 30b218649d..cd8e4ac1b2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-action/restore-from-recycle-bin/restore-from-recycle-bin.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/recycle-bin/entity-action/restore-from-recycle-bin/restore-from-recycle-bin.action.ts @@ -1,6 +1,6 @@ import { UMB_RESTORE_FROM_RECYCLE_BIN_MODAL } from './modal/restore-from-recycle-bin-modal.token.js'; import type { MetaEntityActionRestoreFromRecycleBinKind } from './types.js'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UmbEntityActionBase, UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/entity-action'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; @@ -17,8 +17,7 @@ export class UmbRestoreFromRecycleBinEntityAction extends UmbEntityActionBase { this.#detailStore = instance; - }).asPromise(), + }).asPromise({ preventTimeout: true }), this.consumeContext(UMB_NOTIFICATION_CONTEXT, (instance) => { this.#notificationContext = instance; - }).asPromise(), + }).asPromise({ preventTimeout: true }), ]); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/repository/item/item-repository-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/repository/item/item-repository-base.ts index b8d3daffca..d53d64c5a5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/repository/item/item-repository-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/repository/item/item-repository-base.ts @@ -23,7 +23,7 @@ export class UmbItemRepositoryBase this._init = this.consumeContext(itemStoreContextAlias, (instance) => { this._itemStore = instance as UmbItemStore; - }).asPromise(); + }).asPromise({ preventTimeout: true }); } /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts index d96a843293..d3e77124dd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/resources/resource.controller.ts @@ -107,7 +107,10 @@ export class UmbResourceController extends UmbControllerBase { switch (error.status ?? 0) { case 401: { // See if we can get the UmbAuthContext and let it know the user is timed out - const authContext = await this.getContext(UMB_AUTH_CONTEXT); + const authContext = await this.getContext(UMB_AUTH_CONTEXT, { preventTimeout: true }); + if (!authContext) { + throw new Error('Could not get the auth context'); + } authContext.timeOut(); break; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/router/modal-registration/modal-route-registration.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/core/router/modal-registration/modal-route-registration.controller.ts index 81477fb309..23a7100013 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/router/modal-registration/modal-route-registration.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/router/modal-registration/modal-route-registration.controller.ts @@ -48,7 +48,6 @@ export class UmbModalRouteRegistrationController< { // #init; - #contextConsumer; #addendum?: string; #additionalPath?: string; @@ -101,11 +100,10 @@ export class UmbModalRouteRegistrationController< ); }); - this.#contextConsumer = this.consumeContext(UMB_ROUTE_CONTEXT, (_routeContext) => { + this.#init = this.consumeContext(UMB_ROUTE_CONTEXT, (_routeContext) => { this.#routeContext = _routeContext; this.#registerModal(); - }); - this.#init = this.#contextConsumer.asPromise(); + }).asPromise({ preventTimeout: true }); } /** @@ -349,7 +347,6 @@ export class UmbModalRouteRegistrationController< public override destroy(): void { super.destroy(); - this.#contextConsumer.destroy(); this.#modalRegistrationContext = undefined; this.#uniquePaths = undefined as any; this.#routeContext = undefined; diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/server-file-system/rename/rename-server-file-repository-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/server-file-system/rename/rename-server-file-repository-base.ts index b5fb2dfd65..13ef3ec8b6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/server-file-system/rename/rename-server-file-repository-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/server-file-system/rename/rename-server-file-repository-base.ts @@ -37,6 +37,7 @@ export abstract class UmbRenameServerFileRepositoryBase< if (data) { const detailStore = await this.getContext(this.#detailStoreContextAlias); + if (!detailStore) throw new Error('Detail store is missing'); /* When renaming a file the unique changed because it is based on the path/name We need to remove the old item and append the new item */ @@ -44,6 +45,7 @@ export abstract class UmbRenameServerFileRepositoryBase< detailStore.append(data); const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) throw new Error('Notification context is missing'); const notification = { data: { message: `Renamed` } }; notificationContext.peek('positive', notification); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/server-file-system/rename/rename-server-file.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/server-file-system/rename/rename-server-file.action.ts index c9b8d91ccc..6f180d9404 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/server-file-system/rename/rename-server-file.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/server-file-system/rename/rename-server-file.action.ts @@ -2,15 +2,14 @@ import { UMB_RENAME_SERVER_FILE_MODAL } from './modal/rename-server-file-modal.t import type { MetaEntityActionRenameServerFileKind } from './types.js'; import { UmbServerFileRenamedEntityEvent } from './event/index.js'; import { UmbEntityActionBase, UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; export class UmbRenameEntityAction extends UmbEntityActionBase { override async execute() { if (!this.args.unique) throw new Error('Unique is required to rename an entity'); - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_RENAME_SERVER_FILE_MODAL, { + const res = await umbOpenModal(this, UMB_RENAME_SERVER_FILE_MODAL, { data: { unique: this.args.unique, renameRepositoryAlias: this.args.meta.renameRepositoryAlias, @@ -18,29 +17,25 @@ export class UmbRenameEntityAction extends UmbEntityActionBase maxFileSize) { const notification = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notification) throw new Error('Notification context is missing'); notification.peek('warning', { data: { headline: 'Upload', @@ -112,6 +113,7 @@ export class UmbTemporaryFileManager< (disallowedExtensions?.length && disallowedExtensions.includes(fileExtension)) ) { const notification = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notification) throw new Error('Notification context is missing'); notification.peek('warning', { data: { message: `${this.#localization.term('media_disallowedFileType')}: ${fileExtension}`, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository-base.ts index 358438def7..18baee61ce 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/data/tree-repository-base.ts @@ -62,7 +62,7 @@ export abstract class UmbTreeRepositoryBase< this._init = this.consumeContext(treeStoreContextAlias, (instance) => { this._treeStore = instance; - }).asPromise(); + }).asPromise({ preventTimeout: true }); } /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/default/default-tree.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/default/default-tree.element.ts index 329e9dfb3d..74cbc35141 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/default/default-tree.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/default/default-tree.element.ts @@ -52,61 +52,59 @@ export class UmbDefaultTreeElement extends UmbLitElement { @state() private _totalPages = 1; - #treeContext?: UmbDefaultTreeContext; - #init: Promise; + @state() + _treeContext?: UmbDefaultTreeContext; constructor() { super(); - this.#init = Promise.all([ - // TODO: Notice this can be retrieve via a api property. [NL] - this.consumeContext(UMB_TREE_CONTEXT, (instance) => { - this.#treeContext = instance; - this.observe(this.#treeContext.treeRoot, (treeRoot) => (this._treeRoot = treeRoot)); - this.observe(this.#treeContext.rootItems, (rootItems) => (this._rootItems = rootItems)); - this.observe(this.#treeContext.pagination.currentPage, (value) => (this._currentPage = value)); - this.observe(this.#treeContext.pagination.totalPages, (value) => (this._totalPages = value)); - }).asPromise(), - ]); + // TODO: Notice this can be retrieve via a api property. [NL] + this.consumeContext(UMB_TREE_CONTEXT, (instance) => { + this._treeContext = instance; + this.observe(this._treeContext.treeRoot, (treeRoot) => (this._treeRoot = treeRoot)); + this.observe(this._treeContext.rootItems, (rootItems) => (this._rootItems = rootItems)); + this.observe(this._treeContext.pagination.currentPage, (value) => (this._currentPage = value)); + this.observe(this._treeContext.pagination.totalPages, (value) => (this._totalPages = value)); + }); } protected override async updated( _changedProperties: PropertyValueMap | Map, ): Promise { super.updated(_changedProperties); - await this.#init; + if (this._treeContext === undefined) return; if (_changedProperties.has('selectionConfiguration')) { this._selectionConfiguration = this.selectionConfiguration; - this.#treeContext!.selection.setMultiple(this._selectionConfiguration.multiple ?? false); - this.#treeContext!.selection.setSelectable(this._selectionConfiguration.selectable ?? true); - this.#treeContext!.selection.setSelection(this._selectionConfiguration.selection ?? []); + this._treeContext!.selection.setMultiple(this._selectionConfiguration.multiple ?? false); + this._treeContext!.selection.setSelectable(this._selectionConfiguration.selectable ?? true); + this._treeContext!.selection.setSelection(this._selectionConfiguration.selection ?? []); } if (_changedProperties.has('startNode')) { - this.#treeContext!.setStartNode(this.startNode); + this._treeContext!.setStartNode(this.startNode); } if (_changedProperties.has('hideTreeRoot')) { - this.#treeContext!.setHideTreeRoot(this.hideTreeRoot); + this._treeContext!.setHideTreeRoot(this.hideTreeRoot); } if (_changedProperties.has('foldersOnly')) { - this.#treeContext!.setFoldersOnly(this.foldersOnly ?? false); + this._treeContext!.setFoldersOnly(this.foldersOnly ?? false); } if (_changedProperties.has('selectableFilter')) { - this.#treeContext!.selectableFilter = this.selectableFilter; + this._treeContext!.selectableFilter = this.selectableFilter; } if (_changedProperties.has('filter')) { - this.#treeContext!.filter = this.filter; + this._treeContext!.filter = this.filter; } } getSelection() { - return this.#treeContext?.selection.getSelection(); + return this._treeContext?.selection.getSelection(); } override render() { @@ -144,7 +142,7 @@ export class UmbDefaultTreeElement extends UmbLitElement { #onLoadMoreClick = (event: any) => { event.stopPropagation(); const next = (this._currentPage = this._currentPage + 1); - this.#treeContext?.pagination.setCurrentPageNumber(next); + this._treeContext?.pagination.setCurrentPageNumber(next); }; #renderPaging() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/entity-actions/duplicate-to/duplicate-to.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/entity-actions/duplicate-to/duplicate-to.action.ts index 24ebddd428..c43a309b6e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/entity-actions/duplicate-to/duplicate-to.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/entity-actions/duplicate-to/duplicate-to.action.ts @@ -1,7 +1,7 @@ import { UMB_DUPLICATE_TO_MODAL } from './modal/duplicate-to-modal.token.js'; import type { MetaEntityActionDuplicateToKind, UmbDuplicateToRepository } from './types.js'; import { UmbEntityActionBase, UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; @@ -10,8 +10,7 @@ export class UmbDuplicateToEntityAction extends UmbEntityActionBase( - this, - this.args.meta.duplicateRepositoryAlias, - ); - if (!duplicateRepository) throw new Error('Duplicate repository is not available'); + const duplicateRepository = await createExtensionApiByAlias( + this, + this.args.meta.duplicateRepositoryAlias, + ); + if (!duplicateRepository) throw new Error('Duplicate repository is not available'); - const { error } = await duplicateRepository.requestDuplicateTo({ - unique: this.args.unique, - destination: { unique: destinationUnique }, - }); + const { error } = await duplicateRepository.requestDuplicateTo({ + unique: this.args.unique, + destination: { unique: destinationUnique }, + }); - if (!error) { - this.#reloadMenu(); - } - } catch (error) { - console.error(error); + if (!error) { + this.#reloadMenu(); } } async #reloadMenu() { const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!actionEventContext) throw new Error('Action event context is not available'); const event = new UmbRequestReloadStructureForEntityEvent({ unique: this.args.unique, entityType: this.args.entityType, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/entity-actions/move/move-to.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/entity-actions/move/move-to.action.ts index 239ddb10b3..186e3f41c1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/entity-actions/move/move-to.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/entity-actions/move/move-to.action.ts @@ -1,7 +1,7 @@ import type { UmbMoveRepository } from './move-repository.interface.js'; import type { MetaEntityActionMoveToKind } from './types.js'; import { UmbEntityActionBase, UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UMB_TREE_PICKER_MODAL } from '@umbraco-cms/backoffice/tree'; @@ -11,8 +11,7 @@ export class UmbMoveToEntityAction extends UmbEntityActionBase { override async execute() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modal = modalManager.open(this._host, UMB_SORT_CHILDREN_OF_MODAL, { + await umbOpenModal(this, UMB_SORT_CHILDREN_OF_MODAL, { data: { unique: this.args.unique, entityType: this.args.entityType, sortChildrenOfRepositoryAlias: this.args.meta.sortChildrenOfRepositoryAlias, treeRepositoryAlias: this.args.meta.treeRepositoryAlias, }, - }); - - await modal.onSubmit().catch(() => undefined); + }).catch(() => undefined); const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!eventContext) throw new Error('Event context is not available'); eventContext.dispatchEvent( new UmbRequestReloadChildrenOfEntityEvent({ diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/tree/folder/entity-action/create-folder/create-folder.action.ts b/src/Umbraco.Web.UI.Client/src/packages/core/tree/folder/entity-action/create-folder/create-folder.action.ts index 0d0d6a1bc1..a4c1808f10 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/tree/folder/entity-action/create-folder/create-folder.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/tree/folder/entity-action/create-folder/create-folder.action.ts @@ -2,12 +2,11 @@ import { UMB_FOLDER_CREATE_MODAL } from '../../modal/index.js'; import type { MetaEntityActionFolderKind } from '../../types.js'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UmbEntityActionBase, UmbRequestReloadChildrenOfEntityEvent } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbCreateFolderEntityAction extends UmbEntityActionBase { override async execute() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_FOLDER_CREATE_MODAL, { + await umbOpenModal(this, UMB_FOLDER_CREATE_MODAL, { data: { folderRepositoryAlias: this.args.meta.folderRepositoryAlias, parent: { @@ -17,9 +16,10 @@ export class UmbCreateFolderEntityAction extends UmbEntityActionBase { - // TODO: make base type for item and detail models - #folderRepository?: UmbDetailRepository; - #init: Promise; - - constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { - super(host, args); - - // TODO: We should properly look into how we can simplify the one time usage of a extension api, as its a bit of overkill to take conditions/overwrites and observation of extensions into play here: [NL] - // But since this happens when we execute an action, it does most likely not hurt any users, but it is a bit of a overkill to do this for every action: [NL] - this.#init = Promise.all([ - new UmbExtensionApiInitializer( - this._host, - umbExtensionsRegistry, - this.args.meta.folderRepositoryAlias, - [this._host], - (permitted, ctrl) => { - this.#folderRepository = permitted ? (ctrl.api as UmbDetailRepository) : undefined; - }, - ).asPromise(), - ]); - } - override async execute() { if (!this.args.unique) throw new Error('Unique is not available'); - await this.#init; + const folderRepository = await createExtensionApiByAlias>( + this._host, + this.args.meta.folderRepositoryAlias, + [this._host], + ); - const { data: folder } = await this.#folderRepository!.requestByUnique(this.args.unique); + const { data: folder } = await folderRepository.requestByUnique(this.args.unique); if (folder) { // TODO: maybe we can show something about how many items are part of the folder? @@ -46,9 +25,10 @@ export class UmbDeleteFolderEntityAction extends UmbEntityActionBase { override async execute() { if (!this.args.unique) throw new Error('Unique is not available'); - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_FOLDER_UPDATE_MODAL, { + await umbOpenModal(this, UMB_FOLDER_UPDATE_MODAL, { data: { folderRepositoryAlias: this.args.meta.folderRepositoryAlias, unique: this.args.unique, }, }); - await modalContext.onSubmit(); - const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!actionEventContext) { + throw new Error('Event context not found.'); + } const event = new UmbRequestReloadStructureForEntityEvent({ unique: this.args.unique, entityType: this.args.entityType, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/server-model-validator.context.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/server-model-validator.context.ts index f856d42a55..dc8cdbea9d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/server-model-validator.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/context/server-model-validator.context.ts @@ -44,7 +44,7 @@ export class UmbServerModelValidatorContext context.addValidator(this); // Run translators? - }).asPromise(); + }).asPromise({ preventTimeout: true }); } async askServerForValidation(data: unknown, requestPromise: Promise>): Promise { diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action-menu-item/default/workspace-action-menu-item.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action-menu-item/default/workspace-action-menu-item.element.ts index f1994d1e8e..279f4bc4e1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action-menu-item/default/workspace-action-menu-item.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/components/workspace-action-menu-item/default/workspace-action-menu-item.element.ts @@ -34,7 +34,7 @@ export class UmbWorkspaceActionMenuItemElement< async #onClickLabel(event: UUIMenuItemEvent) { if (!this._href) { event.stopPropagation(); - await this.#api?.execute(); + await this.#api?.execute().catch(() => {}); } this.dispatchEvent(new UmbActionExecutedEvent()); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/entity-detail/entity-detail-workspace-base.ts b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/entity-detail/entity-detail-workspace-base.ts index 643181df69..f4184909e3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/workspace/entity-detail/entity-detail-workspace-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/workspace/entity-detail/entity-detail-workspace-base.ts @@ -4,7 +4,7 @@ import type { UmbEntityDetailWorkspaceContextArgs, UmbEntityDetailWorkspaceConte import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbEntityContext, type UmbEntityModel, type UmbEntityUnique } from '@umbraco-cms/backoffice/entity'; -import { UMB_DISCARD_CHANGES_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { UMB_DISCARD_CHANGES_MODAL, umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; import { UmbEntityUpdatedEvent, @@ -316,6 +316,7 @@ export abstract class UmbEntityDetailWorkspaceContextBase< this._data.setCurrent(data); const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!eventContext) throw new Error('Event context not found.'); const event = new UmbRequestReloadChildrenOfEntityEvent({ entityType: parent.entityType, unique: parent.unique, @@ -337,6 +338,7 @@ export abstract class UmbEntityDetailWorkspaceContextBase< const entityType = this.getEntityType(); const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!eventContext) throw new Error('Event context not found.'); const event = new UmbRequestReloadStructureForEntityEvent({ unique, entityType }); eventContext.dispatchEvent(event); @@ -372,12 +374,10 @@ export abstract class UmbEntityDetailWorkspaceContextBase< 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); - const modal = modalManager.open(this, UMB_DISCARD_CHANGES_MODAL); try { // navigate to the new url when discarding changes - await modal.onSubmit(); + await umbOpenModal(this, UMB_DISCARD_CHANGES_MODAL); this.#allowNavigateAway = true; history.pushState({}, '', e.detail.url); return true; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/collection/repository/data-type-collection.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/collection/repository/data-type-collection.repository.ts index 0eea3000ab..48be1b03c1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/collection/repository/data-type-collection.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/collection/repository/data-type-collection.repository.ts @@ -15,11 +15,9 @@ export class UmbDataTypeCollectionRepository extends UmbRepositoryBase implement constructor(host: UmbControllerHost) { super(host); - this.#init = Promise.all([ - this.consumeContext(UMB_DATA_TYPE_ITEM_STORE_CONTEXT, (instance) => { - this.#itemStore = instance; - }).asPromise(), - ]); + this.#init = this.consumeContext(UMB_DATA_TYPE_ITEM_STORE_CONTEXT, (instance) => { + this.#itemStore = instance; + }).asPromise({ preventTimeout: true }); this.#collectionSource = new UmbDataTypeCollectionServerDataSource(host); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/create/modal/data-type-create-options-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/create/modal/data-type-create-options-modal.element.ts index a06c637c93..d82257a9ad 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/create/modal/data-type-create-options-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/create/modal/data-type-create-options-modal.element.ts @@ -51,12 +51,10 @@ export class UmbDataTypeCreateOptionsModalElement extends UmbModalBaseElement this._submitModal()) + .catch(() => undefined); } override render() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/duplicate/repository/data-type-duplicate.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/duplicate/repository/data-type-duplicate.repository.ts index 5f15e629f2..05053adfe9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/duplicate/repository/data-type-duplicate.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/duplicate/repository/data-type-duplicate.repository.ts @@ -11,6 +11,9 @@ export class UmbDuplicateDataTypeRepository extends UmbRepositoryBase implements if (!error) { const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error('Notification context not found'); + } const notification = { data: { message: `Duplicated` } }; notificationContext.peek('positive', notification); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/move-to/repository/data-type-move.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/move-to/repository/data-type-move.repository.ts index 3977472d70..5f6c1e3d3f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/move-to/repository/data-type-move.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/entity-actions/move-to/repository/data-type-move.repository.ts @@ -11,6 +11,9 @@ export class UmbMoveDataTypeRepository extends UmbRepositoryBase implements UmbM if (!error) { const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error('Notification context not found'); + } const notification = { data: { message: `Moved` } }; notificationContext.peek('positive', notification); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts index 44daf00885..ade2465ced 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/modals/data-type-picker-flow/data-type-picker-flow-modal.element.ts @@ -129,10 +129,13 @@ export class UmbDataTypePickerFlowModalElement extends UmbModalBaseElement< this.removeUmbController(propContextConsumer); }).passContextAliasMatches(); const [contentContext, propContext] = await Promise.all([ - contentContextConsumer.asPromise(), - propContextConsumer.asPromise(), + contentContextConsumer.asPromise({ preventTimeout: true }), + propContextConsumer.asPromise({ preventTimeout: true }), this.#initPromise, ]); + if (!contentContext || !propContext) { + throw new Error('Could not get content or property context'); + } const propertyEditorName = this.#propertyEditorUIs.find((ui) => ui.alias === params.uiAlias)?.name; const dataTypeName = `${contentContext?.getName() ?? ''} - ${propContext.getName() ?? ''} - ${propertyEditorName}`; diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/data-type-detail.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/data-type-detail.repository.ts index 33f5c5a65a..37b65915bc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/data-type-detail.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/repository/detail/data-type-detail.repository.ts @@ -11,11 +11,9 @@ export class UmbDataTypeDetailRepository extends UmbDetailRepositoryBase { - this.#detailStore = instance; - }).asPromise(), - ]); + this.#init = this.consumeContext(UMB_DATA_TYPE_DETAIL_STORE_CONTEXT, (instance) => { + this.#detailStore = instance; + }).asPromise({ preventTimeout: true }); } async byPropertyEditorUiAlias(propertyEditorUiAlias: string) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/views/details/data-type-details-workspace-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/views/details/data-type-details-workspace-view.element.ts index 63e5610914..073774fe8d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/views/details/data-type-details-workspace-view.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/data-type/workspace/views/details/data-type-details-workspace-view.element.ts @@ -2,7 +2,7 @@ import { UMB_DATA_TYPE_WORKSPACE_CONTEXT } from '../../data-type-workspace.conte import { css, customElement, html, nothing, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_PROPERTY_EDITOR_UI_PICKER_MODAL } from '@umbraco-cms/backoffice/property-editor'; import type { UmbWorkspaceViewElement } from '@umbraco-cms/backoffice/workspace'; import { umbBindToValidation } from '@umbraco-cms/backoffice/validation'; @@ -55,15 +55,11 @@ export class UmbDataTypeDetailsWorkspaceViewEditElement extends UmbLitElement im } async #openPropertyEditorUIPicker() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const value = await modalManager - .open(this, UMB_PROPERTY_EDITOR_UI_PICKER_MODAL, { - value: { - selection: this._propertyEditorUiAlias ? [this._propertyEditorUiAlias] : [], - }, - }) - .onSubmit() - .catch(() => undefined); + const value = await umbOpenModal(this, UMB_PROPERTY_EDITOR_UI_PICKER_MODAL, { + value: { + selection: this._propertyEditorUiAlias ? [this._propertyEditorUiAlias] : [], + }, + }).catch(() => undefined); if (value) { this.#workspaceContext?.setPropertyEditorUiAlias(value.selection[0]); diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/entity-action/export/export.action.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/entity-action/export/export.action.ts index aacaf27588..17eafe51bd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/entity-action/export/export.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/entity-action/export/export.action.ts @@ -2,20 +2,17 @@ import { UmbDictionaryExportRepository } from '../../repository/index.js'; import { UMB_EXPORT_DICTIONARY_MODAL } from './export-dictionary-modal.token.js'; import { blobDownload } from '@umbraco-cms/backoffice/utils'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbExportDictionaryEntityAction extends UmbEntityActionBase { override async execute() { if (!this.args.unique) throw new Error('Unique is not available'); - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_EXPORT_DICTIONARY_MODAL, { data: { unique: this.args.unique } }); - - const { includeChildren } = await modalContext.onSubmit(); + const value = await umbOpenModal(this, UMB_EXPORT_DICTIONARY_MODAL, { data: { unique: this.args.unique } }); // Export the file const repository = new UmbDictionaryExportRepository(this); - const { data } = await repository.requestExport(this.args.unique, includeChildren); + const { data } = await repository.requestExport(this.args.unique, value.includeChildren); if (!data) return; diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/entity-action/import/import.action.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/entity-action/import/import.action.ts index 12d8767902..309523b6f5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/entity-action/import/import.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/entity-action/import/import.action.ts @@ -1,12 +1,10 @@ import { UMB_IMPORT_DICTIONARY_MODAL } from './import-dictionary-modal.token.js'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbImportDictionaryEntityAction extends UmbEntityActionBase { override async execute() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_IMPORT_DICTIONARY_MODAL, { data: { unique: this.args.unique } }); - await modalContext.onSubmit(); + await umbOpenModal(this, UMB_IMPORT_DICTIONARY_MODAL, { data: { unique: this.args.unique } }); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/dictionary/entity-action/move-to/repository/dictionary-move.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/dictionary/entity-action/move-to/repository/dictionary-move.repository.ts index 29e58c640e..eb09af93fa 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/dictionary/entity-action/move-to/repository/dictionary-move.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/dictionary/entity-action/move-to/repository/dictionary-move.repository.ts @@ -11,6 +11,9 @@ export class UmbMoveDictionaryRepository extends UmbRepositoryBase implements Um if (!error) { const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error('Notification context not found'); + } const notification = { data: { message: `Moved` } }; notificationContext.peek('positive', notification); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/entity-actions/create/create.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/entity-actions/create/create.action.ts index 128856c9fb..e3c8c9bf2a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/entity-actions/create/create.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/entity-actions/create/create.action.ts @@ -1,18 +1,11 @@ import { UMB_DOCUMENT_BLUEPRINT_ENTITY_TYPE } from '../../entity.js'; import { UMB_DOCUMENT_BLUEPRINT_OPTIONS_CREATE_MODAL } from './constants.js'; -import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbCreateDocumentBlueprintEntityAction extends UmbEntityActionBase { - constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { - super(host, args); - } - override async execute() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_DOCUMENT_BLUEPRINT_OPTIONS_CREATE_MODAL, { + const value = await umbOpenModal(this, UMB_DOCUMENT_BLUEPRINT_OPTIONS_CREATE_MODAL, { data: { parent: { unique: this.args.unique, @@ -21,11 +14,10 @@ export class UmbCreateDocumentBlueprintEntityAction extends UmbEntityActionBase< }, }); - await modalContext.onSubmit().catch(() => undefined); - - const documentTypeUnique = modalContext.getValue().documentTypeUnique; + const documentTypeUnique = value.documentTypeUnique; if (!documentTypeUnique) return; + // TODO: Lets avoid having such hardcoded URLs. [NL] const url = `section/settings/workspace/${UMB_DOCUMENT_BLUEPRINT_ENTITY_TYPE}/create/parent/${this.args.entityType}/${this.args.unique ?? 'null'}/${documentTypeUnique}`; history.pushState(null, '', url); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/entity-actions/create/modal/document-blueprint-options-create-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/entity-actions/create/modal/document-blueprint-options-create-modal.element.ts index 858ba7b565..41bb58dc45 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/entity-actions/create/modal/document-blueprint-options-create-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/entity-actions/create/modal/document-blueprint-options-create-modal.element.ts @@ -50,12 +50,12 @@ export class UmbDocumentBlueprintOptionsCreateModalElement extends UmbModalBaseE async #onCreateFolderClick(event: PointerEvent) { event.stopPropagation(); - try { - await this.#createFolderAction?.execute(); - this._submitModal(); - } catch (error) { - console.error(error); - } + this.#createFolderAction + ?.execute() + .then(() => { + this._submitModal(); + }) + .catch(() => {}); } #onSelected(event: UmbSelectedEvent) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/entity-actions/move-to/repository/document-blueprint-move.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/entity-actions/move-to/repository/document-blueprint-move.repository.ts index 3c295b9f70..21825ca819 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/entity-actions/move-to/repository/document-blueprint-move.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-blueprints/entity-actions/move-to/repository/document-blueprint-move.repository.ts @@ -11,6 +11,9 @@ export class UmbMoveDocumentBlueprintRepository extends UmbRepositoryBase implem if (!error) { const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error('Notification context not found'); + } const notification = { data: { message: `Moved` } }; notificationContext.peek('positive', notification); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/create/create.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/create/create.action.ts index 2e6f543677..6ca1dbe338 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/create/create.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/create/create.action.ts @@ -1,11 +1,10 @@ import { UMB_DOCUMENT_TYPE_CREATE_OPTIONS_MODAL } from './modal/constants.js'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbCreateDocumentTypeEntityAction extends UmbEntityActionBase { override async execute() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_DOCUMENT_TYPE_CREATE_OPTIONS_MODAL, { + await umbOpenModal(this, UMB_DOCUMENT_TYPE_CREATE_OPTIONS_MODAL, { data: { parent: { unique: this.args.unique, @@ -13,8 +12,6 @@ export class UmbCreateDocumentTypeEntityAction extends UmbEntityActionBase { override async execute() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_DOCUMENT_TYPE_IMPORT_MODAL, { + await umbOpenModal(this, UMB_DOCUMENT_TYPE_IMPORT_MODAL, { data: { unique: this.args.unique }, }); - await modalContext.onSubmit().catch(() => {}); const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!actionEventContext) { + throw new Error('Action event context is not available'); + } const event = new UmbRequestReloadChildrenOfEntityEvent({ unique: this.args.unique, entityType: this.args.entityType, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/document-type-import.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/document-type-import.repository.ts index ea520402b1..fbd041798d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/document-type-import.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/import/repository/document-type-import.repository.ts @@ -10,6 +10,9 @@ export class UmbDocumentTypeImportRepository extends UmbRepositoryBase { if (!error) { const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error('Notification context not found'); + } const notification = { data: { message: `Imported` } }; notificationContext.peek('positive', notification); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/move-to/repository/document-type-move.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/move-to/repository/document-type-move.repository.ts index d1f2668114..a4c5e1a136 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/move-to/repository/document-type-move.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/document-types/entity-actions/move-to/repository/document-type-move.repository.ts @@ -11,6 +11,9 @@ export class UmbMoveDocumentTypeRepository extends UmbRepositoryBase implements if (!error) { const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error('Notification context not found'); + } const notification = { data: { message: `Moved` } }; notificationContext.peek('positive', notification); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create-blueprint/create-blueprint.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create-blueprint/create-blueprint.action.ts index 6d5079dc37..50ce4d2a9b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create-blueprint/create-blueprint.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create-blueprint/create-blueprint.action.ts @@ -1,7 +1,7 @@ import { UmbDocumentCreateBlueprintRepository } from './repository/document-create-blueprint.repository.js'; import { UMB_CREATE_BLUEPRINT_MODAL } from './constants.js'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; @@ -15,13 +15,11 @@ export class UmbCreateDocumentBlueprintEntityAction extends UmbEntityActionBase< override async execute() { if (!this.args.unique) throw new Error('Unique is required'); - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_CREATE_BLUEPRINT_MODAL, { + const value = await umbOpenModal(this, UMB_CREATE_BLUEPRINT_MODAL, { data: { unique: this.args.unique }, }); - await modalContext.onSubmit().catch(() => undefined); - const { name, parent } = modalContext.getValue(); + const { name, parent } = value; if (!name) return; await this.#repository.create({ name, parent, document: { id: this.args.unique } }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/create.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/create.action.ts index af2adec3be..9383940f68 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/create.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/create/create.action.ts @@ -3,7 +3,7 @@ import { UMB_DOCUMENT_CREATE_OPTIONS_MODAL } from './document-create-options-mod import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbCreateDocumentEntityAction extends UmbEntityActionBase { constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { @@ -22,15 +22,12 @@ export class UmbCreateDocumentEntityAction extends UmbEntityActionBase { documentItem = data[0]; } - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_DOCUMENT_CREATE_OPTIONS_MODAL, { + await umbOpenModal(this, UMB_DOCUMENT_CREATE_OPTIONS_MODAL, { data: { parent: { unique: this.args.unique, entityType: this.args.entityType }, documentType: documentItem ? { unique: documentItem.documentType.unique } : null, }, }); - - await modalContext.onSubmit(); } } export default UmbCreateDocumentEntityAction; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/culture-and-hostnames.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/culture-and-hostnames.action.ts index 6fa0469af3..08862ff46a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/culture-and-hostnames.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/culture-and-hostnames/culture-and-hostnames.action.ts @@ -1,20 +1,12 @@ -import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_CULTURE_AND_HOSTNAMES_MODAL } from '@umbraco-cms/backoffice/document'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; export class UmbDocumentCultureAndHostnamesEntityAction extends UmbEntityActionBase { - constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { - super(host, args); - } - override async execute() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_CULTURE_AND_HOSTNAMES_MODAL, { + await umbOpenModal(this, UMB_CULTURE_AND_HOSTNAMES_MODAL, { data: { unique: this.args.unique }, }); - await modalContext.onSubmit(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/duplicate/duplicate-document.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/duplicate/duplicate-document.action.ts index 6b2872c074..4a3d9311a1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/duplicate/duplicate-document.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/duplicate/duplicate-document.action.ts @@ -1,7 +1,7 @@ import { UMB_DOCUMENT_ENTITY_TYPE, UMB_DOCUMENT_ROOT_ENTITY_TYPE } from '../../entity.js'; import { UMB_DUPLICATE_DOCUMENT_MODAL } from './modal/index.js'; import { UmbDuplicateDocumentRepository } from './repository/index.js'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UmbEntityActionBase, UmbRequestReloadChildrenOfEntityEvent } from '@umbraco-cms/backoffice/entity-action'; @@ -10,38 +10,35 @@ export class UmbDuplicateDocumentEntityAction extends UmbEntityActionBase if (!this.args.unique) throw new Error('Unique is not available'); if (!this.args.entityType) throw new Error('Entity Type is not available'); - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modal = modalManager.open(this, UMB_DUPLICATE_DOCUMENT_MODAL, { + const value = await umbOpenModal(this, UMB_DUPLICATE_DOCUMENT_MODAL, { data: { unique: this.args.unique, entityType: this.args.entityType, }, }); - try { - const value = await modal.onSubmit(); - const destinationUnique = value.destination.unique; - if (destinationUnique === undefined) throw new Error('Destination Unique is not available'); + const destinationUnique = value.destination.unique; + if (destinationUnique === undefined) throw new Error('Destination Unique is not available'); - const duplicateRepository = new UmbDuplicateDocumentRepository(this); + const duplicateRepository = new UmbDuplicateDocumentRepository(this); - const { error } = await duplicateRepository.requestDuplicate({ - unique: this.args.unique, - destination: { unique: destinationUnique }, - relateToOriginal: value.relateToOriginal, - includeDescendants: value.includeDescendants, - }); + const { error } = await duplicateRepository.requestDuplicate({ + unique: this.args.unique, + destination: { unique: destinationUnique }, + relateToOriginal: value.relateToOriginal, + includeDescendants: value.includeDescendants, + }); - if (!error) { - this.#reloadMenu(destinationUnique); - } - } catch (error) { - console.log(error); + if (!error) { + this.#reloadMenu(destinationUnique); } } async #reloadMenu(destinationUnique: string | null) { const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!actionEventContext) { + throw new Error('Action event context is not available'); + } // When duplicating, the destination entity type may or may not be the same as that of // the item selected for duplication (that is available in this.args). diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/duplicate/repository/document-duplicate.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/duplicate/repository/document-duplicate.repository.ts index 584a2f1a12..4b75d0f9d1 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/duplicate/repository/document-duplicate.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/duplicate/repository/document-duplicate.repository.ts @@ -11,6 +11,9 @@ export class UmbDuplicateDocumentRepository extends UmbRepositoryBase { if (!error) { const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error('Notification context not found'); + } const notification = { data: { message: `Duplicated` } }; notificationContext.peek('positive', notification); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/move-to/repository/document-move.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/move-to/repository/document-move.repository.ts index 7f4c96acc0..deb04abc6e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/move-to/repository/document-move.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/move-to/repository/document-move.repository.ts @@ -11,6 +11,9 @@ export class UmbMoveDocumentRepository extends UmbRepositoryBase implements UmbM if (!error) { const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error('Notification context not found'); + } const notification = { data: { message: `Moved` } }; notificationContext.peek('positive', notification); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/notifications/document-notifications.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/notifications/document-notifications.action.ts index a4844b73e0..36e62d4b05 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/notifications/document-notifications.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/notifications/document-notifications.action.ts @@ -2,7 +2,7 @@ import { UMB_DOCUMENT_NOTIFICATIONS_MODAL } from './modal/document-notifications import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbDocumentNotificationsEntityAction extends UmbEntityActionBase { constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { @@ -10,11 +10,9 @@ export class UmbDocumentNotificationsEntityAction extends UmbEntityActionBase undefined); } } export default UmbDocumentNotificationsEntityAction; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/public-access.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/public-access.action.ts index 3fed990032..54244c4534 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/public-access.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/public-access.action.ts @@ -1,29 +1,24 @@ import { UMB_PUBLIC_ACCESS_MODAL } from './modal/public-access-modal.token.js'; -import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; import { UmbEntityActionBase, UmbRequestReloadChildrenOfEntityEvent, UmbRequestReloadStructureForEntityEvent, } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; -import type { UmbDocumentDetailRepository } from '@umbraco-cms/backoffice/document'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; export class UmbDocumentPublicAccessEntityAction extends UmbEntityActionBase { - constructor(host: UmbDocumentDetailRepository, args: UmbEntityActionArgs) { - super(host, args); - } - override async execute() { if (!this.args.unique) throw new Error('Unique is not available'); - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modal = modalManager.open(this, UMB_PUBLIC_ACCESS_MODAL, { data: { unique: this.args.unique } }); - await modal.onSubmit(); + await umbOpenModal(this, UMB_PUBLIC_ACCESS_MODAL, { data: { unique: this.args.unique } }); this.#requestReloadEntity(); } async #requestReloadEntity() { const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!actionEventContext) { + throw new Error('Action event context is not available'); + } const entityStructureEvent = new UmbRequestReloadStructureForEntityEvent({ unique: this.args.unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/item/document-item-data-resolver.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/item/document-item-data-resolver.ts index 0fc145e928..58699b7d59 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/item/document-item-data-resolver.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/item/document-item-data-resolver.ts @@ -3,7 +3,6 @@ import type { UmbDocumentItemModel } from './types.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { DocumentVariantStateModel } from '@umbraco-cms/backoffice/external/backend-api'; -import type { UmbAppLanguageContext } from '@umbraco-cms/backoffice/language'; import { UMB_APP_LANGUAGE_CONTEXT } from '@umbraco-cms/backoffice/language'; import { UmbBooleanState, UmbStringState } from '@umbraco-cms/backoffice/observable-api'; import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property'; @@ -23,7 +22,7 @@ export class UmbDocumentItemDataResolver; + #init: Promise; #unique = new UmbStringState(undefined); public readonly unique = this.#unique.asObservable(); @@ -47,12 +46,12 @@ export class UmbDocumentItemDataResolver { - this.#propertyDataSetCulture = context.getVariantId(); - this.#setVariantAwareValues(); - }); - this.#init = Promise.all([ + this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, (context) => { + this.#propertyDataSetCulture = context.getVariantId(); + this.#setVariantAwareValues(); + }).asPromise({ preventTimeout: true }), + this.consumeContext(UMB_APP_LANGUAGE_CONTEXT, (context) => { this.observe(context.appLanguageCulture, (culture) => { this.#appCulture = culture; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish-with-descendants/workspace-action/publish-with-descendants.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish-with-descendants/workspace-action/publish-with-descendants.action.ts index 9e802ab80f..79268ffde9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish-with-descendants/workspace-action/publish-with-descendants.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish-with-descendants/workspace-action/publish-with-descendants.action.ts @@ -4,6 +4,9 @@ import { UmbWorkspaceActionBase } from '@umbraco-cms/backoffice/workspace'; export class UmbDocumentPublishWithDescendantsWorkspaceAction extends UmbWorkspaceActionBase { override async execute() { const workspaceContext = await this.getContext(UMB_DOCUMENT_PUBLISHING_WORKSPACE_CONTEXT); + if (!workspaceContext) { + throw new Error('The workspace context is missing'); + } return workspaceContext.publishWithDescendants(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/entity-action/publish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/entity-action/publish.action.ts index 098993fc23..b8a01e567c 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/entity-action/publish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/entity-action/publish.action.ts @@ -7,7 +7,7 @@ import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action' import { UmbEntityActionBase, UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/entity-action'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UMB_CURRENT_USER_CONTEXT } from '@umbraco-cms/backoffice/current-user'; import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; @@ -22,6 +22,7 @@ export class UmbPublishDocumentEntityAction extends UmbEntityActionBase { if (!this.args.unique) throw new Error('The document unique identifier is missing'); const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) throw new Error('The notification context is missing'); const localize = new UmbLocalizationController(this); const languageRepository = new UmbLanguageCollectionRepository(this._host); @@ -33,9 +34,11 @@ export class UmbPublishDocumentEntityAction extends UmbEntityActionBase { if (!documentData) throw new Error('The document was not found'); const appLanguageContext = await this.getContext(UMB_APP_LANGUAGE_CONTEXT); + if (!appLanguageContext) throw new Error('The app language context is missing'); const appCulture = appLanguageContext.getAppCulture(); const currentUserContext = await this.getContext(UMB_CURRENT_USER_CONTEXT); + if (!currentUserContext) throw new Error('The current user context is missing'); const currentUserAllowedLanguages = currentUserContext.getLanguages(); const currentUserHasAccessToAllLanguages = currentUserContext.getHasAccessToAllLanguages(); @@ -61,6 +64,7 @@ export class UmbPublishDocumentEntityAction extends UmbEntityActionBase { ); const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!actionEventContext) throw new Error('The action event context is missing'); const event = new UmbRequestReloadStructureForEntityEvent({ unique: this.args.unique, entityType: this.args.entityType, @@ -94,21 +98,17 @@ export class UmbPublishDocumentEntityAction extends UmbEntityActionBase { selection.push(options[0].unique); } - const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const result = await modalManagerContext - .open(this, UMB_DOCUMENT_PUBLISH_MODAL, { - data: { - options, - pickableFilter: (option) => { - if (!option.culture) return false; - if (currentUserHasAccessToAllLanguages) return true; - return currentUserAllowedLanguages.includes(option.culture); - }, + const result = await umbOpenModal(this, UMB_DOCUMENT_PUBLISH_MODAL, { + data: { + options, + pickableFilter: (option) => { + if (!option.culture) return false; + if (currentUserHasAccessToAllLanguages) return true; + return currentUserAllowedLanguages.includes(option.culture); }, - value: { selection }, - }) - .onSubmit() - .catch(() => undefined); + }, + value: { selection }, + }).catch(() => undefined); if (!result?.selection.length) return; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/entity-bulk-action/publish.bulk-action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/entity-bulk-action/publish.bulk-action.ts index 014946c962..5f9499f8e2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/entity-bulk-action/publish.bulk-action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/entity-bulk-action/publish.bulk-action.ts @@ -6,7 +6,7 @@ import { UmbPublishDocumentEntityAction } from '../entity-action/index.js'; import { UmbEntityBulkActionBase } from '@umbraco-cms/backoffice/entity-bulk-action'; import { UMB_APP_LANGUAGE_CONTEXT, UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; -import { UMB_CONFIRM_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbConfirmModal, umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; import { UMB_ENTITY_CONTEXT } from '@umbraco-cms/backoffice/entity'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; @@ -16,10 +16,16 @@ import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; export class UmbDocumentPublishEntityBulkAction extends UmbEntityBulkActionBase { async execute() { const entityContext = await this.getContext(UMB_ENTITY_CONTEXT); + if (!entityContext) { + throw new Error('Entity context not found'); + } const entityType = entityContext.getEntityType(); const unique = entityContext.getUnique(); const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error('Notification context not found'); + } const localize = new UmbLocalizationController(this); if (!entityType) throw new Error('Entity type not found'); @@ -57,9 +63,10 @@ export class UmbDocumentPublishEntityBulkAction extends UmbEntityBulkActionBase< segment: null, })); - const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!eventContext) { + throw new Error('Event context not found'); + } const event = new UmbRequestReloadChildrenOfEntityEvent({ entityType, unique, @@ -68,17 +75,12 @@ export class UmbDocumentPublishEntityBulkAction extends UmbEntityBulkActionBase< // If there is only one language available, we can skip the modal and publish directly: if (options.length === 1) { const localizationController = new UmbLocalizationController(this._host); - const confirm = await modalManagerContext - .open(this, UMB_CONFIRM_MODAL, { - data: { - headline: localizationController.term('content_readyToPublish'), - content: localizationController.term('prompt_confirmListViewPublish'), - color: 'positive', - confirmLabel: localizationController.term('actions_publish'), - }, - }) - .onSubmit() - .catch(() => false); + const confirm = await umbConfirmModal(this, { + headline: localizationController.term('content_readyToPublish'), + content: localizationController.term('prompt_confirmListViewPublish'), + color: 'positive', + confirmLabel: localizationController.term('actions_publish'), + }).catch(() => false); if (confirm !== false) { const variantId = new UmbVariantId(options[0].language.unique, null); @@ -110,21 +112,21 @@ export class UmbDocumentPublishEntityBulkAction extends UmbEntityBulkActionBase< // TODO: Missing features to pre-select the variant that fits with the variant-id of the tree/collection? (Again only relevant if the action is executed from a Tree or Collection) [NL] const selection: Array = []; const context = await this.getContext(UMB_APP_LANGUAGE_CONTEXT); + if (!context) { + throw new Error('App language context not found'); + } const appCulture = context.getAppCulture(); // If the app language is one of the options, select it by default: if (appCulture && options.some((o) => o.unique === appCulture)) { selection.push(new UmbVariantId(appCulture, null).toString()); } - const result = await modalManagerContext - .open(this, UMB_DOCUMENT_PUBLISH_MODAL, { - data: { - options, - }, - value: { selection }, - }) - .onSubmit() - .catch(() => undefined); + const result = await umbOpenModal(this, UMB_DOCUMENT_PUBLISH_MODAL, { + data: { + options, + }, + value: { selection }, + }).catch(() => undefined); if (!result?.selection.length) return; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/workspace-action/save-and-publish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/workspace-action/save-and-publish.action.ts index 142e5393a5..ae617ef378 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/workspace-action/save-and-publish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/publish/workspace-action/save-and-publish.action.ts @@ -34,12 +34,18 @@ export class UmbDocumentSaveAndPublishWorkspaceAction extends UmbWorkspaceAction async hasAdditionalOptions() { const workspaceContext = await this.getContext(UMB_DOCUMENT_WORKSPACE_CONTEXT); + if (!workspaceContext) { + throw new Error('The workspace context is missing'); + } const variantOptions = await this.observe(workspaceContext.variantOptions).asPromise(); return variantOptions?.length > 1; } override async execute() { const workspaceContext = await this.getContext(UMB_DOCUMENT_PUBLISHING_WORKSPACE_CONTEXT); + if (!workspaceContext) { + throw new Error('The workspace context is missing'); + } return workspaceContext.saveAndPublish(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/repository/document-publishing.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/repository/document-publishing.repository.ts index 7599d8a763..3e19ff85a6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/repository/document-publishing.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/repository/document-publishing.repository.ts @@ -19,11 +19,9 @@ export class UmbDocumentPublishingRepository extends UmbRepositoryBase { this.#publishingDataSource = new UmbDocumentPublishingServerDataSource(this); - this.#init = Promise.all([ - this.consumeContext(UMB_NOTIFICATION_CONTEXT, (instance) => { - this.#notificationContext = instance; - }).asPromise(), - ]); + this.#init = this.consumeContext(UMB_NOTIFICATION_CONTEXT, (instance) => { + this.#notificationContext = instance; + }).asPromise({ preventTimeout: true }); } /** @@ -73,11 +71,7 @@ export class UmbDocumentPublishingRepository extends UmbRepositoryBase { * @param includeUnpublishedDescendants * @memberof UmbDocumentPublishingRepository */ - async publishWithDescendants( - id: string, - variantIds: Array, - includeUnpublishedDescendants: boolean, - ) { + async publishWithDescendants(id: string, variantIds: Array, includeUnpublishedDescendants: boolean) { if (!id) throw new Error('id is missing'); if (!variantIds) throw new Error('variant IDs are missing'); await this.#init; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/schedule-publish/workspace-action/save-and-schedule.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/schedule-publish/workspace-action/save-and-schedule.action.ts index 14cec080ef..38a5c822e6 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/schedule-publish/workspace-action/save-and-schedule.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/schedule-publish/workspace-action/save-and-schedule.action.ts @@ -4,6 +4,9 @@ import { UmbWorkspaceActionBase } from '@umbraco-cms/backoffice/workspace'; export class UmbDocumentSaveAndScheduleWorkspaceAction extends UmbWorkspaceActionBase { override async execute() { const workspaceContext = await this.getContext(UMB_DOCUMENT_PUBLISHING_WORKSPACE_CONTEXT); + if (!workspaceContext) { + throw new Error('Publishing workspace context not found'); + } return workspaceContext.schedule(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/entity-action/unpublish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/entity-action/unpublish.action.ts index 5aa6e4125d..cbce845f59 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/entity-action/unpublish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/entity-action/unpublish.action.ts @@ -10,7 +10,7 @@ import { } from '@umbraco-cms/backoffice/entity-action'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UMB_CURRENT_USER_CONTEXT } from '@umbraco-cms/backoffice/current-user'; @@ -31,9 +31,11 @@ export class UmbUnpublishDocumentEntityAction extends UmbEntityActionBase if (!documentData) throw new Error('The document was not found'); const appLanguageContext = await this.getContext(UMB_APP_LANGUAGE_CONTEXT); + if (!appLanguageContext) throw new Error('The app language context is missing'); const appCulture = appLanguageContext.getAppCulture(); const currentUserContext = await this.getContext(UMB_CURRENT_USER_CONTEXT); + if (!currentUserContext) throw new Error('The current user context is missing'); const currentUserAllowedLanguages = currentUserContext.getLanguages(); const currentUserHasAccessToAllLanguages = currentUserContext.getHasAccessToAllLanguages(); @@ -69,22 +71,18 @@ export class UmbUnpublishDocumentEntityAction extends UmbEntityActionBase selection.push(options[0].unique); } - const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const result = await modalManagerContext - .open(this, UMB_DOCUMENT_UNPUBLISH_MODAL, { - data: { - documentUnique: this.args.unique, - options, - pickableFilter: (option) => { - if (!option.culture) return false; - if (currentUserHasAccessToAllLanguages) return true; - return currentUserAllowedLanguages.includes(option.culture); - }, + const result = await umbOpenModal(this, UMB_DOCUMENT_UNPUBLISH_MODAL, { + data: { + documentUnique: this.args.unique, + options, + pickableFilter: (option) => { + if (!option.culture) return false; + if (currentUserHasAccessToAllLanguages) return true; + return currentUserAllowedLanguages.includes(option.culture); }, - value: { selection }, - }) - .onSubmit() - .catch(() => undefined); + }, + value: { selection }, + }).catch(() => undefined); if (!result?.selection.length) return; @@ -96,6 +94,7 @@ export class UmbUnpublishDocumentEntityAction extends UmbEntityActionBase if (!error) { const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!actionEventContext) throw new Error('The action event context is missing'); const event = new UmbRequestReloadStructureForEntityEvent({ unique: this.args.unique, entityType: this.args.entityType, diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/entity-bulk-action/unpublish.bulk-action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/entity-bulk-action/unpublish.bulk-action.ts index d817030e88..12176627ab 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/entity-bulk-action/unpublish.bulk-action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/entity-bulk-action/unpublish.bulk-action.ts @@ -2,7 +2,7 @@ import { UmbUnpublishDocumentEntityAction } from '../entity-action/index.js'; import type { UmbDocumentVariantOptionModel } from '../../../types.js'; import { UMB_DOCUMENT_ENTITY_TYPE, UMB_DOCUMENT_UNPUBLISH_MODAL } from '../../../constants.js'; import { UmbDocumentPublishingRepository } from '../../repository/index.js'; -import { UMB_CONFIRM_MODAL, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbConfirmModal, umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UmbEntityBulkActionBase } from '@umbraco-cms/backoffice/entity-bulk-action'; import { UMB_APP_LANGUAGE_CONTEXT, UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; @@ -14,6 +14,9 @@ import { UmbRequestReloadChildrenOfEntityEvent } from '@umbraco-cms/backoffice/e export class UmbDocumentUnpublishEntityBulkAction extends UmbEntityBulkActionBase { async execute() { const entityContext = await this.getContext(UMB_ENTITY_CONTEXT); + if (!entityContext) { + throw new Error('Entity context not found'); + } const entityType = entityContext.getEntityType(); const unique = entityContext.getUnique(); @@ -52,9 +55,10 @@ export class UmbDocumentUnpublishEntityBulkAction extends UmbEntityBulkActionBas segment: null, })); - const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!eventContext) { + throw new Error('Event context not found'); + } const event = new UmbRequestReloadChildrenOfEntityEvent({ entityType, unique, @@ -63,17 +67,12 @@ export class UmbDocumentUnpublishEntityBulkAction extends UmbEntityBulkActionBas // If there is only one language available, we can skip the modal and unpublish directly: if (options.length === 1) { const localizationController = new UmbLocalizationController(this._host); - const confirm = await modalManagerContext - .open(this, UMB_CONFIRM_MODAL, { - data: { - headline: localizationController.term('actions_unpublish'), - content: localizationController.term('prompt_confirmListViewUnpublish'), - color: 'warning', - confirmLabel: localizationController.term('actions_unpublish'), - }, - }) - .onSubmit() - .catch(() => false); + const confirm = await umbConfirmModal(this, { + headline: localizationController.term('actions_unpublish'), + content: localizationController.term('prompt_confirmListViewUnpublish'), + color: 'warning', + confirmLabel: localizationController.term('actions_unpublish'), + }).catch(() => false); if (confirm !== false) { const variantId = new UmbVariantId(options[0].language.unique, null); @@ -92,21 +91,19 @@ export class UmbDocumentUnpublishEntityBulkAction extends UmbEntityBulkActionBas // TODO: Missing features to pre-select the variant that fits with the variant-id of the tree/collection? (Again only relevant if the action is executed from a Tree or Collection) [NL] const selection: Array = []; const context = await this.getContext(UMB_APP_LANGUAGE_CONTEXT); + if (!context) throw new Error('App language context not found'); const appCulture = context.getAppCulture(); // If the app language is one of the options, select it by default: if (appCulture && options.some((o) => o.unique === appCulture)) { selection.push(new UmbVariantId(appCulture, null).toString()); } - const result = await modalManagerContext - .open(this, UMB_DOCUMENT_UNPUBLISH_MODAL, { - data: { - options, - }, - value: { selection }, - }) - .onSubmit() - .catch(() => undefined); + const result = await umbOpenModal(this, UMB_DOCUMENT_UNPUBLISH_MODAL, { + data: { + options, + }, + value: { selection }, + }).catch(() => undefined); if (!result?.selection.length) return; diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/modal/document-unpublish-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/modal/document-unpublish-modal.element.ts index 359eac8ab4..72e7de20b8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/modal/document-unpublish-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/modal/document-unpublish-modal.element.ts @@ -120,6 +120,9 @@ export class UmbDocumentUnpublishModalElement extends UmbModalBaseElement< // If there are references, we also want to check if we are allowed to unpublish the document: if (this._hasReferences) { const documentConfigurationContext = await this.getContext(UMB_DOCUMENT_CONFIGURATION_CONTEXT); + if (!documentConfigurationContext) { + throw new Error('Document configuration context not found'); + } this._hasUnpublishPermission = (await documentConfigurationContext.getDocumentConfiguration())?.disableUnpublishWhenReferenced === false; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/workspace-action/unpublish.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/workspace-action/unpublish.action.ts index 319d2fa2b6..3c94159db9 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/workspace-action/unpublish.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/unpublish/workspace-action/unpublish.action.ts @@ -4,6 +4,9 @@ import { UmbWorkspaceActionBase } from '@umbraco-cms/backoffice/workspace'; export class UmbDocumentUnpublishWorkspaceAction extends UmbWorkspaceActionBase { override async execute() { const workspaceContext = await this.getContext(UMB_DOCUMENT_PUBLISHING_WORKSPACE_CONTEXT); + if (!workspaceContext) { + throw new Error('Publishing workspace context not found'); + } return workspaceContext.unpublish(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/workspace-context/document-publishing.workspace-context.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/workspace-context/document-publishing.workspace-context.ts index c2d59220e3..57e0583889 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/workspace-context/document-publishing.workspace-context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/publishing/workspace-context/document-publishing.workspace-context.ts @@ -13,7 +13,7 @@ import { UmbUnpublishDocumentEntityAction } from '../unpublish/index.js'; import { UMB_DOCUMENT_PUBLISHING_WORKSPACE_CONTEXT } from './document-publishing.workspace-context.token.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbContextBase } from '@umbraco-cms/backoffice/class-api'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UmbRequestReloadChildrenOfEntityEvent, UmbRequestReloadStructureForEntityEvent, @@ -50,11 +50,11 @@ export class UmbDocumentPublishingWorkspaceContext extends UmbContextBase { this.#documentWorkspaceContext = context; this.#initPendingChanges(); - }).asPromise(), + }).asPromise({ preventTimeout: true }), this.consumeContext(UMB_ACTION_EVENT_CONTEXT, async (context) => { this.#eventContext = context; - }).asPromise(), + }).asPromise({ preventTimeout: true }), ]); this.consumeContext(UMB_NOTIFICATION_CONTEXT, (context) => { @@ -92,24 +92,20 @@ export class UmbDocumentPublishingWorkspaceContext extends UmbContextBase ({ - unique: option.unique, - schedule: { - publishTime: option.variant?.scheduledPublishDate, - unpublishTime: option.variant?.scheduledUnpublishDate, - }, - })), - }, - }) - .onSubmit() - .catch(() => undefined); + const result = await umbOpenModal(this, UMB_DOCUMENT_SCHEDULE_MODAL, { + data: { + options, + activeVariants: selected, + pickableFilter: this.#publishableVariantsFilter, + prevalues: options.map((option) => ({ + unique: option.unique, + schedule: { + publishTime: option.variant?.scheduledPublishDate, + unpublishTime: option.variant?.scheduledUnpublishDate, + }, + })), + }, + }).catch(() => undefined); if (!result?.selection.length) return; @@ -159,6 +155,9 @@ export class UmbDocumentPublishingWorkspaceContext extends UmbContextBase { const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error('Notification context is missing'); + } notificationContext.peek('danger', { data: { message: this.#localize.term('speechBubbles_editContentScheduledNotSavedText') }, }); @@ -206,17 +205,13 @@ export class UmbDocumentPublishingWorkspaceContext extends UmbContextBase undefined); + const result = await umbOpenModal(this, UMB_DOCUMENT_PUBLISH_WITH_DESCENDANTS_MODAL, { + data: { + options, + pickableFilter: this.#publishableVariantsFilter, + }, + value: { selection: selected }, + }).catch(() => undefined); if (!result?.selection.length) return; @@ -284,17 +279,13 @@ export class UmbDocumentPublishingWorkspaceContext extends UmbContextBase undefined); + const result = await umbOpenModal(this, UMB_DOCUMENT_PUBLISH_MODAL, { + data: { + options, + pickableFilter: this.#publishableVariantsFilter, + }, + value: { selection: selected }, + }).catch(() => undefined); if (!result?.selection.length || !unique) return; @@ -315,6 +306,9 @@ export class UmbDocumentPublishingWorkspaceContext extends UmbContextBase #localize = new UmbLocalizationController(this); override async execute() { - const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManagerContext.open(this, UMB_ROLLBACK_MODAL, {}); - - await modalContext.onSubmit(); + await umbOpenModal(this, UMB_ROLLBACK_MODAL, {}); const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error('Notification context not found'); + } notificationContext.peek('positive', { data: { message: this.#localize.term('rollback_documentRolledBack') }, }); diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/rollback/modal/rollback-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/rollback/modal/rollback-modal.element.ts index e41ad2d053..a62a00a571 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/rollback/modal/rollback-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/rollback/modal/rollback-modal.element.ts @@ -216,6 +216,9 @@ export class UmbRollbackModalElement extends UmbModalBaseElement @@ -297,9 +296,7 @@ export class UmbDocumentWorkspaceViewInfoElement extends UmbLitElement { value: { selection: [this._templateUnique], }, - }); - - const result = await modal?.onSubmit().catch(() => undefined); + }).catch(() => undefined); if (!result?.selection.length) return; diff --git a/src/Umbraco.Web.UI.Client/src/packages/extension-insights/entity-actions/unregister/unregister-extension.action.ts b/src/Umbraco.Web.UI.Client/src/packages/extension-insights/entity-actions/unregister/unregister-extension.action.ts index c904dc092d..a239cfb47f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/extension-insights/entity-actions/unregister/unregister-extension.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/extension-insights/entity-actions/unregister/unregister-extension.action.ts @@ -21,6 +21,9 @@ export class UmbUnregisterExtensionEntityAction extends UmbEntityActionBase { - if (savedSearch) { - this.#saveSearch(savedSearch); - this._isQuerySaved = true; - } - }); + }) + .then((savedSearch) => { + if (savedSearch) { + this.#saveSearch(savedSearch); + this._isQuerySaved = true; + } + }) + .catch(() => {}); } override render() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/components/input-markdown-editor/input-markdown.element.ts b/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/components/input-markdown-editor/input-markdown.element.ts index f0a0f64d7e..4f4e3b67cf 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/components/input-markdown-editor/input-markdown.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/markdown-editor/components/input-markdown-editor/input-markdown.element.ts @@ -16,7 +16,7 @@ import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registr import { UmbChangeEvent, type UmbInputEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_MEDIA_PICKER_MODAL, UmbMediaUrlRepository } from '@umbraco-cms/backoffice/media'; import { UmbCodeEditorLoadedEvent } from '@umbraco-cms/backoffice/code-editor'; import type { UmbCodeEditorController, UmbCodeEditorElement } from '@umbraco-cms/backoffice/code-editor'; @@ -219,34 +219,28 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin { - if (!value) return; - - const uniques = value.selection.filter((unique) => unique !== null) as Array; - const { data: mediaUrls } = await this.#mediaUrlRepository.requestItems(uniques); - const mediaUrl = mediaUrls?.length ? (mediaUrls[0].url ?? 'URL') : 'URL'; - - this.#editor?.monacoEditor?.executeEdits('', [ - { - range: selection, - text: `![${alt}](${mediaUrl})`, - }, - ]); - - this.#editor?.select({ - startColumn: selection.startColumn + 2, - endColumn: selection.startColumn + alt.length + 2, // +2 because of ![ - endLineNumber: selection.startLineNumber, - startLineNumber: selection.startLineNumber, - }); - }) + const value = await umbOpenModal(this, UMB_MEDIA_PICKER_MODAL) .catch(() => undefined) .finally(() => this._focusEditor()); + if (!value) return; + + const uniques = value.selection.filter((unique) => unique !== null) as Array; + const { data: mediaUrls } = await this.#mediaUrlRepository.requestItems(uniques); + const mediaUrl = mediaUrls?.length ? (mediaUrls[0].url ?? 'URL') : 'URL'; + + this.#editor?.monacoEditor?.executeEdits('', [ + { + range: selection, + text: `![${alt}](${mediaUrl})`, + }, + ]); + + this.#editor?.select({ + startColumn: selection.startColumn + 2, + endColumn: selection.startColumn + alt.length + 2, // +2 because of ![ + endLineNumber: selection.startLineNumber, + startLineNumber: selection.startLineNumber, + }); } private _insertLine() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/create/modal/media-type-create-options-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/create/modal/media-type-create-options-modal.element.ts index 345512da59..58ef88ce38 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/create/modal/media-type-create-options-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/create/modal/media-type-create-options-modal.element.ts @@ -41,12 +41,10 @@ export class UmbMediaTypeCreateOptionsModalElement extends UmbModalBaseElement this.modalContext?.submit()) + .catch(() => undefined); } // close the modal when navigating to data type diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/duplicate/repository/media-type-duplicate.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/duplicate/repository/media-type-duplicate.repository.ts index 7407bb11a3..159a8cacb0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/duplicate/repository/media-type-duplicate.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/duplicate/repository/media-type-duplicate.repository.ts @@ -11,6 +11,9 @@ export class UmbDuplicateMediaTypeRepository extends UmbRepositoryBase implement if (!error) { const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error('Notification context not found'); + } const notification = { data: { message: `Duplicated` } }; notificationContext.peek('positive', notification); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/export/repository/media-type-export.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/export/repository/media-type-export.repository.ts index 8f8c65dbdf..70a115671e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/export/repository/media-type-export.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/export/repository/media-type-export.repository.ts @@ -10,6 +10,9 @@ export class UmbExportMediaTypeRepository extends UmbRepositoryBase { if (!error) { const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error('Notification context not found'); + } const notification = { data: { message: `Exported` } }; notificationContext.peek('positive', notification); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/import/media-type-import.action.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/import/media-type-import.action.ts index ee1eaa956c..9814131331 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/import/media-type-import.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/import/media-type-import.action.ts @@ -1,17 +1,18 @@ import { UMB_MEDIA_TYPE_IMPORT_MODAL } from './modal/media-type-import-modal.token.js'; import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action'; import { UmbEntityActionBase, UmbRequestReloadChildrenOfEntityEvent } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbImportMediaTypeEntityAction extends UmbEntityActionBase { override async execute() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_MEDIA_TYPE_IMPORT_MODAL, { + await umbOpenModal(this, UMB_MEDIA_TYPE_IMPORT_MODAL, { data: { unique: this.args.unique }, - }); - await modalContext.onSubmit().catch(() => {}); + }).catch(() => {}); const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!actionEventContext) { + throw new Error('Action event context not found.'); + } const event = new UmbRequestReloadChildrenOfEntityEvent({ unique: this.args.unique, entityType: this.args.entityType, diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/import/repository/media-type-import.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/import/repository/media-type-import.repository.ts index 1f13bbe4f5..b944e3478e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/import/repository/media-type-import.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/import/repository/media-type-import.repository.ts @@ -10,6 +10,9 @@ export class UmbMediaTypeImportRepository extends UmbRepositoryBase { if (!error) { const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error('Notification context not found'); + } const notification = { data: { message: `Imported` } }; notificationContext.peek('positive', notification); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/move-to/repository/media-type-move.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/move-to/repository/media-type-move.repository.ts index 1f1f40363b..2a3afaa32e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/move-to/repository/media-type-move.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media-types/entity-actions/move-to/repository/media-type-move.repository.ts @@ -11,6 +11,9 @@ export class UmbMoveMediaTypeRepository extends UmbRepositoryBase implements Umb if (!error) { const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error(`Failed to load notification context`); + } const notification = { data: { message: `Moved` } }; notificationContext.peek('positive', notification); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.element.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.element.ts index e5deb42fa9..6357b381ef 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/collection/media-collection.element.ts @@ -64,6 +64,9 @@ export class UmbMediaCollectionElement extends UmbCollectionDefaultElement { this.#collectionContext?.requestCollection(); const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!eventContext) { + throw new Error('Could not get event context'); + } const reloadEvent = new UmbRequestReloadChildrenOfEntityEvent({ entityType: this._unique ? UMB_MEDIA_ENTITY_TYPE : UMB_MEDIA_ROOT_ENTITY_TYPE, unique: this._unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/dropzone/dropzone-manager.class.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/dropzone/dropzone-manager.class.ts index b58069b537..0df2a09ad7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/dropzone/dropzone-manager.class.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/dropzone/dropzone-manager.class.ts @@ -16,7 +16,7 @@ import { UmbArrayState, UmbObjectState } from '@umbraco-cms/backoffice/observabl import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; import { UmbId } from '@umbraco-cms/backoffice/id'; import { UmbMediaTypeStructureRepository } from '@umbraco-cms/backoffice/media-type'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import type { UmbAllowedMediaTypeModel } from '@umbraco-cms/backoffice/media-type'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; @@ -132,9 +132,9 @@ export class UmbDropzoneManager extends UmbControllerBase { } async #showDialogMediaTypePicker(options: Array) { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this.#host, UMB_DROPZONE_MEDIA_TYPE_PICKER_MODAL, { data: { options } }); - const value = await modalContext.onSubmit().catch(() => undefined); + const value = await umbOpenModal(this.#host, UMB_DROPZONE_MEDIA_TYPE_PICKER_MODAL, { data: { options } }).catch( + () => undefined, + ); return value?.mediaTypeUnique; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-actions/create/create.action.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-actions/create/create.action.ts index 42e2f95154..3629d1424b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-actions/create/create.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-actions/create/create.action.ts @@ -3,7 +3,7 @@ import { UMB_MEDIA_CREATE_OPTIONS_MODAL } from './media-create-options-modal.tok import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbCreateMediaEntityAction extends UmbEntityActionBase { constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { @@ -22,15 +22,12 @@ export class UmbCreateMediaEntityAction extends UmbEntityActionBase { mediaItem = data[0]; } - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_MEDIA_CREATE_OPTIONS_MODAL, { + await umbOpenModal(this, UMB_MEDIA_CREATE_OPTIONS_MODAL, { data: { parent: { unique: this.args.unique, entityType: this.args.entityType }, mediaType: mediaItem ? { unique: mediaItem.mediaType.unique } : null, }, }); - - await modalContext.onSubmit(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-actions/move-to/repository/media-move.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-actions/move-to/repository/media-move.repository.ts index bb7464b940..9c85792b90 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-actions/move-to/repository/media-move.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/media/media/entity-actions/move-to/repository/media-move.repository.ts @@ -11,6 +11,9 @@ export class UmbMoveMediaRepository extends UmbRepositoryBase implements UmbMove if (!error) { const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error('Notification context not found.'); + } const notification = { data: { message: `Moved` } }; notificationContext.peek('positive', notification); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/entity-actions/duplicate/repository/member-type-duplicate.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/entity-actions/duplicate/repository/member-type-duplicate.repository.ts index 8ac4b8e776..441d2704dc 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member-type/entity-actions/duplicate/repository/member-type-duplicate.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member-type/entity-actions/duplicate/repository/member-type-duplicate.repository.ts @@ -11,6 +11,9 @@ export class UmbDuplicateMemberTypeRepository extends UmbRepositoryBase implemen if (!error) { const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); + if (!notificationContext) { + throw new Error('Notification context not found'); + } const notification = { data: { message: `Duplicated` } }; notificationContext.peek('positive', notification); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/entity-actions/create/create.action.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/entity-actions/create/create.action.ts index 20994dad83..3c36113153 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/entity-actions/create/create.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/entity-actions/create/create.action.ts @@ -2,7 +2,7 @@ import { UMB_MEMBER_CREATE_OPTIONS_MODAL } from './member-create-options-modal.t import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbCreateMemberEntityAction extends UmbEntityActionBase { constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { @@ -10,9 +10,7 @@ export class UmbCreateMemberEntityAction extends UmbEntityActionBase { } override async execute() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_MEMBER_CREATE_OPTIONS_MODAL); - await modalContext.onSubmit(); + await umbOpenModal(this, UMB_MEMBER_CREATE_OPTIONS_MODAL); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/member-repository-base.ts b/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/member-repository-base.ts index 74257955e1..911fe09130 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/member-repository-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/members/member/repository/member-repository-base.ts @@ -19,15 +19,15 @@ export abstract class UmbMemberRepositoryBase extends UmbRepositoryBase { this.init = Promise.all([ this.consumeContext(UMB_MEMBER_DETAIL_STORE_CONTEXT, (instance) => { this.detailStore = instance; - }).asPromise(), + }).asPromise({ preventTimeout: true }), this.consumeContext(UMB_MEMBER_ITEM_STORE_CONTEXT, (instance) => { this.itemStore = instance; - }).asPromise(), + }).asPromise({ preventTimeout: true }), this.consumeContext(UMB_NOTIFICATION_CONTEXT, (instance) => { this.notificationContext = instance; - }).asPromise(), + }).asPromise({ preventTimeout: true }), ]); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/tiny-mce-plugin/tiny-mce-multi-url-picker.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/tiny-mce-plugin/tiny-mce-multi-url-picker.plugin.ts index 436df2067c..308c76fee3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/tiny-mce-plugin/tiny-mce-multi-url-picker.plugin.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/multi-url-picker/tiny-mce-plugin/tiny-mce-multi-url-picker.plugin.ts @@ -3,7 +3,7 @@ import type { UmbLinkPickerLink, UmbLinkPickerLinkType } from '../link-picker-mo import type { UmbLinkPickerModalValue } from '../link-picker-modal/link-picker-modal.token.js'; import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; import { UmbTinyMcePluginBase } from '@umbraco-cms/backoffice/tiny-mce'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import type { TinyMcePluginArguments } from '@umbraco-cms/backoffice/tiny-mce'; type AnchorElementAttributes = { @@ -76,8 +76,7 @@ export default class UmbTinyMceMultiUrlPickerPlugin extends UmbTinyMcePluginBase } async #openLinkPicker(currentTarget?: UmbLinkPickerLink) { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalHandler = modalManager.open(this, UMB_LINK_PICKER_MODAL, { + const linkPickerData = await umbOpenModal(this, UMB_LINK_PICKER_MODAL, { data: { config: {}, index: null, @@ -86,11 +85,8 @@ export default class UmbTinyMceMultiUrlPickerPlugin extends UmbTinyMcePluginBase value: { link: currentTarget ?? {}, }, - }); + }).catch(() => undefined); - if (!modalHandler) return; - - const linkPickerData = await modalHandler.onSubmit().catch(() => undefined); if (!linkPickerData) return; // TODO: This is a workaround for the issue where the link picker modal is returning a frozen object, and we need to extract the link into smaller parts to avoid the frozen object issue. diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/collection/config/layout/layout-configuration.element.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/collection/config/layout/layout-configuration.element.ts index 21a01929b1..df110a535e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/collection/config/layout/layout-configuration.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/collection/config/layout/layout-configuration.element.ts @@ -14,7 +14,6 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; import type { UmbInputManifestElement } from '@umbraco-cms/backoffice/components'; import type { UmbPropertyEditorConfigCollection, @@ -113,9 +112,9 @@ export class UmbPropertyEditorUICollectionLayoutConfigurationElement } async #onIconChange(icon: typeof UMB_ICON_PICKER_MODAL.VALUE, index: number) { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modal = modalManager.open(this, UMB_ICON_PICKER_MODAL, { value: icon }); - const picked = await modal?.onSubmit(); + const picked = await (await import('@umbraco-cms/backoffice/modal')) + .umbOpenModal(this, UMB_ICON_PICKER_MODAL, { value: icon }) + .catch(() => undefined); if (!picked) return; const values = [...(this.value ?? [])]; diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/icon-picker/property-editor-ui-icon-picker.element.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/icon-picker/property-editor-ui-icon-picker.element.ts index 69086922a5..5ff021eed5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/icon-picker/property-editor-ui-icon-picker.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/icon-picker/property-editor-ui-icon-picker.element.ts @@ -1,6 +1,6 @@ import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/property-editor'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_ICON_PICKER_MODAL } from '@umbraco-cms/backoffice/icon'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { extractUmbColorVariable } from '@umbraco-cms/backoffice/resources'; @@ -36,10 +36,7 @@ export class UmbPropertyEditorUIIconPickerElement extends UmbLitElement implemen private _color = ''; private async _openModal() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_ICON_PICKER_MODAL); - - const data = await modalContext?.onSubmit(); + const data = await umbOpenModal(this, UMB_ICON_PICKER_MODAL).catch(() => undefined); if (!data) return; if (data.color) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/relation-type-detail.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/relation-type-detail.repository.ts index 013234458f..4f79653af3 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/relation-type-detail.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relation-types/repository/detail/relation-type-detail.repository.ts @@ -18,11 +18,9 @@ export class UmbRelationTypeDetailRepository constructor(host: UmbControllerHost) { super(host); - this.#init = Promise.all([ - this.consumeContext(UMB_RELATION_TYPE_DETAIL_STORE_CONTEXT, (instance) => { - this.#detailStore = instance; - }).asPromise(), - ]); + this.#init = this.consumeContext(UMB_RELATION_TYPE_DETAIL_STORE_CONTEXT, (instance) => { + this.#detailStore = instance; + }).asPromise({ preventTimeout: true }); } /** diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/bulk-delete-with-relation.action.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/bulk-delete-with-relation.action.ts index 71b26bf653..c93f35e1e2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/bulk-delete-with-relation.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-delete/bulk-delete-with-relation.action.ts @@ -1,21 +1,17 @@ import type { MetaEntityBulkActionDeleteWithRelationKind } from './types.js'; import { UMB_BULK_DELETE_WITH_RELATION_CONFIRM_MODAL } from './modal/bulk-delete-with-relation-modal.token.js'; import { UmbDeleteEntityBulkAction } from '@umbraco-cms/backoffice/entity-bulk-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbBulkDeleteWithRelationEntityAction extends UmbDeleteEntityBulkAction { override async _confirmDelete() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - - const modal = modalManager.open(this, UMB_BULK_DELETE_WITH_RELATION_CONFIRM_MODAL, { + await umbOpenModal(this, UMB_BULK_DELETE_WITH_RELATION_CONFIRM_MODAL, { data: { uniques: this.selection, itemRepositoryAlias: this.args.meta.itemRepositoryAlias, referenceRepositoryAlias: this.args.meta.referenceRepositoryAlias, }, }); - - await modal.onSubmit(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/bulk-trash-with-relation.action.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/bulk-trash-with-relation.action.ts index 40383894fd..6e7b1a1705 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/bulk-trash-with-relation.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/bulk-trash/bulk-trash-with-relation.action.ts @@ -1,21 +1,17 @@ import type { MetaEntityBulkActionTrashWithRelationKind } from './types.js'; import { UMB_BULK_TRASH_WITH_RELATION_CONFIRM_MODAL } from './modal/constants.js'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UmbTrashEntityBulkAction } from '@umbraco-cms/backoffice/recycle-bin'; export class UmbBulkTrashWithRelationEntityAction extends UmbTrashEntityBulkAction { override async _confirmTrash() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - - const modal = modalManager.open(this, UMB_BULK_TRASH_WITH_RELATION_CONFIRM_MODAL, { + await umbOpenModal(this, UMB_BULK_TRASH_WITH_RELATION_CONFIRM_MODAL, { data: { uniques: this.selection, itemRepositoryAlias: this.args.meta.itemRepositoryAlias, referenceRepositoryAlias: this.args.meta.referenceRepositoryAlias, }, }); - - await modal.onSubmit(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/delete/delete-with-relation.action.ts b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/delete/delete-with-relation.action.ts index 89e5c23c7e..5a89f91b46 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/delete/delete-with-relation.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/relations/relations/entity-actions/delete/delete-with-relation.action.ts @@ -1,6 +1,6 @@ import type { MetaEntityActionDeleteWithRelationKind } from './types.js'; import { UMB_DELETE_WITH_RELATION_CONFIRM_MODAL } from './modal/constants.js'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UmbDeleteEntityAction } from '@umbraco-cms/backoffice/entity-action'; /** @@ -12,9 +12,7 @@ export class UmbDeleteWithRelationEntityAction extends UmbDeleteEntityAction { override async _confirmTrash(item: any) { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - - const modal = modalManager.open(this, UMB_TRASH_WITH_RELATION_CONFIRM_MODAL, { + await umbOpenModal(this, UMB_TRASH_WITH_RELATION_CONFIRM_MODAL, { data: { unique: item.unique, entityType: item.entityType, @@ -20,8 +18,6 @@ export class UmbTrashWithRelationEntityAction extends UmbTrashEntityAction undefined); - - const value = modalContext.getValue(); + }).catch(() => undefined); this._exposedFields = value?.fields; } async #onFieldViewClick(rowData: SearchResultResponseModel) { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - - const modalContext = modalManager.open(this, UMB_EXAMINE_FIELDS_VIEWER_MODAL, { + await umbOpenModal(this, UMB_EXAMINE_FIELDS_VIEWER_MODAL, { modal: { type: 'sidebar', size: 'medium', }, data: { searchResult: rowData, name: this.getSearchResultNodeName(rowData) }, - }); - await modalContext.onSubmit().catch(() => undefined); + }).catch(() => undefined); } override render() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/search/umb-search-header-app.element.ts b/src/Umbraco.Web.UI.Client/src/packages/search/umb-search-header-app.element.ts index 64b08b35f1..eda517e289 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/search/umb-search-header-app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/search/umb-search-header-app.element.ts @@ -1,13 +1,12 @@ import { UMB_SEARCH_MODAL } from './search-modal/search-modal.token.js'; import { html, customElement } from '@umbraco-cms/backoffice/external/lit'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UmbHeaderAppButtonElement } from '@umbraco-cms/backoffice/components'; @customElement('umb-search-header-app') export class UmbSearchHeaderAppElement extends UmbHeaderAppButtonElement { async #onSearchClick() { - const context = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - context.open(this, UMB_SEARCH_MODAL); + await umbOpenModal(this, UMB_SEARCH_MODAL).catch(() => undefined); } override render() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts index 1e964b826e..5f5970d6a0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/sysinfo/repository/sysinfo.repository.ts @@ -30,6 +30,10 @@ export class UmbSysinfoRepository extends UmbRepositoryBase { async serverUpgradeCheck(currentVersion: string): Promise { // Check if we are allowed to check again const appContext = await this.getContext(UMB_APP_CONTEXT); + if (!appContext) { + throw new Error('Could not get the app context.'); + } + // TODO: Provide a get method, so we do not need to observe in this case: const versionCheckPeriod = await this.observe(appContext.getServerConnection().versionCheckPeriod).asPromise(); if (versionCheckPeriod <= 0) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/tags/repository/tag.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/tags/repository/tag.repository.ts index 9a16402963..1d73ff8018 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tags/repository/tag.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tags/repository/tag.repository.ts @@ -18,7 +18,7 @@ export class UmbTagRepository extends UmbControllerBase implements UmbApi { this.#init = this.consumeContext(UMB_TAG_STORE_CONTEXT, (instance) => { this.#tagStore = instance; - }).asPromise(); + }).asPromise({ preventTimeout: true }); } async requestTags( diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/create/create.action.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/create/create.action.ts index 24245f3734..8ae4745250 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/create/create.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/create/create.action.ts @@ -1,17 +1,10 @@ import { UMB_PARTIAL_VIEW_CREATE_OPTIONS_MODAL } from './options-modal/index.js'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbPartialViewCreateOptionsEntityAction extends UmbEntityActionBase { - constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { - super(host, args); - } - override async execute() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_PARTIAL_VIEW_CREATE_OPTIONS_MODAL, { + await umbOpenModal(this, UMB_PARTIAL_VIEW_CREATE_OPTIONS_MODAL, { data: { parent: { unique: this.args.unique, @@ -19,8 +12,6 @@ export class UmbPartialViewCreateOptionsEntityAction extends UmbEntityActionBase }, }, }); - - await modalContext.onSubmit(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/create/options-modal/partial-view-create-options-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/create/options-modal/partial-view-create-options-modal.element.ts index 469063adf4..4911002b2e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/create/options-modal/partial-view-create-options-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/entity-actions/create/options-modal/partial-view-create-options-modal.element.ts @@ -2,7 +2,7 @@ import { UMB_PARTIAL_VIEW_FROM_SNIPPET_MODAL } from '../snippet-modal/index.js'; import { UMB_PARTIAL_VIEW_FOLDER_REPOSITORY_ALIAS } from '../../../constants.js'; import type { UmbPartialViewCreateOptionsModalData } from './index.js'; import { html, customElement } from '@umbraco-cms/backoffice/external/lit'; -import { UMB_MODAL_MANAGER_CONTEXT, UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; +import { UmbModalBaseElement, umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UmbCreateFolderEntityAction } from '@umbraco-cms/backoffice/tree'; @customElement('umb-partial-view-create-options-modal') @@ -30,26 +30,23 @@ export class UmbPartialViewCreateOptionsModalElement extends UmbModalBaseElement async #onCreateFolderClick(event: PointerEvent) { event.stopPropagation(); - try { - await this.#createFolderAction?.execute(); - this._submitModal(); - } catch (error) { - console.error(error); - } + await this.#createFolderAction + ?.execute() + .then(() => this._submitModal()) + .catch(() => undefined); } async #onCreateFromSnippetClick(event: PointerEvent) { event.stopPropagation(); if (!this.data?.parent) throw new Error('A parent is required to create a folder'); - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_PARTIAL_VIEW_FROM_SNIPPET_MODAL, { + umbOpenModal(this, UMB_PARTIAL_VIEW_FROM_SNIPPET_MODAL, { data: { parent: this.data.parent, }, - }); - - modalContext?.onSubmit().then(() => this._submitModal()); + }) + .then(() => this._submitModal()) + .catch(() => undefined); } // close the modal when navigating to data type @@ -69,15 +66,15 @@ export class UmbPartialViewCreateOptionsModalElement extends UmbModalBaseElement - } + - } + - } + diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace-editor.element.ts index 2f48701c94..4bf7289934 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/partial-views/workspace/partial-view-workspace-editor.element.ts @@ -3,7 +3,7 @@ import type { UmbTemplatingInsertMenuElement } from '../../local-components/inse import { UMB_PARTIAL_VIEW_WORKSPACE_CONTEXT } from './partial-view-workspace.context-token.js'; import { css, customElement, html, nothing, query, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_TEMPLATE_QUERY_BUILDER_MODAL } from '@umbraco-cms/backoffice/template'; import type { UmbCodeEditorElement } from '@umbraco-cms/backoffice/code-editor'; @@ -52,15 +52,11 @@ export class UmbPartialViewWorkspaceEditorElement extends UmbLitElement { } async #openQueryBuilder() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const queryBuilderModal = modalManager.open(this, UMB_TEMPLATE_QUERY_BUILDER_MODAL); + const queryBuilderModalValue = await umbOpenModal(this, UMB_TEMPLATE_QUERY_BUILDER_MODAL).catch(() => undefined); - queryBuilderModal - ?.onSubmit() - .then((queryBuilderModalValue) => { - if (queryBuilderModalValue.value) this._codeEditor?.insert(getQuerySnippet(queryBuilderModalValue.value)); - }) - .catch(() => undefined); + if (queryBuilderModalValue?.value) { + this._codeEditor?.insert(getQuerySnippet(queryBuilderModalValue.value)); + } } override render() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/entity-actions/create/create.action.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/entity-actions/create/create.action.ts index 254265505c..e6dfa3361e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/entity-actions/create/create.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/scripts/entity-actions/create/create.action.ts @@ -1,17 +1,10 @@ import { UMB_SCRIPT_CREATE_OPTIONS_MODAL } from './options-modal/index.js'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbScriptCreateOptionsEntityAction extends UmbEntityActionBase { - constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { - super(host, args); - } - override async execute() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_SCRIPT_CREATE_OPTIONS_MODAL, { + await umbOpenModal(this, UMB_SCRIPT_CREATE_OPTIONS_MODAL, { data: { parent: { entityType: this.args.entityType, @@ -19,8 +12,6 @@ export class UmbScriptCreateOptionsEntityAction extends UmbEntityActionBase { + this._submitModal(); + }) + .catch(() => {}); } // close the modal when navigating to data type diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/create/create.action.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/create/create.action.ts index 10e016064c..ee2dbb7f52 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/create/create.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/create/create.action.ts @@ -1,11 +1,10 @@ import { UMB_STYLESHEET_CREATE_OPTIONS_MODAL } from './options-modal/stylesheet-create-options.modal-token.js'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbStylesheetCreateOptionsEntityAction extends UmbEntityActionBase { override async execute() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_STYLESHEET_CREATE_OPTIONS_MODAL, { + await umbOpenModal(this, UMB_STYLESHEET_CREATE_OPTIONS_MODAL, { data: { parent: { unique: this.args.unique, @@ -13,8 +12,6 @@ export class UmbStylesheetCreateOptionsEntityAction extends UmbEntityActionBase< }, }, }); - - await modalContext.onSubmit(); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/create/options-modal/stylesheet-create-options-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/create/options-modal/stylesheet-create-options-modal.element.ts index a80e0dd271..ac9328a382 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/create/options-modal/stylesheet-create-options-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/entity-actions/create/options-modal/stylesheet-create-options-modal.element.ts @@ -29,12 +29,12 @@ export class UmbStylesheetCreateOptionsModalElement extends UmbModalBaseElement< async #onCreateFolderClick(event: PointerEvent) { event.stopPropagation(); - try { - await this.#createFolderAction?.execute(); - this._submitModal(); - } catch (error) { - console.error(error); - } + this.#createFolderAction + ?.execute() + .then(() => { + this._submitModal(); + }) + .catch(() => {}); } // close the modal when navigating to data type diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/global-components/stylesheet-rule-input/stylesheet-rule-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/global-components/stylesheet-rule-input/stylesheet-rule-input.element.ts index 103e7f7a1b..1741e9f661 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/global-components/stylesheet-rule-input/stylesheet-rule-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/stylesheets/global-components/stylesheet-rule-input/stylesheet-rule-input.element.ts @@ -4,7 +4,7 @@ import { css, html, customElement, repeat, property } from '@umbraco-cms/backoff import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbSorterController } from '@umbraco-cms/backoffice/sorter'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; @customElement('umb-stylesheet-rule-input') @@ -29,17 +29,11 @@ export class UmbStylesheetRuleInputElement extends UUIFormControlMixin(UmbLitEle } async #openRuleSettings(rule: UmbStylesheetRule | null = null) { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - - const value = { - rule: rule ? { name: rule.name, selector: rule.selector, styles: rule.styles } : null, - }; - - const modalContext = modalManager.open(this, UMB_STYLESHEET_RULE_SETTINGS_MODAL, { - value, + return await umbOpenModal(this, UMB_STYLESHEET_RULE_SETTINGS_MODAL, { + value: { + rule: rule ? { name: rule.name, selector: rule.selector, styles: rule.styles } : null, + }, }); - - return modalContext?.onSubmit(); } #appendRule = () => { diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/global-components/input-template/input-template.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/global-components/input-template/input-template.element.ts index db6f6815d2..d28fe7333e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/global-components/input-template/input-template.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/global-components/input-template/input-template.element.ts @@ -5,7 +5,7 @@ import { UmbTemplateItemRepository } from '../../repository/item/index.js'; import { UMB_TEMPLATE_PICKER_MODAL } from '../../modals/index.js'; import { css, html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_WORKSPACE_MODAL } from '@umbraco-cms/backoffice/workspace'; import { UmbModalRouteRegistrationController } from '@umbraco-cms/backoffice/router'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; @@ -124,15 +124,12 @@ export class UmbInputTemplateElement extends UUIFormControlMixin(UmbLitElement, } async #openPicker() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_TEMPLATE_PICKER_MODAL, { + const value = await umbOpenModal(this, UMB_TEMPLATE_PICKER_MODAL, { data: { multiple: true, pickableFilter: (template) => template.unique !== null && !this._selection.includes(template.unique), }, - }); - - const value = await modalContext?.onSubmit().catch(() => undefined); + }).catch(() => undefined); if (!value?.selection) return; diff --git a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/modals/query-builder/query-builder-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/modals/query-builder/query-builder-modal.element.ts index 2ecf70b819..bb54394631 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/templating/templates/modals/query-builder/query-builder-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/templating/templates/modals/query-builder/query-builder-modal.element.ts @@ -8,7 +8,7 @@ import type { } from './query-builder-modal.token.js'; import type { UUIComboboxListElement } from '@umbraco-cms/backoffice/external/uui'; import { css, html, customElement, state, query, queryAll, ifDefined } from '@umbraco-cms/backoffice/external/lit'; -import { UmbModalBaseElement, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { UmbModalBaseElement, umbOpenModal } from '@umbraco-cms/backoffice/modal'; import type { TemplateQueryResultResponseModel, TemplateQuerySettingsResponseModel, @@ -90,24 +90,24 @@ export default class UmbTemplateQueryBuilderModalElement extends UmbModalBaseEle }; async #openDocumentPicker() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - modalManager - .open(this, UMB_DOCUMENT_PICKER_MODAL, { data: { hideTreeRoot: true } }) - .onSubmit() - .then((result) => { - const selection = result.selection[0]; - this.#updateQueryRequest({ rootDocument: selection ? { unique: selection } : null }); + const result = await umbOpenModal(this, UMB_DOCUMENT_PICKER_MODAL, { data: { hideTreeRoot: true } }).catch( + () => undefined, + ); - if (result.selection.length > 0 && result.selection[0] === null) { - this._selectedRootContentName = 'all pages'; - return; - } + if (!result) return; - if (result.selection.length > 0) { - this.#getDocumentItem(result.selection as string[]); - return; - } - }); + const selection = result.selection[0]; + this.#updateQueryRequest({ rootDocument: selection ? { unique: selection } : null }); + + if (result.selection.length > 0 && result.selection[0] === null) { + this._selectedRootContentName = 'all pages'; + return; + } + + if (result.selection.length > 0) { + this.#getDocumentItem(result.selection as string[]); + return; + } } async #getDocumentItem(ids: string[]) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-code-editor.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-code-editor.plugin.ts index f524840f51..b6609ade8e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-code-editor.plugin.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-code-editor.plugin.ts @@ -1,7 +1,7 @@ import { type TinyMcePluginArguments, UmbTinyMcePluginBase } from '../components/input-tiny-mce/tiny-mce-plugin.js'; import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; import { UMB_CODE_EDITOR_MODAL } from '@umbraco-cms/backoffice/code-editor'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export default class UmbTinyMceCodeEditorPlugin extends UmbTinyMcePluginBase { constructor(args: TinyMcePluginArguments) { @@ -16,16 +16,14 @@ export default class UmbTinyMceCodeEditorPlugin extends UmbTinyMcePluginBase { } async #showCodeEditor() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modal = modalManager.open(this, UMB_CODE_EDITOR_MODAL, { + const value = await umbOpenModal(this, UMB_CODE_EDITOR_MODAL, { data: { headline: 'Edit source code', content: this.editor.getContent() ?? '', language: 'html', }, - }); + }).catch(() => undefined); - const value = await modal.onSubmit().catch(() => undefined); if (!value) { return; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-embeddedmedia.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-embeddedmedia.plugin.ts index e451c9d6d7..75e16fa29a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-embeddedmedia.plugin.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiny-mce/plugins/tiny-mce-embeddedmedia.plugin.ts @@ -1,7 +1,7 @@ import { type TinyMcePluginArguments, UmbTinyMcePluginBase } from '../components/input-tiny-mce/tiny-mce-plugin.js'; import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; import type { UmbEmbeddedMediaModalData, UmbEmbeddedMediaModalValue } from '@umbraco-cms/backoffice/embedded-media'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_EMBEDDED_MEDIA_MODAL } from '@umbraco-cms/backoffice/embedded-media'; export default class UmbTinyMceEmbeddedMediaPlugin extends UmbTinyMcePluginBase { @@ -76,12 +76,10 @@ export default class UmbTinyMceEmbeddedMediaPlugin extends UmbTinyMcePluginBase } async #showModal(selectedElm: HTMLElement, embeddedMediaModalData: UmbEmbeddedMediaModalData) { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalHandler = modalManager.open(this, UMB_EMBEDDED_MEDIA_MODAL, { data: embeddedMediaModalData }); + const result = await umbOpenModal(this, UMB_EMBEDDED_MEDIA_MODAL, { data: embeddedMediaModalData }).catch( + () => undefined, + ); - if (!modalHandler) return; - - const result = await modalHandler.onSubmit(); if (!result) return; this.#insertInEditor(result, selectedElm); diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/anchor.tiptap-toolbar-api.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/anchor.tiptap-toolbar-api.ts index 42a8d5f96a..234df7ae8a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/anchor.tiptap-toolbar-api.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/anchor.tiptap-toolbar-api.ts @@ -10,6 +10,7 @@ export default class UmbTiptapToolbarAnchorExtensionApi extends UmbTiptapToolbar if (!attrs) return; const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + if (!modalManager) throw new Error('Modal manager not found'); const modal = modalManager.open(this, UMB_TIPTAP_ANCHOR_MODAL, { data: { id: attrs?.id } }); if (!modal) return; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/character-map.tiptap-toolbar-api.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/character-map.tiptap-toolbar-api.ts index ef239b3727..784a45e242 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/character-map.tiptap-toolbar-api.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/character-map.tiptap-toolbar-api.ts @@ -8,6 +8,7 @@ export default class UmbTiptapToolbarCharacterMapExtensionApi extends UmbTiptapT if (!editor) return; const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); + if (!modalManager) throw new Error('Modal manager not found'); const modal = modalManager.open(this, UMB_TIPTAP_CHARACTER_MAP_MODAL); if (!modal) return; diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/embedded-media.tiptap-toolbar-api.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/embedded-media.tiptap-toolbar-api.ts index b691bb7c7a..d3a5509ab8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/embedded-media.tiptap-toolbar-api.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/embedded-media.tiptap-toolbar-api.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../base.js'; import { umbEmbeddedMedia } from '@umbraco-cms/backoffice/external/tiptap'; import { UMB_EMBEDDED_MEDIA_MODAL } from '@umbraco-cms/backoffice/embedded-media'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; export default class UmbTiptapToolbarEmbeddedMediaExtensionApi extends UmbTiptapToolbarElementApiBase { @@ -21,12 +21,8 @@ export default class UmbTiptapToolbarEmbeddedMediaExtensionApi extends UmbTiptap data.url = attrs['data-embed-url']; } - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalHandler = modalManager.open(this, UMB_EMBEDDED_MEDIA_MODAL, { data }); + const result = await umbOpenModal(this, UMB_EMBEDDED_MEDIA_MODAL, { data }).catch(() => undefined); - if (!modalHandler) return; - - const result = await modalHandler.onSubmit().catch(() => undefined); if (!result) return; editor?.commands.setEmbeddedMedia({ diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/link.tiptap-toolbar-api.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/link.tiptap-toolbar-api.ts index 0761ee50af..a369af6a0f 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/link.tiptap-toolbar-api.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/link.tiptap-toolbar-api.ts @@ -1,7 +1,7 @@ import { UmbTiptapToolbarElementApiBase } from '../base.js'; import { UmbLink } from '@umbraco-cms/backoffice/external/tiptap'; import { UMB_LINK_PICKER_MODAL } from '@umbraco-cms/backoffice/multi-url-picker'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import type { UmbLinkPickerLink } from '@umbraco-cms/backoffice/multi-url-picker'; import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui'; @@ -15,12 +15,9 @@ export default class UmbTiptapToolbarLinkExtensionApi extends UmbTiptapToolbarEl const overlaySize = this.configuration?.getValueByAlias('overlaySize') ?? 'small'; - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalHandler = modalManager.open(this, UMB_LINK_PICKER_MODAL, { data, value, modal: { size: overlaySize } }); - - if (!modalHandler) return; - - const result = await modalHandler.onSubmit().catch(() => undefined); + const result = await umbOpenModal(this, UMB_LINK_PICKER_MODAL, { data, value, modal: { size: overlaySize } }).catch( + () => undefined, + ); if (!result?.link) return; const linkAttrs = this.#parseLinkData(result.link); diff --git a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/source-editor.tiptap-toolbar-api.ts b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/source-editor.tiptap-toolbar-api.ts index 0125fff1b6..3d5593ab05 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/source-editor.tiptap-toolbar-api.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/tiptap/extensions/toolbar/source-editor.tiptap-toolbar-api.ts @@ -1,6 +1,6 @@ import { UmbTiptapToolbarElementApiBase } from '../base.js'; import { UMB_CODE_EDITOR_MODAL } from '@umbraco-cms/backoffice/code-editor'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import type { Editor } from '@umbraco-cms/backoffice/external/tiptap'; import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; @@ -10,19 +10,15 @@ export default class UmbTiptapToolbarSourceEditorExtensionApi extends UmbTiptapT override async execute(editor?: Editor) { if (!editor) return; - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modal = modalManager.open(this, UMB_CODE_EDITOR_MODAL, { + const data = await umbOpenModal(this, UMB_CODE_EDITOR_MODAL, { data: { headline: this.#localize.term('tiptap_sourceCodeEdit'), content: editor?.getHTML() ?? '', language: 'html', formatOnLoad: true, }, - }); + }).catch(() => undefined); - if (!modal) return; - - const data = await modal.onSubmit().catch(() => undefined); if (!data) return; editor?.commands.setContent(data.content, true); diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/change-password/entity-action/change-user-password.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/change-password/entity-action/change-user-password.action.ts index 6e836d9bf8..9ebf53b74e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/change-password/entity-action/change-user-password.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/change-password/entity-action/change-user-password.action.ts @@ -3,7 +3,7 @@ import { UmbChangeUserPasswordRepository } from '@umbraco-cms/backoffice/user'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_CURRENT_USER_CONTEXT, UmbCurrentUserRepository } from '@umbraco-cms/backoffice/current-user'; export class UmbChangeUserPasswordEntityAction extends UmbEntityActionBase { @@ -14,18 +14,19 @@ export class UmbChangeUserPasswordEntityAction extends UmbEntityActionBase undefined); + if (!data) return; const currentUserContext = await this.getContext(UMB_CURRENT_USER_CONTEXT); + if (!currentUserContext) { + throw new Error('Current user context is not available'); + } const isCurrentUser = await currentUserContext.isUserCurrentUser(this.args.unique); if (isCurrentUser) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user-header-app.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user-header-app.element.ts index 362d2d1e37..259424cc21 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user-header-app.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user-header-app.element.ts @@ -1,7 +1,7 @@ import { UMB_CURRENT_USER_MODAL } from './modals/current-user/current-user-modal.token.js'; import type { CSSResultGroup } from '@umbraco-cms/backoffice/external/lit'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_CURRENT_USER_CONTEXT, type UmbCurrentUserModel } from '@umbraco-cms/backoffice/current-user'; import { UmbHeaderAppButtonElement } from '@umbraco-cms/backoffice/components'; @@ -34,8 +34,7 @@ export class UmbCurrentUserHeaderAppElement extends UmbHeaderAppButtonElement { } async #handleUserClick() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - modalManager.open(this, UMB_CURRENT_USER_MODAL); + await umbOpenModal(this, UMB_CURRENT_USER_MODAL).catch(() => undefined); } override render() { diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user.context.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user.context.ts index 96c00fc17f..cdc944c907 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user.context.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/current-user.context.ts @@ -233,6 +233,9 @@ export class UmbCurrentUserContext extends UmbContextBase const url = new URL(window.location.href); const appContext = await this.getContext(UMB_APP_CONTEXT); + if (!appContext) { + throw new Error('App context not available'); + } const backofficePath = appContext.getBackofficePath(); if (url.pathname === backofficePath || url.pathname === ensurePathEndsWithSlash(backofficePath)) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/configure-external-login-providers-action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/configure-external-login-providers-action.ts index 379f4e0a3b..b2cac5832e 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/configure-external-login-providers-action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/configure-external-login-providers-action.ts @@ -1,7 +1,7 @@ import type { UmbCurrentUserAction, UmbCurrentUserActionArgs } from '../current-user-action.extension.js'; import { UMB_CURRENT_USER_EXTERNAL_LOGIN_MODAL } from './modals/external-login-modal.token.js'; import { UmbActionBase } from '@umbraco-cms/backoffice/action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbConfigureExternalLoginProvidersApi extends UmbActionBase> @@ -12,8 +12,7 @@ export class UmbConfigureExternalLoginProvidersApi } async execute() { - const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - await modalManagerContext.open(this, UMB_CURRENT_USER_EXTERNAL_LOGIN_MODAL).onSubmit(); + await umbOpenModal(this, UMB_CURRENT_USER_EXTERNAL_LOGIN_MODAL); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/modals/external-login-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/modals/external-login-modal.element.ts index a6c7cf07c9..2167dfe016 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/modals/external-login-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/external-login/modals/external-login-modal.element.ts @@ -168,6 +168,9 @@ export class UmbCurrentUserExternalLoginModalElement extends UmbLitElement { color: 'positive', }); const authContext = await this.getContext(UMB_AUTH_CONTEXT); + if (!authContext) { + throw new Error('Auth context is missing'); + } await authContext.linkLogin(item.providerSchemeName); } catch (error) { if (error instanceof Error) { @@ -195,6 +198,9 @@ export class UmbCurrentUserExternalLoginModalElement extends UmbLitElement { color: 'danger', }); const authContext = await this.getContext(UMB_AUTH_CONTEXT); + if (!authContext) { + throw new Error('Auth context is missing'); + } await authContext.unlinkLogin(item.providerSchemeName, item.providerKey); } catch (error) { let message = this.localize.term('errors_receivedErrorFromServer'); diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/configure-mfa-providers-action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/configure-mfa-providers-action.ts index e7a923d3b5..993a4d8238 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/configure-mfa-providers-action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/mfa-login/configure-mfa-providers-action.ts @@ -1,7 +1,7 @@ import { UMB_CURRENT_USER_MFA_MODAL } from '../modals/current-user-mfa/current-user-mfa-modal.token.js'; import type { UmbCurrentUserAction, UmbCurrentUserActionArgs } from '../current-user-action.extension.js'; import { UmbActionBase } from '@umbraco-cms/backoffice/action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbConfigureMfaProvidersApi extends UmbActionBase> @@ -12,8 +12,7 @@ export class UmbConfigureMfaProvidersApi } async execute() { - const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - await modalManagerContext.open(this, UMB_CURRENT_USER_MFA_MODAL).onSubmit(); + await umbOpenModal(this, UMB_CURRENT_USER_MFA_MODAL); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user-mfa/current-user-mfa-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user-mfa/current-user-mfa-modal.element.ts index 5dc7fd0d00..7c7fa88ced 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user-mfa/current-user-mfa-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/modals/current-user-mfa/current-user-mfa-modal.element.ts @@ -4,7 +4,7 @@ import { UMB_CURRENT_USER_MFA_DISABLE_PROVIDER_MODAL } from '../current-user-mfa import type { UmbCurrentUserMfaProviderModel } from '../../types.js'; import { css, customElement, html, nothing, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; -import { UMB_MODAL_MANAGER_CONTEXT, type UmbModalContext } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal, type UmbModalContext } from '@umbraco-cms/backoffice/modal'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { mergeObservables } from '@umbraco-cms/backoffice/observable-api'; @@ -141,13 +141,9 @@ export class UmbCurrentUserMfaModalElement extends UmbLitElement { * @param item */ async #onProviderEnable(item: UmbMfaLoginProviderOption) { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - await modalManager - .open(this, UMB_CURRENT_USER_MFA_ENABLE_PROVIDER_MODAL, { - data: { providerName: item.providerName, displayName: item.displayName }, - }) - .onSubmit() - .catch(() => undefined); + await umbOpenModal(this, UMB_CURRENT_USER_MFA_ENABLE_PROVIDER_MODAL, { + data: { providerName: item.providerName, displayName: item.displayName }, + }).catch(() => undefined); } /** @@ -157,13 +153,9 @@ export class UmbCurrentUserMfaModalElement extends UmbLitElement { * @param item */ async #onProviderDisable(item: UmbMfaLoginProviderOption) { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - await modalManager - .open(this, UMB_CURRENT_USER_MFA_DISABLE_PROVIDER_MODAL, { - data: { providerName: item.providerName, displayName: item.displayName }, - }) - .onSubmit() - .catch(() => undefined); + await umbOpenModal(this, UMB_CURRENT_USER_MFA_DISABLE_PROVIDER_MODAL, { + data: { providerName: item.providerName, displayName: item.displayName }, + }).catch(() => undefined); } static override readonly styles = [ diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/change-password-current-user.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/change-password-current-user.action.ts index 6b830126fd..cdbda6f4c5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/change-password-current-user.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/profile/change-password-current-user.action.ts @@ -3,7 +3,7 @@ import { UmbCurrentUserRepository } from '../repository/index.js'; import type { UmbCurrentUserAction, UmbCurrentUserActionArgs } from '../current-user-action.extension.js'; import { UmbActionBase } from '@umbraco-cms/backoffice/action'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_CHANGE_PASSWORD_MODAL } from '@umbraco-cms/backoffice/user-change-password'; export class UmbChangePasswordCurrentUserAction extends UmbActionBase> @@ -32,8 +32,7 @@ export class UmbChangePasswordCurrentUserAction async execute() { if (!this.#unique) return; - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_CHANGE_PASSWORD_MODAL, { + const data = await umbOpenModal(this, UMB_CHANGE_PASSWORD_MODAL, { data: { user: { unique: this.#unique, @@ -41,7 +40,6 @@ export class UmbChangePasswordCurrentUserAction }, }); - const data = await modalContext.onSubmit(); const repository = new UmbCurrentUserRepository(this); await repository.changePassword(data.newPassword, data.oldPassword); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.repository.ts index e823c37939..f6dd8ccc98 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/repository/current-user.repository.ts @@ -22,11 +22,11 @@ export class UmbCurrentUserRepository extends UmbRepositoryBase { this.#init = Promise.all([ this.consumeContext(UMB_CURRENT_USER_STORE_CONTEXT, (instance) => { this.#currentUserStore = instance; - }).asPromise(), + }).asPromise({ preventTimeout: true }), this.consumeContext(UMB_NOTIFICATION_CONTEXT, (instance) => { this.notificationContext = instance; - }).asPromise(), + }).asPromise({ preventTimeout: true }), ]); } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user-an-admin.function.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user-an-admin.function.ts index c39d139297..a858fda128 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user-an-admin.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user-an-admin.function.ts @@ -8,8 +8,8 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; */ export const isCurrentUserAnAdmin = async (host: UmbControllerHost) => { const ctrl = new UmbContextConsumerController(host, UMB_CURRENT_USER_CONTEXT); - const currentUserContext = await ctrl.asPromise(); + const currentUserContext = await ctrl.asPromise().catch(() => undefined); ctrl.destroy(); - return currentUserContext!.isCurrentUserAdmin(); + return currentUserContext?.isCurrentUserAdmin() ?? false; }; diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user.function.ts b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user.function.ts index fcb077363d..2fb8e2e6fd 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user.function.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/current-user/utils/is-current-user.function.ts @@ -9,7 +9,7 @@ import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; */ export const isCurrentUser = async (host: UmbControllerHost, userUnique: string) => { const ctrl = new UmbContextConsumerController(host, UMB_CURRENT_USER_CONTEXT); - const currentUserContext = await ctrl.asPromise(); + const currentUserContext = await ctrl.asPromise().catch(() => undefined); ctrl.destroy(); return await currentUserContext!.isUserCurrentUser(userUnique); diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/collection/repository/user-group-collection.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/collection/repository/user-group-collection.repository.ts index c2479659ab..a5f32f9b69 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/collection/repository/user-group-collection.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/collection/repository/user-group-collection.repository.ts @@ -19,7 +19,7 @@ export class UmbUserGroupCollectionRepository extends UmbControllerBase implemen this.#init = this.consumeContext(UMB_USER_GROUP_DETAIL_STORE_CONTEXT, (instance) => { this.#detailStore = instance; - }).asPromise(); + }).asPromise({ preventTimeout: true }); } async requestCollection(filter: UmbUserGroupCollectionFilterModel = { skip: 0, take: 100 }) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group/user-group-workspace-editor.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group/user-group-workspace-editor.element.ts index e44a4a6a7c..b8a7de7a9a 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group/user-group-workspace-editor.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user-group/workspace/user-group/user-group-workspace-editor.element.ts @@ -9,7 +9,7 @@ import type { UmbInputDocumentElement } from '@umbraco-cms/backoffice/document'; import type { UmbInputSectionElement } from '@umbraco-cms/backoffice/section'; import type { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import type { UmbInputMediaElement } from '@umbraco-cms/backoffice/media'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import type { UmbInputLanguageElement } from '@umbraco-cms/backoffice/language'; import { UMB_ICON_PICKER_MODAL } from '@umbraco-cms/backoffice/icon'; import type { UmbInputWithAliasElement } from '@umbraco-cms/backoffice/components'; @@ -177,21 +177,20 @@ export class UmbUserGroupWorkspaceEditorElement extends UmbLitElement { async #onIconClick() { const [alias, color] = this._icon?.replace('color-', '')?.split(' ') ?? []; - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_ICON_PICKER_MODAL, { + const result = await umbOpenModal(this, UMB_ICON_PICKER_MODAL, { value: { icon: alias, color: color, }, - }); + }).catch(() => undefined); - modalContext?.onSubmit().then((saved) => { - if (saved.icon && saved.color) { - this.#workspaceContext?.updateProperty('icon', `${saved.icon} color-${saved.color}`); - } else if (saved.icon) { - this.#workspaceContext?.updateProperty('icon', saved.icon); - } - }); + if (!result) return; + + if (result.icon && result.color) { + this.#workspaceContext?.updateProperty('icon', `${result.icon} color-${result.color}`); + } else if (result.icon) { + this.#workspaceContext?.updateProperty('icon', result.icon); + } } #onNameAndAliasChange(event: InputEvent & { target: UmbInputWithAliasElement }) { diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/create/modal/create-user-modal.element.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/create/modal/create-user-modal.element.ts index cea24184d8..a2b2eb60d0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/create/modal/create-user-modal.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/create/modal/create-user-modal.element.ts @@ -4,8 +4,9 @@ import { UMB_CREATE_USER_SUCCESS_MODAL } from './create-user-success-modal.token import type { UmbCreateUserModalData } from './create-user-modal.token.js'; import type { UmbUserGroupInputElement } from '@umbraco-cms/backoffice/user-group'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { css, html, customElement, query } from '@umbraco-cms/backoffice/external/lit'; -import { UmbModalBaseElement, UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models'; @customElement('umb-create-user-modal') @@ -56,21 +57,17 @@ export class UmbCreateUserModalElement extends UmbModalBaseElement { this._submitModal(); }) - .catch((reason) => { + .catch((reason: any) => { if (reason?.type === 'createAnotherUser') { this._form?.reset(); } else { diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/create/user-entity-create-option-action-base.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/create/user-entity-create-option-action-base.ts index ca55652d5a..7b493db7f0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/create/user-entity-create-option-action-base.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/create/user-entity-create-option-action-base.ts @@ -8,7 +8,7 @@ import { type MetaEntityCreateOptionAction, type UmbEntityCreateOptionActionArgs, } from '@umbraco-cms/backoffice/entity-create-option-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export interface UmbUserEntityCreateOptionActionBaseArgs extends UmbEntityCreateOptionActionArgs { @@ -24,18 +24,13 @@ export abstract class UmbUserEntityCreateOptionActionBase extends UmbEntityCreat } override async execute() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - - const modalContext = modalManager.open(this, UMB_CREATE_USER_MODAL, { + await umbOpenModal(this, UMB_CREATE_USER_MODAL, { data: { user: { kind: this.#kind, }, }, - }); - - await modalContext - ?.onSubmit() + }) .then(() => { this.#requestReloadChildrenOfEntity(); }) @@ -48,6 +43,7 @@ export abstract class UmbUserEntityCreateOptionActionBase extends UmbEntityCreat async #requestReloadChildrenOfEntity() { const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT); + if (!eventContext) throw new Error('Event context not found'); const event = new UmbRequestReloadChildrenOfEntityEvent({ entityType: this.args.entityType, unique: this.args.unique, diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/mfa/mfa-user.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/mfa/mfa-user.action.ts index a6bdc5f5f0..daf2c8cdff 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/mfa/mfa-user.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-actions/mfa/mfa-user.action.ts @@ -5,7 +5,7 @@ import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UMB_CURRENT_USER_CONTEXT } from '@umbraco-cms/backoffice/current-user'; import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbMfaUserEntityAction extends UmbEntityActionBase { constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { @@ -17,27 +17,21 @@ export class UmbMfaUserEntityAction extends UmbEntityActionBase { if (!unique) throw new Error('Unique is not available'); const currentUserContext = await this.getContext(UMB_CURRENT_USER_CONTEXT); + if (!currentUserContext) throw new Error('Current user context not found'); const currentUserModel = await firstValueFrom(currentUserContext.currentUser); if (!currentUserModel) throw new Error('Current user is not available'); // If you clicked on yourself, we can just use the current user modal - const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); if (currentUserModel.unique === unique) { - await modalManagerContext - .open(this, UMB_CURRENT_USER_MFA_MODAL) - .onSubmit() - .catch(() => undefined); + await umbOpenModal(this, UMB_CURRENT_USER_MFA_MODAL).catch(() => undefined); return; } // Otherwise we will show the generic mfa modal - await modalManagerContext - .open(this, UMB_USER_MFA_MODAL, { - data: { unique }, - }) - .onSubmit() - .catch(() => undefined); + await umbOpenModal(this, UMB_USER_MFA_MODAL, { + data: { unique }, + }).catch(() => undefined); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-bulk-actions/disable/disable.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-bulk-actions/disable/disable.action.ts index 44ee673a77..354a69f818 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-bulk-actions/disable/disable.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/entity-bulk-actions/disable/disable.action.ts @@ -10,6 +10,7 @@ export class UmbDisableUserEntityBulkAction extends UmbEntityBulkActionBase { override async execute() { - const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - const modalContext = modalManager.open(this, UMB_INVITE_USER_MODAL); - await modalContext?.onSubmit(); + await umbOpenModal(this, UMB_INVITE_USER_MODAL); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/user/user/invite/entity-action/resend-invite/resend-invite.action.ts b/src/Umbraco.Web.UI.Client/src/packages/user/user/invite/entity-action/resend-invite/resend-invite.action.ts index 53fc3ae5fa..c52108ded8 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/user/user/invite/entity-action/resend-invite/resend-invite.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/user/user/invite/entity-action/resend-invite/resend-invite.action.ts @@ -3,7 +3,7 @@ import { UMB_RESEND_INVITE_TO_USER_MODAL } from '../../index.js'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; export class UmbResendInviteToUserEntityAction extends UmbEntityActionBase { constructor(host: UmbControllerHost, args: UmbEntityActionArgs) { @@ -13,16 +13,13 @@ export class UmbResendInviteToUserEntityAction extends UmbEntityActionBase { this.detailStore = instance; - }).asPromise(), + }).asPromise({ preventTimeout: true }), this.consumeContext(UMB_USER_ITEM_STORE_CONTEXT, (instance) => { this.itemStore = instance; - }).asPromise(), + }).asPromise({ preventTimeout: true }), this.consumeContext(UMB_NOTIFICATION_CONTEXT, (instance) => { this.notificationContext = instance; - }).asPromise(), + }).asPromise({ preventTimeout: true }), ]); } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/webhook/webhook-event/repository/webhook-event.repository.ts b/src/Umbraco.Web.UI.Client/src/packages/webhook/webhook-event/repository/webhook-event.repository.ts index aa57d568ec..669f3f53c2 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/webhook/webhook-event/repository/webhook-event.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/webhook/webhook-event/repository/webhook-event.repository.ts @@ -16,11 +16,9 @@ export class UmbWebhookEventRepository extends UmbRepositoryBase implements UmbA this.#source = new UmbWebhookEventServerDataSource(host); - this.#init = Promise.all([ - this.consumeContext(UMB_WEBHOOK_EVENT_STORE_CONTEXT, (instance) => { - this.#store = instance; - }).asPromise(), - ]); + this.#init = this.consumeContext(UMB_WEBHOOK_EVENT_STORE_CONTEXT, (instance) => { + this.#store = instance; + }).asPromise({ preventTimeout: true }); } async requestEvents() {