Skip to content

Commit cee441d

Browse files
Fix #19676 (#19886)
* observation as promise util * all success observer * next step todos * await everything loaded * contentTypeLoaded observable * tidying up * Apply suggestion from @Copilot Co-authored-by: Copilot <[email protected]> * remove comment --------- Co-authored-by: Copilot <[email protected]>
1 parent aa269e3 commit cee441d

File tree

5 files changed

+73
-5
lines changed

5 files changed

+73
-5
lines changed

src/Umbraco.Web.UI.Client/src/libs/observable-api/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export * from './default-memoization.function.js';
77
export * from './filter-frozen-array.function.js';
88
export * from './json-string-comparison.function.js';
99
export * from './merge-observables.function.js';
10+
export * from './observation-as-promise.function.js';
1011
export * from './observe-multiple.function.js';
1112
export * from './partial-update-frozen-array.function.js';
1213
export * from './push-at-to-unique-array.function.js';
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
2+
3+
/**
4+
* @function observationAsPromise
5+
* @param {Observable<unknown>} observable - an Array of Observables to use for this combined observation.
6+
* @param {Promise<condition>} condition - a method which should return true or false, if rejected or returning undefined the observation will result in a rejected Promise.
7+
* @description - Observes an Observable and returns a Promise that resolves when the condition returns true. If the condition returns undefined or rejects, the Promise will reject with the current value.
8+
* @returns {Promise<unknown>} - Returns a Promise which resolves when the condition returns true or rejects when the condition returns undefined or is rejecting it self.
9+
*/
10+
export function observationAsPromise<T>(
11+
observable: Observable<T>,
12+
condition: (value: T) => Promise<boolean | undefined>,
13+
): Promise<T> {
14+
return new Promise<T>((resolve, reject) => {
15+
let initialCallback = true;
16+
let wantedToClose = false;
17+
const subscription = observable.subscribe(async (value) => {
18+
const shouldClose = await condition(value).catch(() => {
19+
if (initialCallback) {
20+
wantedToClose = true;
21+
} else {
22+
subscription.unsubscribe();
23+
}
24+
reject(value);
25+
});
26+
if (shouldClose === true) {
27+
if (initialCallback) {
28+
wantedToClose = true;
29+
} else {
30+
subscription.unsubscribe();
31+
}
32+
resolve(value);
33+
}
34+
});
35+
initialCallback = false;
36+
if (wantedToClose) {
37+
subscription.unsubscribe();
38+
}
39+
});
40+
}

src/Umbraco.Web.UI.Client/src/packages/content/content-type/structure/content-type-structure-manager.class.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@ import {
1919
appendToFrozenArray,
2020
filterFrozenArray,
2121
createObservablePart,
22+
observationAsPromise,
23+
mergeObservables,
2224
} from '@umbraco-cms/backoffice/observable-api';
2325
import { incrementString } from '@umbraco-cms/backoffice/utils';
2426
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
2527
import { UmbExtensionApiInitializer } from '@umbraco-cms/backoffice/extension-api';
2628
import { umbExtensionsRegistry, type ManifestRepository } from '@umbraco-cms/backoffice/extension-registry';
2729
import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
30+
import { UmbError } from '@umbraco-cms/backoffice/resources';
2831

2932
type UmbPropertyTypeUnique = UmbPropertyTypeModel['unique'];
3033

@@ -112,6 +115,13 @@ export class UmbContentTypeStructureManager<
112115
readonly contentTypeUniques = this.#contentTypes.asObservablePart((x) => x.map((y) => y.unique));
113116
readonly contentTypeAliases = this.#contentTypes.asObservablePart((x) => x.map((y) => y.alias));
114117

118+
readonly contentTypeLoaded = mergeObservables(
119+
[this.contentTypeCompositions, this.contentTypeUniques],
120+
([comps, uniques]) => {
121+
return comps.every((x) => uniques.includes(x.contentType.unique));
122+
},
123+
);
124+
115125
readonly variesByCulture = createObservablePart(this.ownerContentType, (x) => x?.variesByCulture);
116126
readonly variesBySegment = createObservablePart(this.ownerContentType, (x) => x?.variesBySegment);
117127

@@ -191,9 +201,26 @@ export class UmbContentTypeStructureManager<
191201
);
192202
}
193203
this.#repoManager!.setUniques([unique]);
194-
const result = await this.observe(this.#repoManager!.entryByUnique(unique)).asPromise();
204+
const observable = this.#repoManager!.entryByUnique(unique);
205+
const result = await this.observe(observable).asPromise();
206+
if (!result) {
207+
this.#initRejection?.(`Content Type structure manager could not load: ${unique}`);
208+
return {
209+
error: new UmbError(`Content Type structure manager could not load: ${unique}`),
210+
asObservable: () => observable,
211+
};
212+
}
213+
214+
// Awaits that everything is loaded:
215+
await observationAsPromise(this.contentTypeLoaded, async (loaded) => {
216+
return loaded === true;
217+
}).catch(() => {
218+
const msg = `Content Type structure manager could not load: ${unique}. Not all Content Types loaded successfully.`;
219+
this.#initRejection?.(msg);
220+
return Promise.reject(new UmbError(msg));
221+
});
222+
195223
this.#initResolver?.(result);
196-
await this.#init;
197224
return { data: result, asObservable: () => this.ownerContentType };
198225
}
199226

src/Umbraco.Web.UI.Client/src/packages/core/repository/repository-details.manager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,14 @@ export class UmbRepositoryDetailsManager<DetailType extends { unique: string }>
151151
*/
152152
addEntry(data: DetailType): void {
153153
const unique = data.unique;
154+
this.#entries.appendOne(data);
155+
this.#uniques.appendOne(unique);
154156
this.#statuses.appendOne({
155157
state: {
156158
type: 'success',
157159
},
158160
unique,
159161
});
160-
this.#entries.appendOne(data);
161-
this.#uniques.appendOne(unique);
162162
// Notice in this case we do not have a observable from the repo, but it should maybe be fine that we just listen for ACTION EVENTS.
163163
}
164164

src/Umbraco.Web.UI.Client/src/packages/documents/documents/workspace/document-workspace.context.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ export class UmbDocumentWorkspaceContext
415415
this.readOnlyGuard?.addRule({
416416
unique: identifier,
417417
message,
418-
/* This guard is a bit backwards. The rule is permitted to be read-only.
418+
/* This guard is a bit backwards. The rule is permitted to be read-only.
419419
If the user does not have permission, we set it to true = permitted to be read-only. */
420420
permitted: true,
421421
});

0 commit comments

Comments
 (0)