This commit is contained in:
Niels Lyngsø
2024-02-26 19:29:33 +01:00
parent cacc4804a0
commit 0bb38b5729
22 changed files with 301 additions and 337 deletions

View File

@@ -8,11 +8,11 @@ import { UmbArrayState, UmbBooleanState } from '@umbraco-cms/backoffice/observab
* @export
* @class UmbSelectionManager
*/
export class UmbSelectionManager extends UmbBaseController {
export class UmbSelectionManager<ValueType = string | null> extends UmbBaseController {
#selectable = new UmbBooleanState(false);
public readonly selectable = this.#selectable.asObservable();
#selection = new UmbArrayState(<Array<string | null>>[], (x) => x);
#selection = new UmbArrayState(<Array<ValueType>>[], (x) => x);
public readonly selection = this.#selection.asObservable();
#multiple = new UmbBooleanState(false);
@@ -51,10 +51,10 @@ export class UmbSelectionManager extends UmbBaseController {
/**
* Sets the current selection.
* @param {Array<string | null>} value
* @param {Array<ValueType>} value
* @memberof UmbSelectionManager
*/
public setSelection(value: Array<string | null>) {
public setSelection(value: Array<ValueType>) {
if (this.getSelectable() === false) return;
if (value === undefined) throw new Error('Value cannot be undefined');
const newSelection = this.getMultiple() ? value : value.slice(0, 1);
@@ -87,20 +87,20 @@ export class UmbSelectionManager extends UmbBaseController {
/**
* Toggles the given unique id in the current selection.
* @param {(string | null)} unique
* @param {(ValueType)} unique
* @memberof UmbSelectionManager
*/
public toggleSelect(unique: string | null) {
public toggleSelect(unique: ValueType) {
if (this.getSelectable() === false) return;
this.isSelected(unique) ? this.deselect(unique) : this.select(unique);
}
/**
* Appends the given unique id to the current selection.
* @param {(string | null)} unique
* @param {(ValueType)} unique
* @memberof UmbSelectionManager
*/
public select(unique: string | null) {
public select(unique: ValueType) {
if (this.getSelectable() === false) return;
if (this.isSelected(unique)) return;
const newSelection = this.getMultiple() ? [...this.getSelection(), unique] : [unique];
@@ -110,10 +110,10 @@ export class UmbSelectionManager extends UmbBaseController {
/**
* Removes the given unique id from the current selection.
* @param {(string | null)} unique
* @param {(ValueType)} unique
* @memberof UmbSelectionManager
*/
public deselect(unique: string | null) {
public deselect(unique: ValueType) {
if (this.getSelectable() === false) return;
const newSelection = this.getSelection().filter((x) => x !== unique);
this.#selection.setValue(newSelection);
@@ -122,11 +122,11 @@ export class UmbSelectionManager extends UmbBaseController {
/**
* Returns true if the given unique id is selected.
* @param {(string | null)} unique
* @param {(ValueType)} unique
* @return {*}
* @memberof UmbSelectionManager
*/
public isSelected(unique: string | null) {
public isSelected(unique: ValueType) {
return this.getSelection().includes(unique);
}

View File

@@ -11,4 +11,8 @@ export interface UmbVariantModel {
export interface UmbVariantOptionModel<VariantType extends UmbVariantModel = UmbVariantModel> {
variant?: VariantType;
language: UmbLanguageDetailModel;
/**
* The unique identifier is a VariantId string.
*/
unique: string;
}

View File

@@ -10,6 +10,10 @@ export function variantPropertiesObjectToString(variant: UmbObjectWithVariantPro
export const UMB_INVARIANT_CULTURE = 'invariant';
/**
* An identifier representing a Variant. This is at current state a culture and a segment.
* The identifier is not specific for ContentType Variants, but is used for many type of identification of a culture and a segment. One case is any property of a ContentType can be resolved into a VariantId depending on their structural settings such as Vary by Culture and Vary by Segmentation.
*/
export class UmbVariantId {
public static Create(variantData: UmbObjectWithVariantProperties): UmbVariantId {
return Object.freeze(new UmbVariantId(variantData.culture, variantData.segment));
@@ -30,7 +34,7 @@ export class UmbVariantId {
public readonly segment: string | null = null;
public readonly schedule: { publishTime?: string | null; unpublishTime?: string | null } | null = null;
constructor(culture: string | null, segment: string | null) {
constructor(culture?: string | null, segment?: string | null) {
this.culture = (culture === UMB_INVARIANT_CULTURE ? null : culture?.toLowerCase()) ?? null;
this.segment = segment ?? null;
}

View File

@@ -5,15 +5,12 @@ import {
UUIInputEvent,
type UUIPopoverContainerElement,
} from '@umbraco-cms/backoffice/external/uui';
import { css, html, nothing, customElement, state, ifDefined, query } from '@umbraco-cms/backoffice/external/lit';
import { css, html, nothing, customElement, state, query } from '@umbraco-cms/backoffice/external/lit';
import { UMB_WORKSPACE_SPLIT_VIEW_CONTEXT, type ActiveVariant } from '@umbraco-cms/backoffice/workspace';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { DocumentVariantStateModel } from '@umbraco-cms/backoffice/external/backend-api';
import type { UmbDocumentVariantModel } from '@umbraco-cms/backoffice/document';
import { UmbLanguageCollectionRepository, type UmbLanguageDetailModel } from '@umbraco-cms/backoffice/language';
import { UmbArrayState, combineObservables } from '@umbraco-cms/backoffice/observable-api';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
import type { UmbDocumentWorkspaceContext } from '@umbraco-cms/backoffice/document';
type UmbDocumentVariantOption = {
culture: string | null;
@@ -38,9 +35,7 @@ export class UmbVariantSelectorElement extends UmbLitElement {
_activeVariants: Array<ActiveVariant> = [];
@state()
get _activeVariantsCultures(): string[] {
return this._activeVariants.map((el) => el.culture ?? '') ?? [];
}
_activeVariantsCultures: string[] = [];
#splitViewContext?: typeof UMB_WORKSPACE_SPLIT_VIEW_CONTEXT.TYPE;
#datasetContext?: typeof UMB_PROPERTY_DATASET_CONTEXT.TYPE;
@@ -57,9 +52,6 @@ export class UmbVariantSelectorElement extends UmbLitElement {
@state()
private _variantSelectorOpen = false;
#languageRepository = new UmbLanguageCollectionRepository(this);
#languages = new UmbArrayState<UmbLanguageDetailModel>([], (x) => x.unique);
constructor() {
super();
@@ -67,37 +59,36 @@ export class UmbVariantSelectorElement extends UmbLitElement {
this.#splitViewContext = instance;
this.#observeVariants();
this.#observeActiveVariants();
this.#observeCurrentVariant();
});
this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, (instance) => {
this.#datasetContext = instance;
this.#observeDatasetContext();
this.#observeCurrentVariant();
});
this.#loadLanguages();
}
async #loadLanguages() {
const { data: languages } = await this.#languageRepository.requestCollection({});
if (!languages) return;
this.#languages.setValue(languages.items);
}
async #observeVariants() {
if (!this.#splitViewContext) return;
const workspaceContext = this.#splitViewContext.getWorkspaceContext();
// NOTICE: This is dirty (the TypeScript casting), we can only accept doing this so far because we currently only use the Variant Selector on Document Workspace. [NL]
// This would need a refactor to enable the code below to work with different ContentTypes. Main problem here is the state, which is not generic for them all. [NL]
const workspaceContext = this.#splitViewContext.getWorkspaceContext() as UmbDocumentWorkspaceContext;
if (!workspaceContext) throw new Error('Split View Workspace context not found');
this.observe(
workspaceContext.allowedVariants,
(variants) => {
this._variants = variants.map<UmbDocumentVariantOption>((variant) => {
workspaceContext.variantOptions,
(options) => {
this._variants = options.map<UmbDocumentVariantOption>((option) => {
const name = option.variant?.name ?? option.language.name;
const segment = option.variant?.segment ?? null;
return {
culture: variant.culture,
segment: variant.segment,
title: variant.name + (variant.segment ? `${variant.segment}` : ''),
displayName: variant.name + (variant.segment ? `${variant.segment}` : ''),
state: (variant as UmbDocumentVariantModel).state ?? DocumentVariantStateModel.NOT_CREATED,
// Notice the option object has a unique property, but it's not used here. (Its equivalent to a UmbVariantId string) [NL]
culture: option.language.unique,
segment: segment,
title: name + (segment ? `${segment}` : ''),
displayName: name + (segment ? `${segment}` : ''),
state: option.variant?.state ?? DocumentVariantStateModel.NOT_CREATED,
};
});
},
@@ -115,6 +106,7 @@ export class UmbVariantSelectorElement extends UmbLitElement {
(activeVariants) => {
if (activeVariants) {
this._activeVariants = activeVariants;
this._activeVariantsCultures = this._activeVariants.map((el) => el.culture ?? '') ?? [];
}
},
'_observeActiveVariants',
@@ -124,24 +116,6 @@ export class UmbVariantSelectorElement extends UmbLitElement {
async #observeDatasetContext() {
if (!this.#datasetContext) return;
const variantId = this.#datasetContext.getVariantId();
const culture = variantId.culture;
const segment = variantId.segment;
this.observe(
this.#languages.asObservablePart(
(languages) => languages.find((language) => language.unique === variantId.culture)?.name ?? '',
),
(languageName) => {
this._variantDisplayName = (languageName ? languageName : '') + (segment ? ' — ' + segment : '');
this._variantTitleName =
(languageName ? `${languageName} (${culture})` : '') + (segment ? ' — ' + segment : '');
},
'_languages',
);
this.observe(
this.#datasetContext.name,
(name) => {
@@ -151,6 +125,30 @@ export class UmbVariantSelectorElement extends UmbLitElement {
);
}
async #observeCurrentVariant() {
if (!this.#datasetContext || !this.#splitViewContext) return;
const workspaceContext = this.#splitViewContext.getWorkspaceContext();
if (!workspaceContext) return;
const variantId = this.#datasetContext.getVariantId();
// Find the variant option matching this, to get the language name...
const culture = variantId.culture;
const segment = variantId.segment;
this.observe(
workspaceContext.variantOptions,
(options) => {
const option = options.find((option) => option.language.unique === culture);
const languageName = option?.language.name;
this._variantDisplayName = (languageName ? languageName : '') + (segment ? `${segment}` : '');
this._variantTitleName =
(languageName ? `${languageName} (${culture})` : '') + (segment ? `${segment}` : '');
},
'_currentLanguage',
);
}
#handleInput(event: UUIInputEvent) {
if (event instanceof UUIInputEvent) {
const target = event.composedPath()[0] as UUIInputElement;

View File

@@ -2,17 +2,18 @@ import type { UmbWorkspaceSplitViewManager } from '../workspace-split-view-manag
import type { UmbPropertyDatasetContext } from '../../property/property-dataset/property-dataset-context.interface.js';
import type { UmbSaveableWorkspaceContextInterface } from './saveable-workspace-context.interface.js';
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
import type { UmbVariantId, UmbVariantModel } from '@umbraco-cms/backoffice/variant';
import type { UmbVariantId, UmbVariantModel, UmbVariantOptionModel } from '@umbraco-cms/backoffice/variant';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
export interface UmbVariantableWorkspaceContextInterface extends UmbSaveableWorkspaceContextInterface {
export interface UmbVariantableWorkspaceContextInterface<VariantType extends UmbVariantModel = UmbVariantModel>
extends UmbSaveableWorkspaceContextInterface {
// Name:
getName(variantId?: UmbVariantId): string | undefined;
setName(name: string, variantId?: UmbVariantId): void;
// Variant:
variants: Observable<Array<UmbVariantModel>>;
allowedVariants: Observable<Array<UmbVariantModel>>;
variantOptions: Observable<Array<UmbVariantOptionModel<VariantType>>>;
splitView: UmbWorkspaceSplitViewManager;
getVariant(variantId: UmbVariantId): UmbVariantModel | undefined;

View File

@@ -70,7 +70,7 @@ export class UmbWorkspaceSplitViewManager {
const currentVariant = this.getActiveVariants()[0];
const workspaceRoute = this.getWorkspaceRoute();
if (currentVariant && workspaceRoute) {
history.pushState(null, '', `${workspaceRoute}/${new UmbVariantId(currentVariant)}_&_${newVariant.toString()}`);
history.pushState(null, '', `${workspaceRoute}/${UmbVariantId.Create(currentVariant)}_&_${newVariant}`);
return true;
}
return false;
@@ -83,7 +83,7 @@ export class UmbWorkspaceSplitViewManager {
if (activeVariants && index < activeVariants.length) {
const newVariants = activeVariants.filter((x) => x.index !== index);
const variantPart: string = newVariants.map((v) => new UmbVariantId(v).toString()).join('_&_');
const variantPart: string = newVariants.map((v) => UmbVariantId.Create(v)).join('_&_');
history.pushState(null, '', `${workspaceRoute}/${variantPart}`);
return true;

View File

@@ -1,21 +1,10 @@
import { UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT } from '../global-contexts/index.js';
import type { UmbDocumentPublishingRepository } from '../repository/index.js';
import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action';
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
export class UmbPublishDocumentEntityAction extends UmbEntityActionBase<UmbDocumentPublishingRepository> {
#variantManagerContext?: typeof UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT.TYPE;
constructor(host: UmbControllerHostElement, repositoryAlias: string, unique: string, entityType: string) {
super(host, repositoryAlias, unique, entityType);
this.consumeContext(UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT, (context) => {
this.#variantManagerContext = context;
});
}
async execute() {
if (!this.#variantManagerContext) throw new Error('Variant manager context is missing');
await this.#variantManagerContext.publish(this.unique);
throw new Error('This action not implemented.');
//if (!this.#variantManagerContext) throw new Error('Variant manager context is missing');
//await this.#variantManagerContext.publish(this.unique);
}
}

View File

@@ -1,21 +1,10 @@
import { UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT } from '../global-contexts/index.js';
import type { UmbDocumentPublishingRepository } from '../repository/index.js';
import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action';
import type { UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
export class UmbUnpublishDocumentEntityAction extends UmbEntityActionBase<UmbDocumentPublishingRepository> {
#variantManagerContext?: typeof UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT.TYPE;
constructor(host: UmbControllerHostElement, repositoryAlias: string, unique: string, entityType: string) {
super(host, repositoryAlias, unique, entityType);
this.consumeContext(UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT, (context) => {
this.#variantManagerContext = context;
});
}
async execute() {
if (!this.#variantManagerContext) throw new Error('Variant manager context is missing');
await this.#variantManagerContext.unpublish(this.unique);
throw new Error('This action not implemented.');
//if (!this.#variantManagerContext) throw new Error('Variant manager context is missing');
//await this.#variantManagerContext.unpublish(this.unique);
}
}

View File

@@ -1,4 +1,4 @@
import { UmbDocumentVariantState, type UmbDocumentVariantModel } from '../types.js';
import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../types.js';
import { UmbDocumentDetailRepository } from '../repository/detail/document-detail.repository.js';
import {
UMB_DOCUMENT_LANGUAGE_PICKER_MODAL,
@@ -47,9 +47,9 @@ export class UmbDocumentVariantManagerContext
* @returns The selected variants to perform the operation on.
*/
async pickVariants(
availableVariants: Array<UmbDocumentVariantModel>,
type: UmbDocumentVariantPickerModalData['type'],
activeVariantCultures?: Array<string>,
availableVariants: Array<UmbDocumentVariantOptionModel>,
activeVariantCultures?: Array<UmbVariantId>,
): Promise<UmbVariantId[]> {
// If there is only one variant, we don't need to select anything.
if (availableVariants.length === 1) {
@@ -60,7 +60,7 @@ export class UmbDocumentVariantManagerContext
const modalData: UmbDocumentVariantPickerModalData = {
type,
variants: availableVariants,
options: availableVariants,
};
const modalContext = this.#modalManagerContext.open(UMB_DOCUMENT_LANGUAGE_PICKER_MODAL, {
@@ -75,6 +75,7 @@ export class UmbDocumentVariantManagerContext
const selectedVariants = result.selection.map((x) => x?.toLowerCase() ?? '');
// Match the result to the available variants.
// Why would this be needed if the modal is only showing available variants? [NL]
const variantIds = availableVariants
.filter((x) => selectedVariants.includes(x.culture!))
.map((x) => UmbVariantId.Create(x));
@@ -90,8 +91,8 @@ export class UmbDocumentVariantManagerContext
const { data } = await this.#documentRepository.requestByUnique(documentUnique);
if (!data) throw new Error('Document not found');
const variantIds = await this.pickVariants(
data.variants,
'publish',
data.variants,
this.#appLanguageCulture ? [this.#appLanguageCulture] : undefined,
);
if (variantIds.length) {
@@ -111,8 +112,8 @@ export class UmbDocumentVariantManagerContext
const variants = data.variants.filter((variant) => variant.state === UmbDocumentVariantState.PUBLISHED);
const variantIds = await this.pickVariants(
variants,
'unpublish',
variants,
this.#appLanguageCulture ? [this.#appLanguageCulture] : undefined,
);

View File

@@ -1 +0,0 @@
export * from './document-variant-manager.context.js';

View File

@@ -1,10 +0,0 @@
import type { ManifestGlobalContext } from '@umbraco-cms/backoffice/extension-registry';
export const manifests: Array<ManifestGlobalContext> = [
{
type: 'globalContext',
alias: 'Umb.GlobalContext.DocumentVariantManager',
name: 'Document Variant Manager Context',
js: () => import('./document-variant-manager.context.js'),
},
];

View File

@@ -12,7 +12,6 @@ import { manifests as modalManifests } from './modals/manifests.js';
import { manifests as treeManifests } from './tree/manifests.js';
import { manifests as userPermissionManifests } from './user-permissions/manifests.js';
import { manifests as workspaceManifests } from './workspace/manifests.js';
import { manifests as globalContextManifests } from './global-contexts/manifests.js';
export const manifests = [
...breadcrumbManifests,
@@ -29,5 +28,4 @@ export const manifests = [
...treeManifests,
...userPermissionManifests,
...workspaceManifests,
...globalContextManifests,
];

View File

@@ -1,10 +1,10 @@
import { type UmbDocumentVariantModel, UmbDocumentVariantState } from '../../types.js';
import { UmbDocumentVariantState, type UmbDocumentVariantOptionModel } from '../../types.js';
import type {
UmbDocumentVariantPickerModalValue,
UmbDocumentVariantPickerModalData,
} from './document-variant-picker-modal.token.js';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, html, customElement, repeat } from '@umbraco-cms/backoffice/external/lit';
import { css, html, customElement, repeat, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbSelectionManager } from '@umbraco-cms/backoffice/utils';
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
@@ -13,7 +13,17 @@ export class UmbDocumentVariantPickerModalElement extends UmbModalBaseElement<
UmbDocumentVariantPickerModalData,
UmbDocumentVariantPickerModalValue
> {
#selectionManager = new UmbSelectionManager(this);
#selectionManager = new UmbSelectionManager<string>(this);
@state()
_selection: Array<string> = [];
constructor() {
super();
this.observe(this.#selectionManager.selection, (selection) => {
this._selection = selection;
});
}
connectedCallback(): void {
super.connectedCallback();
@@ -21,16 +31,18 @@ export class UmbDocumentVariantPickerModalElement extends UmbModalBaseElement<
this.#selectionManager.setMultiple(true);
// Make sure all mandatory variants are selected when not in unpublish mode
// TODO: Currently only supports culture variants, not segment variants, but as well our Selection Manager also only supports a single string value pr. selection... [NL]
this.#selectionManager.setSelection(this.value?.selection ?? []);
if (this.data?.type !== 'unpublish') {
this.#selectMandatoryVariants();
}
}
#selectMandatoryVariants() {
this.data?.variants.forEach((variant) => {
if (variant.isMandatory) {
this.#selectionManager.select(variant.culture);
this.data?.options.forEach((variant) => {
if (variant.language?.isMandatory) {
this.#selectionManager.select(variant.unique);
}
});
}
@@ -87,17 +99,17 @@ export class UmbDocumentVariantPickerModalElement extends UmbModalBaseElement<
return html`<umb-body-layout headline=${this.localize.term(this.#headline)}>
<p id="subtitle">${this.localize.term(this.#subtitle)}</p>
${repeat(
this.data?.variants ?? [],
(item) => item.culture,
(item) => html`
this.data?.options ?? [],
(option) => option.unique,
(option) => html`
<uui-menu-item
selectable
label=${item.name}
@selected=${() => this.#selectionManager.select(item.culture)}
@deselected=${() => this.#selectionManager.deselect(item.culture)}
?selected=${this.#selectionManager.isSelected(item.culture)}>
label=${option.variant?.name ?? option.language.name}
@selected=${() => this.#selectionManager.select(option.unique)}
@deselected=${() => this.#selectionManager.deselect(option.unique)}
?selected=${this._selection.includes(option.language.unique)}>
<uui-icon slot="icon" name="icon-globe"></uui-icon>
${this.#renderLabel(item)}
${this.#renderLabel(option)}
</uui-menu-item>
`,
)}
@@ -114,11 +126,14 @@ export class UmbDocumentVariantPickerModalElement extends UmbModalBaseElement<
</umb-body-layout> `;
}
#renderLabel(variant: UmbDocumentVariantModel) {
#renderLabel(option: UmbDocumentVariantOptionModel) {
return html`<div class="label" slot="label">
<strong>${variant.segment ? variant.segment + ' - ' : ''}${variant.name}</strong>
<div class="label-status">${this.#renderVariantStatus(variant)}</div>
${variant.isMandatory && variant.state !== UmbDocumentVariantState.PUBLISHED
<strong
>${option.variant?.segment ? option.variant.segment + ' - ' : ''}${option.variant?.name ??
option.language.name}</strong
>
<div class="label-status">${this.#renderVariantStatus(option)}</div>
${option.language.isMandatory && option.variant?.state !== UmbDocumentVariantState.PUBLISHED
? html`<div class="label-status">
<umb-localize key="languages_mandatoryLanguage">Mandatory language</umb-localize>
</div>`
@@ -126,16 +141,17 @@ export class UmbDocumentVariantPickerModalElement extends UmbModalBaseElement<
</div>`;
}
#renderVariantStatus(variant: UmbDocumentVariantModel) {
switch (variant.state) {
#renderVariantStatus(option: UmbDocumentVariantOptionModel) {
switch (option.variant?.state) {
case UmbDocumentVariantState.PUBLISHED:
return this.localize.term('content_published');
case UmbDocumentVariantState.PUBLISHED_PENDING_CHANGES:
return this.localize.term('content_publishedPendingChanges');
case UmbDocumentVariantState.NOT_CREATED:
case UmbDocumentVariantState.DRAFT:
default:
return this.localize.term('content_unpublished');
case UmbDocumentVariantState.NOT_CREATED:
default:
return this.localize.term('content_notCreated');
}
}

View File

@@ -12,17 +12,29 @@ import { html } from '@umbraco-cms/backoffice/external/lit';
const modalData: UmbDocumentVariantPickerModalData = {
type: 'save',
variants: [
options: [
{
name: 'English',
culture: 'en-us',
state: UmbDocumentVariantState.PUBLISHED,
createDate: '2021-08-25T14:00:00Z',
publishDate: null,
updateDate: null,
segment: null,
isMandatory: true,
unique: 'en-us',
variant: {
name: 'English variant name',
culture: 'en-us',
state: UmbDocumentVariantState.PUBLISHED,
createDate: '2021-08-25T14:00:00Z',
publishDate: null,
updateDate: null,
segment: null,
},
language: {
entityType: 'language',
name: 'English',
unique: 'en-us',
isDefault: true,
isMandatory: true,
fallbackIsoCode: null,
},
},
/*
// TODO: We do not support segments currently
{
name: 'English',
culture: 'en-us',
@@ -31,17 +43,27 @@ const modalData: UmbDocumentVariantPickerModalData = {
publishDate: null,
updateDate: null,
segment: 'GTM',
isMandatory: true,
},
*/
{
name: 'Danish',
culture: 'da-dk',
state: UmbDocumentVariantState.NOT_CREATED,
createDate: null,
publishDate: null,
updateDate: null,
segment: null,
isMandatory: false,
unique: 'da-dk',
variant: {
name: 'Danish variant name',
culture: 'da-dk',
state: UmbDocumentVariantState.NOT_CREATED,
createDate: null,
publishDate: null,
updateDate: null,
segment: null,
},
language: {
entityType: 'language',
name: 'Danish',
unique: 'da-dk',
isDefault: false,
isMandatory: false,
fallbackIsoCode: null,
},
},
],
};
@@ -80,7 +102,6 @@ this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (modalManager) => {
publishDate: '2021-08-25T14:00:00Z',
updateDate: null,
segment: null,
isMandatory: true,
},
{
name: 'English',
@@ -90,7 +111,6 @@ this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (modalManager) => {
publishDate: '2021-08-25T14:00:00Z',
updateDate: null,
segment: 'GTM',
isMandatory: false,
},
{
name: 'Danish',
@@ -100,7 +120,6 @@ this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, (modalManager) => {
publishDate: null,
updateDate: null,
segment: null,
isMandatory: false,
},
],
}

View File

@@ -1,14 +1,14 @@
import { UMB_DOCUMENT_VARIANT_PICKER_MODAL_ALIAS } from '../manifests.js';
import type { UmbDocumentVariantModel } from '../../types.js';
import type { UmbDocumentVariantOptionModel } from '../../types.js';
import { UmbModalToken } from '@umbraco-cms/backoffice/modal';
export interface UmbDocumentVariantPickerModalData {
type: 'save' | 'publish' | 'schedule' | 'unpublish';
variants: Array<UmbDocumentVariantModel>;
options: Array<UmbDocumentVariantOptionModel>;
}
export interface UmbDocumentVariantPickerModalValue {
selection: Array<string | null>;
selection: Array<string>;
}
export const UMB_DOCUMENT_LANGUAGE_PICKER_MODAL = new UmbModalToken<

View File

@@ -46,7 +46,7 @@ export class UmbDocumentServerDataSource implements UmbDetailDataSource<UmbDocum
},
isTrashed: false,
values: [],
variants: [this.createVariantScaffold()],
variants: [],
...preset,
};
@@ -57,6 +57,8 @@ export class UmbDocumentServerDataSource implements UmbDetailDataSource<UmbDocum
* Creates a new variant scaffold.
* @returns A new variant scaffold.
*/
/*
// TDOD: remove if not used
createVariantScaffold(): UmbDocumentVariantModel {
return {
state: null,
@@ -66,9 +68,9 @@ export class UmbDocumentServerDataSource implements UmbDetailDataSource<UmbDocum
publishDate: null,
createDate: null,
updateDate: null,
isMandatory: false,
};
}
*/
/**
* Fetches a Document with the given id from the server
@@ -107,7 +109,6 @@ export class UmbDocumentServerDataSource implements UmbDetailDataSource<UmbDocum
publishDate: variant.publishDate || null,
createDate: variant.createDate,
updateDate: variant.updateDate,
isMandatory: false, // TODO: this is not correct. It will be solved when we know where to get the isMandatory from
};
}),
urls: data.urls.map((url) => {

View File

@@ -1,5 +1,5 @@
import type { UmbDocumentEntityType } from './entity.js';
import type { UmbVariantModel } from '@umbraco-cms/backoffice/variant';
import type { UmbVariantModel, UmbVariantOptionModel } from '@umbraco-cms/backoffice/variant';
import type { UmbReferenceById } from '@umbraco-cms/backoffice/models';
import { DocumentVariantStateModel as UmbDocumentVariantState } from '@umbraco-cms/backoffice/external/backend-api';
export { UmbDocumentVariantState };
@@ -22,7 +22,6 @@ export interface UmbDocumentDetailModel {
export interface UmbDocumentVariantModel extends UmbVariantModel {
state: UmbDocumentVariantState | null;
publishDate: string | null;
isMandatory: boolean;
}
export interface UmbDocumentUrlInfoModel {
@@ -36,3 +35,5 @@ export interface UmbDocumentValueModel<ValueType = unknown> {
alias: string;
value: ValueType;
}
export interface UmbDocumentVariantOptionModel extends UmbVariantOptionModel<UmbDocumentVariantModel> {}

View File

@@ -1,64 +1,36 @@
import type { UmbDocumentVariantOptionModel } from '../types.js';
import { UmbDocumentWorkspaceSplitViewElement } from './document-workspace-split-view.element.js';
import { UMB_DOCUMENT_WORKSPACE_CONTEXT } from './document-workspace.context-token.js';
import { customElement, state, css, html } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
import type { ActiveVariant } from '@umbraco-cms/backoffice/workspace';
import type { UmbRoute, UmbRouterSlotInitEvent } from '@umbraco-cms/backoffice/router';
import type { UmbVariantModel } from '@umbraco-cms/backoffice/variant';
import { UmbLanguageCollectionRepository } from '@umbraco-cms/backoffice/language';
import { combineLatest } from '@umbraco-cms/backoffice/external/rxjs';
// TODO: This seem fully identical with Media Workspace Editor, so we can refactor this to a generic component. [NL]
@customElement('umb-document-workspace-editor')
export class UmbDocumentWorkspaceEditorElement extends UmbLitElement {
//private _defaultVariant?: VariantViewModelBaseModel;
// TODO: Refactor: when having a split view/variants context token, we can rename the split view/variants component to a generic and make this component generic as well.
//
// TODO: Refactor: when having a split view/variants context token, we can rename the split view/variants component to a generic and make this component generic as well. [NL]
private splitViewElement = new UmbDocumentWorkspaceSplitViewElement();
#workspaceContext?: typeof UMB_DOCUMENT_WORKSPACE_CONTEXT.TYPE;
@state()
_routes?: Array<UmbRoute>;
@state()
_availableVariants: Array<UmbVariantModel> = [];
@state()
_workspaceSplitViews: Array<ActiveVariant> = [];
#workspaceContext?: typeof UMB_DOCUMENT_WORKSPACE_CONTEXT.TYPE;
constructor() {
super();
this.consumeContext(UMB_DOCUMENT_WORKSPACE_CONTEXT, (instance) => {
this.#workspaceContext = instance;
this.#observeVariants();
this.#observeSplitViews();
});
}
#observeVariants() {
if (!this.#workspaceContext) return;
this.observe(
this.#workspaceContext.allowedVariants,
(variants) => {
this._availableVariants = variants;
this._generateRoutes();
},
'_observeVariants',
);
}
#observeSplitViews() {
if (!this.#workspaceContext) return;
this.observe(
this.#workspaceContext.splitView.activeVariantsInfo,
(variants) => {
this._workspaceSplitViews = variants;
},
'_observeSplitViews',
);
// TODO: the variantOptions observable is like too broad as this will be triggered then there is any change in the variant options, we need to only update routes when there is a relevant change to them. [NL]
this.observe(this.#workspaceContext.variantOptions, (options) => this._generateRoutes(options), '_observeVariants');
}
private _handleVariantFolderPart(index: number, folderPart: string) {
@@ -68,17 +40,18 @@ export class UmbDocumentWorkspaceEditorElement extends UmbLitElement {
this.#workspaceContext?.splitView.setActiveVariant(index, culture, segment);
}
private async _generateRoutes() {
if (!this._availableVariants || this._availableVariants.length === 0) return;
private async _generateRoutes(options: Array<UmbDocumentVariantOptionModel>) {
if (!options || options.length === 0) return;
// Generate split view routes for all available routes
const routes: Array<UmbRoute> = [];
// Split view routes:
this._availableVariants.forEach((variantA) => {
this._availableVariants.forEach((variantB) => {
options.forEach((variantA) => {
options.forEach((variantB) => {
routes.push({
path: new UmbVariantId(variantA).toString() + '_&_' + new UmbVariantId(variantB).toString(),
// TODO: When implementing Segments, be aware if using the unique is URL Safe... [NL]
path: variantA.unique + '_&_' + variantB.unique,
component: this.splitViewElement,
setup: (_component, info) => {
// Set split view/active info..
@@ -92,9 +65,10 @@ export class UmbDocumentWorkspaceEditorElement extends UmbLitElement {
});
// Single view:
this._availableVariants.forEach((variant) => {
options.forEach((variant) => {
routes.push({
path: new UmbVariantId(variant).toString(),
// TODO: When implementing Segments, be aware if using the unique is URL Safe... [NL]
path: variant.unique,
component: this.splitViewElement,
setup: (_component, info) => {
// cause we might come from a split-view, we need to reset index 1.
@@ -108,11 +82,21 @@ export class UmbDocumentWorkspaceEditorElement extends UmbLitElement {
// Using first single view as the default route for now (hence the math below):
routes.push({
path: '',
redirectTo: routes[this._availableVariants.length * this._availableVariants.length]?.path,
redirectTo: routes[options.length * options.length]?.path,
});
}
const oldValue = this._routes;
// is there any differences in the amount ot the paths? [NL]
// TODO: if we make a memorization function as the observer, we can avoid this check and avoid the whole build of routes. [NL]
if (oldValue && oldValue.length === routes.length) {
// is there any differences in the paths? [NL]
const hasDifferences = oldValue.some((route, index) => route.path !== routes[index].path);
if (!hasDifferences) return;
}
this._routes = routes;
this.requestUpdate('_routes', oldValue);
}
private _gotWorkspaceRoute = (e: UmbRouterSlotInitEvent) => {

View File

@@ -2,12 +2,15 @@ import { UmbDocumentTypeDetailRepository } from '../../document-types/repository
import { UmbDocumentPropertyDataContext } from '../property-dataset-context/document-property-dataset-context.js';
import { UMB_DOCUMENT_ENTITY_TYPE } from '../entity.js';
import { UmbDocumentDetailRepository } from '../repository/index.js';
import { UmbDocumentVariantState, type UmbDocumentDetailModel, type UmbDocumentVariantModel } from '../types.js';
import type { UmbDocumentVariantPickerModalData } from '../modals/index.js';
import type { UmbDocumentDetailModel, UmbDocumentVariantModel, UmbDocumentVariantOptionModel } from '../types.js';
import { UMB_DOCUMENT_LANGUAGE_PICKER_MODAL, type UmbDocumentVariantPickerModalData } from '../modals/index.js';
import { UmbDocumentPublishingRepository } from '../repository/publishing/index.js';
import { UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT } from '../global-contexts/document-variant-manager.context.js';
import { UMB_DOCUMENT_WORKSPACE_ALIAS } from './manifests.js';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
import {
type UmbObjectWithVariantProperties,
UmbVariantId,
variantPropertiesObjectToString,
} from '@umbraco-cms/backoffice/variant';
import { UmbContentTypePropertyStructureManager } from '@umbraco-cms/backoffice/content-type';
import {
UmbEditableWorkspaceContextBase,
@@ -25,11 +28,12 @@ import {
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbLanguageCollectionRepository, type UmbLanguageDetailModel } from '@umbraco-cms/backoffice/language';
import { firstValueFrom } from '@umbraco-cms/backoffice/external/rxjs';
import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal';
type EntityType = UmbDocumentDetailModel;
export class UmbDocumentWorkspaceContext
extends UmbEditableWorkspaceContextBase<EntityType>
implements UmbVariantableWorkspaceContextInterface, UmbPublishableWorkspaceContextInterface
implements UmbVariantableWorkspaceContextInterface<UmbDocumentVariantModel>, UmbPublishableWorkspaceContextInterface
{
//
public readonly repository = new UmbDocumentDetailRepository(this);
@@ -40,7 +44,7 @@ export class UmbDocumentWorkspaceContext
*/
#currentData = new UmbObjectState<EntityType | undefined>(undefined);
#getDataPromise?: Promise<any>;
#variantManagerContext?: typeof UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT.TYPE;
// TODo: Optimize this so it uses either a App Language Context? [NL]
#languageRepository = new UmbLanguageCollectionRepository(this);
#languages = new UmbArrayState<UmbLanguageDetailModel>([], (x) => x.unique);
public readonly languages = this.#languages.asObservable();
@@ -54,26 +58,18 @@ export class UmbDocumentWorkspaceContext
readonly contentTypeUnique = this.#currentData.asObservablePart((data) => data?.documentType.unique);
readonly contentTypeHasCollection = this.#currentData.asObservablePart((data) => !!data?.documentType.collection);
readonly variants = this.#currentData.asObservablePart((data) => data?.variants ?? []);
readonly allowedVariants = combineObservables([this.variants, this.languages], ([variants, languages]) => {
const missingLanguages = languages.filter((x) => !variants.some((v) => v.culture === x.unique));
const newVariants = variants.concat(
missingLanguages.map(
(language) =>
({
state: UmbDocumentVariantState.NOT_CREATED,
isMandatory: language.isMandatory,
culture: language.unique,
segment: null,
name: language.name,
createDate: null,
publishDate: null,
updateDate: null,
}) as UmbDocumentVariantModel,
),
);
return newVariants;
readonly variantOptions = combineObservables([this.variants, this.languages], ([variants, languages]) => {
return languages.map((language) => {
return {
variant: variants.find((x) => x.culture === language.unique),
language,
// TODO: When including segments, this should be updated to include the segment as well. [NL]
unique: language.unique, // This must be a variantId string!
} as UmbDocumentVariantOptionModel;
});
});
readonly changedVariants = new UmbArrayState<UmbVariantId>([], (x) => x.compare);
readonly changedVariants = new UmbArrayState<UmbObjectWithVariantProperties>([], variantPropertiesObjectToString);
readonly urls = this.#currentData.asObservablePart((data) => data?.urls || []);
readonly templateId = this.#currentData.asObservablePart((data) => data?.template?.unique || null);
@@ -83,16 +79,13 @@ export class UmbDocumentWorkspaceContext
constructor(host: UmbControllerHost) {
super(host, UMB_DOCUMENT_WORKSPACE_ALIAS);
this.consumeContext(UMB_DOCUMENT_VARIANT_MANAGER_CONTEXT, (instance) => {
this.#variantManagerContext = instance;
});
this.observe(this.contentTypeUnique, (unique) => this.structure.loadType(unique));
this.loadLanguages();
}
async loadLanguages() {
// TODO: If we don't end up having a Global Context for languages, then we should at least change this into using a asObservable which should be returned from the repository. [Nl]
const { data } = await this.#languageRepository.requestCollection({});
this.#languages.setValue(data?.items ?? []);
}
@@ -222,50 +215,13 @@ export class UmbDocumentWorkspaceContext
const data = this.getData();
if (!data) throw new Error('Data is missing');
if (!data.unique) throw new Error('Unique is missing');
if (!this.#variantManagerContext) throw new Error('Variant manager context is missing');
const activeVariants = this.splitView.getActiveVariants();
const activeVariant = activeVariants.length ? activeVariants[0] : undefined;
// Calculate variants
const pickedVariants: string[] = [];
const availableVariants = await firstValueFrom(this.allowedVariants);
let allowedVariants = data.variants;
const pickedVariants = activeVariants.map((activeVariant) => UmbVariantId.Create(activeVariant).toString());
const allowedVariants = await firstValueFrom(this.variantOptions);
// Make sure that the active variant is in the allowed variants
if (activeVariant) {
const activeVariantInAvailableVariants = availableVariants.find(
(x) => x.culture === activeVariant.culture && x.segment === activeVariant.segment,
);
if (activeVariantInAvailableVariants) {
pickedVariants.push(activeVariantInAvailableVariants.culture!);
allowedVariants = appendToFrozenArray(
allowedVariants,
activeVariantInAvailableVariants,
(x) =>
x.culture === activeVariantInAvailableVariants.culture &&
x.segment === activeVariantInAvailableVariants.segment,
);
}
}
// Make sure the changed variants are in the allowed variants
const changedVariants = this.changedVariants.getValue();
if (changedVariants.length) {
pickedVariants.push(...changedVariants.map((x) => x.culture!));
const changedVariantsInAvailableVariants = availableVariants.filter((x) =>
changedVariants.some((y) => y.equal(new UmbVariantId(x))),
);
for (const changedVariant of changedVariantsInAvailableVariants) {
allowedVariants = appendToFrozenArray(
allowedVariants,
changedVariant,
(x) => changedVariant.culture === x.culture && changedVariant.segment === x.segment,
);
}
}
const selectedVariants = await this.#variantManagerContext.pickVariants(allowedVariants, type, pickedVariants);
const selectedVariants = await this.pickVariants(type, allowedVariants, pickedVariants);
// If no variants are selected, we don't save anything.
if (!selectedVariants.length) return [];
@@ -281,6 +237,35 @@ export class UmbDocumentWorkspaceContext
return selectedVariants;
}
// TODO: refactor this part so it can be utilized by others? [NL]
async pickVariants(
type: UmbDocumentVariantPickerModalData['type'],
availableVariants: Array<UmbDocumentVariantOptionModel>,
selectedVariants?: Array<string>,
): Promise<UmbVariantId[]> {
// If there is only one variant, we don't need to select anything.
if (availableVariants.length === 1) {
// TODO: we are missing a good way to make a variantId from a variantOptionModel. [NL]
return [new UmbVariantId(availableVariants[0].language.unique, null)];
}
const modalManagerContext = await this.consumeContext(UMB_MODAL_MANAGER_CONTEXT, () => {}).asPromise();
const modalData: UmbDocumentVariantPickerModalData = {
type,
options: availableVariants,
};
const modalContext = modalManagerContext.open(UMB_DOCUMENT_LANGUAGE_PICKER_MODAL, {
data: modalData,
value: { selection: selectedVariants?.map((x) => x.toString()) ?? [] },
});
const result = await modalContext.onSubmit().catch(() => undefined);
return result?.selection.map((x) => UmbVariantId.FromString(x)) ?? [];
}
async save() {
await this.#createOrSave('save');
const data = this.getData();
@@ -304,9 +289,11 @@ export class UmbDocumentWorkspaceContext
const unique = this.getEntityId();
if (!unique) throw new Error('Unique is missing');
if (!this.#variantManagerContext) throw new Error('Variant manager context is missing');
//if (!this.#variantManagerContext) throw new Error('Variant manager context is missing');
this.#variantManagerContext.unpublish(unique);
//this.#variantManagerContext.unpublish(unique);
alert('not implemented');
throw new Error('Not implemented');
}
async delete() {

View File

@@ -1,5 +1,5 @@
import type { UmbMediaEntityType } from './entity.js';
import type { UmbVariantModel } from '@umbraco-cms/backoffice/variant';
import type { UmbVariantModel, UmbVariantOptionModel } from '@umbraco-cms/backoffice/variant';
import type { MediaUrlInfoModel, MediaValueModel } from '@umbraco-cms/backoffice/external/backend-api';
export interface UmbMediaDetailModel {
@@ -12,3 +12,5 @@ export interface UmbMediaDetailModel {
values: Array<MediaValueModel>;
variants: Array<UmbVariantModel>;
}
export interface UmbMediaVariantOptionModel extends UmbVariantOptionModel<UmbVariantModel> {}

View File

@@ -1,28 +1,19 @@
import type { UmbMediaVariantOptionModel } from '../types.js';
import { UmbMediaWorkspaceSplitViewElement } from './media-workspace-split-view.element.js';
import { UMB_MEDIA_WORKSPACE_CONTEXT } from './media-workspace.context-token.js';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { customElement, state, css, html } from '@umbraco-cms/backoffice/external/lit';
import type { UmbVariantModel } from '@umbraco-cms/backoffice/variant';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
import type { UmbRoute, UmbRouterSlotInitEvent } from '@umbraco-cms/backoffice/router';
import type { ActiveVariant } from '@umbraco-cms/backoffice/workspace';
@customElement('umb-media-workspace-editor')
export class UmbMediaWorkspaceEditorElement extends UmbLitElement {
//private _defaultVariant?: VariantViewModelBaseModel;
// TODO: Refactor: when having a split view/variants context token, we can rename the split view/variants component to a generic and make this component generic as well.
//
// TODO: Refactor: when having a split view/variants context token, we can rename the split view/variants component to a generic and make this component generic as well. [NL]
private splitViewElement = new UmbMediaWorkspaceSplitViewElement();
@state()
_routes?: Array<UmbRoute>;
@state()
_availableVariants: Array<UmbVariantModel> = [];
@state()
_workspaceSplitViews: Array<ActiveVariant> = [];
#workspaceContext?: typeof UMB_MEDIA_WORKSPACE_CONTEXT.TYPE;
constructor() {
@@ -31,31 +22,12 @@ export class UmbMediaWorkspaceEditorElement extends UmbLitElement {
this.consumeContext(UMB_MEDIA_WORKSPACE_CONTEXT, (instance) => {
this.#workspaceContext = instance;
this.#observeVariants();
this.#observeSplitViews();
});
}
#observeVariants() {
if (!this.#workspaceContext) return;
this.observe(
this.#workspaceContext.allowedVariants,
(variants) => {
this._availableVariants = variants;
this._generateRoutes();
},
'_observeVariants',
);
}
#observeSplitViews() {
if (!this.#workspaceContext) return;
this.observe(
this.#workspaceContext.splitView.activeVariantsInfo,
(variants) => {
this._workspaceSplitViews = variants;
},
'_observeSplitViews',
);
this.observe(this.#workspaceContext.variantOptions, (options) => this._generateRoutes(options), '_observeVariants');
}
private _handleVariantFolderPart(index: number, folderPart: string) {
@@ -65,17 +37,18 @@ export class UmbMediaWorkspaceEditorElement extends UmbLitElement {
this.#workspaceContext?.splitView.setActiveVariant(index, culture, segment);
}
private _generateRoutes() {
if (!this._availableVariants || this._availableVariants.length === 0) return;
private async _generateRoutes(variants: Array<UmbMediaVariantOptionModel>) {
if (!variants || variants.length === 0) return;
// Generate split view routes for all available routes
const routes: Array<UmbRoute> = [];
// Split view routes:
this._availableVariants.forEach((variantA) => {
this._availableVariants.forEach((variantB) => {
variants.forEach((variantA) => {
variants.forEach((variantB) => {
routes.push({
path: UmbVariantId.Create(variantA).toString() + '_&_' + UmbVariantId.Create(variantB).toString(),
// TODO: When implementing Segments, be aware if using the unique is URL Safe... [NL]
path: variantA.unique + '_&_' + variantB.unique,
component: this.splitViewElement,
setup: (_component, info) => {
// Set split view/active info..
@@ -89,9 +62,10 @@ export class UmbMediaWorkspaceEditorElement extends UmbLitElement {
});
// Single view:
this._availableVariants.forEach((variant) => {
variants.forEach((variant) => {
routes.push({
path: UmbVariantId.Create(variant).toString(),
// TODO: When implementing Segments, be aware if using the unique is URL Safe... [NL]
path: variant.unique,
component: this.splitViewElement,
setup: (_component, info) => {
// cause we might come from a split-view, we need to reset index 1.
@@ -105,11 +79,21 @@ export class UmbMediaWorkspaceEditorElement extends UmbLitElement {
// Using first single view as the default route for now (hence the math below):
routes.push({
path: '',
redirectTo: routes[this._availableVariants.length * this._availableVariants.length]?.path,
redirectTo: routes[variants.length * variants.length]?.path,
});
}
const oldValue = this._routes;
// is there any differences in the amount ot the paths? [NL]
// TODO: if we make a memorization function as the observer, we can avoid this check and avoid the whole build of routes. [NL]
if (oldValue && oldValue.length === routes.length) {
// is there any differences in the paths? [NL]
const hasDifferences = oldValue.some((route, index) => route.path !== routes[index].path);
if (!hasDifferences) return;
}
this._routes = routes;
this.requestUpdate('_routes', oldValue);
}
private _gotWorkspaceRoute = (e: UmbRouterSlotInitEvent) => {

View File

@@ -2,7 +2,7 @@ import { UmbMediaTypeDetailRepository } from '../../media-types/repository/detai
import { UmbMediaPropertyDataContext } from '../property-dataset-context/media-property-dataset-context.js';
import { UMB_MEDIA_ENTITY_TYPE } from '../entity.js';
import { UmbMediaDetailRepository } from '../repository/index.js';
import type { UmbMediaDetailModel } from '../types.js';
import type { UmbMediaDetailModel, UmbMediaVariantOptionModel } from '../types.js';
import type { UmbVariantId } from '@umbraco-cms/backoffice/variant';
import { UmbContentTypePropertyStructureManager } from '@umbraco-cms/backoffice/content-type';
import {
@@ -33,8 +33,11 @@ export class UmbMediaWorkspaceContext
*/
#currentData = new UmbObjectState<EntityType | undefined>(undefined);
#getDataPromise?: Promise<any>;
// TODo: Optimize this so it uses either a App Language Context? [NL]
#languageRepository = new UmbLanguageCollectionRepository(this);
#languageCollection = new UmbArrayState<UmbLanguageDetailModel>([], (x) => x.unique);
#languages = new UmbArrayState<UmbLanguageDetailModel>([], (x) => x.unique);
public readonly languages = this.#languages.asObservable();
public isLoaded() {
return this.#getDataPromise;
}
@@ -43,22 +46,16 @@ export class UmbMediaWorkspaceContext
readonly contentTypeUnique = this.#currentData.asObservablePart((data) => data?.mediaType.unique);
readonly variants = this.#currentData.asObservablePart((data) => data?.variants || []);
readonly allowedVariants = combineObservables(
[this.variants, this.#languageCollection.asObservable()],
([variants, languages]) => {
const missingLanguages = languages.filter((x) => !variants.some((v) => v.culture === x.unique));
const newVariants = variants.concat(
missingLanguages.map((x) => ({
culture: x.unique,
segment: null,
name: x.name,
createDate: '',
updateDate: '',
})),
);
return newVariants;
},
);
readonly variantOptions = combineObservables([this.variants, this.languages], ([variants, languages]) => {
return languages.map((language) => {
return {
variant: variants.find((x) => x.culture === language.unique),
language,
// TODO: When including segments, this should be updated to include the segment as well. [NL]
unique: language.unique, // This must be a variantId string!
} as UmbMediaVariantOptionModel;
});
});
readonly urls = this.#currentData.asObservablePart((data) => data?.urls || []);
readonly structure = new UmbContentTypePropertyStructureManager(this, new UmbMediaTypeDetailRepository(this));
@@ -73,7 +70,7 @@ export class UmbMediaWorkspaceContext
async loadLanguages() {
const { data } = await this.#languageRepository.requestCollection({});
this.#languageCollection.setValue(data?.items ?? []);
this.#languages.setValue(data?.items ?? []);
}
async load(unique: string) {