From c18f574aeec45400c16c8836cfd20859b148b039 Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Tue, 3 Feb 2026 18:52:30 +0100 Subject: [PATCH] Trigger refetch workflow on version creation (#17685) https://github.com/user-attachments/assets/ec156aba-3a67-4eef-addf-86158c3f1837 --- .../object-record/hooks/useFindOneRecord.ts | 5 +- .../hooks/useWorkflowWithCurrentVersion.ts | 47 +++++++++++++------ ...shouldWorkflowRefetchRequestFamilyState.ts | 9 ++++ .../components/WorkflowSSESubscribeEffect.tsx | 25 ++++++++++ 4 files changed, 70 insertions(+), 16 deletions(-) create mode 100644 packages/twenty-front/src/modules/workflow/states/shouldWorkflowRefetchRequestFamilyState.ts diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecord.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecord.ts index 13821dcec6..64c4944264 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecord.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindOneRecord.ts @@ -7,10 +7,10 @@ import { type ObjectMetadataItemIdentifier } from '@/object-metadata/types/Objec import { getRecordFromRecordNode } from '@/object-record/cache/utils/getRecordFromRecordNode'; import { useGenerateDepthRecordGqlFieldsFromObject } from '@/object-record/graphql/record-gql-fields/hooks/useGenerateDepthRecordGqlFieldsFromObject'; import { type RecordGqlNode } from '@/object-record/graphql/types/RecordGqlNode'; -import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types'; import { useFindOneRecordQuery } from '@/object-record/hooks/useFindOneRecordQuery'; import { useObjectPermissionsForObject } from '@/object-record/hooks/useObjectPermissionsForObject'; import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; +import { type RecordGqlOperationGqlRecordFields } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; export const useFindOneRecord = ({ @@ -53,7 +53,7 @@ export const useFindOneRecord = ({ const hasReadPermission = objectPermissions.canReadObjectRecords; - const { data, loading, error } = useQuery<{ + const { data, loading, error, refetch } = useQuery<{ [nameSingular: string]: RecordGqlNode; }>(findOneRecordQuery, { skip: !objectMetadataItem || !objectRecordId || skip || !hasReadPermission, @@ -85,5 +85,6 @@ export const useFindOneRecord = ({ record: recordWithoutConnection, loading, error, + refetch, }; }; diff --git a/packages/twenty-front/src/modules/workflow/hooks/useWorkflowWithCurrentVersion.ts b/packages/twenty-front/src/modules/workflow/hooks/useWorkflowWithCurrentVersion.ts index 3210666f84..ecf01b19fb 100644 --- a/packages/twenty-front/src/modules/workflow/hooks/useWorkflowWithCurrentVersion.ts +++ b/packages/twenty-front/src/modules/workflow/hooks/useWorkflowWithCurrentVersion.ts @@ -1,5 +1,9 @@ +import { useEffect } from 'react'; +import { useRecoilState } from 'recoil'; + import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord'; +import { shouldWorkflowRefetchRequestFamilyState } from '@/workflow/states/shouldWorkflowRefetchRequestFamilyState'; import { type Workflow, type WorkflowVersion, @@ -16,23 +20,38 @@ type WorkflowWithAllVersions = Omit & { export const useWorkflowWithCurrentVersion = ( workflowId: string | undefined, ): WorkflowWithCurrentVersion | undefined => { - const { record: workflow } = useFindOneRecord({ - objectNameSingular: CoreObjectNameSingular.Workflow, - objectRecordId: workflowId, - recordGqlFields: { - id: true, - name: true, - statuses: true, - lastPublishedVersionId: true, - versions: { + const [shouldWorkflowRefetchRequest, setShouldWorkflowRefetchRequest] = + useRecoilState(shouldWorkflowRefetchRequestFamilyState(workflowId ?? '')); + + const { record: workflow, refetch: refetchWorkflow } = + useFindOneRecord({ + objectNameSingular: CoreObjectNameSingular.Workflow, + objectRecordId: workflowId, + recordGqlFields: { id: true, - status: true, name: true, - createdAt: true, + statuses: true, + lastPublishedVersionId: true, + versions: { + id: true, + status: true, + name: true, + createdAt: true, + }, }, - }, - skip: !isDefined(workflowId), - }); + skip: !isDefined(workflowId), + }); + + useEffect(() => { + if (shouldWorkflowRefetchRequest) { + setShouldWorkflowRefetchRequest(false); + refetchWorkflow(); + } + }, [ + shouldWorkflowRefetchRequest, + setShouldWorkflowRefetchRequest, + refetchWorkflow, + ]); const draftVersion = workflow?.versions.find( (workflowVersion) => workflowVersion.status === 'DRAFT', diff --git a/packages/twenty-front/src/modules/workflow/states/shouldWorkflowRefetchRequestFamilyState.ts b/packages/twenty-front/src/modules/workflow/states/shouldWorkflowRefetchRequestFamilyState.ts new file mode 100644 index 0000000000..98988fd045 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/states/shouldWorkflowRefetchRequestFamilyState.ts @@ -0,0 +1,9 @@ +import { createFamilyState } from '@/ui/utilities/state/utils/createFamilyState'; + +export const shouldWorkflowRefetchRequestFamilyState = createFamilyState< + boolean, + string +>({ + key: 'shouldWorkflowRefetchRequestFamilyState', + defaultValue: false, +}); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowSSESubscribeEffect.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowSSESubscribeEffect.tsx index 3b19953fda..06d07af0bd 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowSSESubscribeEffect.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowSSESubscribeEffect.tsx @@ -1,5 +1,11 @@ +import { useCallback } from 'react'; +import { useSetRecoilState } from 'recoil'; + +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { useListenToObjectRecordOperationBrowserEvent } from '@/object-record/hooks/useListenToObjectRecordOperationBrowserEvent'; import { useListenToObjectRecordEventsForQuery } from '@/sse-db-event/hooks/useListenToObjectRecordEventsForQuery'; +import { shouldWorkflowRefetchRequestFamilyState } from '@/workflow/states/shouldWorkflowRefetchRequestFamilyState'; export const WorkflowSSESubscribeEffect = ({ workflowId, @@ -8,6 +14,15 @@ export const WorkflowSSESubscribeEffect = ({ }) => { const queryId = `workflow-versions-for-workflow-${workflowId}`; + const setShouldWorkflowRefetchRequest = useSetRecoilState( + shouldWorkflowRefetchRequestFamilyState(workflowId), + ); + + const { objectMetadataItem: workflowVersionMetadataItem } = + useObjectMetadataItem({ + objectNameSingular: CoreObjectNameSingular.WorkflowVersion, + }); + useListenToObjectRecordEventsForQuery({ queryId, operationSignature: { @@ -20,5 +35,15 @@ export const WorkflowSSESubscribeEffect = ({ }, }); + const handleWorkflowVersionCreateOne = useCallback(() => { + setShouldWorkflowRefetchRequest(true); + }, [setShouldWorkflowRefetchRequest]); + + useListenToObjectRecordOperationBrowserEvent({ + onObjectRecordOperationBrowserEvent: handleWorkflowVersionCreateOne, + objectMetadataItemId: workflowVersionMetadataItem.id, + operationTypes: ['create-one'], + }); + return null; };