From 60a2022f663ca5dbd49f821300ddbb935bd3ef85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 24 Jan 2023 13:01:40 +0100 Subject: [PATCH 01/13] new subjects --- .../libs/observable-api/basic-state.ts | 19 +++++++++++++++++++ .../libs/observable-api/number-state.ts | 11 +++++++++++ .../libs/observable-api/string-state.ts | 11 +++++++++++ 3 files changed, 41 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/libs/observable-api/basic-state.ts create mode 100644 src/Umbraco.Web.UI.Client/libs/observable-api/number-state.ts create mode 100644 src/Umbraco.Web.UI.Client/libs/observable-api/string-state.ts diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/basic-state.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/basic-state.ts new file mode 100644 index 0000000000..da449ed38c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/basic-state.ts @@ -0,0 +1,19 @@ +import { BehaviorSubject } from "rxjs"; + +/** + * @export + * @class BasicState + * @extends {BehaviorSubject} + * @description - A RxJS BehaviorSubject this Subject ensures the data is unique, not updating any Observes unless there is an actual change of the value. + */ +export class BasicState extends BehaviorSubject { + constructor(initialData: T) { + super(initialData); + } + + next(newData: T): void { + if(newData !== this.getValue()) { + super.next(newData); + } + } +} diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/number-state.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/number-state.ts new file mode 100644 index 0000000000..8a2b775351 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/number-state.ts @@ -0,0 +1,11 @@ +import { BasicState } from "./basic-state"; + +/** + * @export + * @class NumberState + * @extends {BehaviorSubject} + * @description - A RxJS BehaviorSubject this Subject ensures the data is unique, not updating any Observes unless there is an actual change of the value. + */ +export class NumberState extends BasicState { + +} diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/string-state.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/string-state.ts new file mode 100644 index 0000000000..4ec129fe84 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/string-state.ts @@ -0,0 +1,11 @@ +import { BasicState } from "./basic-state"; + +/** + * @export + * @class StringState + * @extends {BehaviorSubject} + * @description - A RxJS BehaviorSubject this Subject ensures the data is unique, not updating any Observes unless there is an actual change of the value. + */ +export class StringState extends BasicState { + +} From beb4b629e56d881162bd3f8571a2265c7f95b360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 24 Jan 2023 13:01:58 +0100 Subject: [PATCH 02/13] clean up our way of using subjects for section --- .../section-views/section-views.element.ts | 14 +++++----- .../components/section/section.context.ts | 26 ++++++++++++------- .../tree/action/tree-item-action.element.ts | 10 ++++--- .../components/tree/tree-item.element.ts | 3 ++- 4 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-views/section-views.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-views/section-views.element.ts index c7d498e252..b9946e3bde 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-views/section-views.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-views/section-views.element.ts @@ -35,11 +35,11 @@ export class UmbSectionViewsElement extends UmbLitElement { private _routerFolder = ''; @state() - private _activeView?: ManifestSectionView; + private _activeViewPathname?: ManifestSectionView; private _sectionContext?: UmbSectionContext; private _viewsSubscription?: Subscription; - private _activeViewSubscription?: Subscription; + private _activeViewPathnameSubscription?: Subscription; constructor() { super(); @@ -86,17 +86,17 @@ export class UmbSectionViewsElement extends UmbLitElement { } private _observeActiveView() { - this._activeViewSubscription?.unsubscribe(); + this._activeViewPathnameSubscription?.unsubscribe(); - this._activeViewSubscription = this._sectionContext?.activeView.subscribe((view) => { - this._activeView = view; + this._activeViewPathnameSubscription = this._sectionContext?.activeViewPathname.subscribe((pathname) => { + this._activeViewPathname = pathName; }); } disconnectedCallback(): void { super.disconnectedCallback(); this._viewsSubscription?.unsubscribe(); - this._activeViewSubscription?.unsubscribe(); + this._activeViewPathnameSubscription?.unsubscribe(); } render() { @@ -113,7 +113,7 @@ export class UmbSectionViewsElement extends UmbLitElement { + ?active="${this._activeViewPathname.includes(view.meta.pathname)}"> ${view.meta.label || view.name} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts index e5b4af939d..28d1439994 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts @@ -1,23 +1,28 @@ import { BehaviorSubject } from 'rxjs'; -import type { Entity, ManifestSection, ManifestSectionView, ManifestTree } from '@umbraco-cms/models'; +import type { Entity, ManifestSection, ManifestSectionView } from '@umbraco-cms/models'; import { UniqueObjectBehaviorSubject } from '@umbraco-cms/observable-api'; import { UmbContextToken } from '@umbraco-cms/context-api'; +export type ActiveTreeItemType = Entity | undefined; + export class UmbSectionContext { + #manifest; public readonly manifest; - // TODO: what is the best context to put this in? + /* + This was not used anywhere private _activeTree = new BehaviorSubject(undefined); public readonly activeTree = this._activeTree.asObservable(); + */ // TODO: what is the best context to put this in? - private _activeTreeItem = new UniqueObjectBehaviorSubject(undefined); - public readonly activeTreeItem = this._activeTreeItem.asObservable(); + #activeTreeItem = new UniqueObjectBehaviorSubject(undefined); + public readonly activeTreeItem = this.#activeTreeItem.asObservable(); // TODO: what is the best context to put this in? - private _activeView = new BehaviorSubject(undefined); - public readonly activeView = this._activeView.asObservable(); + #activeViewPathname = new BehaviorSubject(undefined); + public readonly activeViewPathname = this.#activeViewPathname.asObservable(); constructor(sectionManifest: ManifestSection) { this.#manifest = new BehaviorSubject(sectionManifest); @@ -32,16 +37,19 @@ export class UmbSectionContext { return this.#manifest.getValue(); } + /* + This was not used anywhere public setActiveTree(tree: ManifestTree) { this._activeTree.next(tree); } + */ - public setActiveTreeItem(item: Entity) { - this._activeTreeItem.next(item); + public setActiveTreeItem(item: ActiveTreeItemType) { + this.#activeTreeItem.next(item); } public setActiveView(view: ManifestSectionView) { - this._activeView.next(view); + this.#activeViewPathname.next(view.meta.pathname); } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/action/tree-item-action.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/action/tree-item-action.element.ts index a738d06b5f..0e31b90975 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/action/tree-item-action.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/action/tree-item-action.element.ts @@ -1,5 +1,5 @@ import { customElement, property, state } from 'lit/decorators.js'; -import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../../section/section.context'; +import { ActiveTreeItemType, UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../../section/section.context'; import { UmbTreeContextMenuPageService, UMB_TREE_CONTEXT_MENU_PAGE_SERVICE_CONTEXT_TOKEN, @@ -8,7 +8,7 @@ import { UmbTreeContextMenuService, UMB_TREE_CONTEXT_MENU_SERVICE_CONTEXT_TOKEN, } from '../context-menu/tree-context-menu.service'; -import type { Entity, ManifestTreeItemAction, ManifestTree } from '@umbraco-cms/models'; +import type { ManifestTreeItemAction } from '@umbraco-cms/models'; import { UmbLitElement } from '@umbraco-cms/element'; export type ActionPageEntity = { @@ -24,8 +24,8 @@ export default class UmbTreeItemActionElement extends UmbLitElement { @state() protected _entity: ActionPageEntity = { name: '', key: '' }; - protected _activeTree?: ManifestTree; - protected _activeTreeItem?: Entity; + //protected _activeTree?: ManifestTree; + protected _activeTreeItem?: ActiveTreeItemType; protected _sectionContext?: UmbSectionContext; protected _treeContextMenuService?: UmbTreeContextMenuService; @@ -61,9 +61,11 @@ export default class UmbTreeItemActionElement extends UmbLitElement { private _observeActiveTree() { if (!this._sectionContext) return; + /* this.observe(this._sectionContext.activeTree, (tree) => { this._activeTree = tree; }); + */ } private _observeActiveTreeItem() { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item.element.ts index 86ba45d099..4ae7b87aff 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item.element.ts @@ -184,7 +184,8 @@ export class UmbTreeItem extends UmbLitElement { private _openActions() { if (!this._treeContext || !this._sectionContext) return; - this._sectionContext?.setActiveTree(this._treeContext?.tree); + // This is out-commented as it was not used. only kept if someone need this later: + //this._sectionContext?.setActiveTree(this._treeContext?.tree); this._sectionContext?.setActiveTreeItem({ key: this.key, From 5e58d1fc61507d7fa648a26ebfb4271604a7d20b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 24 Jan 2023 13:37:11 +0100 Subject: [PATCH 03/13] section context subject implementation --- .../create-observable-part.method.ts | 2 +- .../observable-api/observer.controller.ts | 2 +- .../section-dashboards.element.ts | 15 ++-- .../section-sidebar-menu.element.ts | 4 +- .../section-sidebar.element.ts | 9 +-- .../section-views/section-views.element.ts | 70 ++++++++----------- .../components/section/section.context.ts | 31 ++++---- .../components/section/section.element.ts | 6 +- .../components/tree/tree-item.element.ts | 4 +- 9 files changed, 68 insertions(+), 75 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/create-observable-part.method.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/create-observable-part.method.ts index 4d10b39e78..588ef9e96e 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/create-observable-part.method.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/create-observable-part.method.ts @@ -12,7 +12,7 @@ import { MappingFunction, MemoizationFunction, defaultMemoization } from "./uniq * public readonly myPart = CreateObservablePart(this._data, (data) => data.myPart); */ -export function createObservablePart( +export function createObservablePart( source$: Observable, mappingFunction: MappingFunction, memoizationFunction?: MemoizationFunction diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/observer.controller.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/observer.controller.ts index 6598b7a358..38f51421cc 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/observer.controller.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/observer.controller.ts @@ -2,7 +2,7 @@ import { Observable } from 'rxjs'; import { UmbObserver } from './observer'; import { UmbControllerInterface, UmbControllerHostInterface } from '@umbraco-cms/controller'; -export class UmbObserverController extends UmbObserver implements UmbControllerInterface { +export class UmbObserverController extends UmbObserver implements UmbControllerInterface { _alias?: string; public get unique() { return this._alias; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-dashboards/section-dashboards.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-dashboards/section-dashboards.element.ts index ba493baa14..5ed63e6e8d 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-dashboards/section-dashboards.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-dashboards/section-dashboards.element.ts @@ -8,7 +8,6 @@ import { createExtensionElement } from '@umbraco-cms/extensions-api'; import type { ManifestDashboard, ManifestDashboardCollection, - ManifestSection, ManifestWithMeta, } from '@umbraco-cms/models'; import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry'; @@ -58,7 +57,7 @@ export class UmbSectionDashboardsElement extends UmbLitElement { @state() private _currentSectionPathname = ''; - private _currentSectionAlias = ''; + private _currentSectionAlias?: string; private _sectionContext?: UmbSectionContext; constructor() { @@ -73,12 +72,12 @@ export class UmbSectionDashboardsElement extends UmbLitElement { private _observeSectionContext() { if (!this._sectionContext) return; - this.observe(this._sectionContext.manifest.pipe(first()), (section) => { - if (section) { - this._currentSectionAlias = section.alias; - this._currentSectionPathname = section.meta.pathname; - this._observeDashboards(); - } + this.observe(this._sectionContext.alias.pipe(first()), (alias) => { + this._currentSectionAlias = alias; + this._observeDashboards(); + }); + this.observe(this._sectionContext.pathname.pipe(first()), (pathname) => { + this._currentSectionPathname = pathname || ''; }); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar-menu/section-sidebar-menu.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar-menu/section-sidebar-menu.element.ts index f46bc00785..f52c749747 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar-menu/section-sidebar-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar-menu/section-sidebar-menu.element.ts @@ -28,8 +28,8 @@ export class UmbSectionSidebarMenuElement extends UmbLitElement { private _observeCurrentSection() { if (!this._sectionContext) return; - this.observe(this._sectionContext?.manifest, (section) => { - this._currentSectionAlias = section.alias; + this.observe(this._sectionContext?.alias, (alias) => { + this._currentSectionAlias = alias; }); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar/section-sidebar.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar/section-sidebar.element.ts index 7964674b21..ddd1d24f6b 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar/section-sidebar.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar/section-sidebar.element.ts @@ -2,7 +2,6 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../section.context'; -import type { ManifestSection } from '@umbraco-cms/models'; import '../../tree/context-menu/tree-context-menu.service'; import { UmbLitElement } from '@umbraco-cms/element'; @@ -48,9 +47,11 @@ export class UmbSectionSidebarElement extends UmbLitElement { private _observeSectionContext() { if (!this._sectionContext) return; - this.observe(this._sectionContext.manifest, (section) => { - this._sectionLabel = section.meta.label || section.name; - this._sectionPathname = section.meta.pathname; + this.observe(this._sectionContext.pathname, (pathname) => { + this._sectionPathname = pathname || ''; + }); + this.observe(this._sectionContext.label, (label) => { + this._sectionLabel = label || ''; }); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-views/section-views.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-views/section-views.element.ts index b9946e3bde..cca976d6a2 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-views/section-views.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-views/section-views.element.ts @@ -1,11 +1,12 @@ import { UUITextStyles } from '@umbraco-ui/uui-css'; import { css, html, nothing } from 'lit'; import { customElement, state } from 'lit/decorators.js'; -import { EMPTY, map, of, Subscription, switchMap } from 'rxjs'; +import { map, of } from 'rxjs'; import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../section.context'; import type { ManifestSectionView } from '@umbraco-cms/models'; import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry'; import { UmbLitElement } from '@umbraco-cms/element'; +import { UmbObserverController } from '@umbraco-cms/observable-api'; @customElement('umb-section-views') export class UmbSectionViewsElement extends UmbLitElement { @@ -35,11 +36,10 @@ export class UmbSectionViewsElement extends UmbLitElement { private _routerFolder = ''; @state() - private _activeViewPathname?: ManifestSectionView; + private _activeViewPathname?: string; private _sectionContext?: UmbSectionContext; - private _viewsSubscription?: Subscription; - private _activeViewPathnameSubscription?: Subscription; + private _extensionsObserver?: UmbObserverController; constructor() { super(); @@ -60,43 +60,35 @@ export class UmbSectionViewsElement extends UmbLitElement { private _observeViews() { if (!this._sectionContext) return; - this._viewsSubscription?.unsubscribe(); - - this._viewsSubscription = this._sectionContext?.manifest - .pipe( - switchMap((section) => { - if (!section) return EMPTY; - - return ( - umbExtensionsRegistry - ?.extensionsOfType('sectionView') - .pipe( - map((views) => - views - .filter((view) => view.meta.sections.includes(section.alias)) - .sort((a, b) => b.meta.weight - a.meta.weight) - ) - ) ?? of([]) - ); - }) - ) - .subscribe((views) => { - this._views = views; - }); + this.observe(this._sectionContext.alias, (sectionAlias) => { + this._observeExtensions(sectionAlias); + }, 'viewsObserver') + } + private _observeExtensions(sectionAlias?: string) { + this._extensionsObserver?.destroy(); + if(sectionAlias) { + this._extensionsObserver = this.observe( + umbExtensionsRegistry?.extensionsOfType('sectionView').pipe( + map((views) => + views + .filter((view) => view.meta.sections.includes(sectionAlias)) + .sort((a, b) => b.meta.weight - a.meta.weight) + ) + ) ?? of([]) + , + (views) => { + this._views = views; + } + ); + } } private _observeActiveView() { - this._activeViewPathnameSubscription?.unsubscribe(); - - this._activeViewPathnameSubscription = this._sectionContext?.activeViewPathname.subscribe((pathname) => { - this._activeViewPathname = pathName; - }); - } - - disconnectedCallback(): void { - super.disconnectedCallback(); - this._viewsSubscription?.unsubscribe(); - this._activeViewPathnameSubscription?.unsubscribe(); + if(this._sectionContext) { + this.observe(this._sectionContext?.activeViewPathname, (pathname) => { + this._activeViewPathname = pathname; + }, 'activeViewPathnameObserver'); + } } render() { @@ -113,7 +105,7 @@ export class UmbSectionViewsElement extends UmbLitElement { + ?active="${this._activeViewPathname?.includes(view.meta.pathname)}"> ${view.meta.label || view.name} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts index 28d1439994..9cb0e9594d 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts @@ -7,8 +7,12 @@ export type ActiveTreeItemType = Entity | undefined; export class UmbSectionContext { - #manifest; - public readonly manifest; + #manifestAlias = new BehaviorSubject(undefined); + #manifestPathname = new BehaviorSubject(undefined); + #manifestLabel = new BehaviorSubject(undefined); + public readonly alias = this.#manifestAlias.asObservable(); + public readonly pathname = this.#manifestPathname.asObservable(); + public readonly label = this.#manifestLabel.asObservable(); /* This was not used anywhere @@ -17,24 +21,21 @@ export class UmbSectionContext { */ // TODO: what is the best context to put this in? - #activeTreeItem = new UniqueObjectBehaviorSubject(undefined); + #activeTreeItem = new UniqueObjectBehaviorSubject(undefined); public readonly activeTreeItem = this.#activeTreeItem.asObservable(); // TODO: what is the best context to put this in? #activeViewPathname = new BehaviorSubject(undefined); public readonly activeViewPathname = this.#activeViewPathname.asObservable(); - constructor(sectionManifest: ManifestSection) { - this.#manifest = new BehaviorSubject(sectionManifest); - this.manifest = this.#manifest.asObservable(); + constructor(manifest: ManifestSection) { + this.setManifest(manifest); } - public setManifest(data: ManifestSection) { - this.#manifest.next({ ...data }); - } - - public getData() { - return this.#manifest.getValue(); + public setManifest(manifest?: ManifestSection) { + this.#manifestAlias.next(manifest?.alias); + this.#manifestPathname.next(manifest?.meta?.pathname); + this.#manifestLabel.next(manifest ? (manifest.meta?.label || manifest.name) : undefined); } /* @@ -44,12 +45,12 @@ export class UmbSectionContext { } */ - public setActiveTreeItem(item: ActiveTreeItemType) { + public setActiveTreeItem(item?: ActiveTreeItemType) { this.#activeTreeItem.next(item); } - public setActiveView(view: ManifestSectionView) { - this.#activeViewPathname.next(view.meta.pathname); + public setActiveView(view?: ManifestSectionView) { + this.#activeViewPathname.next(view?.meta.pathname); } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts index 4b04b25ca2..8d30e70f66 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts @@ -60,8 +60,8 @@ export class UmbSectionElement extends UmbLitElement { private _observeMenuItems() { if (!this._sectionContext) return; - this.observe(this._sectionContext?.manifest, (section) => { - this._observeSidebarMenuItem(section?.alias); + this.observe(this._sectionContext?.alias, (alias) => { + this._observeSidebarMenuItem(alias); }); this.observe(umbExtensionsRegistry.extensionsOfType('workspace'), (workspaceExtensions) => { @@ -142,7 +142,7 @@ export class UmbSectionElement extends UmbLitElement { if (!this._sectionContext) return; this.observe( - this._sectionContext.manifest.pipe( + this._sectionContext.alias.pipe( switchMap((section) => { if (!section) return EMPTY; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item.element.ts index 4ae7b87aff..3b638b09bb 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree-item.element.ts @@ -116,8 +116,8 @@ export class UmbTreeItem extends UmbLitElement { private _observeSection() { if (!this._sectionContext) return; - this.observe(this._sectionContext?.manifest, (section) => { - this._href = this._constructPath(section?.meta.pathname || '', this.entityType, this.key); + this.observe(this._sectionContext?.pathname, (pathname) => { + this._href = this._constructPath(pathname || '', this.entityType, this.key); }); } From 905c1b3632c50d6969f48336bb0bb494905fe541 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 24 Jan 2023 13:40:25 +0100 Subject: [PATCH 04/13] rename specific subjects to state --- ...ior-subject.test.ts => array-state.test.ts} | 8 ++++---- ...rray-behavior-subject.ts => array-state.ts} | 18 +++++++++--------- .../create-observable-part.method.ts | 2 +- ...vior-subject.test.ts => deep-state.test.ts} | 8 ++++---- ...nique-behavior-subject.ts => deep-state.ts} | 4 ++-- .../libs/observable-api/index.ts | 6 +++--- ...or-subject.test.ts => object-state.test.ts} | 8 ++++---- ...ect-behavior-subject.ts => object-state.ts} | 14 +++++++------- .../document-blueprint.detail.store.ts | 4 ++-- .../document-blueprint.tree.store.ts | 4 ++-- .../document-type.detail.store.ts | 4 ++-- .../document-types/document-type.tree.store.ts | 4 ++-- .../documents/document.detail.store.ts | 4 ++-- .../documents/documents/document.tree.store.ts | 4 ++-- .../media-types/media-type.detail.store.ts | 4 ++-- .../media/media-types/media-type.tree.store.ts | 4 ++-- .../media/media/media.detail.store.ts | 4 ++-- .../backoffice/media/media/media.tree.store.ts | 4 ++-- .../member-group.details.store.ts | 4 ++-- .../member-types/member-type.detail.store.ts | 4 ++-- .../member-types/member-type.tree.store.ts | 4 ++-- .../data-types/data-type.detail.store.ts | 4 ++-- .../data-types/data-type.tree.store.ts | 4 ++-- .../shared/collection/collection.context.ts | 6 +++--- .../components/section/section.context.ts | 4 ++-- .../tree-context-menu-page.service.ts | 4 ++-- .../shared/components/tree/tree.context.ts | 6 +++--- .../workspace-property.context.ts | 4 ++-- .../workspace-content.context.ts | 4 ++-- .../property-action-menu.context.ts | 4 ++-- .../dictionary/dictionary.detail.store.ts | 4 ++-- .../dictionary/dictionary.tree.store.ts | 4 ++-- .../current-user/current-user-history.store.ts | 4 ++-- .../users/user-groups/user-group.store.ts | 4 ++-- .../views/users/section-view-users.element.ts | 8 ++++---- .../src/backoffice/users/users/user.store.ts | 4 ++-- 36 files changed, 94 insertions(+), 94 deletions(-) rename src/Umbraco.Web.UI.Client/libs/observable-api/{unique-array-behavior-subject.test.ts => array-state.test.ts} (92%) rename src/Umbraco.Web.UI.Client/libs/observable-api/{unique-array-behavior-subject.ts => array-state.ts} (76%) rename src/Umbraco.Web.UI.Client/libs/observable-api/{unique-behavior-subject.test.ts => deep-state.test.ts} (87%) rename src/Umbraco.Web.UI.Client/libs/observable-api/{unique-behavior-subject.ts => deep-state.ts} (94%) rename src/Umbraco.Web.UI.Client/libs/observable-api/{unique-object-behavior-subject.test.ts => object-state.test.ts} (82%) rename src/Umbraco.Web.UI.Client/libs/observable-api/{unique-object-behavior-subject.ts => object-state.ts} (52%) diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/unique-array-behavior-subject.test.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/array-state.test.ts similarity index 92% rename from src/Umbraco.Web.UI.Client/libs/observable-api/unique-array-behavior-subject.test.ts rename to src/Umbraco.Web.UI.Client/libs/observable-api/array-state.test.ts index 8ab7688698..2c6f7b747b 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/unique-array-behavior-subject.test.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/array-state.test.ts @@ -1,13 +1,13 @@ import { expect } from '@open-wc/testing'; -import { UniqueArrayBehaviorSubject } from './unique-array-behavior-subject'; +import { ArrayState } from './array-state'; import { createObservablePart } from '@umbraco-cms/observable-api'; -describe('UniqueArrayBehaviorSubject', () => { +describe('ArrayState', () => { type ObjectType = {key: string, another: string}; type ArrayType = ObjectType[]; - let subject: UniqueArrayBehaviorSubject; + let subject: ArrayState; let initialData: ArrayType; beforeEach(() => { @@ -16,7 +16,7 @@ describe('UniqueArrayBehaviorSubject', () => { {key: '2', another: 'myValue2'}, {key: '3', another: 'myValue3'} ]; - subject = new UniqueArrayBehaviorSubject(initialData, x => x.key); + subject = new ArrayState(initialData, x => x.key); }); diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/unique-array-behavior-subject.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/array-state.ts similarity index 76% rename from src/Umbraco.Web.UI.Client/libs/observable-api/unique-array-behavior-subject.ts rename to src/Umbraco.Web.UI.Client/libs/observable-api/array-state.ts index 94267f6379..00f1365d52 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/unique-array-behavior-subject.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/array-state.ts @@ -1,17 +1,17 @@ -import { UniqueBehaviorSubject } from "./unique-behavior-subject"; +import { DeepState } from "./deep-state"; import { appendToFrozenArray } from "./append-to-frozen-array.method"; /** * @export - * @class UniqueObjectBehaviorSubject - * @extends {UniqueBehaviorSubject} - * @description - A RxJS UniqueObjectBehaviorSubject which deepFreezes the object-data to ensure its not manipulated from any implementations. + * @class ArrayState + * @extends {DeepState} + * @description - A RxJS BehaviorSubject which deepFreezes the object-data to ensure its not manipulated from any implementations. * Additionally the Subject ensures the data is unique, not updating any Observes unless there is an actual change of the content. * - * The UniqueObjectBehaviorSubject provides methods to append data when the data is an Object. + * The ArrayState provides methods to append data when the data is an Object. */ -export class UniqueArrayBehaviorSubject extends UniqueBehaviorSubject { +export class ArrayState extends DeepState { constructor(initialData: T[], private _getUnique?: (entry: T) => unknown) { @@ -27,7 +27,7 @@ export class UniqueArrayBehaviorSubject extends UniqueBehaviorSubject { * { key: 1, value: 'foo'}, * { key: 2, value: 'bar'} * ]; - * const mySubject = new UniqueArrayBehaviorSubject(data, (x) => x.key); + * const mySubject = new ArrayState(data, (x) => x.key); * mySubject.remove([1]); */ remove(uniques: unknown[]) { @@ -54,7 +54,7 @@ export class UniqueArrayBehaviorSubject extends UniqueBehaviorSubject { * { key: 1, value: 'foo'}, * { key: 2, value: 'bar'} * ]; - * const mySubject = new UniqueArrayBehaviorSubject(data); + * const mySubject = new ArrayState(data); * mySubject.append({ key: 1, value: 'replaced-foo'}); */ appendOne(entry: T) { @@ -70,7 +70,7 @@ export class UniqueArrayBehaviorSubject extends UniqueBehaviorSubject { * { key: 1, value: 'foo'}, * { key: 2, value: 'bar'} * ]; - * const mySubject = new UniqueArrayBehaviorSubject(data); + * const mySubject = new ArrayState(data); * mySubject.append([ * { key: 1, value: 'replaced-foo'}, * { key: 3, value: 'another-bla'} diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/create-observable-part.method.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/create-observable-part.method.ts index 588ef9e96e..73ceffe98c 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/create-observable-part.method.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/create-observable-part.method.ts @@ -1,5 +1,5 @@ import { distinctUntilChanged, map, Observable, shareReplay } from "rxjs"; -import { MappingFunction, MemoizationFunction, defaultMemoization } from "./unique-behavior-subject"; +import { MappingFunction, MemoizationFunction, defaultMemoization } from "./deep-state"; /** * @export diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/unique-behavior-subject.test.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.test.ts similarity index 87% rename from src/Umbraco.Web.UI.Client/libs/observable-api/unique-behavior-subject.test.ts rename to src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.test.ts index 672e990190..9f05db1c0e 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/unique-behavior-subject.test.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.test.ts @@ -1,17 +1,17 @@ import { expect } from '@open-wc/testing'; -import { UniqueBehaviorSubject } from './unique-behavior-subject'; +import { DeepState } from './deep-state'; import { createObservablePart } from '@umbraco-cms/observable-api'; -describe('UniqueBehaviorSubject', () => { +describe('DeepState', () => { type ObjectType = {key: string, another: string}; - let subject: UniqueBehaviorSubject; + let subject: DeepState; let initialData: ObjectType; beforeEach(() => { initialData = {key: 'some', another: 'myValue'}; - subject = new UniqueBehaviorSubject(initialData); + subject = new DeepState(initialData); }); diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/unique-behavior-subject.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts similarity index 94% rename from src/Umbraco.Web.UI.Client/libs/observable-api/unique-behavior-subject.ts rename to src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts index 20af12b3b1..fbf5dbea81 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/unique-behavior-subject.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts @@ -40,12 +40,12 @@ export function defaultMemoization(previousValue: any, currentValue: any): boole /** * @export - * @class UniqueBehaviorSubject + * @class DeepState * @extends {BehaviorSubject} * @description - A RxJS BehaviorSubject which deepFreezes the data to ensure its not manipulated from any implementations. * Additionally the Subject ensures the data is unique, not updating any Observes unless there is an actual change of the content. */ -export class UniqueBehaviorSubject extends BehaviorSubject { +export class DeepState extends BehaviorSubject { constructor(initialData: T) { super(deepFreeze(initialData)); } diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/index.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/index.ts index a742f8b666..4040e0da0f 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/index.ts @@ -1,7 +1,7 @@ export * from './observer.controller'; export * from './observer'; -export * from './unique-behavior-subject'; -export * from './unique-array-behavior-subject'; -export * from './unique-object-behavior-subject'; +export * from './deep-state'; +export * from './array-state'; +export * from './object-state'; export * from './create-observable-part.method' export * from './append-to-frozen-array.method' diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/unique-object-behavior-subject.test.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/object-state.test.ts similarity index 82% rename from src/Umbraco.Web.UI.Client/libs/observable-api/unique-object-behavior-subject.test.ts rename to src/Umbraco.Web.UI.Client/libs/observable-api/object-state.test.ts index 86ca20394d..d0cf2f5d43 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/unique-object-behavior-subject.test.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/object-state.test.ts @@ -1,17 +1,17 @@ import { expect } from '@open-wc/testing'; -import { UniqueObjectBehaviorSubject } from './unique-object-behavior-subject'; +import { ObjectState } from './object-state'; import { createObservablePart } from '@umbraco-cms/observable-api'; -describe('UniqueObjectBehaviorSubject', () => { +describe('ObjectState', () => { type ObjectType = {key: string, another: string}; - let subject: UniqueObjectBehaviorSubject; + let subject: ObjectState; let initialData: ObjectType; beforeEach(() => { initialData = {key: 'some', another: 'myValue'}; - subject = new UniqueObjectBehaviorSubject(initialData); + subject = new ObjectState(initialData); }); diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/unique-object-behavior-subject.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/object-state.ts similarity index 52% rename from src/Umbraco.Web.UI.Client/libs/observable-api/unique-object-behavior-subject.ts rename to src/Umbraco.Web.UI.Client/libs/observable-api/object-state.ts index 0c2deed7d8..5175616f20 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/unique-object-behavior-subject.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/object-state.ts @@ -1,15 +1,15 @@ -import { UniqueBehaviorSubject } from "./unique-behavior-subject"; +import { DeepState } from "./deep-state"; /** * @export - * @class UniqueObjectBehaviorSubject - * @extends {UniqueBehaviorSubject} - * @description - A RxJS UniqueObjectBehaviorSubject which deepFreezes the object-data to ensure its not manipulated from any implementations. + * @class ObjectState + * @extends {DeepState} + * @description - A RxJS BehaviorSubject which deepFreezes the object-data to ensure its not manipulated from any implementations. * Additionally the Subject ensures the data is unique, not updating any Observes unless there is an actual change of the content. * - * The UniqueObjectBehaviorSubject provides methods to append data when the data is an Object. + * The ObjectState provides methods to append data when the data is an Object. */ -export class UniqueObjectBehaviorSubject extends UniqueBehaviorSubject { +export class ObjectState extends DeepState { /** * @method append @@ -17,7 +17,7 @@ export class UniqueObjectBehaviorSubject extends UniqueBehaviorSubject { * @description - Append some new data to this Object. * @example Example append some data. * const data = {key: 'myKey', value: 'myInitialValue'}; - * const mySubject = new UniqueObjectBehaviorSubject(data) + * const mySubject = new ObjectState(data) * mySubject.append({value: 'myNewValue'}) */ update(partialData: Partial) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-blueprints/document-blueprint.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-blueprints/document-blueprint.detail.store.ts index 8c42284bfb..6e21699ba4 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-blueprints/document-blueprint.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-blueprints/document-blueprint.detail.store.ts @@ -1,6 +1,6 @@ import type { DocumentBlueprintDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -18,7 +18,7 @@ export class UmbDocumentBlueprintDetailStore extends UmbStoreBase { // TODO: use the right type: - #data = new UniqueArrayBehaviorSubject([], (x) => x.key); + #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-blueprints/document-blueprint.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-blueprints/document-blueprint.tree.store.ts index 1f6018f2dc..f7cc3a11da 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-blueprints/document-blueprint.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-blueprints/document-blueprint.tree.store.ts @@ -1,7 +1,7 @@ import { DocumentBlueprintResource, DocumentTreeItem } from '@umbraco-cms/backend-api'; import { tryExecuteAndNotify } from '@umbraco-cms/resources'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -18,7 +18,7 @@ export const UMB_DocumentBlueprint_TREE_STORE_CONTEXT_TOKEN = new UmbContextToke export class UmbDocumentBlueprintTreeStore extends UmbStoreBase { - #data = new UniqueArrayBehaviorSubject([], (x) => x.key); + #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts index 449005c578..261c1b9ab9 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts @@ -1,6 +1,6 @@ import type { DocumentTypeDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -17,7 +17,7 @@ export const UMB_DOCUMENT_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken< export class UmbDocumentTypeDetailStore extends UmbStoreBase { - #data = new UniqueArrayBehaviorSubject([], (x) => x.key); + #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.tree.store.ts index dbca0c235c..bb94357d57 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.tree.store.ts @@ -1,7 +1,7 @@ import { DocumentTypeResource, DocumentTreeItem } from '@umbraco-cms/backend-api'; import { tryExecuteAndNotify } from '@umbraco-cms/resources'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -18,7 +18,7 @@ export const UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken([], (x) => x.key); + #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.detail.store.ts index 2b2d750a77..5e2d438e97 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.detail.store.ts @@ -1,6 +1,6 @@ import type { DocumentDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase, UmbContentStore } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -17,7 +17,7 @@ export const UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken { - private _data = new UniqueArrayBehaviorSubject([], (x) => x.key); + private _data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.tree.store.ts index b46b06cbb9..9ad6db2262 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.tree.store.ts @@ -1,7 +1,7 @@ import { DocumentResource, DocumentTreeItem } from '@umbraco-cms/backend-api'; import { tryExecuteAndNotify } from '@umbraco-cms/resources'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -18,7 +18,7 @@ export const UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken([], (x) => x.key); + private _data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/media-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/media-type.detail.store.ts index a0ff1cd408..bc32dbd8f1 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/media-type.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/media-type.detail.store.ts @@ -1,6 +1,6 @@ import type { DataTypeDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -17,7 +17,7 @@ export const UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken([], (x) => x.key); + private _data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/media-type.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/media-type.tree.store.ts index cb0fa4f758..f62d04040c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/media-type.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/media-type.tree.store.ts @@ -1,7 +1,7 @@ import { FolderTreeItem, MediaTypeResource } from '@umbraco-cms/backend-api'; import { tryExecuteAndNotify } from '@umbraco-cms/resources'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -17,7 +17,7 @@ export const UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken([], (x) => x.key); + #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.detail.store.ts index 81e777d384..46dbb42bf3 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.detail.store.ts @@ -1,6 +1,6 @@ import type { DocumentDetails, MediaDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase, UmbContentStore } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -17,7 +17,7 @@ export const UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken { - #data = new UniqueArrayBehaviorSubject([], (x) => x.key); + #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.tree.store.ts index adc6467b25..7ddff0e003 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.tree.store.ts @@ -2,7 +2,7 @@ import type { Observable } from 'rxjs'; import { MediaResource, ContentTreeItem } from '@umbraco-cms/backend-api'; import { tryExecuteAndNotify } from '@umbraco-cms/resources'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -22,7 +22,7 @@ export class UmbMediaTreeStore extends UmbStoreBase { - #data = new UniqueArrayBehaviorSubject([], (x) => x.key); + #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/member-group.details.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/member-group.details.store.ts index d18fecc6e8..8f2f06aec1 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/member-group.details.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/member-group.details.store.ts @@ -1,7 +1,7 @@ import { Observable } from 'rxjs'; import type { MemberGroupDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { ArrayState } from '@umbraco-cms/observable-api'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; import { UmbStoreBase } from '@umbraco-cms/store'; @@ -16,7 +16,7 @@ export const UMB_MEMBER_GROUP_STORE_CONTEXT_TOKEN = new UmbContextToken([], x => x.key); + #groups = new ArrayState([], x => x.key); public groups = this.#groups.asObservable(); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/member-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/member-type.detail.store.ts index a312141aaa..f74ea20e1c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/member-type.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/member-type.detail.store.ts @@ -1,6 +1,6 @@ import type { MemberTypeDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -17,7 +17,7 @@ export const UMB_MEMBER_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken([], (x) => x.key); + #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/member-type.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/member-type.tree.store.ts index 75c5742769..a5842155b1 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/member-type.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/member-type.tree.store.ts @@ -1,7 +1,7 @@ import { EntityTreeItem, MemberTypeResource, } from '@umbraco-cms/backend-api'; import { tryExecuteAndNotify } from '@umbraco-cms/resources'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -19,7 +19,7 @@ export class UmbMemberTypeTreeStore extends UmbStoreBase { // TODO: use the right type here: - #data = new UniqueArrayBehaviorSubject([], (x) => x.key); + #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.detail.store.ts index fe8413aba0..c0797a18e5 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.detail.store.ts @@ -1,6 +1,6 @@ import type { DataTypeDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -17,7 +17,7 @@ export const UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken([], (x) => x.key); + #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.tree.store.ts index 0258d53a5a..7363defdc9 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.tree.store.ts @@ -1,7 +1,7 @@ import { DataTypeResource, DocumentTreeItem } from '@umbraco-cms/backend-api'; import { tryExecuteAndNotify } from '@umbraco-cms/resources'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -18,7 +18,7 @@ export const UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken([], (x) => x.key); + #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts index 1a9e37abd3..5bb1a76e6a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts @@ -2,7 +2,7 @@ import { ContentTreeItem } from '@umbraco-cms/backend-api'; import { UmbTreeStore } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; import { UmbContextToken, UmbContextConsumerController } from '@umbraco-cms/context-api'; -import { UniqueBehaviorSubject, UmbObserverController } from '@umbraco-cms/observable-api'; +import { DeepState, UmbObserverController } from '@umbraco-cms/observable-api'; export class UmbCollectionContext< DataType extends ContentTreeItem, StoreType extends UmbTreeStore = UmbTreeStore @@ -13,10 +13,10 @@ export class UmbCollectionContext< private _store?: StoreType; protected _dataObserver?: UmbObserverController; - #data = new UniqueBehaviorSubject(>[]); + #data = new DeepState(>[]); public readonly data = this.#data.asObservable(); - #selection = new UniqueBehaviorSubject(>[]); + #selection = new DeepState(>[]); public readonly selection = this.#selection.asObservable(); /* diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts index 9cb0e9594d..3940a25f10 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts @@ -1,6 +1,6 @@ import { BehaviorSubject } from 'rxjs'; import type { Entity, ManifestSection, ManifestSectionView } from '@umbraco-cms/models'; -import { UniqueObjectBehaviorSubject } from '@umbraco-cms/observable-api'; +import { ObjectState } from '@umbraco-cms/observable-api'; import { UmbContextToken } from '@umbraco-cms/context-api'; export type ActiveTreeItemType = Entity | undefined; @@ -21,7 +21,7 @@ export class UmbSectionContext { */ // TODO: what is the best context to put this in? - #activeTreeItem = new UniqueObjectBehaviorSubject(undefined); + #activeTreeItem = new ObjectState(undefined); public readonly activeTreeItem = this.#activeTreeItem.asObservable(); // TODO: what is the best context to put this in? diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/context-menu/tree-context-menu-page.service.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/context-menu/tree-context-menu-page.service.ts index 36bcd4bc21..08adff9284 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/context-menu/tree-context-menu-page.service.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/context-menu/tree-context-menu-page.service.ts @@ -4,7 +4,7 @@ import { customElement, property, state } from 'lit/decorators.js'; import UmbTreeItemActionElement, { ActionPageEntity } from '../action/tree-item-action.element'; import { UmbTreeContextMenuService } from './tree-context-menu.service'; import { UmbLitElement } from '@umbraco-cms/element'; -import { UniqueBehaviorSubject } from '@umbraco-cms/observable-api'; +import { DeepState } from '@umbraco-cms/observable-api'; import { UmbContextToken } from '@umbraco-cms/context-api'; // TODO: Refactor this, its not a service and the data should be handled by a context api. @@ -15,7 +15,7 @@ export class UmbTreeContextMenuPageService extends UmbLitElement { @property({ type: Object }) public actionEntity: ActionPageEntity = { key: '', name: '' }; - #entity = new UniqueBehaviorSubject({ key: '', name: '' } as ActionPageEntity); + #entity = new DeepState({ key: '', name: '' } as ActionPageEntity); public readonly entity = this.#entity.asObservable(); @state() diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree.context.ts index 3cc05f0ee3..45a04dec82 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/tree.context.ts @@ -1,6 +1,6 @@ import type { Observable } from 'rxjs'; import type { ManifestTree } from '@umbraco-cms/models'; -import { UniqueBehaviorSubject } from '@umbraco-cms/observable-api'; +import { DeepState } from '@umbraco-cms/observable-api'; export interface UmbTreeContext { tree: ManifestTree; @@ -14,10 +14,10 @@ export interface UmbTreeContext { export class UmbTreeContextBase implements UmbTreeContext { public tree: ManifestTree; - #selectable = new UniqueBehaviorSubject(false); + #selectable = new DeepState(false); public readonly selectable = this.#selectable.asObservable(); - #selection = new UniqueBehaviorSubject(>[]); + #selection = new DeepState(>[]); public readonly selection = this.#selection.asObservable(); constructor(tree: ManifestTree) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts index 24dd2e2cfa..7c4ec8fa7b 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts @@ -1,7 +1,7 @@ import { UmbWorkspaceContentContext } from '../workspace/workspace-content/workspace-content.context'; import type { DataTypeDetails } from '@umbraco-cms/models'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; -import { createObservablePart, UniqueObjectBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ObjectState } from '@umbraco-cms/observable-api'; import { UmbContextConsumerController, UmbContextProviderController } from '@umbraco-cms/context-api'; // If we get this from the server then we can consider using TypeScripts Partial<> around the model from the Management-API. @@ -16,7 +16,7 @@ export type WorkspacePropertyData = { export class UmbWorkspacePropertyContext { private _providerController: UmbContextProviderController; - private _data = new UniqueObjectBehaviorSubject>({}); + private _data = new ObjectState>({}); public readonly alias = createObservablePart(this._data, (data) => data.alias); public readonly label = createObservablePart(this._data, (data) => data.label); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.context.ts index 3a494630a3..76852a829f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace/workspace-content/workspace-content.context.ts @@ -2,7 +2,7 @@ import { v4 as uuidv4 } from 'uuid'; import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN, UmbNotificationDefaultData } from '@umbraco-cms/notification'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; import { UmbContextConsumerController, UmbContextProviderController } from '@umbraco-cms/context-api'; -import { UniqueBehaviorSubject, UmbObserverController, createObservablePart } from '@umbraco-cms/observable-api'; +import { DeepState, UmbObserverController, createObservablePart } from '@umbraco-cms/observable-api'; import { UmbContentStore } from '@umbraco-cms/store'; import type { ContentTreeItem } from '@umbraco-cms/backend-api'; @@ -33,7 +33,7 @@ export abstract class UmbWorkspaceContentContext< constructor(host: UmbControllerHostInterface, defaultData: ContentTypeType, storeAlias: string, entityType: string) { this._host = host; - this._data = new UniqueBehaviorSubject(defaultData); + this._data = new DeepState(defaultData); this.data = this._data.asObservable(); this.name = createObservablePart(this._data, (data) => data.name); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.context.ts index da7137c8e4..3964a7a5f0 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/property-actions/shared/property-action-menu/property-action-menu.context.ts @@ -1,9 +1,9 @@ import { UmbContextProviderController } from '@umbraco-cms/context-api'; import type { UmbControllerHostInterface } from '@umbraco-cms/controller'; -import { UniqueBehaviorSubject } from '@umbraco-cms/observable-api'; +import { DeepState } from '@umbraco-cms/observable-api'; export class UmbPropertyActionMenuContext { - #isOpen = new UniqueBehaviorSubject(false); + #isOpen = new DeepState(false); public readonly isOpen = this.#isOpen.asObservable(); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/dictionary.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/dictionary.detail.store.ts index 598a2f16d1..8ad7d063ba 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/dictionary.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/dictionary.detail.store.ts @@ -1,6 +1,6 @@ import type { DictionaryDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; import { EntityTreeItem } from '@umbraco-cms/backend-api'; @@ -19,7 +19,7 @@ export class UmbDictionaryDetailStore extends UmbStoreBase { // TODO: use the right type: - #data = new UniqueArrayBehaviorSubject([], (x) => x.key); + #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/dictionary.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/dictionary.tree.store.ts index f7f8687833..04c810fc85 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/dictionary.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/dictionary.tree.store.ts @@ -1,7 +1,7 @@ import { DictionaryResource, DocumentTreeItem } from '@umbraco-cms/backend-api'; import { tryExecuteAndNotify } from '@umbraco-cms/resources'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -18,7 +18,7 @@ export const UMB_DICTIONARY_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken([], (x) => x.key); + #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user-history.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user-history.store.ts index 6044e059f1..192c5f13b9 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user-history.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user-history.store.ts @@ -1,5 +1,5 @@ import { UmbContextToken } from '@umbraco-cms/context-api'; -import { createObservablePart, UniqueBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, DeepState } from '@umbraco-cms/observable-api'; export type UmbModelType = 'dialog' | 'sidebar'; @@ -10,7 +10,7 @@ export type UmbCurrentUserHistoryItem = { }; export class UmbCurrentUserHistoryStore { - #history = new UniqueBehaviorSubject(>[]); + #history = new DeepState(>[]); public readonly history = this.#history.asObservable(); public readonly latestHistory = createObservablePart(this.#history, (historyItems) => historyItems.slice(-10)); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/user-group.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/user-group.store.ts index 6c2e68b807..31f3b99a22 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/user-group.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-groups/user-group.store.ts @@ -1,7 +1,7 @@ import type { UserGroupDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; -import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; // TODO: get rid of this type addition & { ... }: @@ -18,7 +18,7 @@ export const UMB_USER_GROUP_STORE_CONTEXT_TOKEN = new UmbContextToken([], x => x.key); + #groups = new ArrayState([], x => x.key); public groups = this.#groups.asObservable(); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-section/views/users/section-view-users.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-section/views/users/section-view-users.element.ts index 01eaac240a..bcd4f3f8ad 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/user-section/views/users/section-view-users.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/user-section/views/users/section-view-users.element.ts @@ -12,7 +12,7 @@ import type { ManifestWorkspace, UserDetails } from '@umbraco-cms/models'; import { UmbUserStore, UMB_USER_STORE_CONTEXT_TOKEN } from 'src/backoffice/users/users/user.store'; import { createExtensionElement } from '@umbraco-cms/extensions-api'; import { UmbLitElement } from '@umbraco-cms/element'; -import { UniqueBehaviorSubject } from '@umbraco-cms/observable-api'; +import { DeepState } from '@umbraco-cms/observable-api'; @customElement('umb-section-view-users') export class UmbSectionViewUsersElement extends UmbLitElement { @@ -33,13 +33,13 @@ export class UmbSectionViewUsersElement extends UmbLitElement { // TODO: This must be turned into context api: Maybe its a Collection View (SectionView Collection View)? private _userStore?: UmbUserStore; - #selection = new UniqueBehaviorSubject(>[]); + #selection = new DeepState(>[]); public readonly selection = this.#selection.asObservable(); - #users = new UniqueBehaviorSubject(>[]); + #users = new DeepState(>[]); public readonly users = this.#users.asObservable(); - #search = new UniqueBehaviorSubject(''); + #search = new DeepState(''); public readonly search = this.#search.asObservable(); constructor() { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/user.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/user.store.ts index 670173fa49..3c4243d110 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/user.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/user.store.ts @@ -1,6 +1,6 @@ import { BehaviorSubject } from 'rxjs'; import type { UserDetails } from '@umbraco-cms/models'; -import { createObservablePart, UniqueArrayBehaviorSubject } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbContextToken } from '@umbraco-cms/context-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import type { UmbControllerHostInterface } from '@umbraco-cms/controller'; @@ -18,7 +18,7 @@ export const UMB_USER_STORE_CONTEXT_TOKEN = new UmbContextToken('U export class UmbUserStore extends UmbStoreBase { - #users = new UniqueArrayBehaviorSubject([], x => x.key); + #users = new ArrayState([], x => x.key); public users = this.#users.asObservable(); #totalUsers = new BehaviorSubject(0); From eee77fff33b931298ede4497dc814c4fc78185fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 24 Jan 2023 13:44:04 +0100 Subject: [PATCH 05/13] update JSDocs --- .../libs/observable-api/append-to-frozen-array.method.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/append-to-frozen-array.method.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/append-to-frozen-array.method.ts index 638008ffb9..ffbc2a8890 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/append-to-frozen-array.method.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/append-to-frozen-array.method.ts @@ -5,7 +5,7 @@ * @param {(mappable: T) => R} mappingFunction - Method to return the part for this Observable to return. * @param {(previousResult: R, currentResult: R) => boolean} [memoizationFunction] - Method to Compare if the data has changed. Should return true when data is different. * @description - Creates a RxJS Observable from RxJS Subject. - * @example Example append new entry for a UniqueBehaviorSubject which is an array. Where the key is unique and the item will be updated if matched with existing. + * @example Example append new entry for a ArrayState or a part of DeepState/ObjectState it which is an array. Where the key is unique and the item will be updated if matched with existing. * const entry = {key: 'myKey', value: 'myValue'}; * const newDataSet = appendToFrozenArray(mySubject.getValue(), entry, x => x.key === key); * mySubject.next(newDataSet); From 749a231559084554b7702f018dcb23afad57653b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 24 Jan 2023 14:13:10 +0100 Subject: [PATCH 06/13] correct typings of simple states --- .../libs/observable-api/basic-state.ts | 2 +- .../libs/observable-api/number-state.ts | 5 ++++- .../libs/observable-api/string-state.ts | 8 +++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/basic-state.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/basic-state.ts index da449ed38c..3512c0508c 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/basic-state.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/basic-state.ts @@ -6,7 +6,7 @@ import { BehaviorSubject } from "rxjs"; * @extends {BehaviorSubject} * @description - A RxJS BehaviorSubject this Subject ensures the data is unique, not updating any Observes unless there is an actual change of the value. */ -export class BasicState extends BehaviorSubject { +export class BasicState extends BehaviorSubject { constructor(initialData: T) { super(initialData); } diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/number-state.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/number-state.ts index 8a2b775351..9d1cfd5c21 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/number-state.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/number-state.ts @@ -6,6 +6,9 @@ import { BasicState } from "./basic-state"; * @extends {BehaviorSubject} * @description - A RxJS BehaviorSubject this Subject ensures the data is unique, not updating any Observes unless there is an actual change of the value. */ -export class NumberState extends BasicState { +export class NumberState extends BasicState { + constructor(initialData: T | number) { + super(initialData); + } } diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/string-state.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/string-state.ts index 4ec129fe84..7a92238a6a 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/string-state.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/string-state.ts @@ -3,9 +3,11 @@ import { BasicState } from "./basic-state"; /** * @export * @class StringState - * @extends {BehaviorSubject} + * @extends {BasicState} * @description - A RxJS BehaviorSubject this Subject ensures the data is unique, not updating any Observes unless there is an actual change of the value. */ -export class StringState extends BasicState { - +export class StringState extends BasicState { + constructor(initialData: T | string) { + super(initialData); + } } From d73cb45f68cb9c8984e7f6e57f15f1ff669e3cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 24 Jan 2023 14:37:51 +0100 Subject: [PATCH 07/13] correct implementation of states across --- .../src/backoffice/backoffice.element.ts | 2 -- .../shared/collection/collection.context.ts | 2 +- .../backoffice-header-sections.element.ts | 35 ++++++++---------- .../backoffice-main.element.ts | 12 ++----- .../components/section/section.context.ts | 20 ++++++++--- .../components/section/section.element.ts | 36 ++++++++----------- .../components/section/section.store.ts | 25 ------------- .../tree/action/tree-item-action.element.ts | 12 ------- ...e-context-menu-page-action-list.element.ts | 14 +------- .../workspace-property.context.ts | 2 +- .../users/current-user/current-user.store.ts | 11 +++--- .../src/backoffice/users/users/user.store.ts | 4 +-- .../src/core/modal/modal.service.ts | 5 +-- .../consent/installer-consent.element.ts | 2 +- .../src/installer/installer.context.ts | 12 ++++--- .../src/stories/store.stories.mdx | 2 +- 16 files changed, 69 insertions(+), 127 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.store.ts diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts index 011b537e8e..da857f00f6 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts @@ -30,7 +30,6 @@ import { UmbDictionaryTreeStore } from './translation/dictionary/dictionary.tree import { UmbDocumentBlueprintDetailStore } from './documents/document-blueprints/document-blueprint.detail.store'; import { UmbDocumentBlueprintTreeStore } from './documents/document-blueprints/document-blueprint.tree.store'; -import { UmbSectionStore, UMB_SECTION_STORE_CONTEXT_TOKEN } from './shared/components/section/section.store'; import { UmbDataTypeDetailStore } from './settings/data-types/data-type.detail.store'; import { UmbDataTypeTreeStore } from './settings/data-types/data-type.tree.store'; @@ -92,7 +91,6 @@ export class UmbBackofficeElement extends UmbLitElement { new UmbDocumentBlueprintDetailStore(this); new UmbDocumentBlueprintTreeStore(this); - this.provideContext(UMB_SECTION_STORE_CONTEXT_TOKEN, new UmbSectionStore()); this.provideContext(UMB_CURRENT_USER_HISTORY_STORE_CONTEXT_TOKEN, new UmbCurrentUserHistoryStore()); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts index 5bb1a76e6a..e023fbbca9 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts @@ -21,7 +21,7 @@ export class UmbCollectionContext< /* TODO: - private _search = new UniqueBehaviorSubject(''); + private _search = new StringState(''); public readonly search = this._search.asObservable(); */ diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice-header-sections.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice-header-sections.element.ts index ba01e94be6..86588db2d0 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice-header-sections.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice-header-sections.element.ts @@ -2,7 +2,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, CSSResultGroup, html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { when } from 'lit/directives/when.js'; -import { UmbSectionStore, UMB_SECTION_STORE_CONTEXT_TOKEN } from '../section/section.store'; +import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../section/section.context'; import type { ManifestSection } from '@umbraco-cms/models'; import { UmbLitElement } from '@umbraco-cms/element'; @@ -49,13 +49,13 @@ export class UmbBackofficeHeaderSections extends UmbLitElement { @state() private _currentSectionAlias = ''; - private _sectionStore?: UmbSectionStore; + private _sectionContext?: UmbSectionContext; constructor() { super(); - this.consumeContext(UMB_SECTION_STORE_CONTEXT_TOKEN, (sectionStore) => { - this._sectionStore = sectionStore; + this.consumeContext(UMB_SECTION_CONTEXT_TOKEN, (sectionStore) => { + this._sectionContext = sectionStore; this._observeSections(); this._observeCurrentSection(); }); @@ -66,15 +66,8 @@ export class UmbBackofficeHeaderSections extends UmbLitElement { this._open = !this._open; } - private _handleTabClick(e: PointerEvent) { - const tab = e.currentTarget as HTMLElement; - - // TODO: we need to be able to prevent the tab from setting the active state - if (tab.id === 'moreTab') return; - - if (!tab.dataset.alias) return; - - this._sectionStore?.setCurrent(tab.dataset.alias); + private _handleSectionTabClick(sectionManifest: ManifestSection) { + this._sectionContext?.setManifest(sectionManifest); } private _handleLabelClick() { @@ -85,19 +78,19 @@ export class UmbBackofficeHeaderSections extends UmbLitElement { } private _observeSections() { - if (!this._sectionStore) return; + if (!this._sectionContext) return; - this.observe(this._sectionStore.getAllowed(), (allowedSections) => { + this.observe(this._sectionContext.getAllowed(), (allowedSections) => { this._sections = allowedSections; this._visibleSections = this._sections; }); } private _observeCurrentSection() { - if (!this._sectionStore) return; + if (!this._sectionContext) return; - this.observe(this._sectionStore.currentAlias, (currentSectionAlias) => { - this._currentSectionAlias = currentSectionAlias; + this.observe(this._sectionContext.alias, (currentSectionAlias) => { + this._currentSectionAlias = currentSectionAlias || ''; }); } @@ -107,11 +100,11 @@ export class UmbBackofficeHeaderSections extends UmbLitElement { ${this._visibleSections.map( (section: ManifestSection) => html` + > ` )} ${this._renderExtraSections()} @@ -123,7 +116,7 @@ export class UmbBackofficeHeaderSections extends UmbLitElement { return when( this._extraSections.length > 0, () => html` - + diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice-main.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice-main.element.ts index a0b655669a..e6a7810dbf 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice-main.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice-main.element.ts @@ -3,10 +3,10 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html } from 'lit'; import { state } from 'lit/decorators.js'; import { IRoutingInfo } from 'router-slot'; -import { UmbSectionStore, UMB_SECTION_STORE_CONTEXT_TOKEN } from '../section/section.store'; import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../section/section.context'; import { UmbSectionElement } from '../section/section.element'; import { createExtensionElement } from '@umbraco-cms/extensions-api'; +import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry'; import type { ManifestSection } from '@umbraco-cms/models'; import { UmbLitElement } from '@umbraco-cms/element'; @@ -36,21 +36,16 @@ export class UmbBackofficeMain extends UmbLitElement { private _routePrefix = 'section/'; private _sectionContext?: UmbSectionContext; - private _sectionStore?: UmbSectionStore; constructor() { super(); - this.consumeContext(UMB_SECTION_STORE_CONTEXT_TOKEN, (_instance) => { - this._sectionStore = _instance; - this._observeSections(); - }); + this._observeSections(); } private async _observeSections() { - if (!this._sectionStore) return; - this.observe(this._sectionStore.getAllowed(), (sections) => { + this.observe(umbExtensionsRegistry.extensionsOfType('section'), (sections) => { this._sections = sections; if (!sections) return; this._createRoutes(); @@ -86,7 +81,6 @@ export class UmbBackofficeMain extends UmbLitElement { const currentPath = info.match.route.path; const section = this._sections.find((s) => this._routePrefix + s.meta.pathname === currentPath); if (!section) return; - this._sectionStore?.setCurrent(section.alias); this._provideSectionContext(section); }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts index 3940a25f10..0e6468bb13 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts @@ -1,15 +1,16 @@ -import { BehaviorSubject } from 'rxjs'; import type { Entity, ManifestSection, ManifestSectionView } from '@umbraco-cms/models'; +import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry'; import { ObjectState } from '@umbraco-cms/observable-api'; import { UmbContextToken } from '@umbraco-cms/context-api'; +import { StringState } from 'libs/observable-api/string-state'; export type ActiveTreeItemType = Entity | undefined; export class UmbSectionContext { - #manifestAlias = new BehaviorSubject(undefined); - #manifestPathname = new BehaviorSubject(undefined); - #manifestLabel = new BehaviorSubject(undefined); + #manifestAlias = new StringState(undefined); + #manifestPathname = new StringState(undefined); + #manifestLabel = new StringState(undefined); public readonly alias = this.#manifestAlias.asObservable(); public readonly pathname = this.#manifestPathname.asObservable(); public readonly label = this.#manifestLabel.asObservable(); @@ -25,13 +26,22 @@ export class UmbSectionContext { public readonly activeTreeItem = this.#activeTreeItem.asObservable(); // TODO: what is the best context to put this in? - #activeViewPathname = new BehaviorSubject(undefined); + #activeViewPathname = new StringState(undefined); public readonly activeViewPathname = this.#activeViewPathname.asObservable(); constructor(manifest: ManifestSection) { this.setManifest(manifest); } + public getAllowed() { + // TODO: implemented allowed filtering + /* + const { data } = await getUserSections({}); + this._allowedSection = data.sections; + */ + return umbExtensionsRegistry.extensionsOfType('section'); + } + public setManifest(manifest?: ManifestSection) { this.#manifestAlias.next(manifest?.alias); this.#manifestPathname.next(manifest?.meta?.pathname); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts index 8d30e70f66..3f477b8091 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts @@ -44,6 +44,7 @@ export class UmbSectionElement extends UmbLitElement { private _views?: Array; private _sectionContext?: UmbSectionContext; + private _sectionAlias?: string; constructor() { super(); @@ -138,32 +139,23 @@ export class UmbSectionElement extends UmbLitElement { ]; } - private _observeViews() { + + + private _observeSection() { if (!this._sectionContext) return; this.observe( - this._sectionContext.alias.pipe( - switchMap((section) => { - if (!section) return EMPTY; + this._sectionContext.alias, (alias) => { + this._sectionAlias = alias; + this._observeViews(); + } + ); + } - return ( - umbExtensionsRegistry - ?.extensionsOfType('sectionView') - .pipe( - map((views) => - views - .filter((view) => view.meta.sections.includes(section.alias)) - .sort((a, b) => b.meta.weight - a.meta.weight) - ) - ) ?? of([]) - ); - }) - ), - (views) => { - if (views.length > 0) { - this._views = views; - this._createViewRoutes(); - } + private _observeViews() { + this.observe(umbExtensionsRegistry?.extensionsOfType('sectionView'), (views) => { + this._views = views.filter((view) => this._sectionAlias ? view.meta.sections.includes(this._sectionAlias) : false).sort((a, b) => b.meta.weight - a.meta.weight) + this._createViewRoutes(); } ); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.store.ts deleted file mode 100644 index 5ef99b054d..0000000000 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.store.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Observable, ReplaySubject } from 'rxjs'; -import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry'; -import { UmbContextToken } from '@umbraco-cms/context-api'; - -// TODO: maybe this should be named something else than store? -export class UmbSectionStore { - private _currentAlias: ReplaySubject = new ReplaySubject(1); - public readonly currentAlias: Observable = this._currentAlias.asObservable(); - - public getAllowed() { - // TODO: implemented allowed filtering - /* - const { data } = await getUserSections({}); - this._allowedSection = data.sections; - */ - - return umbExtensionsRegistry.extensionsOfType('section'); - } - - public setCurrent(alias: string) { - this._currentAlias.next(alias); - } -} - -export const UMB_SECTION_STORE_CONTEXT_TOKEN = new UmbContextToken(UmbSectionStore.name); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/action/tree-item-action.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/action/tree-item-action.element.ts index 0e31b90975..438dd649a0 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/action/tree-item-action.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/action/tree-item-action.element.ts @@ -24,7 +24,6 @@ export default class UmbTreeItemActionElement extends UmbLitElement { @state() protected _entity: ActionPageEntity = { name: '', key: '' }; - //protected _activeTree?: ManifestTree; protected _activeTreeItem?: ActiveTreeItemType; protected _sectionContext?: UmbSectionContext; @@ -36,7 +35,6 @@ export default class UmbTreeItemActionElement extends UmbLitElement { this.consumeContext(UMB_SECTION_CONTEXT_TOKEN, (sectionContext) => { this._sectionContext = sectionContext; - this._observeActiveTree(); this._observeActiveTreeItem(); }); @@ -58,16 +56,6 @@ export default class UmbTreeItemActionElement extends UmbLitElement { }); } - private _observeActiveTree() { - if (!this._sectionContext) return; - - /* - this.observe(this._sectionContext.activeTree, (tree) => { - this._activeTree = tree; - }); - */ - } - private _observeActiveTreeItem() { if (!this._sectionContext) return; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/context-menu/tree-context-menu-page-action-list.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/context-menu/tree-context-menu-page-action-list.element.ts index 9ef5edbae1..8fa31cac09 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/context-menu/tree-context-menu-page-action-list.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/context-menu/tree-context-menu-page-action-list.element.ts @@ -3,7 +3,7 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { customElement, state } from 'lit/decorators.js'; import { map } from 'rxjs'; import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../../section/section.context'; -import type { Entity, ManifestTreeItemAction, ManifestTree } from '@umbraco-cms/models'; +import type { Entity, ManifestTreeItemAction } from '@umbraco-cms/models'; import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry'; import { UmbLitElement } from '@umbraco-cms/element'; @@ -30,9 +30,6 @@ export class UmbTreeContextMenuPageActionListElement extends UmbLitElement { @state() private _actions?: Array; - @state() - private _activeTree?: ManifestTree; - @state() private _activeTreeItem?: Entity; @@ -43,7 +40,6 @@ export class UmbTreeContextMenuPageActionListElement extends UmbLitElement { this.consumeContext(UMB_SECTION_CONTEXT_TOKEN, (sectionContext) => { this._sectionContext = sectionContext; - this._observeActiveTree(); this._observeActiveTreeItem(); this._observeTreeItemActions(); }); @@ -62,14 +58,6 @@ export class UmbTreeContextMenuPageActionListElement extends UmbLitElement { ); } - private _observeActiveTree() { - if (!this._sectionContext) return; - - this.observe(this._sectionContext.activeTree, (tree) => { - this._activeTree = tree || undefined; - }); - } - private _observeActiveTreeItem() { if (!this._sectionContext) return; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts index 7c4ec8fa7b..c5553a5d74 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/workspace-property/workspace-property.context.ts @@ -45,7 +45,7 @@ export class UmbWorkspacePropertyContext { this._data.update({ description: description }); } public setValue(value: WorkspacePropertyData['value']) { - // Note: Do not try to compare new / old value, as it can of any type. We trust the UniqueBehaviorSubject in doing such. + // Note: Do not try to compare new / old value, as it can of any type. We trust the ObjectState in doing such. this._data.update({ value: value }); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user.store.ts index d3d60997cd..489372e445 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user.store.ts @@ -1,12 +1,12 @@ -import { BehaviorSubject, Observable } from 'rxjs'; -import { umbUsersData } from '../../../core/mocks/data/users.data'; import type { UserDetails } from '@umbraco-cms/models'; import { umbracoPath } from '@umbraco-cms/utils'; import { UmbContextToken } from '@umbraco-cms/context-api'; +import { ObjectState } from '@umbraco-cms/observable-api'; export class UmbCurrentUserStore { - private _currentUser = new BehaviorSubject(umbUsersData.getAll()[0]); //TODO: Temp solution to set the first user as the current logged in user - public readonly currentUser: Observable = this._currentUser.asObservable(); + + private _currentUser = new ObjectState(undefined); + public readonly currentUser = this._currentUser.asObservable(); /** * logs out the user @@ -24,7 +24,8 @@ export class UmbCurrentUserStore { public get isAdmin(): boolean { //TODO: Find a way to figure out if current user is in the admin group const adminUserGroupKey = 'c630d49e-4e7b-42ea-b2bc-edc0edacb6b1'; - return this._currentUser.getValue()?.userGroups.includes(adminUserGroupKey); + const currentUser = this._currentUser.getValue(); + return currentUser ? currentUser.userGroups.includes(adminUserGroupKey) : false; } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/user.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/user.store.ts index 3c4243d110..eee87afc1c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/user.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/user.store.ts @@ -1,9 +1,9 @@ -import { BehaviorSubject } from 'rxjs'; import type { UserDetails } from '@umbraco-cms/models'; import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbContextToken } from '@umbraco-cms/context-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import type { UmbControllerHostInterface } from '@umbraco-cms/controller'; +import { NumberState } from 'libs/observable-api/number-state'; export type UmbUserStoreItemType = UserDetails; @@ -21,7 +21,7 @@ export class UmbUserStore extends UmbStoreBase { #users = new ArrayState([], x => x.key); public users = this.#users.asObservable(); - #totalUsers = new BehaviorSubject(0); + #totalUsers = new NumberState(0); public readonly totalUsers = this.#totalUsers.asObservable(); diff --git a/src/Umbraco.Web.UI.Client/src/core/modal/modal.service.ts b/src/Umbraco.Web.UI.Client/src/core/modal/modal.service.ts index 25bb56cad3..64e390f82c 100644 --- a/src/Umbraco.Web.UI.Client/src/core/modal/modal.service.ts +++ b/src/Umbraco.Web.UI.Client/src/core/modal/modal.service.ts @@ -5,7 +5,6 @@ import './layouts/property-editor-ui-picker/modal-layout-property-editor-ui-pick import './layouts/modal-layout-current-user.element'; import { UUIModalSidebarSize } from '@umbraco-ui/uui-modal-sidebar'; -import { BehaviorSubject } from 'rxjs'; import { UmbModalChangePasswordData } from './layouts/modal-layout-change-password.element'; import type { UmbModalIconPickerData } from './layouts/icon-picker/modal-layout-icon-picker.element'; @@ -16,6 +15,7 @@ import type { UmbModalContentPickerData } from './layouts/content-picker/modal-l import type { UmbModalPropertyEditorUIPickerData } from './layouts/property-editor-ui-picker/modal-layout-property-editor-ui-picker.element'; import { UmbModalHandler } from './modal-handler'; import { UmbContextToken } from '@umbraco-cms/context-api'; +import { ArrayState } from '@umbraco-cms/observable-api'; export type UmbModalType = 'dialog' | 'sidebar'; @@ -27,7 +27,8 @@ export interface UmbModalOptions { // TODO: Should this be called UmbModalContext ? as we don't have 'services' as a term. export class UmbModalService { - #modals = new BehaviorSubject(>[]); + + #modals = new ArrayState(>[]); public readonly modals = this.#modals.asObservable(); /** diff --git a/src/Umbraco.Web.UI.Client/src/installer/consent/installer-consent.element.ts b/src/Umbraco.Web.UI.Client/src/installer/consent/installer-consent.element.ts index ae2c08282e..224745328b 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/consent/installer-consent.element.ts +++ b/src/Umbraco.Web.UI.Client/src/installer/consent/installer-consent.element.ts @@ -66,7 +66,7 @@ export class UmbInstallerConsentElement extends UmbLitElement { if (!this._installerContext) return; this.observe(this._installerContext.settings, (settings) => { - this._telemetryLevels = settings.user?.consentLevels ?? []; + this._telemetryLevels = settings?.user?.consentLevels ?? []; }); } diff --git a/src/Umbraco.Web.UI.Client/src/installer/installer.context.ts b/src/Umbraco.Web.UI.Client/src/installer/installer.context.ts index 716e2363e4..56994cfdc6 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/installer.context.ts +++ b/src/Umbraco.Web.UI.Client/src/installer/installer.context.ts @@ -1,7 +1,9 @@ -import { BehaviorSubject, Observable, ReplaySubject } from 'rxjs'; +import { Observable, ReplaySubject } from 'rxjs'; import { Install, InstallResource, InstallSettings, ProblemDetails, TelemetryLevel } from '@umbraco-cms/backend-api'; import { tryExecute } from '@umbraco-cms/resources'; import { UmbContextToken } from '@umbraco-cms/context-api'; +import { NumberState } from 'libs/observable-api/number-state'; +import { ObjectState } from '@umbraco-cms/observable-api'; /** * Context API for the installer @@ -9,20 +11,20 @@ import { UmbContextToken } from '@umbraco-cms/context-api'; * @class UmbInstallerContext */ export class UmbInstallerContext { - private _data = new BehaviorSubject({ + private _data = new ObjectState({ user: { name: '', email: '', password: '', subscribeToNewsletter: false }, database: { id: '', providerName: '' }, telemetryLevel: TelemetryLevel.BASIC, }); public readonly data = this._data.asObservable(); - private _currentStep = new BehaviorSubject(1); + private _currentStep = new NumberState(1); public readonly currentStep = this._currentStep.asObservable(); - private _settings = new ReplaySubject(); + private _settings = new ObjectState(undefined); public readonly settings = this._settings.asObservable(); - private _installStatus = new ReplaySubject(1); + private _installStatus = new ObjectState(null); public readonly installStatus = this._installStatus.asObservable(); constructor() { diff --git a/src/Umbraco.Web.UI.Client/src/stories/store.stories.mdx b/src/Umbraco.Web.UI.Client/src/stories/store.stories.mdx index 6ae85084a0..f339233429 100644 --- a/src/Umbraco.Web.UI.Client/src/stories/store.stories.mdx +++ b/src/Umbraco.Web.UI.Client/src/stories/store.stories.mdx @@ -13,7 +13,7 @@ Generally a Store will be holding one or more RxJS Subjects, each Subject is mad ```` class MyProductStore { - protected _products = new UniqueBehaviorSubject(>[]); + protected _products = new ArrayState(>[]); public readonly products = this._items.asObservable(); protected host: UmbControllerHostInterface; From 30614f5f160ac4ab20e51da807f3f44dc5371460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 24 Jan 2023 16:25:52 +0100 Subject: [PATCH 08/13] Fixed mistakes --- .../src/backoffice/backoffice.element.ts | 6 ++-- .../document-blueprint.detail.store.ts | 2 +- .../document-type.detail.store.ts | 2 +- .../documents/document.detail.store.ts | 1 + .../documents/documents/tree/manifests.ts | 4 +-- .../workspace/document-workspace.context.ts | 2 ++ .../media/media/media.detail.store.ts | 4 +-- .../media/media/media.tree.store.ts | 2 +- .../media/media/workspace/manifests.ts | 4 +-- .../src/backoffice/media/section.manifests.ts | 4 +-- .../data-types/data-type.detail.store.ts | 5 +++- .../collection-view-media-grid.element.ts | 30 +++++++++++-------- .../backoffice-header-sections.element.ts | 23 +++++++------- .../backoffice-main.element.ts | 26 ++++++++++------ .../backoffice-frame/backoffice.context.ts | 28 +++++++++++++++++ .../section-sidebar-menu.element.ts | 3 +- .../components/section/section.context.ts | 10 ------- .../components/section/section.element.ts | 17 +++++++++-- .../dictionary/dictionary.detail.store.ts | 2 +- 19 files changed, 114 insertions(+), 61 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice.context.ts diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts index da857f00f6..1d684f6192 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/backoffice.element.ts @@ -2,8 +2,6 @@ import { defineElement } from '@umbraco-ui/uui-base/lib/registration'; import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, html } from 'lit'; -import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification'; -import { UmbLitElement } from '@umbraco-cms/element'; import { UmbModalService, UMB_MODAL_SERVICE_CONTEXT_TOKEN } from '../core/modal'; import { UmbUserStore } from './users/users/user.store'; @@ -14,6 +12,7 @@ import { UMB_CURRENT_USER_HISTORY_STORE_CONTEXT_TOKEN, } from './users/current-user/current-user-history.store'; +import { UmbBackofficeContext, UMB_BACKOFFICE_CONTEXT_TOKEN } from './shared/components/backoffice-frame/backoffice.context'; import {UmbDocumentTypeDetailStore} from './documents/document-types/document-type.detail.store'; import {UmbDocumentTypeTreeStore} from './documents/document-types/document-type.tree.store'; import { UmbMediaTypeDetailStore } from './media/media-types/media-type.detail.store'; @@ -32,6 +31,7 @@ import { UmbDocumentBlueprintTreeStore } from './documents/document-blueprints/d import { UmbDataTypeDetailStore } from './settings/data-types/data-type.detail.store'; import { UmbDataTypeTreeStore } from './settings/data-types/data-type.tree.store'; +import { UmbNotificationService, UMB_NOTIFICATION_SERVICE_CONTEXT_TOKEN } from '@umbraco-cms/notification'; // Domains @@ -44,6 +44,7 @@ import './users'; import './packages'; import './search'; import './shared'; +import { UmbLitElement } from '@umbraco-cms/element'; @defineElement('umb-backoffice') export class UmbBackofficeElement extends UmbLitElement { @@ -91,6 +92,7 @@ export class UmbBackofficeElement extends UmbLitElement { new UmbDocumentBlueprintDetailStore(this); new UmbDocumentBlueprintTreeStore(this); + this.provideContext(UMB_BACKOFFICE_CONTEXT_TOKEN, new UmbBackofficeContext()); this.provideContext(UMB_CURRENT_USER_HISTORY_STORE_CONTEXT_TOKEN, new UmbCurrentUserHistoryStore()); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-blueprints/document-blueprint.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-blueprints/document-blueprint.detail.store.ts index 6e21699ba4..3e44801258 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-blueprints/document-blueprint.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-blueprints/document-blueprint.detail.store.ts @@ -33,7 +33,7 @@ export class UmbDocumentBlueprintDetailStore extends UmbStoreBase { */ getByKey(key: string) { // TODO: use backend cli when available. - fetch(`/umbraco/management/api/v1/document/document-blueprint/${key}`) + fetch(`/umbraco/management/api/v1/document-blueprint/details/${key}`) .then((res) => res.json()) .then((data) => { this.#data.append(data); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts index 261c1b9ab9..62b8c31c5d 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/document-type.detail.store.ts @@ -32,7 +32,7 @@ export class UmbDocumentTypeDetailStore extends UmbStoreBase { */ getByKey(key: string) { // TODO: use backend cli when available. - fetch(`/umbraco/management/api/v1/document/document-type/${key}`) + fetch(`/umbraco/management/api/v1/document-type/details/${key}`) .then((res) => res.json()) .then((data) => { this.#data.append(data); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.detail.store.ts index 5e2d438e97..d02ab4c941 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/document.detail.store.ts @@ -25,6 +25,7 @@ export class UmbDocumentDetailStore extends UmbStoreBase implements UmbContentSt } getByKey(key: string) { + console.log("document getByKey", key) // TODO: use backend cli when available. fetch(`/umbraco/management/api/v1/document/details/${key}`) .then((res) => res.json()) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/tree/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/tree/manifests.ts index 6021b6d817..676ffe6c07 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/tree/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/tree/manifests.ts @@ -1,4 +1,4 @@ -import { UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN } from '../document.detail.store'; +import { UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN } from '../document.tree.store'; import type { ManifestTree, ManifestTreeItemAction } from '@umbraco-cms/models'; const treeAlias = 'Umb.Tree.Documents'; @@ -8,7 +8,7 @@ const tree: ManifestTree = { alias: treeAlias, name: 'Documents Tree', meta: { - storeAlias: UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN.toString(), + storeAlias: UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN.toString(), }, }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts index 4f60b6d583..b76b29f580 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/workspace/document-workspace.context.ts @@ -37,6 +37,8 @@ const DefaultDocumentData = { export class UmbWorkspaceDocumentContext extends UmbWorkspaceContentContext { constructor(host: UmbControllerHostInterface) { super(host, DefaultDocumentData, UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN.toString(), 'document'); + + console.log("UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN", UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN.toString()) } public setPropertyValue(alias: string, value: unknown) { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.detail.store.ts index 46dbb42bf3..8783a9a6a2 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.detail.store.ts @@ -5,12 +5,12 @@ import { UmbStoreBase, UmbContentStore } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; -export const UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbDocumentDetailStore'); +export const UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbMediaDetailStore'); /** * @export - * @class UmbMediaStore + * @class UmbMediaDetailStore * @extends {UmbStoreBase} * @description - Data Store for Media */ diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.tree.store.ts index 7ddff0e003..b5133ddc1a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/media.tree.store.ts @@ -10,7 +10,7 @@ import { UmbControllerHostInterface } from '@umbraco-cms/controller'; export const UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbMediaTreeStore'); // TODO: Stop using ContentTreeItem -type MediaTreeItem = ContentTreeItem; +export type MediaTreeItem = ContentTreeItem; /** * @export diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/workspace/manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/workspace/manifests.ts index 91da78515e..dbdbe34146 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/workspace/manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/workspace/manifests.ts @@ -1,10 +1,10 @@ +import { UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN } from '../media.tree.store'; import type { ManifestWorkspace, ManifestWorkspaceAction, ManifestWorkspaceView, ManifestWorkspaceViewCollection, } from '@umbraco-cms/models'; -import { UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN } from '../media.detail.store'; const workspace: ManifestWorkspace = { type: 'workspace', @@ -59,7 +59,7 @@ const workspaceViewCollections: Array = [ pathname: 'collection', icon: 'umb:grid', entityType: 'media', - storeAlias: UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN.toString(), + storeAlias: UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN.toString(), }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/section.manifests.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/section.manifests.ts index 81c1dbd762..3b5f60f050 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/section.manifests.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/section.manifests.ts @@ -1,4 +1,4 @@ -import { UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN } from './media/media.detail.store'; +import { UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN } from './media/media.tree.store'; import type { ManifestDashboardCollection, ManifestSection } from '@umbraco-cms/models'; const sectionAlias = 'Umb.Section.Media'; @@ -25,7 +25,7 @@ const dashboards: Array = [ sections: [sectionAlias], pathname: 'media-management', entityType: 'media', - storeAlias: UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN.toString(), + storeAlias: UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN.toString(), }, }, ]; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.detail.store.ts index c0797a18e5..0704f2b0b6 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/data-type.detail.store.ts @@ -3,6 +3,8 @@ import { UmbContextToken } from '@umbraco-cms/context-api'; import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; +import { DataTypeResource } from '@umbraco-cms/backend-api'; +import { tryExecuteAndNotify } from '@umbraco-cms/resources'; export const UMB_DATA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbDataTypeDetailStore'); @@ -32,12 +34,13 @@ export class UmbDataTypeDetailStore extends UmbStoreBase { */ getByKey(key: string) { // TODO: use backend cli when available. - fetch(`/umbraco/management/api/v1/document/data-type/${key}`) + fetch(`/umbraco/backoffice/data-type/details/${key}`) .then((res) => res.json()) .then((data) => { this.#data.append(data); }); + return createObservablePart(this.#data, (documents) => documents.find((document) => document.key === key) ); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-grid.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-grid.element.ts index 794cdb6f6a..924b058c34 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-grid.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-grid.element.ts @@ -3,7 +3,7 @@ import { css, html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { repeat } from 'lit/directives/repeat.js'; import { UmbCollectionContext, UMB_COLLECTION_CONTEXT_TOKEN } from '../collection.context'; -import type { MediaDetails } from '@umbraco-cms/models'; +import type { MediaTreeItem } from '../../../media/media/media.tree.store'; import { UmbLitElement } from '@umbraco-cms/element'; @customElement('umb-collection-view-media-grid') @@ -65,12 +65,12 @@ export class UmbCollectionViewsMediaGridElement extends UmbLitElement { ]; @state() - private _mediaItems?: Array; + private _mediaItems?: Array; @state() private _selection?: Array; - private _collectionContext?: UmbCollectionContext; + private _collectionContext?: UmbCollectionContext; constructor() { super(); @@ -115,24 +115,30 @@ export class UmbCollectionViewsMediaGridElement extends UmbLitElement { }); } - private _handleOpenItem(mediaItem: MediaDetails) { + private _handleOpenItem(mediaItem: MediaTreeItem) { //TODO: Fix when we have dynamic routing history.pushState(null, '', 'section/media/media/' + mediaItem.key); } - private _handleSelect(mediaItem: MediaDetails) { - this._collectionContext?.select(mediaItem.key); + private _handleSelect(mediaItem: MediaTreeItem) { + if(mediaItem.key) { + this._collectionContext?.select(mediaItem.key); + } } - private _handleDeselect(mediaItem: MediaDetails) { - this._collectionContext?.deselect(mediaItem.key); + private _handleDeselect(mediaItem: MediaTreeItem) { + if(mediaItem.key) { + this._collectionContext?.deselect(mediaItem.key); + } } - private _isSelected(mediaItem: MediaDetails) { - return this._selection?.includes(mediaItem.key); + private _isSelected(mediaItem: MediaTreeItem) { + if(mediaItem.key) { + return this._selection?.includes(mediaItem.key); + } } - private _renderMediaItem(item: MediaDetails) { + private _renderMediaItem(item: MediaTreeItem) { const name = item.name || ''; //TODO: fix the file extension when media items have a file extension. return html` file.key + index, + (file, index) => (file.key || '') + index, (file) => this._renderMediaItem(file) ) : ''} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice-header-sections.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice-header-sections.element.ts index 86588db2d0..1c696d7216 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice-header-sections.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice-header-sections.element.ts @@ -2,7 +2,8 @@ import { UUITextStyles } from '@umbraco-ui/uui-css/lib'; import { css, CSSResultGroup, html } from 'lit'; import { customElement, state } from 'lit/decorators.js'; import { when } from 'lit/directives/when.js'; -import { UmbSectionContext, UMB_SECTION_CONTEXT_TOKEN } from '../section/section.context'; +import { UMB_BACKOFFICE_CONTEXT_TOKEN } from './backoffice.context'; +import type { UmbBackofficeContext } from './backoffice.context'; import type { ManifestSection } from '@umbraco-cms/models'; import { UmbLitElement } from '@umbraco-cms/element'; @@ -49,13 +50,13 @@ export class UmbBackofficeHeaderSections extends UmbLitElement { @state() private _currentSectionAlias = ''; - private _sectionContext?: UmbSectionContext; + private _backofficeContext?: UmbBackofficeContext; constructor() { super(); - this.consumeContext(UMB_SECTION_CONTEXT_TOKEN, (sectionStore) => { - this._sectionContext = sectionStore; + this.consumeContext(UMB_BACKOFFICE_CONTEXT_TOKEN, (backofficeContext) => { + this._backofficeContext = backofficeContext; this._observeSections(); this._observeCurrentSection(); }); @@ -66,8 +67,8 @@ export class UmbBackofficeHeaderSections extends UmbLitElement { this._open = !this._open; } - private _handleSectionTabClick(sectionManifest: ManifestSection) { - this._sectionContext?.setManifest(sectionManifest); + private _handleSectionTabClick(alias: string) { + this._backofficeContext?.setActiveSectionAlias(alias); } private _handleLabelClick() { @@ -78,18 +79,18 @@ export class UmbBackofficeHeaderSections extends UmbLitElement { } private _observeSections() { - if (!this._sectionContext) return; + if (!this._backofficeContext) return; - this.observe(this._sectionContext.getAllowed(), (allowedSections) => { + this.observe(this._backofficeContext.getAllowedSections(), (allowedSections) => { this._sections = allowedSections; this._visibleSections = this._sections; }); } private _observeCurrentSection() { - if (!this._sectionContext) return; + if (!this._backofficeContext) return; - this.observe(this._sectionContext.alias, (currentSectionAlias) => { + this.observe(this._backofficeContext.activeSectionAlias, (currentSectionAlias) => { this._currentSectionAlias = currentSectionAlias || ''; }); } @@ -100,7 +101,7 @@ export class UmbBackofficeHeaderSections extends UmbLitElement { ${this._visibleSections.map( (section: ManifestSection) => html` = []; private _routePrefix = 'section/'; + private _backofficeContext?: UmbBackofficeContext; private _sectionContext?: UmbSectionContext; constructor() { super(); - this._observeSections(); + this.consumeContext(UMB_BACKOFFICE_CONTEXT_TOKEN, (_instance) => { + this._backofficeContext = _instance; + this._observeBackoffice(); + }); + } - private async _observeSections() { - - this.observe(umbExtensionsRegistry.extensionsOfType('section'), (sections) => { - this._sections = sections; - if (!sections) return; - this._createRoutes(); - }); + private async _observeBackoffice() { + if(this._backofficeContext) { + this.observe(this._backofficeContext.getAllowedSections(), (sections) => { + this._sections = sections; + this._createRoutes(); + }, 'observeAllowedSections'); + } } private _createRoutes() { + if (!this._sections) return; + this._routes = []; this._routes = this._sections.map((section) => { return { @@ -81,6 +88,7 @@ export class UmbBackofficeMain extends UmbLitElement { const currentPath = info.match.route.path; const section = this._sections.find((s) => this._routePrefix + s.meta.pathname === currentPath); if (!section) return; + this._backofficeContext?.setActiveSectionAlias(section.alias); this._provideSectionContext(section); }; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice.context.ts new file mode 100644 index 0000000000..0791270f3d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice.context.ts @@ -0,0 +1,28 @@ +import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry'; +import { UmbContextToken } from '@umbraco-cms/context-api'; +import { StringState } from 'libs/observable-api/string-state'; + +export class UmbBackofficeContext { + + + #activeSectionAlias = new StringState(undefined); + public readonly activeSectionAlias = this.#activeSectionAlias.asObservable(); + + public getAllowedSections() { + // TODO: implemented allowed filtering based on user, maybe this will be a general need and solved else where so this might not be needed in the end. + /* + const { data } = await getUserSections({}); + this._allowedSection = data.sections; + */ + return umbExtensionsRegistry.extensionsOfType('section'); + } + + + public setActiveSectionAlias(alias: string) { + this.#activeSectionAlias.next(alias); + } + +} + + +export const UMB_BACKOFFICE_CONTEXT_TOKEN = new UmbContextToken('UmbBackofficeContext'); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar-menu/section-sidebar-menu.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar-menu/section-sidebar-menu.element.ts index f52c749747..4f00f0afbe 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar-menu/section-sidebar-menu.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar-menu/section-sidebar-menu.element.ts @@ -28,7 +28,8 @@ export class UmbSectionSidebarMenuElement extends UmbLitElement { private _observeCurrentSection() { if (!this._sectionContext) return; - this.observe(this._sectionContext?.alias, (alias) => { + this.observe(this._sectionContext.alias, (alias) => { + console.log("this._sectionContext?.alias", alias) this._currentSectionAlias = alias; }); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts index 0e6468bb13..117ecc6d7f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts @@ -1,5 +1,4 @@ import type { Entity, ManifestSection, ManifestSectionView } from '@umbraco-cms/models'; -import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry'; import { ObjectState } from '@umbraco-cms/observable-api'; import { UmbContextToken } from '@umbraco-cms/context-api'; import { StringState } from 'libs/observable-api/string-state'; @@ -33,15 +32,6 @@ export class UmbSectionContext { this.setManifest(manifest); } - public getAllowed() { - // TODO: implemented allowed filtering - /* - const { data } = await getUserSections({}); - this._allowedSection = data.sections; - */ - return umbExtensionsRegistry.extensionsOfType('section'); - } - public setManifest(manifest?: ManifestSection) { this.#manifestAlias.next(manifest?.alias); this.#manifestPathname.next(manifest?.meta?.pathname); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts index 3f477b8091..4349c462a6 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.element.ts @@ -54,7 +54,7 @@ export class UmbSectionElement extends UmbLitElement { // TODO: currently they don't corporate, as they overwrite each other... this._observeMenuItems(); - this._observeViews(); + this._observeSection(); }); } @@ -89,6 +89,8 @@ export class UmbSectionElement extends UmbLitElement { } private _createMenuRoutes() { + + console.log("_createMenuRoutes") // TODO: find a way to make this reuseable across: const workspaceRoutes = this._workspaces?.map((workspace: ManifestWorkspace) => { return [ @@ -153,14 +155,23 @@ export class UmbSectionElement extends UmbLitElement { } private _observeViews() { + this.observe(umbExtensionsRegistry?.extensionsOfType('sectionView'), (views) => { - this._views = views.filter((view) => this._sectionAlias ? view.meta.sections.includes(this._sectionAlias) : false).sort((a, b) => b.meta.weight - a.meta.weight) - this._createViewRoutes(); + const sectionViews = views.filter((view) => { + return this._sectionAlias ? view.meta.sections.includes(this._sectionAlias) : false + }).sort((a, b) => b.meta.weight - a.meta.weight); + if(sectionViews.length > 0) { + this._views = sectionViews; + this._createViewRoutes(); + } } ); } private _createViewRoutes() { + + console.log("_createViewRoutes") + this._routes = this._views?.map((view) => { return { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/dictionary.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/dictionary.detail.store.ts index 8ad7d063ba..e22c529d0d 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/dictionary.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/dictionary.detail.store.ts @@ -34,7 +34,7 @@ export class UmbDictionaryDetailStore extends UmbStoreBase { */ getByKey(key: string) { // TODO: use backend cli when available. - fetch(`/umbraco/management/api/v1/document/dictionary/${key}`) + fetch(`/umbraco/management/api/v1/dictionary/details/${key}`) .then((res) => res.json()) .then((data) => { this.#data.append(data); From b10c777646a66e1d4bb7c4250bf3903ad2f1789f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 24 Jan 2023 16:30:40 +0100 Subject: [PATCH 09/13] make sure to return a value --- .../collection/views/collection-view-media-grid.element.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-grid.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-grid.element.ts index 924b058c34..a56e2c96ba 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-grid.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-grid.element.ts @@ -136,6 +136,7 @@ export class UmbCollectionViewsMediaGridElement extends UmbLitElement { if(mediaItem.key) { return this._selection?.includes(mediaItem.key); } + return false; } private _renderMediaItem(item: MediaTreeItem) { From 41a778a82aed78e8c7beebaec85d8931ecfd1448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 24 Jan 2023 16:31:40 +0100 Subject: [PATCH 10/13] always have an array for the selection --- .../collection/views/collection-view-media-grid.element.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-grid.element.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-grid.element.ts index a56e2c96ba..682e8b28a7 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-grid.element.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/views/collection-view-media-grid.element.ts @@ -68,7 +68,7 @@ export class UmbCollectionViewsMediaGridElement extends UmbLitElement { private _mediaItems?: Array; @state() - private _selection?: Array; + private _selection: Array = []; private _collectionContext?: UmbCollectionContext; @@ -134,7 +134,7 @@ export class UmbCollectionViewsMediaGridElement extends UmbLitElement { private _isSelected(mediaItem: MediaTreeItem) { if(mediaItem.key) { - return this._selection?.includes(mediaItem.key); + return this._selection.includes(mediaItem.key); } return false; } From 567588cc780fc1bd8e4d05e73b01ae1c79780dd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 24 Jan 2023 16:39:06 +0100 Subject: [PATCH 11/13] fix imports --- src/Umbraco.Web.UI.Client/libs/observable-api/index.ts | 2 ++ .../shared/components/backoffice-frame/backoffice.context.ts | 2 +- .../backoffice/shared/components/section/section.context.ts | 3 +-- .../src/backoffice/users/users/user.store.ts | 3 +-- src/Umbraco.Web.UI.Client/src/installer/installer.context.ts | 5 ++--- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/index.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/index.ts index 4040e0da0f..d823d82739 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/index.ts @@ -1,5 +1,7 @@ export * from './observer.controller'; export * from './observer'; +export * from './number-state'; +export * from './string-state'; export * from './deep-state'; export * from './array-state'; export * from './object-state'; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice.context.ts index 0791270f3d..818ab52d68 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/backoffice-frame/backoffice.context.ts @@ -1,6 +1,6 @@ import { umbExtensionsRegistry } from '@umbraco-cms/extensions-registry'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { StringState } from 'libs/observable-api/string-state'; +import { StringState } from '@umbraco-cms/observable-api'; export class UmbBackofficeContext { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts index 117ecc6d7f..884797ca62 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts @@ -1,7 +1,6 @@ import type { Entity, ManifestSection, ManifestSectionView } from '@umbraco-cms/models'; -import { ObjectState } from '@umbraco-cms/observable-api'; +import { ObjectState, StringState } from '@umbraco-cms/observable-api'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { StringState } from 'libs/observable-api/string-state'; export type ActiveTreeItemType = Entity | undefined; diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/user.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/user.store.ts index eee87afc1c..a34f3106e9 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/users/user.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/users/user.store.ts @@ -1,9 +1,8 @@ import type { UserDetails } from '@umbraco-cms/models'; -import { createObservablePart, ArrayState } from '@umbraco-cms/observable-api'; +import { createObservablePart, ArrayState, NumberState } from '@umbraco-cms/observable-api'; import { UmbContextToken } from '@umbraco-cms/context-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import type { UmbControllerHostInterface } from '@umbraco-cms/controller'; -import { NumberState } from 'libs/observable-api/number-state'; export type UmbUserStoreItemType = UserDetails; diff --git a/src/Umbraco.Web.UI.Client/src/installer/installer.context.ts b/src/Umbraco.Web.UI.Client/src/installer/installer.context.ts index 56994cfdc6..db2e30f505 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/installer.context.ts +++ b/src/Umbraco.Web.UI.Client/src/installer/installer.context.ts @@ -1,9 +1,8 @@ -import { Observable, ReplaySubject } from 'rxjs'; +import { Observable } from 'rxjs'; import { Install, InstallResource, InstallSettings, ProblemDetails, TelemetryLevel } from '@umbraco-cms/backend-api'; import { tryExecute } from '@umbraco-cms/resources'; import { UmbContextToken } from '@umbraco-cms/context-api'; -import { NumberState } from 'libs/observable-api/number-state'; -import { ObjectState } from '@umbraco-cms/observable-api'; +import { ObjectState, NumberState } from '@umbraco-cms/observable-api'; /** * Context API for the installer From 5f40d609d983bb4cbaf2c4c3544176e9fdd681b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 24 Jan 2023 16:47:49 +0100 Subject: [PATCH 12/13] make sure to only deepFreeze actual objects --- src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts index fbf5dbea81..e3235b221c 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts @@ -3,7 +3,7 @@ import { BehaviorSubject } from "rxjs"; // TODO: Should this handle array as well? function deepFreeze(inObj: T): T { - if(typeof inObj === 'object') { + if(inObj && typeof inObj === 'object') { Object.freeze(inObj); Object.getOwnPropertyNames(inObj)?.forEach(function (prop) { From 408bd7bb5dbdbc03e48a1303781bb04c8701cb0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Tue, 24 Jan 2023 16:48:36 +0100 Subject: [PATCH 13/13] use != null --- src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts b/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts index e3235b221c..03f2097033 100644 --- a/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts +++ b/src/Umbraco.Web.UI.Client/libs/observable-api/deep-state.ts @@ -3,7 +3,7 @@ import { BehaviorSubject } from "rxjs"; // TODO: Should this handle array as well? function deepFreeze(inObj: T): T { - if(inObj && typeof inObj === 'object') { + if(inObj != null && typeof inObj === 'object') { Object.freeze(inObj); Object.getOwnPropertyNames(inObj)?.forEach(function (prop) {