Skip to content

Commit

Permalink
Improve fields implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
thecalcc committed Dec 11, 2024
1 parent c52bad8 commit eefc8c7
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 62 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import {IProfileSchemaTypeList} from 'interfaces';
import {IDropdownConfigVocabulary, IAuthoringFieldV2, ISubject, IVocabularyItem} from 'superdesk-api';
import {planningApi, superdeskApi} from 'superdeskApi';
import {superdeskApi} from '../../../superdeskApi';
import {IFieldDefinition} from '../profile';

export const SUBJECT_PREFIX_ID = 'subject--';
const getCustomVocabulariesId = (vocabularyId: string) => `${SUBJECT_PREFIX_ID}${vocabularyId}`;
const getStrippedCustomVocabularyId = (customVocabularyId: string) => customVocabularyId.replace(SUBJECT_PREFIX_ID, '');
import {getProfileFieldsConverted} from '../profile-converter';

export const getCustomVocabularyFields = () => {
const planningProfile = planningApi.contentProfiles.get('planning');
const customVocabularyIds =
(planningProfile.schema?.['custom_vocabularies'] as IProfileSchemaTypeList)?.vocabularies;
const customVocabularyIds = getProfileFieldsConverted()
.filter((x) => x.type === 'custom_vocabulary')
.map(({vocabularyId}) => vocabularyId);
const result: Array<IFieldDefinition> = [];

if ((customVocabularyIds?.length ?? 0) > 0) {
Expand All @@ -20,7 +16,7 @@ export const getCustomVocabularyFields = () => {
const vocabulary = allVocabularies.get(id);

result.push({
fieldId: getCustomVocabulariesId(id),
fieldId: id,
getField: ({required, id: _id}) => {
const fieldConfig: IDropdownConfigVocabulary = {
source: 'vocabulary',
Expand All @@ -39,15 +35,9 @@ export const getCustomVocabularyFields = () => {
return field;
},
storageAdapter: {
storeValue: (item, operationalValue: {
existing: Array<ISubject>;
fieldId: string;
value?: Array<IVocabularyItem['qcode']>;
}) => {
const {existing = [], fieldId, value = []} = operationalValue;
const strippedId = getStrippedCustomVocabularyId(fieldId);
const vocabulary = allVocabularies.get(strippedId);
const vocabItems = vocabulary.items.filter((x) => value?.includes(x.qcode)) ?? [];
storeValue: (item, operationalValue: IVocabularyItem['qcode']) => {
const vocabulary = allVocabularies.get(id);
const vocabItems = vocabulary.items.filter((x) => operationalValue?.includes(x.qcode)) ?? [];

// Subfield values
const itemsToSubject: Array<ISubject> = vocabItems.map((x) => ({
Expand All @@ -57,7 +47,7 @@ export const getCustomVocabularyFields = () => {
}));

// Remove values that don't match the "subfield" ID, so there's no item duplication
const restOfValues = existing.filter((x) => x.scheme !== strippedId);
const restOfValues = item.subject.filter((x) => x.scheme !== id);

return {
...item,
Expand All @@ -67,11 +57,8 @@ export const getCustomVocabularyFields = () => {
],
};
},
retrieveStoredValue: (storageValue: {fieldId: string; subject: Array<ISubject>;}) => {
const {fieldId, subject = []} = storageValue;
const strippedId = getStrippedCustomVocabularyId(fieldId);

return subject.filter((x) => x.scheme === strippedId).map((x) => x.qcode);
retrieveStoredValue: (item) => {
return (item.subject ?? []).filter((x) => x.scheme === id).map((x) => x.qcode);
},
}
});
Expand Down
45 changes: 45 additions & 0 deletions client/components/planning-editor-standalone/profile-converter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {flatMap} from 'lodash';
import {planningApi} from '../../superdeskApi';
import {getEditorFormGroupsFromProfile} from '../../utils/contentProfiles';

interface IBaseField<T> {
type: T;
fieldId: string;
required: boolean;
}

interface ICustomVocabularyField extends IBaseField<'custom_vocabulary'> {
vocabularyId: string;
}

type IFieldConverted = IBaseField<'normal'> | ICustomVocabularyField;

export const getProfileFieldsConverted = (): Array<IFieldConverted> => {
const planningProfile = planningApi.contentProfiles.get('planning');
const planningGroups = getEditorFormGroupsFromProfile(planningProfile);
const planningFieldIds = flatMap(Object.values(planningGroups).map((x) => x.fields));
const convertedFieldIds: Array<IFieldConverted> = [];

for (const fieldId of planningFieldIds) {
const fieldSchema = planningProfile.schema[fieldId];

if (fieldSchema?.type === 'list' && ((fieldSchema.vocabularies ?? []).length > 0)) {
for (const vocabId of fieldSchema.vocabularies) {
convertedFieldIds.push({
type: 'custom_vocabulary',
fieldId: vocabId,
required: fieldSchema.required ?? false,
vocabularyId: vocabId,
});
}
} else {
convertedFieldIds.push({
type: 'normal',
fieldId: fieldId,
required: fieldSchema.required ?? false,
});
}
}

return convertedFieldIds;
};
42 changes: 12 additions & 30 deletions client/components/planning-editor-standalone/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@ import {
IDateTimeFieldConfig,
IDropdownConfigVocabulary,
IEditor3Config,
ISubject,
IVocabularyItem,
} from 'superdesk-api';
import {planningApi, superdeskApi} from '../../superdeskApi';
import {getEditorFormGroupsFromProfile} from '../../utils/contentProfiles';
import {superdeskApi} from '../../superdeskApi';
import {
IAttachmentsFieldConfig,
} from '../../planning-extension/src/authoring-react-fields/planning-attachments/interfaces';
import {IProfileSchemaTypeList} from 'interfaces';
import {getCustomVocabularyFields} from './field-adapters/custom-vocabularies';
import {getProfileFieldsConverted} from './profile-converter';
import {IEventOrPlanningItem} from 'interfaces';

function getTextFieldConfig(options: {id: string; label: string, required: boolean}): IAuthoringFieldV2 {
const editor3ConfigWithoutFormatting: IEditor3Config = {
Expand Down Expand Up @@ -65,14 +64,14 @@ export interface IFieldDefinition {
fieldId: string;
getField: (options: {required: boolean, id: string}) => IAuthoringFieldV2;
storageAdapter?: {
storeValue: <T>(item, operationalValue) => T; // returns stored value
retrieveStoredValue: (storageValue) => unknown; // returns operational value
storeValue: <T extends IEventOrPlanningItem>(item, operationalValue) => T; // returns stored value
retrieveStoredValue:
<T extends IEventOrPlanningItem>(item: T, fieldId: string) => unknown; // returns operational value
};
}

type IFieldDefinitions = {[fieldId: string]: IFieldDefinition};


export function getFieldDefinitions(): IFieldDefinitions {
const {gettext} = superdeskApi.localization;
const result: Array<IFieldDefinition> = [
Expand Down Expand Up @@ -155,7 +154,7 @@ export function getFieldDefinitions(): IFieldDefinitions {
place: operationalValue.map((qcode) => vocabularyItems.get(qcode)),
};
},
retrieveStoredValue: (storageValue: Array<IVocabularyItem>) => storageValue.map(({qcode}) => qcode),
retrieveStoredValue: (item, fieldId) => item[fieldId].map(({qcode}) => qcode),
},
},
{
Expand Down Expand Up @@ -191,35 +190,18 @@ export function getFieldDefinitions(): IFieldDefinitions {
}

export function getProfile() {
const planningProfile = planningApi.contentProfiles.get('planning');
const planningGroups = getEditorFormGroupsFromProfile(planningProfile);
const planningFieldIds = Object.values(planningGroups).flatMap(({fields}) => fields);

const planningFieldIds = getProfileFieldsConverted();
const skipped = new Set<string>();
const fieldDefinitions = getFieldDefinitions();
const profileV2: IContentProfileV2 = {
id: 'not-used',
name: 'not-used',
content: OrderedMap([]),
header: OrderedMap(),
};

const skipped = new Set<string>();
const fieldDefinitions = getFieldDefinitions();
const customVocabularyIds =
(planningProfile.schema?.['custom_vocabularies'] as IProfileSchemaTypeList)?.vocabularies;

for (const fieldId of planningFieldIds) {
const required = planningProfile.schema?.[fieldId]?.required ?? false;

if (fieldId === 'custom_vocabularies') {
for (const id of customVocabularyIds) {
const customVocabFieldId = getCustomVocabulariesId(id);

profileV2.header = profileV2.header.set(
customVocabFieldId,
fieldDefinitions[customVocabFieldId].getField({id: customVocabFieldId, required: required}),
);
}
} else if (fieldDefinitions[fieldId] != null) {
for (const {fieldId, required} of planningFieldIds) {
if (fieldDefinitions[fieldId] != null) {
profileV2.header = profileV2.header.set(
fieldId,
fieldDefinitions[fieldId].getField({id: fieldId, required: required}),
Expand Down
10 changes: 3 additions & 7 deletions client/components/planning-editor-standalone/storage-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ import {
IEditor3Config,
IEditor3ValueStorage,
IStorageAdapter,
ISubject,
} from 'superdesk-api';
import {superdeskApi} from '../../superdeskApi';
import {getFieldDefinitions} from './profile';
import {SUBJECT_PREFIX_ID} from './field-adapters/custom-vocabularies';

export const storageAdapterPlanningItem: IStorageAdapter<IPlanningItem> = {
storeValue: (value, fieldId, item, config, fieldType) => {
Expand Down Expand Up @@ -42,14 +40,12 @@ export const storageAdapterPlanningItem: IStorageAdapter<IPlanningItem> = {

retrieveStoredValue: (item, fieldId, fieldType) => {
const {getContentStateFromHtml} = superdeskApi.helpers;
const value = (item as {[key: string]: any})[fieldId] ?? undefined;
const fieldDefinitions = getFieldDefinitions();
const value = (item as {[key: string]: any})[fieldId] ?? undefined;
const fieldStorageAdapter = fieldDefinitions[fieldId]?.storageAdapter;

if (fieldId.startsWith(SUBJECT_PREFIX_ID)) {
return fieldStorageAdapter.retrieveStoredValue({fieldId: fieldId, subject: item.subject});
} else if (fieldStorageAdapter != null) {
return fieldStorageAdapter.retrieveStoredValue(value);
if (fieldStorageAdapter != null) {
return fieldStorageAdapter.retrieveStoredValue(item, fieldId);
} else if (fieldType === 'editor3') {
const returnValue: IEditor3ValueStorage
= {rawContentState: convertToRaw(getContentStateFromHtml(value ?? ''))};
Expand Down

0 comments on commit eefc8c7

Please sign in to comment.