V16/feature: get context resolves in undefined if not found (#18611)
* implement getContext rejecter + allow for resolving in undefined * fix test * timeout concept * adjustments * adapt implementation * ability to set timeoutFrame for faster test * update * make sure provider only provides when connected * make sure action apis can fail without resolving * make sure to await the arrival of the UMB_AUTH_CONTEXT * no need to be async * consume to stay up to date * one rendering cycle approach * adjusting context consumption part 1 * implementation adjustments * correction patch 2 * correction patch 3 * correction batch 4 * correction batch 5 * correction batch 6
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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<unknown>;
|
||||
#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<boolean> {
|
||||
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<boolean> {
|
||||
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<boolean> {
|
||||
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, {
|
||||
|
||||
@@ -34,29 +34,26 @@ export class UmbBackofficeContext extends UmbContextBase<UmbBackofficeContext> {
|
||||
});
|
||||
});
|
||||
|
||||
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() {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -24,24 +24,22 @@ export class UmbPreviewContext extends UmbContextBase<UmbPreviewContext> {
|
||||
|
||||
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() {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type {
|
||||
UmbContextCallback,
|
||||
UmbContextConsumerAsPromiseOptionsType,
|
||||
UmbContextConsumerController,
|
||||
UmbContextProviderController,
|
||||
UmbContextToken,
|
||||
@@ -64,5 +65,6 @@ export interface UmbClassInterface extends UmbControllerHost {
|
||||
*/
|
||||
getContext<BaseType = unknown, ResultType extends BaseType = BaseType>(
|
||||
alias: string | UmbContextToken<BaseType, ResultType>,
|
||||
): Promise<ResultType>;
|
||||
options?: UmbContextConsumerAsPromiseOptionsType,
|
||||
): Promise<ResultType | undefined>;
|
||||
}
|
||||
|
||||
@@ -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 = <T extends ClassConstructor<EventTarget>>(superClas
|
||||
|
||||
async getContext<BaseType = unknown, ResultType extends BaseType = BaseType>(
|
||||
contextAlias: string | UmbContextToken<BaseType, ResultType>,
|
||||
): Promise<ResultType> {
|
||||
options?: UmbContextConsumerAsPromiseOptionsType,
|
||||
): Promise<ResultType | undefined> {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<UmbTestContextConsumerClass>(
|
||||
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<UmbTestContextConsumerClass>(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<UmbTestContextConsumerClass>(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<UmbTestContextConsumerClass>(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<UmbTestContextConsumerClass>(
|
||||
() => 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<UmbTestContextConsumerClass>(
|
||||
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<typeof localConsumer.instance, undefined> 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();
|
||||
|
||||
@@ -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<BaseType = unknown, ResultType extends BaseType = BaseType> {
|
||||
protected _retrieveHost: HostElementMethod;
|
||||
|
||||
#raf?: number;
|
||||
#skipHost?: boolean;
|
||||
#stopAtContextMatch = true;
|
||||
#callback?: UmbContextCallback<ResultType>;
|
||||
#promise?: Promise<ResultType>;
|
||||
#promise?: Promise<ResultType | undefined>;
|
||||
#promiseResolver?: (instance: ResultType) => void;
|
||||
#promiseRejecter?: (reason: string) => void;
|
||||
|
||||
#instance?: ResultType;
|
||||
get instance() {
|
||||
@@ -83,7 +92,7 @@ export class UmbContextConsumer<BaseType = unknown, ResultType extends BaseType
|
||||
throw new Error('Not allowed to set context api instance to undefined.');
|
||||
}
|
||||
if (this.#discriminator) {
|
||||
// Notice if discriminator returns false, we do not want to setInstance.
|
||||
// Notice if discriminator returns false, we do not want to setInstance. [NL]
|
||||
if (this.#discriminator(instance)) {
|
||||
this.setInstance(instance as unknown as ResultType);
|
||||
return true;
|
||||
@@ -102,23 +111,29 @@ export class UmbContextConsumer<BaseType = unknown, ResultType extends BaseType
|
||||
if (promiseResolver && instance !== undefined) {
|
||||
promiseResolver(instance);
|
||||
this.#promise = undefined;
|
||||
this.#promiseResolver = undefined;
|
||||
this.#promiseRejecter = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @memberof UmbContextConsumer
|
||||
* @param {UmbContextConsumerAsPromiseOptionsType} options - Prevent the promise from timing out.
|
||||
* @description Get the context as a promise.
|
||||
* @returns {UmbContextConsumer} - A promise that resolves when the context is consumed.
|
||||
*/
|
||||
public asPromise(): Promise<ResultType> {
|
||||
public asPromise(options?: UmbContextConsumerAsPromiseOptionsType): Promise<ResultType | undefined> {
|
||||
return (
|
||||
this.#promise ??
|
||||
(this.#promise = new Promise<ResultType>((resolve) => {
|
||||
(this.#promise = new Promise<ResultType | undefined>((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<BaseType = unknown, ResultType extends BaseType
|
||||
this.#stopAtContextMatch,
|
||||
);
|
||||
(this.#skipHost ? this._retrieveHost()?.parentNode : this._retrieveHost())?.dispatchEvent(event);
|
||||
|
||||
/*
|
||||
let i: number = this.#timeoutFrames ?? 1;
|
||||
while (i-- > 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<BaseType = unknown, ResultType extends BaseType
|
||||
}
|
||||
|
||||
public hostDisconnected(): void {
|
||||
if (this.#raf) {
|
||||
cancelAnimationFrame(this.#raf);
|
||||
this.#promiseRejecter?.('Context request was cancelled, host was disconnected.');
|
||||
}
|
||||
// TODO: We need to use closets application element. We need this in order to have separate Backoffice running within or next to each other.
|
||||
window.removeEventListener(UMB_CONTEXT_PROVIDE_EVENT_TYPE, this.#handleNewProvider);
|
||||
//window.removeEventListener(umbContextUnprovidedEventType, this.#handleRemovedProvider);
|
||||
@@ -161,7 +194,7 @@ export class UmbContextConsumer<BaseType = unknown, ResultType extends BaseType
|
||||
}
|
||||
};
|
||||
|
||||
//Niels: I'm keeping this code around as it might be relevant, but I wanted to try to see if leaving this feature out for now could work for us.
|
||||
//Niels: I'm keeping this code around as it might be relevant, but I wanted to try to see if leaving this feature out for now could work for us. [NL]
|
||||
/*
|
||||
#handleRemovedProvider = (event: Event) => {
|
||||
// Does seem a bit unnecessary, we could just assume the type via type casting...
|
||||
@@ -186,6 +219,7 @@ export class UmbContextConsumer<BaseType = unknown, ResultType extends BaseType
|
||||
this.#callback = undefined;
|
||||
this.#promise = undefined;
|
||||
this.#promiseResolver = undefined;
|
||||
this.#promiseRejecter = undefined;
|
||||
this.#instance = undefined;
|
||||
this.#discriminator = undefined;
|
||||
}
|
||||
|
||||
@@ -40,15 +40,15 @@ export class UmbContextProvider<BaseType = unknown, ResultType extends BaseType
|
||||
this.#contextAlias = idSplit[0];
|
||||
this.#apiAlias = idSplit[1] ?? 'default';
|
||||
this.#instance = instance;
|
||||
|
||||
this.#eventTarget.addEventListener(UMB_CONTEXT_REQUEST_EVENT_TYPE, this.#handleContextRequest);
|
||||
this.#eventTarget.addEventListener(UMB_DEBUG_CONTEXT_EVENT_TYPE, this.#handleDebugContextRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* @memberof UmbContextProvider
|
||||
*/
|
||||
public hostConnected(): void {
|
||||
this.#eventTarget.addEventListener(UMB_CONTEXT_REQUEST_EVENT_TYPE, this.#handleContextRequest);
|
||||
this.#eventTarget.addEventListener(UMB_DEBUG_CONTEXT_EVENT_TYPE, this.#handleDebugContextRequest);
|
||||
|
||||
this.#eventTarget.dispatchEvent(new UmbContextProvideEventImplementation(this.#contextAlias));
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ export class UmbContextProvider<BaseType = unknown, ResultType extends BaseType
|
||||
* @memberof UmbContextProvider
|
||||
*/
|
||||
public hostDisconnected(): void {
|
||||
this.#eventTarget.removeEventListener(UMB_CONTEXT_REQUEST_EVENT_TYPE, this.#handleContextRequest);
|
||||
this.#eventTarget.removeEventListener(UMB_DEBUG_CONTEXT_EVENT_TYPE, this.#handleDebugContextRequest);
|
||||
// Out-commented for now, but kept if we like to reintroduce this:
|
||||
//window.dispatchEvent(new UmbContextUnprovidedEventImplementation(this._contextAlias, this.#instance));
|
||||
}
|
||||
|
||||
@@ -3,7 +3,11 @@ import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-
|
||||
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
|
||||
import type { HTMLElementConstructor } from '@umbraco-cms/backoffice/extension-api';
|
||||
import { type UmbControllerAlias, UmbControllerHostElementMixin } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { UmbContextToken, UmbContextCallback } from '@umbraco-cms/backoffice/context-api';
|
||||
import type {
|
||||
UmbContextToken,
|
||||
UmbContextCallback,
|
||||
UmbContextConsumerAsPromiseOptionsType,
|
||||
} from '@umbraco-cms/backoffice/context-api';
|
||||
import { UmbContextConsumerController, UmbContextProviderController } from '@umbraco-cms/backoffice/context-api';
|
||||
import type { ObserverCallback } from '@umbraco-cms/backoffice/observable-api';
|
||||
import { UmbObserverController, simpleHashCode } from '@umbraco-cms/backoffice/observable-api';
|
||||
@@ -74,12 +78,19 @@ export const UmbElementMixin = <T extends HTMLElementConstructor>(superClass: T)
|
||||
|
||||
async getContext<BaseType = unknown, ResultType extends BaseType = BaseType>(
|
||||
contextAlias: string | UmbContextToken<BaseType, ResultType>,
|
||||
): Promise<ResultType> {
|
||||
options?: UmbContextConsumerAsPromiseOptionsType,
|
||||
): Promise<ResultType | undefined> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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<UmbBlockGridValueModel>(
|
||||
value.clipboard.selection,
|
||||
UMB_BLOCK_GRID_PROPERTY_EDITOR_UI_ALIAS,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -33,7 +33,7 @@ export class UmbBlockGridManagerContext<
|
||||
return this.#inlineEditingMode.getValue();
|
||||
}
|
||||
|
||||
#initAppUrl: Promise<void>;
|
||||
#initAppUrl: Promise<unknown>;
|
||||
|
||||
#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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<UmbBlockListValueModel>(
|
||||
value.clipboard.selection,
|
||||
UMB_BLOCK_LIST_PROPERTY_EDITOR_UI_ALIAS,
|
||||
|
||||
@@ -12,7 +12,7 @@ import { UUICardEvent } from '@umbraco-cms/backoffice/external/uui';
|
||||
@customElement('umb-block-type-card')
|
||||
export class UmbBlockTypeCardElement extends UmbLitElement {
|
||||
//
|
||||
#init: Promise<void>;
|
||||
#init: Promise<unknown>;
|
||||
#serverUrl: string = '';
|
||||
|
||||
readonly #itemManager = new UmbRepositoryItemsManager<UmbDocumentTypeItemModel>(
|
||||
@@ -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];
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -25,6 +25,9 @@ export class UmbBlockTypeWorkspaceContext<BlockTypeData extends UmbBlockTypeWith
|
||||
// Just for context token safety:
|
||||
public readonly IS_BLOCK_TYPE_WORKSPACE_CONTEXT = true;
|
||||
|
||||
#gotPropertyContext: Promise<unknown>;
|
||||
#propertyContext?: typeof UMB_PROPERTY_CONTEXT.TYPE;
|
||||
|
||||
#entityType: string;
|
||||
#data = new UmbObjectState<BlockTypeData | undefined>(undefined);
|
||||
readonly data = this.#data.asObservable();
|
||||
@@ -44,6 +47,10 @@ export class UmbBlockTypeWorkspaceContext<BlockTypeData extends UmbBlockTypeWith
|
||||
const manifest = args.manifest;
|
||||
this.#entityType = manifest.meta?.entityType;
|
||||
|
||||
this.#gotPropertyContext = this.consumeContext(UMB_PROPERTY_CONTEXT, (context) => {
|
||||
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<BlockTypeData extends UmbBlockTypeWith
|
||||
|
||||
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: 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<BlockTypeData extends UmbBlockTypeWith
|
||||
throw new Error('No data to submit.');
|
||||
}
|
||||
|
||||
const context = await this.getContext(UMB_PROPERTY_CONTEXT);
|
||||
|
||||
context.setValue(
|
||||
appendToFrozenArray(context.getValue() ?? [], this.#data.getValue(), (x) => 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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -88,7 +88,7 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
|
||||
this.#modalContext = context;
|
||||
this.#originData = context?.data.originData;
|
||||
context.onSubmit().catch(this.#modalRejected);
|
||||
}).asPromise();
|
||||
}).asPromise({ preventTimeout: true });
|
||||
|
||||
this.#retrieveBlockManager = this.consumeContext(UMB_BLOCK_MANAGER_CONTEXT, (manager) => {
|
||||
this.#blockManager = manager;
|
||||
@@ -97,7 +97,7 @@ export class UmbBlockWorkspaceContext<LayoutDataType extends UmbBlockLayoutBaseM
|
||||
|
||||
this.#retrieveBlockEntries = this.consumeContext(UMB_BLOCK_ENTRIES_CONTEXT, (context) => {
|
||||
this.#blockEntries = context;
|
||||
}).asPromise();
|
||||
}).asPromise({ preventTimeout: true });
|
||||
|
||||
this.consumeContext(UMB_BLOCK_ENTRY_CONTEXT, (context) => {
|
||||
this.#name.setValue(context.getName());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import { UMB_CLIPBOARD_PROPERTY_CONTEXT } from './clipboard.property-context-token.js';
|
||||
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
|
||||
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_PROPERTY_CONTEXT, UmbPropertyValueCloneController } from '@umbraco-cms/backoffice/property';
|
||||
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/property-editor';
|
||||
@@ -27,16 +27,8 @@ import type { UmbEntityUnique } from '@umbraco-cms/backoffice/entity';
|
||||
export class UmbClipboardPropertyContext extends UmbContextBase<UmbClipboardPropertyContext> {
|
||||
#init?: Promise<unknown>;
|
||||
|
||||
#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<UmbClipboardProp
|
||||
propertyEditorUiAlias: string;
|
||||
}): Promise<UmbClipboardEntryDetailModel | undefined> {
|
||||
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<UmbClipboardProp
|
||||
|
||||
const pasteTranslatorManifests = this.getPasteTranslatorManifests(args.propertyEditorUiAlias);
|
||||
const propertyEditorUiManifest = await this.#findPropertyEditorUiManifest(args.propertyEditorUiAlias);
|
||||
const config = (await this.getContext(UMB_PROPERTY_CONTEXT)).getConfig();
|
||||
const config = (await this.getContext(UMB_PROPERTY_CONTEXT))?.getConfig();
|
||||
|
||||
if (!config) {
|
||||
throw new Error('Property context is required');
|
||||
}
|
||||
|
||||
const valueResolver = new UmbClipboardPastePropertyValueTranslatorValueResolver(this);
|
||||
|
||||
const modal = this.#modalManagerContext?.open(this, UMB_CLIPBOARD_ENTRY_PICKER_MODAL, {
|
||||
const result = await umbOpenModal(this, UMB_CLIPBOARD_ENTRY_PICKER_MODAL, {
|
||||
data: {
|
||||
asyncFilter: async (clipboardEntryDetail) => {
|
||||
const hasSupportedPasteTranslator = this.hasSupportedPasteTranslator(
|
||||
@@ -164,7 +163,6 @@ export class UmbClipboardPropertyContext extends UmbContextBase<UmbClipboardProp
|
||||
},
|
||||
});
|
||||
|
||||
const result = await modal?.onSubmit();
|
||||
const selection = result?.selection || [];
|
||||
|
||||
if (!selection.length) {
|
||||
@@ -223,6 +221,9 @@ export class UmbClipboardPropertyContext extends UmbContextBase<UmbClipboardProp
|
||||
}
|
||||
|
||||
const clipboardContext = await this.getContext(UMB_CLIPBOARD_CONTEXT);
|
||||
if (!clipboardContext) {
|
||||
throw new Error('Clipboard context is required');
|
||||
}
|
||||
const entry = await clipboardContext.read(clipboardEntryUnique);
|
||||
|
||||
if (!entry) {
|
||||
|
||||
@@ -42,7 +42,7 @@ export class UmbCollectionCreateActionButtonElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
if (!controller.api) throw new Error('No API found');
|
||||
await controller.api.execute();
|
||||
await controller.api.execute().catch(() => {});
|
||||
}
|
||||
|
||||
constructor() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ export class UmbEntityActionsBundleElement extends UmbLitElement {
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
await this._firstActionApi?.execute();
|
||||
await this._firstActionApi?.execute().catch(() => {});
|
||||
}
|
||||
|
||||
#onActionExecuted() {
|
||||
|
||||
@@ -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')}...`,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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 })),
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
@@ -61,15 +61,12 @@ export class UmbCreateEntityAction extends UmbEntityActionBase<MetaEntityActionC
|
||||
return;
|
||||
}
|
||||
|
||||
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
|
||||
const modalContext = modalManager.open(this, UMB_ENTITY_CREATE_OPTION_ACTION_LIST_MODAL, {
|
||||
await umbOpenModal(this, UMB_ENTITY_CREATE_OPTION_ACTION_LIST_MODAL, {
|
||||
data: {
|
||||
unique: this.args.unique,
|
||||
entityType: this.args.entityType,
|
||||
},
|
||||
});
|
||||
|
||||
await modalContext.onSubmit();
|
||||
}
|
||||
|
||||
async #createSingleOptionApi(
|
||||
|
||||
@@ -68,9 +68,12 @@ export class UmbEntityCreateOptionActionListModalElement extends UmbModalBaseEle
|
||||
throw new Error('No API found');
|
||||
}
|
||||
|
||||
await controller.api.execute();
|
||||
|
||||
this._submitModal();
|
||||
controller.api
|
||||
.execute()
|
||||
.then(() => {
|
||||
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)}>
|
||||
</umb-ref-item>
|
||||
`;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -30,6 +30,9 @@ export class UmbDuplicateEntityAction extends UmbEntityActionBase<any> {
|
||||
|
||||
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,
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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<M
|
||||
async execute() {
|
||||
if (this.selection?.length === 0) return;
|
||||
|
||||
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
|
||||
|
||||
const modalContext = modalManager.open(this, UMB_TREE_PICKER_MODAL, {
|
||||
const value = await umbOpenModal(this, UMB_TREE_PICKER_MODAL, {
|
||||
data: {
|
||||
foldersOnly: this.args.meta.foldersOnly,
|
||||
hideTreeRoot: this.args.meta.hideTreeRoot,
|
||||
@@ -25,7 +23,6 @@ export class UmbMediaDuplicateEntityBulkAction extends UmbEntityBulkActionBase<M
|
||||
},
|
||||
});
|
||||
|
||||
const value = await modalContext.onSubmit().catch(() => undefined);
|
||||
if (!value?.selection?.length) return;
|
||||
|
||||
const destinationUnique = value.selection[0];
|
||||
|
||||
@@ -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<MetaEn
|
||||
async execute() {
|
||||
if (this.selection?.length === 0) return;
|
||||
|
||||
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
|
||||
|
||||
const modalContext = modalManager.open(this, UMB_TREE_PICKER_MODAL, {
|
||||
const value = await umbOpenModal(this, UMB_TREE_PICKER_MODAL, {
|
||||
data: {
|
||||
foldersOnly: this.args.meta.foldersOnly,
|
||||
hideTreeRoot: this.args.meta.hideTreeRoot,
|
||||
@@ -25,7 +23,6 @@ export class UmbMediaMoveEntityBulkAction extends UmbEntityBulkActionBase<MetaEn
|
||||
},
|
||||
});
|
||||
|
||||
const value = await modalContext.onSubmit().catch(() => undefined);
|
||||
if (!value?.selection?.length) return;
|
||||
|
||||
const destinationUnique = value.selection[0];
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<void> {
|
||||
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 });
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './open-modal.controller.js';
|
||||
@@ -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<ModalData, ModalValue>,
|
||||
>(
|
||||
modalAlias: UmbModalToken<ModalData, ModalValue> | string,
|
||||
args: UmbModalContextClassArgs<ModalAliasTypeAsToken> = {},
|
||||
): Promise<ModalValue> {
|
||||
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<ModalData, ModalValue>,
|
||||
>(
|
||||
host: UmbControllerHost,
|
||||
modalAlias: UmbModalToken<ModalData, ModalValue> | string,
|
||||
args: UmbModalContextClassArgs<ModalAliasTypeAsToken> = {},
|
||||
): Promise<ModalValue> {
|
||||
return new UmbOpenModalController(host).open(modalAlias, args);
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
@@ -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 });
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ import './peek-error-notification.element.js';
|
||||
export class UmbPeekErrorController extends UmbControllerBase {
|
||||
async open(args: UmbPeekErrorArgs): Promise<void> {
|
||||
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',
|
||||
|
||||
@@ -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<PickerModalConfigType>) {
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export class UmbPropertyTypeWorkspaceContext<PropertyTypeData extends UmbPropert
|
||||
this.#contentTypeContext = context;
|
||||
})
|
||||
.skipHost()
|
||||
.asPromise();
|
||||
.asPromise({ preventTimeout: true });
|
||||
|
||||
this.routes.setRoutes([
|
||||
{
|
||||
|
||||
@@ -30,6 +30,7 @@ export class UmbEmptyRecycleBinEntityAction extends UmbEntityActionBase<MetaEnti
|
||||
await recycleBinRepository.requestEmpty();
|
||||
|
||||
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,
|
||||
|
||||
@@ -6,7 +6,7 @@ import type {
|
||||
import type { PropertyValueMap } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { html, customElement, state, css } from '@umbraco-cms/backoffice/external/lit';
|
||||
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
|
||||
import { UMB_MODAL_MANAGER_CONTEXT, UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
|
||||
import { UmbModalBaseElement, umbOpenModal } from '@umbraco-cms/backoffice/modal';
|
||||
import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
@@ -102,15 +102,12 @@ export class UmbRestoreFromRecycleBinModalElement extends UmbModalBaseElement<
|
||||
async #onSelectCustomDestination() {
|
||||
if (!this.data?.pickerModal) throw new Error('Cannot select a destination without a picker modal.');
|
||||
|
||||
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
|
||||
const modal = modalManager.open(this, this.data.pickerModal, {
|
||||
const { selection } = await umbOpenModal(this, this.data.pickerModal, {
|
||||
data: {
|
||||
multiple: false,
|
||||
},
|
||||
});
|
||||
|
||||
const { selection } = await modal.onSubmit();
|
||||
|
||||
if (selection.length > 0) {
|
||||
const unique = selection[0];
|
||||
this.setDestination(unique);
|
||||
|
||||
@@ -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<Me
|
||||
override async execute() {
|
||||
if (!this.args.unique) throw new Error('Cannot restore an item without a unique identifier.');
|
||||
|
||||
const modalManager = await this.getContext(UMB_MODAL_MANAGER_CONTEXT);
|
||||
const modal = modalManager.open(this, UMB_RESTORE_FROM_RECYCLE_BIN_MODAL, {
|
||||
const { destination } = await umbOpenModal(this, UMB_RESTORE_FROM_RECYCLE_BIN_MODAL, {
|
||||
data: {
|
||||
unique: this.args.unique,
|
||||
entityType: this.args.entityType,
|
||||
@@ -28,10 +27,12 @@ export class UmbRestoreFromRecycleBinEntityAction extends UmbEntityActionBase<Me
|
||||
},
|
||||
});
|
||||
|
||||
const { destination } = await modal.onSubmit();
|
||||
if (!destination) throw new Error('Cannot reload the structure without a destination.');
|
||||
|
||||
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,
|
||||
|
||||
@@ -68,6 +68,7 @@ export class UmbTrashEntityAction<
|
||||
|
||||
async #notify() {
|
||||
const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
|
||||
if (!actionEventContext) throw new Error('Action event context is missing.');
|
||||
|
||||
const event = new UmbRequestReloadStructureForEntityEvent({
|
||||
unique: this.args.unique,
|
||||
|
||||
@@ -38,14 +38,15 @@ export abstract class UmbDetailRepositoryBase<
|
||||
|
||||
this.detailDataSource = new detailSource(host) as UmbDetailDataSourceType;
|
||||
|
||||
// TODO: ideally no preventTimeouts here.. [NL]
|
||||
this.#init = Promise.all([
|
||||
this.consumeContext(detailStoreContextAlias, (instance) => {
|
||||
this.#detailStore = instance;
|
||||
}).asPromise(),
|
||||
}).asPromise({ preventTimeout: true }),
|
||||
|
||||
this.consumeContext(UMB_NOTIFICATION_CONTEXT, (instance) => {
|
||||
this.#notificationContext = instance;
|
||||
}).asPromise(),
|
||||
}).asPromise({ preventTimeout: true }),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ export class UmbItemRepositoryBase<ItemType extends { unique: string }>
|
||||
|
||||
this._init = this.consumeContext(itemStoreContextAlias, (instance) => {
|
||||
this._itemStore = instance as UmbItemStore<ItemType>;
|
||||
}).asPromise();
|
||||
}).asPromise({ preventTimeout: true });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<MetaEntityActionRenameServerFileKind> {
|
||||
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<MetaEntityActionR
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const res = await modalContext.onSubmit();
|
||||
|
||||
const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
|
||||
const event = new UmbRequestReloadStructureForEntityEvent({
|
||||
unique: this.args.unique,
|
||||
entityType: this.args.entityType,
|
||||
});
|
||||
|
||||
actionEventContext.dispatchEvent(event);
|
||||
|
||||
const event2 = new UmbServerFileRenamedEntityEvent({
|
||||
unique: this.args.unique,
|
||||
entityType: this.args.entityType,
|
||||
newName: res.name,
|
||||
newUnique: res.unique,
|
||||
});
|
||||
|
||||
actionEventContext.dispatchEvent(event2);
|
||||
} catch (error) {
|
||||
// TODO: Handle error
|
||||
console.log(error);
|
||||
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,
|
||||
});
|
||||
|
||||
actionEventContext.dispatchEvent(event);
|
||||
|
||||
const event2 = new UmbServerFileRenamedEntityEvent({
|
||||
unique: this.args.unique,
|
||||
entityType: this.args.entityType,
|
||||
newName: res.name,
|
||||
newUnique: res.unique,
|
||||
});
|
||||
|
||||
actionEventContext.dispatchEvent(event2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ export class UmbTemporaryFileManager<
|
||||
maxFileSize *= 1024;
|
||||
if (item.file.size > 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}`,
|
||||
|
||||
@@ -62,7 +62,7 @@ export abstract class UmbTreeRepositoryBase<
|
||||
|
||||
this._init = this.consumeContext(treeStoreContextAlias, (instance) => {
|
||||
this._treeStore = instance;
|
||||
}).asPromise();
|
||||
}).asPromise({ preventTimeout: true });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,61 +52,59 @@ export class UmbDefaultTreeElement extends UmbLitElement {
|
||||
@state()
|
||||
private _totalPages = 1;
|
||||
|
||||
#treeContext?: UmbDefaultTreeContext<UmbTreeItemModel, UmbTreeRootModel>;
|
||||
#init: Promise<unknown>;
|
||||
@state()
|
||||
_treeContext?: UmbDefaultTreeContext<UmbTreeItemModel, UmbTreeRootModel>;
|
||||
|
||||
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<any> | Map<PropertyKey, unknown>,
|
||||
): Promise<void> {
|
||||
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() {
|
||||
|
||||
@@ -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<MetaEntityAc
|
||||
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_TO_MODAL, {
|
||||
const value = await umbOpenModal(this, UMB_DUPLICATE_TO_MODAL, {
|
||||
data: {
|
||||
unique: this.args.unique,
|
||||
entityType: this.args.entityType,
|
||||
@@ -20,32 +19,28 @@ export class UmbDuplicateToEntityAction extends UmbEntityActionBase<MetaEntityAc
|
||||
},
|
||||
});
|
||||
|
||||
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 = await createExtensionApiByAlias<UmbDuplicateToRepository>(
|
||||
this,
|
||||
this.args.meta.duplicateRepositoryAlias,
|
||||
);
|
||||
if (!duplicateRepository) throw new Error('Duplicate repository is not available');
|
||||
const duplicateRepository = await createExtensionApiByAlias<UmbDuplicateToRepository>(
|
||||
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,
|
||||
|
||||
@@ -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<MetaEntityActionM
|
||||
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 modalContext = modalManager.open(this, UMB_TREE_PICKER_MODAL, {
|
||||
const value = await umbOpenModal(this, UMB_TREE_PICKER_MODAL, {
|
||||
data: {
|
||||
treeAlias: this.args.meta.treeAlias,
|
||||
foldersOnly: this.args.meta.foldersOnly,
|
||||
@@ -20,7 +19,6 @@ export class UmbMoveToEntityAction extends UmbEntityActionBase<MetaEntityActionM
|
||||
},
|
||||
});
|
||||
|
||||
const value = await modalContext.onSubmit();
|
||||
const destinationUnique = value.selection[0];
|
||||
if (destinationUnique === undefined) throw new Error('Destination Unique is not available');
|
||||
|
||||
@@ -34,6 +32,7 @@ export class UmbMoveToEntityAction extends UmbEntityActionBase<MetaEntityActionM
|
||||
|
||||
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,
|
||||
|
||||
@@ -11,7 +11,7 @@ export class UmbReloadTreeItemChildrenEntityAction extends UmbEntityActionBase<M
|
||||
|
||||
override async execute() {
|
||||
const eventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
|
||||
|
||||
if (!eventContext) throw new Error('Event context is not available');
|
||||
eventContext.dispatchEvent(
|
||||
new UmbRequestReloadChildrenOfEntityEvent({
|
||||
unique: this.args.unique,
|
||||
|
||||
@@ -1,24 +1,22 @@
|
||||
import { UMB_SORT_CHILDREN_OF_MODAL } from './modal/index.js';
|
||||
import type { MetaEntityActionSortChildrenOfKind } from './types.js';
|
||||
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';
|
||||
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
|
||||
|
||||
export class UmbSortChildrenOfEntityAction extends UmbEntityActionBase<MetaEntityActionSortChildrenOfKind> {
|
||||
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({
|
||||
|
||||
@@ -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<MetaEntityActionFolderKind> {
|
||||
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<MetaEntityA
|
||||
},
|
||||
});
|
||||
|
||||
await modalContext.onSubmit();
|
||||
|
||||
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,
|
||||
|
||||
@@ -1,41 +1,20 @@
|
||||
import type { MetaEntityActionFolderKind, UmbFolderModel } from '../../types.js';
|
||||
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
|
||||
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
|
||||
import type { UmbEntityActionArgs } from '@umbraco-cms/backoffice/entity-action';
|
||||
import { UmbEntityActionBase, UmbRequestReloadStructureForEntityEvent } from '@umbraco-cms/backoffice/entity-action';
|
||||
import { UmbExtensionApiInitializer } from '@umbraco-cms/backoffice/extension-api';
|
||||
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry';
|
||||
import { umbConfirmModal } from '@umbraco-cms/backoffice/modal';
|
||||
import type { UmbDetailRepository } from '@umbraco-cms/backoffice/repository';
|
||||
|
||||
export class UmbDeleteFolderEntityAction extends UmbEntityActionBase<MetaEntityActionFolderKind> {
|
||||
// TODO: make base type for item and detail models
|
||||
#folderRepository?: UmbDetailRepository<UmbFolderModel>;
|
||||
#init: Promise<unknown>;
|
||||
|
||||
constructor(host: UmbControllerHost, args: UmbEntityActionArgs<MetaEntityActionFolderKind>) {
|
||||
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<UmbFolderModel>) : undefined;
|
||||
},
|
||||
).asPromise(),
|
||||
]);
|
||||
}
|
||||
|
||||
override async execute() {
|
||||
if (!this.args.unique) throw new Error('Unique is not available');
|
||||
await this.#init;
|
||||
const folderRepository = await createExtensionApiByAlias<UmbDetailRepository<UmbFolderModel>>(
|
||||
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<MetaEntityA
|
||||
confirmLabel: 'Delete',
|
||||
});
|
||||
|
||||
await this.#folderRepository?.delete(this.args.unique);
|
||||
await folderRepository.delete(this.args.unique);
|
||||
|
||||
const actionEventContext = await this.getContext(UMB_ACTION_EVENT_CONTEXT);
|
||||
if (!actionEventContext) throw new Error('Action event context is missing');
|
||||
const event = new UmbRequestReloadStructureForEntityEvent({
|
||||
unique: this.args.unique,
|
||||
entityType: this.args.entityType,
|
||||
|
||||
@@ -2,23 +2,23 @@ import { UMB_FOLDER_UPDATE_MODAL } from '../../modal/index.js';
|
||||
import type { MetaEntityActionFolderKind } from '../../types.js';
|
||||
import { UMB_ACTION_EVENT_CONTEXT } from '@umbraco-cms/backoffice/action';
|
||||
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';
|
||||
|
||||
export class UmbUpdateFolderEntityAction extends UmbEntityActionBase<MetaEntityActionFolderKind> {
|
||||
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,
|
||||
|
||||
@@ -44,7 +44,7 @@ export class UmbServerModelValidatorContext
|
||||
context.addValidator(this);
|
||||
|
||||
// Run translators?
|
||||
}).asPromise();
|
||||
}).asPromise({ preventTimeout: true });
|
||||
}
|
||||
|
||||
async askServerForValidation(data: unknown, requestPromise: Promise<UmbDataSourceResponse<string>>): Promise<void> {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -51,12 +51,10 @@ export class UmbDataTypeCreateOptionsModalElement extends UmbModalBaseElement<Um
|
||||
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);
|
||||
}
|
||||
|
||||
override render() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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}`;
|
||||
|
||||
|
||||
@@ -11,11 +11,9 @@ export class UmbDataTypeDetailRepository extends UmbDetailRepositoryBase<UmbData
|
||||
constructor(host: UmbControllerHost) {
|
||||
super(host, UmbDataTypeServerDataSource, UMB_DATA_TYPE_DETAIL_STORE_CONTEXT);
|
||||
|
||||
this.#init = Promise.all([
|
||||
this.consumeContext(UMB_DATA_TYPE_DETAIL_STORE_CONTEXT, (instance) => {
|
||||
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) {
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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<object> {
|
||||
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;
|
||||
|
||||
|
||||
@@ -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<object> {
|
||||
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 } });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<never> {
|
||||
constructor(host: UmbControllerHost, args: UmbEntityActionArgs<never>) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<never> {
|
||||
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<never
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await modalContext.onSubmit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ export class UmbDataTypeCreateOptionsModalElement extends UmbModalBaseElement<Um
|
||||
this._submitModal();
|
||||
return;
|
||||
} catch {
|
||||
//console.error(error);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -11,6 +11,9 @@ export class UmbDuplicateDocumentTypeRepository 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: `Duplicated` } };
|
||||
notificationContext.peek('positive', notification);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ export class UmbExportDocumentTypeRepository 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);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { UMB_DOCUMENT_TYPE_IMPORT_MODAL } from './modal/document-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 UmbImportDocumentTypeEntityAction extends UmbEntityActionBase<object> {
|
||||
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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user