From 981956a636f46713894dbaf929ccad5332e07941 Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Wed, 21 Jan 2026 18:26:43 +0100 Subject: [PATCH] Use new SSE setup for workflow runs (#17309) - Add a specific subscriber for workflow run - Workflow runs use apollo cache. Adding updateRecordFromCache on record updates --- .../components/RecordTableRow.tsx | 17 +++++++++---- ...ggerOptimisticEffectFromSseUpdateEvents.ts | 23 ++++++++++++++---- .../components/WorkflowRunCard.tsx | 21 ++++++++++++---- .../WorkflowRunSSESubscribeEffect.tsx | 24 +++++++++++++++++++ 4 files changed, 71 insertions(+), 14 deletions(-) create mode 100644 packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunSSESubscribeEffect.tsx diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableRow.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableRow.tsx index 24c5ce2b48..cfc2a2c75a 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableRow.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableRow.tsx @@ -15,6 +15,8 @@ import { ListenRecordUpdatesEffect } from '@/sse-db-event/components/ListenRecor import { getDefaultRecordFieldsToListen } from '@/sse-db-event/utils/getDefaultRecordFieldsToListen'; import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; +import { FeatureFlagKey } from '~/generated/graphql'; type RecordTableRowProps = { recordId: string; @@ -40,6 +42,9 @@ export const RecordTableRow = ({ const isRowFocusActive = useRecoilComponentValue( isRecordTableRowFocusActiveComponentState, ); + const isSseDbEventsEnabled = useIsFeatureEnabled( + FeatureFlagKey.IS_SSE_DB_EVENTS_ENABLED, + ); return isFirstRowOfGroup ? ( - + {!isSseDbEventsEnabled && ( + + )} ) : ( { computeReferences: false, }); + if ( + !isDefined(cachedRecord) || + !isDefined(cachedRecordWithConnection) + ) { + continue; + } + const computedOptimisticRecord = { + ...cachedRecord, ...updatedRecord, id: updatedRecord.id, __typename: getObjectTypename(objectMetadataItem.nameSingular), }; + updateRecordFromCache({ + objectMetadataItems, + objectMetadataItem, + cache: apolloCoreClient.cache, + record: computedOptimisticRecord, + recordGqlFields, + objectPermissionsByObjectMetadataId, + }); + const computedOptimisticRecordWithConnection = getRecordNodeFromRecord({ record: computedOptimisticRecord, objectMetadataItem, @@ -81,10 +99,7 @@ export const useTriggerOptimisticEffectFromSseUpdateEvents = () => { recordGqlFields, }); - if ( - !isDefined(cachedRecordWithConnection) || - !isDefined(computedOptimisticRecordWithConnection) - ) { + if (!isDefined(computedOptimisticRecordWithConnection)) { continue; } diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunCard.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunCard.tsx index d07eab4cf7..fb6a71ee85 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunCard.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunCard.tsx @@ -1,15 +1,19 @@ import { SKELETON_LOADER_HEIGHT_SIZES } from '@/activities/components/SkeletonLoader'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { ListenRecordUpdatesEffect } from '@/sse-db-event/components/ListenRecordUpdatesEffect'; import { useTargetRecord } from '@/ui/layout/contexts/useTargetRecord'; import { getWorkflowVisualizerComponentInstanceId } from '@/workflow/utils/getWorkflowVisualizerComponentInstanceId'; +import { WorkflowRunSSESubscribeEffect } from '@/workflow/workflow-diagram/components/WorkflowRunSSESubscribeEffect'; import { WorkflowRunVisualizer } from '@/workflow/workflow-diagram/components/WorkflowRunVisualizer'; import { WorkflowRunVisualizerEffect } from '@/workflow/workflow-diagram/components/WorkflowRunVisualizerEffect'; import { WorkflowRunVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowRunVisualizerComponentInstanceContext'; import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; +import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { Suspense, useId } from 'react'; import Skeleton, { SkeletonTheme } from 'react-loading-skeleton'; +import { FeatureFlagKey } from '~/generated/graphql'; const StyledLoadingSkeletonContainer = styled.div` display: flex; @@ -41,6 +45,9 @@ const LoadingSkeleton = () => { export const WorkflowRunCard = () => { const targetRecord = useTargetRecord(); const componentId = useId(); + const isSseDbEventsEnabled = useIsFeatureEnabled( + FeatureFlagKey.IS_SSE_DB_EVENTS_ENABLED, + ); return ( { }} > - + {isSseDbEventsEnabled ? ( + + ) : ( + + )} }> diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunSSESubscribeEffect.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunSSESubscribeEffect.tsx new file mode 100644 index 0000000000..4219433b7f --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunSSESubscribeEffect.tsx @@ -0,0 +1,24 @@ +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { useListenToObjectRecordEventsForQuery } from '@/sse-db-event/hooks/useListenToObjectRecordEventsForQuery'; + +export const WorkflowRunSSESubscribeEffect = ({ + workflowRunId, +}: { + workflowRunId: string; +}) => { + const queryId = `workflow-run-${workflowRunId}`; + + useListenToObjectRecordEventsForQuery({ + queryId, + operationSignature: { + objectNameSingular: CoreObjectNameSingular.WorkflowRun, + variables: { + filter: { + id: { eq: workflowRunId }, + }, + }, + }, + }); + + return null; +};