diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-registry/models.ts b/src/Umbraco.Web.UI.Client/libs/extensions-registry/models.ts index 387abf6331..d49d57d2bb 100644 --- a/src/Umbraco.Web.UI.Client/libs/extensions-registry/models.ts +++ b/src/Umbraco.Web.UI.Client/libs/extensions-registry/models.ts @@ -24,7 +24,7 @@ import type { ManifestWorkspaceView } from './workspace-view.models'; import type { ManifestWorkspaceViewCollection } from './workspace-view-collection.models'; import type { ManifestRepository } from './repository.models'; import type { ManifestModal } from './modal.models'; -import type { ManifestStore, ManifestTreeStore } from './store.models'; +import type { ManifestStore, ManifestTreeStore, ManifestItemStore } from './store.models'; import type { ClassConstructor } from '@umbraco-cms/backoffice/models'; export * from './collection-view.models'; @@ -89,6 +89,7 @@ export type ManifestTypes = | ManifestModal | ManifestStore | ManifestTreeStore + | ManifestItemStore | ManifestBase; export type ManifestStandardTypes = ManifestTypes['type']; diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-registry/store.models.ts b/src/Umbraco.Web.UI.Client/libs/extensions-registry/store.models.ts index 18e8c10c17..dbca5d6956 100644 --- a/src/Umbraco.Web.UI.Client/libs/extensions-registry/store.models.ts +++ b/src/Umbraco.Web.UI.Client/libs/extensions-registry/store.models.ts @@ -1,5 +1,5 @@ import type { ManifestClass } from './models'; -import { UmbStoreBase, UmbTreeStore } from '@umbraco-cms/backoffice/store'; +import { UmbItemStore, UmbStoreBase, UmbTreeStore } from '@umbraco-cms/backoffice/store'; export interface ManifestStore extends ManifestClass { type: 'store'; @@ -8,3 +8,7 @@ export interface ManifestStore extends ManifestClass { export interface ManifestTreeStore extends ManifestClass { type: 'treeStore'; } + +export interface ManifestItemStore extends ManifestClass { + type: 'itemStore'; +} diff --git a/src/Umbraco.Web.UI.Client/libs/store/index.ts b/src/Umbraco.Web.UI.Client/libs/store/index.ts index f9152d3ebd..a72d304eca 100644 --- a/src/Umbraco.Web.UI.Client/libs/store/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/store/index.ts @@ -3,3 +3,4 @@ export * from './store-base'; export * from './entity-tree-store'; export * from './file-system-tree.store'; export * from './tree-store.interface'; +export * from './item-store.interface'; diff --git a/src/Umbraco.Web.UI.Client/libs/store/item-store.interface.ts b/src/Umbraco.Web.UI.Client/libs/store/item-store.interface.ts index d5d8e3d124..a68e83b5c1 100644 --- a/src/Umbraco.Web.UI.Client/libs/store/item-store.interface.ts +++ b/src/Umbraco.Web.UI.Client/libs/store/item-store.interface.ts @@ -2,6 +2,6 @@ import type { Observable } from 'rxjs'; import { ItemResponseModelBaseModel } from '../backend-api'; import { UmbStore } from './store.interface'; -export interface UmbItemStore extends UmbStore { +export interface UmbItemStore extends UmbStore { items: (uniques: Array) => Observable>; } diff --git a/src/Umbraco.Web.UI.Client/libs/store/store-base.ts b/src/Umbraco.Web.UI.Client/libs/store/store-base.ts index d1a454c1ee..5092c7bba2 100644 --- a/src/Umbraco.Web.UI.Client/libs/store/store-base.ts +++ b/src/Umbraco.Web.UI.Client/libs/store/store-base.ts @@ -1,7 +1,7 @@ import { UmbStore } from './store.interface'; import { UmbContextProviderController } from '@umbraco-cms/backoffice/context-api'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; -import { ArrayState, partialUpdateFrozenArray } from '@umbraco-cms/backoffice/observable-api'; +import { ArrayState } from '@umbraco-cms/backoffice/observable-api'; // TODO: Make a Store interface? export class UmbStoreBase implements UmbStore { @@ -33,8 +33,8 @@ export class UmbStoreBase implements UmbStore} data * @memberof UmbEntityTreeStore */ - updateItem(id: string, data: Partial) { - this._data.next(partialUpdateFrozenArray(this._data.getValue(), data, (entry) => entry.id === id)); + updateItem(unique: string, data: Partial) { + this._data.updateOne(unique, data); } /** @@ -42,7 +42,7 @@ export class UmbStoreBase implements UmbStore([], (x) => x.id); - /** * Creates an instance of UmbDocumentTypeStore. * @param {UmbControllerHostElement} host * @memberof UmbDocumentTypeStore */ constructor(host: UmbControllerHostElement) { - super(host, UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN.toString()); + super( + host, + UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN.toString(), + new ArrayState([], (x) => x.id) + ); } /** @@ -28,7 +30,7 @@ export class UmbDocumentTypeStore extends UmbStoreBase { * @memberof UmbDocumentTypeStore */ append(document: DocumentTypeResponseModel) { - this.#data.append([document]); + this._data.append([document]); } /** @@ -37,7 +39,7 @@ export class UmbDocumentTypeStore extends UmbStoreBase { * @memberof UmbDocumentTypeStore */ byId(id: DocumentTypeResponseModel['id']) { - return this.#data.getObservablePart((x) => x.find((y) => y.id === id)); + return this._data.getObservablePart((x) => x.find((y) => y.id === id)); } /** @@ -46,7 +48,7 @@ export class UmbDocumentTypeStore extends UmbStoreBase { * @memberof UmbDocumentTypeStore */ remove(uniques: Array) { - this.#data.remove(uniques); + this._data.remove(uniques); } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.store.ts index d53818f95e..ed2fb34fb2 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.store.ts @@ -11,15 +11,13 @@ import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; * @description - Data Store for Template Details */ export class UmbDocumentStore extends UmbStoreBase { - #data = new ArrayState([], (x) => x.id); - /** * Creates an instance of UmbDocumentDetailStore. * @param {UmbControllerHostElement} host * @memberof UmbDocumentDetailStore */ constructor(host: UmbControllerHostElement) { - super(host, UMB_DOCUMENT_STORE_CONTEXT_TOKEN.toString()); + super(host, UMB_DOCUMENT_STORE_CONTEXT_TOKEN.toString(), new ArrayState([], (x) => x.id)); } /** @@ -28,7 +26,7 @@ export class UmbDocumentStore extends UmbStoreBase { * @memberof UmbDocumentDetailStore */ append(document: DocumentResponseModel) { - this.#data.append([document]); + this._data.append([document]); } /** @@ -37,7 +35,7 @@ export class UmbDocumentStore extends UmbStoreBase { * @memberof UmbDocumentStore */ byKey(id: DocumentResponseModel['id']) { - return this.#data.getObservablePart((x) => x.find((y) => y.id === id)); + return this._data.getObservablePart((x) => x.find((y) => y.id === id)); } /** @@ -46,7 +44,7 @@ export class UmbDocumentStore extends UmbStoreBase { * @memberof UmbDocumentDetailStore */ remove(uniques: Array) { - this.#data.remove(uniques); + this._data.remove(uniques); } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/media-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/media-type.detail.store.ts index d6ef9c0fa9..555d7fa0f3 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/media-type.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/media-type.detail.store.ts @@ -11,18 +11,16 @@ import type { MediaTypeDetails } from '@umbraco-cms/backoffice/models'; * @description - Details Data Store for Media Types */ export class UmbMediaTypeStore extends UmbStoreBase { - #data = new ArrayState([], (x) => x.id); - constructor(host: UmbControllerHostElement) { - super(host, UMB_MEDIA_TYPE_STORE_CONTEXT_TOKEN.toString()); + super(host, UMB_MEDIA_TYPE_STORE_CONTEXT_TOKEN.toString(), new ArrayState([], (x) => x.id)); } append(mediaType: MediaTypeDetails) { - this.#data.append([mediaType]); + this._data.append([mediaType]); } remove(uniques: string[]) { - this.#data.remove(uniques); + this._data.remove(uniques); } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.store.ts index ddc1a8b5fc..dff90e3885 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.store.ts @@ -11,15 +11,13 @@ import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; * @description - Data Store for Template Details */ export class UmbMediaStore extends UmbStoreBase { - #data = new ArrayState([], (x) => x.id); - /** * Creates an instance of UmbMediaStore. * @param {UmbControllerHostElement} host * @memberof UmbMediaStore */ constructor(host: UmbControllerHostElement) { - super(host, UMB_MEDIA_STORE_CONTEXT_TOKEN.toString()); + super(host, UMB_MEDIA_STORE_CONTEXT_TOKEN.toString(), new ArrayState([], (x) => x.id)); } /** @@ -28,7 +26,7 @@ export class UmbMediaStore extends UmbStoreBase { * @memberof UmbMediaStore */ append(media: MediaDetails) { - this.#data.append([media]); + this._data.append([media]); } /** @@ -37,7 +35,7 @@ export class UmbMediaStore extends UmbStoreBase { * @memberof UmbMediaStore */ remove(uniques: string[]) { - this.#data.remove(uniques); + this._data.remove(uniques); } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/member-group.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/member-group.store.ts index 38fd8ede11..30ed7f8785 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/member-group.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/member-group.store.ts @@ -11,18 +11,16 @@ import { UmbStoreBase } from '@umbraco-cms/backoffice/store'; * @description - Data Store for Member Groups */ export class UmbMemberGroupStore extends UmbStoreBase { - #data = new ArrayState([], (x) => x.id); - constructor(host: UmbControllerHostElement) { - super(host, UMB_MEMBER_GROUP_STORE_CONTEXT_TOKEN.toString()); + super(host, UMB_MEMBER_GROUP_STORE_CONTEXT_TOKEN.toString(), new ArrayState([], (x) => x.id)); } append(memberGroup: MemberGroupDetails) { - this.#data.append([memberGroup]); + this._data.append([memberGroup]); } remove(uniques: string[]) { - this.#data.remove(uniques); + this._data.remove(uniques); } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/member-type.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/member-type.store.ts index b9afd506a2..5d0bf4bcfd 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/member-type.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/member-type.store.ts @@ -11,18 +11,16 @@ import type { MemberTypeDetails } from '@umbraco-cms/backoffice/models'; * @description - Data Store for Member Types */ export class UmbMemberTypeStore extends UmbStoreBase { - #data = new ArrayState([], (x) => x.id); - constructor(host: UmbControllerHostElement) { - super(host, UMB_MEMBER_TYPE_STORE_CONTEXT_TOKEN.toString()); + super(host, UMB_MEMBER_TYPE_STORE_CONTEXT_TOKEN.toString(), new ArrayState([], (x) => x.id)); } append(MemberType: MemberTypeDetails) { - this.#data.append([MemberType]); + this._data.append([MemberType]); } remove(uniques: string[]) { - this.#data.remove(uniques); + this._data.remove(uniques); } } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/package.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/package.store.ts index 8ed4792471..3eeacc0b15 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/package.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/package.store.ts @@ -42,7 +42,9 @@ export class UmbPackageStore extends UmbStoreBase { * @memberof PackageStore */ constructor(host: UmbControllerHostElement) { - super(host, UMB_PACKAGE_STORE_TOKEN.toString()); + // TODO: revisit this store. Is it ok to have multiple data sets? + // temp hack to satisfy the base class + super(host, UMB_PACKAGE_STORE_TOKEN.toString(), new ArrayState([], (x) => x.name)); } /** diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/language-item.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/language-item.store.ts new file mode 100644 index 0000000000..5c1645003d --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/language-item.store.ts @@ -0,0 +1,41 @@ +import { UmbContextToken } from '@umbraco-cms/backoffice/context-api'; +import { UmbStoreBase } from '@umbraco-cms/backoffice/store'; +import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; +import { ArrayState, partialUpdateFrozenArray } from '@umbraco-cms/backoffice/observable-api'; +import { LanguageResponseModel } from '@umbraco-cms/backoffice/backend-api'; +import type { UmbItemStore } from '@umbraco-cms/backoffice/store'; + +export const UMB_LANGUAGE_ITEM_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbLanguageItemStore'); + +/** + * @export + * @class UmbLanguageItemStore + * @extends {UmbStoreBase} + * @description - Store for Languages items + */ +export class UmbLanguageItemStore + extends UmbStoreBase + implements UmbItemStore +{ + constructor(host: UmbControllerHostElement) { + super( + host, + UMB_LANGUAGE_ITEM_STORE_CONTEXT_TOKEN.toString(), + new ArrayState([], (x) => x.isoCode) + ); + } + + /** + * Updates an item in the store + * @param {string} isoCode + * @param {Partial} data + * @memberof UmbLanguageItemStore + */ + updateItem(isoCode: string, data: Partial) { + this._data.next(partialUpdateFrozenArray(this._data.getValue(), data, (entry) => entry.isoCode === isoCode)); + } + + items(isoCodes: Array) { + return this._data.getObservablePart((items) => items.filter((item) => isoCodes.includes(item.isoCode ?? ''))); + } +} diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/language.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/language.repository.ts index d37dada762..cd854018d4 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/language.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/language.repository.ts @@ -1,17 +1,26 @@ import { UmbLanguageServerDataSource } from './sources/language.server.data'; import { UmbLanguageStore, UMB_LANGUAGE_STORE_CONTEXT_TOKEN } from './language.store'; +import { UmbLanguageItemServerDataSource } from './sources/language-item.server.data'; +import { UMB_LANGUAGE_ITEM_STORE_CONTEXT_TOKEN, UmbLanguageItemStore } from './language-item.store'; import { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller'; import { UmbContextConsumerController } from '@umbraco-cms/backoffice/context-api'; import { UmbNotificationContext, UMB_NOTIFICATION_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/notification'; -import { LanguageResponseModel, ProblemDetailsModel } from '@umbraco-cms/backoffice/backend-api'; +import { + LanguageItemResponseModel, + LanguageResponseModel, + ProblemDetailsModel, +} from '@umbraco-cms/backoffice/backend-api'; +import { UmbItemRepository } from '@umbraco-cms/backoffice/repository'; -export class UmbLanguageRepository { - #init!: Promise; +export class UmbLanguageRepository implements UmbItemRepository { + #init: Promise; #host: UmbControllerHostElement; #dataSource: UmbLanguageServerDataSource; + #itemDataSource: UmbLanguageItemServerDataSource; #languageStore?: UmbLanguageStore; + #languageItemStore?: UmbLanguageItemStore; #notificationContext?: UmbNotificationContext; @@ -19,15 +28,21 @@ export class UmbLanguageRepository { this.#host = host; this.#dataSource = new UmbLanguageServerDataSource(this.#host); + this.#itemDataSource = new UmbLanguageItemServerDataSource(this.#host); this.#init = Promise.all([ new UmbContextConsumerController(this.#host, UMB_NOTIFICATION_CONTEXT_TOKEN, (instance) => { this.#notificationContext = instance; - }), + }).asPromise(), new UmbContextConsumerController(this.#host, UMB_LANGUAGE_STORE_CONTEXT_TOKEN, (instance) => { this.#languageStore = instance; }).asPromise(), + + new UmbContextConsumerController(this.#host, UMB_LANGUAGE_ITEM_STORE_CONTEXT_TOKEN, (instance) => { + this.#languageItemStore = instance; + debugger; + }).asPromise(), ]); } @@ -59,19 +74,19 @@ export class UmbLanguageRepository { } async requestItems(isoCodes: Array) { - // HACK: filter client side until we have a proper server side endpoint - // TODO: we will get a different size model here, how do we handle that in the store? - const { data, error } = await this.requestLanguages(); - - let items = undefined; + await this.#init; + const { data, error } = await this.#itemDataSource.getItems(isoCodes); if (data) { - // TODO: how do we best handle this? They might have a smaller data set than the details - items = data.items = data.items.filter((x) => isoCodes.includes(x.isoCode!)); - data.items.forEach((x) => this.#languageStore?.append(x)); + this.#languageItemStore?.appendItems(data); } - return { data: items, error, asObservable: () => this.#languageStore!.items(isoCodes) }; + return { data, error, asObservable: () => this.#languageItemStore!.items(isoCodes) }; + } + + async items(isoCodes: Array) { + await this.#init; + return this.#languageItemStore!.items(isoCodes); } /** diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/language.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/language.store.ts index cba0758559..8cb8c55a0a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/language.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/language.store.ts @@ -13,6 +13,8 @@ export const UMB_LANGUAGE_STORE_CONTEXT_TOKEN = new UmbContextToken