Skip to content

Commit beed8c3

Browse files
committed
improvement(store-hydration): refactor loading state tracking for workflows
1 parent f609b6e commit beed8c3

File tree

5 files changed

+286
-319
lines changed

5 files changed

+286
-319
lines changed

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import { useCopilotStore } from '@/stores/panel-new/copilot/store'
4747
import { usePanelEditorStore } from '@/stores/panel-new/editor/store'
4848
import { useGeneralStore } from '@/stores/settings/general/store'
4949
import { useWorkflowDiffStore } from '@/stores/workflow-diff/store'
50-
import { hasWorkflowsInitiallyLoaded, useWorkflowRegistry } from '@/stores/workflows/registry/store'
50+
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
5151
import { getUniqueBlockName } from '@/stores/workflows/utils'
5252
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
5353

@@ -83,7 +83,6 @@ interface BlockData {
8383

8484
const WorkflowContent = React.memo(() => {
8585
// State
86-
const [isWorkflowReady, setIsWorkflowReady] = useState(false)
8786

8887
// State for tracking node dragging
8988
const [draggedNodeId, setDraggedNodeId] = useState<string | null>(null)
@@ -102,11 +101,12 @@ const WorkflowContent = React.memo(() => {
102101

103102
// Get workspace ID from the params
104103
const workspaceId = params.workspaceId as string
104+
const workflowIdParam = params.workflowId as string
105105

106106
// Notification store
107107
const addNotification = useNotificationStore((state) => state.addNotification)
108108

109-
const { workflows, activeWorkflowId, isLoading, setActiveWorkflow } = useWorkflowRegistry()
109+
const { workflows, activeWorkflowId, hydration, setActiveWorkflow } = useWorkflowRegistry()
110110

111111
// Use the clean abstraction for current workflow state
112112
const currentWorkflow = useCurrentWorkflow()
@@ -127,6 +127,13 @@ const WorkflowContent = React.memo(() => {
127127
// Extract workflow data from the abstraction
128128
const { blocks, edges, isDiffMode, lastSaved } = currentWorkflow
129129

130+
const isWorkflowReady =
131+
hydration.phase === 'ready' &&
132+
hydration.workflowId === workflowIdParam &&
133+
activeWorkflowId === workflowIdParam &&
134+
Boolean(workflows[workflowIdParam]) &&
135+
lastSaved !== undefined
136+
130137
// Node utilities hook for position/hierarchy calculations (requires blocks)
131138
const {
132139
getNodeDepth,
@@ -1209,10 +1216,19 @@ const WorkflowContent = React.memo(() => {
12091216
useEffect(() => {
12101217
let cancelled = false
12111218
const currentId = params.workflowId as string
1219+
const currentWorkspaceHydration = hydration.workspaceId
1220+
1221+
const isRegistryReady = hydration.phase !== 'metadata-loading' && hydration.phase !== 'idle'
12121222

12131223
// Wait for registry to be ready to prevent race conditions
1214-
// Don't proceed if: no workflowId, registry is loading, or workflow not in registry
1215-
if (!currentId || isLoading || !workflows[currentId]) return
1224+
if (
1225+
!currentId ||
1226+
!workflows[currentId] ||
1227+
!isRegistryReady ||
1228+
(currentWorkspaceHydration && currentWorkspaceHydration !== workspaceId)
1229+
) {
1230+
return
1231+
}
12161232

12171233
if (activeWorkflowId !== currentId) {
12181234
// Clear diff and set as active
@@ -1229,25 +1245,15 @@ const WorkflowContent = React.memo(() => {
12291245
return () => {
12301246
cancelled = true
12311247
}
1232-
}, [params.workflowId, workflows, activeWorkflowId, setActiveWorkflow, isLoading])
1233-
1234-
// Track when workflow is ready for rendering
1235-
useEffect(() => {
1236-
const currentId = params.workflowId as string
1237-
1238-
// Workflow is ready when:
1239-
// 1. We have an active workflow that matches the URL
1240-
// 2. The workflow exists in the registry
1241-
// 3. Workflows are not currently loading
1242-
// 4. The workflow store has been initialized (lastSaved exists means state was loaded)
1243-
const shouldBeReady =
1244-
activeWorkflowId === currentId &&
1245-
Boolean(workflows[currentId]) &&
1246-
!isLoading &&
1247-
lastSaved !== undefined
1248-
1249-
setIsWorkflowReady(shouldBeReady)
1250-
}, [activeWorkflowId, params.workflowId, workflows, isLoading, lastSaved])
1248+
}, [
1249+
params.workflowId,
1250+
workflows,
1251+
activeWorkflowId,
1252+
setActiveWorkflow,
1253+
hydration.phase,
1254+
hydration.workspaceId,
1255+
workspaceId,
1256+
])
12511257

12521258
// Preload workspace environment - React Query handles caching automatically
12531259
useWorkspaceEnvironment(workspaceId)
@@ -1258,8 +1264,8 @@ const WorkflowContent = React.memo(() => {
12581264
const workflowIds = Object.keys(workflows)
12591265
const currentId = params.workflowId as string
12601266

1261-
// Wait for initial load to complete before making navigation decisions
1262-
if (!hasWorkflowsInitiallyLoaded() || isLoading) {
1267+
// Wait for metadata to finish loading before making navigation decisions
1268+
if (hydration.phase === 'metadata-loading' || hydration.phase === 'idle') {
12631269
return
12641270
}
12651271

@@ -1302,7 +1308,7 @@ const WorkflowContent = React.memo(() => {
13021308
}
13031309

13041310
validateAndNavigate()
1305-
}, [params.workflowId, workflows, isLoading, workspaceId, router])
1311+
}, [params.workflowId, workflows, hydration.phase, workspaceId, router])
13061312

13071313
// Cache block configs to prevent unnecessary re-fetches
13081314
const blockConfigCache = useRef<Map<string, any>>(new Map())

apps/sim/hooks/queries/workflows.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ async function fetchWorkflows(workspaceId: string): Promise<WorkflowMetadata[]>
4343
}
4444

4545
export function useWorkflows(workspaceId?: string) {
46-
const setWorkflows = useWorkflowRegistry((state) => state.setWorkflows)
46+
const beginMetadataLoad = useWorkflowRegistry((state) => state.beginMetadataLoad)
47+
const completeMetadataLoad = useWorkflowRegistry((state) => state.completeMetadataLoad)
48+
const failMetadataLoad = useWorkflowRegistry((state) => state.failMetadataLoad)
4749

4850
const query = useQuery({
4951
queryKey: workflowKeys.list(workspaceId),
@@ -54,10 +56,24 @@ export function useWorkflows(workspaceId?: string) {
5456
})
5557

5658
useEffect(() => {
57-
if (query.data) {
58-
setWorkflows(query.data)
59+
if (workspaceId && query.status === 'pending') {
60+
beginMetadataLoad(workspaceId)
5961
}
60-
}, [query.data, setWorkflows])
62+
}, [workspaceId, query.status, beginMetadataLoad])
63+
64+
useEffect(() => {
65+
if (workspaceId && query.status === 'success' && query.data) {
66+
completeMetadataLoad(workspaceId, query.data)
67+
}
68+
}, [workspaceId, query.status, query.data, completeMetadataLoad])
69+
70+
useEffect(() => {
71+
if (workspaceId && query.status === 'error') {
72+
const message =
73+
query.error instanceof Error ? query.error.message : 'Failed to fetch workflows'
74+
failMetadataLoad(workspaceId, message)
75+
}
76+
}, [workspaceId, query.status, query.error, failMetadataLoad])
6177

6278
return query
6379
}

0 commit comments

Comments
 (0)