Skip to content

Commit 35eb028

Browse files
DrJKLampcode-com
andcommitted
refactor: move workflow loading state from bootstrapStore to workflowStore
- Add useAsyncState in workflowStore for syncWorkflows with loading state - Add loadWorkflows action that guards against double-loading - Simplify bootstrapStore by delegating workflow loading to workflowStore - Update test mocks for workflowStore Amp-Thread-ID: https://ampcode.com/threads/T-019bfcce-cce0-70b0-abc6-742669ac86e3 Co-authored-by: Amp <amp@ampcode.com>
1 parent c171227 commit 35eb028

File tree

3 files changed

+68
-98
lines changed

3 files changed

+68
-98
lines changed

src/platform/workflow/management/stores/workflowStore.ts

Lines changed: 60 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import _ from 'es-toolkit/compat'
2+
import { useAsyncState } from '@vueuse/core'
23
import { defineStore } from 'pinia'
34
import { computed, markRaw, ref, shallowRef, watch } from 'vue'
45
import type { Raw } from 'vue'
@@ -500,48 +501,67 @@ export const useWorkflowStore = defineStore('workflow', () => {
500501
workflow.isPersisted && !workflow.path.startsWith('subgraphs/')
501502
)
502503
)
503-
const syncWorkflows = async (dir: string = '') => {
504-
await syncEntities(
505-
dir ? 'workflows/' + dir : 'workflows',
506-
workflowLookup.value,
507-
(file) =>
508-
new ComfyWorkflow({
509-
path: file.path,
510-
modified: file.modified,
511-
size: file.size
512-
}),
513-
(existingWorkflow, file) => {
514-
const isActiveWorkflow =
515-
activeWorkflow.value?.path === existingWorkflow.path
516-
517-
const nextLastModified = Math.max(
518-
existingWorkflow.lastModified,
519-
file.modified
520-
)
521-
522-
const isMetadataUnchanged =
523-
nextLastModified === existingWorkflow.lastModified &&
524-
file.size === existingWorkflow.size
525-
526-
if (!isMetadataUnchanged) {
527-
existingWorkflow.lastModified = nextLastModified
528-
existingWorkflow.size = file.size
529-
}
530504

531-
// Never unload the active workflow - it may contain unsaved in-memory edits.
532-
if (isActiveWorkflow) {
533-
return
534-
}
505+
const {
506+
isReady: isSyncReady,
507+
isLoading: isSyncLoading,
508+
execute: executeSyncWorkflows
509+
} = useAsyncState(
510+
async (dir: string = '') => {
511+
await syncEntities(
512+
dir ? 'workflows/' + dir : 'workflows',
513+
workflowLookup.value,
514+
(file) =>
515+
new ComfyWorkflow({
516+
path: file.path,
517+
modified: file.modified,
518+
size: file.size
519+
}),
520+
(existingWorkflow, file) => {
521+
const isActiveWorkflow =
522+
activeWorkflow.value?.path === existingWorkflow.path
523+
524+
const nextLastModified = Math.max(
525+
existingWorkflow.lastModified,
526+
file.modified
527+
)
528+
529+
const isMetadataUnchanged =
530+
nextLastModified === existingWorkflow.lastModified &&
531+
file.size === existingWorkflow.size
532+
533+
if (!isMetadataUnchanged) {
534+
existingWorkflow.lastModified = nextLastModified
535+
existingWorkflow.size = file.size
536+
}
537+
538+
// Never unload the active workflow - it may contain unsaved in-memory edits.
539+
if (isActiveWorkflow) {
540+
return
541+
}
542+
543+
// If nothing changed, keep any loaded content cached.
544+
if (isMetadataUnchanged) {
545+
return
546+
}
547+
548+
existingWorkflow.unload()
549+
},
550+
/* exclude */ (workflow) => workflow.isTemporary
551+
)
552+
},
553+
undefined,
554+
{ immediate: false }
555+
)
535556

536-
// If nothing changed, keep any loaded content cached.
537-
if (isMetadataUnchanged) {
538-
return
539-
}
557+
async function syncWorkflows(dir: string = '') {
558+
return executeSyncWorkflows(0, dir)
559+
}
540560

541-
existingWorkflow.unload()
542-
},
543-
/* exclude */ (workflow) => workflow.isTemporary
544-
)
561+
async function loadWorkflows() {
562+
if (!isSyncReady.value && !isSyncLoading.value) {
563+
return syncWorkflows()
564+
}
545565
}
546566

547567
const bookmarkStore = useWorkflowBookmarkStore()
@@ -849,6 +869,7 @@ export const useWorkflowStore = defineStore('workflow', () => {
849869
modifiedWorkflows,
850870
getWorkflowByPath,
851871
syncWorkflows,
872+
loadWorkflows,
852873

853874
isSubgraphActive,
854875
activeSubgraph,

src/stores/bootstrapStore.test.ts

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,10 @@ vi.mock('@/platform/settings/settingStore', () => ({
3333
}))
3434
}))
3535

36-
vi.mock('@/stores/workspaceStore', () => ({
37-
useWorkspaceStore: vi.fn(() => ({
38-
workflow: {
39-
syncWorkflows: vi.fn().mockResolvedValue(undefined)
40-
}
36+
vi.mock('@/platform/workflow/management/stores/workflowStore', () => ({
37+
useWorkflowStore: vi.fn(() => ({
38+
loadWorkflows: vi.fn(),
39+
syncWorkflows: vi.fn().mockResolvedValue(undefined)
4140
}))
4241
}))
4342

@@ -53,23 +52,10 @@ describe('bootstrapStore', () => {
5352

5453
it('initializes with all flags false', () => {
5554
const settingStore = useSettingStore()
56-
expect(store.isNodeDefsReady).toBe(false)
5755
expect(settingStore.isReady).toBe(false)
5856
expect(store.isI18nReady).toBe(false)
5957
})
6058

61-
it('starts early bootstrap (node defs)', async () => {
62-
const { api } = await import('@/scripts/api')
63-
64-
store.startEarlyBootstrap()
65-
66-
await vi.waitFor(() => {
67-
expect(store.isNodeDefsReady).toBe(true)
68-
})
69-
70-
expect(api.getNodeDefs).toHaveBeenCalled()
71-
})
72-
7359
it('starts store bootstrap (settings, i18n)', async () => {
7460
const settingStore = useSettingStore()
7561
void store.startStoreBootstrap()

src/stores/bootstrapStore.ts

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,13 @@ import { useAsyncState } from '@vueuse/core'
22
import { defineStore } from 'pinia'
33

44
import { useSettingStore } from '@/platform/settings/settingStore'
5-
import type { ComfyNodeDef } from '@/schemas/nodeDefSchema'
5+
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
66
import { api } from '@/scripts/api'
77
import { useUserStore } from '@/stores/userStore'
88

99
export const useBootstrapStore = defineStore('bootstrap', () => {
1010
const settingStore = useSettingStore()
11-
12-
const {
13-
state: nodeDefs,
14-
isReady: isNodeDefsReady,
15-
error: nodeDefsError,
16-
execute: fetchNodeDefs
17-
} = useAsyncState<Record<string, ComfyNodeDef>>(
18-
async () => {
19-
const defs = await api.getNodeDefs()
20-
return defs
21-
},
22-
{},
23-
{ immediate: false }
24-
)
11+
const workflowStore = useWorkflowStore()
2512

2613
const {
2714
isReady: isI18nReady,
@@ -37,28 +24,7 @@ export const useBootstrapStore = defineStore('bootstrap', () => {
3724
{ immediate: false }
3825
)
3926

40-
const {
41-
isReady: isWorkflowsReady,
42-
isLoading: isWorkflowsLoading,
43-
execute: executeSyncWorkflows
44-
} = useAsyncState(
45-
async () => {
46-
const { useWorkspaceStore } = await import('@/stores/workspaceStore')
47-
await useWorkspaceStore().workflow.syncWorkflows()
48-
},
49-
undefined,
50-
{ immediate: false }
51-
)
52-
53-
function syncWorkflows() {
54-
if (!isWorkflowsReady.value && !isWorkflowsLoading.value) {
55-
void executeSyncWorkflows()
56-
}
57-
}
58-
59-
function startEarlyBootstrap() {
60-
void fetchNodeDefs()
61-
}
27+
function startEarlyBootstrap() {}
6228

6329
async function startStoreBootstrap() {
6430
// Defer settings and workflows if multi-user login is required
@@ -71,14 +37,11 @@ export const useBootstrapStore = defineStore('bootstrap', () => {
7137

7238
if (!userStore.needsLogin) {
7339
await settingStore.load()
74-
syncWorkflows()
40+
await workflowStore.loadWorkflows()
7541
}
7642
}
7743

7844
return {
79-
nodeDefs,
80-
isNodeDefsReady,
81-
nodeDefsError,
8245
isI18nReady,
8346
i18nError,
8447
startEarlyBootstrap,

0 commit comments

Comments
 (0)