From dbdfd1aca34e2eca8c776dff1f4ea59ad253d4d3 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Sun, 29 Dec 2024 16:09:45 +0100 Subject: [PATCH 01/29] Make RICH_TEXT composite WiP --- .../services/type-mapper.service.ts | 3 --- ...p-field-metadata-to-graphql-query.utils.ts | 9 ++++++- .../open-api/utils/components.utils.ts | 14 +++++++++- .../field-metadata/composite-types/index.ts | 2 ++ .../rich-text.composite-type.ts | 26 +++++++++++++++++++ .../dtos/default-value.input.ts | 11 +++++--- .../validate-default-value-for-type.util.ts | 3 ++- .../composite-column-action.factory.ts | 3 ++- ...field-metadata-type-to-column-type.util.ts | 1 - .../workspace-migration.factory.ts | 5 +++- .../standard-objects/note.workspace-entity.ts | 5 ++-- .../standard-objects/task.workspace-entity.ts | 5 ++-- 12 files changed, 71 insertions(+), 16 deletions(-) create mode 100644 packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts index aa3259aea9b8..f261581a662e 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts @@ -80,7 +80,6 @@ export class TypeMapperService { FieldMetadataType.ARRAY, StringArrayScalarType as unknown as GraphQLScalarType, ], - [FieldMetadataType.RICH_TEXT, GraphQLString], [FieldMetadataType.TS_VECTOR, GraphQLString], ]); @@ -115,7 +114,6 @@ export class TypeMapperService { [FieldMetadataType.NUMERIC, BigFloatFilterType], [FieldMetadataType.POSITION, FloatFilterType], [FieldMetadataType.RAW_JSON, RawJsonFilterType], - [FieldMetadataType.RICH_TEXT, StringFilterType], [FieldMetadataType.ARRAY, ArrayFilterType], [FieldMetadataType.MULTI_SELECT, MultiSelectFilterType], [FieldMetadataType.SELECT, SelectFilterType], @@ -141,7 +139,6 @@ export class TypeMapperService { [FieldMetadataType.MULTI_SELECT, OrderByDirectionType], [FieldMetadataType.POSITION, OrderByDirectionType], [FieldMetadataType.RAW_JSON, OrderByDirectionType], - [FieldMetadataType.RICH_TEXT, OrderByDirectionType], [FieldMetadataType.ARRAY, OrderByDirectionType], [FieldMetadataType.TS_VECTOR, OrderByDirectionType], // TODO: Add TSVectorOrderByType ]); diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts index fb229a1f8eb5..491b615b20c8 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts @@ -28,7 +28,6 @@ export const mapFieldMetadataToGraphqlQuery = ( FieldMetadataType.MULTI_SELECT, FieldMetadataType.POSITION, FieldMetadataType.RAW_JSON, - FieldMetadataType.RICH_TEXT, FieldMetadataType.ARRAY, FieldMetadataType.TS_VECTOR, ].includes(fieldType); @@ -154,5 +153,13 @@ export const mapFieldMetadataToGraphqlQuery = ( additionalPhones } `; + } else if (fieldType === FieldMetadataType.RICH_TEXT) { + return ` + ${field.name} + { + blocknote + markdown + } + `; } }; diff --git a/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts b/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts index 4d93e29c070e..1056c015f75c 100644 --- a/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts +++ b/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts @@ -43,7 +43,6 @@ const getFieldProperties = (type: FieldMetadataType): Property => { case FieldMetadataType.UUID: return { type: 'string', format: 'uuid' }; case FieldMetadataType.TEXT: - case FieldMetadataType.RICH_TEXT: return { type: 'string' }; case FieldMetadataType.DATE_TIME: return { type: 'string', format: 'date-time' }; @@ -269,6 +268,19 @@ const getSchemaComponentsProperties = ({ type: 'object', }; break; + case FieldMetadataType.RICH_TEXT: + itemProperty = { + type: 'object', + properties: { + blocknote: { + type: 'string', + }, + markdown: { + type: 'string', + }, + }, + }; + break; default: itemProperty = getFieldProperties(field.type); break; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/index.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/index.ts index fbc999d49e70..4a899c0e5df4 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/index.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/index.ts @@ -7,6 +7,7 @@ import { emailsCompositeType } from 'src/engine/metadata-modules/field-metadata/ import { fullNameCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/full-name.composite-type'; import { linksCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/links.composite-type'; import { phonesCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/phones.composite-type'; +import { richTextCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type'; import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; export const compositeTypeDefinitions = new Map< @@ -20,4 +21,5 @@ export const compositeTypeDefinitions = new Map< [FieldMetadataType.ACTOR, actorCompositeType], [FieldMetadataType.EMAILS, emailsCompositeType], [FieldMetadataType.PHONES, phonesCompositeType], + [FieldMetadataType.RICH_TEXT, richTextCompositeType], ]); diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts new file mode 100644 index 000000000000..ba8fbe50a511 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts @@ -0,0 +1,26 @@ +import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface'; + +import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; + +export const richTextCompositeType: CompositeType = { + type: FieldMetadataType.RICH_TEXT, + properties: [ + { + name: 'blocknote', + type: FieldMetadataType.TEXT, + hidden: false, + isRequired: false, + }, + { + name: 'markdown', + type: FieldMetadataType.TEXT, + hidden: false, + isRequired: false, + }, + ], +}; + +export type RichTextMetadata = { + blocknote: string; + markdown: string; +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts index 68b88bc28fd0..f71500bf3c2f 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts @@ -36,10 +36,15 @@ export class FieldMetadataDefaultValueRawJson { } export class FieldMetadataDefaultValueRichText { - @ValidateIf((_object, value) => value !== null) - @IsString() - value: string | null; + @ValidateIf((object, value) => value !== null) + @IsQuotedString() + blocknote: string | null; + + @ValidateIf((object, value) => value !== null) + @IsQuotedString() + markdown: string | null; } + export class FieldMetadataDefaultValueNumber { @ValidateIf((object, value) => value !== null) @IsNumber() diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts index a5cea6d00482..111a42b14029 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts @@ -20,6 +20,7 @@ import { FieldMetadataDefaultValueNumber, FieldMetadataDefaultValuePhones, FieldMetadataDefaultValueRawJson, + FieldMetadataDefaultValueRichText, FieldMetadataDefaultValueString, FieldMetadataDefaultValueStringArray, FieldMetadataDefaultValueUuidFunction, @@ -47,7 +48,7 @@ export const defaultValueValidatorsMap = { [FieldMetadataType.SELECT]: [FieldMetadataDefaultValueString], [FieldMetadataType.MULTI_SELECT]: [FieldMetadataDefaultValueStringArray], [FieldMetadataType.ADDRESS]: [FieldMetadataDefaultValueAddress], - [FieldMetadataType.RICH_TEXT]: [FieldMetadataDefaultValueString], + [FieldMetadataType.RICH_TEXT]: [FieldMetadataDefaultValueRichText], [FieldMetadataType.RAW_JSON]: [FieldMetadataDefaultValueRawJson], [FieldMetadataType.LINKS]: [FieldMetadataDefaultValueLinks], [FieldMetadataType.ACTOR]: [FieldMetadataDefaultActor], diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory.ts index 0051d7293a65..497454816e5f 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory.ts @@ -25,7 +25,8 @@ export type CompositeFieldMetadataType = | FieldMetadataType.FULL_NAME | FieldMetadataType.LINKS | FieldMetadataType.EMAILS - | FieldMetadataType.PHONES; + | FieldMetadataType.PHONES + | FieldMetadataType.RICH_TEXT; @Injectable() export class CompositeColumnActionFactory extends ColumnActionAbstractFactory { diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts index 46ec14ad3fc1..f83e0d91144c 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts @@ -15,7 +15,6 @@ export const fieldMetadataTypeToColumnType = ( case FieldMetadataType.UUID: return 'uuid'; case FieldMetadataType.TEXT: - case FieldMetadataType.RICH_TEXT: case FieldMetadataType.ARRAY: return 'text'; case FieldMetadataType.NUMERIC: diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts index 5df5da052f2b..d9ffda8a5fbf 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts @@ -56,7 +56,6 @@ export class WorkspaceMigrationFactory { [FieldMetadataType.NUMBER, { factory: this.basicColumnActionFactory }], [FieldMetadataType.POSITION, { factory: this.basicColumnActionFactory }], [FieldMetadataType.RAW_JSON, { factory: this.basicColumnActionFactory }], - [FieldMetadataType.RICH_TEXT, { factory: this.basicColumnActionFactory }], [FieldMetadataType.BOOLEAN, { factory: this.basicColumnActionFactory }], [FieldMetadataType.DATE_TIME, { factory: this.basicColumnActionFactory }], [FieldMetadataType.DATE, { factory: this.basicColumnActionFactory }], @@ -93,6 +92,10 @@ export class WorkspaceMigrationFactory { FieldMetadataType.TS_VECTOR, { factory: this.tsVectorColumnActionFactory }, ], + [ + FieldMetadataType.RICH_TEXT, + { factory: this.compositeColumnActionFactory }, + ], ]); } diff --git a/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts b/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts index 2b51ae509fef..31d9ce222765 100644 --- a/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts +++ b/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts @@ -5,6 +5,7 @@ import { ActorMetadata, FieldActorSource, } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; +import { RichTextMetadata } from 'src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type'; import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { IndexType } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity'; import { @@ -35,7 +36,7 @@ const BODY_FIELD_NAME = 'body'; export const SEARCH_FIELDS_FOR_NOTES: FieldTypeAndNameMetadata[] = [ { name: TITLE_FIELD_NAME, type: FieldMetadataType.TEXT }, - { name: BODY_FIELD_NAME, type: FieldMetadataType.RICH_TEXT }, + { name: `${BODY_FIELD_NAME}Blocknote`, type: FieldMetadataType.RICH_TEXT }, // TODO: Check later if and how this works ]; @WorkspaceEntity({ @@ -77,7 +78,7 @@ export class NoteWorkspaceEntity extends BaseWorkspaceEntity { icon: 'IconFilePencil', }) @WorkspaceIsNullable() - [BODY_FIELD_NAME]: string | null; + [BODY_FIELD_NAME]: RichTextMetadata | null; @WorkspaceField({ standardId: NOTE_STANDARD_FIELD_IDS.createdBy, diff --git a/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts b/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts index ff3b83e6c07b..490976972b7d 100644 --- a/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts +++ b/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts @@ -5,6 +5,7 @@ import { ActorMetadata, FieldActorSource, } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; +import { RichTextMetadata } from 'src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type'; import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { IndexType } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity'; import { @@ -37,7 +38,7 @@ const BODY_FIELD_NAME = 'body'; export const SEARCH_FIELDS_FOR_TASK: FieldTypeAndNameMetadata[] = [ { name: TITLE_FIELD_NAME, type: FieldMetadataType.TEXT }, - { name: BODY_FIELD_NAME, type: FieldMetadataType.RICH_TEXT }, + { name: `${BODY_FIELD_NAME}Blocknote`, type: FieldMetadataType.RICH_TEXT }, // TODO: Check later if and how this works ]; @WorkspaceEntity({ @@ -79,7 +80,7 @@ export class TaskWorkspaceEntity extends BaseWorkspaceEntity { icon: 'IconFilePencil', }) @WorkspaceIsNullable() - [BODY_FIELD_NAME]: string | null; + [BODY_FIELD_NAME]: RichTextMetadata | null; @WorkspaceField({ standardId: TASK_STANDARD_FIELD_IDS.dueAt, From 45b68d06fcf8e7f0d5573cfb98f829cb3e87d85e Mon Sep 17 00:00:00 2001 From: ad-elias Date: Sun, 29 Dec 2024 22:07:43 +0100 Subject: [PATCH 02/29] Make RICH_TEXT composite WiP --- .../data-model/types/CompositeFieldType.ts | 1 + .../utils/generate-default-value.ts | 5 +++++ .../is-composite-field-metadata-type.util.ts | 4 +++- .../src/utils/computeInputFields.ts | 21 ++++++++++++++++++- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/twenty-front/src/modules/settings/data-model/types/CompositeFieldType.ts b/packages/twenty-front/src/modules/settings/data-model/types/CompositeFieldType.ts index ddf7b0d579e0..f3d1d86d2523 100644 --- a/packages/twenty-front/src/modules/settings/data-model/types/CompositeFieldType.ts +++ b/packages/twenty-front/src/modules/settings/data-model/types/CompositeFieldType.ts @@ -10,6 +10,7 @@ export const COMPOSITE_FIELD_TYPES = [ 'PHONES', 'FULL_NAME', 'ACTOR', + 'RICH_TEXT', ] as const; type CompositeFieldTypeBaseLiteral = (typeof COMPOSITE_FIELD_TYPES)[number]; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/generate-default-value.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/generate-default-value.ts index 17b55a4b6622..6f8b34a89127 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/generate-default-value.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/generate-default-value.ts @@ -47,6 +47,11 @@ export function generateDefaultValue( primaryPhoneCallingCode: "''", additionalPhones: null, }; + case FieldMetadataType.RICH_TEXT: + return { + blocknote: "''", + markdown: "''", + }; default: return null; } diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util.ts index 911162c9ae39..fda5b7d8dfd0 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util.ts @@ -9,7 +9,8 @@ export const isCompositeFieldMetadataType = ( | FieldMetadataType.LINKS | FieldMetadataType.ACTOR | FieldMetadataType.EMAILS - | FieldMetadataType.PHONES => { + | FieldMetadataType.PHONES + | FieldMetadataType.RICH_TEXT => { return [ FieldMetadataType.CURRENCY, FieldMetadataType.FULL_NAME, @@ -18,5 +19,6 @@ export const isCompositeFieldMetadataType = ( FieldMetadataType.ACTOR, FieldMetadataType.EMAILS, FieldMetadataType.PHONES, + FieldMetadataType.RICH_TEXT, ].includes(type); }; diff --git a/packages/twenty-zapier/src/utils/computeInputFields.ts b/packages/twenty-zapier/src/utils/computeInputFields.ts index 0802361469f1..a288ec6ee2ac 100644 --- a/packages/twenty-zapier/src/utils/computeInputFields.ts +++ b/packages/twenty-zapier/src/utils/computeInputFields.ts @@ -226,6 +226,25 @@ const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => { }; return [primaryLinkLabel, primaryLinkUrl, secondaryLinks]; } + case FieldMetadataType.RICH_TEXT: { + const blocknote: NodeField = { + type: FieldMetadataType.TEXT, + name: 'blocknote', + label: 'Blocknote', + description: 'Blocknote', + isNullable: true, + defaultValue: null, + }; + const markdown: NodeField = { + type: FieldMetadataType.TEXT, + name: 'markdown', + label: 'Markdown', + description: 'Markdown', + isNullable: true, + defaultValue: null, + }; + return [blocknote, markdown]; + } default: throw new Error(`Unknown nodeField type: ${nodeField.type}`); } @@ -250,6 +269,7 @@ export const computeInputFields = ( case FieldMetadataType.EMAILS: case FieldMetadataType.LINKS: case FieldMetadataType.ADDRESS: + case FieldMetadataType.RICH_TEXT: for (const subNodeField of get_subfieldsFromField(nodeField)) { const field = { key: `${nodeField.name}__${subNodeField.name}`, @@ -265,7 +285,6 @@ export const computeInputFields = ( break; case FieldMetadataType.UUID: case FieldMetadataType.TEXT: - case FieldMetadataType.RICH_TEXT: case FieldMetadataType.PHONE: case FieldMetadataType.EMAIL: case FieldMetadataType.DATE_TIME: From 4f5ae944aebcf5a23f39dfa2d2966838c055f427 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Sun, 29 Dec 2024 23:42:48 +0100 Subject: [PATCH 03/29] Frontend handling of composite RICH_TEXT WiP --- .../meta-types/hooks/useRichTextFieldDisplay.ts | 2 +- .../record-field/types/FieldMetadata.ts | 8 ++++++-- .../types/guards/isFieldRichTextValue.ts | 8 ++++---- .../SettingsCompositeFieldTypeConfigs.ts | 17 +++++++++++++++++ .../SettingsNonCompositeFieldTypeConfigs.ts | 12 ++---------- .../activity-query-result-getter.handler.ts | 7 +++++-- 6 files changed, 35 insertions(+), 19 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts index bc933a92b949..4adbbf748b96 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts @@ -26,7 +26,7 @@ export const useRichTextFieldDisplay = () => { fieldName, ); - const fieldValueParsed = parseJson(fieldValue); + const fieldValueParsed = parseJson(fieldValue?.blocknote); return { fieldDefinition, diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts b/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts index faca16b44e1e..50fa47c9ddec 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts @@ -210,7 +210,8 @@ export type FieldMetadata = | FieldAddressMetadata | FieldActorMetadata | FieldArrayMetadata - | FieldTsVectorMetadata; + | FieldTsVectorMetadata + | FieldRichTextMetadata; export type FieldTextValue = string; export type FieldUUidValue = string; // TODO: can we replace with a template literal type, or maybe overkill ? @@ -258,7 +259,10 @@ export type FieldRelationValue< export type Json = ZodHelperLiteral | { [key: string]: Json } | Json[]; export type FieldJsonValue = Record | Json[] | null; -export type FieldRichTextValue = null | string; +export type FieldRichTextValue = { + blocknote: string | null; // TODO: Can these be null? + markdown: string | null; +}; export type FieldActorValue = { source: string; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValue.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValue.ts index 0b1645644f55..d6e0cfc355bf 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValue.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValue.ts @@ -1,10 +1,10 @@ import { z } from 'zod'; import { FieldRichTextValue } from '../FieldMetadata'; -export const richTextSchema: z.ZodType = z.union([ - z.null(), // Exclude literal values other than null - z.string(), -]); +export const richTextSchema: z.ZodType = z.object({ + blocknote: z.string().nullable(), + markdown: z.string().nullable(), +}); export const isFieldRichTextValue = ( fieldValue: unknown, diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsCompositeFieldTypeConfigs.ts b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsCompositeFieldTypeConfigs.ts index c991341b716f..993a3d6fa092 100644 --- a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsCompositeFieldTypeConfigs.ts +++ b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsCompositeFieldTypeConfigs.ts @@ -7,6 +7,7 @@ import { FieldFullNameValue, FieldLinksValue, FieldPhonesValue, + FieldRichTextValue, } from '@/object-record/record-field/types/FieldMetadata'; import { SettingsFieldTypeConfig } from '@/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs'; import { CompositeFieldType } from '@/settings/data-model/types/CompositeFieldType'; @@ -17,6 +18,7 @@ import { IllustrationIconMap, IllustrationIconPhone, IllustrationIconSetting, + IllustrationIconText, IllustrationIconUser, } from 'twenty-ui'; import { FieldMetadataType } from '~/generated-metadata/graphql'; @@ -178,4 +180,19 @@ export const SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS = { }, exampleValue: { source: 'source', name: 'name', workspaceMemberId: 'id' }, } as const satisfies SettingsCompositeFieldTypeConfig, + [FieldMetadataType.RichText]: { + label: 'Rich Text', + Icon: IllustrationIconText, + subFields: ['blocknote', 'markdown'], + filterableSubFields: [], + labelBySubField: { + blocknote: 'BlockNote', + markdown: 'Markdown', + }, + exampleValue: { + blocknote: 'TODO', // TODO + markdown: 'TODO', + }, + category: 'Basic', + } as const satisfies SettingsCompositeFieldTypeConfig, } as const satisfies SettingsCompositeFieldTypeConfigArray; diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts index a762b6f76dcb..b166dd714f1b 100644 --- a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts +++ b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts @@ -6,13 +6,12 @@ import { IllustrationIconJson, IllustrationIconNumbers, IllustrationIconOneToMany, - IllustrationIconSetting, IllustrationIconStar, IllustrationIconTag, IllustrationIconTags, IllustrationIconText, IllustrationIconToggle, - IllustrationIconUid, + IllustrationIconUid } from 'twenty-ui'; import { @@ -25,10 +24,9 @@ import { FieldNumberValue, FieldRatingValue, FieldRelationValue, - FieldRichTextValue, FieldSelectValue, FieldTextValue, - FieldUUidValue, + FieldUUidValue } from '@/object-record/record-field/types/FieldMetadata'; import { DEFAULT_DATE_VALUE } from '@/settings/data-model/constants/DefaultDateValue'; import { SettingsFieldTypeCategoryType } from '@/settings/data-model/types/SettingsFieldTypeCategoryType'; @@ -122,12 +120,6 @@ export const SETTINGS_NON_COMPOSITE_FIELD_TYPE_CONFIGS: SettingsNonCompositeFiel exampleValue: { key: 'value' }, category: 'Advanced', } as const satisfies SettingsFieldTypeConfig, - [FieldMetadataType.RichText]: { - label: 'Rich Text', - Icon: IllustrationIconSetting, - exampleValue: "{ key: 'value' }", - category: 'Basic', - } as const satisfies SettingsFieldTypeConfig, [FieldMetadataType.Array]: { label: 'Array', Icon: IllustrationIconArray, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/activity-query-result-getter.handler.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/activity-query-result-getter.handler.ts index f8dceb09215e..6f32a877d70e 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/activity-query-result-getter.handler.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/activity-query-result-getter.handler.ts @@ -21,7 +21,7 @@ export class ActivityQueryResultGetterHandler return activity; } - const body: RichTextBody = JSON.parse(activity.body); + const body: RichTextBody = JSON.parse(activity.body.blocknote); const bodyWithSignedPayload = await Promise.all( body.map(async (block: RichTextBlock) => { @@ -51,7 +51,10 @@ export class ActivityQueryResultGetterHandler return { ...activity, - body: JSON.stringify(bodyWithSignedPayload), + body: { + blocknote: JSON.stringify(bodyWithSignedPayload), + markdown: activity.body.markdown, + }, }; } } From b65d6f278d22bc50caf16cdcb5b25cccfc2aab99 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Thu, 2 Jan 2025 19:52:17 +0100 Subject: [PATCH 04/29] Add old RICH_TEXT field as RICH_TEXT_OLD --- .../src/generated-metadata/graphql.ts | 2 +- .../twenty-front/src/generated/graphql.tsx | 2 +- .../utils/mapFieldMetadataToGraphQLQuery.ts | 2 +- .../record-field/components/FieldDisplay.tsx | 4 +- .../record-field/hooks/usePersistField.ts | 12 ++--- .../meta-types/hooks/useRichTextField.ts | 24 ++++----- .../hooks/useRichTextFieldDisplay.ts | 12 ++--- .../input/components/RichTextFieldInput.tsx | 20 +++---- .../record-field/types/FieldMetadata.ts | 4 +- .../types/guards/assertFieldMetadata.ts | 52 +++++++++---------- .../types/guards/isFieldRichText.ts | 8 +-- .../types/guards/isFieldRichTextValue.ts | 1 - .../__tests__/isFieldValueReadOnly.test.ts | 6 +-- .../record-field/utils/isFieldValueEmpty.ts | 4 +- .../utils/isFieldValueReadOnly.ts | 4 +- .../utils/isRecordMatchingFilter.ts | 2 +- .../utils/generateEmptyFieldValue.ts | 2 +- .../utils/isFieldCellSupported.ts | 2 +- .../SettingsNonCompositeFieldTypeConfigs.ts | 18 ++++++- .../SettingsObjectNewFieldSelect.tsx | 2 +- .../constants/DefaultIconsByFieldType.ts | 2 +- .../generated/mock-metadata-query-result.ts | 4 +- .../__mocks__/object-metadata-item.mock.ts | 8 +-- .../services/type-mapper.service.ts | 3 ++ ...p-field-metadata-to-graphql-query.utils.ts | 1 + .../open-api/utils/components.utils.ts | 1 + .../dtos/default-value.input.ts | 6 +++ .../field-metadata/field-metadata.entity.ts | 2 +- .../field-metadata-default-value.interface.ts | 4 +- .../validate-default-value-for-type.util.ts | 1 + ...field-metadata-type-to-column-type.util.ts | 1 + .../workspace-migration.factory.ts | 4 ++ .../metadata-seeds/pets-metadata-seeds.ts | 2 +- .../utils/is-searchable-field.util.ts | 2 +- .../standard-objects/note.workspace-entity.ts | 5 +- .../standard-objects/task.workspace-entity.ts | 5 +- .../src/utils/computeInputFields.ts | 3 +- .../twenty-zapier/src/utils/data.types.ts | 2 +- 38 files changed, 137 insertions(+), 102 deletions(-) diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 52ed927fb8d1..52a5de2b3b5c 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -420,7 +420,7 @@ export enum FieldMetadataType { Rating = 'RATING', RawJson = 'RAW_JSON', Relation = 'RELATION', - RichText = 'RICH_TEXT', + RichTextOld = 'RICH_TEXT_OLD', Select = 'SELECT', Text = 'TEXT', TsVector = 'TS_VECTOR', diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index d7167df82462..2a3a8b9b4c26 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -374,7 +374,7 @@ export enum FieldMetadataType { Rating = 'RATING', RawJson = 'RAW_JSON', Relation = 'RELATION', - RichText = 'RICH_TEXT', + RichTextOld = 'RICH_TEXT_OLD', Select = 'SELECT', Text = 'TEXT', TsVector = 'TS_VECTOR', diff --git a/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts b/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts index 4cd8dbcbb009..51e373abdfba 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts @@ -35,7 +35,7 @@ export const mapFieldMetadataToGraphQLQuery = ({ FieldMetadataType.MultiSelect, FieldMetadataType.Position, FieldMetadataType.RawJson, - FieldMetadataType.RichText, + FieldMetadataType.RichTextOld, FieldMetadataType.Array, ].includes(fieldType); diff --git a/packages/twenty-front/src/modules/object-record/record-field/components/FieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/components/FieldDisplay.tsx index 41740a1a4191..525ed88a2b4d 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/components/FieldDisplay.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/components/FieldDisplay.tsx @@ -19,7 +19,7 @@ import { isFieldPhones } from '@/object-record/record-field/types/guards/isField import { isFieldRating } from '@/object-record/record-field/types/guards/isFieldRating'; import { isFieldRelationFromManyObjects } from '@/object-record/record-field/types/guards/isFieldRelationFromManyObjects'; import { isFieldRelationToOneObject } from '@/object-record/record-field/types/guards/isFieldRelationToOneObject'; -import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; +import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; import { FieldContext } from '../contexts/FieldContext'; import { AddressFieldDisplay } from '../meta-types/display/components/AddressFieldDisplay'; import { ChipFieldDisplay } from '../meta-types/display/components/ChipFieldDisplay'; @@ -88,7 +88,7 @@ export const FieldDisplay = () => { ) : isFieldRating(fieldDefinition) ? ( - ) : isFieldRichText(fieldDefinition) ? ( + ) : isFieldRichTextOld(fieldDefinition) ? ( ) : isFieldActor(fieldDefinition) ? ( diff --git a/packages/twenty-front/src/modules/object-record/record-field/hooks/usePersistField.ts b/packages/twenty-front/src/modules/object-record/record-field/hooks/usePersistField.ts index ecb9711af7d2..1ea95ea816f2 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/hooks/usePersistField.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/hooks/usePersistField.ts @@ -28,8 +28,8 @@ import { RecordForSelect } from '@/object-record/relation-picker/types/RecordFor import { isFieldArray } from '@/object-record/record-field/types/guards/isFieldArray'; import { isFieldArrayValue } from '@/object-record/record-field/types/guards/isFieldArrayValue'; -import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; -import { isFieldRichTextValue } from '@/object-record/record-field/types/guards/isFieldRichTextValue'; +import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; +import { isFieldRichTextOldValue } from '@/object-record/record-field/types/guards/isFieldRichTextOldValue'; import { FieldContext } from '../contexts/FieldContext'; import { isFieldBoolean } from '../types/guards/isFieldBoolean'; import { isFieldBooleanValue } from '../types/guards/isFieldBooleanValue'; @@ -113,9 +113,9 @@ export const usePersistField = () => { isFieldRawJson(fieldDefinition) && isFieldRawJsonValue(valueToPersist); - const fieldIsRichText = - isFieldRichText(fieldDefinition) && - isFieldRichTextValue(valueToPersist); + const fieldIsRichTextOld = + isFieldRichTextOld(fieldDefinition) && + isFieldRichTextOldValue(valueToPersist); const fieldIsArray = isFieldArray(fieldDefinition) && isFieldArrayValue(valueToPersist); @@ -138,7 +138,7 @@ export const usePersistField = () => { fieldIsAddress || fieldIsRawJson || fieldIsArray || - fieldIsRichText; + fieldIsRichTextOld; if (isValuePersistable) { const fieldName = fieldDefinition.metadata.fieldName; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextField.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextField.ts index cad7bc3bb53c..f87d27b6d1c3 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextField.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextField.ts @@ -2,40 +2,40 @@ import { useContext } from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput'; -import { FieldRichTextValue } from '@/object-record/record-field/types/FieldMetadata'; +import { FieldRichTextOldValue } from '@/object-record/record-field/types/FieldMetadata'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { usePersistField } from '@/object-record/record-field/hooks/usePersistField'; -import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; -import { isFieldRichTextValue } from '@/object-record/record-field/types/guards/isFieldRichTextValue'; +import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; +import { isFieldRichTextOldValue } from '@/object-record/record-field/types/guards/isFieldRichTextOldValue'; import { PartialBlock } from '@blocknote/core'; import { isNonEmptyString } from '@sniptt/guards'; import { FieldContext } from '../../contexts/FieldContext'; import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; -export const useRichTextField = () => { +export const useRichTextOldField = () => { const { recordId, fieldDefinition, hotkeyScope, maxWidth } = useContext(FieldContext); assertFieldMetadata( - FieldMetadataType.RichText, - isFieldRichText, + FieldMetadataType.RichTextOld, + isFieldRichTextOld, fieldDefinition, ); const fieldName = fieldDefinition.metadata.fieldName; - const [fieldValue, setFieldValue] = useRecoilState( + const [fieldValue, setFieldValue] = useRecoilState( recordStoreFamilySelector({ recordId, fieldName: fieldName, }), ); - const fieldRichTextValue = isFieldRichTextValue(fieldValue) ? fieldValue : ''; + const fieldRichTextOldValue = isFieldRichTextOldValue(fieldValue) ? fieldValue : ''; const { setDraftValue, getDraftValueSelector } = - useRecordFieldInput(`${recordId}-${fieldName}`); + useRecordFieldInput(`${recordId}-${fieldName}`); const draftValue = useRecoilValue(getDraftValueSelector()); @@ -45,7 +45,7 @@ export const useRichTextField = () => { const persistField = usePersistField(); - const persistRichTextField = (nextValue: PartialBlock[]) => { + const persistRichTextOldField = (nextValue: PartialBlock[]) => { if (!nextValue) { persistField(null); } else { @@ -60,9 +60,9 @@ export const useRichTextField = () => { setDraftValue, maxWidth, fieldDefinition, - fieldValue: fieldRichTextValue, + fieldValue: fieldRichTextOldValue, setFieldValue, hotkeyScope, - persistRichTextField, + persistRichTextOldField, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts index 4adbbf748b96..f49fb2de6b9a 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts @@ -2,26 +2,26 @@ import { useContext } from 'react'; import { useRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext'; -import { FieldRichTextValue } from '@/object-record/record-field/types/FieldMetadata'; +import { FieldRichTextOldValue } from '@/object-record/record-field/types/FieldMetadata'; import { assertFieldMetadata } from '@/object-record/record-field/types/guards/assertFieldMetadata'; -import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; +import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; import { PartialBlock } from '@blocknote/core'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { parseJson } from '~/utils/parseJson'; import { FieldContext } from '../../contexts/FieldContext'; -export const useRichTextFieldDisplay = () => { +export const useRichTextOldFieldDisplay = () => { const { recordId, fieldDefinition, hotkeyScope } = useContext(FieldContext); assertFieldMetadata( - FieldMetadataType.RichText, - isFieldRichText, + FieldMetadataType.RichTextOld, + isFieldRichTextOld, fieldDefinition, ); const fieldName = fieldDefinition.metadata.fieldName; - const fieldValue = useRecordFieldValue( + const fieldValue = useRecordFieldValue( recordId, fieldName, ); diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RichTextFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RichTextFieldInput.tsx index 718ae9e5af8d..3ab6df281489 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RichTextFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RichTextFieldInput.tsx @@ -1,6 +1,6 @@ import { BLOCK_SCHEMA } from '@/activities/blocks/constants/Schema'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; -import { useRichTextField } from '@/object-record/record-field/meta-types/hooks/useRichTextField'; +import { useRichTextOldField } from '@/object-record/record-field/meta-types/hooks/useRichTextOldField'; import { FieldInputClickOutsideEvent } from '@/object-record/record-field/meta-types/input/components/DateTimeFieldInput'; import { useRegisterInputEvents } from '@/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents'; import { BlockEditor } from '@/ui/input/editor/components/BlockEditor'; @@ -11,24 +11,24 @@ import styled from '@emotion/styled'; import { useContext, useRef } from 'react'; -const StyledRichTextContainer = styled.div` +const StyledRichTextOldContainer = styled.div` height: 400px; width: 500px; overflow: auto; `; -export type RichTextFieldInputProps = { +export type RichTextOldFieldInputProps = { onClickOutside?: FieldInputClickOutsideEvent; }; -export const RichTextFieldInput = ({ +export const RichTextOldFieldInput = ({ onClickOutside, -}: RichTextFieldInputProps) => { +}: RichTextOldFieldInputProps) => { const containerRef = useRef(null); const { recordId } = useContext(FieldContext); - const { draftValue, hotkeyScope, persistRichTextField, fieldDefinition } = - useRichTextField(); + const { draftValue, hotkeyScope, persistRichTextOldField, fieldDefinition } = + useRichTextOldField(); const editor = useCreateBlockNote({ initialContent: draftValue, @@ -37,7 +37,7 @@ export const RichTextFieldInput = ({ }); const handleClickOutside = (event: MouseEvent | TouchEvent) => { - onClickOutside?.(() => persistRichTextField(editor.document), event); + onClickOutside?.(() => persistRichTextOldField(editor.document), event); }; useRegisterInputEvents({ @@ -48,12 +48,12 @@ export const RichTextFieldInput = ({ }); return ( - + - + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts b/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts index 50fa47c9ddec..4887f2bf64cf 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts @@ -127,7 +127,7 @@ export type FieldRawJsonMetadata = { settings?: null; }; -export type FieldRichTextMetadata = { +export type FieldRichTextOldMetadata = { objectMetadataNameSingular?: string; fieldName: string; settings?: null; @@ -264,6 +264,8 @@ export type FieldRichTextValue = { markdown: string | null; }; +export type FieldRichTextOldValue = null | string; + export type FieldActorValue = { source: string; workspaceMemberId?: string; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts index 70640e39314e..7a288b2c1e4e 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts @@ -2,30 +2,30 @@ import { FieldMetadataType } from '~/generated-metadata/graphql'; import { FieldDefinition } from '../FieldDefinition'; import { - FieldActorMetadata, - FieldAddressMetadata, - FieldArrayMetadata, - FieldBooleanMetadata, - FieldCurrencyMetadata, - FieldDateMetadata, - FieldDateTimeMetadata, - FieldEmailMetadata, - FieldEmailsMetadata, - FieldFullNameMetadata, - FieldLinkMetadata, - FieldLinksMetadata, - FieldMetadata, - FieldMultiSelectMetadata, - FieldNumberMetadata, - FieldPhoneMetadata, - FieldPhonesMetadata, - FieldRatingMetadata, - FieldRawJsonMetadata, - FieldRelationMetadata, - FieldRichTextMetadata, - FieldSelectMetadata, - FieldTextMetadata, - FieldUuidMetadata, + FieldActorMetadata, + FieldAddressMetadata, + FieldArrayMetadata, + FieldBooleanMetadata, + FieldCurrencyMetadata, + FieldDateMetadata, + FieldDateTimeMetadata, + FieldEmailMetadata, + FieldEmailsMetadata, + FieldFullNameMetadata, + FieldLinkMetadata, + FieldLinksMetadata, + FieldMetadata, + FieldMultiSelectMetadata, + FieldNumberMetadata, + FieldPhoneMetadata, + FieldPhonesMetadata, + FieldRatingMetadata, + FieldRawJsonMetadata, + FieldRelationMetadata, + FieldRichTextOldMetadata, + FieldSelectMetadata, + FieldTextMetadata, + FieldUuidMetadata, } from '../FieldMetadata'; type AssertFieldMetadataFunction = < @@ -68,8 +68,8 @@ type AssertFieldMetadataFunction = < ? FieldAddressMetadata : E extends 'RAW_JSON' ? FieldRawJsonMetadata - : E extends 'RICH_TEXT' - ? FieldRichTextMetadata + : E extends 'RICH_TEXT_OLD' + ? FieldRichTextOldMetadata : E extends 'ACTOR' ? FieldActorMetadata : E extends 'ARRAY' diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichText.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichText.ts index 2b4c5908286c..df39f91af122 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichText.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichText.ts @@ -1,9 +1,9 @@ import { FieldMetadataType } from '~/generated-metadata/graphql'; import { FieldDefinition } from '../FieldDefinition'; -import { FieldMetadata, FieldRichTextMetadata } from '../FieldMetadata'; +import { FieldMetadata, FieldRichTextOldMetadata } from '../FieldMetadata'; -export const isFieldRichText = ( +export const isFieldRichTextOld = ( field: Pick, 'type'>, -): field is FieldDefinition => - field.type === FieldMetadataType.RichText; +): field is FieldDefinition => + field.type === FieldMetadataType.RichTextOld; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValue.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValue.ts index d6e0cfc355bf..b3d6c059c349 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValue.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValue.ts @@ -1,5 +1,4 @@ import { z } from 'zod'; -import { FieldRichTextValue } from '../FieldMetadata'; export const richTextSchema: z.ZodType = z.object({ blocknote: z.string().nullable(), diff --git a/packages/twenty-front/src/modules/object-record/record-field/utils/__tests__/isFieldValueReadOnly.test.ts b/packages/twenty-front/src/modules/object-record/record-field/utils/__tests__/isFieldValueReadOnly.test.ts index a5dff94f7a97..563dd7218b1d 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/utils/__tests__/isFieldValueReadOnly.test.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/utils/__tests__/isFieldValueReadOnly.test.ts @@ -89,15 +89,15 @@ describe('isFieldValueReadOnly', () => { expect(result).toBe(true); }); - it('should return true if fieldType is FieldMetadataType.RichText', () => { + it('should return true if fieldType is FieldMetadataType.RichTextOld', () => { const result = isFieldValueReadOnly({ - fieldType: FieldMetadataType.RichText, + fieldType: FieldMetadataType.RichTextOld, }); expect(result).toBe(true); }); - it('should return false if fieldType is not FieldMetadataType.Actor or FieldMetadataType.RichText', () => { + it('should return false if fieldType is not FieldMetadataType.Actor or FieldMetadataType.RichTextOld', () => { const result = isFieldValueReadOnly({ fieldType: FieldMetadataType.Text, }); diff --git a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueEmpty.ts b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueEmpty.ts index e8e3ebe3fb86..acab134f3784 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueEmpty.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueEmpty.ts @@ -28,7 +28,7 @@ import { isFieldPosition } from '@/object-record/record-field/types/guards/isFie import { isFieldRating } from '@/object-record/record-field/types/guards/isFieldRating'; import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson'; import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation'; -import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; +import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect'; import { isFieldSelectValue } from '@/object-record/record-field/types/guards/isFieldSelectValue'; import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText'; @@ -59,7 +59,7 @@ export const isFieldValueEmpty = ({ isFieldRating(fieldDefinition) || isFieldBoolean(fieldDefinition) || isFieldRawJson(fieldDefinition) || - isFieldRichText(fieldDefinition) || + isFieldRichTextOld(fieldDefinition) || isFieldPosition(fieldDefinition) ) { return isValueEmpty(fieldValue); diff --git a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts index e3e59574d385..594604984e7c 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts @@ -1,7 +1,7 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { isWorkflowSubObjectMetadata } from '@/object-metadata/utils/isWorkflowSubObjectMetadata'; import { isFieldActor } from '@/object-record/record-field/types/guards/isFieldActor'; -import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; +import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; import { isDefined } from 'twenty-ui'; import { FieldMetadataType } from '~/generated-metadata/graphql'; @@ -45,7 +45,7 @@ export const isFieldValueReadOnly = ({ if ( isDefined(fieldType) && - (isFieldActor({ type: fieldType }) || isFieldRichText({ type: fieldType })) + (isFieldActor({ type: fieldType }) || isFieldRichTextOld({ type: fieldType })) ) { return true; } diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts index 88b3780b8c7e..3c2f269ab242 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts @@ -177,7 +177,7 @@ export const isRecordMatchingFilter = ({ value: record[filterKey], }); } - case FieldMetadataType.RichText: { + case FieldMetadataType.RichTextOld: { // TODO: Implement a better rich text filter once it becomes a composite field // See this issue for more context: https://github.com/twentyhq/twenty/issues/7613#issuecomment-2408944585 // This should be tackled in Q4'24 diff --git a/packages/twenty-front/src/modules/object-record/utils/generateEmptyFieldValue.ts b/packages/twenty-front/src/modules/object-record/utils/generateEmptyFieldValue.ts index 7b1059b15eeb..8eabe621ea0c 100644 --- a/packages/twenty-front/src/modules/object-record/utils/generateEmptyFieldValue.ts +++ b/packages/twenty-front/src/modules/object-record/utils/generateEmptyFieldValue.ts @@ -81,7 +81,7 @@ export const generateEmptyFieldValue = ( case FieldMetadataType.RawJson: { return null; } - case FieldMetadataType.RichText: { + case FieldMetadataType.RichTextOld: { return null; } case FieldMetadataType.Actor: { diff --git a/packages/twenty-front/src/modules/object-record/utils/isFieldCellSupported.ts b/packages/twenty-front/src/modules/object-record/utils/isFieldCellSupported.ts index df03a87311ca..8944feca2d8b 100644 --- a/packages/twenty-front/src/modules/object-record/utils/isFieldCellSupported.ts +++ b/packages/twenty-front/src/modules/object-record/utils/isFieldCellSupported.ts @@ -15,7 +15,7 @@ export const isFieldCellSupported = ( [ FieldMetadataType.Uuid, FieldMetadataType.Position, - FieldMetadataType.RichText, + FieldMetadataType.RichTextOld, ].includes(fieldMetadataItem.type) ) { return false; diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts index b166dd714f1b..5dc4cca15c3e 100644 --- a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts +++ b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts @@ -11,7 +11,7 @@ import { IllustrationIconTags, IllustrationIconText, IllustrationIconToggle, - IllustrationIconUid + IllustrationIconUid, } from 'twenty-ui'; import { @@ -24,9 +24,11 @@ import { FieldNumberValue, FieldRatingValue, FieldRelationValue, + FieldRichTextValue, + FieldRichTextOldValue, FieldSelectValue, FieldTextValue, - FieldUUidValue + FieldUUidValue, } from '@/object-record/record-field/types/FieldMetadata'; import { DEFAULT_DATE_VALUE } from '@/settings/data-model/constants/DefaultDateValue'; import { SettingsFieldTypeCategoryType } from '@/settings/data-model/types/SettingsFieldTypeCategoryType'; @@ -120,6 +122,18 @@ export const SETTINGS_NON_COMPOSITE_FIELD_TYPE_CONFIGS: SettingsNonCompositeFiel exampleValue: { key: 'value' }, category: 'Advanced', } as const satisfies SettingsFieldTypeConfig, + [FieldMetadataType.RichText]: { + label: 'Rich Text', + Icon: IllustrationIconSetting, + exampleValue: "{ key: 'value' }", + category: 'Basic', + } as const satisfies SettingsFieldTypeConfig, + [FieldMetadataType.RichTextOld]: { + label: 'Rich Text Old', + Icon: IllustrationIconSetting, + exampleValue: "{ key: 'value' }", + category: 'Basic', + } as const satisfies SettingsFieldTypeConfig, [FieldMetadataType.Array]: { label: 'Array', Icon: IllustrationIconArray, diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect.tsx index 793279f38836..69306376b695 100644 --- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect.tsx @@ -44,7 +44,7 @@ export const SettingsObjectNewFieldSelect = () => { const excludedFieldTypes: SettingsFieldType[] = ( [ FieldMetadataType.Numeric, - FieldMetadataType.RichText, + FieldMetadataType.RichTextOld, FieldMetadataType.Actor, ] as const ).filter(isDefined); diff --git a/packages/twenty-front/src/pages/settings/data-model/constants/DefaultIconsByFieldType.ts b/packages/twenty-front/src/pages/settings/data-model/constants/DefaultIconsByFieldType.ts index 3b8f482b17fa..781e07e71c2c 100644 --- a/packages/twenty-front/src/pages/settings/data-model/constants/DefaultIconsByFieldType.ts +++ b/packages/twenty-front/src/pages/settings/data-model/constants/DefaultIconsByFieldType.ts @@ -22,6 +22,6 @@ export const DEFAULT_ICONS_BY_FIELD_TYPE: Record = { [FieldMetadataType.Actor]: 'IconUsers', [FieldMetadataType.Numeric]: 'IconUsers', [FieldMetadataType.Position]: 'IconUsers', - [FieldMetadataType.RichText]: 'IconUsers', + [FieldMetadataType.RichTextOld]: 'IconUsers', [FieldMetadataType.TsVector]: 'IconUsers', }; diff --git a/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts b/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts index 31e08110ea54..8dbabb21096c 100644 --- a/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts +++ b/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts @@ -3584,7 +3584,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = "node": { "__typename": "field", "id": "2fdf421d-fab6-4135-81ac-50582724e20e", - "type": "RICH_TEXT", + "type": "RICH_TEXT_OLD", "name": "body", "label": "Body", "description": "Note body", @@ -18827,7 +18827,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = "node": { "__typename": "field", "id": "ae853d86-e770-4196-8b4b-2dee50957236", - "type": "RICH_TEXT", + "type": "RICH_TEXT_OLD", "name": "body", "label": "Body", "description": "Task body", diff --git a/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts b/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts index 093b4a0efbee..76d9d5dcfd12 100644 --- a/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts +++ b/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts @@ -192,9 +192,9 @@ const fieldRawJsonMock = { defaultValue: null, }; -const fieldRichTextMock = { - name: 'fieldRichText', - type: FieldMetadataType.RICH_TEXT, +const fieldRichTextOldMock = { + name: 'fieldRichTextOld', + type: FieldMetadataType.RICH_TEXT_OLD, isNullable: true, defaultValue: null, }; @@ -257,7 +257,7 @@ export const fields = [ fieldPositionMock, fieldAddressMock, fieldRawJsonMock, - fieldRichTextMock, + fieldRichTextOldMock, fieldActorMock, fieldArrayMock, ]; diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts index f261581a662e..7e99cb8e9b8b 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts @@ -80,6 +80,7 @@ export class TypeMapperService { FieldMetadataType.ARRAY, StringArrayScalarType as unknown as GraphQLScalarType, ], + [FieldMetadataType.RICH_TEXT_OLD, GraphQLString], [FieldMetadataType.TS_VECTOR, GraphQLString], ]); @@ -114,6 +115,7 @@ export class TypeMapperService { [FieldMetadataType.NUMERIC, BigFloatFilterType], [FieldMetadataType.POSITION, FloatFilterType], [FieldMetadataType.RAW_JSON, RawJsonFilterType], + [FieldMetadataType.RICH_TEXT_OLD, StringFilterType], [FieldMetadataType.ARRAY, ArrayFilterType], [FieldMetadataType.MULTI_SELECT, MultiSelectFilterType], [FieldMetadataType.SELECT, SelectFilterType], @@ -139,6 +141,7 @@ export class TypeMapperService { [FieldMetadataType.MULTI_SELECT, OrderByDirectionType], [FieldMetadataType.POSITION, OrderByDirectionType], [FieldMetadataType.RAW_JSON, OrderByDirectionType], + [FieldMetadataType.RICH_TEXT_OLD, OrderByDirectionType], [FieldMetadataType.ARRAY, OrderByDirectionType], [FieldMetadataType.TS_VECTOR, OrderByDirectionType], // TODO: Add TSVectorOrderByType ]); diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts index 491b615b20c8..6a967267680a 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts @@ -28,6 +28,7 @@ export const mapFieldMetadataToGraphqlQuery = ( FieldMetadataType.MULTI_SELECT, FieldMetadataType.POSITION, FieldMetadataType.RAW_JSON, + FieldMetadataType.RICH_TEXT_OLD, FieldMetadataType.ARRAY, FieldMetadataType.TS_VECTOR, ].includes(fieldType); diff --git a/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts b/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts index 1056c015f75c..f02ff5d0d9ef 100644 --- a/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts +++ b/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts @@ -43,6 +43,7 @@ const getFieldProperties = (type: FieldMetadataType): Property => { case FieldMetadataType.UUID: return { type: 'string', format: 'uuid' }; case FieldMetadataType.TEXT: + case FieldMetadataType.RICH_TEXT_OLD: return { type: 'string' }; case FieldMetadataType.DATE_TIME: return { type: 'string', format: 'date-time' }; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts index f71500bf3c2f..ed17fe472b10 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts @@ -45,6 +45,12 @@ export class FieldMetadataDefaultValueRichText { markdown: string | null; } +export class FieldMetadataDefaultValueRichTextOld { + @ValidateIf((_object, value) => value !== null) + @IsString() + value: string | null; +} + export class FieldMetadataDefaultValueNumber { @ValidateIf((object, value) => value !== null) @IsNumber() diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts index 4f8e5219abb0..bdd241573c74 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts @@ -41,7 +41,7 @@ export enum FieldMetadataType { POSITION = 'POSITION', ADDRESS = 'ADDRESS', RAW_JSON = 'RAW_JSON', - RICH_TEXT = 'RICH_TEXT', + RICH_TEXT_OLD = 'RICH_TEXT_OLD', ACTOR = 'ACTOR', ARRAY = 'ARRAY', TS_VECTOR = 'TS_VECTOR', diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts index 8acf362373e7..7673ce753342 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts @@ -12,7 +12,7 @@ import { FieldMetadataDefaultValueNumber, FieldMetadataDefaultValuePhones, FieldMetadataDefaultValueRawJson, - FieldMetadataDefaultValueRichText, + FieldMetadataDefaultValueRichTextOld, FieldMetadataDefaultValueString, FieldMetadataDefaultValueUuidFunction, } from 'src/engine/metadata-modules/field-metadata/dtos/default-value.input'; @@ -47,7 +47,7 @@ type FieldMetadataDefaultValueMapping = { [FieldMetadataType.SELECT]: FieldMetadataDefaultValueString; [FieldMetadataType.MULTI_SELECT]: FieldMetadataDefaultValueString; [FieldMetadataType.RAW_JSON]: FieldMetadataDefaultValueRawJson; - [FieldMetadataType.RICH_TEXT]: FieldMetadataDefaultValueRichText; + [FieldMetadataType.RICH_TEXT_OLD]: FieldMetadataDefaultValueRichTextOld; [FieldMetadataType.ACTOR]: FieldMetadataDefaultActor; [FieldMetadataType.ARRAY]: FieldMetadataDefaultArray; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts index 111a42b14029..d5bd9eb25f30 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts @@ -49,6 +49,7 @@ export const defaultValueValidatorsMap = { [FieldMetadataType.MULTI_SELECT]: [FieldMetadataDefaultValueStringArray], [FieldMetadataType.ADDRESS]: [FieldMetadataDefaultValueAddress], [FieldMetadataType.RICH_TEXT]: [FieldMetadataDefaultValueRichText], + [FieldMetadataType.RICH_TEXT_OLD]: [FieldMetadataDefaultValueString], [FieldMetadataType.RAW_JSON]: [FieldMetadataDefaultValueRawJson], [FieldMetadataType.LINKS]: [FieldMetadataDefaultValueLinks], [FieldMetadataType.ACTOR]: [FieldMetadataDefaultActor], diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts index f83e0d91144c..3afff94dd643 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts @@ -15,6 +15,7 @@ export const fieldMetadataTypeToColumnType = ( case FieldMetadataType.UUID: return 'uuid'; case FieldMetadataType.TEXT: + case FieldMetadataType.RICH_TEXT_OLD: case FieldMetadataType.ARRAY: return 'text'; case FieldMetadataType.NUMERIC: diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts index d9ffda8a5fbf..3957476ad5d3 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts @@ -56,6 +56,10 @@ export class WorkspaceMigrationFactory { [FieldMetadataType.NUMBER, { factory: this.basicColumnActionFactory }], [FieldMetadataType.POSITION, { factory: this.basicColumnActionFactory }], [FieldMetadataType.RAW_JSON, { factory: this.basicColumnActionFactory }], + [ + FieldMetadataType.RICH_TEXT_OLD, + { factory: this.basicColumnActionFactory }, + ], [FieldMetadataType.BOOLEAN, { factory: this.basicColumnActionFactory }], [FieldMetadataType.DATE_TIME, { factory: this.basicColumnActionFactory }], [FieldMetadataType.DATE, { factory: this.basicColumnActionFactory }], diff --git a/packages/twenty-server/src/engine/seeder/metadata-seeds/pets-metadata-seeds.ts b/packages/twenty-server/src/engine/seeder/metadata-seeds/pets-metadata-seeds.ts index e3acc9daed49..3544acaeca0b 100644 --- a/packages/twenty-server/src/engine/seeder/metadata-seeds/pets-metadata-seeds.ts +++ b/packages/twenty-server/src/engine/seeder/metadata-seeds/pets-metadata-seeds.ts @@ -96,7 +96,7 @@ export const PETS_METADATA_SEEDS: ObjectMetadataSeed = { name: 'soundSwag', }, { - type: FieldMetadataType.RICH_TEXT, + type: FieldMetadataType.RICH_TEXT_OLD, label: 'Bio', name: 'bio', }, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util.ts index 78b9c62e75cf..a603617cfa36 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util.ts @@ -6,7 +6,7 @@ const SEARCHABLE_FIELD_TYPES = [ FieldMetadataType.EMAILS, FieldMetadataType.ADDRESS, FieldMetadataType.LINKS, - FieldMetadataType.RICH_TEXT, + FieldMetadataType.RICH_TEXT_OLD, ] as const; export type SearchableFieldType = (typeof SEARCHABLE_FIELD_TYPES)[number]; diff --git a/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts b/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts index 31d9ce222765..4c145dde1cc2 100644 --- a/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts +++ b/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts @@ -36,7 +36,8 @@ const BODY_FIELD_NAME = 'body'; export const SEARCH_FIELDS_FOR_NOTES: FieldTypeAndNameMetadata[] = [ { name: TITLE_FIELD_NAME, type: FieldMetadataType.TEXT }, - { name: `${BODY_FIELD_NAME}Blocknote`, type: FieldMetadataType.RICH_TEXT }, // TODO: Check later if and how this works + // { name: BODY_FIELD_NAME, type: FieldMetadataType.RICH_TEXT }, // TODO: Check later if and how this works + { name: BODY_FIELD_NAME, type: FieldMetadataType.RICH_TEXT_OLD }, ]; @WorkspaceEntity({ @@ -72,7 +73,7 @@ export class NoteWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceField({ standardId: NOTE_STANDARD_FIELD_IDS.body, - type: FieldMetadataType.RICH_TEXT, + type: FieldMetadataType.RICH_TEXT_OLD, label: 'Body', description: 'Note body', icon: 'IconFilePencil', diff --git a/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts b/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts index 490976972b7d..ce5efe288831 100644 --- a/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts +++ b/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts @@ -38,7 +38,8 @@ const BODY_FIELD_NAME = 'body'; export const SEARCH_FIELDS_FOR_TASK: FieldTypeAndNameMetadata[] = [ { name: TITLE_FIELD_NAME, type: FieldMetadataType.TEXT }, - { name: `${BODY_FIELD_NAME}Blocknote`, type: FieldMetadataType.RICH_TEXT }, // TODO: Check later if and how this works + // { name: BODY_FIELD_NAME, type: FieldMetadataType.RICH_TEXT }, // TODO: Check later if and how this works + { name: BODY_FIELD_NAME, type: FieldMetadataType.RICH_TEXT_OLD }, ]; @WorkspaceEntity({ @@ -74,7 +75,7 @@ export class TaskWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceField({ standardId: TASK_STANDARD_FIELD_IDS.body, - type: FieldMetadataType.RICH_TEXT, + type: FieldMetadataType.RICH_TEXT_OLD, label: 'Body', description: 'Task body', icon: 'IconFilePencil', diff --git a/packages/twenty-zapier/src/utils/computeInputFields.ts b/packages/twenty-zapier/src/utils/computeInputFields.ts index a288ec6ee2ac..d6069dd911dc 100644 --- a/packages/twenty-zapier/src/utils/computeInputFields.ts +++ b/packages/twenty-zapier/src/utils/computeInputFields.ts @@ -15,7 +15,7 @@ const getTypeFromFieldMetadataType = ( switch (fieldMetadataType) { case FieldMetadataType.UUID: case FieldMetadataType.TEXT: - case FieldMetadataType.RICH_TEXT: + case FieldMetadataType.RICH_TEXT_OLD: case FieldMetadataType.PHONE: case FieldMetadataType.EMAIL: case FieldMetadataType.LINK: @@ -285,6 +285,7 @@ export const computeInputFields = ( break; case FieldMetadataType.UUID: case FieldMetadataType.TEXT: + case FieldMetadataType.RICH_TEXT_OLD: case FieldMetadataType.PHONE: case FieldMetadataType.EMAIL: case FieldMetadataType.DATE_TIME: diff --git a/packages/twenty-zapier/src/utils/data.types.ts b/packages/twenty-zapier/src/utils/data.types.ts index 776b8c8cc3e4..345a5277e42b 100644 --- a/packages/twenty-zapier/src/utils/data.types.ts +++ b/packages/twenty-zapier/src/utils/data.types.ts @@ -53,7 +53,7 @@ export enum FieldMetadataType { MULTI_SELECT = 'MULTI_SELECT', POSITION = 'POSITION', ADDRESS = 'ADDRESS', - RICH_TEXT = 'RICH_TEXT', + RICH_TEXT_OLD = 'RICH_TEXT_OLD', ARRAY = 'ARRAY', // Ignored fieldTypes From 032c1152867d72bacecd13d9166680a99b33534a Mon Sep 17 00:00:00 2001 From: ad-elias Date: Thu, 2 Jan 2025 19:53:02 +0100 Subject: [PATCH 05/29] Add isFieldRichTextOldValue fn --- .../types/guards/isFieldRichTextOldValue.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextOldValue.ts diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextOldValue.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextOldValue.ts new file mode 100644 index 000000000000..51c5f09227e8 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextOldValue.ts @@ -0,0 +1,12 @@ +import { z } from 'zod'; +import { FieldRichTextOldValue } from '../FieldMetadata'; + +export const richTextOldSchema: z.ZodType = z.union([ + z.null(), // Exclude literal values other than null + z.string(), +]); + +export const isFieldRichTextOldValue = ( + fieldValue: unknown, +): fieldValue is FieldRichTextOldValue => + richTextOldSchema.safeParse(fieldValue).success; From 73dfe3071d416ba72ca04b25adb3bd21c8e4227b Mon Sep 17 00:00:00 2001 From: ad-elias Date: Fri, 3 Jan 2025 18:41:07 +0100 Subject: [PATCH 06/29] Fix errors adding RICH_TEXT_OLD --- .../src/generated-metadata/graphql.ts | 1 + .../twenty-front/src/generated/graphql.tsx | 3 +- .../meta-types/hooks/useRichTextField.ts | 19 +++-- .../hooks/useRichTextFieldDisplay.ts | 12 ++-- .../meta-types/hooks/useRichTextOldField.ts | 70 +++++++++++++++++++ .../hooks/useRichTextOldFieldDisplay.ts | 36 ++++++++++ .../record-field/types/FieldMetadata.ts | 9 ++- .../types/guards/assertFieldMetadata.ts | 69 +++++++++--------- .../types/guards/isFieldRichText.ts | 8 +-- .../types/guards/isFieldRichTextOld.ts | 9 +++ .../types/guards/isFieldRichTextValue.ts | 1 + .../SettingsNonCompositeFieldTypeConfigs.ts | 10 +-- .../constants/DefaultIconsByFieldType.ts | 1 + .../field-metadata/field-metadata.entity.ts | 1 + .../utils/is-searchable-field.util.ts | 1 + 15 files changed, 190 insertions(+), 60 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextOldField.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextOldFieldDisplay.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextOld.ts diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 52a5de2b3b5c..66ac4c6d1e43 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -421,6 +421,7 @@ export enum FieldMetadataType { RawJson = 'RAW_JSON', Relation = 'RELATION', RichTextOld = 'RICH_TEXT_OLD', + RichText = 'RICH_TEXT', Select = 'SELECT', Text = 'TEXT', TsVector = 'TS_VECTOR', diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 2a3a8b9b4c26..6b4205c802b4 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -1,5 +1,5 @@ -import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; +import { gql } from '@apollo/client'; export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; @@ -375,6 +375,7 @@ export enum FieldMetadataType { RawJson = 'RAW_JSON', Relation = 'RELATION', RichTextOld = 'RICH_TEXT_OLD', + RichText = 'RICH_TEXT', Select = 'SELECT', Text = 'TEXT', TsVector = 'TS_VECTOR', diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextField.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextField.ts index f87d27b6d1c3..a6e24d6058ed 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextField.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextField.ts @@ -2,19 +2,22 @@ import { useContext } from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput'; -import { FieldRichTextOldValue } from '@/object-record/record-field/types/FieldMetadata'; +import { + FieldRichTextOldValue, + FieldRichTextValue, +} from '@/object-record/record-field/types/FieldMetadata'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { usePersistField } from '@/object-record/record-field/hooks/usePersistField'; import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; -import { isFieldRichTextOldValue } from '@/object-record/record-field/types/guards/isFieldRichTextOldValue'; +import { isFieldRichTextValue } from '@/object-record/record-field/types/guards/isFieldRichTextValue'; import { PartialBlock } from '@blocknote/core'; import { isNonEmptyString } from '@sniptt/guards'; import { FieldContext } from '../../contexts/FieldContext'; import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; -export const useRichTextOldField = () => { +export const useRichTextField = () => { const { recordId, fieldDefinition, hotkeyScope, maxWidth } = useContext(FieldContext); @@ -32,7 +35,9 @@ export const useRichTextOldField = () => { fieldName: fieldName, }), ); - const fieldRichTextOldValue = isFieldRichTextOldValue(fieldValue) ? fieldValue : ''; + const fieldRichTextValue = isFieldRichTextValue(fieldValue) + ? fieldValue + : ({ blocknote: null, markdown: null } as FieldRichTextValue); const { setDraftValue, getDraftValueSelector } = useRecordFieldInput(`${recordId}-${fieldName}`); @@ -45,7 +50,7 @@ export const useRichTextOldField = () => { const persistField = usePersistField(); - const persistRichTextOldField = (nextValue: PartialBlock[]) => { + const persistRichTextField = (nextValue: PartialBlock[]) => { if (!nextValue) { persistField(null); } else { @@ -60,9 +65,9 @@ export const useRichTextOldField = () => { setDraftValue, maxWidth, fieldDefinition, - fieldValue: fieldRichTextOldValue, + fieldValue: fieldRichTextValue, setFieldValue, hotkeyScope, - persistRichTextOldField, + persistRichTextField, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts index f49fb2de6b9a..4adbbf748b96 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts @@ -2,26 +2,26 @@ import { useContext } from 'react'; import { useRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext'; -import { FieldRichTextOldValue } from '@/object-record/record-field/types/FieldMetadata'; +import { FieldRichTextValue } from '@/object-record/record-field/types/FieldMetadata'; import { assertFieldMetadata } from '@/object-record/record-field/types/guards/assertFieldMetadata'; -import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; +import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; import { PartialBlock } from '@blocknote/core'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { parseJson } from '~/utils/parseJson'; import { FieldContext } from '../../contexts/FieldContext'; -export const useRichTextOldFieldDisplay = () => { +export const useRichTextFieldDisplay = () => { const { recordId, fieldDefinition, hotkeyScope } = useContext(FieldContext); assertFieldMetadata( - FieldMetadataType.RichTextOld, - isFieldRichTextOld, + FieldMetadataType.RichText, + isFieldRichText, fieldDefinition, ); const fieldName = fieldDefinition.metadata.fieldName; - const fieldValue = useRecordFieldValue( + const fieldValue = useRecordFieldValue( recordId, fieldName, ); diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextOldField.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextOldField.ts new file mode 100644 index 000000000000..3171ff600101 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextOldField.ts @@ -0,0 +1,70 @@ +import { useContext } from 'react'; +import { useRecoilState, useRecoilValue } from 'recoil'; + +import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput'; +import { FieldRichTextOldValue } from '@/object-record/record-field/types/FieldMetadata'; +import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; +import { FieldMetadataType } from '~/generated-metadata/graphql'; + +import { usePersistField } from '@/object-record/record-field/hooks/usePersistField'; +import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; +import { isFieldRichTextOldValue } from '@/object-record/record-field/types/guards/isFieldRichTextOldValue'; +import { PartialBlock } from '@blocknote/core'; +import { isNonEmptyString } from '@sniptt/guards'; +import { FieldContext } from '../../contexts/FieldContext'; +import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; + +export const useRichTextOldField = () => { + const { recordId, fieldDefinition, hotkeyScope, maxWidth } = + useContext(FieldContext); + + assertFieldMetadata( + FieldMetadataType.RichTextOld, + isFieldRichTextOld, + fieldDefinition, + ); + + const fieldName = fieldDefinition.metadata.fieldName; + + const [fieldValue, setFieldValue] = useRecoilState( + recordStoreFamilySelector({ + recordId, + fieldName: fieldName, + }), + ); + const fieldRichTextOldValue = isFieldRichTextOldValue(fieldValue) + ? fieldValue + : ''; + + const { setDraftValue, getDraftValueSelector } = + useRecordFieldInput(`${recordId}-${fieldName}`); + + const draftValue = useRecoilValue(getDraftValueSelector()); + + const draftValueParsed: PartialBlock[] = isNonEmptyString(draftValue) + ? JSON.parse(draftValue) + : draftValue; + + const persistField = usePersistField(); + + const persistRichTextOldField = (nextValue: PartialBlock[]) => { + if (!nextValue) { + persistField(null); + } else { + const parsedValueToPersist = JSON.stringify(nextValue); + + persistField(parsedValueToPersist); + } + }; + + return { + draftValue: draftValueParsed, + setDraftValue, + maxWidth, + fieldDefinition, + fieldValue: fieldRichTextOldValue, + setFieldValue, + hotkeyScope, + persistRichTextOldField, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextOldFieldDisplay.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextOldFieldDisplay.ts new file mode 100644 index 000000000000..834688058155 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextOldFieldDisplay.ts @@ -0,0 +1,36 @@ +import { useContext } from 'react'; + +import { useRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext'; + +import { FieldRichTextOldValue } from '@/object-record/record-field/types/FieldMetadata'; +import { assertFieldMetadata } from '@/object-record/record-field/types/guards/assertFieldMetadata'; +import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; +import { PartialBlock } from '@blocknote/core'; +import { FieldMetadataType } from '~/generated-metadata/graphql'; +import { parseJson } from '~/utils/parseJson'; +import { FieldContext } from '../../contexts/FieldContext'; + +export const useRichTextOldFieldDisplay = () => { + const { recordId, fieldDefinition, hotkeyScope } = useContext(FieldContext); + + assertFieldMetadata( + FieldMetadataType.RichTextOld, + isFieldRichTextOld, + fieldDefinition, + ); + + const fieldName = fieldDefinition.metadata.fieldName; + + const fieldValue = useRecordFieldValue( + recordId, + fieldName, + ); + + const fieldValueParsed = parseJson(fieldValue); + + return { + fieldDefinition, + fieldValue: fieldValueParsed, + hotkeyScope, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts b/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts index 4887f2bf64cf..16394c478573 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts @@ -127,6 +127,12 @@ export type FieldRawJsonMetadata = { settings?: null; }; +export type FieldRichTextMetadata = { + objectMetadataNameSingular?: string; + fieldName: string; + settings?: null; +}; + export type FieldRichTextOldMetadata = { objectMetadataNameSingular?: string; fieldName: string; @@ -211,7 +217,8 @@ export type FieldMetadata = | FieldActorMetadata | FieldArrayMetadata | FieldTsVectorMetadata - | FieldRichTextMetadata; + | FieldRichTextMetadata + | FieldRichTextOldMetadata; export type FieldTextValue = string; export type FieldUUidValue = string; // TODO: can we replace with a template literal type, or maybe overkill ? diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts index 7a288b2c1e4e..6a433d3c1f89 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts @@ -2,30 +2,31 @@ import { FieldMetadataType } from '~/generated-metadata/graphql'; import { FieldDefinition } from '../FieldDefinition'; import { - FieldActorMetadata, - FieldAddressMetadata, - FieldArrayMetadata, - FieldBooleanMetadata, - FieldCurrencyMetadata, - FieldDateMetadata, - FieldDateTimeMetadata, - FieldEmailMetadata, - FieldEmailsMetadata, - FieldFullNameMetadata, - FieldLinkMetadata, - FieldLinksMetadata, - FieldMetadata, - FieldMultiSelectMetadata, - FieldNumberMetadata, - FieldPhoneMetadata, - FieldPhonesMetadata, - FieldRatingMetadata, - FieldRawJsonMetadata, - FieldRelationMetadata, - FieldRichTextOldMetadata, - FieldSelectMetadata, - FieldTextMetadata, - FieldUuidMetadata, + FieldActorMetadata, + FieldAddressMetadata, + FieldArrayMetadata, + FieldBooleanMetadata, + FieldCurrencyMetadata, + FieldDateMetadata, + FieldDateTimeMetadata, + FieldEmailMetadata, + FieldEmailsMetadata, + FieldFullNameMetadata, + FieldLinkMetadata, + FieldLinksMetadata, + FieldMetadata, + FieldMultiSelectMetadata, + FieldNumberMetadata, + FieldPhoneMetadata, + FieldPhonesMetadata, + FieldRatingMetadata, + FieldRawJsonMetadata, + FieldRelationMetadata, + FieldRichTextMetadata, + FieldRichTextOldMetadata, + FieldSelectMetadata, + FieldTextMetadata, + FieldUuidMetadata, } from '../FieldMetadata'; type AssertFieldMetadataFunction = < @@ -68,15 +69,17 @@ type AssertFieldMetadataFunction = < ? FieldAddressMetadata : E extends 'RAW_JSON' ? FieldRawJsonMetadata - : E extends 'RICH_TEXT_OLD' - ? FieldRichTextOldMetadata - : E extends 'ACTOR' - ? FieldActorMetadata - : E extends 'ARRAY' - ? FieldArrayMetadata - : E extends 'PHONES' - ? FieldPhonesMetadata - : never, + : E extends 'RICH_TEXT' + ? FieldRichTextMetadata + : E extends 'RICH_TEXT_OLD' + ? FieldRichTextOldMetadata + : E extends 'ACTOR' + ? FieldActorMetadata + : E extends 'ARRAY' + ? FieldArrayMetadata + : E extends 'PHONES' + ? FieldPhonesMetadata + : never, >( fieldType: E, fieldTypeGuard: ( diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichText.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichText.ts index df39f91af122..2b4c5908286c 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichText.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichText.ts @@ -1,9 +1,9 @@ import { FieldMetadataType } from '~/generated-metadata/graphql'; import { FieldDefinition } from '../FieldDefinition'; -import { FieldMetadata, FieldRichTextOldMetadata } from '../FieldMetadata'; +import { FieldMetadata, FieldRichTextMetadata } from '../FieldMetadata'; -export const isFieldRichTextOld = ( +export const isFieldRichText = ( field: Pick, 'type'>, -): field is FieldDefinition => - field.type === FieldMetadataType.RichTextOld; +): field is FieldDefinition => + field.type === FieldMetadataType.RichText; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextOld.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextOld.ts new file mode 100644 index 000000000000..df39f91af122 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextOld.ts @@ -0,0 +1,9 @@ +import { FieldMetadataType } from '~/generated-metadata/graphql'; + +import { FieldDefinition } from '../FieldDefinition'; +import { FieldMetadata, FieldRichTextOldMetadata } from '../FieldMetadata'; + +export const isFieldRichTextOld = ( + field: Pick, 'type'>, +): field is FieldDefinition => + field.type === FieldMetadataType.RichTextOld; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValue.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValue.ts index b3d6c059c349..d37e1e0678a9 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValue.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValue.ts @@ -1,3 +1,4 @@ +import { FieldRichTextValue } from '@/object-record/record-field/types/FieldMetadata'; import { z } from 'zod'; export const richTextSchema: z.ZodType = z.object({ diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts index 5dc4cca15c3e..542d5f5de418 100644 --- a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts +++ b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts @@ -6,6 +6,7 @@ import { IllustrationIconJson, IllustrationIconNumbers, IllustrationIconOneToMany, + IllustrationIconSetting, IllustrationIconStar, IllustrationIconTag, IllustrationIconTags, @@ -24,11 +25,10 @@ import { FieldNumberValue, FieldRatingValue, FieldRelationValue, - FieldRichTextValue, FieldRichTextOldValue, FieldSelectValue, FieldTextValue, - FieldUUidValue, + FieldUUidValue } from '@/object-record/record-field/types/FieldMetadata'; import { DEFAULT_DATE_VALUE } from '@/settings/data-model/constants/DefaultDateValue'; import { SettingsFieldTypeCategoryType } from '@/settings/data-model/types/SettingsFieldTypeCategoryType'; @@ -122,12 +122,6 @@ export const SETTINGS_NON_COMPOSITE_FIELD_TYPE_CONFIGS: SettingsNonCompositeFiel exampleValue: { key: 'value' }, category: 'Advanced', } as const satisfies SettingsFieldTypeConfig, - [FieldMetadataType.RichText]: { - label: 'Rich Text', - Icon: IllustrationIconSetting, - exampleValue: "{ key: 'value' }", - category: 'Basic', - } as const satisfies SettingsFieldTypeConfig, [FieldMetadataType.RichTextOld]: { label: 'Rich Text Old', Icon: IllustrationIconSetting, diff --git a/packages/twenty-front/src/pages/settings/data-model/constants/DefaultIconsByFieldType.ts b/packages/twenty-front/src/pages/settings/data-model/constants/DefaultIconsByFieldType.ts index 781e07e71c2c..d0a2e18ad9d2 100644 --- a/packages/twenty-front/src/pages/settings/data-model/constants/DefaultIconsByFieldType.ts +++ b/packages/twenty-front/src/pages/settings/data-model/constants/DefaultIconsByFieldType.ts @@ -23,5 +23,6 @@ export const DEFAULT_ICONS_BY_FIELD_TYPE: Record = { [FieldMetadataType.Numeric]: 'IconUsers', [FieldMetadataType.Position]: 'IconUsers', [FieldMetadataType.RichTextOld]: 'IconUsers', + [FieldMetadataType.RichText]: 'IconUsers', [FieldMetadataType.TsVector]: 'IconUsers', }; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts index bdd241573c74..0c6cca5b2014 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts @@ -41,6 +41,7 @@ export enum FieldMetadataType { POSITION = 'POSITION', ADDRESS = 'ADDRESS', RAW_JSON = 'RAW_JSON', + RICH_TEXT = 'RICH_TEXT', RICH_TEXT_OLD = 'RICH_TEXT_OLD', ACTOR = 'ACTOR', ARRAY = 'ARRAY', diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util.ts index a603617cfa36..2889760ea664 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util.ts @@ -7,6 +7,7 @@ const SEARCHABLE_FIELD_TYPES = [ FieldMetadataType.ADDRESS, FieldMetadataType.LINKS, FieldMetadataType.RICH_TEXT_OLD, + FieldMetadataType.RICH_TEXT, ] as const; export type SearchableFieldType = (typeof SEARCHABLE_FIELD_TYPES)[number]; From 10ffd6ae37293519aa19efcb0bb200fe7824136f Mon Sep 17 00:00:00 2001 From: ad-elias Date: Sat, 4 Jan 2025 12:21:32 +0100 Subject: [PATCH 07/29] Rename to RICH_TEXT_DEPRECATED --- .../src/generated-metadata/graphql.ts | 2 +- .../twenty-front/src/generated/graphql.tsx | 2 +- .../utils/mapFieldMetadataToGraphQLQuery.ts | 2 +- .../record-field/components/FieldDisplay.tsx | 4 +- .../record-field/hooks/usePersistField.ts | 12 +++--- ...Field.ts => useRichTextDeprecatedField.ts} | 39 +++++++++++-------- ...s => useRichTextDeprecatedFieldDisplay.ts} | 17 ++++---- .../meta-types/hooks/useRichTextField.ts | 25 ++++++------ .../input/components/RichTextFieldInput.tsx | 27 ++++++++----- .../record-field/types/FieldMetadata.ts | 6 +-- .../types/guards/assertFieldMetadata.ts | 4 +- .../types/guards/isFieldRichTextDeprecated.ts | 12 ++++++ .../guards/isFieldRichTextDeprecatedValue.ts | 13 +++++++ .../types/guards/isFieldRichTextOld.ts | 9 ----- .../types/guards/isFieldRichTextOldValue.ts | 12 ------ .../__tests__/isFieldValueReadOnly.test.ts | 6 +-- .../record-field/utils/isFieldValueEmpty.ts | 4 +- .../utils/isFieldValueReadOnly.ts | 5 ++- .../utils/isRecordMatchingFilter.ts | 2 +- .../utils/generateEmptyFieldValue.ts | 2 +- .../utils/isFieldCellSupported.ts | 2 +- .../SettingsNonCompositeFieldTypeConfigs.ts | 8 ++-- .../SettingsObjectNewFieldSelect.tsx | 2 +- .../constants/DefaultIconsByFieldType.ts | 2 +- .../__mocks__/object-metadata-item.mock.ts | 6 +-- .../dtos/default-value.input.ts | 26 ++++++------- .../field-metadata-default-value.interface.ts | 34 ++++++++-------- 27 files changed, 152 insertions(+), 133 deletions(-) rename packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/{useRichTextOldField.ts => useRichTextDeprecatedField.ts} (60%) rename packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/{useRichTextOldFieldDisplay.ts => useRichTextDeprecatedFieldDisplay.ts} (64%) create mode 100644 packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextDeprecated.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextDeprecatedValue.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextOld.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextOldValue.ts diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 66ac4c6d1e43..f76f5622c1de 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -420,7 +420,7 @@ export enum FieldMetadataType { Rating = 'RATING', RawJson = 'RAW_JSON', Relation = 'RELATION', - RichTextOld = 'RICH_TEXT_OLD', + RichTextDeprecated = 'RICH_TEXT_OLD', RichText = 'RICH_TEXT', Select = 'SELECT', Text = 'TEXT', diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 6b4205c802b4..7e098fea2805 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -374,7 +374,7 @@ export enum FieldMetadataType { Rating = 'RATING', RawJson = 'RAW_JSON', Relation = 'RELATION', - RichTextOld = 'RICH_TEXT_OLD', + RichTextDeprecated = 'RICH_TEXT_OLD', RichText = 'RICH_TEXT', Select = 'SELECT', Text = 'TEXT', diff --git a/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts b/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts index 51e373abdfba..9028fe6bc319 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts @@ -35,7 +35,7 @@ export const mapFieldMetadataToGraphQLQuery = ({ FieldMetadataType.MultiSelect, FieldMetadataType.Position, FieldMetadataType.RawJson, - FieldMetadataType.RichTextOld, + FieldMetadataType.RichTextDeprecated, FieldMetadataType.Array, ].includes(fieldType); diff --git a/packages/twenty-front/src/modules/object-record/record-field/components/FieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/components/FieldDisplay.tsx index 525ed88a2b4d..16e9b19ea924 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/components/FieldDisplay.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/components/FieldDisplay.tsx @@ -19,7 +19,7 @@ import { isFieldPhones } from '@/object-record/record-field/types/guards/isField import { isFieldRating } from '@/object-record/record-field/types/guards/isFieldRating'; import { isFieldRelationFromManyObjects } from '@/object-record/record-field/types/guards/isFieldRelationFromManyObjects'; import { isFieldRelationToOneObject } from '@/object-record/record-field/types/guards/isFieldRelationToOneObject'; -import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; +import { isFieldRichTextDeprecated } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecated'; import { FieldContext } from '../contexts/FieldContext'; import { AddressFieldDisplay } from '../meta-types/display/components/AddressFieldDisplay'; import { ChipFieldDisplay } from '../meta-types/display/components/ChipFieldDisplay'; @@ -88,7 +88,7 @@ export const FieldDisplay = () => { ) : isFieldRating(fieldDefinition) ? ( - ) : isFieldRichTextOld(fieldDefinition) ? ( + ) : isFieldRichTextDeprecated(fieldDefinition) ? ( ) : isFieldActor(fieldDefinition) ? ( diff --git a/packages/twenty-front/src/modules/object-record/record-field/hooks/usePersistField.ts b/packages/twenty-front/src/modules/object-record/record-field/hooks/usePersistField.ts index 1ea95ea816f2..51ba775af5d2 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/hooks/usePersistField.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/hooks/usePersistField.ts @@ -28,8 +28,8 @@ import { RecordForSelect } from '@/object-record/relation-picker/types/RecordFor import { isFieldArray } from '@/object-record/record-field/types/guards/isFieldArray'; import { isFieldArrayValue } from '@/object-record/record-field/types/guards/isFieldArrayValue'; -import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; -import { isFieldRichTextOldValue } from '@/object-record/record-field/types/guards/isFieldRichTextOldValue'; +import { isFieldRichTextDeprecated } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecated'; +import { isFieldRichTextDeprecatedValue } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecatedValue'; import { FieldContext } from '../contexts/FieldContext'; import { isFieldBoolean } from '../types/guards/isFieldBoolean'; import { isFieldBooleanValue } from '../types/guards/isFieldBooleanValue'; @@ -113,9 +113,9 @@ export const usePersistField = () => { isFieldRawJson(fieldDefinition) && isFieldRawJsonValue(valueToPersist); - const fieldIsRichTextOld = - isFieldRichTextOld(fieldDefinition) && - isFieldRichTextOldValue(valueToPersist); + const fieldIsRichTextDeprecated = + isFieldRichTextDeprecated(fieldDefinition) && + isFieldRichTextDeprecatedValue(valueToPersist); const fieldIsArray = isFieldArray(fieldDefinition) && isFieldArrayValue(valueToPersist); @@ -138,7 +138,7 @@ export const usePersistField = () => { fieldIsAddress || fieldIsRawJson || fieldIsArray || - fieldIsRichTextOld; + fieldIsRichTextDeprecated; if (isValuePersistable) { const fieldName = fieldDefinition.metadata.fieldName; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextOldField.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextDeprecatedField.ts similarity index 60% rename from packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextOldField.ts rename to packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextDeprecatedField.ts index 3171ff600101..f54c3c970cd1 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextOldField.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextDeprecatedField.ts @@ -2,42 +2,47 @@ import { useContext } from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput'; -import { FieldRichTextOldValue } from '@/object-record/record-field/types/FieldMetadata'; +import { FieldRichTextDeprecatedValue } from '@/object-record/record-field/types/FieldMetadata'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { usePersistField } from '@/object-record/record-field/hooks/usePersistField'; -import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; -import { isFieldRichTextOldValue } from '@/object-record/record-field/types/guards/isFieldRichTextOldValue'; +import { isFieldRichTextDeprecated } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecated'; +import { isFieldRichTextDeprecatedValue } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecatedValue'; import { PartialBlock } from '@blocknote/core'; import { isNonEmptyString } from '@sniptt/guards'; import { FieldContext } from '../../contexts/FieldContext'; import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; -export const useRichTextOldField = () => { +export const useRichTextDeprecatedField = () => { const { recordId, fieldDefinition, hotkeyScope, maxWidth } = useContext(FieldContext); assertFieldMetadata( - FieldMetadataType.RichTextOld, - isFieldRichTextOld, + FieldMetadataType.RichTextDeprecated, + isFieldRichTextDeprecated, fieldDefinition, ); const fieldName = fieldDefinition.metadata.fieldName; - const [fieldValue, setFieldValue] = useRecoilState( - recordStoreFamilySelector({ - recordId, - fieldName: fieldName, - }), - ); - const fieldRichTextOldValue = isFieldRichTextOldValue(fieldValue) + const [fieldValue, setFieldValue] = + useRecoilState( + recordStoreFamilySelector({ + recordId, + fieldName: fieldName, + }), + ); + const fieldRichTextDeprecatedValue = isFieldRichTextDeprecatedValue( + fieldValue, + ) ? fieldValue : ''; const { setDraftValue, getDraftValueSelector } = - useRecordFieldInput(`${recordId}-${fieldName}`); + useRecordFieldInput( + `${recordId}-${fieldName}`, + ); const draftValue = useRecoilValue(getDraftValueSelector()); @@ -47,7 +52,7 @@ export const useRichTextOldField = () => { const persistField = usePersistField(); - const persistRichTextOldField = (nextValue: PartialBlock[]) => { + const persistRichTextDeprecatedField = (nextValue: PartialBlock[]) => { if (!nextValue) { persistField(null); } else { @@ -62,9 +67,9 @@ export const useRichTextOldField = () => { setDraftValue, maxWidth, fieldDefinition, - fieldValue: fieldRichTextOldValue, + fieldValue: fieldRichTextDeprecatedValue, setFieldValue, hotkeyScope, - persistRichTextOldField, + persistRichTextDeprecatedField, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextOldFieldDisplay.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextDeprecatedFieldDisplay.ts similarity index 64% rename from packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextOldFieldDisplay.ts rename to packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextDeprecatedFieldDisplay.ts index 834688058155..0ae4910066d3 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextOldFieldDisplay.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextDeprecatedFieldDisplay.ts @@ -2,29 +2,28 @@ import { useContext } from 'react'; import { useRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext'; -import { FieldRichTextOldValue } from '@/object-record/record-field/types/FieldMetadata'; +import { FieldRichTextDeprecatedValue } from '@/object-record/record-field/types/FieldMetadata'; import { assertFieldMetadata } from '@/object-record/record-field/types/guards/assertFieldMetadata'; -import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; +import { isFieldRichTextDeprecated } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecated'; import { PartialBlock } from '@blocknote/core'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { parseJson } from '~/utils/parseJson'; import { FieldContext } from '../../contexts/FieldContext'; -export const useRichTextOldFieldDisplay = () => { +export const useRichTextDeprecatedFieldDisplay = () => { const { recordId, fieldDefinition, hotkeyScope } = useContext(FieldContext); assertFieldMetadata( - FieldMetadataType.RichTextOld, - isFieldRichTextOld, + FieldMetadataType.RichTextDeprecated, + isFieldRichTextDeprecated, fieldDefinition, ); const fieldName = fieldDefinition.metadata.fieldName; - const fieldValue = useRecordFieldValue( - recordId, - fieldName, - ); + const fieldValue = useRecordFieldValue< + FieldRichTextDeprecatedValue | undefined + >(recordId, fieldName); const fieldValueParsed = parseJson(fieldValue); diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextField.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextField.ts index a6e24d6058ed..557b9b93517c 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextField.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextField.ts @@ -3,14 +3,14 @@ import { useRecoilState, useRecoilValue } from 'recoil'; import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput'; import { - FieldRichTextOldValue, + FieldRichTextDeprecatedValue, FieldRichTextValue, } from '@/object-record/record-field/types/FieldMetadata'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { usePersistField } from '@/object-record/record-field/hooks/usePersistField'; -import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; +import { isFieldRichTextDeprecated } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecated'; import { isFieldRichTextValue } from '@/object-record/record-field/types/guards/isFieldRichTextValue'; import { PartialBlock } from '@blocknote/core'; import { isNonEmptyString } from '@sniptt/guards'; @@ -22,25 +22,28 @@ export const useRichTextField = () => { useContext(FieldContext); assertFieldMetadata( - FieldMetadataType.RichTextOld, - isFieldRichTextOld, + FieldMetadataType.RichTextDeprecated, + isFieldRichTextDeprecated, fieldDefinition, ); const fieldName = fieldDefinition.metadata.fieldName; - const [fieldValue, setFieldValue] = useRecoilState( - recordStoreFamilySelector({ - recordId, - fieldName: fieldName, - }), - ); + const [fieldValue, setFieldValue] = + useRecoilState( + recordStoreFamilySelector({ + recordId, + fieldName: fieldName, + }), + ); const fieldRichTextValue = isFieldRichTextValue(fieldValue) ? fieldValue : ({ blocknote: null, markdown: null } as FieldRichTextValue); const { setDraftValue, getDraftValueSelector } = - useRecordFieldInput(`${recordId}-${fieldName}`); + useRecordFieldInput( + `${recordId}-${fieldName}`, + ); const draftValue = useRecoilValue(getDraftValueSelector()); diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RichTextFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RichTextFieldInput.tsx index 3ab6df281489..7ae1dece6397 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RichTextFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RichTextFieldInput.tsx @@ -1,6 +1,6 @@ import { BLOCK_SCHEMA } from '@/activities/blocks/constants/Schema'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; -import { useRichTextOldField } from '@/object-record/record-field/meta-types/hooks/useRichTextOldField'; +import { useRichTextDeprecatedField } from '@/object-record/record-field/meta-types/hooks/useRichTextDeprecatedField'; import { FieldInputClickOutsideEvent } from '@/object-record/record-field/meta-types/input/components/DateTimeFieldInput'; import { useRegisterInputEvents } from '@/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents'; import { BlockEditor } from '@/ui/input/editor/components/BlockEditor'; @@ -11,24 +11,28 @@ import styled from '@emotion/styled'; import { useContext, useRef } from 'react'; -const StyledRichTextOldContainer = styled.div` +const StyledRichTextDeprecatedContainer = styled.div` height: 400px; width: 500px; overflow: auto; `; -export type RichTextOldFieldInputProps = { +export type RichTextDeprecatedFieldInputProps = { onClickOutside?: FieldInputClickOutsideEvent; }; -export const RichTextOldFieldInput = ({ +export const RichTextDeprecatedFieldInput = ({ onClickOutside, -}: RichTextOldFieldInputProps) => { +}: RichTextDeprecatedFieldInputProps) => { const containerRef = useRef(null); const { recordId } = useContext(FieldContext); - const { draftValue, hotkeyScope, persistRichTextOldField, fieldDefinition } = - useRichTextOldField(); + const { + draftValue, + hotkeyScope, + persistRichTextDeprecatedField, + fieldDefinition, + } = useRichTextDeprecatedField(); const editor = useCreateBlockNote({ initialContent: draftValue, @@ -37,7 +41,10 @@ export const RichTextOldFieldInput = ({ }); const handleClickOutside = (event: MouseEvent | TouchEvent) => { - onClickOutside?.(() => persistRichTextOldField(editor.document), event); + onClickOutside?.( + () => persistRichTextDeprecatedField(editor.document), + event, + ); }; useRegisterInputEvents({ @@ -48,12 +55,12 @@ export const RichTextOldFieldInput = ({ }); return ( - + - + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts b/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts index 16394c478573..d492268d4587 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts @@ -133,7 +133,7 @@ export type FieldRichTextMetadata = { settings?: null; }; -export type FieldRichTextOldMetadata = { +export type FieldRichTextDeprecatedMetadata = { objectMetadataNameSingular?: string; fieldName: string; settings?: null; @@ -218,7 +218,7 @@ export type FieldMetadata = | FieldArrayMetadata | FieldTsVectorMetadata | FieldRichTextMetadata - | FieldRichTextOldMetadata; + | FieldRichTextDeprecatedMetadata; export type FieldTextValue = string; export type FieldUUidValue = string; // TODO: can we replace with a template literal type, or maybe overkill ? @@ -271,7 +271,7 @@ export type FieldRichTextValue = { markdown: string | null; }; -export type FieldRichTextOldValue = null | string; +export type FieldRichTextDeprecatedValue = null | string; export type FieldActorValue = { source: string; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts index 6a433d3c1f89..6530835e28bd 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts @@ -22,8 +22,8 @@ import { FieldRatingMetadata, FieldRawJsonMetadata, FieldRelationMetadata, + FieldRichTextDeprecatedMetadata, FieldRichTextMetadata, - FieldRichTextOldMetadata, FieldSelectMetadata, FieldTextMetadata, FieldUuidMetadata, @@ -72,7 +72,7 @@ type AssertFieldMetadataFunction = < : E extends 'RICH_TEXT' ? FieldRichTextMetadata : E extends 'RICH_TEXT_OLD' - ? FieldRichTextOldMetadata + ? FieldRichTextDeprecatedMetadata : E extends 'ACTOR' ? FieldActorMetadata : E extends 'ARRAY' diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextDeprecated.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextDeprecated.ts new file mode 100644 index 000000000000..785edba3945c --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextDeprecated.ts @@ -0,0 +1,12 @@ +import { FieldMetadataType } from '~/generated-metadata/graphql'; + +import { FieldDefinition } from '../FieldDefinition'; +import { + FieldMetadata, + FieldRichTextDeprecatedMetadata, +} from '../FieldMetadata'; + +export const isFieldRichTextDeprecated = ( + field: Pick, 'type'>, +): field is FieldDefinition => + field.type === FieldMetadataType.RichTextDeprecated; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextDeprecatedValue.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextDeprecatedValue.ts new file mode 100644 index 000000000000..bd49b71c4f19 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextDeprecatedValue.ts @@ -0,0 +1,13 @@ +import { z } from 'zod'; +import { FieldRichTextDeprecatedValue } from '../FieldMetadata'; + +export const richTextDeprecatedSchema: z.ZodType = + z.union([ + z.null(), // Exclude literal values other than null + z.string(), + ]); + +export const isFieldRichTextDeprecatedValue = ( + fieldValue: unknown, +): fieldValue is FieldRichTextDeprecatedValue => + richTextDeprecatedSchema.safeParse(fieldValue).success; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextOld.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextOld.ts deleted file mode 100644 index df39f91af122..000000000000 --- a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextOld.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { FieldMetadataType } from '~/generated-metadata/graphql'; - -import { FieldDefinition } from '../FieldDefinition'; -import { FieldMetadata, FieldRichTextOldMetadata } from '../FieldMetadata'; - -export const isFieldRichTextOld = ( - field: Pick, 'type'>, -): field is FieldDefinition => - field.type === FieldMetadataType.RichTextOld; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextOldValue.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextOldValue.ts deleted file mode 100644 index 51c5f09227e8..000000000000 --- a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextOldValue.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { z } from 'zod'; -import { FieldRichTextOldValue } from '../FieldMetadata'; - -export const richTextOldSchema: z.ZodType = z.union([ - z.null(), // Exclude literal values other than null - z.string(), -]); - -export const isFieldRichTextOldValue = ( - fieldValue: unknown, -): fieldValue is FieldRichTextOldValue => - richTextOldSchema.safeParse(fieldValue).success; diff --git a/packages/twenty-front/src/modules/object-record/record-field/utils/__tests__/isFieldValueReadOnly.test.ts b/packages/twenty-front/src/modules/object-record/record-field/utils/__tests__/isFieldValueReadOnly.test.ts index 563dd7218b1d..cf9534a459ab 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/utils/__tests__/isFieldValueReadOnly.test.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/utils/__tests__/isFieldValueReadOnly.test.ts @@ -89,15 +89,15 @@ describe('isFieldValueReadOnly', () => { expect(result).toBe(true); }); - it('should return true if fieldType is FieldMetadataType.RichTextOld', () => { + it('should return true if fieldType is FieldMetadataType.RichTextDeprecated', () => { const result = isFieldValueReadOnly({ - fieldType: FieldMetadataType.RichTextOld, + fieldType: FieldMetadataType.RichTextDeprecated, }); expect(result).toBe(true); }); - it('should return false if fieldType is not FieldMetadataType.Actor or FieldMetadataType.RichTextOld', () => { + it('should return false if fieldType is not FieldMetadataType.Actor or FieldMetadataType.RichTextDeprecated', () => { const result = isFieldValueReadOnly({ fieldType: FieldMetadataType.Text, }); diff --git a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueEmpty.ts b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueEmpty.ts index acab134f3784..126e688edd59 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueEmpty.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueEmpty.ts @@ -28,7 +28,7 @@ import { isFieldPosition } from '@/object-record/record-field/types/guards/isFie import { isFieldRating } from '@/object-record/record-field/types/guards/isFieldRating'; import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson'; import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation'; -import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; +import { isFieldRichTextDeprecated } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecated'; import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect'; import { isFieldSelectValue } from '@/object-record/record-field/types/guards/isFieldSelectValue'; import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText'; @@ -59,7 +59,7 @@ export const isFieldValueEmpty = ({ isFieldRating(fieldDefinition) || isFieldBoolean(fieldDefinition) || isFieldRawJson(fieldDefinition) || - isFieldRichTextOld(fieldDefinition) || + isFieldRichTextDeprecated(fieldDefinition) || isFieldPosition(fieldDefinition) ) { return isValueEmpty(fieldValue); diff --git a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts index 594604984e7c..0ba6c3e8d5af 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts @@ -1,7 +1,7 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { isWorkflowSubObjectMetadata } from '@/object-metadata/utils/isWorkflowSubObjectMetadata'; import { isFieldActor } from '@/object-record/record-field/types/guards/isFieldActor'; -import { isFieldRichTextOld } from '@/object-record/record-field/types/guards/isFieldRichTextOld'; +import { isFieldRichTextDeprecated } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecated'; import { isDefined } from 'twenty-ui'; import { FieldMetadataType } from '~/generated-metadata/graphql'; @@ -45,7 +45,8 @@ export const isFieldValueReadOnly = ({ if ( isDefined(fieldType) && - (isFieldActor({ type: fieldType }) || isFieldRichTextOld({ type: fieldType })) + (isFieldActor({ type: fieldType }) || + isFieldRichTextDeprecated({ type: fieldType })) ) { return true; } diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts index 3c2f269ab242..dad9f432bdca 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts @@ -177,7 +177,7 @@ export const isRecordMatchingFilter = ({ value: record[filterKey], }); } - case FieldMetadataType.RichTextOld: { + case FieldMetadataType.RichTextDeprecated: { // TODO: Implement a better rich text filter once it becomes a composite field // See this issue for more context: https://github.com/twentyhq/twenty/issues/7613#issuecomment-2408944585 // This should be tackled in Q4'24 diff --git a/packages/twenty-front/src/modules/object-record/utils/generateEmptyFieldValue.ts b/packages/twenty-front/src/modules/object-record/utils/generateEmptyFieldValue.ts index 8eabe621ea0c..f270dbaf3a9a 100644 --- a/packages/twenty-front/src/modules/object-record/utils/generateEmptyFieldValue.ts +++ b/packages/twenty-front/src/modules/object-record/utils/generateEmptyFieldValue.ts @@ -81,7 +81,7 @@ export const generateEmptyFieldValue = ( case FieldMetadataType.RawJson: { return null; } - case FieldMetadataType.RichTextOld: { + case FieldMetadataType.RichTextDeprecated: { return null; } case FieldMetadataType.Actor: { diff --git a/packages/twenty-front/src/modules/object-record/utils/isFieldCellSupported.ts b/packages/twenty-front/src/modules/object-record/utils/isFieldCellSupported.ts index 8944feca2d8b..937963df1f22 100644 --- a/packages/twenty-front/src/modules/object-record/utils/isFieldCellSupported.ts +++ b/packages/twenty-front/src/modules/object-record/utils/isFieldCellSupported.ts @@ -15,7 +15,7 @@ export const isFieldCellSupported = ( [ FieldMetadataType.Uuid, FieldMetadataType.Position, - FieldMetadataType.RichTextOld, + FieldMetadataType.RichTextDeprecated, ].includes(fieldMetadataItem.type) ) { return false; diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts index 542d5f5de418..8e3442825d8a 100644 --- a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts +++ b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts @@ -25,10 +25,10 @@ import { FieldNumberValue, FieldRatingValue, FieldRelationValue, - FieldRichTextOldValue, + FieldRichTextDeprecatedValue, FieldSelectValue, FieldTextValue, - FieldUUidValue + FieldUUidValue, } from '@/object-record/record-field/types/FieldMetadata'; import { DEFAULT_DATE_VALUE } from '@/settings/data-model/constants/DefaultDateValue'; import { SettingsFieldTypeCategoryType } from '@/settings/data-model/types/SettingsFieldTypeCategoryType'; @@ -122,12 +122,12 @@ export const SETTINGS_NON_COMPOSITE_FIELD_TYPE_CONFIGS: SettingsNonCompositeFiel exampleValue: { key: 'value' }, category: 'Advanced', } as const satisfies SettingsFieldTypeConfig, - [FieldMetadataType.RichTextOld]: { + [FieldMetadataType.RichTextDeprecated]: { label: 'Rich Text Old', Icon: IllustrationIconSetting, exampleValue: "{ key: 'value' }", category: 'Basic', - } as const satisfies SettingsFieldTypeConfig, + } as const satisfies SettingsFieldTypeConfig, [FieldMetadataType.Array]: { label: 'Array', Icon: IllustrationIconArray, diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect.tsx index 69306376b695..29d7b5dfbb2c 100644 --- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect.tsx @@ -44,7 +44,7 @@ export const SettingsObjectNewFieldSelect = () => { const excludedFieldTypes: SettingsFieldType[] = ( [ FieldMetadataType.Numeric, - FieldMetadataType.RichTextOld, + FieldMetadataType.RichTextDeprecated, FieldMetadataType.Actor, ] as const ).filter(isDefined); diff --git a/packages/twenty-front/src/pages/settings/data-model/constants/DefaultIconsByFieldType.ts b/packages/twenty-front/src/pages/settings/data-model/constants/DefaultIconsByFieldType.ts index d0a2e18ad9d2..7c6c9b63dc02 100644 --- a/packages/twenty-front/src/pages/settings/data-model/constants/DefaultIconsByFieldType.ts +++ b/packages/twenty-front/src/pages/settings/data-model/constants/DefaultIconsByFieldType.ts @@ -22,7 +22,7 @@ export const DEFAULT_ICONS_BY_FIELD_TYPE: Record = { [FieldMetadataType.Actor]: 'IconUsers', [FieldMetadataType.Numeric]: 'IconUsers', [FieldMetadataType.Position]: 'IconUsers', - [FieldMetadataType.RichTextOld]: 'IconUsers', + [FieldMetadataType.RichTextDeprecated]: 'IconUsers', [FieldMetadataType.RichText]: 'IconUsers', [FieldMetadataType.TsVector]: 'IconUsers', }; diff --git a/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts b/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts index 76d9d5dcfd12..635c8245a71f 100644 --- a/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts +++ b/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts @@ -192,8 +192,8 @@ const fieldRawJsonMock = { defaultValue: null, }; -const fieldRichTextOldMock = { - name: 'fieldRichTextOld', +const fieldRichTextDeprecatedMock = { + name: 'fieldRichTextDeprecated', type: FieldMetadataType.RICH_TEXT_OLD, isNullable: true, defaultValue: null, @@ -257,7 +257,7 @@ export const fields = [ fieldPositionMock, fieldAddressMock, fieldRawJsonMock, - fieldRichTextOldMock, + fieldRichTextDeprecatedMock, fieldActorMock, fieldArrayMock, ]; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts index ed17fe472b10..76234d6f640d 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts @@ -1,16 +1,16 @@ import { - IsArray, - IsBoolean, - IsDate, - IsNotEmpty, - IsNumber, - IsNumberString, - IsObject, - IsOptional, - IsString, - IsUUID, - Matches, - ValidateIf, + IsArray, + IsBoolean, + IsDate, + IsNotEmpty, + IsNumber, + IsNumberString, + IsObject, + IsOptional, + IsString, + IsUUID, + Matches, + ValidateIf, } from 'class-validator'; import { IsQuotedString } from 'src/engine/metadata-modules/field-metadata/validators/is-quoted-string.validator'; @@ -45,7 +45,7 @@ export class FieldMetadataDefaultValueRichText { markdown: string | null; } -export class FieldMetadataDefaultValueRichTextOld { +export class FieldMetadataDefaultValueRichTextDeprecated { @ValidateIf((_object, value) => value !== null) @IsString() value: string | null; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts index 7673ce753342..44c796d3be84 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts @@ -1,20 +1,20 @@ import { - FieldMetadataDefaultActor, - FieldMetadataDefaultArray, - FieldMetadataDefaultValueAddress, - FieldMetadataDefaultValueBoolean, - FieldMetadataDefaultValueCurrency, - FieldMetadataDefaultValueDateTime, - FieldMetadataDefaultValueEmails, - FieldMetadataDefaultValueFullName, - FieldMetadataDefaultValueLinks, - FieldMetadataDefaultValueNowFunction, - FieldMetadataDefaultValueNumber, - FieldMetadataDefaultValuePhones, - FieldMetadataDefaultValueRawJson, - FieldMetadataDefaultValueRichTextOld, - FieldMetadataDefaultValueString, - FieldMetadataDefaultValueUuidFunction, + FieldMetadataDefaultActor, + FieldMetadataDefaultArray, + FieldMetadataDefaultValueAddress, + FieldMetadataDefaultValueBoolean, + FieldMetadataDefaultValueCurrency, + FieldMetadataDefaultValueDateTime, + FieldMetadataDefaultValueEmails, + FieldMetadataDefaultValueFullName, + FieldMetadataDefaultValueLinks, + FieldMetadataDefaultValueNowFunction, + FieldMetadataDefaultValueNumber, + FieldMetadataDefaultValuePhones, + FieldMetadataDefaultValueRawJson, + FieldMetadataDefaultValueRichTextDeprecated, + FieldMetadataDefaultValueString, + FieldMetadataDefaultValueUuidFunction, } from 'src/engine/metadata-modules/field-metadata/dtos/default-value.input'; import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; @@ -47,7 +47,7 @@ type FieldMetadataDefaultValueMapping = { [FieldMetadataType.SELECT]: FieldMetadataDefaultValueString; [FieldMetadataType.MULTI_SELECT]: FieldMetadataDefaultValueString; [FieldMetadataType.RAW_JSON]: FieldMetadataDefaultValueRawJson; - [FieldMetadataType.RICH_TEXT_OLD]: FieldMetadataDefaultValueRichTextOld; + [FieldMetadataType.RICH_TEXT_OLD]: FieldMetadataDefaultValueRichTextDeprecated; [FieldMetadataType.ACTOR]: FieldMetadataDefaultActor; [FieldMetadataType.ARRAY]: FieldMetadataDefaultArray; }; From 2be5673063733f8705b5c0d590d7040a75ea9614 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Sun, 5 Jan 2025 13:40:57 +0100 Subject: [PATCH 08/29] Move notes to new RICH_TEXT WiP --- .../src/generated-metadata/graphql.ts | 2 +- .../twenty-front/src/generated/graphql.tsx | 2 +- .../utils/mapFieldMetadataToGraphQLQuery.ts | 8 +++ .../types/guards/assertFieldMetadata.ts | 52 +++++++++---------- .../record-field/utils/isFieldValueEmpty.ts | 10 ++++ .../utils/generateEmptyFieldValue.ts | 6 +++ .../generated/mock-metadata-query-result.ts | 4 +- .../__mocks__/object-metadata-item.mock.ts | 2 +- .../services/type-mapper.service.ts | 44 ++++++++-------- ...p-field-metadata-to-graphql-query.utils.ts | 2 +- .../open-api/utils/components.utils.ts | 20 +++---- .../field-metadata/field-metadata.entity.ts | 2 +- .../field-metadata-default-value.interface.ts | 2 +- .../validate-default-value-for-type.util.ts | 40 +++++++------- ...field-metadata-type-to-column-type.util.ts | 6 +-- .../workspace-migration.factory.ts | 10 ++-- .../metadata-seeds/pets-metadata-seeds.ts | 2 +- .../utils/is-searchable-field.util.ts | 2 +- .../standard-objects/note.workspace-entity.ts | 3 +- .../standard-objects/task.workspace-entity.ts | 3 +- .../src/utils/computeInputFields.ts | 12 ++--- .../twenty-zapier/src/utils/data.types.ts | 3 +- 22 files changed, 130 insertions(+), 107 deletions(-) diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index f76f5622c1de..f01b1d3710f8 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -420,7 +420,7 @@ export enum FieldMetadataType { Rating = 'RATING', RawJson = 'RAW_JSON', Relation = 'RELATION', - RichTextDeprecated = 'RICH_TEXT_OLD', + RichTextDeprecated = 'RICH_TEXT_DEPRECATED', RichText = 'RICH_TEXT', Select = 'SELECT', Text = 'TEXT', diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 7e098fea2805..21c6b2715854 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -374,7 +374,7 @@ export enum FieldMetadataType { Rating = 'RATING', RawJson = 'RAW_JSON', Relation = 'RELATION', - RichTextDeprecated = 'RICH_TEXT_OLD', + RichTextDeprecated = 'RICH_TEXT_DEPRECATED', RichText = 'RICH_TEXT', Select = 'SELECT', Text = 'TEXT', diff --git a/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts b/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts index 9028fe6bc319..dca7976cb624 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts @@ -162,5 +162,13 @@ ${mapObjectMetadataToGraphQLQuery({ }`; } + if (fieldType === FieldMetadataType.RichText) { + return `${field.name} +{ + blocknote + markdown +}`; + } + return ''; }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts index 6530835e28bd..07eae77ed416 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts @@ -2,31 +2,31 @@ import { FieldMetadataType } from '~/generated-metadata/graphql'; import { FieldDefinition } from '../FieldDefinition'; import { - FieldActorMetadata, - FieldAddressMetadata, - FieldArrayMetadata, - FieldBooleanMetadata, - FieldCurrencyMetadata, - FieldDateMetadata, - FieldDateTimeMetadata, - FieldEmailMetadata, - FieldEmailsMetadata, - FieldFullNameMetadata, - FieldLinkMetadata, - FieldLinksMetadata, - FieldMetadata, - FieldMultiSelectMetadata, - FieldNumberMetadata, - FieldPhoneMetadata, - FieldPhonesMetadata, - FieldRatingMetadata, - FieldRawJsonMetadata, - FieldRelationMetadata, - FieldRichTextDeprecatedMetadata, - FieldRichTextMetadata, - FieldSelectMetadata, - FieldTextMetadata, - FieldUuidMetadata, + FieldActorMetadata, + FieldAddressMetadata, + FieldArrayMetadata, + FieldBooleanMetadata, + FieldCurrencyMetadata, + FieldDateMetadata, + FieldDateTimeMetadata, + FieldEmailMetadata, + FieldEmailsMetadata, + FieldFullNameMetadata, + FieldLinkMetadata, + FieldLinksMetadata, + FieldMetadata, + FieldMultiSelectMetadata, + FieldNumberMetadata, + FieldPhoneMetadata, + FieldPhonesMetadata, + FieldRatingMetadata, + FieldRawJsonMetadata, + FieldRelationMetadata, + FieldRichTextDeprecatedMetadata, + FieldRichTextMetadata, + FieldSelectMetadata, + FieldTextMetadata, + FieldUuidMetadata, } from '../FieldMetadata'; type AssertFieldMetadataFunction = < @@ -71,7 +71,7 @@ type AssertFieldMetadataFunction = < ? FieldRawJsonMetadata : E extends 'RICH_TEXT' ? FieldRichTextMetadata - : E extends 'RICH_TEXT_OLD' + : E extends 'RICH_TEXT_DEPRECATED' ? FieldRichTextDeprecatedMetadata : E extends 'ACTOR' ? FieldActorMetadata diff --git a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueEmpty.ts b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueEmpty.ts index 126e688edd59..c1c1159c400f 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueEmpty.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueEmpty.ts @@ -28,7 +28,9 @@ import { isFieldPosition } from '@/object-record/record-field/types/guards/isFie import { isFieldRating } from '@/object-record/record-field/types/guards/isFieldRating'; import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson'; import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation'; +import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; import { isFieldRichTextDeprecated } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecated'; +import { isFieldRichTextValue } from '@/object-record/record-field/types/guards/isFieldRichTextValue'; import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect'; import { isFieldSelectValue } from '@/object-record/record-field/types/guards/isFieldSelectValue'; import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText'; @@ -142,6 +144,14 @@ export const isFieldValueEmpty = ({ return false; } + if (isFieldRichText(fieldDefinition)) { + return ( + !isFieldRichTextValue(fieldValue) || + (isValueEmpty(fieldValue?.blocknote) && + isValueEmpty(fieldValue?.markdown)) + ); + } + throw new Error( `Entity field type not supported in isFieldValueEmpty : ${fieldDefinition.type}}`, ); diff --git a/packages/twenty-front/src/modules/object-record/utils/generateEmptyFieldValue.ts b/packages/twenty-front/src/modules/object-record/utils/generateEmptyFieldValue.ts index f270dbaf3a9a..97a78b06d362 100644 --- a/packages/twenty-front/src/modules/object-record/utils/generateEmptyFieldValue.ts +++ b/packages/twenty-front/src/modules/object-record/utils/generateEmptyFieldValue.ts @@ -84,6 +84,12 @@ export const generateEmptyFieldValue = ( case FieldMetadataType.RichTextDeprecated: { return null; } + case FieldMetadataType.RichText: { + return { + blocknote: null, + markdown: null, + } + } case FieldMetadataType.Actor: { return { source: 'MANUAL', diff --git a/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts b/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts index 8dbabb21096c..02792bf4bf27 100644 --- a/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts +++ b/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts @@ -3584,7 +3584,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = "node": { "__typename": "field", "id": "2fdf421d-fab6-4135-81ac-50582724e20e", - "type": "RICH_TEXT_OLD", + "type": "RICH_TEXT_DEPRECATED", "name": "body", "label": "Body", "description": "Note body", @@ -18827,7 +18827,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = "node": { "__typename": "field", "id": "ae853d86-e770-4196-8b4b-2dee50957236", - "type": "RICH_TEXT_OLD", + "type": "RICH_TEXT_DEPRECATED", "name": "body", "label": "Body", "description": "Task body", diff --git a/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts b/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts index 635c8245a71f..44cfb3384547 100644 --- a/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts +++ b/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts @@ -194,7 +194,7 @@ const fieldRawJsonMock = { const fieldRichTextDeprecatedMock = { name: 'fieldRichTextDeprecated', - type: FieldMetadataType.RICH_TEXT_OLD, + type: FieldMetadataType.RICH_TEXT_DEPRECATED, isNullable: true, defaultValue: null, }; diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts index 7e99cb8e9b8b..625f5b403075 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts @@ -2,36 +2,36 @@ import { Injectable } from '@nestjs/common'; import { GraphQLISODateTime } from '@nestjs/graphql'; import { - GraphQLBoolean, - GraphQLEnumType, - GraphQLID, - GraphQLInputObjectType, - GraphQLInputType, - GraphQLList, - GraphQLNonNull, - GraphQLScalarType, - GraphQLString, - GraphQLType, + GraphQLBoolean, + GraphQLEnumType, + GraphQLID, + GraphQLInputObjectType, + GraphQLInputType, + GraphQLList, + GraphQLNonNull, + GraphQLScalarType, + GraphQLString, + GraphQLType, } from 'graphql'; import { FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface'; import { OrderByDirectionType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/enum'; import { - ArrayFilterType, - BigFloatFilterType, - BooleanFilterType, - DateFilterType, - FloatFilterType, - RawJsonFilterType, - StringFilterType, + ArrayFilterType, + BigFloatFilterType, + BooleanFilterType, + DateFilterType, + FloatFilterType, + RawJsonFilterType, + StringFilterType, } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input'; import { IDFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/id-filter.input-type'; import { MultiSelectFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/multi-select-filter.input-type'; import { SelectFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/select-filter.input-type'; import { - BigFloatScalarType, - UUIDScalarType, + BigFloatScalarType, + UUIDScalarType, } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; import { PositionScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars/position.scalar'; import { RawJSONScalar } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars/raw-json.scalar'; @@ -80,7 +80,7 @@ export class TypeMapperService { FieldMetadataType.ARRAY, StringArrayScalarType as unknown as GraphQLScalarType, ], - [FieldMetadataType.RICH_TEXT_OLD, GraphQLString], + [FieldMetadataType.RICH_TEXT_DEPRECATED, GraphQLString], [FieldMetadataType.TS_VECTOR, GraphQLString], ]); @@ -115,7 +115,7 @@ export class TypeMapperService { [FieldMetadataType.NUMERIC, BigFloatFilterType], [FieldMetadataType.POSITION, FloatFilterType], [FieldMetadataType.RAW_JSON, RawJsonFilterType], - [FieldMetadataType.RICH_TEXT_OLD, StringFilterType], + [FieldMetadataType.RICH_TEXT_DEPRECATED, StringFilterType], [FieldMetadataType.ARRAY, ArrayFilterType], [FieldMetadataType.MULTI_SELECT, MultiSelectFilterType], [FieldMetadataType.SELECT, SelectFilterType], @@ -141,7 +141,7 @@ export class TypeMapperService { [FieldMetadataType.MULTI_SELECT, OrderByDirectionType], [FieldMetadataType.POSITION, OrderByDirectionType], [FieldMetadataType.RAW_JSON, OrderByDirectionType], - [FieldMetadataType.RICH_TEXT_OLD, OrderByDirectionType], + [FieldMetadataType.RICH_TEXT_DEPRECATED, OrderByDirectionType], [FieldMetadataType.ARRAY, OrderByDirectionType], [FieldMetadataType.TS_VECTOR, OrderByDirectionType], // TODO: Add TSVectorOrderByType ]); diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts index 6a967267680a..860be4920905 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts @@ -28,7 +28,7 @@ export const mapFieldMetadataToGraphqlQuery = ( FieldMetadataType.MULTI_SELECT, FieldMetadataType.POSITION, FieldMetadataType.RAW_JSON, - FieldMetadataType.RICH_TEXT_OLD, + FieldMetadataType.RICH_TEXT_DEPRECATED, FieldMetadataType.ARRAY, FieldMetadataType.TS_VECTOR, ].includes(fieldType); diff --git a/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts b/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts index f02ff5d0d9ef..a19bed15adc3 100644 --- a/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts +++ b/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts @@ -1,17 +1,17 @@ import { OpenAPIV3_1 } from 'openapi-types'; import { - computeDepthParameters, - computeEndingBeforeParameters, - computeFilterParameters, - computeIdPathParameter, - computeLimitParameters, - computeOrderByParameters, - computeStartingAfterParameters, + computeDepthParameters, + computeEndingBeforeParameters, + computeFilterParameters, + computeIdPathParameter, + computeLimitParameters, + computeOrderByParameters, + computeStartingAfterParameters, } from 'src/engine/core-modules/open-api/utils/parameters.utils'; import { - FieldMetadataEntity, - FieldMetadataType, + FieldMetadataEntity, + FieldMetadataType, } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { RelationMetadataType } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity'; @@ -43,7 +43,7 @@ const getFieldProperties = (type: FieldMetadataType): Property => { case FieldMetadataType.UUID: return { type: 'string', format: 'uuid' }; case FieldMetadataType.TEXT: - case FieldMetadataType.RICH_TEXT_OLD: + case FieldMetadataType.RICH_TEXT_DEPRECATED: return { type: 'string' }; case FieldMetadataType.DATE_TIME: return { type: 'string', format: 'date-time' }; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts index 0c6cca5b2014..f526f070f803 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/field-metadata.entity.ts @@ -42,7 +42,7 @@ export enum FieldMetadataType { ADDRESS = 'ADDRESS', RAW_JSON = 'RAW_JSON', RICH_TEXT = 'RICH_TEXT', - RICH_TEXT_OLD = 'RICH_TEXT_OLD', + RICH_TEXT_DEPRECATED = 'RICH_TEXT_DEPRECATED', ACTOR = 'ACTOR', ARRAY = 'ARRAY', TS_VECTOR = 'TS_VECTOR', diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts index 44c796d3be84..8121eb33ff41 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts @@ -47,7 +47,7 @@ type FieldMetadataDefaultValueMapping = { [FieldMetadataType.SELECT]: FieldMetadataDefaultValueString; [FieldMetadataType.MULTI_SELECT]: FieldMetadataDefaultValueString; [FieldMetadataType.RAW_JSON]: FieldMetadataDefaultValueRawJson; - [FieldMetadataType.RICH_TEXT_OLD]: FieldMetadataDefaultValueRichTextDeprecated; + [FieldMetadataType.RICH_TEXT_DEPRECATED]: FieldMetadataDefaultValueRichTextDeprecated; [FieldMetadataType.ACTOR]: FieldMetadataDefaultActor; [FieldMetadataType.ARRAY]: FieldMetadataDefaultArray; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts index d5bd9eb25f30..771d7235a646 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts @@ -2,28 +2,28 @@ import { plainToInstance } from 'class-transformer'; import { ValidationError, validateSync } from 'class-validator'; import { - FieldMetadataClassValidation, - FieldMetadataDefaultValue, + FieldMetadataClassValidation, + FieldMetadataDefaultValue, } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface'; import { - FieldMetadataDefaultActor, - FieldMetadataDefaultValueAddress, - FieldMetadataDefaultValueBoolean, - FieldMetadataDefaultValueCurrency, - FieldMetadataDefaultValueDate, - FieldMetadataDefaultValueDateTime, - FieldMetadataDefaultValueEmails, - FieldMetadataDefaultValueFullName, - FieldMetadataDefaultValueLinks, - FieldMetadataDefaultValueNowFunction, - FieldMetadataDefaultValueNumber, - FieldMetadataDefaultValuePhones, - FieldMetadataDefaultValueRawJson, - FieldMetadataDefaultValueRichText, - FieldMetadataDefaultValueString, - FieldMetadataDefaultValueStringArray, - FieldMetadataDefaultValueUuidFunction, + FieldMetadataDefaultActor, + FieldMetadataDefaultValueAddress, + FieldMetadataDefaultValueBoolean, + FieldMetadataDefaultValueCurrency, + FieldMetadataDefaultValueDate, + FieldMetadataDefaultValueDateTime, + FieldMetadataDefaultValueEmails, + FieldMetadataDefaultValueFullName, + FieldMetadataDefaultValueLinks, + FieldMetadataDefaultValueNowFunction, + FieldMetadataDefaultValueNumber, + FieldMetadataDefaultValuePhones, + FieldMetadataDefaultValueRawJson, + FieldMetadataDefaultValueRichText, + FieldMetadataDefaultValueString, + FieldMetadataDefaultValueStringArray, + FieldMetadataDefaultValueUuidFunction, } from 'src/engine/metadata-modules/field-metadata/dtos/default-value.input'; import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; @@ -49,7 +49,7 @@ export const defaultValueValidatorsMap = { [FieldMetadataType.MULTI_SELECT]: [FieldMetadataDefaultValueStringArray], [FieldMetadataType.ADDRESS]: [FieldMetadataDefaultValueAddress], [FieldMetadataType.RICH_TEXT]: [FieldMetadataDefaultValueRichText], - [FieldMetadataType.RICH_TEXT_OLD]: [FieldMetadataDefaultValueString], + [FieldMetadataType.RICH_TEXT_DEPRECATED]: [FieldMetadataDefaultValueString], [FieldMetadataType.RAW_JSON]: [FieldMetadataDefaultValueRawJson], [FieldMetadataType.LINKS]: [FieldMetadataDefaultValueLinks], [FieldMetadataType.ACTOR]: [FieldMetadataDefaultActor], diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts index 3afff94dd643..fdee0d4cf3c3 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts @@ -1,7 +1,7 @@ import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { - WorkspaceMigrationException, - WorkspaceMigrationExceptionCode, + WorkspaceMigrationException, + WorkspaceMigrationExceptionCode, } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.exception'; export const fieldMetadataTypeToColumnType = ( @@ -15,7 +15,7 @@ export const fieldMetadataTypeToColumnType = ( case FieldMetadataType.UUID: return 'uuid'; case FieldMetadataType.TEXT: - case FieldMetadataType.RICH_TEXT_OLD: + case FieldMetadataType.RICH_TEXT_DEPRECATED: case FieldMetadataType.ARRAY: return 'text'; case FieldMetadataType.NUMERIC: diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts index 3957476ad5d3..aae1ad62b8dd 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts @@ -10,12 +10,12 @@ import { CompositeColumnActionFactory } from 'src/engine/metadata-modules/worksp import { EnumColumnActionFactory } from 'src/engine/metadata-modules/workspace-migration/factories/enum-column-action.factory'; import { TsVectorColumnActionFactory } from 'src/engine/metadata-modules/workspace-migration/factories/ts-vector-column-action.factory'; import { - WorkspaceMigrationColumnAction, - WorkspaceMigrationColumnActionType, + WorkspaceMigrationColumnAction, + WorkspaceMigrationColumnActionType, } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity'; import { - WorkspaceMigrationException, - WorkspaceMigrationExceptionCode, + WorkspaceMigrationException, + WorkspaceMigrationExceptionCode, } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.exception'; @Injectable() @@ -57,7 +57,7 @@ export class WorkspaceMigrationFactory { [FieldMetadataType.POSITION, { factory: this.basicColumnActionFactory }], [FieldMetadataType.RAW_JSON, { factory: this.basicColumnActionFactory }], [ - FieldMetadataType.RICH_TEXT_OLD, + FieldMetadataType.RICH_TEXT_DEPRECATED, { factory: this.basicColumnActionFactory }, ], [FieldMetadataType.BOOLEAN, { factory: this.basicColumnActionFactory }], diff --git a/packages/twenty-server/src/engine/seeder/metadata-seeds/pets-metadata-seeds.ts b/packages/twenty-server/src/engine/seeder/metadata-seeds/pets-metadata-seeds.ts index 3544acaeca0b..b3c88d8aa07f 100644 --- a/packages/twenty-server/src/engine/seeder/metadata-seeds/pets-metadata-seeds.ts +++ b/packages/twenty-server/src/engine/seeder/metadata-seeds/pets-metadata-seeds.ts @@ -96,7 +96,7 @@ export const PETS_METADATA_SEEDS: ObjectMetadataSeed = { name: 'soundSwag', }, { - type: FieldMetadataType.RICH_TEXT_OLD, + type: FieldMetadataType.RICH_TEXT_DEPRECATED, label: 'Bio', name: 'bio', }, diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util.ts index 2889760ea664..58fe257ae2e7 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util.ts @@ -6,7 +6,7 @@ const SEARCHABLE_FIELD_TYPES = [ FieldMetadataType.EMAILS, FieldMetadataType.ADDRESS, FieldMetadataType.LINKS, - FieldMetadataType.RICH_TEXT_OLD, + FieldMetadataType.RICH_TEXT_DEPRECATED, FieldMetadataType.RICH_TEXT, ] as const; diff --git a/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts b/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts index 4c145dde1cc2..81968bc1a044 100644 --- a/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts +++ b/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts @@ -37,7 +37,6 @@ const BODY_FIELD_NAME = 'body'; export const SEARCH_FIELDS_FOR_NOTES: FieldTypeAndNameMetadata[] = [ { name: TITLE_FIELD_NAME, type: FieldMetadataType.TEXT }, // { name: BODY_FIELD_NAME, type: FieldMetadataType.RICH_TEXT }, // TODO: Check later if and how this works - { name: BODY_FIELD_NAME, type: FieldMetadataType.RICH_TEXT_OLD }, ]; @WorkspaceEntity({ @@ -73,7 +72,7 @@ export class NoteWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceField({ standardId: NOTE_STANDARD_FIELD_IDS.body, - type: FieldMetadataType.RICH_TEXT_OLD, + type: FieldMetadataType.RICH_TEXT, label: 'Body', description: 'Note body', icon: 'IconFilePencil', diff --git a/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts b/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts index ce5efe288831..c1a644eadb55 100644 --- a/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts +++ b/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts @@ -39,7 +39,6 @@ const BODY_FIELD_NAME = 'body'; export const SEARCH_FIELDS_FOR_TASK: FieldTypeAndNameMetadata[] = [ { name: TITLE_FIELD_NAME, type: FieldMetadataType.TEXT }, // { name: BODY_FIELD_NAME, type: FieldMetadataType.RICH_TEXT }, // TODO: Check later if and how this works - { name: BODY_FIELD_NAME, type: FieldMetadataType.RICH_TEXT_OLD }, ]; @WorkspaceEntity({ @@ -75,7 +74,7 @@ export class TaskWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceField({ standardId: TASK_STANDARD_FIELD_IDS.body, - type: FieldMetadataType.RICH_TEXT_OLD, + type: FieldMetadataType.RICH_TEXT, label: 'Body', description: 'Task body', icon: 'IconFilePencil', diff --git a/packages/twenty-zapier/src/utils/computeInputFields.ts b/packages/twenty-zapier/src/utils/computeInputFields.ts index d6069dd911dc..ac63fd24c9b7 100644 --- a/packages/twenty-zapier/src/utils/computeInputFields.ts +++ b/packages/twenty-zapier/src/utils/computeInputFields.ts @@ -1,8 +1,8 @@ import { - FieldMetadataType, - InputField, - Node, - NodeField, + FieldMetadataType, + InputField, + Node, + NodeField, } from '../utils/data.types'; const getListFromFieldMetadataType = (fieldMetadataType: FieldMetadataType) => { @@ -15,7 +15,7 @@ const getTypeFromFieldMetadataType = ( switch (fieldMetadataType) { case FieldMetadataType.UUID: case FieldMetadataType.TEXT: - case FieldMetadataType.RICH_TEXT_OLD: + case FieldMetadataType.RICH_TEXT_DEPRECATED: case FieldMetadataType.PHONE: case FieldMetadataType.EMAIL: case FieldMetadataType.LINK: @@ -285,7 +285,7 @@ export const computeInputFields = ( break; case FieldMetadataType.UUID: case FieldMetadataType.TEXT: - case FieldMetadataType.RICH_TEXT_OLD: + case FieldMetadataType.RICH_TEXT_DEPRECATED: case FieldMetadataType.PHONE: case FieldMetadataType.EMAIL: case FieldMetadataType.DATE_TIME: diff --git a/packages/twenty-zapier/src/utils/data.types.ts b/packages/twenty-zapier/src/utils/data.types.ts index 345a5277e42b..9b6319277c5f 100644 --- a/packages/twenty-zapier/src/utils/data.types.ts +++ b/packages/twenty-zapier/src/utils/data.types.ts @@ -53,7 +53,8 @@ export enum FieldMetadataType { MULTI_SELECT = 'MULTI_SELECT', POSITION = 'POSITION', ADDRESS = 'ADDRESS', - RICH_TEXT_OLD = 'RICH_TEXT_OLD', + RICH_TEXT = 'RICH_TEXT', + RICH_TEXT_DEPRECATED = 'RICH_TEXT_DEPRECATED', ARRAY = 'ARRAY', // Ignored fieldTypes From 2a41f075f01a7f43aa2c251ac92fc4179daf60c7 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Mon, 6 Jan 2025 23:56:15 +0100 Subject: [PATCH 09/29] Fix notes loading --- .../activities/components/ActivityRichTextEditor.tsx | 5 ++++- .../__tests__/useActivityTargetObjectRecords.test.tsx | 5 ++++- .../modules/activities/notes/components/NoteCard.tsx | 2 +- .../modules/activities/tasks/components/TaskRow.tsx | 2 +- .../tasks/hooks/__tests__/useCompleteTask.test.tsx | 5 ++++- .../src/modules/activities/types/Activity.ts | 5 ++++- packages/twenty-front/src/testing/mock-data/notes.ts | 10 ++++++++-- packages/twenty-front/src/testing/mock-data/tasks.ts | 5 ++++- .../handlers/activity-query-result-getter.handler.ts | 2 +- .../utils/field-metadata-type-to-column-type.util.ts | 2 ++ 10 files changed, 33 insertions(+), 10 deletions(-) diff --git a/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx b/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx index f871566fff4b..24503470725b 100644 --- a/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx +++ b/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx @@ -65,7 +65,10 @@ export const ActivityRichTextEditor = ({ upsertActivity({ activity, input: { - body: newBody, + body: { + blocknote: newBody, + markdown: null, + }, }, }); } diff --git a/packages/twenty-front/src/modules/activities/hooks/__tests__/useActivityTargetObjectRecords.test.tsx b/packages/twenty-front/src/modules/activities/hooks/__tests__/useActivityTargetObjectRecords.test.tsx index 7d1426f4cb75..96c2e918895f 100644 --- a/packages/twenty-front/src/modules/activities/hooks/__tests__/useActivityTargetObjectRecords.test.tsx +++ b/packages/twenty-front/src/modules/activities/hooks/__tests__/useActivityTargetObjectRecords.test.tsx @@ -94,7 +94,10 @@ const task = { createdAt: '2023-04-26T10:12:42.33625+00:00', updatedAt: '2023-04-26T10:23:42.33625+00:00', title: 'Task title', - body: null, + body: { + blocknote: null, + markdown: null, + }, assigneeId: null, status: null, dueAt: '2023-04-26T10:12:42.33625+00:00', diff --git a/packages/twenty-front/src/modules/activities/notes/components/NoteCard.tsx b/packages/twenty-front/src/modules/activities/notes/components/NoteCard.tsx index 22879b117a0c..8ed440e393d8 100644 --- a/packages/twenty-front/src/modules/activities/notes/components/NoteCard.tsx +++ b/packages/twenty-front/src/modules/activities/notes/components/NoteCard.tsx @@ -71,7 +71,7 @@ export const NoteCard = ({ const openActivityRightDrawer = useOpenActivityRightDrawer({ objectNameSingular: CoreObjectNameSingular.Note, }); - const body = getActivityPreview(note.body); + const body = getActivityPreview(note.body.blocknote); const { FieldContextProvider: NoteTargetsContextProvider } = useFieldContext({ objectNameSingular: CoreObjectNameSingular.Note, diff --git a/packages/twenty-front/src/modules/activities/tasks/components/TaskRow.tsx b/packages/twenty-front/src/modules/activities/tasks/components/TaskRow.tsx index 90e11ab06448..3049dd24f1fe 100644 --- a/packages/twenty-front/src/modules/activities/tasks/components/TaskRow.tsx +++ b/packages/twenty-front/src/modules/activities/tasks/components/TaskRow.tsx @@ -82,7 +82,7 @@ export const TaskRow = ({ task }: { task: Task }) => { objectNameSingular: CoreObjectNameSingular.Task, }); - const body = getActivitySummary(task.body); + const body = getActivitySummary(task.body.blocknote); const { completeTask } = useCompleteTask(task); const { FieldContextProvider: TaskTargetsContextProvider } = useFieldContext({ diff --git a/packages/twenty-front/src/modules/activities/tasks/hooks/__tests__/useCompleteTask.test.tsx b/packages/twenty-front/src/modules/activities/tasks/hooks/__tests__/useCompleteTask.test.tsx index 51408dac5983..a06dcc35c21d 100644 --- a/packages/twenty-front/src/modules/activities/tasks/hooks/__tests__/useCompleteTask.test.tsx +++ b/packages/twenty-front/src/modules/activities/tasks/hooks/__tests__/useCompleteTask.test.tsx @@ -10,7 +10,10 @@ const task: Task = { id: '123', status: 'DONE', title: 'Test', - body: 'Test', + body: { + blocknote: 'Test', + markdown: 'Test', + }, dueAt: '2024-03-15T07:33:14.212Z', createdAt: '2024-03-15T07:33:14.212Z', updatedAt: '2024-03-15T07:33:14.212Z', diff --git a/packages/twenty-front/src/modules/activities/types/Activity.ts b/packages/twenty-front/src/modules/activities/types/Activity.ts index b54fd02a2b30..62d0448400a2 100644 --- a/packages/twenty-front/src/modules/activities/types/Activity.ts +++ b/packages/twenty-front/src/modules/activities/types/Activity.ts @@ -3,5 +3,8 @@ export type Activity = { createdAt: string; updatedAt: string; title: string; - body: string | null; + body: { + blocknote: string | null; + markdown: string | null; + }; }; diff --git a/packages/twenty-front/src/testing/mock-data/notes.ts b/packages/twenty-front/src/testing/mock-data/notes.ts index e1bdcab0b2b6..f9242a7a3769 100644 --- a/packages/twenty-front/src/testing/mock-data/notes.ts +++ b/packages/twenty-front/src/testing/mock-data/notes.ts @@ -9,7 +9,10 @@ export const mockedNotes: Array = [ createdAt: '2023-04-26T10:12:42.33625+00:00', updatedAt: '2023-04-26T10:23:42.33625+00:00', title: 'My very first note', - body: null, + body: { + blocknote: null, + markdown: null, + }, noteTargets: [ { id: '89bb825c-171e-4bcc-9cf7-43448d6fb300', @@ -64,7 +67,10 @@ export const mockedNotes: Array = [ createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), title: 'Another note', - body: null, + body: { + blocknote: null, + markdown: null, + }, noteTargets: [ { id: '89bb825c-171e-4bcc-9cf7-43448d6fb278t', diff --git a/packages/twenty-front/src/testing/mock-data/tasks.ts b/packages/twenty-front/src/testing/mock-data/tasks.ts index aa906a69b254..eb8a193dae78 100644 --- a/packages/twenty-front/src/testing/mock-data/tasks.ts +++ b/packages/twenty-front/src/testing/mock-data/tasks.ts @@ -26,7 +26,10 @@ export const mockedTasks: Array = [ createdAt: '2023-04-26T10:12:42.33625+00:00', updatedAt: '2023-04-26T10:23:42.33625+00:00', title: 'My very first note', - body: null, + body: { + blocknote: null, + markdown: null, + }, dueAt: '2023-04-26T10:12:42.33625+00:00', status: null, assignee: workspaceMember, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/activity-query-result-getter.handler.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/activity-query-result-getter.handler.ts index 6f32a877d70e..069b62904062 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/activity-query-result-getter.handler.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/handlers/activity-query-result-getter.handler.ts @@ -17,7 +17,7 @@ export class ActivityQueryResultGetterHandler activity: TaskWorkspaceEntity | NoteWorkspaceEntity, workspaceId: string, ): Promise { - if (!activity.id || !activity.body) { + if (!activity.id || !activity.body?.blocknote) { return activity; } diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts index 48961fe8530d..61f119dc0b83 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts @@ -37,6 +37,8 @@ export const fieldMetadataTypeToColumnType = ( return 'jsonb'; case FieldMetadataType.TS_VECTOR: return 'tsvector'; + case FieldMetadataType.RICH_TEXT_DEPRECATED: + return 'text'; default: throw new WorkspaceMigrationException( `Cannot convert ${fieldMetadataType} to column type.`, From dad7024fd892bbd6bf5bf6307a47caf0f8a7ad3c Mon Sep 17 00:00:00 2001 From: ad-elias Date: Tue, 7 Jan 2025 16:47:06 +0100 Subject: [PATCH 10/29] Fix RICH_TEXT value displaying --- .../activities/components/ActivityRichTextEditor.tsx | 6 +++--- .../record-field/components/FieldDisplay.tsx | 4 ++++ .../components/RichTextDeprecatedFieldDisplay.tsx | 12 ++++++++++++ .../display/components/RichTextFieldDisplay.tsx | 6 +++++- .../meta-types/hooks/useRichTextFieldDisplay.ts | 6 +----- .../editor/utils/getFirstNonEmptyLineOfRichText.ts | 10 +++++----- 6 files changed, 30 insertions(+), 14 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextDeprecatedFieldDisplay.tsx diff --git a/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx b/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx index 24503470725b..caa0f7917b2d 100644 --- a/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx +++ b/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx @@ -161,10 +161,10 @@ export const ActivityRichTextEditor = ({ const initialBody = useMemo(() => { if ( isDefined(activity) && - isNonEmptyString(activity.body) && - activity?.body !== '{}' + isNonEmptyString(activity.body.blocknote) && + activity?.body.blocknote !== '{}' ) { - return JSON.parse(activity.body); + return JSON.parse(activity.body.blocknote); } }, [activity]); diff --git a/packages/twenty-front/src/modules/object-record/record-field/components/FieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/components/FieldDisplay.tsx index 16e9b19ea924..a0edd99dd567 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/components/FieldDisplay.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/components/FieldDisplay.tsx @@ -8,6 +8,7 @@ import { LinksFieldDisplay } from '@/object-record/record-field/meta-types/displ import { PhonesFieldDisplay } from '@/object-record/record-field/meta-types/display/components/PhonesFieldDisplay'; import { RatingFieldDisplay } from '@/object-record/record-field/meta-types/display/components/RatingFieldDisplay'; import { RelationFromManyFieldDisplay } from '@/object-record/record-field/meta-types/display/components/RelationFromManyFieldDisplay'; +import { RichTextDeprecatedFieldDisplay } from '@/object-record/record-field/meta-types/display/components/RichTextDeprecatedFieldDisplay'; import { RichTextFieldDisplay } from '@/object-record/record-field/meta-types/display/components/RichTextFieldDisplay'; import { isFieldIdentifierDisplay } from '@/object-record/record-field/meta-types/display/utils/isFieldIdentifierDisplay'; import { isFieldActor } from '@/object-record/record-field/types/guards/isFieldActor'; @@ -19,6 +20,7 @@ import { isFieldPhones } from '@/object-record/record-field/types/guards/isField import { isFieldRating } from '@/object-record/record-field/types/guards/isFieldRating'; import { isFieldRelationFromManyObjects } from '@/object-record/record-field/types/guards/isFieldRelationFromManyObjects'; import { isFieldRelationToOneObject } from '@/object-record/record-field/types/guards/isFieldRelationToOneObject'; +import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; import { isFieldRichTextDeprecated } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecated'; import { FieldContext } from '../contexts/FieldContext'; import { AddressFieldDisplay } from '../meta-types/display/components/AddressFieldDisplay'; @@ -89,6 +91,8 @@ export const FieldDisplay = () => { ) : isFieldRating(fieldDefinition) ? ( ) : isFieldRichTextDeprecated(fieldDefinition) ? ( + + ) : isFieldRichText(fieldDefinition) ? ( ) : isFieldActor(fieldDefinition) ? ( diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextDeprecatedFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextDeprecatedFieldDisplay.tsx new file mode 100644 index 000000000000..a8de5e2635c1 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextDeprecatedFieldDisplay.tsx @@ -0,0 +1,12 @@ +import { useRichTextDeprecatedFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useRichTextDeprecatedFieldDisplay'; +import { getFirstNonEmptyLineOfRichText } from '@/ui/input/editor/utils/getFirstNonEmptyLineOfRichText'; + +export const RichTextDeprecatedFieldDisplay = () => { + const { fieldValue } = useRichTextDeprecatedFieldDisplay(); + + return ( +
+ {getFirstNonEmptyLineOfRichText(fieldValue)} +
+ ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextFieldDisplay.tsx index d163bc0134bd..9815602dede7 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextFieldDisplay.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextFieldDisplay.tsx @@ -1,12 +1,16 @@ import { useRichTextFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay'; import { getFirstNonEmptyLineOfRichText } from '@/ui/input/editor/utils/getFirstNonEmptyLineOfRichText'; +import { PartialBlock } from '@blocknote/core'; +import { parseJson } from '~/utils/parseJson'; export const RichTextFieldDisplay = () => { const { fieldValue } = useRichTextFieldDisplay(); + const blocks = parseJson(fieldValue?.blocknote); + return (
- {getFirstNonEmptyLineOfRichText(fieldValue)} + {getFirstNonEmptyLineOfRichText(blocks)}
); }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts index 4adbbf748b96..ff10926fd868 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts @@ -5,9 +5,7 @@ import { useRecordFieldValue } from '@/object-record/record-store/contexts/Recor import { FieldRichTextValue } from '@/object-record/record-field/types/FieldMetadata'; import { assertFieldMetadata } from '@/object-record/record-field/types/guards/assertFieldMetadata'; import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; -import { PartialBlock } from '@blocknote/core'; import { FieldMetadataType } from '~/generated-metadata/graphql'; -import { parseJson } from '~/utils/parseJson'; import { FieldContext } from '../../contexts/FieldContext'; export const useRichTextFieldDisplay = () => { @@ -26,11 +24,9 @@ export const useRichTextFieldDisplay = () => { fieldName, ); - const fieldValueParsed = parseJson(fieldValue?.blocknote); - return { fieldDefinition, - fieldValue: fieldValueParsed, + fieldValue, hotkeyScope, }; }; diff --git a/packages/twenty-front/src/modules/ui/input/editor/utils/getFirstNonEmptyLineOfRichText.ts b/packages/twenty-front/src/modules/ui/input/editor/utils/getFirstNonEmptyLineOfRichText.ts index 38a1ccceb9e3..4cfafc6cce0a 100644 --- a/packages/twenty-front/src/modules/ui/input/editor/utils/getFirstNonEmptyLineOfRichText.ts +++ b/packages/twenty-front/src/modules/ui/input/editor/utils/getFirstNonEmptyLineOfRichText.ts @@ -2,14 +2,14 @@ import { PartialBlock } from '@blocknote/core'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; export const getFirstNonEmptyLineOfRichText = ( - fieldValue: PartialBlock[] | null, + blocks: PartialBlock[] | null, ): string => { - if (fieldValue === null) { + if (blocks === null) { return ''; } - for (const node of fieldValue) { - if (!isUndefinedOrNull(node.content)) { - const contentArray = node.content as Array< + for (const block of blocks) { + if (!isUndefinedOrNull(block.content)) { + const contentArray = block.content as Array< { text: string } | { link: string } >; if (contentArray.length > 0) { From dfaaf6e932dca6bf371a614146aa15e6b801d133 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Tue, 7 Jan 2025 23:14:51 +0100 Subject: [PATCH 11/29] Import composite RICH_TEXT fields --- .../constants/CompositeFieldImportLabels.ts | 5 +++++ .../hooks/useBuildAvailableFieldsForImport.ts | 13 ++++++++++++ .../buildRecordFromImportedStructuredRow.ts | 21 +++++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/packages/twenty-front/src/modules/object-record/spreadsheet-import/constants/CompositeFieldImportLabels.ts b/packages/twenty-front/src/modules/object-record/spreadsheet-import/constants/CompositeFieldImportLabels.ts index 030601f241bc..9769338f99be 100644 --- a/packages/twenty-front/src/modules/object-record/spreadsheet-import/constants/CompositeFieldImportLabels.ts +++ b/packages/twenty-front/src/modules/object-record/spreadsheet-import/constants/CompositeFieldImportLabels.ts @@ -5,6 +5,7 @@ import { FieldFullNameValue, FieldLinksValue, FieldPhonesValue, + FieldRichTextValue, } from '@/object-record/record-field/types/FieldMetadata'; import { CompositeFieldLabels } from '@/object-record/spreadsheet-import/types/CompositeFieldLabels'; import { FieldMetadataType } from '~/generated-metadata/graphql'; @@ -39,6 +40,10 @@ export const COMPOSITE_FIELD_IMPORT_LABELS = { primaryPhoneCountryCodeLabel: 'Phone country code', primaryPhoneNumberLabel: 'Phone number', } satisfies Partial>, + [FieldMetadataType.RichText]: { + blocknoteLabel: 'BlockNote', + markdownLabel: 'Markdown', + } satisfies Partial>, [FieldMetadataType.Actor]: { sourceLabel: 'Source', }, diff --git a/packages/twenty-front/src/modules/object-record/spreadsheet-import/hooks/useBuildAvailableFieldsForImport.ts b/packages/twenty-front/src/modules/object-record/spreadsheet-import/hooks/useBuildAvailableFieldsForImport.ts index 5dfb80454f67..84eff136c46e 100644 --- a/packages/twenty-front/src/modules/object-record/spreadsheet-import/hooks/useBuildAvailableFieldsForImport.ts +++ b/packages/twenty-front/src/modules/object-record/spreadsheet-import/hooks/useBuildAvailableFieldsForImport.ts @@ -211,6 +211,19 @@ export const useBuildAvailableFieldsForImport = () => { ), }); }); + } else if (fieldMetadataItem.type === FieldMetadataType.RichText) { + Object.entries( + COMPOSITE_FIELD_IMPORT_LABELS[FieldMetadataType.RichText], + ).forEach(([_, fieldLabel]) => { + availableFieldsForImport.push({ + icon: getIcon(fieldMetadataItem.icon), + label: `${fieldLabel} (${fieldMetadataItem.label})`, + key: `${fieldLabel} (${fieldMetadataItem.name})`, + fieldType: { + type: 'input', + }, + }); + }); } else { availableFieldsForImport.push({ icon: getIcon(fieldMetadataItem.icon), diff --git a/packages/twenty-front/src/modules/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow.ts b/packages/twenty-front/src/modules/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow.ts index 78bd4310f98b..48032379ddb1 100644 --- a/packages/twenty-front/src/modules/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow.ts +++ b/packages/twenty-front/src/modules/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow.ts @@ -4,6 +4,7 @@ import { FieldEmailsValue, FieldLinksValue, FieldPhonesValue, + FieldRichTextValue, } from '@/object-record/record-field/types/FieldMetadata'; import { COMPOSITE_FIELD_IMPORT_LABELS } from '@/object-record/spreadsheet-import/constants/CompositeFieldImportLabels'; import { ImportedStructuredRow } from '@/spreadsheet-import/types'; @@ -36,6 +37,7 @@ export const buildRecordFromImportedStructuredRow = ( LINKS: { primaryLinkLabelLabel, primaryLinkUrlLabel }, EMAILS: { primaryEmailLabel }, PHONES: { primaryPhoneNumberLabel, primaryPhoneCountryCodeLabel }, + RICH_TEXT: { blocknoteLabel, markdownLabel }, } = COMPOSITE_FIELD_IMPORT_LABELS; for (const field of fields) { @@ -161,6 +163,25 @@ export const buildRecordFromImportedStructuredRow = ( } break; } + case FieldMetadataType.RichText: { + if ( + isDefined( + importedStructuredRow[`${blocknoteLabel} (${field.name})`] || + importedStructuredRow[`${markdownLabel} (${field.name})`], + ) + ) { + // TODO: Convert back and forth from blocknote here + recordToBuild[field.name] = { + blocknote: castToString( + importedStructuredRow[`${blocknoteLabel} (${field.name})`], + ), + markdown: castToString( + importedStructuredRow[`${markdownLabel} (${field.name})`], + ), + } satisfies FieldRichTextValue; + } + break; + } case FieldMetadataType.Emails: { if ( isDefined( From e56475429f46b4e6b953262b7c466976a6b22cd7 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Tue, 7 Jan 2025 23:26:40 +0100 Subject: [PATCH 12/29] Fix typo, add todo --- .../utils/buildRecordFromImportedStructuredRow.ts | 3 ++- .../constants/SettingsNonCompositeFieldTypeConfigs.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow.ts b/packages/twenty-front/src/modules/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow.ts index 48032379ddb1..5aba8d3fe2a2 100644 --- a/packages/twenty-front/src/modules/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow.ts +++ b/packages/twenty-front/src/modules/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow.ts @@ -170,7 +170,8 @@ export const buildRecordFromImportedStructuredRow = ( importedStructuredRow[`${markdownLabel} (${field.name})`], ) ) { - // TODO: Convert back and forth from blocknote here + // TODO: Convert back and forth from BlockNote. + // Has to happen server-side, because API shouldn't require both fields? How / where? recordToBuild[field.name] = { blocknote: castToString( importedStructuredRow[`${blocknoteLabel} (${field.name})`], diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts index 8e3442825d8a..d4aa50977596 100644 --- a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts +++ b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts @@ -123,7 +123,7 @@ export const SETTINGS_NON_COMPOSITE_FIELD_TYPE_CONFIGS: SettingsNonCompositeFiel category: 'Advanced', } as const satisfies SettingsFieldTypeConfig, [FieldMetadataType.RichTextDeprecated]: { - label: 'Rich Text Old', + label: 'Rich Text Deprecated', Icon: IllustrationIconSetting, exampleValue: "{ key: 'value' }", category: 'Basic', From 00b4b12cfc3e164300c7ea03c1eb9f4c7604fce6 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Wed, 8 Jan 2025 15:28:56 +0100 Subject: [PATCH 13/29] Override markdown and blocknote values WiP --- packages/twenty-server/package.json | 1 + .../factories/query-runner-args.factory.ts | 85 +++++--- .../rich-text.composite-type.ts | 14 +- yarn.lock | 197 +++++++++++++++++- 4 files changed, 258 insertions(+), 39 deletions(-) diff --git a/packages/twenty-server/package.json b/packages/twenty-server/package.json index 6de9253d6e35..f1813151db09 100644 --- a/packages/twenty-server/package.json +++ b/packages/twenty-server/package.json @@ -15,6 +15,7 @@ "typeorm": "../../node_modules/typeorm/.bin/typeorm" }, "dependencies": { + "@blocknote/server-util": "^0.22.0", "@esbuild-plugins/node-modules-polyfill": "^0.2.2", "@graphql-yoga/nestjs": "patch:@graphql-yoga/nestjs@2.1.0#./patches/@graphql-yoga+nestjs+2.1.0.patch", "@langchain/mistralai": "^0.0.24", diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts index 6fae9cb18e37..fbc8ba3b1150 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts @@ -1,5 +1,7 @@ import { Injectable } from '@nestjs/common'; +import { ServerBlockNoteEditor } from '@blocknote/server-util'; + import { ObjectRecord, ObjectRecordFilter, @@ -16,6 +18,10 @@ import { } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; +import { + RichTextMetadata, + richTextValueSchema, +} from 'src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type'; import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map'; @@ -129,47 +135,66 @@ export class QueryRunnerArgsFactory { options: WorkspaceQueryRunnerOptions, fieldMetadataMapByNameByName: Record, argPositionBackfillInput: ArgPositionBackfillInput, - ) { + ): Promise> { if (!data) { - return; + return Promise.resolve({}); } let isFieldPositionPresent = false; - const createArgPromiseByArgKey = Object.entries(data).map( - async ([key, value]) => { - const fieldMetadata = fieldMetadataMapByNameByName[key]; + const createArgByArgKeyPromises: Promise<[string, any]>[] = Object.entries( + data, + ).map(async ([key, value]): Promise<[string, any]> => { + const fieldMetadata = fieldMetadataMapByNameByName[key]; + + if (!fieldMetadata) { + return [key, value]; + } + + switch (fieldMetadata.type) { + case FieldMetadataType.POSITION: { + isFieldPositionPresent = true; + + const newValue = await this.recordPositionFactory.create( + value, + { + isCustom: options.objectMetadataItemWithFieldMaps.isCustom, + nameSingular: + options.objectMetadataItemWithFieldMaps.nameSingular, + }, + options.authContext.workspace.id, + argPositionBackfillInput.argIndex, + ); - if (!fieldMetadata) { - return [key, await Promise.resolve(value)]; + return [key, newValue]; } + case FieldMetadataType.NUMBER: + return [key, Number(value)] as const; + case FieldMetadataType.RICH_TEXT: { + const richTextValue = richTextValueSchema.parse(value); - switch (fieldMetadata.type) { - case FieldMetadataType.POSITION: - isFieldPositionPresent = true; + const { blocksToMarkdownLossy, tryParseMarkdownToBlocks } = + ServerBlockNoteEditor.create(); - return [ - key, - await this.recordPositionFactory.create( - value, - { - isCustom: options.objectMetadataItemWithFieldMaps.isCustom, - nameSingular: - options.objectMetadataItemWithFieldMaps.nameSingular, - }, - options.authContext.workspace.id, - argPositionBackfillInput.argIndex, - ), - ]; - case FieldMetadataType.NUMBER: - return [key, Number(value)]; - default: - return [key, await Promise.resolve(value)]; + const valueInBothFormats: RichTextMetadata = { + markdown: richTextValue.blocknote + ? await blocksToMarkdownLossy(JSON.parse(richTextValue.blocknote)) + : null, + blocknote: richTextValue.markdown + ? JSON.stringify( + await tryParseMarkdownToBlocks(richTextValue.markdown), + ) + : null, + }; + + return [key, richTextValue]; } - }, - ); + default: + return [key, value]; + } + }); - const newArgEntries = await Promise.all(createArgPromiseByArgKey); + const newArgEntries = await Promise.all(createArgByArgKeyPromises); if ( !isFieldPositionPresent && diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts index ba8fbe50a511..5665c2e95483 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts @@ -1,6 +1,6 @@ -import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface'; - import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface'; +import { z } from 'zod'; export const richTextCompositeType: CompositeType = { type: FieldMetadataType.RICH_TEXT, @@ -20,7 +20,9 @@ export const richTextCompositeType: CompositeType = { ], }; -export type RichTextMetadata = { - blocknote: string; - markdown: string; -}; +export const richTextValueSchema = z.object({ + blocknote: z.string().nullable(), + markdown: z.string().nullable(), +}); + +export type RichTextMetadata = z.infer; diff --git a/yarn.lock b/yarn.lock index 352de163adde..8c0f35f69ceb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3332,6 +3332,25 @@ __metadata: languageName: node linkType: hard +"@blocknote/server-util@npm:^0.22.0": + version: 0.22.0 + resolution: "@blocknote/server-util@npm:0.22.0" + dependencies: + "@blocknote/core": "npm:^0.22.0" + "@blocknote/react": "npm:^0.22.0" + "@tiptap/core": "npm:^2.7.1" + "@tiptap/pm": "npm:^2.7.1" + jsdom: "npm:^25.0.1" + y-prosemirror: "npm:1.2.13" + y-protocols: "npm:^1.0.6" + yjs: "npm:^13.6.15" + peerDependencies: + react: ^18.0 || ^19.0 || >= 19.0.0-rc + react-dom: ^18.0 || ^19.0 || >= 19.0.0-rc + checksum: 10c0/1cdb89a73f6b8dc1ff05f0206960a0b30e3273ba39b0f74e2fad04fdfbf64253a34c5d07ec4d2213600a0e73ce8855310e1947b9cbdcac51c81119ec44c190c8 + languageName: node + linkType: hard + "@blocknote/xl-docx-exporter@npm:^0.22.0": version: 0.22.0 resolution: "@blocknote/xl-docx-exporter@npm:0.22.0" @@ -18630,6 +18649,13 @@ __metadata: languageName: node linkType: hard +"agent-base@npm:^7.1.2": + version: 7.1.3 + resolution: "agent-base@npm:7.1.3" + checksum: 10c0/6192b580c5b1d8fb399b9c62bf8343d76654c2dd62afcb9a52b2cf44a8b6ace1e3b704d3fe3547d91555c857d3df02603341ff2cb961b9cfe2b12f9f3c38ee11 + languageName: node + linkType: hard + "agentkeepalive@npm:^4.1.3, agentkeepalive@npm:^4.2.1": version: 4.5.0 resolution: "agentkeepalive@npm:4.5.0" @@ -23649,6 +23675,15 @@ __metadata: languageName: node linkType: hard +"cssstyle@npm:^4.1.0": + version: 4.1.0 + resolution: "cssstyle@npm:4.1.0" + dependencies: + rrweb-cssom: "npm:^0.7.1" + checksum: 10c0/05c6597e5d3e0ec6b15221f2c0ce9a0443a46cc50a6089a3ba9ee1ac27f83ff86a445a8f95435137dadd859f091fc61b6d342abaf396d3c910471b5b33cfcbfa + languageName: node + linkType: hard + "csstype@npm:^3.0.2, csstype@npm:^3.1.2": version: 3.1.3 resolution: "csstype@npm:3.1.3" @@ -23998,6 +24033,16 @@ __metadata: languageName: node linkType: hard +"data-urls@npm:^5.0.0": + version: 5.0.0 + resolution: "data-urls@npm:5.0.0" + dependencies: + whatwg-mimetype: "npm:^4.0.0" + whatwg-url: "npm:^14.0.0" + checksum: 10c0/1b894d7d41c861f3a4ed2ae9b1c3f0909d4575ada02e36d3d3bc584bdd84278e20709070c79c3b3bff7ac98598cb191eb3e86a89a79ea4ee1ef360e1694f92ad + languageName: node + linkType: hard + "data-view-buffer@npm:^1.0.1": version: 1.0.1 resolution: "data-view-buffer@npm:1.0.1" @@ -29570,6 +29615,15 @@ __metadata: languageName: node linkType: hard +"html-encoding-sniffer@npm:^4.0.0": + version: 4.0.0 + resolution: "html-encoding-sniffer@npm:4.0.0" + dependencies: + whatwg-encoding: "npm:^3.1.1" + checksum: 10c0/523398055dc61ac9b34718a719cb4aa691e4166f29187e211e1607de63dc25ac7af52ca7c9aead0c4b3c0415ffecb17326396e1202e2e86ff4bca4c0ee4c6140 + languageName: node + linkType: hard + "html-escaper@npm:^2.0.0": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" @@ -29716,7 +29770,7 @@ __metadata: languageName: node linkType: hard -"http-proxy-agent@npm:^7.0.0": +"http-proxy-agent@npm:^7.0.0, http-proxy-agent@npm:^7.0.2": version: 7.0.2 resolution: "http-proxy-agent@npm:7.0.2" dependencies: @@ -29835,6 +29889,16 @@ __metadata: languageName: node linkType: hard +"https-proxy-agent@npm:^7.0.5": + version: 7.0.6 + resolution: "https-proxy-agent@npm:7.0.6" + dependencies: + agent-base: "npm:^7.1.2" + debug: "npm:4" + checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac + languageName: node + linkType: hard + "httpsnippet-lite@npm:^3.0.5": version: 3.0.5 resolution: "httpsnippet-lite@npm:3.0.5" @@ -32347,6 +32411,40 @@ __metadata: languageName: node linkType: hard +"jsdom@npm:^25.0.1": + version: 25.0.1 + resolution: "jsdom@npm:25.0.1" + dependencies: + cssstyle: "npm:^4.1.0" + data-urls: "npm:^5.0.0" + decimal.js: "npm:^10.4.3" + form-data: "npm:^4.0.0" + html-encoding-sniffer: "npm:^4.0.0" + http-proxy-agent: "npm:^7.0.2" + https-proxy-agent: "npm:^7.0.5" + is-potential-custom-element-name: "npm:^1.0.1" + nwsapi: "npm:^2.2.12" + parse5: "npm:^7.1.2" + rrweb-cssom: "npm:^0.7.1" + saxes: "npm:^6.0.0" + symbol-tree: "npm:^3.2.4" + tough-cookie: "npm:^5.0.0" + w3c-xmlserializer: "npm:^5.0.0" + webidl-conversions: "npm:^7.0.0" + whatwg-encoding: "npm:^3.1.1" + whatwg-mimetype: "npm:^4.0.0" + whatwg-url: "npm:^14.0.0" + ws: "npm:^8.18.0" + xml-name-validator: "npm:^5.0.0" + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true + checksum: 10c0/6bda32a6dfe4e37a30568bf51136bdb3ba9c0b72aadd6356280404275a34c9e097c8c25b5eb3c742e602623741e172da977ff456684befd77c9042ed9bf8c2b4 + languageName: node + linkType: hard + "jsdom@npm:~22.1.0": version: 22.1.0 resolution: "jsdom@npm:22.1.0" @@ -36940,6 +37038,13 @@ __metadata: languageName: node linkType: hard +"nwsapi@npm:^2.2.12": + version: 2.2.16 + resolution: "nwsapi@npm:2.2.16" + checksum: 10c0/0aa0637f4d51043d0183d994e08336bae996b03b42984381bf09ebdf3ff4909c018eda6b2a8aba0a08f3ea8303db8a0dad0608b38dc0bff15fd87017286ae21a + languageName: node + linkType: hard + "nwsapi@npm:^2.2.2, nwsapi@npm:^2.2.4": version: 2.2.12 resolution: "nwsapi@npm:2.2.12" @@ -39615,7 +39720,7 @@ __metadata: languageName: node linkType: hard -"punycode@npm:^2.1.0, punycode@npm:^2.1.1, punycode@npm:^2.3.0": +"punycode@npm:^2.1.0, punycode@npm:^2.1.1, punycode@npm:^2.3.0, punycode@npm:^2.3.1": version: 2.3.1 resolution: "punycode@npm:2.3.1" checksum: 10c0/14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9 @@ -41811,6 +41916,13 @@ __metadata: languageName: node linkType: hard +"rrweb-cssom@npm:^0.7.1": + version: 0.7.1 + resolution: "rrweb-cssom@npm:0.7.1" + checksum: 10c0/127b8ca6c8aac45e2755abbae6138d4a813b1bedc2caabf79466ae83ab3cfc84b5bfab513b7033f0aa4561c7753edf787d0dd01163ceacdee2e8eb1b6bf7237e + languageName: node + linkType: hard + "rtl-css-js@npm:^1.16.1": version: 1.16.1 resolution: "rtl-css-js@npm:1.16.1" @@ -44233,6 +44345,24 @@ __metadata: languageName: node linkType: hard +"tldts-core@npm:^6.1.71": + version: 6.1.71 + resolution: "tldts-core@npm:6.1.71" + checksum: 10c0/68c4e9ea7f02f14f811f5be5b50b176b099bc8385cae177b8265c1bb0c45215efecf54195a267326848024a24e204bba6d9f47cb899da1d81fd00da0c7f661b7 + languageName: node + linkType: hard + +"tldts@npm:^6.1.32": + version: 6.1.71 + resolution: "tldts@npm:6.1.71" + dependencies: + tldts-core: "npm:^6.1.71" + bin: + tldts: bin/cli.js + checksum: 10c0/fb1bfb6ec78ce334b9d7b0c8813ab553a9f9f8759d681e6f109dd55caced45f901a19d162d8bc2f12b37afc366e438154248ae1dab67ad091565146b8e57d217 + languageName: node + linkType: hard + "tmp@npm:0.2.1": version: 0.2.1 resolution: "tmp@npm:0.2.1" @@ -44372,6 +44502,15 @@ __metadata: languageName: node linkType: hard +"tough-cookie@npm:^5.0.0": + version: 5.0.0 + resolution: "tough-cookie@npm:5.0.0" + dependencies: + tldts: "npm:^6.1.32" + checksum: 10c0/4a69c885bf6f45c5a64e60262af99e8c0d58a33bd3d0ce5da62121eeb9c00996d0128a72df8fc4614cbde59cc8b70aa3e21e4c3c98c2bbde137d7aba7fa00124 + languageName: node + linkType: hard + "tough-cookie@npm:~2.5.0": version: 2.5.0 resolution: "tough-cookie@npm:2.5.0" @@ -44409,6 +44548,15 @@ __metadata: languageName: node linkType: hard +"tr46@npm:^5.0.0": + version: 5.0.0 + resolution: "tr46@npm:5.0.0" + dependencies: + punycode: "npm:^2.3.1" + checksum: 10c0/1521b6e7bbc8adc825c4561480f9fe48eb2276c81335eed9fa610aa4c44a48a3221f78b10e5f18b875769eb3413e30efbf209ed556a17a42aa8d690df44b7bee + languageName: node + linkType: hard + "tr46@npm:~0.0.3": version: 0.0.3 resolution: "tr46@npm:0.0.3" @@ -44965,6 +45113,7 @@ __metadata: version: 0.0.0-use.local resolution: "twenty-server@workspace:packages/twenty-server" dependencies: + "@blocknote/server-util": "npm:^0.22.0" "@esbuild-plugins/node-modules-polyfill": "npm:^0.2.2" "@graphql-yoga/nestjs": "patch:@graphql-yoga/nestjs@2.1.0#./patches/@graphql-yoga+nestjs+2.1.0.patch" "@langchain/mistralai": "npm:^0.0.24" @@ -47263,6 +47412,15 @@ __metadata: languageName: node linkType: hard +"w3c-xmlserializer@npm:^5.0.0": + version: 5.0.0 + resolution: "w3c-xmlserializer@npm:5.0.0" + dependencies: + xml-name-validator: "npm:^5.0.0" + checksum: 10c0/8712774c1aeb62dec22928bf1cdfd11426c2c9383a1a63f2bcae18db87ca574165a0fbe96b312b73652149167ac6c7f4cf5409f2eb101d9c805efe0e4bae798b + languageName: node + linkType: hard + "wait-on@npm:^7.0.0": version: 7.2.0 resolution: "wait-on@npm:7.2.0" @@ -47504,6 +47662,15 @@ __metadata: languageName: node linkType: hard +"whatwg-encoding@npm:^3.1.1": + version: 3.1.1 + resolution: "whatwg-encoding@npm:3.1.1" + dependencies: + iconv-lite: "npm:0.6.3" + checksum: 10c0/273b5f441c2f7fda3368a496c3009edbaa5e43b71b09728f90425e7f487e5cef9eb2b846a31bd760dd8077739c26faf6b5ca43a5f24033172b003b72cf61a93e + languageName: node + linkType: hard + "whatwg-fetch@npm:^3.4.1": version: 3.6.20 resolution: "whatwg-fetch@npm:3.6.20" @@ -47518,6 +47685,13 @@ __metadata: languageName: node linkType: hard +"whatwg-mimetype@npm:^4.0.0": + version: 4.0.0 + resolution: "whatwg-mimetype@npm:4.0.0" + checksum: 10c0/a773cdc8126b514d790bdae7052e8bf242970cebd84af62fb2f35a33411e78e981f6c0ab9ed1fe6ec5071b09d5340ac9178e05b52d35a9c4bcf558ba1b1551df + languageName: node + linkType: hard + "whatwg-url@npm:^11.0.0": version: 11.0.0 resolution: "whatwg-url@npm:11.0.0" @@ -47538,6 +47712,16 @@ __metadata: languageName: node linkType: hard +"whatwg-url@npm:^14.0.0": + version: 14.1.0 + resolution: "whatwg-url@npm:14.1.0" + dependencies: + tr46: "npm:^5.0.0" + webidl-conversions: "npm:^7.0.0" + checksum: 10c0/f00104f1c67ce086ba8ffedab529cbbd9aefd8c0a6555320026de7aeff31f91c38680f95818b140a7c9cc657cde3781e567835dda552ddb1e2b8faaba0ac3cb6 + languageName: node + linkType: hard + "whatwg-url@npm:^5.0.0": version: 5.0.0 resolution: "whatwg-url@npm:5.0.0" @@ -47865,7 +48049,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.11.0, ws@npm:^8.12.0, ws@npm:^8.13.0, ws@npm:^8.2.3": +"ws@npm:^8.11.0, ws@npm:^8.12.0, ws@npm:^8.13.0, ws@npm:^8.18.0, ws@npm:^8.2.3": version: 8.18.0 resolution: "ws@npm:8.18.0" peerDependencies: @@ -47952,6 +48136,13 @@ __metadata: languageName: node linkType: hard +"xml-name-validator@npm:^5.0.0": + version: 5.0.0 + resolution: "xml-name-validator@npm:5.0.0" + checksum: 10c0/3fcf44e7b73fb18be917fdd4ccffff3639373c7cb83f8fc35df6001fecba7942f1dbead29d91ebb8315e2f2ff786b508f0c9dc0215b6353f9983c6b7d62cb1f5 + languageName: node + linkType: hard + "xml-parser-xo@npm:^3.2.0": version: 3.2.0 resolution: "xml-parser-xo@npm:3.2.0" From eda4f09fc6b290e94a2cfc279cb7f56ec26ff273 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Wed, 8 Jan 2025 22:15:54 +0100 Subject: [PATCH 14/29] BlockNote server util import testing --- package.json | 1 + packages/twenty-server/package.json | 1 - .../factories/query-runner-args.factory.ts | 13 ++++++++----- yarn.lock | 2 +- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index f9b0f86ca8d3..b8cf112a9a87 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "@aws-sdk/credential-providers": "^3.363.0", "@blocknote/mantine": "^0.22.0", "@blocknote/react": "^0.22.0", + "@blocknote/server-util": "^0.22.0", "@codesandbox/sandpack-react": "^2.13.5", "@dagrejs/dagre": "^1.1.2", "@emotion/react": "^11.11.1", diff --git a/packages/twenty-server/package.json b/packages/twenty-server/package.json index f1813151db09..6de9253d6e35 100644 --- a/packages/twenty-server/package.json +++ b/packages/twenty-server/package.json @@ -15,7 +15,6 @@ "typeorm": "../../node_modules/typeorm/.bin/typeorm" }, "dependencies": { - "@blocknote/server-util": "^0.22.0", "@esbuild-plugins/node-modules-polyfill": "^0.2.2", "@graphql-yoga/nestjs": "patch:@graphql-yoga/nestjs@2.1.0#./patches/@graphql-yoga+nestjs+2.1.0.patch", "@langchain/mistralai": "^0.0.24", diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts index fbc8ba3b1150..27a89acb4420 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts @@ -173,21 +173,24 @@ export class QueryRunnerArgsFactory { case FieldMetadataType.RICH_TEXT: { const richTextValue = richTextValueSchema.parse(value); - const { blocksToMarkdownLossy, tryParseMarkdownToBlocks } = - ServerBlockNoteEditor.create(); + const serverBlockNoteEditor = ServerBlockNoteEditor.create(); const valueInBothFormats: RichTextMetadata = { markdown: richTextValue.blocknote - ? await blocksToMarkdownLossy(JSON.parse(richTextValue.blocknote)) + ? await serverBlockNoteEditor.blocksToMarkdownLossy( + JSON.parse(richTextValue.blocknote), + ) : null, blocknote: richTextValue.markdown ? JSON.stringify( - await tryParseMarkdownToBlocks(richTextValue.markdown), + await serverBlockNoteEditor.tryParseMarkdownToBlocks( + richTextValue.markdown, + ), ) : null, }; - return [key, richTextValue]; + return [key, valueInBothFormats]; } default: return [key, value]; diff --git a/yarn.lock b/yarn.lock index 8c0f35f69ceb..46ccdf0de2e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -45113,7 +45113,6 @@ __metadata: version: 0.0.0-use.local resolution: "twenty-server@workspace:packages/twenty-server" dependencies: - "@blocknote/server-util": "npm:^0.22.0" "@esbuild-plugins/node-modules-polyfill": "npm:^0.2.2" "@graphql-yoga/nestjs": "patch:@graphql-yoga/nestjs@2.1.0#./patches/@graphql-yoga+nestjs+2.1.0.patch" "@langchain/mistralai": "npm:^0.0.24" @@ -45232,6 +45231,7 @@ __metadata: "@babel/preset-typescript": "npm:^7.24.6" "@blocknote/mantine": "npm:^0.22.0" "@blocknote/react": "npm:^0.22.0" + "@blocknote/server-util": "npm:^0.22.0" "@codesandbox/sandpack-react": "npm:^2.13.5" "@crxjs/vite-plugin": "npm:^1.0.14" "@dagrejs/dagre": "npm:^1.1.2" From 2db9cf47ff96284f39a04d605773ffa33af778fd Mon Sep 17 00:00:00 2001 From: ad-elias Date: Wed, 8 Jan 2025 23:07:58 +0100 Subject: [PATCH 15/29] Downgrade @blocknote/server-util to fix import --- package.json | 2 +- yarn.lock | 309 +++++++++++++++++++++++---------------------------- 2 files changed, 137 insertions(+), 174 deletions(-) diff --git a/package.json b/package.json index b8cf112a9a87..be9bd8099495 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "@aws-sdk/credential-providers": "^3.363.0", "@blocknote/mantine": "^0.22.0", "@blocknote/react": "^0.22.0", - "@blocknote/server-util": "^0.22.0", + "@blocknote/server-util": "0.17.1", "@codesandbox/sandpack-react": "^2.13.5", "@dagrejs/dagre": "^1.1.2", "@emotion/react": "^11.11.1", diff --git a/yarn.lock b/yarn.lock index 46ccdf0de2e7..169ba733cb1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3247,6 +3247,55 @@ __metadata: languageName: node linkType: hard +"@blocknote/core@npm:^0.17.1": + version: 0.17.1 + resolution: "@blocknote/core@npm:0.17.1" + dependencies: + "@emoji-mart/data": "npm:^1.2.1" + "@tiptap/core": "npm:^2.7.1" + "@tiptap/extension-bold": "npm:^2.7.1" + "@tiptap/extension-code": "npm:^2.7.1" + "@tiptap/extension-collaboration": "npm:^2.7.1" + "@tiptap/extension-collaboration-cursor": "npm:^2.7.1" + "@tiptap/extension-dropcursor": "npm:^2.7.1" + "@tiptap/extension-gapcursor": "npm:^2.7.1" + "@tiptap/extension-hard-break": "npm:^2.7.1" + "@tiptap/extension-history": "npm:^2.7.1" + "@tiptap/extension-horizontal-rule": "npm:^2.7.1" + "@tiptap/extension-italic": "npm:^2.7.1" + "@tiptap/extension-link": "npm:^2.7.1" + "@tiptap/extension-paragraph": "npm:^2.7.1" + "@tiptap/extension-strike": "npm:^2.7.1" + "@tiptap/extension-table-cell": "npm:^2.7.1" + "@tiptap/extension-table-header": "npm:^2.7.1" + "@tiptap/extension-table-row": "npm:^2.7.1" + "@tiptap/extension-text": "npm:^2.7.1" + "@tiptap/extension-underline": "npm:^2.7.1" + "@tiptap/pm": "npm:^2.7.1" + emoji-mart: "npm:^5.6.0" + hast-util-from-dom: "npm:^4.2.0" + prosemirror-model: "npm:^1.21.0" + prosemirror-state: "npm:^1.4.3" + prosemirror-tables: "npm:^1.3.7" + prosemirror-transform: "npm:^1.9.0" + prosemirror-view: "npm:^1.33.7" + rehype-format: "npm:^5.0.0" + rehype-parse: "npm:^8.0.4" + rehype-remark: "npm:^9.1.2" + rehype-stringify: "npm:^9.0.3" + remark-gfm: "npm:^3.0.1" + remark-parse: "npm:^10.0.1" + remark-rehype: "npm:^10.1.0" + remark-stringify: "npm:^10.0.2" + unified: "npm:^10.1.2" + uuid: "npm:^8.3.2" + y-prosemirror: "npm:1.2.12" + y-protocols: "npm:^1.0.6" + yjs: "npm:^13.6.15" + checksum: 10c0/0acd1a099832d8e271983924f19e59aa056ead278a8bac8ab7a64d6c7d40a787a27143bdee3d6b6f3dfa26c7723207d98d7124ffef8ff9c4cfdf3034140716ab + languageName: node + linkType: hard + "@blocknote/core@npm:^0.22.0": version: 0.22.0 resolution: "@blocknote/core@npm:0.22.0" @@ -3315,6 +3364,25 @@ __metadata: languageName: node linkType: hard +"@blocknote/react@npm:^0.17.1": + version: 0.17.1 + resolution: "@blocknote/react@npm:0.17.1" + dependencies: + "@blocknote/core": "npm:^0.17.1" + "@floating-ui/react": "npm:^0.26.4" + "@tiptap/core": "npm:^2.7.1" + "@tiptap/react": "npm:^2.7.1" + lodash.merge: "npm:^4.6.2" + react: "npm:^18" + react-dom: "npm:^18" + react-icons: "npm:^5.2.1" + peerDependencies: + react: ^18 + react-dom: ^18 + checksum: 10c0/4914dce225f60905b3dfe59805d7cf1f0c0c6d87295a09204333b913f6449cc90c2947baeb65c1f06106beaea8b50ac5c2785782cb2528f7e913b2877427c3e4 + languageName: node + linkType: hard + "@blocknote/react@npm:^0.22.0": version: 0.22.0 resolution: "@blocknote/react@npm:0.22.0" @@ -3332,22 +3400,24 @@ __metadata: languageName: node linkType: hard -"@blocknote/server-util@npm:^0.22.0": - version: 0.22.0 - resolution: "@blocknote/server-util@npm:0.22.0" +"@blocknote/server-util@npm:0.17.1": + version: 0.17.1 + resolution: "@blocknote/server-util@npm:0.17.1" dependencies: - "@blocknote/core": "npm:^0.22.0" - "@blocknote/react": "npm:^0.22.0" + "@blocknote/core": "npm:^0.17.1" + "@blocknote/react": "npm:^0.17.1" "@tiptap/core": "npm:^2.7.1" "@tiptap/pm": "npm:^2.7.1" - jsdom: "npm:^25.0.1" - y-prosemirror: "npm:1.2.13" + jsdom: "npm:^21.1.0" + react: "npm:^18" + react-dom: "npm:^18" + y-prosemirror: "npm:1.2.12" y-protocols: "npm:^1.0.6" yjs: "npm:^13.6.15" peerDependencies: - react: ^18.0 || ^19.0 || >= 19.0.0-rc - react-dom: ^18.0 || ^19.0 || >= 19.0.0-rc - checksum: 10c0/1cdb89a73f6b8dc1ff05f0206960a0b30e3273ba39b0f74e2fad04fdfbf64253a34c5d07ec4d2213600a0e73ce8855310e1947b9cbdcac51c81119ec44c190c8 + react: ^18 + react-dom: ^18 + checksum: 10c0/7d400dbf19562f8827bc524f87d673d711fba95a50fb299e0eb638f01c2dc87fd840a132b33dae60c0944637208f18a632f72f7664cb03b8ce81f5be7f8e59f0 languageName: node linkType: hard @@ -15307,6 +15377,16 @@ __metadata: languageName: node linkType: hard +"@tiptap/extension-dropcursor@npm:^2.7.1": + version: 2.11.0 + resolution: "@tiptap/extension-dropcursor@npm:2.11.0" + peerDependencies: + "@tiptap/core": ^2.7.0 + "@tiptap/pm": ^2.7.0 + checksum: 10c0/12ace987deec4bd02f52ee7a8f837bd71d560bca1ce670d43c6a715526a336aa5431ed044cba44babd45f7f0ed79002d16f03430ce72899a4a9713679e924717 + languageName: node + linkType: hard + "@tiptap/extension-floating-menu@npm:^2.10.4": version: 2.10.4 resolution: "@tiptap/extension-floating-menu@npm:2.10.4" @@ -18649,13 +18729,6 @@ __metadata: languageName: node linkType: hard -"agent-base@npm:^7.1.2": - version: 7.1.3 - resolution: "agent-base@npm:7.1.3" - checksum: 10c0/6192b580c5b1d8fb399b9c62bf8343d76654c2dd62afcb9a52b2cf44a8b6ace1e3b704d3fe3547d91555c857d3df02603341ff2cb961b9cfe2b12f9f3c38ee11 - languageName: node - linkType: hard - "agentkeepalive@npm:^4.1.3, agentkeepalive@npm:^4.2.1": version: 4.5.0 resolution: "agentkeepalive@npm:4.5.0" @@ -23675,15 +23748,6 @@ __metadata: languageName: node linkType: hard -"cssstyle@npm:^4.1.0": - version: 4.1.0 - resolution: "cssstyle@npm:4.1.0" - dependencies: - rrweb-cssom: "npm:^0.7.1" - checksum: 10c0/05c6597e5d3e0ec6b15221f2c0ce9a0443a46cc50a6089a3ba9ee1ac27f83ff86a445a8f95435137dadd859f091fc61b6d342abaf396d3c910471b5b33cfcbfa - languageName: node - linkType: hard - "csstype@npm:^3.0.2, csstype@npm:^3.1.2": version: 3.1.3 resolution: "csstype@npm:3.1.3" @@ -24033,16 +24097,6 @@ __metadata: languageName: node linkType: hard -"data-urls@npm:^5.0.0": - version: 5.0.0 - resolution: "data-urls@npm:5.0.0" - dependencies: - whatwg-mimetype: "npm:^4.0.0" - whatwg-url: "npm:^14.0.0" - checksum: 10c0/1b894d7d41c861f3a4ed2ae9b1c3f0909d4575ada02e36d3d3bc584bdd84278e20709070c79c3b3bff7ac98598cb191eb3e86a89a79ea4ee1ef360e1694f92ad - languageName: node - linkType: hard - "data-view-buffer@npm:^1.0.1": version: 1.0.1 resolution: "data-view-buffer@npm:1.0.1" @@ -29615,15 +29669,6 @@ __metadata: languageName: node linkType: hard -"html-encoding-sniffer@npm:^4.0.0": - version: 4.0.0 - resolution: "html-encoding-sniffer@npm:4.0.0" - dependencies: - whatwg-encoding: "npm:^3.1.1" - checksum: 10c0/523398055dc61ac9b34718a719cb4aa691e4166f29187e211e1607de63dc25ac7af52ca7c9aead0c4b3c0415ffecb17326396e1202e2e86ff4bca4c0ee4c6140 - languageName: node - linkType: hard - "html-escaper@npm:^2.0.0": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" @@ -29770,7 +29815,7 @@ __metadata: languageName: node linkType: hard -"http-proxy-agent@npm:^7.0.0, http-proxy-agent@npm:^7.0.2": +"http-proxy-agent@npm:^7.0.0": version: 7.0.2 resolution: "http-proxy-agent@npm:7.0.2" dependencies: @@ -29889,16 +29934,6 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^7.0.5": - version: 7.0.6 - resolution: "https-proxy-agent@npm:7.0.6" - dependencies: - agent-base: "npm:^7.1.2" - debug: "npm:4" - checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac - languageName: node - linkType: hard - "httpsnippet-lite@npm:^3.0.5": version: 3.0.5 resolution: "httpsnippet-lite@npm:3.0.5" @@ -32411,37 +32446,42 @@ __metadata: languageName: node linkType: hard -"jsdom@npm:^25.0.1": - version: 25.0.1 - resolution: "jsdom@npm:25.0.1" +"jsdom@npm:^21.1.0": + version: 21.1.2 + resolution: "jsdom@npm:21.1.2" dependencies: - cssstyle: "npm:^4.1.0" - data-urls: "npm:^5.0.0" + abab: "npm:^2.0.6" + acorn: "npm:^8.8.2" + acorn-globals: "npm:^7.0.0" + cssstyle: "npm:^3.0.0" + data-urls: "npm:^4.0.0" decimal.js: "npm:^10.4.3" + domexception: "npm:^4.0.0" + escodegen: "npm:^2.0.0" form-data: "npm:^4.0.0" - html-encoding-sniffer: "npm:^4.0.0" - http-proxy-agent: "npm:^7.0.2" - https-proxy-agent: "npm:^7.0.5" + html-encoding-sniffer: "npm:^3.0.0" + http-proxy-agent: "npm:^5.0.0" + https-proxy-agent: "npm:^5.0.1" is-potential-custom-element-name: "npm:^1.0.1" - nwsapi: "npm:^2.2.12" + nwsapi: "npm:^2.2.4" parse5: "npm:^7.1.2" - rrweb-cssom: "npm:^0.7.1" + rrweb-cssom: "npm:^0.6.0" saxes: "npm:^6.0.0" symbol-tree: "npm:^3.2.4" - tough-cookie: "npm:^5.0.0" - w3c-xmlserializer: "npm:^5.0.0" + tough-cookie: "npm:^4.1.2" + w3c-xmlserializer: "npm:^4.0.0" webidl-conversions: "npm:^7.0.0" - whatwg-encoding: "npm:^3.1.1" - whatwg-mimetype: "npm:^4.0.0" - whatwg-url: "npm:^14.0.0" - ws: "npm:^8.18.0" - xml-name-validator: "npm:^5.0.0" + whatwg-encoding: "npm:^2.0.0" + whatwg-mimetype: "npm:^3.0.0" + whatwg-url: "npm:^12.0.1" + ws: "npm:^8.13.0" + xml-name-validator: "npm:^4.0.0" peerDependencies: - canvas: ^2.11.2 + canvas: ^2.5.0 peerDependenciesMeta: canvas: optional: true - checksum: 10c0/6bda32a6dfe4e37a30568bf51136bdb3ba9c0b72aadd6356280404275a34c9e097c8c25b5eb3c742e602623741e172da977ff456684befd77c9042ed9bf8c2b4 + checksum: 10c0/905012680891fa0c92b8c18acfa35fc0b3e4b15f778ee3494aec1aca3274875160d2be35917d666b8eacd0b3121f483bd95fbe35e14790a004b805b1cf01818a languageName: node linkType: hard @@ -37038,13 +37078,6 @@ __metadata: languageName: node linkType: hard -"nwsapi@npm:^2.2.12": - version: 2.2.16 - resolution: "nwsapi@npm:2.2.16" - checksum: 10c0/0aa0637f4d51043d0183d994e08336bae996b03b42984381bf09ebdf3ff4909c018eda6b2a8aba0a08f3ea8303db8a0dad0608b38dc0bff15fd87017286ae21a - languageName: node - linkType: hard - "nwsapi@npm:^2.2.2, nwsapi@npm:^2.2.4": version: 2.2.12 resolution: "nwsapi@npm:2.2.12" @@ -39556,7 +39589,7 @@ __metadata: languageName: node linkType: hard -"prosemirror-tables@npm:^1.6.1": +"prosemirror-tables@npm:^1.3.7, prosemirror-tables@npm:^1.6.1": version: 1.6.2 resolution: "prosemirror-tables@npm:1.6.2" dependencies: @@ -39592,7 +39625,7 @@ __metadata: languageName: node linkType: hard -"prosemirror-transform@npm:^1.10.2": +"prosemirror-transform@npm:^1.10.2, prosemirror-transform@npm:^1.9.0": version: 1.10.2 resolution: "prosemirror-transform@npm:1.10.2" dependencies: @@ -39720,7 +39753,7 @@ __metadata: languageName: node linkType: hard -"punycode@npm:^2.1.0, punycode@npm:^2.1.1, punycode@npm:^2.3.0, punycode@npm:^2.3.1": +"punycode@npm:^2.1.0, punycode@npm:^2.1.1, punycode@npm:^2.3.0": version: 2.3.1 resolution: "punycode@npm:2.3.1" checksum: 10c0/14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9 @@ -40059,7 +40092,7 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:^18.2.0": +"react-dom@npm:^18, react-dom@npm:^18.2.0": version: 18.3.1 resolution: "react-dom@npm:18.3.1" dependencies: @@ -41916,13 +41949,6 @@ __metadata: languageName: node linkType: hard -"rrweb-cssom@npm:^0.7.1": - version: 0.7.1 - resolution: "rrweb-cssom@npm:0.7.1" - checksum: 10c0/127b8ca6c8aac45e2755abbae6138d4a813b1bedc2caabf79466ae83ab3cfc84b5bfab513b7033f0aa4561c7753edf787d0dd01163ceacdee2e8eb1b6bf7237e - languageName: node - linkType: hard - "rtl-css-js@npm:^1.16.1": version: 1.16.1 resolution: "rtl-css-js@npm:1.16.1" @@ -44345,24 +44371,6 @@ __metadata: languageName: node linkType: hard -"tldts-core@npm:^6.1.71": - version: 6.1.71 - resolution: "tldts-core@npm:6.1.71" - checksum: 10c0/68c4e9ea7f02f14f811f5be5b50b176b099bc8385cae177b8265c1bb0c45215efecf54195a267326848024a24e204bba6d9f47cb899da1d81fd00da0c7f661b7 - languageName: node - linkType: hard - -"tldts@npm:^6.1.32": - version: 6.1.71 - resolution: "tldts@npm:6.1.71" - dependencies: - tldts-core: "npm:^6.1.71" - bin: - tldts: bin/cli.js - checksum: 10c0/fb1bfb6ec78ce334b9d7b0c8813ab553a9f9f8759d681e6f109dd55caced45f901a19d162d8bc2f12b37afc366e438154248ae1dab67ad091565146b8e57d217 - languageName: node - linkType: hard - "tmp@npm:0.2.1": version: 0.2.1 resolution: "tmp@npm:0.2.1" @@ -44502,15 +44510,6 @@ __metadata: languageName: node linkType: hard -"tough-cookie@npm:^5.0.0": - version: 5.0.0 - resolution: "tough-cookie@npm:5.0.0" - dependencies: - tldts: "npm:^6.1.32" - checksum: 10c0/4a69c885bf6f45c5a64e60262af99e8c0d58a33bd3d0ce5da62121eeb9c00996d0128a72df8fc4614cbde59cc8b70aa3e21e4c3c98c2bbde137d7aba7fa00124 - languageName: node - linkType: hard - "tough-cookie@npm:~2.5.0": version: 2.5.0 resolution: "tough-cookie@npm:2.5.0" @@ -44548,15 +44547,6 @@ __metadata: languageName: node linkType: hard -"tr46@npm:^5.0.0": - version: 5.0.0 - resolution: "tr46@npm:5.0.0" - dependencies: - punycode: "npm:^2.3.1" - checksum: 10c0/1521b6e7bbc8adc825c4561480f9fe48eb2276c81335eed9fa610aa4c44a48a3221f78b10e5f18b875769eb3413e30efbf209ed556a17a42aa8d690df44b7bee - languageName: node - linkType: hard - "tr46@npm:~0.0.3": version: 0.0.3 resolution: "tr46@npm:0.0.3" @@ -45231,7 +45221,7 @@ __metadata: "@babel/preset-typescript": "npm:^7.24.6" "@blocknote/mantine": "npm:^0.22.0" "@blocknote/react": "npm:^0.22.0" - "@blocknote/server-util": "npm:^0.22.0" + "@blocknote/server-util": "npm:0.17.1" "@codesandbox/sandpack-react": "npm:^2.13.5" "@crxjs/vite-plugin": "npm:^1.0.14" "@dagrejs/dagre": "npm:^1.1.2" @@ -47412,15 +47402,6 @@ __metadata: languageName: node linkType: hard -"w3c-xmlserializer@npm:^5.0.0": - version: 5.0.0 - resolution: "w3c-xmlserializer@npm:5.0.0" - dependencies: - xml-name-validator: "npm:^5.0.0" - checksum: 10c0/8712774c1aeb62dec22928bf1cdfd11426c2c9383a1a63f2bcae18db87ca574165a0fbe96b312b73652149167ac6c7f4cf5409f2eb101d9c805efe0e4bae798b - languageName: node - linkType: hard - "wait-on@npm:^7.0.0": version: 7.2.0 resolution: "wait-on@npm:7.2.0" @@ -47662,15 +47643,6 @@ __metadata: languageName: node linkType: hard -"whatwg-encoding@npm:^3.1.1": - version: 3.1.1 - resolution: "whatwg-encoding@npm:3.1.1" - dependencies: - iconv-lite: "npm:0.6.3" - checksum: 10c0/273b5f441c2f7fda3368a496c3009edbaa5e43b71b09728f90425e7f487e5cef9eb2b846a31bd760dd8077739c26faf6b5ca43a5f24033172b003b72cf61a93e - languageName: node - linkType: hard - "whatwg-fetch@npm:^3.4.1": version: 3.6.20 resolution: "whatwg-fetch@npm:3.6.20" @@ -47685,13 +47657,6 @@ __metadata: languageName: node linkType: hard -"whatwg-mimetype@npm:^4.0.0": - version: 4.0.0 - resolution: "whatwg-mimetype@npm:4.0.0" - checksum: 10c0/a773cdc8126b514d790bdae7052e8bf242970cebd84af62fb2f35a33411e78e981f6c0ab9ed1fe6ec5071b09d5340ac9178e05b52d35a9c4bcf558ba1b1551df - languageName: node - linkType: hard - "whatwg-url@npm:^11.0.0": version: 11.0.0 resolution: "whatwg-url@npm:11.0.0" @@ -47712,16 +47677,6 @@ __metadata: languageName: node linkType: hard -"whatwg-url@npm:^14.0.0": - version: 14.1.0 - resolution: "whatwg-url@npm:14.1.0" - dependencies: - tr46: "npm:^5.0.0" - webidl-conversions: "npm:^7.0.0" - checksum: 10c0/f00104f1c67ce086ba8ffedab529cbbd9aefd8c0a6555320026de7aeff31f91c38680f95818b140a7c9cc657cde3781e567835dda552ddb1e2b8faaba0ac3cb6 - languageName: node - linkType: hard - "whatwg-url@npm:^5.0.0": version: 5.0.0 resolution: "whatwg-url@npm:5.0.0" @@ -48049,7 +48004,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.11.0, ws@npm:^8.12.0, ws@npm:^8.13.0, ws@npm:^8.18.0, ws@npm:^8.2.3": +"ws@npm:^8.11.0, ws@npm:^8.12.0, ws@npm:^8.13.0, ws@npm:^8.2.3": version: 8.18.0 resolution: "ws@npm:8.18.0" peerDependencies: @@ -48136,13 +48091,6 @@ __metadata: languageName: node linkType: hard -"xml-name-validator@npm:^5.0.0": - version: 5.0.0 - resolution: "xml-name-validator@npm:5.0.0" - checksum: 10c0/3fcf44e7b73fb18be917fdd4ccffff3639373c7cb83f8fc35df6001fecba7942f1dbead29d91ebb8315e2f2ff786b508f0c9dc0215b6353f9983c6b7d62cb1f5 - languageName: node - linkType: hard - "xml-parser-xo@npm:^3.2.0": version: 3.2.0 resolution: "xml-parser-xo@npm:3.2.0" @@ -48228,6 +48176,21 @@ __metadata: languageName: node linkType: hard +"y-prosemirror@npm:1.2.12": + version: 1.2.12 + resolution: "y-prosemirror@npm:1.2.12" + dependencies: + lib0: "npm:^0.2.42" + peerDependencies: + prosemirror-model: ^1.7.1 + prosemirror-state: ^1.2.3 + prosemirror-view: ^1.9.10 + y-protocols: ^1.0.1 + yjs: ^13.5.38 + checksum: 10c0/c460aa9104c71806112a17b52449221343095c774bc929a3bcfaa6d752ce9af1a5a8359c974625c70de8bf48e10b2aa8702f12ca2027f85c6097d1621969beeb + languageName: node + linkType: hard + "y-prosemirror@npm:1.2.13": version: 1.2.13 resolution: "y-prosemirror@npm:1.2.13" From 94983fc58570d6a0755ce9646f7d78fc7b054068 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Thu, 9 Jan 2025 14:11:01 +0100 Subject: [PATCH 16/29] Fix rich text value updating --- .../factories/query-runner-args.factory.ts | 65 +++++++++++++++---- 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts index 27a89acb4420..bcb52f321bad 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts @@ -15,6 +15,8 @@ import { FindOneResolverArgs, ResolverArgs, ResolverArgsType, + UpdateManyResolverArgs, + UpdateOneResolverArgs, } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; @@ -82,6 +84,41 @@ export class QueryRunnerArgsFactory { ) ?? [], ), } satisfies CreateManyResolverArgs; + case ResolverArgsType.UpdateOne: + return { + ...args, + id: (args as UpdateOneResolverArgs).id, + data: await this.overrideDataByFieldMetadata( + (args as UpdateOneResolverArgs).data, + options, + fieldMetadataMapByNameByName, + { + argIndex: 0, + shouldBackfillPosition, + }, + ), + } satisfies UpdateOneResolverArgs; + case ResolverArgsType.UpdateMany: + return { + ...args, + filter: await this.overrideFilterByFieldMetadata( + (args as UpdateManyResolverArgs).filter, + fieldMetadataMapByNameByName, + ), + data: await Promise.all( + (args as UpdateManyResolverArgs).data?.map((arg, index) => + this.overrideDataByFieldMetadata( + arg, + options, + fieldMetadataMapByNameByName, + { + argIndex: index, + shouldBackfillPosition, + }, + ), + ) ?? [], + ), + } satisfies UpdateManyResolverArgs; case ResolverArgsType.FindOne: return { ...args, @@ -175,19 +212,23 @@ export class QueryRunnerArgsFactory { const serverBlockNoteEditor = ServerBlockNoteEditor.create(); + const convertedMarkdown = richTextValue.blocknote + ? await serverBlockNoteEditor.blocksToMarkdownLossy( + JSON.parse(richTextValue.blocknote), + ) + : null; + + const convertedBlocknote = richTextValue.markdown + ? JSON.stringify( + await serverBlockNoteEditor.tryParseMarkdownToBlocks( + richTextValue.markdown, + ), + ) + : null; + const valueInBothFormats: RichTextMetadata = { - markdown: richTextValue.blocknote - ? await serverBlockNoteEditor.blocksToMarkdownLossy( - JSON.parse(richTextValue.blocknote), - ) - : null, - blocknote: richTextValue.markdown - ? JSON.stringify( - await serverBlockNoteEditor.tryParseMarkdownToBlocks( - richTextValue.markdown, - ), - ) - : null, + markdown: richTextValue.markdown || convertedMarkdown, + blocknote: richTextValue.blocknote || convertedBlocknote, }; return [key, valueInBothFormats]; From bb7b4b8545a84d609e6554360e3c6c8ce481e4e7 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Thu, 9 Jan 2025 19:49:28 +0100 Subject: [PATCH 17/29] Composite RICH_TEXT upgade command WiP --- .../commands/database-command.module.ts | 2 + .../0-41-migrate-rich-text-field.command.ts | 179 ++++++++++++++++++ .../0-41/0-41-upgrade-version.command.ts | 37 ++++ .../0-41/0-41-upgrade-version.module.ts | 26 +++ 4 files changed, 244 insertions(+) create mode 100644 packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts create mode 100644 packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.command.ts create mode 100644 packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.module.ts diff --git a/packages/twenty-server/src/database/commands/database-command.module.ts b/packages/twenty-server/src/database/commands/database-command.module.ts index e3aee52bea0c..b41e0f151d2e 100644 --- a/packages/twenty-server/src/database/commands/database-command.module.ts +++ b/packages/twenty-server/src/database/commands/database-command.module.ts @@ -12,6 +12,7 @@ import { UpgradeTo0_33CommandModule } from 'src/database/commands/upgrade-versio import { UpgradeTo0_34CommandModule } from 'src/database/commands/upgrade-version/0-34/0-34-upgrade-version.module'; import { UpgradeTo0_35CommandModule } from 'src/database/commands/upgrade-version/0-35/0-35-upgrade-version.module'; import { UpgradeTo0_40CommandModule } from 'src/database/commands/upgrade-version/0-40/0-40-upgrade-version.module'; +import { UpgradeTo0_41CommandModule } from 'src/database/commands/upgrade-version/0-41/0-41-upgrade-version.module'; import { TypeORMModule } from 'src/database/typeorm/typeorm.module'; import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; @@ -57,6 +58,7 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp UpgradeTo0_34CommandModule, UpgradeTo0_35CommandModule, UpgradeTo0_40CommandModule, + UpgradeTo0_41CommandModule, FeatureFlagModule, ], providers: [ diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts new file mode 100644 index 000000000000..9c7cb14d9216 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts @@ -0,0 +1,179 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import chalk from 'chalk'; +import { Command } from 'nest-commander'; +import { Repository } from 'typeorm'; + +import { + ActiveWorkspacesCommandOptions, + ActiveWorkspacesCommandRunner, +} from 'src/database/commands/active-workspaces.command'; +import { isCommandLogger } from 'src/database/commands/logger'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { + FieldMetadataEntity, + FieldMetadataType, +} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service'; +import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util'; +import { + WorkspaceMigrationColumnActionType, + WorkspaceMigrationColumnAlter, + WorkspaceMigrationColumnCreate, + WorkspaceMigrationTableAction, + WorkspaceMigrationTableActionType, +} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity'; +import { WorkspaceMigrationFactory } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.factory'; +import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service'; +import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util'; +import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service'; +import { isDefined } from 'src/utils/is-defined'; + +@Command({ + name: 'upgrade-0.41:migrate-rich-text-field', + description: 'Migrate RICH_TEXT fields to new composite structure', +}) +export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner { + constructor( + @InjectRepository(Workspace, 'core') + protected readonly workspaceRepository: Repository, + @InjectRepository(FieldMetadataEntity, 'metadata') + private readonly fieldMetadataRepository: Repository, + @InjectRepository(ObjectMetadataEntity, 'metadata') + private readonly objectMetadataRepository: Repository, + private readonly workspaceMigrationService: WorkspaceMigrationService, + private readonly workspaceMigrationFactory: WorkspaceMigrationFactory, + private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService, + private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService, + ) { + super(workspaceRepository); + } + + async executeActiveWorkspacesCommand( + _passedParam: string[], + options: ActiveWorkspacesCommandOptions, + workspaceIds: string[], + ): Promise { + this.logger.log( + 'Running command to migrate RICH_TEXT fields to new composite structure', + ); + + if (isCommandLogger(this.logger)) { + this.logger.setVerbose(options.verbose ?? false); + } + + let workspaceIterator = 1; + + for (const workspaceId of workspaceIds) { + this.logger.log( + `Running command for workspace ${workspaceId} ${workspaceIterator}/${workspaceIds.length}`, + ); + + // eslint-disable-next-line + try { + const richTextFields = await this.fieldMetadataRepository.find({ + where: { + workspaceId, + type: FieldMetadataType.RICH_TEXT, + }, + }); + + this.logger.log(`Found ${richTextFields.length} RICH_TEXT fields`); + + if (!richTextFields.length) { + this.logger.log('No RICH_TEXT fields found in this workspace'); + workspaceIterator++; + continue; + } + + for (const richTextField of richTextFields) { + const objectMetadata = await this.objectMetadataRepository.findOne({ + where: { id: richTextField.objectMetadataId }, + }); + + if (!isDefined(objectMetadata)) { + this.logger.log( + `Object metadata not found for rich text field ${richTextField.name} in workspace ${workspaceId}`, + ); + continue; + } + + const deprecatedField: Partial = { + ...richTextField, + type: FieldMetadataType.RICH_TEXT_DEPRECATED, + }; + + await this.fieldMetadataRepository.save(deprecatedField); + + const newRichTextField: Partial = { + ...richTextField, + id: undefined, + type: FieldMetadataType.RICH_TEXT, + defaultValue: null, + }; + + await this.fieldMetadataRepository.save(newRichTextField); + + await this.workspaceMigrationService.createCustomMigration( + generateMigrationName( + `migrate-rich-text-field-${objectMetadata.nameSingular}-${richTextField.name}`, + ), + workspaceId, + [ + { + name: computeObjectTargetTable(objectMetadata), + action: WorkspaceMigrationTableActionType.ALTER, + columns: [ + { + action: WorkspaceMigrationColumnActionType.ALTER, + currentColumnDefinition: { + columnName: `${richTextField.name}`, + columnType: 'text', + isNullable: true, + defaultValue: null, + }, + alteredColumnDefinition: { + columnName: `${richTextField.name}Blocknote`, + columnType: 'text', + isNullable: true, + defaultValue: null, + }, + } satisfies WorkspaceMigrationColumnAlter, + { + action: WorkspaceMigrationColumnActionType.CREATE, + columnName: `${richTextField.name}Markdown`, + columnType: 'text', + isNullable: true, + defaultValue: null, + } satisfies WorkspaceMigrationColumnCreate, + ], + } satisfies WorkspaceMigrationTableAction, + ], + ); + } + + await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations( + workspaceId, + ); + + await this.workspaceMetadataVersionService.incrementMetadataVersion( + workspaceId, + ); + + workspaceIterator++; + this.logger.log( + chalk.green(`Command completed for workspace ${workspaceId}`), + ); + } catch (error) { + throw error; + /* this.logger.error( + chalk.red(`Error in workspace ${workspaceId}: ${error.message}`), + ); + workspaceIterator++; */ + } + } + + this.logger.log(chalk.green('Command completed!')); + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.command.ts new file mode 100644 index 000000000000..828762b8f922 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.command.ts @@ -0,0 +1,37 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import { Repository } from 'typeorm'; + +import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command'; +import { BaseCommandOptions } from 'src/database/commands/base.command'; +import { MigrateRichTextFieldCommand } from 'src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; + +@Command({ + name: 'upgrade-0.41', + description: 'Upgrade to 0.41', +}) +export class UpgradeTo0_41Command extends ActiveWorkspacesCommandRunner { + constructor( + @InjectRepository(Workspace, 'core') + protected readonly workspaceRepository: Repository, + private readonly migrateRichTextFieldCommand: MigrateRichTextFieldCommand, + ) { + super(workspaceRepository); + } + + async executeActiveWorkspacesCommand( + passedParam: string[], + options: BaseCommandOptions, + workspaceIds: string[], + ): Promise { + this.logger.log('Running command to upgrade to 0.41'); + + await this.migrateRichTextFieldCommand.executeActiveWorkspacesCommand( + passedParam, + options, + workspaceIds, + ); + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.module.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.module.ts new file mode 100644 index 000000000000..4a95e6214fcc --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.module.ts @@ -0,0 +1,26 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; + +import { MigrateRichTextFieldCommand } from 'src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command'; +import { UpgradeTo0_41Command } from 'src/database/commands/upgrade-version/0-41/0-41-upgrade-version.command'; +import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; +import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; +import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module'; +import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module'; +import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([Workspace], 'core'), + TypeOrmModule.forFeature( + [ObjectMetadataEntity, FieldMetadataEntity], + 'metadata', + ), + WorkspaceMigrationRunnerModule, + WorkspaceMigrationModule, + WorkspaceMetadataVersionModule, + ], + providers: [UpgradeTo0_41Command, MigrateRichTextFieldCommand], +}) +export class UpgradeTo0_41CommandModule {} From 017defed008912155b906b3977c2281f676352c6 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Thu, 9 Jan 2025 22:01:14 +0100 Subject: [PATCH 18/29] Upgade command fixes --- .../0-41/0-41-migrate-rich-text-field.command.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts index 9c7cb14d9216..78fff97ecd93 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts @@ -99,13 +99,11 @@ export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner { continue; } - const deprecatedField: Partial = { + const deprecatedRichTextField: Partial = { ...richTextField, type: FieldMetadataType.RICH_TEXT_DEPRECATED, }; - await this.fieldMetadataRepository.save(deprecatedField); - const newRichTextField: Partial = { ...richTextField, id: undefined, @@ -113,7 +111,16 @@ export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner { defaultValue: null, }; - await this.fieldMetadataRepository.save(newRichTextField); + if (!deprecatedRichTextField.id) { + throw new Error('Missing deprecated rich textfield id'); + } + + await this.fieldMetadataRepository.update( + deprecatedRichTextField.id, + deprecatedRichTextField, + ); + + await this.fieldMetadataRepository.create(newRichTextField); await this.workspaceMigrationService.createCustomMigration( generateMigrationName( From a2f207ad082e50594247ec2e300b59cac9c68b92 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Thu, 9 Jan 2025 22:17:58 +0100 Subject: [PATCH 19/29] Remove debug throw --- .../0-41/0-41-migrate-rich-text-field.command.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts index 78fff97ecd93..9392f86756be 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts @@ -70,7 +70,6 @@ export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner { `Running command for workspace ${workspaceId} ${workspaceIterator}/${workspaceIds.length}`, ); - // eslint-disable-next-line try { const richTextFields = await this.fieldMetadataRepository.find({ where: { @@ -173,11 +172,10 @@ export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner { chalk.green(`Command completed for workspace ${workspaceId}`), ); } catch (error) { - throw error; - /* this.logger.error( + this.logger.error( chalk.red(`Error in workspace ${workspaceId}: ${error.message}`), ); - workspaceIterator++; */ + workspaceIterator++; } } From 1b4aae56d8cd6c88fe91e65942e20b9f8de64e18 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Fri, 10 Jan 2025 01:36:48 +0100 Subject: [PATCH 20/29] Composite RICH_TEXT upgade command works --- .../0-41-migrate-rich-text-field.command.ts | 70 ++++++++++++------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts index 9392f86756be..4c611d7819fb 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts @@ -1,5 +1,6 @@ import { InjectRepository } from '@nestjs/typeorm'; +import { ServerBlockNoteEditor } from '@blocknote/server-util'; import chalk from 'chalk'; import { Command } from 'nest-commander'; import { Repository } from 'typeorm'; @@ -26,6 +27,7 @@ import { } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity'; import { WorkspaceMigrationFactory } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.factory'; import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util'; import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service'; import { isDefined } from 'src/utils/is-defined'; @@ -42,6 +44,7 @@ export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner { private readonly fieldMetadataRepository: Repository, @InjectRepository(ObjectMetadataEntity, 'metadata') private readonly objectMetadataRepository: Repository, + private readonly twentyORMGlobalManager: TwentyORMGlobalManager, private readonly workspaceMigrationService: WorkspaceMigrationService, private readonly workspaceMigrationFactory: WorkspaceMigrationFactory, private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService, @@ -78,14 +81,14 @@ export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner { }, }); - this.logger.log(`Found ${richTextFields.length} RICH_TEXT fields`); - if (!richTextFields.length) { this.logger.log('No RICH_TEXT fields found in this workspace'); workspaceIterator++; continue; } + this.logger.log(`Found ${richTextFields.length} RICH_TEXT fields`); + for (const richTextField of richTextFields) { const objectMetadata = await this.objectMetadataRepository.findOne({ where: { id: richTextField.objectMetadataId }, @@ -98,29 +101,6 @@ export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner { continue; } - const deprecatedRichTextField: Partial = { - ...richTextField, - type: FieldMetadataType.RICH_TEXT_DEPRECATED, - }; - - const newRichTextField: Partial = { - ...richTextField, - id: undefined, - type: FieldMetadataType.RICH_TEXT, - defaultValue: null, - }; - - if (!deprecatedRichTextField.id) { - throw new Error('Missing deprecated rich textfield id'); - } - - await this.fieldMetadataRepository.update( - deprecatedRichTextField.id, - deprecatedRichTextField, - ); - - await this.fieldMetadataRepository.create(newRichTextField); - await this.workspaceMigrationService.createCustomMigration( generateMigrationName( `migrate-rich-text-field-${objectMetadata.nameSingular}-${richTextField.name}`, @@ -167,6 +147,46 @@ export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner { workspaceId, ); + const serverBlockNoteEditor = ServerBlockNoteEditor.create(); + + for (const richTextField of richTextFields) { + const objectMetadata = await this.objectMetadataRepository.findOne({ + where: { id: richTextField.objectMetadataId }, + }); + + if (!isDefined(objectMetadata)) { + this.logger.log( + `Object metadata not found for rich text field ${richTextField.name} in workspace ${workspaceId}`, + ); + continue; + } + + const repository = + await this.twentyORMGlobalManager.getRepositoryForWorkspace( + workspaceId, + objectMetadata.nameSingular, + false, + ); + + const records = await repository.find(); + + this.logger.log(`Generating markdown for ${records.length} records`); + + for (const record of records) { + const recordRichTextField = record[richTextField.name]; + const blocknoteValue = recordRichTextField.blocknote; + const markdown = blocknoteValue + ? await serverBlockNoteEditor.blocksToMarkdownLossy( + JSON.parse(blocknoteValue), + ) + : null; + + await repository.update(record.id, { + [`${richTextField.name}Markdown`]: markdown, + }); + } + } + workspaceIterator++; this.logger.log( chalk.green(`Command completed for workspace ${workspaceId}`), From b8e21daf479f0e33dcb452aa54302f726b4b8121 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Fri, 10 Jan 2025 12:47:54 +0100 Subject: [PATCH 21/29] Fix lint --- .../services/type-mapper.service.ts | 38 +++++++++---------- .../rich-text.composite-type.ts | 6 ++- .../dtos/default-value.input.ts | 24 ++++++------ .../field-metadata-default-value.interface.ts | 32 ++++++++-------- .../validate-default-value-for-type.util.ts | 38 +++++++++---------- .../workspace-migration.factory.ts | 8 ++-- 6 files changed, 74 insertions(+), 72 deletions(-) diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts index 84d467d24f38..e960d438c0bc 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts @@ -2,16 +2,16 @@ import { Injectable } from '@nestjs/common'; import { GraphQLISODateTime } from '@nestjs/graphql'; import { - GraphQLBoolean, - GraphQLEnumType, - GraphQLID, - GraphQLInputObjectType, - GraphQLInputType, - GraphQLList, - GraphQLNonNull, - GraphQLScalarType, - GraphQLString, - GraphQLType, + GraphQLBoolean, + GraphQLEnumType, + GraphQLID, + GraphQLInputObjectType, + GraphQLInputType, + GraphQLList, + GraphQLNonNull, + GraphQLScalarType, + GraphQLString, + GraphQLType, } from 'graphql'; import { FieldMetadataType } from 'twenty-shared'; @@ -19,20 +19,20 @@ import { FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadat import { OrderByDirectionType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/enum'; import { - ArrayFilterType, - BigFloatFilterType, - BooleanFilterType, - DateFilterType, - FloatFilterType, - RawJsonFilterType, - StringFilterType, + ArrayFilterType, + BigFloatFilterType, + BooleanFilterType, + DateFilterType, + FloatFilterType, + RawJsonFilterType, + StringFilterType, } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input'; import { IDFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/id-filter.input-type'; import { MultiSelectFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/multi-select-filter.input-type'; import { SelectFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/select-filter.input-type'; import { - BigFloatScalarType, - UUIDScalarType, + BigFloatScalarType, + UUIDScalarType, } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; import { PositionScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars/position.scalar'; import { RawJSONScalar } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars/raw-json.scalar'; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts index 5665c2e95483..d2f58f89d24b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts @@ -1,7 +1,9 @@ -import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface'; import { z } from 'zod'; +import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface'; + +import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; + export const richTextCompositeType: CompositeType = { type: FieldMetadataType.RICH_TEXT, properties: [ diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts index 76234d6f640d..7332782ef640 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts @@ -1,16 +1,16 @@ import { - IsArray, - IsBoolean, - IsDate, - IsNotEmpty, - IsNumber, - IsNumberString, - IsObject, - IsOptional, - IsString, - IsUUID, - Matches, - ValidateIf, + IsArray, + IsBoolean, + IsDate, + IsNotEmpty, + IsNumber, + IsNumberString, + IsObject, + IsOptional, + IsString, + IsUUID, + Matches, + ValidateIf, } from 'class-validator'; import { IsQuotedString } from 'src/engine/metadata-modules/field-metadata/validators/is-quoted-string.validator'; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts index 986372e4a44c..02ffde94c487 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts @@ -1,22 +1,22 @@ import { FieldMetadataType } from 'twenty-shared'; import { - FieldMetadataDefaultActor, - FieldMetadataDefaultArray, - FieldMetadataDefaultValueAddress, - FieldMetadataDefaultValueBoolean, - FieldMetadataDefaultValueCurrency, - FieldMetadataDefaultValueDateTime, - FieldMetadataDefaultValueEmails, - FieldMetadataDefaultValueFullName, - FieldMetadataDefaultValueLinks, - FieldMetadataDefaultValueNowFunction, - FieldMetadataDefaultValueNumber, - FieldMetadataDefaultValuePhones, - FieldMetadataDefaultValueRawJson, - FieldMetadataDefaultValueRichTextDeprecated, - FieldMetadataDefaultValueString, - FieldMetadataDefaultValueUuidFunction, + FieldMetadataDefaultActor, + FieldMetadataDefaultArray, + FieldMetadataDefaultValueAddress, + FieldMetadataDefaultValueBoolean, + FieldMetadataDefaultValueCurrency, + FieldMetadataDefaultValueDateTime, + FieldMetadataDefaultValueEmails, + FieldMetadataDefaultValueFullName, + FieldMetadataDefaultValueLinks, + FieldMetadataDefaultValueNowFunction, + FieldMetadataDefaultValueNumber, + FieldMetadataDefaultValuePhones, + FieldMetadataDefaultValueRawJson, + FieldMetadataDefaultValueRichTextDeprecated, + FieldMetadataDefaultValueString, + FieldMetadataDefaultValueUuidFunction, } from 'src/engine/metadata-modules/field-metadata/dtos/default-value.input'; type ExtractValueType = T extends { value: infer V } ? V : T; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts index b26c12e92552..ca1529252f4a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts @@ -3,28 +3,28 @@ import { ValidationError, validateSync } from 'class-validator'; import { FieldMetadataType } from 'twenty-shared'; import { - FieldMetadataClassValidation, - FieldMetadataDefaultValue, + FieldMetadataClassValidation, + FieldMetadataDefaultValue, } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface'; import { - FieldMetadataDefaultActor, - FieldMetadataDefaultValueAddress, - FieldMetadataDefaultValueBoolean, - FieldMetadataDefaultValueCurrency, - FieldMetadataDefaultValueDate, - FieldMetadataDefaultValueDateTime, - FieldMetadataDefaultValueEmails, - FieldMetadataDefaultValueFullName, - FieldMetadataDefaultValueLinks, - FieldMetadataDefaultValueNowFunction, - FieldMetadataDefaultValueNumber, - FieldMetadataDefaultValuePhones, - FieldMetadataDefaultValueRawJson, - FieldMetadataDefaultValueRichText, - FieldMetadataDefaultValueString, - FieldMetadataDefaultValueStringArray, - FieldMetadataDefaultValueUuidFunction, + FieldMetadataDefaultActor, + FieldMetadataDefaultValueAddress, + FieldMetadataDefaultValueBoolean, + FieldMetadataDefaultValueCurrency, + FieldMetadataDefaultValueDate, + FieldMetadataDefaultValueDateTime, + FieldMetadataDefaultValueEmails, + FieldMetadataDefaultValueFullName, + FieldMetadataDefaultValueLinks, + FieldMetadataDefaultValueNowFunction, + FieldMetadataDefaultValueNumber, + FieldMetadataDefaultValuePhones, + FieldMetadataDefaultValueRawJson, + FieldMetadataDefaultValueRichText, + FieldMetadataDefaultValueString, + FieldMetadataDefaultValueStringArray, + FieldMetadataDefaultValueUuidFunction, } from 'src/engine/metadata-modules/field-metadata/dtos/default-value.input'; import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util'; diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts index 1c1b2d16ab26..5514c83714ff 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts @@ -11,12 +11,12 @@ import { CompositeColumnActionFactory } from 'src/engine/metadata-modules/worksp import { EnumColumnActionFactory } from 'src/engine/metadata-modules/workspace-migration/factories/enum-column-action.factory'; import { TsVectorColumnActionFactory } from 'src/engine/metadata-modules/workspace-migration/factories/ts-vector-column-action.factory'; import { - WorkspaceMigrationColumnAction, - WorkspaceMigrationColumnActionType, + WorkspaceMigrationColumnAction, + WorkspaceMigrationColumnActionType, } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity'; import { - WorkspaceMigrationException, - WorkspaceMigrationExceptionCode, + WorkspaceMigrationException, + WorkspaceMigrationExceptionCode, } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.exception'; @Injectable() From 3357f66de49860cc7c29111d785d7918e5c104d4 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Fri, 10 Jan 2025 12:51:51 +0100 Subject: [PATCH 22/29] Move upgrade command to a separate branch --- .../commands/database-command.module.ts | 2 - .../0-41-migrate-rich-text-field.command.ts | 204 ------------------ .../0-41/0-41-upgrade-version.command.ts | 37 ---- .../0-41/0-41-upgrade-version.module.ts | 26 --- 4 files changed, 269 deletions(-) delete mode 100644 packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts delete mode 100644 packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.command.ts delete mode 100644 packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.module.ts diff --git a/packages/twenty-server/src/database/commands/database-command.module.ts b/packages/twenty-server/src/database/commands/database-command.module.ts index b41e0f151d2e..e3aee52bea0c 100644 --- a/packages/twenty-server/src/database/commands/database-command.module.ts +++ b/packages/twenty-server/src/database/commands/database-command.module.ts @@ -12,7 +12,6 @@ import { UpgradeTo0_33CommandModule } from 'src/database/commands/upgrade-versio import { UpgradeTo0_34CommandModule } from 'src/database/commands/upgrade-version/0-34/0-34-upgrade-version.module'; import { UpgradeTo0_35CommandModule } from 'src/database/commands/upgrade-version/0-35/0-35-upgrade-version.module'; import { UpgradeTo0_40CommandModule } from 'src/database/commands/upgrade-version/0-40/0-40-upgrade-version.module'; -import { UpgradeTo0_41CommandModule } from 'src/database/commands/upgrade-version/0-41/0-41-upgrade-version.module'; import { TypeORMModule } from 'src/database/typeorm/typeorm.module'; import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; @@ -58,7 +57,6 @@ import { WorkspaceSyncMetadataModule } from 'src/engine/workspace-manager/worksp UpgradeTo0_34CommandModule, UpgradeTo0_35CommandModule, UpgradeTo0_40CommandModule, - UpgradeTo0_41CommandModule, FeatureFlagModule, ], providers: [ diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts deleted file mode 100644 index 4c611d7819fb..000000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command.ts +++ /dev/null @@ -1,204 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; - -import { ServerBlockNoteEditor } from '@blocknote/server-util'; -import chalk from 'chalk'; -import { Command } from 'nest-commander'; -import { Repository } from 'typeorm'; - -import { - ActiveWorkspacesCommandOptions, - ActiveWorkspacesCommandRunner, -} from 'src/database/commands/active-workspaces.command'; -import { isCommandLogger } from 'src/database/commands/logger'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { - FieldMetadataEntity, - FieldMetadataType, -} from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; -import { WorkspaceMetadataVersionService } from 'src/engine/metadata-modules/workspace-metadata-version/services/workspace-metadata-version.service'; -import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util'; -import { - WorkspaceMigrationColumnActionType, - WorkspaceMigrationColumnAlter, - WorkspaceMigrationColumnCreate, - WorkspaceMigrationTableAction, - WorkspaceMigrationTableActionType, -} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity'; -import { WorkspaceMigrationFactory } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.factory'; -import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service'; -import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; -import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util'; -import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service'; -import { isDefined } from 'src/utils/is-defined'; - -@Command({ - name: 'upgrade-0.41:migrate-rich-text-field', - description: 'Migrate RICH_TEXT fields to new composite structure', -}) -export class MigrateRichTextFieldCommand extends ActiveWorkspacesCommandRunner { - constructor( - @InjectRepository(Workspace, 'core') - protected readonly workspaceRepository: Repository, - @InjectRepository(FieldMetadataEntity, 'metadata') - private readonly fieldMetadataRepository: Repository, - @InjectRepository(ObjectMetadataEntity, 'metadata') - private readonly objectMetadataRepository: Repository, - private readonly twentyORMGlobalManager: TwentyORMGlobalManager, - private readonly workspaceMigrationService: WorkspaceMigrationService, - private readonly workspaceMigrationFactory: WorkspaceMigrationFactory, - private readonly workspaceMigrationRunnerService: WorkspaceMigrationRunnerService, - private readonly workspaceMetadataVersionService: WorkspaceMetadataVersionService, - ) { - super(workspaceRepository); - } - - async executeActiveWorkspacesCommand( - _passedParam: string[], - options: ActiveWorkspacesCommandOptions, - workspaceIds: string[], - ): Promise { - this.logger.log( - 'Running command to migrate RICH_TEXT fields to new composite structure', - ); - - if (isCommandLogger(this.logger)) { - this.logger.setVerbose(options.verbose ?? false); - } - - let workspaceIterator = 1; - - for (const workspaceId of workspaceIds) { - this.logger.log( - `Running command for workspace ${workspaceId} ${workspaceIterator}/${workspaceIds.length}`, - ); - - try { - const richTextFields = await this.fieldMetadataRepository.find({ - where: { - workspaceId, - type: FieldMetadataType.RICH_TEXT, - }, - }); - - if (!richTextFields.length) { - this.logger.log('No RICH_TEXT fields found in this workspace'); - workspaceIterator++; - continue; - } - - this.logger.log(`Found ${richTextFields.length} RICH_TEXT fields`); - - for (const richTextField of richTextFields) { - const objectMetadata = await this.objectMetadataRepository.findOne({ - where: { id: richTextField.objectMetadataId }, - }); - - if (!isDefined(objectMetadata)) { - this.logger.log( - `Object metadata not found for rich text field ${richTextField.name} in workspace ${workspaceId}`, - ); - continue; - } - - await this.workspaceMigrationService.createCustomMigration( - generateMigrationName( - `migrate-rich-text-field-${objectMetadata.nameSingular}-${richTextField.name}`, - ), - workspaceId, - [ - { - name: computeObjectTargetTable(objectMetadata), - action: WorkspaceMigrationTableActionType.ALTER, - columns: [ - { - action: WorkspaceMigrationColumnActionType.ALTER, - currentColumnDefinition: { - columnName: `${richTextField.name}`, - columnType: 'text', - isNullable: true, - defaultValue: null, - }, - alteredColumnDefinition: { - columnName: `${richTextField.name}Blocknote`, - columnType: 'text', - isNullable: true, - defaultValue: null, - }, - } satisfies WorkspaceMigrationColumnAlter, - { - action: WorkspaceMigrationColumnActionType.CREATE, - columnName: `${richTextField.name}Markdown`, - columnType: 'text', - isNullable: true, - defaultValue: null, - } satisfies WorkspaceMigrationColumnCreate, - ], - } satisfies WorkspaceMigrationTableAction, - ], - ); - } - - await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations( - workspaceId, - ); - - await this.workspaceMetadataVersionService.incrementMetadataVersion( - workspaceId, - ); - - const serverBlockNoteEditor = ServerBlockNoteEditor.create(); - - for (const richTextField of richTextFields) { - const objectMetadata = await this.objectMetadataRepository.findOne({ - where: { id: richTextField.objectMetadataId }, - }); - - if (!isDefined(objectMetadata)) { - this.logger.log( - `Object metadata not found for rich text field ${richTextField.name} in workspace ${workspaceId}`, - ); - continue; - } - - const repository = - await this.twentyORMGlobalManager.getRepositoryForWorkspace( - workspaceId, - objectMetadata.nameSingular, - false, - ); - - const records = await repository.find(); - - this.logger.log(`Generating markdown for ${records.length} records`); - - for (const record of records) { - const recordRichTextField = record[richTextField.name]; - const blocknoteValue = recordRichTextField.blocknote; - const markdown = blocknoteValue - ? await serverBlockNoteEditor.blocksToMarkdownLossy( - JSON.parse(blocknoteValue), - ) - : null; - - await repository.update(record.id, { - [`${richTextField.name}Markdown`]: markdown, - }); - } - } - - workspaceIterator++; - this.logger.log( - chalk.green(`Command completed for workspace ${workspaceId}`), - ); - } catch (error) { - this.logger.error( - chalk.red(`Error in workspace ${workspaceId}: ${error.message}`), - ); - workspaceIterator++; - } - } - - this.logger.log(chalk.green('Command completed!')); - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.command.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.command.ts deleted file mode 100644 index 828762b8f922..000000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.command.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { InjectRepository } from '@nestjs/typeorm'; - -import { Command } from 'nest-commander'; -import { Repository } from 'typeorm'; - -import { ActiveWorkspacesCommandRunner } from 'src/database/commands/active-workspaces.command'; -import { BaseCommandOptions } from 'src/database/commands/base.command'; -import { MigrateRichTextFieldCommand } from 'src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; - -@Command({ - name: 'upgrade-0.41', - description: 'Upgrade to 0.41', -}) -export class UpgradeTo0_41Command extends ActiveWorkspacesCommandRunner { - constructor( - @InjectRepository(Workspace, 'core') - protected readonly workspaceRepository: Repository, - private readonly migrateRichTextFieldCommand: MigrateRichTextFieldCommand, - ) { - super(workspaceRepository); - } - - async executeActiveWorkspacesCommand( - passedParam: string[], - options: BaseCommandOptions, - workspaceIds: string[], - ): Promise { - this.logger.log('Running command to upgrade to 0.41'); - - await this.migrateRichTextFieldCommand.executeActiveWorkspacesCommand( - passedParam, - options, - workspaceIds, - ); - } -} diff --git a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.module.ts b/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.module.ts deleted file mode 100644 index 4a95e6214fcc..000000000000 --- a/packages/twenty-server/src/database/commands/upgrade-version/0-41/0-41-upgrade-version.module.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; - -import { MigrateRichTextFieldCommand } from 'src/database/commands/upgrade-version/0-41/0-41-migrate-rich-text-field.command'; -import { UpgradeTo0_41Command } from 'src/database/commands/upgrade-version/0-41/0-41-upgrade-version.command'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; -import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; -import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; -import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/workspace-metadata-version/workspace-metadata-version.module'; -import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module'; -import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module'; - -@Module({ - imports: [ - TypeOrmModule.forFeature([Workspace], 'core'), - TypeOrmModule.forFeature( - [ObjectMetadataEntity, FieldMetadataEntity], - 'metadata', - ), - WorkspaceMigrationRunnerModule, - WorkspaceMigrationModule, - WorkspaceMetadataVersionModule, - ], - providers: [UpgradeTo0_41Command, MigrateRichTextFieldCommand], -}) -export class UpgradeTo0_41CommandModule {} From 513a6236554571109c4b21e555dc8f89aecc0462 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Fri, 10 Jan 2025 14:06:14 +0100 Subject: [PATCH 23/29] Fix import --- .../field-metadata/composite-types/rich-text.composite-type.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts index d2f58f89d24b..c39efacdbb20 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts @@ -1,9 +1,8 @@ +import { FieldMetadataType } from 'twenty-shared'; import { z } from 'zod'; import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface'; -import { FieldMetadataType } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; - export const richTextCompositeType: CompositeType = { type: FieldMetadataType.RICH_TEXT, properties: [ From fabfd834b171295b8d37167367a690130a31e1c6 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Sat, 11 Jan 2025 01:34:36 +0100 Subject: [PATCH 24/29] Fix command menu note and task search --- packages/twenty-front/src/generated/graphql.tsx | 4 ++-- .../hooks/useCommandMenuCommands.tsx | 12 ++++++++++-- .../graphql/types/RecordGqlOperationFilter.ts | 10 ++++++++++ .../graphql-types/input/rich-text.input-type.ts | 16 ++++++++++++++++ .../services/type-mapper.service.ts | 2 ++ ...get-subfields-for-aggregate-operation.util.ts | 2 ++ 6 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/graphql-types/input/rich-text.input-type.ts diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 96ba375994cc..d5b0d74b2482 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -1,5 +1,5 @@ -import * as Apollo from '@apollo/client'; import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; @@ -379,8 +379,8 @@ export enum FieldMetadataType { Rating = 'RATING', RawJson = 'RAW_JSON', Relation = 'RELATION', - RichTextDeprecated = 'RICH_TEXT_DEPRECATED', RichText = 'RICH_TEXT', + RichTextDeprecated = 'RICH_TEXT_DEPRECATED', Select = 'SELECT', Text = 'TEXT', TsVector = 'TS_VECTOR', diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCommands.tsx b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCommands.tsx index 6504bb5c256d..a3b8ee9fb982 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCommands.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCommands.tsx @@ -146,7 +146,11 @@ export const useCommandMenuCommands = () => { filter: deferredCommandMenuSearch ? makeOrFilterVariables([ { title: { ilike: `%${deferredCommandMenuSearch}%` } }, - { body: { ilike: `%${deferredCommandMenuSearch}%` } }, + { + body: { + blocknote: { ilike: `%${deferredCommandMenuSearch}%` }, + }, + }, ]) : undefined, limit: 3, @@ -158,7 +162,11 @@ export const useCommandMenuCommands = () => { filter: deferredCommandMenuSearch ? makeOrFilterVariables([ { title: { ilike: `%${deferredCommandMenuSearch}%` } }, - { body: { ilike: `%${deferredCommandMenuSearch}%` } }, + { + body: { + blocknote: { ilike: `%${deferredCommandMenuSearch}%` }, + }, + }, ]) : undefined, limit: 3, diff --git a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationFilter.ts b/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationFilter.ts index 1d4fb29bc991..1671fea79414 100644 --- a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationFilter.ts +++ b/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationFilter.ts @@ -129,6 +129,15 @@ export type RawJsonFilter = { is?: IsFilter; }; +export type RichTextLeafFilter = { + ilike?: string; +}; + +export type RichTextFilter = { + blocknote?: RichTextLeafFilter; + markdown?: RichTextLeafFilter; +}; + export type LeafFilter = | UUIDFilter | StringFilter @@ -144,6 +153,7 @@ export type LeafFilter = | PhonesFilter | ArrayFilter | RawJsonFilter + | RichTextFilter | undefined; export type AndObjectRecordFilter = { diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/graphql-types/input/rich-text.input-type.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/graphql-types/input/rich-text.input-type.ts new file mode 100644 index 000000000000..f3a8cd078404 --- /dev/null +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/graphql-types/input/rich-text.input-type.ts @@ -0,0 +1,16 @@ +import { GraphQLInputObjectType, GraphQLString } from 'graphql'; + +const richTextLeafFilter = new GraphQLInputObjectType({ + name: 'RichTextLeafFilter', + fields: { + ilike: { type: GraphQLString }, + }, +}); + +export const RichTextFilterType = new GraphQLInputObjectType({ + name: 'RichTextFilter', + fields: { + blocknote: { type: richTextLeafFilter }, + markdown: { type: richTextLeafFilter }, + }, +}); diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts index e960d438c0bc..d85d4c4fe61b 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts @@ -29,6 +29,7 @@ import { } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input'; import { IDFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/id-filter.input-type'; import { MultiSelectFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/multi-select-filter.input-type'; +import { RichTextFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/rich-text.input-type'; import { SelectFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/select-filter.input-type'; import { BigFloatScalarType, @@ -116,6 +117,7 @@ export class TypeMapperService { [FieldMetadataType.POSITION, FloatFilterType], [FieldMetadataType.RAW_JSON, RawJsonFilterType], [FieldMetadataType.RICH_TEXT_DEPRECATED, StringFilterType], + [FieldMetadataType.RICH_TEXT, RichTextFilterType], [FieldMetadataType.ARRAY, ArrayFilterType], [FieldMetadataType.MULTI_SELECT, MultiSelectFilterType], [FieldMetadataType.SELECT, SelectFilterType], diff --git a/packages/twenty-server/src/engine/twenty-orm/utils/get-subfields-for-aggregate-operation.util.ts b/packages/twenty-server/src/engine/twenty-orm/utils/get-subfields-for-aggregate-operation.util.ts index a33a96afdd05..ffdd490fd602 100644 --- a/packages/twenty-server/src/engine/twenty-orm/utils/get-subfields-for-aggregate-operation.util.ts +++ b/packages/twenty-server/src/engine/twenty-orm/utils/get-subfields-for-aggregate-operation.util.ts @@ -36,6 +36,8 @@ export const getSubfieldsForAggregateOperation = ( 'primaryPhoneCountryCode', 'primaryPhoneCallingCode', ]; + case FieldMetadataType.RICH_TEXT: + return ['blocknote', 'markdown']; default: throw new Error(`Unsupported composite field type: ${fieldType}`); } From 7985753d44371c155b58f368545872ceb06e817e Mon Sep 17 00:00:00 2001 From: ad-elias Date: Sat, 11 Jan 2025 20:50:32 +0100 Subject: [PATCH 25/29] Rename new rich text field to RICH_TEXT_V2 --- .../src/generated-metadata/graphql.ts | 2 +- .../twenty-front/src/generated/graphql.tsx | 4 +- .../utils/mapFieldMetadataToGraphQLQuery.ts | 4 +- .../graphql/types/RecordGqlOperationFilter.ts | 10 ++-- .../record-field/components/FieldDisplay.tsx | 8 +-- .../record-field/hooks/usePersistField.ts | 12 ++-- .../RichTextDeprecatedFieldDisplay.tsx | 12 ---- .../components/RichTextFieldDisplay.tsx | 6 +- .../components/RichTextV2FieldDisplay.tsx | 16 +++++ .../useRichTextDeprecatedFieldDisplay.ts | 35 ----------- .../meta-types/hooks/useRichTextField.ts | 32 ++++------ .../hooks/useRichTextFieldDisplay.ts | 6 +- ...precatedField.ts => useRichTextV2Field.ts} | 44 +++++++------- .../hooks/useRichTextV2FieldDisplay.ts | 32 ++++++++++ .../input/components/RichTextFieldInput.tsx | 27 ++++----- .../record-field/types/FieldMetadata.ts | 12 ++-- .../types/guards/assertFieldMetadata.ts | 58 +++++++++---------- .../types/guards/isFieldRichTextDeprecated.ts | 12 ---- .../guards/isFieldRichTextDeprecatedValue.ts | 13 ----- .../types/guards/isFieldRichTextV2.ts | 9 +++ .../types/guards/isFieldRichTextValue.ts | 10 ++-- .../types/guards/isFieldRichTextValueV2.ts | 12 ++++ .../__tests__/isFieldValueReadOnly.test.ts | 6 +- .../record-field/utils/isFieldValueEmpty.ts | 10 ++-- .../utils/isFieldValueReadOnly.ts | 7 ++- .../utils/isRecordMatchingFilter.ts | 2 +- .../record-show/components/CardComponents.tsx | 2 +- .../hooks/useRecordShowContainerTabs.ts | 8 +-- .../record-show/types/CardType.ts | 2 +- .../constants/CompositeFieldImportLabels.ts | 6 +- .../hooks/useBuildAvailableFieldsForImport.ts | 4 +- .../buildRecordFromImportedStructuredRow.ts | 8 +-- .../utils/generateEmptyFieldValue.ts | 6 +- .../utils/isFieldCellSupported.ts | 2 +- .../SettingsCompositeFieldTypeConfigs.ts | 6 +- .../SettingsNonCompositeFieldTypeConfigs.ts | 6 +- .../data-model/types/CompositeFieldType.ts | 2 +- .../SettingsObjectNewFieldSelect.tsx | 2 +- .../constants/DefaultIconsByFieldType.ts | 2 +- .../generated/mock-metadata-query-result.ts | 4 +- .../__mocks__/object-metadata-item.mock.ts | 8 +-- .../factories/query-runner-args.factory.ts | 22 +++---- .../input/rich-text.input-type.ts | 12 ++-- .../services/type-mapper.service.ts | 10 ++-- ...p-field-metadata-to-graphql-query.utils.ts | 4 +- .../open-api/utils/components.utils.ts | 4 +- .../field-metadata/composite-types/index.ts | 4 +- .../rich-text.composite-type.ts | 8 +-- .../dtos/default-value.input.ts | 4 +- .../field-metadata-default-value.interface.ts | 4 +- .../utils/generate-default-value.ts | 2 +- .../is-composite-field-metadata-type.util.ts | 4 +- .../validate-default-value-for-type.util.ts | 6 +- .../fieldMetadataTypesToTextColumnType.ts | 2 +- .../composite-column-action.factory.ts | 2 +- ...field-metadata-type-to-column-type.util.ts | 2 +- .../utils/is-text-column-type.util.ts | 2 +- .../workspace-migration.factory.ts | 7 +-- .../metadata-seeds/pets-metadata-seeds.ts | 2 +- ...-subfields-for-aggregate-operation.util.ts | 2 +- .../utils/is-searchable-field.util.ts | 2 +- .../standard-objects/note.workspace-entity.ts | 8 +-- .../standard-objects/task.workspace-entity.ts | 8 +-- .../src/types/FieldMetadataType.ts | 2 +- .../src/utils/computeInputFields.ts | 8 +-- 65 files changed, 290 insertions(+), 310 deletions(-) delete mode 100644 packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextDeprecatedFieldDisplay.tsx create mode 100644 packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextV2FieldDisplay.tsx delete mode 100644 packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextDeprecatedFieldDisplay.ts rename packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/{useRichTextDeprecatedField.ts => useRichTextV2Field.ts} (60%) create mode 100644 packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextV2FieldDisplay.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextDeprecated.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextDeprecatedValue.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextV2.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValueV2.ts diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 6f4d5515979a..6920295baae6 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -447,8 +447,8 @@ export enum FieldMetadataType { Rating = 'RATING', RawJson = 'RAW_JSON', Relation = 'RELATION', - RichTextDeprecated = 'RICH_TEXT_DEPRECATED', RichText = 'RICH_TEXT', + RichTextV2 = 'RICH_TEXT_V2', Select = 'SELECT', Text = 'TEXT', TsVector = 'TS_VECTOR', diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index d5b0d74b2482..abf055e18143 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -1,5 +1,5 @@ -import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; +import { gql } from '@apollo/client'; export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; @@ -379,8 +379,8 @@ export enum FieldMetadataType { Rating = 'RATING', RawJson = 'RAW_JSON', Relation = 'RELATION', + RichTextV2 = 'RICH_TEXT_V2', RichText = 'RICH_TEXT', - RichTextDeprecated = 'RICH_TEXT_DEPRECATED', Select = 'SELECT', Text = 'TEXT', TsVector = 'TS_VECTOR', diff --git a/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts b/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts index dca7976cb624..3301a9038282 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/mapFieldMetadataToGraphQLQuery.ts @@ -35,7 +35,7 @@ export const mapFieldMetadataToGraphQLQuery = ({ FieldMetadataType.MultiSelect, FieldMetadataType.Position, FieldMetadataType.RawJson, - FieldMetadataType.RichTextDeprecated, + FieldMetadataType.RichText, FieldMetadataType.Array, ].includes(fieldType); @@ -162,7 +162,7 @@ ${mapObjectMetadataToGraphQLQuery({ }`; } - if (fieldType === FieldMetadataType.RichText) { + if (fieldType === FieldMetadataType.RichTextV2) { return `${field.name} { blocknote diff --git a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationFilter.ts b/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationFilter.ts index 1671fea79414..d1fa4826869d 100644 --- a/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationFilter.ts +++ b/packages/twenty-front/src/modules/object-record/graphql/types/RecordGqlOperationFilter.ts @@ -129,13 +129,13 @@ export type RawJsonFilter = { is?: IsFilter; }; -export type RichTextLeafFilter = { +export type RichTextV2LeafFilter = { ilike?: string; }; -export type RichTextFilter = { - blocknote?: RichTextLeafFilter; - markdown?: RichTextLeafFilter; +export type RichTextV2Filter = { + blocknote?: RichTextV2LeafFilter; + markdown?: RichTextV2LeafFilter; }; export type LeafFilter = @@ -153,7 +153,7 @@ export type LeafFilter = | PhonesFilter | ArrayFilter | RawJsonFilter - | RichTextFilter + | RichTextV2Filter | undefined; export type AndObjectRecordFilter = { diff --git a/packages/twenty-front/src/modules/object-record/record-field/components/FieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/components/FieldDisplay.tsx index a0edd99dd567..0b0c5d287be8 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/components/FieldDisplay.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/components/FieldDisplay.tsx @@ -8,8 +8,8 @@ import { LinksFieldDisplay } from '@/object-record/record-field/meta-types/displ import { PhonesFieldDisplay } from '@/object-record/record-field/meta-types/display/components/PhonesFieldDisplay'; import { RatingFieldDisplay } from '@/object-record/record-field/meta-types/display/components/RatingFieldDisplay'; import { RelationFromManyFieldDisplay } from '@/object-record/record-field/meta-types/display/components/RelationFromManyFieldDisplay'; -import { RichTextDeprecatedFieldDisplay } from '@/object-record/record-field/meta-types/display/components/RichTextDeprecatedFieldDisplay'; import { RichTextFieldDisplay } from '@/object-record/record-field/meta-types/display/components/RichTextFieldDisplay'; +import { RichTextV2FieldDisplay } from '@/object-record/record-field/meta-types/display/components/RichTextV2FieldDisplay'; import { isFieldIdentifierDisplay } from '@/object-record/record-field/meta-types/display/utils/isFieldIdentifierDisplay'; import { isFieldActor } from '@/object-record/record-field/types/guards/isFieldActor'; import { isFieldArray } from '@/object-record/record-field/types/guards/isFieldArray'; @@ -21,7 +21,7 @@ import { isFieldRating } from '@/object-record/record-field/types/guards/isField import { isFieldRelationFromManyObjects } from '@/object-record/record-field/types/guards/isFieldRelationFromManyObjects'; import { isFieldRelationToOneObject } from '@/object-record/record-field/types/guards/isFieldRelationToOneObject'; import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; -import { isFieldRichTextDeprecated } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecated'; +import { isFieldRichTextV2 } from '@/object-record/record-field/types/guards/isFieldRichTextV2'; import { FieldContext } from '../contexts/FieldContext'; import { AddressFieldDisplay } from '../meta-types/display/components/AddressFieldDisplay'; import { ChipFieldDisplay } from '../meta-types/display/components/ChipFieldDisplay'; @@ -90,10 +90,10 @@ export const FieldDisplay = () => { ) : isFieldRating(fieldDefinition) ? ( - ) : isFieldRichTextDeprecated(fieldDefinition) ? ( - ) : isFieldRichText(fieldDefinition) ? ( + ) : isFieldRichTextV2(fieldDefinition) ? ( + ) : isFieldActor(fieldDefinition) ? ( ) : isFieldArray(fieldDefinition) ? ( diff --git a/packages/twenty-front/src/modules/object-record/record-field/hooks/usePersistField.ts b/packages/twenty-front/src/modules/object-record/record-field/hooks/usePersistField.ts index 51ba775af5d2..2df2916301ac 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/hooks/usePersistField.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/hooks/usePersistField.ts @@ -28,8 +28,8 @@ import { RecordForSelect } from '@/object-record/relation-picker/types/RecordFor import { isFieldArray } from '@/object-record/record-field/types/guards/isFieldArray'; import { isFieldArrayValue } from '@/object-record/record-field/types/guards/isFieldArrayValue'; -import { isFieldRichTextDeprecated } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecated'; -import { isFieldRichTextDeprecatedValue } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecatedValue'; +import { isFieldRichTextV2 } from '@/object-record/record-field/types/guards/isFieldRichTextV2'; +import { isFieldRichTextV2Value } from '@/object-record/record-field/types/guards/isFieldRichTextValueV2'; import { FieldContext } from '../contexts/FieldContext'; import { isFieldBoolean } from '../types/guards/isFieldBoolean'; import { isFieldBooleanValue } from '../types/guards/isFieldBooleanValue'; @@ -113,9 +113,9 @@ export const usePersistField = () => { isFieldRawJson(fieldDefinition) && isFieldRawJsonValue(valueToPersist); - const fieldIsRichTextDeprecated = - isFieldRichTextDeprecated(fieldDefinition) && - isFieldRichTextDeprecatedValue(valueToPersist); + const fieldIsRichText = + isFieldRichTextV2(fieldDefinition) && + isFieldRichTextV2Value(valueToPersist); const fieldIsArray = isFieldArray(fieldDefinition) && isFieldArrayValue(valueToPersist); @@ -138,7 +138,7 @@ export const usePersistField = () => { fieldIsAddress || fieldIsRawJson || fieldIsArray || - fieldIsRichTextDeprecated; + fieldIsRichText; if (isValuePersistable) { const fieldName = fieldDefinition.metadata.fieldName; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextDeprecatedFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextDeprecatedFieldDisplay.tsx deleted file mode 100644 index a8de5e2635c1..000000000000 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextDeprecatedFieldDisplay.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { useRichTextDeprecatedFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useRichTextDeprecatedFieldDisplay'; -import { getFirstNonEmptyLineOfRichText } from '@/ui/input/editor/utils/getFirstNonEmptyLineOfRichText'; - -export const RichTextDeprecatedFieldDisplay = () => { - const { fieldValue } = useRichTextDeprecatedFieldDisplay(); - - return ( -
- {getFirstNonEmptyLineOfRichText(fieldValue)} -
- ); -}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextFieldDisplay.tsx index 9815602dede7..d163bc0134bd 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextFieldDisplay.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextFieldDisplay.tsx @@ -1,16 +1,12 @@ import { useRichTextFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay'; import { getFirstNonEmptyLineOfRichText } from '@/ui/input/editor/utils/getFirstNonEmptyLineOfRichText'; -import { PartialBlock } from '@blocknote/core'; -import { parseJson } from '~/utils/parseJson'; export const RichTextFieldDisplay = () => { const { fieldValue } = useRichTextFieldDisplay(); - const blocks = parseJson(fieldValue?.blocknote); - return (
- {getFirstNonEmptyLineOfRichText(blocks)} + {getFirstNonEmptyLineOfRichText(fieldValue)}
); }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextV2FieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextV2FieldDisplay.tsx new file mode 100644 index 000000000000..d04f739d985f --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RichTextV2FieldDisplay.tsx @@ -0,0 +1,16 @@ +import { useRichTextV2FieldDisplay } from '@/object-record/record-field/meta-types/hooks/useRichTextV2FieldDisplay'; +import { getFirstNonEmptyLineOfRichText } from '@/ui/input/editor/utils/getFirstNonEmptyLineOfRichText'; +import { PartialBlock } from '@blocknote/core'; +import { parseJson } from '~/utils/parseJson'; + +export const RichTextV2FieldDisplay = () => { + const { fieldValue } = useRichTextV2FieldDisplay(); + + const blocks = parseJson(fieldValue?.blocknote); + + return ( +
+ {getFirstNonEmptyLineOfRichText(blocks)} +
+ ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextDeprecatedFieldDisplay.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextDeprecatedFieldDisplay.ts deleted file mode 100644 index 0ae4910066d3..000000000000 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextDeprecatedFieldDisplay.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { useContext } from 'react'; - -import { useRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext'; - -import { FieldRichTextDeprecatedValue } from '@/object-record/record-field/types/FieldMetadata'; -import { assertFieldMetadata } from '@/object-record/record-field/types/guards/assertFieldMetadata'; -import { isFieldRichTextDeprecated } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecated'; -import { PartialBlock } from '@blocknote/core'; -import { FieldMetadataType } from '~/generated-metadata/graphql'; -import { parseJson } from '~/utils/parseJson'; -import { FieldContext } from '../../contexts/FieldContext'; - -export const useRichTextDeprecatedFieldDisplay = () => { - const { recordId, fieldDefinition, hotkeyScope } = useContext(FieldContext); - - assertFieldMetadata( - FieldMetadataType.RichTextDeprecated, - isFieldRichTextDeprecated, - fieldDefinition, - ); - - const fieldName = fieldDefinition.metadata.fieldName; - - const fieldValue = useRecordFieldValue< - FieldRichTextDeprecatedValue | undefined - >(recordId, fieldName); - - const fieldValueParsed = parseJson(fieldValue); - - return { - fieldDefinition, - fieldValue: fieldValueParsed, - hotkeyScope, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextField.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextField.ts index 557b9b93517c..cad7bc3bb53c 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextField.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextField.ts @@ -2,15 +2,12 @@ import { useContext } from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput'; -import { - FieldRichTextDeprecatedValue, - FieldRichTextValue, -} from '@/object-record/record-field/types/FieldMetadata'; +import { FieldRichTextValue } from '@/object-record/record-field/types/FieldMetadata'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { usePersistField } from '@/object-record/record-field/hooks/usePersistField'; -import { isFieldRichTextDeprecated } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecated'; +import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; import { isFieldRichTextValue } from '@/object-record/record-field/types/guards/isFieldRichTextValue'; import { PartialBlock } from '@blocknote/core'; import { isNonEmptyString } from '@sniptt/guards'; @@ -22,28 +19,23 @@ export const useRichTextField = () => { useContext(FieldContext); assertFieldMetadata( - FieldMetadataType.RichTextDeprecated, - isFieldRichTextDeprecated, + FieldMetadataType.RichText, + isFieldRichText, fieldDefinition, ); const fieldName = fieldDefinition.metadata.fieldName; - const [fieldValue, setFieldValue] = - useRecoilState( - recordStoreFamilySelector({ - recordId, - fieldName: fieldName, - }), - ); - const fieldRichTextValue = isFieldRichTextValue(fieldValue) - ? fieldValue - : ({ blocknote: null, markdown: null } as FieldRichTextValue); + const [fieldValue, setFieldValue] = useRecoilState( + recordStoreFamilySelector({ + recordId, + fieldName: fieldName, + }), + ); + const fieldRichTextValue = isFieldRichTextValue(fieldValue) ? fieldValue : ''; const { setDraftValue, getDraftValueSelector } = - useRecordFieldInput( - `${recordId}-${fieldName}`, - ); + useRecordFieldInput(`${recordId}-${fieldName}`); const draftValue = useRecoilValue(getDraftValueSelector()); diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts index ff10926fd868..bc933a92b949 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextFieldDisplay.ts @@ -5,7 +5,9 @@ import { useRecordFieldValue } from '@/object-record/record-store/contexts/Recor import { FieldRichTextValue } from '@/object-record/record-field/types/FieldMetadata'; import { assertFieldMetadata } from '@/object-record/record-field/types/guards/assertFieldMetadata'; import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; +import { PartialBlock } from '@blocknote/core'; import { FieldMetadataType } from '~/generated-metadata/graphql'; +import { parseJson } from '~/utils/parseJson'; import { FieldContext } from '../../contexts/FieldContext'; export const useRichTextFieldDisplay = () => { @@ -24,9 +26,11 @@ export const useRichTextFieldDisplay = () => { fieldName, ); + const fieldValueParsed = parseJson(fieldValue); + return { fieldDefinition, - fieldValue, + fieldValue: fieldValueParsed, hotkeyScope, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextDeprecatedField.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextV2Field.ts similarity index 60% rename from packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextDeprecatedField.ts rename to packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextV2Field.ts index f54c3c970cd1..3fa8cedf5472 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextDeprecatedField.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextV2Field.ts @@ -2,47 +2,45 @@ import { useContext } from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput'; -import { FieldRichTextDeprecatedValue } from '@/object-record/record-field/types/FieldMetadata'; +import { + FieldRichTextV2Value, + FieldRichTextValue, +} from '@/object-record/record-field/types/FieldMetadata'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { usePersistField } from '@/object-record/record-field/hooks/usePersistField'; -import { isFieldRichTextDeprecated } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecated'; -import { isFieldRichTextDeprecatedValue } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecatedValue'; +import { isFieldRichTextV2 } from '@/object-record/record-field/types/guards/isFieldRichTextV2'; +import { isFieldRichTextV2Value } from '@/object-record/record-field/types/guards/isFieldRichTextValueV2'; import { PartialBlock } from '@blocknote/core'; import { isNonEmptyString } from '@sniptt/guards'; import { FieldContext } from '../../contexts/FieldContext'; import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; -export const useRichTextDeprecatedField = () => { +export const useRichTextV2Field = () => { const { recordId, fieldDefinition, hotkeyScope, maxWidth } = useContext(FieldContext); assertFieldMetadata( - FieldMetadataType.RichTextDeprecated, - isFieldRichTextDeprecated, + FieldMetadataType.RichTextV2, + isFieldRichTextV2, fieldDefinition, ); const fieldName = fieldDefinition.metadata.fieldName; - const [fieldValue, setFieldValue] = - useRecoilState( - recordStoreFamilySelector({ - recordId, - fieldName: fieldName, - }), - ); - const fieldRichTextDeprecatedValue = isFieldRichTextDeprecatedValue( - fieldValue, - ) + const [fieldValue, setFieldValue] = useRecoilState( + recordStoreFamilySelector({ + recordId, + fieldName: fieldName, + }), + ); + const fieldRichTextV2Value = isFieldRichTextV2Value(fieldValue) ? fieldValue - : ''; + : ({ blocknote: null, markdown: null } as FieldRichTextV2Value); const { setDraftValue, getDraftValueSelector } = - useRecordFieldInput( - `${recordId}-${fieldName}`, - ); + useRecordFieldInput(`${recordId}-${fieldName}`); const draftValue = useRecoilValue(getDraftValueSelector()); @@ -52,7 +50,7 @@ export const useRichTextDeprecatedField = () => { const persistField = usePersistField(); - const persistRichTextDeprecatedField = (nextValue: PartialBlock[]) => { + const persistRichTextField = (nextValue: PartialBlock[]) => { if (!nextValue) { persistField(null); } else { @@ -67,9 +65,9 @@ export const useRichTextDeprecatedField = () => { setDraftValue, maxWidth, fieldDefinition, - fieldValue: fieldRichTextDeprecatedValue, + fieldValue: fieldRichTextV2Value, setFieldValue, hotkeyScope, - persistRichTextDeprecatedField, + persistRichTextField, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextV2FieldDisplay.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextV2FieldDisplay.ts new file mode 100644 index 000000000000..8b33e1c639c2 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRichTextV2FieldDisplay.ts @@ -0,0 +1,32 @@ +import { useContext } from 'react'; + +import { useRecordFieldValue } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext'; + +import { FieldRichTextV2Value } from '@/object-record/record-field/types/FieldMetadata'; +import { assertFieldMetadata } from '@/object-record/record-field/types/guards/assertFieldMetadata'; +import { isFieldRichTextV2 } from '@/object-record/record-field/types/guards/isFieldRichTextV2'; +import { FieldMetadataType } from '~/generated-metadata/graphql'; +import { FieldContext } from '../../contexts/FieldContext'; + +export const useRichTextV2FieldDisplay = () => { + const { recordId, fieldDefinition, hotkeyScope } = useContext(FieldContext); + + assertFieldMetadata( + FieldMetadataType.RichTextV2, + isFieldRichTextV2, + fieldDefinition, + ); + + const fieldName = fieldDefinition.metadata.fieldName; + + const fieldValue = useRecordFieldValue( + recordId, + fieldName, + ); + + return { + fieldDefinition, + fieldValue, + hotkeyScope, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RichTextFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RichTextFieldInput.tsx index 7ae1dece6397..718ae9e5af8d 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RichTextFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RichTextFieldInput.tsx @@ -1,6 +1,6 @@ import { BLOCK_SCHEMA } from '@/activities/blocks/constants/Schema'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; -import { useRichTextDeprecatedField } from '@/object-record/record-field/meta-types/hooks/useRichTextDeprecatedField'; +import { useRichTextField } from '@/object-record/record-field/meta-types/hooks/useRichTextField'; import { FieldInputClickOutsideEvent } from '@/object-record/record-field/meta-types/input/components/DateTimeFieldInput'; import { useRegisterInputEvents } from '@/object-record/record-field/meta-types/input/hooks/useRegisterInputEvents'; import { BlockEditor } from '@/ui/input/editor/components/BlockEditor'; @@ -11,28 +11,24 @@ import styled from '@emotion/styled'; import { useContext, useRef } from 'react'; -const StyledRichTextDeprecatedContainer = styled.div` +const StyledRichTextContainer = styled.div` height: 400px; width: 500px; overflow: auto; `; -export type RichTextDeprecatedFieldInputProps = { +export type RichTextFieldInputProps = { onClickOutside?: FieldInputClickOutsideEvent; }; -export const RichTextDeprecatedFieldInput = ({ +export const RichTextFieldInput = ({ onClickOutside, -}: RichTextDeprecatedFieldInputProps) => { +}: RichTextFieldInputProps) => { const containerRef = useRef(null); const { recordId } = useContext(FieldContext); - const { - draftValue, - hotkeyScope, - persistRichTextDeprecatedField, - fieldDefinition, - } = useRichTextDeprecatedField(); + const { draftValue, hotkeyScope, persistRichTextField, fieldDefinition } = + useRichTextField(); const editor = useCreateBlockNote({ initialContent: draftValue, @@ -41,10 +37,7 @@ export const RichTextDeprecatedFieldInput = ({ }); const handleClickOutside = (event: MouseEvent | TouchEvent) => { - onClickOutside?.( - () => persistRichTextDeprecatedField(editor.document), - event, - ); + onClickOutside?.(() => persistRichTextField(editor.document), event); }; useRegisterInputEvents({ @@ -55,12 +48,12 @@ export const RichTextDeprecatedFieldInput = ({ }); return ( - + - + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts b/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts index 2a266aabd328..767b3bedf917 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts @@ -127,13 +127,13 @@ export type FieldRawJsonMetadata = { settings?: null; }; -export type FieldRichTextMetadata = { +export type FieldRichTextV2Metadata = { objectMetadataNameSingular?: string; fieldName: string; settings?: null; }; -export type FieldRichTextDeprecatedMetadata = { +export type FieldRichTextMetadata = { objectMetadataNameSingular?: string; fieldName: string; settings?: null; @@ -217,8 +217,8 @@ export type FieldMetadata = | FieldActorMetadata | FieldArrayMetadata | FieldTsVectorMetadata - | FieldRichTextMetadata - | FieldRichTextDeprecatedMetadata; + | FieldRichTextV2Metadata + | FieldRichTextMetadata; export type FieldTextValue = string; export type FieldUUidValue = string; // TODO: can we replace with a template literal type, or maybe overkill ? @@ -270,12 +270,12 @@ export type FieldRelationValue< export type Json = ZodHelperLiteral | { [key: string]: Json } | Json[]; export type FieldJsonValue = Record | Json[] | null; -export type FieldRichTextValue = { +export type FieldRichTextV2Value = { blocknote: string | null; // TODO: Can these be null? markdown: string | null; }; -export type FieldRichTextDeprecatedValue = null | string; +export type FieldRichTextValue = null | string; export type FieldActorValue = { source: string; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts index 07eae77ed416..45367c966386 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/assertFieldMetadata.ts @@ -2,31 +2,31 @@ import { FieldMetadataType } from '~/generated-metadata/graphql'; import { FieldDefinition } from '../FieldDefinition'; import { - FieldActorMetadata, - FieldAddressMetadata, - FieldArrayMetadata, - FieldBooleanMetadata, - FieldCurrencyMetadata, - FieldDateMetadata, - FieldDateTimeMetadata, - FieldEmailMetadata, - FieldEmailsMetadata, - FieldFullNameMetadata, - FieldLinkMetadata, - FieldLinksMetadata, - FieldMetadata, - FieldMultiSelectMetadata, - FieldNumberMetadata, - FieldPhoneMetadata, - FieldPhonesMetadata, - FieldRatingMetadata, - FieldRawJsonMetadata, - FieldRelationMetadata, - FieldRichTextDeprecatedMetadata, - FieldRichTextMetadata, - FieldSelectMetadata, - FieldTextMetadata, - FieldUuidMetadata, + FieldActorMetadata, + FieldAddressMetadata, + FieldArrayMetadata, + FieldBooleanMetadata, + FieldCurrencyMetadata, + FieldDateMetadata, + FieldDateTimeMetadata, + FieldEmailMetadata, + FieldEmailsMetadata, + FieldFullNameMetadata, + FieldLinkMetadata, + FieldLinksMetadata, + FieldMetadata, + FieldMultiSelectMetadata, + FieldNumberMetadata, + FieldPhoneMetadata, + FieldPhonesMetadata, + FieldRatingMetadata, + FieldRawJsonMetadata, + FieldRelationMetadata, + FieldRichTextMetadata, + FieldRichTextV2Metadata, + FieldSelectMetadata, + FieldTextMetadata, + FieldUuidMetadata, } from '../FieldMetadata'; type AssertFieldMetadataFunction = < @@ -69,10 +69,10 @@ type AssertFieldMetadataFunction = < ? FieldAddressMetadata : E extends 'RAW_JSON' ? FieldRawJsonMetadata - : E extends 'RICH_TEXT' - ? FieldRichTextMetadata - : E extends 'RICH_TEXT_DEPRECATED' - ? FieldRichTextDeprecatedMetadata + : E extends 'RICH_TEXT_V2' + ? FieldRichTextV2Metadata + : E extends 'RICH_TEXT' + ? FieldRichTextMetadata : E extends 'ACTOR' ? FieldActorMetadata : E extends 'ARRAY' diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextDeprecated.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextDeprecated.ts deleted file mode 100644 index 785edba3945c..000000000000 --- a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextDeprecated.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { FieldMetadataType } from '~/generated-metadata/graphql'; - -import { FieldDefinition } from '../FieldDefinition'; -import { - FieldMetadata, - FieldRichTextDeprecatedMetadata, -} from '../FieldMetadata'; - -export const isFieldRichTextDeprecated = ( - field: Pick, 'type'>, -): field is FieldDefinition => - field.type === FieldMetadataType.RichTextDeprecated; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextDeprecatedValue.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextDeprecatedValue.ts deleted file mode 100644 index bd49b71c4f19..000000000000 --- a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextDeprecatedValue.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { z } from 'zod'; -import { FieldRichTextDeprecatedValue } from '../FieldMetadata'; - -export const richTextDeprecatedSchema: z.ZodType = - z.union([ - z.null(), // Exclude literal values other than null - z.string(), - ]); - -export const isFieldRichTextDeprecatedValue = ( - fieldValue: unknown, -): fieldValue is FieldRichTextDeprecatedValue => - richTextDeprecatedSchema.safeParse(fieldValue).success; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextV2.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextV2.ts new file mode 100644 index 000000000000..c887cc1173cf --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextV2.ts @@ -0,0 +1,9 @@ +import { FieldMetadataType } from '~/generated-metadata/graphql'; + +import { FieldDefinition } from '../FieldDefinition'; +import { FieldMetadata, FieldRichTextV2Metadata } from '../FieldMetadata'; + +export const isFieldRichTextV2 = ( + field: Pick, 'type'>, +): field is FieldDefinition => + field.type === FieldMetadataType.RichTextV2; diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValue.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValue.ts index d37e1e0678a9..d2fc793fd6d2 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValue.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValue.ts @@ -1,10 +1,10 @@ -import { FieldRichTextValue } from '@/object-record/record-field/types/FieldMetadata'; import { z } from 'zod'; +import { FieldRichTextValue } from '../FieldMetadata'; -export const richTextSchema: z.ZodType = z.object({ - blocknote: z.string().nullable(), - markdown: z.string().nullable(), -}); +export const richTextSchema: z.ZodType = z.union([ + z.null(), + z.string(), +]); export const isFieldRichTextValue = ( fieldValue: unknown, diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValueV2.ts b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValueV2.ts new file mode 100644 index 000000000000..b163d008b99d --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/types/guards/isFieldRichTextValueV2.ts @@ -0,0 +1,12 @@ +import { FieldRichTextV2Value } from '@/object-record/record-field/types/FieldMetadata'; +import { z } from 'zod'; + +export const richTextV2Schema: z.ZodType = z.object({ + blocknote: z.string().nullable(), + markdown: z.string().nullable(), +}); + +export const isFieldRichTextV2Value = ( + fieldValue: unknown, +): fieldValue is FieldRichTextV2Value => + richTextV2Schema.safeParse(fieldValue).success; diff --git a/packages/twenty-front/src/modules/object-record/record-field/utils/__tests__/isFieldValueReadOnly.test.ts b/packages/twenty-front/src/modules/object-record/record-field/utils/__tests__/isFieldValueReadOnly.test.ts index cf9534a459ab..a5dff94f7a97 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/utils/__tests__/isFieldValueReadOnly.test.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/utils/__tests__/isFieldValueReadOnly.test.ts @@ -89,15 +89,15 @@ describe('isFieldValueReadOnly', () => { expect(result).toBe(true); }); - it('should return true if fieldType is FieldMetadataType.RichTextDeprecated', () => { + it('should return true if fieldType is FieldMetadataType.RichText', () => { const result = isFieldValueReadOnly({ - fieldType: FieldMetadataType.RichTextDeprecated, + fieldType: FieldMetadataType.RichText, }); expect(result).toBe(true); }); - it('should return false if fieldType is not FieldMetadataType.Actor or FieldMetadataType.RichTextDeprecated', () => { + it('should return false if fieldType is not FieldMetadataType.Actor or FieldMetadataType.RichText', () => { const result = isFieldValueReadOnly({ fieldType: FieldMetadataType.Text, }); diff --git a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueEmpty.ts b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueEmpty.ts index c1c1159c400f..d2a01d55c3c7 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueEmpty.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueEmpty.ts @@ -29,8 +29,8 @@ import { isFieldRating } from '@/object-record/record-field/types/guards/isField import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson'; import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation'; import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; -import { isFieldRichTextDeprecated } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecated'; -import { isFieldRichTextValue } from '@/object-record/record-field/types/guards/isFieldRichTextValue'; +import { isFieldRichTextV2 } from '@/object-record/record-field/types/guards/isFieldRichTextV2'; +import { isFieldRichTextV2Value } from '@/object-record/record-field/types/guards/isFieldRichTextValueV2'; import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect'; import { isFieldSelectValue } from '@/object-record/record-field/types/guards/isFieldSelectValue'; import { isFieldText } from '@/object-record/record-field/types/guards/isFieldText'; @@ -61,7 +61,7 @@ export const isFieldValueEmpty = ({ isFieldRating(fieldDefinition) || isFieldBoolean(fieldDefinition) || isFieldRawJson(fieldDefinition) || - isFieldRichTextDeprecated(fieldDefinition) || + isFieldRichText(fieldDefinition) || isFieldPosition(fieldDefinition) ) { return isValueEmpty(fieldValue); @@ -144,9 +144,9 @@ export const isFieldValueEmpty = ({ return false; } - if (isFieldRichText(fieldDefinition)) { + if (isFieldRichTextV2(fieldDefinition)) { return ( - !isFieldRichTextValue(fieldValue) || + !isFieldRichTextV2Value(fieldValue) || (isValueEmpty(fieldValue?.blocknote) && isValueEmpty(fieldValue?.markdown)) ); diff --git a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts index 0ba6c3e8d5af..bf85caa35a24 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/utils/isFieldValueReadOnly.ts @@ -1,7 +1,9 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { isWorkflowSubObjectMetadata } from '@/object-metadata/utils/isWorkflowSubObjectMetadata'; import { isFieldActor } from '@/object-record/record-field/types/guards/isFieldActor'; -import { isFieldRichTextDeprecated } from '@/object-record/record-field/types/guards/isFieldRichTextDeprecated'; +import { isFieldRichText } from '@/object-record/record-field/types/guards/isFieldRichText'; + +import { isFieldRichTextV2 } from '@/object-record/record-field/types/guards/isFieldRichTextV2'; import { isDefined } from 'twenty-ui'; import { FieldMetadataType } from '~/generated-metadata/graphql'; @@ -46,7 +48,8 @@ export const isFieldValueReadOnly = ({ if ( isDefined(fieldType) && (isFieldActor({ type: fieldType }) || - isFieldRichTextDeprecated({ type: fieldType })) + isFieldRichText({ type: fieldType }) || + isFieldRichTextV2({ type: fieldType })) ) { return true; } diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts index dad9f432bdca..88b3780b8c7e 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/isRecordMatchingFilter.ts @@ -177,7 +177,7 @@ export const isRecordMatchingFilter = ({ value: record[filterKey], }); } - case FieldMetadataType.RichTextDeprecated: { + case FieldMetadataType.RichText: { // TODO: Implement a better rich text filter once it becomes a composite field // See this issue for more context: https://github.com/twentyhq/twenty/issues/7613#issuecomment-2408944585 // This should be tackled in Q4'24 diff --git a/packages/twenty-front/src/modules/object-record/record-show/components/CardComponents.tsx b/packages/twenty-front/src/modules/object-record/record-show/components/CardComponents.tsx index 2220d2cb8fcf..20a5a02390d3 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/components/CardComponents.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/components/CardComponents.tsx @@ -53,7 +53,7 @@ export const CardComponents: Record = { ), - [CardType.RichTextCard]: ({ targetableObject }) => ( + [CardType.RichTextV2Card]: ({ targetableObject }) => ( ), diff --git a/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowContainerTabs.ts b/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowContainerTabs.ts index d6a7ce756036..d58d80be8c91 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowContainerTabs.ts +++ b/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowContainerTabs.ts @@ -36,11 +36,11 @@ export const useRecordShowContainerTabs = ( > = { [CoreObjectNameSingular.Note]: { tabs: { - richText: { + richTextV2: { title: 'Note', position: 0, Icon: IconNotes, - cards: [{ type: CardType.RichTextCard }], + cards: [{ type: CardType.RichTextV2Card }], hide: { ifMobile: false, ifDesktop: false, @@ -56,11 +56,11 @@ export const useRecordShowContainerTabs = ( }, [CoreObjectNameSingular.Task]: { tabs: { - richText: { + richTextV2: { title: 'Note', position: 0, Icon: IconNotes, - cards: [{ type: CardType.RichTextCard }], + cards: [{ type: CardType.RichTextV2Card }], hide: { ifMobile: false, ifDesktop: false, diff --git a/packages/twenty-front/src/modules/object-record/record-show/types/CardType.ts b/packages/twenty-front/src/modules/object-record/record-show/types/CardType.ts index 6a805d0af131..d053d89b9f53 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/types/CardType.ts +++ b/packages/twenty-front/src/modules/object-record/record-show/types/CardType.ts @@ -10,5 +10,5 @@ export enum CardType { WorkflowVersionCard = 'WorkflowVersionCard', WorkflowRunCard = 'WorkflowRunCard', WorkflowRunOutputCard = 'WorkflowRunOutputCard', - RichTextCard = 'RichTextCard', + RichTextV2Card = 'RichTextV2Card', } diff --git a/packages/twenty-front/src/modules/object-record/spreadsheet-import/constants/CompositeFieldImportLabels.ts b/packages/twenty-front/src/modules/object-record/spreadsheet-import/constants/CompositeFieldImportLabels.ts index 9769338f99be..fb04b12fcee3 100644 --- a/packages/twenty-front/src/modules/object-record/spreadsheet-import/constants/CompositeFieldImportLabels.ts +++ b/packages/twenty-front/src/modules/object-record/spreadsheet-import/constants/CompositeFieldImportLabels.ts @@ -5,7 +5,7 @@ import { FieldFullNameValue, FieldLinksValue, FieldPhonesValue, - FieldRichTextValue, + FieldRichTextV2Value, } from '@/object-record/record-field/types/FieldMetadata'; import { CompositeFieldLabels } from '@/object-record/spreadsheet-import/types/CompositeFieldLabels'; import { FieldMetadataType } from '~/generated-metadata/graphql'; @@ -40,10 +40,10 @@ export const COMPOSITE_FIELD_IMPORT_LABELS = { primaryPhoneCountryCodeLabel: 'Phone country code', primaryPhoneNumberLabel: 'Phone number', } satisfies Partial>, - [FieldMetadataType.RichText]: { + [FieldMetadataType.RichTextV2]: { blocknoteLabel: 'BlockNote', markdownLabel: 'Markdown', - } satisfies Partial>, + } satisfies Partial>, [FieldMetadataType.Actor]: { sourceLabel: 'Source', }, diff --git a/packages/twenty-front/src/modules/object-record/spreadsheet-import/hooks/useBuildAvailableFieldsForImport.ts b/packages/twenty-front/src/modules/object-record/spreadsheet-import/hooks/useBuildAvailableFieldsForImport.ts index 84eff136c46e..2bc020e0c78a 100644 --- a/packages/twenty-front/src/modules/object-record/spreadsheet-import/hooks/useBuildAvailableFieldsForImport.ts +++ b/packages/twenty-front/src/modules/object-record/spreadsheet-import/hooks/useBuildAvailableFieldsForImport.ts @@ -211,9 +211,9 @@ export const useBuildAvailableFieldsForImport = () => { ), }); }); - } else if (fieldMetadataItem.type === FieldMetadataType.RichText) { + } else if (fieldMetadataItem.type === FieldMetadataType.RichTextV2) { Object.entries( - COMPOSITE_FIELD_IMPORT_LABELS[FieldMetadataType.RichText], + COMPOSITE_FIELD_IMPORT_LABELS[FieldMetadataType.RichTextV2], ).forEach(([_, fieldLabel]) => { availableFieldsForImport.push({ icon: getIcon(fieldMetadataItem.icon), diff --git a/packages/twenty-front/src/modules/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow.ts b/packages/twenty-front/src/modules/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow.ts index 5aba8d3fe2a2..8c3ae0afd840 100644 --- a/packages/twenty-front/src/modules/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow.ts +++ b/packages/twenty-front/src/modules/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow.ts @@ -4,7 +4,7 @@ import { FieldEmailsValue, FieldLinksValue, FieldPhonesValue, - FieldRichTextValue, + FieldRichTextV2Value, } from '@/object-record/record-field/types/FieldMetadata'; import { COMPOSITE_FIELD_IMPORT_LABELS } from '@/object-record/spreadsheet-import/constants/CompositeFieldImportLabels'; import { ImportedStructuredRow } from '@/spreadsheet-import/types'; @@ -37,7 +37,7 @@ export const buildRecordFromImportedStructuredRow = ( LINKS: { primaryLinkLabelLabel, primaryLinkUrlLabel }, EMAILS: { primaryEmailLabel }, PHONES: { primaryPhoneNumberLabel, primaryPhoneCountryCodeLabel }, - RICH_TEXT: { blocknoteLabel, markdownLabel }, + RICH_TEXT_V2: { blocknoteLabel, markdownLabel }, } = COMPOSITE_FIELD_IMPORT_LABELS; for (const field of fields) { @@ -163,7 +163,7 @@ export const buildRecordFromImportedStructuredRow = ( } break; } - case FieldMetadataType.RichText: { + case FieldMetadataType.RichTextV2: { if ( isDefined( importedStructuredRow[`${blocknoteLabel} (${field.name})`] || @@ -179,7 +179,7 @@ export const buildRecordFromImportedStructuredRow = ( markdown: castToString( importedStructuredRow[`${markdownLabel} (${field.name})`], ), - } satisfies FieldRichTextValue; + } satisfies FieldRichTextV2Value; } break; } diff --git a/packages/twenty-front/src/modules/object-record/utils/generateEmptyFieldValue.ts b/packages/twenty-front/src/modules/object-record/utils/generateEmptyFieldValue.ts index 97a78b06d362..8f5fd425e42b 100644 --- a/packages/twenty-front/src/modules/object-record/utils/generateEmptyFieldValue.ts +++ b/packages/twenty-front/src/modules/object-record/utils/generateEmptyFieldValue.ts @@ -81,14 +81,14 @@ export const generateEmptyFieldValue = ( case FieldMetadataType.RawJson: { return null; } - case FieldMetadataType.RichTextDeprecated: { + case FieldMetadataType.RichText: { return null; } - case FieldMetadataType.RichText: { + case FieldMetadataType.RichTextV2: { return { blocknote: null, markdown: null, - } + }; } case FieldMetadataType.Actor: { return { diff --git a/packages/twenty-front/src/modules/object-record/utils/isFieldCellSupported.ts b/packages/twenty-front/src/modules/object-record/utils/isFieldCellSupported.ts index 937963df1f22..df03a87311ca 100644 --- a/packages/twenty-front/src/modules/object-record/utils/isFieldCellSupported.ts +++ b/packages/twenty-front/src/modules/object-record/utils/isFieldCellSupported.ts @@ -15,7 +15,7 @@ export const isFieldCellSupported = ( [ FieldMetadataType.Uuid, FieldMetadataType.Position, - FieldMetadataType.RichTextDeprecated, + FieldMetadataType.RichText, ].includes(fieldMetadataItem.type) ) { return false; diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsCompositeFieldTypeConfigs.ts b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsCompositeFieldTypeConfigs.ts index 993a3d6fa092..2e6602b77ccf 100644 --- a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsCompositeFieldTypeConfigs.ts +++ b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsCompositeFieldTypeConfigs.ts @@ -7,7 +7,7 @@ import { FieldFullNameValue, FieldLinksValue, FieldPhonesValue, - FieldRichTextValue, + FieldRichTextV2Value, } from '@/object-record/record-field/types/FieldMetadata'; import { SettingsFieldTypeConfig } from '@/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs'; import { CompositeFieldType } from '@/settings/data-model/types/CompositeFieldType'; @@ -180,7 +180,7 @@ export const SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS = { }, exampleValue: { source: 'source', name: 'name', workspaceMemberId: 'id' }, } as const satisfies SettingsCompositeFieldTypeConfig, - [FieldMetadataType.RichText]: { + [FieldMetadataType.RichTextV2]: { label: 'Rich Text', Icon: IllustrationIconText, subFields: ['blocknote', 'markdown'], @@ -194,5 +194,5 @@ export const SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS = { markdown: 'TODO', }, category: 'Basic', - } as const satisfies SettingsCompositeFieldTypeConfig, + } as const satisfies SettingsCompositeFieldTypeConfig, } as const satisfies SettingsCompositeFieldTypeConfigArray; diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts index d4aa50977596..a01b5d7f3d3b 100644 --- a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts +++ b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsNonCompositeFieldTypeConfigs.ts @@ -25,7 +25,7 @@ import { FieldNumberValue, FieldRatingValue, FieldRelationValue, - FieldRichTextDeprecatedValue, + FieldRichTextValue, FieldSelectValue, FieldTextValue, FieldUUidValue, @@ -122,12 +122,12 @@ export const SETTINGS_NON_COMPOSITE_FIELD_TYPE_CONFIGS: SettingsNonCompositeFiel exampleValue: { key: 'value' }, category: 'Advanced', } as const satisfies SettingsFieldTypeConfig, - [FieldMetadataType.RichTextDeprecated]: { + [FieldMetadataType.RichText]: { label: 'Rich Text Deprecated', Icon: IllustrationIconSetting, exampleValue: "{ key: 'value' }", category: 'Basic', - } as const satisfies SettingsFieldTypeConfig, + } as const satisfies SettingsFieldTypeConfig, [FieldMetadataType.Array]: { label: 'Array', Icon: IllustrationIconArray, diff --git a/packages/twenty-front/src/modules/settings/data-model/types/CompositeFieldType.ts b/packages/twenty-front/src/modules/settings/data-model/types/CompositeFieldType.ts index f3d1d86d2523..24a134b5a9e8 100644 --- a/packages/twenty-front/src/modules/settings/data-model/types/CompositeFieldType.ts +++ b/packages/twenty-front/src/modules/settings/data-model/types/CompositeFieldType.ts @@ -10,7 +10,7 @@ export const COMPOSITE_FIELD_TYPES = [ 'PHONES', 'FULL_NAME', 'ACTOR', - 'RICH_TEXT', + 'RICH_TEXT_V2', ] as const; type CompositeFieldTypeBaseLiteral = (typeof COMPOSITE_FIELD_TYPES)[number]; diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect.tsx index bb68d9b38eb7..74db76b0f60a 100644 --- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect.tsx @@ -44,7 +44,7 @@ export const SettingsObjectNewFieldSelect = () => { const excludedFieldTypes: SettingsFieldType[] = ( [ FieldMetadataType.Numeric, - FieldMetadataType.RichTextDeprecated, + FieldMetadataType.RichText, FieldMetadataType.Actor, ] as const ).filter(isDefined); diff --git a/packages/twenty-front/src/pages/settings/data-model/constants/DefaultIconsByFieldType.ts b/packages/twenty-front/src/pages/settings/data-model/constants/DefaultIconsByFieldType.ts index 7c6c9b63dc02..63f0e65829a5 100644 --- a/packages/twenty-front/src/pages/settings/data-model/constants/DefaultIconsByFieldType.ts +++ b/packages/twenty-front/src/pages/settings/data-model/constants/DefaultIconsByFieldType.ts @@ -22,7 +22,7 @@ export const DEFAULT_ICONS_BY_FIELD_TYPE: Record = { [FieldMetadataType.Actor]: 'IconUsers', [FieldMetadataType.Numeric]: 'IconUsers', [FieldMetadataType.Position]: 'IconUsers', - [FieldMetadataType.RichTextDeprecated]: 'IconUsers', [FieldMetadataType.RichText]: 'IconUsers', + [FieldMetadataType.RichTextV2]: 'IconUsers', [FieldMetadataType.TsVector]: 'IconUsers', }; diff --git a/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts b/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts index 02792bf4bf27..31e08110ea54 100644 --- a/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts +++ b/packages/twenty-front/src/testing/mock-data/generated/mock-metadata-query-result.ts @@ -3584,7 +3584,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = "node": { "__typename": "field", "id": "2fdf421d-fab6-4135-81ac-50582724e20e", - "type": "RICH_TEXT_DEPRECATED", + "type": "RICH_TEXT", "name": "body", "label": "Body", "description": "Note body", @@ -18827,7 +18827,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = "node": { "__typename": "field", "id": "ae853d86-e770-4196-8b4b-2dee50957236", - "type": "RICH_TEXT_DEPRECATED", + "type": "RICH_TEXT", "name": "body", "label": "Body", "description": "Task body", diff --git a/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts b/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts index e7db08028ea0..3b3951645968 100644 --- a/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts +++ b/packages/twenty-server/src/engine/api/__mocks__/object-metadata-item.mock.ts @@ -193,9 +193,9 @@ const fieldRawJsonMock = { defaultValue: null, }; -const fieldRichTextDeprecatedMock = { - name: 'fieldRichTextDeprecated', - type: FieldMetadataType.RICH_TEXT_DEPRECATED, +const fieldRichTextMock = { + name: 'fieldRichText', + type: FieldMetadataType.RICH_TEXT, isNullable: true, defaultValue: null, }; @@ -258,7 +258,7 @@ export const fields = [ fieldPositionMock, fieldAddressMock, fieldRawJsonMock, - fieldRichTextDeprecatedMock, + fieldRichTextMock, fieldActorMock, fieldArrayMock, ]; diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts index 36c523c8d2ad..6ef3d25c6589 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/factories/query-runner-args.factory.ts @@ -22,8 +22,8 @@ import { import { FieldMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata.interface'; import { - RichTextMetadata, - richTextValueSchema, + RichTextV2Metadata, + richTextV2ValueSchema, } from 'src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type'; import { FieldMetadataMap } from 'src/engine/metadata-modules/types/field-metadata-map'; @@ -207,28 +207,28 @@ export class QueryRunnerArgsFactory { } case FieldMetadataType.NUMBER: return [key, Number(value)] as const; - case FieldMetadataType.RICH_TEXT: { - const richTextValue = richTextValueSchema.parse(value); + case FieldMetadataType.RICH_TEXT_V2: { + const richTextV2Value = richTextV2ValueSchema.parse(value); const serverBlockNoteEditor = ServerBlockNoteEditor.create(); - const convertedMarkdown = richTextValue.blocknote + const convertedMarkdown = richTextV2Value.blocknote ? await serverBlockNoteEditor.blocksToMarkdownLossy( - JSON.parse(richTextValue.blocknote), + JSON.parse(richTextV2Value.blocknote), ) : null; - const convertedBlocknote = richTextValue.markdown + const convertedBlocknote = richTextV2Value.markdown ? JSON.stringify( await serverBlockNoteEditor.tryParseMarkdownToBlocks( - richTextValue.markdown, + richTextV2Value.markdown, ), ) : null; - const valueInBothFormats: RichTextMetadata = { - markdown: richTextValue.markdown || convertedMarkdown, - blocknote: richTextValue.blocknote || convertedBlocknote, + const valueInBothFormats: RichTextV2Metadata = { + markdown: richTextV2Value.markdown || convertedMarkdown, + blocknote: richTextV2Value.blocknote || convertedBlocknote, }; return [key, valueInBothFormats]; diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/graphql-types/input/rich-text.input-type.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/graphql-types/input/rich-text.input-type.ts index f3a8cd078404..e99353b16984 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/graphql-types/input/rich-text.input-type.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/graphql-types/input/rich-text.input-type.ts @@ -1,16 +1,16 @@ import { GraphQLInputObjectType, GraphQLString } from 'graphql'; -const richTextLeafFilter = new GraphQLInputObjectType({ - name: 'RichTextLeafFilter', +const richTextV2LeafFilter = new GraphQLInputObjectType({ + name: 'RichTextV2LeafFilter', fields: { ilike: { type: GraphQLString }, }, }); -export const RichTextFilterType = new GraphQLInputObjectType({ - name: 'RichTextFilter', +export const RichTextV2FilterType = new GraphQLInputObjectType({ + name: 'RichTextV2Filter', fields: { - blocknote: { type: richTextLeafFilter }, - markdown: { type: richTextLeafFilter }, + blocknote: { type: richTextV2LeafFilter }, + markdown: { type: richTextV2LeafFilter }, }, }); diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts index d85d4c4fe61b..c53658ffced9 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/services/type-mapper.service.ts @@ -29,7 +29,7 @@ import { } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input'; import { IDFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/id-filter.input-type'; import { MultiSelectFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/multi-select-filter.input-type'; -import { RichTextFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/rich-text.input-type'; +import { RichTextV2FilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/rich-text.input-type'; import { SelectFilterType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/input/select-filter.input-type'; import { BigFloatScalarType, @@ -81,7 +81,7 @@ export class TypeMapperService { FieldMetadataType.ARRAY, StringArrayScalarType as unknown as GraphQLScalarType, ], - [FieldMetadataType.RICH_TEXT_DEPRECATED, GraphQLString], + [FieldMetadataType.RICH_TEXT, GraphQLString], [FieldMetadataType.TS_VECTOR, GraphQLString], ]); @@ -116,8 +116,8 @@ export class TypeMapperService { [FieldMetadataType.NUMERIC, BigFloatFilterType], [FieldMetadataType.POSITION, FloatFilterType], [FieldMetadataType.RAW_JSON, RawJsonFilterType], - [FieldMetadataType.RICH_TEXT_DEPRECATED, StringFilterType], - [FieldMetadataType.RICH_TEXT, RichTextFilterType], + [FieldMetadataType.RICH_TEXT, StringFilterType], + [FieldMetadataType.RICH_TEXT_V2, RichTextV2FilterType], [FieldMetadataType.ARRAY, ArrayFilterType], [FieldMetadataType.MULTI_SELECT, MultiSelectFilterType], [FieldMetadataType.SELECT, SelectFilterType], @@ -143,7 +143,7 @@ export class TypeMapperService { [FieldMetadataType.MULTI_SELECT, OrderByDirectionType], [FieldMetadataType.POSITION, OrderByDirectionType], [FieldMetadataType.RAW_JSON, OrderByDirectionType], - [FieldMetadataType.RICH_TEXT_DEPRECATED, OrderByDirectionType], + [FieldMetadataType.RICH_TEXT, OrderByDirectionType], [FieldMetadataType.ARRAY, OrderByDirectionType], [FieldMetadataType.TS_VECTOR, OrderByDirectionType], // TODO: Add TSVectorOrderByType ]); diff --git a/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts b/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts index 5813fd5107e8..885d215c105e 100644 --- a/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts +++ b/packages/twenty-server/src/engine/api/rest/core/query-builder/utils/map-field-metadata-to-graphql-query.utils.ts @@ -29,7 +29,7 @@ export const mapFieldMetadataToGraphqlQuery = ( FieldMetadataType.MULTI_SELECT, FieldMetadataType.POSITION, FieldMetadataType.RAW_JSON, - FieldMetadataType.RICH_TEXT_DEPRECATED, + FieldMetadataType.RICH_TEXT, FieldMetadataType.ARRAY, FieldMetadataType.TS_VECTOR, ].includes(fieldType); @@ -155,7 +155,7 @@ export const mapFieldMetadataToGraphqlQuery = ( additionalPhones } `; - } else if (fieldType === FieldMetadataType.RICH_TEXT) { + } else if (fieldType === FieldMetadataType.RICH_TEXT_V2) { return ` ${field.name} { diff --git a/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts b/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts index aeab70fb1902..f32903d72610 100644 --- a/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts +++ b/packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts @@ -40,7 +40,7 @@ const getFieldProperties = (type: FieldMetadataType): Property => { case FieldMetadataType.UUID: return { type: 'string', format: 'uuid' }; case FieldMetadataType.TEXT: - case FieldMetadataType.RICH_TEXT_DEPRECATED: + case FieldMetadataType.RICH_TEXT: return { type: 'string' }; case FieldMetadataType.DATE_TIME: return { type: 'string', format: 'date-time' }; @@ -266,7 +266,7 @@ const getSchemaComponentsProperties = ({ type: 'object', }; break; - case FieldMetadataType.RICH_TEXT: + case FieldMetadataType.RICH_TEXT_V2: itemProperty = { type: 'object', properties: { diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/index.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/index.ts index 97d2c1377637..c9ddcdd53a53 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/index.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/index.ts @@ -9,7 +9,7 @@ import { emailsCompositeType } from 'src/engine/metadata-modules/field-metadata/ import { fullNameCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/full-name.composite-type'; import { linksCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/links.composite-type'; import { phonesCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/phones.composite-type'; -import { richTextCompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type'; +import { richTextV2CompositeType } from 'src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type'; export const compositeTypeDefinitions = new Map< FieldMetadataType, @@ -22,5 +22,5 @@ export const compositeTypeDefinitions = new Map< [FieldMetadataType.ACTOR, actorCompositeType], [FieldMetadataType.EMAILS, emailsCompositeType], [FieldMetadataType.PHONES, phonesCompositeType], - [FieldMetadataType.RICH_TEXT, richTextCompositeType], + [FieldMetadataType.RICH_TEXT_V2, richTextV2CompositeType], ]); diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts index c39efacdbb20..0d7a98cedaab 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type.ts @@ -3,8 +3,8 @@ import { z } from 'zod'; import { CompositeType } from 'src/engine/metadata-modules/field-metadata/interfaces/composite-type.interface'; -export const richTextCompositeType: CompositeType = { - type: FieldMetadataType.RICH_TEXT, +export const richTextV2CompositeType: CompositeType = { + type: FieldMetadataType.RICH_TEXT_V2, properties: [ { name: 'blocknote', @@ -21,9 +21,9 @@ export const richTextCompositeType: CompositeType = { ], }; -export const richTextValueSchema = z.object({ +export const richTextV2ValueSchema = z.object({ blocknote: z.string().nullable(), markdown: z.string().nullable(), }); -export type RichTextMetadata = z.infer; +export type RichTextV2Metadata = z.infer; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts index 7332782ef640..4479d0260e01 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/dtos/default-value.input.ts @@ -35,7 +35,7 @@ export class FieldMetadataDefaultValueRawJson { value: object | null; } -export class FieldMetadataDefaultValueRichText { +export class FieldMetadataDefaultValueRichTextV2 { @ValidateIf((object, value) => value !== null) @IsQuotedString() blocknote: string | null; @@ -45,7 +45,7 @@ export class FieldMetadataDefaultValueRichText { markdown: string | null; } -export class FieldMetadataDefaultValueRichTextDeprecated { +export class FieldMetadataDefaultValueRichText { @ValidateIf((_object, value) => value !== null) @IsString() value: string | null; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts index 02ffde94c487..5196dac246ef 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface.ts @@ -14,7 +14,7 @@ import { FieldMetadataDefaultValueNumber, FieldMetadataDefaultValuePhones, FieldMetadataDefaultValueRawJson, - FieldMetadataDefaultValueRichTextDeprecated, + FieldMetadataDefaultValueRichText, FieldMetadataDefaultValueString, FieldMetadataDefaultValueUuidFunction, } from 'src/engine/metadata-modules/field-metadata/dtos/default-value.input'; @@ -48,7 +48,7 @@ type FieldMetadataDefaultValueMapping = { [FieldMetadataType.SELECT]: FieldMetadataDefaultValueString; [FieldMetadataType.MULTI_SELECT]: FieldMetadataDefaultValueString; [FieldMetadataType.RAW_JSON]: FieldMetadataDefaultValueRawJson; - [FieldMetadataType.RICH_TEXT_DEPRECATED]: FieldMetadataDefaultValueRichTextDeprecated; + [FieldMetadataType.RICH_TEXT]: FieldMetadataDefaultValueRichText; [FieldMetadataType.ACTOR]: FieldMetadataDefaultActor; [FieldMetadataType.ARRAY]: FieldMetadataDefaultArray; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/generate-default-value.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/generate-default-value.ts index 22cd2e189111..d2e3d2bd77a3 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/generate-default-value.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/generate-default-value.ts @@ -47,7 +47,7 @@ export function generateDefaultValue( primaryPhoneCallingCode: "''", additionalPhones: null, }; - case FieldMetadataType.RICH_TEXT: + case FieldMetadataType.RICH_TEXT_V2: return { blocknote: "''", markdown: "''", diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util.ts index 5cfb8e1f8bca..172751bb0fd4 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util.ts @@ -10,7 +10,7 @@ export const isCompositeFieldMetadataType = ( | FieldMetadataType.ACTOR | FieldMetadataType.EMAILS | FieldMetadataType.PHONES - | FieldMetadataType.RICH_TEXT => { + | FieldMetadataType.RICH_TEXT_V2 => { return [ FieldMetadataType.CURRENCY, FieldMetadataType.FULL_NAME, @@ -19,6 +19,6 @@ export const isCompositeFieldMetadataType = ( FieldMetadataType.ACTOR, FieldMetadataType.EMAILS, FieldMetadataType.PHONES, - FieldMetadataType.RICH_TEXT, + FieldMetadataType.RICH_TEXT_V2, ].includes(type); }; diff --git a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts index ca1529252f4a..c495836e8602 100644 --- a/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util.ts @@ -21,7 +21,7 @@ import { FieldMetadataDefaultValueNumber, FieldMetadataDefaultValuePhones, FieldMetadataDefaultValueRawJson, - FieldMetadataDefaultValueRichText, + FieldMetadataDefaultValueRichTextV2, FieldMetadataDefaultValueString, FieldMetadataDefaultValueStringArray, FieldMetadataDefaultValueUuidFunction, @@ -48,8 +48,8 @@ export const defaultValueValidatorsMap = { [FieldMetadataType.SELECT]: [FieldMetadataDefaultValueString], [FieldMetadataType.MULTI_SELECT]: [FieldMetadataDefaultValueStringArray], [FieldMetadataType.ADDRESS]: [FieldMetadataDefaultValueAddress], - [FieldMetadataType.RICH_TEXT]: [FieldMetadataDefaultValueRichText], - [FieldMetadataType.RICH_TEXT_DEPRECATED]: [FieldMetadataDefaultValueString], + [FieldMetadataType.RICH_TEXT_V2]: [FieldMetadataDefaultValueRichTextV2], + [FieldMetadataType.RICH_TEXT]: [FieldMetadataDefaultValueString], [FieldMetadataType.RAW_JSON]: [FieldMetadataDefaultValueRawJson], [FieldMetadataType.LINKS]: [FieldMetadataDefaultValueLinks], [FieldMetadataType.ACTOR]: [FieldMetadataDefaultActor], diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/constants/fieldMetadataTypesToTextColumnType.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/constants/fieldMetadataTypesToTextColumnType.ts index 7a447a9b8b7d..e8a984e69ce1 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/constants/fieldMetadataTypesToTextColumnType.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/constants/fieldMetadataTypesToTextColumnType.ts @@ -2,6 +2,6 @@ import { FieldMetadataType } from 'twenty-shared'; export const FIELD_METADATA_TYPES_TO_TEXT_COLUMN_TYPE = [ FieldMetadataType.TEXT, - FieldMetadataType.RICH_TEXT, + FieldMetadataType.RICH_TEXT_V2, FieldMetadataType.ARRAY, ]; diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory.ts index 51c0c2c89920..830e62281179 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory.ts @@ -27,7 +27,7 @@ export type CompositeFieldMetadataType = | FieldMetadataType.LINKS | FieldMetadataType.EMAILS | FieldMetadataType.PHONES - | FieldMetadataType.RICH_TEXT; + | FieldMetadataType.RICH_TEXT_V2; @Injectable() export class CompositeColumnActionFactory extends ColumnActionAbstractFactory { diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts index 9ae62d80b6e3..eb5b4ec87ae2 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/field-metadata-type-to-column-type.util.ts @@ -38,7 +38,7 @@ export const fieldMetadataTypeToColumnType = ( return 'jsonb'; case FieldMetadataType.TS_VECTOR: return 'tsvector'; - case FieldMetadataType.RICH_TEXT_DEPRECATED: + case FieldMetadataType.RICH_TEXT: return 'text'; default: throw new WorkspaceMigrationException( diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/is-text-column-type.util.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/is-text-column-type.util.ts index 6dc0b0ef88a6..62a39aaea18c 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/is-text-column-type.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/utils/is-text-column-type.util.ts @@ -3,7 +3,7 @@ import { FieldMetadataType } from 'twenty-shared'; export const isTextColumnType = (type: FieldMetadataType) => { return ( type === FieldMetadataType.TEXT || - type === FieldMetadataType.RICH_TEXT || + type === FieldMetadataType.RICH_TEXT_V2 || type === FieldMetadataType.ARRAY ); }; diff --git a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts index 5514c83714ff..a231a029a53c 100644 --- a/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts +++ b/packages/twenty-server/src/engine/metadata-modules/workspace-migration/workspace-migration.factory.ts @@ -57,10 +57,7 @@ export class WorkspaceMigrationFactory { [FieldMetadataType.NUMBER, { factory: this.basicColumnActionFactory }], [FieldMetadataType.POSITION, { factory: this.basicColumnActionFactory }], [FieldMetadataType.RAW_JSON, { factory: this.basicColumnActionFactory }], - [ - FieldMetadataType.RICH_TEXT_DEPRECATED, - { factory: this.basicColumnActionFactory }, - ], + [FieldMetadataType.RICH_TEXT, { factory: this.basicColumnActionFactory }], [FieldMetadataType.BOOLEAN, { factory: this.basicColumnActionFactory }], [FieldMetadataType.DATE_TIME, { factory: this.basicColumnActionFactory }], [FieldMetadataType.DATE, { factory: this.basicColumnActionFactory }], @@ -98,7 +95,7 @@ export class WorkspaceMigrationFactory { { factory: this.tsVectorColumnActionFactory }, ], [ - FieldMetadataType.RICH_TEXT, + FieldMetadataType.RICH_TEXT_V2, { factory: this.compositeColumnActionFactory }, ], ]); diff --git a/packages/twenty-server/src/engine/seeder/metadata-seeds/pets-metadata-seeds.ts b/packages/twenty-server/src/engine/seeder/metadata-seeds/pets-metadata-seeds.ts index 2bd8ac7d2b10..13728594dea2 100644 --- a/packages/twenty-server/src/engine/seeder/metadata-seeds/pets-metadata-seeds.ts +++ b/packages/twenty-server/src/engine/seeder/metadata-seeds/pets-metadata-seeds.ts @@ -96,7 +96,7 @@ export const PETS_METADATA_SEEDS: ObjectMetadataSeed = { name: 'soundSwag', }, { - type: FieldMetadataType.RICH_TEXT_DEPRECATED, + type: FieldMetadataType.RICH_TEXT, label: 'Bio', name: 'bio', }, diff --git a/packages/twenty-server/src/engine/twenty-orm/utils/get-subfields-for-aggregate-operation.util.ts b/packages/twenty-server/src/engine/twenty-orm/utils/get-subfields-for-aggregate-operation.util.ts index ffdd490fd602..4000ea2ad02c 100644 --- a/packages/twenty-server/src/engine/twenty-orm/utils/get-subfields-for-aggregate-operation.util.ts +++ b/packages/twenty-server/src/engine/twenty-orm/utils/get-subfields-for-aggregate-operation.util.ts @@ -36,7 +36,7 @@ export const getSubfieldsForAggregateOperation = ( 'primaryPhoneCountryCode', 'primaryPhoneCallingCode', ]; - case FieldMetadataType.RICH_TEXT: + case FieldMetadataType.RICH_TEXT_V2: return ['blocknote', 'markdown']; default: throw new Error(`Unsupported composite field type: ${fieldType}`); diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util.ts index c59aa4a943a7..a7d775190b7f 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/utils/is-searchable-field.util.ts @@ -6,8 +6,8 @@ const SEARCHABLE_FIELD_TYPES = [ FieldMetadataType.EMAILS, FieldMetadataType.ADDRESS, FieldMetadataType.LINKS, - FieldMetadataType.RICH_TEXT_DEPRECATED, FieldMetadataType.RICH_TEXT, + FieldMetadataType.RICH_TEXT_V2, ] as const; export type SearchableFieldType = (typeof SEARCHABLE_FIELD_TYPES)[number]; diff --git a/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts b/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts index 596bbc5ce58e..a100a9a7f86b 100644 --- a/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts +++ b/packages/twenty-server/src/modules/note/standard-objects/note.workspace-entity.ts @@ -7,7 +7,7 @@ import { ActorMetadata, FieldActorSource, } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; -import { RichTextMetadata } from 'src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type'; +import { RichTextV2Metadata } from 'src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type'; import { IndexType } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity'; import { RelationMetadataType, @@ -37,7 +37,7 @@ const BODY_FIELD_NAME = 'body'; export const SEARCH_FIELDS_FOR_NOTES: FieldTypeAndNameMetadata[] = [ { name: TITLE_FIELD_NAME, type: FieldMetadataType.TEXT }, - // { name: BODY_FIELD_NAME, type: FieldMetadataType.RICH_TEXT }, // TODO: Check later if and how this works + // { name: BODY_FIELD_NAME, type: FieldMetadataType.RICH_TEXT_V2 }, // TODO: Check later if and how this works ]; @WorkspaceEntity({ @@ -73,13 +73,13 @@ export class NoteWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceField({ standardId: NOTE_STANDARD_FIELD_IDS.body, - type: FieldMetadataType.RICH_TEXT, + type: FieldMetadataType.RICH_TEXT_V2, label: 'Body', description: 'Note body', icon: 'IconFilePencil', }) @WorkspaceIsNullable() - [BODY_FIELD_NAME]: RichTextMetadata | null; + [BODY_FIELD_NAME]: RichTextV2Metadata | null; @WorkspaceField({ standardId: NOTE_STANDARD_FIELD_IDS.createdBy, diff --git a/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts b/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts index b0966e7e960f..ee0e60e5c179 100644 --- a/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts +++ b/packages/twenty-server/src/modules/task/standard-objects/task.workspace-entity.ts @@ -7,7 +7,7 @@ import { ActorMetadata, FieldActorSource, } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; -import { RichTextMetadata } from 'src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type'; +import { RichTextV2Metadata } from 'src/engine/metadata-modules/field-metadata/composite-types/rich-text.composite-type'; import { IndexType } from 'src/engine/metadata-modules/index-metadata/index-metadata.entity'; import { RelationMetadataType, @@ -39,7 +39,7 @@ const BODY_FIELD_NAME = 'body'; export const SEARCH_FIELDS_FOR_TASK: FieldTypeAndNameMetadata[] = [ { name: TITLE_FIELD_NAME, type: FieldMetadataType.TEXT }, - // { name: BODY_FIELD_NAME, type: FieldMetadataType.RICH_TEXT }, // TODO: Check later if and how this works + // { name: BODY_FIELD_NAME, type: FieldMetadataType.RICH_TEXT_V2 }, // TODO: Check later if and how this works ]; @WorkspaceEntity({ @@ -75,13 +75,13 @@ export class TaskWorkspaceEntity extends BaseWorkspaceEntity { @WorkspaceField({ standardId: TASK_STANDARD_FIELD_IDS.body, - type: FieldMetadataType.RICH_TEXT, + type: FieldMetadataType.RICH_TEXT_V2, label: 'Body', description: 'Task body', icon: 'IconFilePencil', }) @WorkspaceIsNullable() - [BODY_FIELD_NAME]: RichTextMetadata | null; + [BODY_FIELD_NAME]: RichTextV2Metadata | null; @WorkspaceField({ standardId: TASK_STANDARD_FIELD_IDS.dueAt, diff --git a/packages/twenty-shared/src/types/FieldMetadataType.ts b/packages/twenty-shared/src/types/FieldMetadataType.ts index 6fe3b3f94a25..686da7e67fea 100644 --- a/packages/twenty-shared/src/types/FieldMetadataType.ts +++ b/packages/twenty-shared/src/types/FieldMetadataType.ts @@ -18,8 +18,8 @@ export enum FieldMetadataType { POSITION = 'POSITION', ADDRESS = 'ADDRESS', RAW_JSON = 'RAW_JSON', + RICH_TEXT_V2 = 'RICH_TEXT_V2', RICH_TEXT = 'RICH_TEXT', - RICH_TEXT_DEPRECATED = 'RICH_TEXT_DEPRECATED', ACTOR = 'ACTOR', ARRAY = 'ARRAY', TS_VECTOR = 'TS_VECTOR', diff --git a/packages/twenty-zapier/src/utils/computeInputFields.ts b/packages/twenty-zapier/src/utils/computeInputFields.ts index c15ec37d28bd..f06d437d32c9 100644 --- a/packages/twenty-zapier/src/utils/computeInputFields.ts +++ b/packages/twenty-zapier/src/utils/computeInputFields.ts @@ -11,7 +11,7 @@ const getTypeFromFieldMetadataType = ( switch (fieldMetadataType) { case FieldMetadataType.UUID: case FieldMetadataType.TEXT: - case FieldMetadataType.RICH_TEXT_DEPRECATED: + case FieldMetadataType.RICH_TEXT: case FieldMetadataType.ARRAY: case FieldMetadataType.RATING: return 'string'; @@ -200,7 +200,7 @@ const get_subfieldsFromField = (nodeField: NodeField): NodeField[] => { }; return [primaryLinkLabel, primaryLinkUrl, secondaryLinks]; } - case FieldMetadataType.RICH_TEXT: { + case FieldMetadataType.RICH_TEXT_V2: { const blocknote: NodeField = { type: FieldMetadataType.TEXT, name: 'blocknote', @@ -242,7 +242,7 @@ export const computeInputFields = ( case FieldMetadataType.EMAILS: case FieldMetadataType.LINKS: case FieldMetadataType.ADDRESS: - case FieldMetadataType.RICH_TEXT: + case FieldMetadataType.RICH_TEXT_V2: for (const subNodeField of get_subfieldsFromField(nodeField)) { const field = { key: `${nodeField.name}__${subNodeField.name}`, @@ -258,7 +258,7 @@ export const computeInputFields = ( break; case FieldMetadataType.UUID: case FieldMetadataType.TEXT: - case FieldMetadataType.RICH_TEXT_DEPRECATED: + case FieldMetadataType.RICH_TEXT: case FieldMetadataType.DATE_TIME: case FieldMetadataType.DATE: case FieldMetadataType.BOOLEAN: From 0080a2551e546e98b12ddd46a8c27e8e81e5109c Mon Sep 17 00:00:00 2001 From: ad-elias Date: Sun, 12 Jan 2025 18:47:21 +0100 Subject: [PATCH 26/29] Add IS_RICH_TEXT_V2_ENABLED feature flag --- .../src/generated-metadata/graphql.ts | 1 + .../twenty-front/src/generated/graphql.tsx | 5 ++-- .../components/ActivityRichTextEditor.tsx | 30 +++++++++++++------ .../enums/feature-flag-key.enum.ts | 1 + 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 6920295baae6..f0cddd33099d 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -406,6 +406,7 @@ export enum FeatureFlagKey { IsStripeIntegrationEnabled = 'IsStripeIntegrationEnabled', IsUniqueIndexesEnabled = 'IsUniqueIndexesEnabled', IsViewGroupsEnabled = 'IsViewGroupsEnabled', + IsRichTextV2Enabled = 'IsRichTextV2Enabled', IsWorkflowEnabled = 'IsWorkflowEnabled' } diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index abf055e18143..0c5bdb65abf4 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -1,5 +1,5 @@ -import * as Apollo from '@apollo/client'; import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; @@ -334,6 +334,7 @@ export enum FeatureFlagKey { IsJsonFilterEnabled = 'IsJsonFilterEnabled', IsMicrosoftSyncEnabled = 'IsMicrosoftSyncEnabled', IsPostgreSqlIntegrationEnabled = 'IsPostgreSQLIntegrationEnabled', + IsRichTextV2Enabled = 'IsRichTextV2Enabled', IsSsoEnabled = 'IsSSOEnabled', IsStripeIntegrationEnabled = 'IsStripeIntegrationEnabled', IsUniqueIndexesEnabled = 'IsUniqueIndexesEnabled', @@ -379,8 +380,8 @@ export enum FieldMetadataType { Rating = 'RATING', RawJson = 'RAW_JSON', Relation = 'RELATION', - RichTextV2 = 'RICH_TEXT_V2', RichText = 'RICH_TEXT', + RichTextV2 = 'RICH_TEXT_V2', Select = 'SELECT', Text = 'TEXT', TsVector = 'TS_VECTOR', diff --git a/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx b/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx index fa4b23bc8a5f..c430744b5732 100644 --- a/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx +++ b/packages/twenty-front/src/modules/activities/components/ActivityRichTextEditor.tsx @@ -44,6 +44,10 @@ export const ActivityRichTextEditor = ({ activityId, activityObjectNameSingular, }: ActivityRichTextEditorProps) => { + const isRichTextV2Enabled = useIsFeatureEnabled( + FeatureFlagKey.IsRichTextV2Enabled, + ); + const [activityInStore] = useRecoilState(recordStoreFamilyState(activityId)); const cache = useApolloClient().cache; @@ -67,15 +71,19 @@ export const ActivityRichTextEditor = ({ activityObjectNameSingular: activityObjectNameSingular, }); - const persistBodyDebounced = useDebouncedCallback((newBody: string) => { + const persistBodyDebounced = useDebouncedCallback((blocknote: string) => { + const body = isRichTextV2Enabled + ? { + blocknote, + markdown: null, + } + : (blocknote as any); + if (isDefined(activity)) { upsertActivity({ activity, input: { - body: { - blocknote: newBody, - markdown: null, - }, + body, }, }); } @@ -166,14 +174,18 @@ export const ActivityRichTextEditor = ({ }; const initialBody = useMemo(() => { + const blocknote = isRichTextV2Enabled + ? activity?.body.blocknote + : activity?.body; + if ( isDefined(activity) && - isNonEmptyString(activity.body.blocknote) && - activity?.body.blocknote !== '{}' + isNonEmptyString(blocknote) && + blocknote !== '{}' ) { - return JSON.parse(activity.body.blocknote); + return JSON.parse(blocknote); } - }, [activity]); + }, [activity, isRichTextV2Enabled]); const handleEditorBuiltInUploadFile = async (file: File) => { const { attachmentAbsoluteURL } = await handleUploadAttachment(file); diff --git a/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts b/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts index 2bafc436120d..884cb210000b 100644 --- a/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts @@ -15,6 +15,7 @@ export enum FeatureFlagKey { IsAdvancedFiltersEnabled = 'IS_ADVANCED_FILTERS_ENABLED', IsAggregateQueryEnabled = 'IS_AGGREGATE_QUERY_ENABLED', IsViewGroupsEnabled = 'IS_VIEW_GROUPS_ENABLED', + IsRichTextV2Enabled = 'IS_RICH_TEXT_V2_ENABLED', IsCommandMenuV2Enabled = 'IS_COMMAND_MENU_V2_ENABLED', IsCrmMigrationEnabled = 'IS_CRM_MIGRATION_ENABLED', IsJsonFilterEnabled = 'IS_JSON_FILTER_ENABLED', From 683558787456a9132aef771d7a6acd6adae17f6a Mon Sep 17 00:00:00 2001 From: ad-elias Date: Sun, 12 Jan 2025 18:57:38 +0100 Subject: [PATCH 27/29] Add feature flag to command menu search --- .../hooks/useCommandMenuCommands.tsx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCommands.tsx b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCommands.tsx index a3b8ee9fb982..803b6b27d565 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCommands.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCommands.tsx @@ -35,6 +35,9 @@ import { FeatureFlagKey } from '~/generated/graphql'; import { getLogoUrlFromDomainName } from '~/utils'; export const useCommandMenuCommands = () => { + const isRichTextV2Enabled = useIsFeatureEnabled( + FeatureFlagKey.IsRichTextV2Enabled, + ); const actionMenuEntries = useRecoilComponentValueV2( actionMenuEntriesComponentSelector, ); @@ -147,9 +150,11 @@ export const useCommandMenuCommands = () => { ? makeOrFilterVariables([ { title: { ilike: `%${deferredCommandMenuSearch}%` } }, { - body: { - blocknote: { ilike: `%${deferredCommandMenuSearch}%` }, - }, + body: isRichTextV2Enabled + ? { + blocknote: { ilike: `%${deferredCommandMenuSearch}%` }, + } + : { ilike: `%${deferredCommandMenuSearch}%` }, }, ]) : undefined, @@ -163,9 +168,11 @@ export const useCommandMenuCommands = () => { ? makeOrFilterVariables([ { title: { ilike: `%${deferredCommandMenuSearch}%` } }, { - body: { - blocknote: { ilike: `%${deferredCommandMenuSearch}%` }, - }, + body: isRichTextV2Enabled + ? { + blocknote: { ilike: `%${deferredCommandMenuSearch}%` }, + } + : { ilike: `%${deferredCommandMenuSearch}%` }, }, ]) : undefined, From 657b1e86af7d4aa669fc27131297e56f0a2ced39 Mon Sep 17 00:00:00 2001 From: ad-elias Date: Sun, 12 Jan 2025 19:21:03 +0100 Subject: [PATCH 28/29] Remove comments --- .../modules/object-record/record-field/types/FieldMetadata.ts | 2 +- .../utils/buildRecordFromImportedStructuredRow.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts b/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts index 767b3bedf917..0207f24123c4 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts @@ -271,7 +271,7 @@ export type Json = ZodHelperLiteral | { [key: string]: Json } | Json[]; export type FieldJsonValue = Record | Json[] | null; export type FieldRichTextV2Value = { - blocknote: string | null; // TODO: Can these be null? + blocknote: string | null; markdown: string | null; }; diff --git a/packages/twenty-front/src/modules/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow.ts b/packages/twenty-front/src/modules/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow.ts index 8c3ae0afd840..7ac95c815f09 100644 --- a/packages/twenty-front/src/modules/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow.ts +++ b/packages/twenty-front/src/modules/object-record/spreadsheet-import/utils/buildRecordFromImportedStructuredRow.ts @@ -170,8 +170,6 @@ export const buildRecordFromImportedStructuredRow = ( importedStructuredRow[`${markdownLabel} (${field.name})`], ) ) { - // TODO: Convert back and forth from BlockNote. - // Has to happen server-side, because API shouldn't require both fields? How / where? recordToBuild[field.name] = { blocknote: castToString( importedStructuredRow[`${blocknoteLabel} (${field.name})`], From 66e7d17160f45e9b65d3350305fc0d8efd3769fb Mon Sep 17 00:00:00 2001 From: ad-elias Date: Sun, 12 Jan 2025 20:53:03 +0100 Subject: [PATCH 29/29] Add example value --- .../data-model/constants/SettingsCompositeFieldTypeConfigs.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsCompositeFieldTypeConfigs.ts b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsCompositeFieldTypeConfigs.ts index 2e6602b77ccf..3d048b1b8d8b 100644 --- a/packages/twenty-front/src/modules/settings/data-model/constants/SettingsCompositeFieldTypeConfigs.ts +++ b/packages/twenty-front/src/modules/settings/data-model/constants/SettingsCompositeFieldTypeConfigs.ts @@ -190,8 +190,8 @@ export const SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS = { markdown: 'Markdown', }, exampleValue: { - blocknote: 'TODO', // TODO - markdown: 'TODO', + blocknote: '[{"type":"heading","content":"Hello"}]', + markdown: '# Hello', }, category: 'Basic', } as const satisfies SettingsCompositeFieldTypeConfig,