@@ -8,9 +8,18 @@ import {
8
8
ModalHeader ,
9
9
ModalOverlay ,
10
10
} from '@invoke-ai/ui-library' ;
11
+ import { useStore } from '@nanostores/react' ;
12
+ import { useAppDispatch , useAppSelector } from 'app/store/storeHooks' ;
13
+ import { IAINoContentFallback } from 'common/components/IAIImageFallback' ;
11
14
import { useWorkflowLibraryModal } from 'features/nodes/store/workflowLibraryModal' ;
12
- import { memo } from 'react' ;
15
+ import {
16
+ $workflowLibraryCategoriesOptions ,
17
+ selectWorkflowLibraryView ,
18
+ workflowLibraryViewChanged ,
19
+ } from 'features/nodes/store/workflowLibrarySlice' ;
20
+ import { memo , useEffect , useMemo , useState } from 'react' ;
13
21
import { useTranslation } from 'react-i18next' ;
22
+ import { useGetCountsByCategoryQuery } from 'services/api/endpoints/workflows' ;
14
23
15
24
import { WorkflowLibrarySideNav } from './WorkflowLibrarySideNav' ;
16
25
import { WorkflowLibraryTopNav } from './WorkflowLibraryTopNav' ;
@@ -19,6 +28,7 @@ import { WorkflowList } from './WorkflowList';
19
28
export const WorkflowLibraryModal = memo ( ( ) => {
20
29
const { t } = useTranslation ( ) ;
21
30
const workflowLibraryModal = useWorkflowLibraryModal ( ) ;
31
+ const didSync = useSyncInitialWorkflowLibraryCategories ( ) ;
22
32
return (
23
33
< Modal isOpen = { workflowLibraryModal . isOpen } onClose = { workflowLibraryModal . close } isCentered >
24
34
< ModalOverlay />
@@ -31,18 +41,102 @@ export const WorkflowLibraryModal = memo(() => {
31
41
< ModalHeader > { t ( 'workflows.workflowLibrary' ) } </ ModalHeader >
32
42
< ModalCloseButton />
33
43
< ModalBody pb = { 6 } >
34
- < Flex gap = { 4 } h = "100%" >
35
- < WorkflowLibrarySideNav />
36
- < Divider orientation = "vertical" />
37
- < Flex flexDir = "column" flex = { 1 } gap = { 4 } >
38
- < WorkflowLibraryTopNav />
39
- < WorkflowList />
44
+ { didSync && (
45
+ < Flex gap = { 4 } h = "100%" >
46
+ < WorkflowLibrarySideNav />
47
+ < Divider orientation = "vertical" />
48
+ < Flex flexDir = "column" flex = { 1 } gap = { 4 } >
49
+ < WorkflowLibraryTopNav />
50
+ < WorkflowList />
51
+ </ Flex >
40
52
</ Flex >
41
- </ Flex >
53
+ ) }
54
+ { ! didSync && < IAINoContentFallback label = { t ( 'workflows.loading' ) } icon = { null } /> }
42
55
</ ModalBody >
43
56
</ ModalContent >
44
57
</ Modal >
45
58
) ;
46
59
} ) ;
47
60
48
61
WorkflowLibraryModal . displayName = 'WorkflowLibraryModal' ;
62
+
63
+ /**
64
+ * On first app load, if the user's selected view has no workflows, switches to the next available view.
65
+ */
66
+ const useSyncInitialWorkflowLibraryCategories = ( ) => {
67
+ const dispatch = useAppDispatch ( ) ;
68
+ const view = useAppSelector ( selectWorkflowLibraryView ) ;
69
+ const categoryOptions = useStore ( $workflowLibraryCategoriesOptions ) ;
70
+ const [ didSync , setDidSync ] = useState ( false ) ;
71
+ const recentWorkflowsCountQueryArg = useMemo (
72
+ ( ) =>
73
+ ( {
74
+ categories : [ 'user' , 'project' , 'default' ] ,
75
+ has_been_opened : true ,
76
+ } ) satisfies Parameters < typeof useGetCountsByCategoryQuery > [ 0 ] ,
77
+ [ ]
78
+ ) ;
79
+ const yourWorkflowsCountQueryArg = useMemo (
80
+ ( ) =>
81
+ ( {
82
+ categories : [ 'user' , 'project' ] ,
83
+ } ) satisfies Parameters < typeof useGetCountsByCategoryQuery > [ 0 ] ,
84
+ [ ]
85
+ ) ;
86
+ const queryOptions = useMemo (
87
+ ( ) =>
88
+ ( {
89
+ selectFromResult : ( { data, isLoading } ) => {
90
+ if ( ! data ) {
91
+ return { count : 0 , isLoading : true } ;
92
+ }
93
+ return {
94
+ count : Object . values ( data ) . reduce ( ( acc , count ) => acc + count , 0 ) ,
95
+ isLoading,
96
+ } ;
97
+ } ,
98
+ } ) satisfies Parameters < typeof useGetCountsByCategoryQuery > [ 1 ] ,
99
+ [ ]
100
+ ) ;
101
+
102
+ const { count : recentWorkflowsCount , isLoading : isLoadingRecentWorkflowsCount } = useGetCountsByCategoryQuery (
103
+ recentWorkflowsCountQueryArg ,
104
+ queryOptions
105
+ ) ;
106
+ const { count : yourWorkflowsCount , isLoading : isLoadingYourWorkflowsCount } = useGetCountsByCategoryQuery (
107
+ yourWorkflowsCountQueryArg ,
108
+ queryOptions
109
+ ) ;
110
+
111
+ useEffect ( ( ) => {
112
+ if ( didSync || isLoadingRecentWorkflowsCount || isLoadingYourWorkflowsCount ) {
113
+ return ;
114
+ }
115
+ // If the user's selected view has no workflows, switch to the next available view
116
+ if ( recentWorkflowsCount === 0 && view === 'recent' ) {
117
+ if ( yourWorkflowsCount > 0 ) {
118
+ dispatch ( workflowLibraryViewChanged ( 'yours' ) ) ;
119
+ } else {
120
+ dispatch ( workflowLibraryViewChanged ( 'defaults' ) ) ;
121
+ }
122
+ } else if ( yourWorkflowsCount === 0 && ( view === 'yours' || view === 'shared' || view === 'private' ) ) {
123
+ if ( recentWorkflowsCount > 0 ) {
124
+ dispatch ( workflowLibraryViewChanged ( 'recent' ) ) ;
125
+ } else {
126
+ dispatch ( workflowLibraryViewChanged ( 'defaults' ) ) ;
127
+ }
128
+ }
129
+ setDidSync ( true ) ;
130
+ } , [
131
+ categoryOptions ,
132
+ didSync ,
133
+ dispatch ,
134
+ isLoadingRecentWorkflowsCount ,
135
+ isLoadingYourWorkflowsCount ,
136
+ recentWorkflowsCount ,
137
+ view ,
138
+ yourWorkflowsCount ,
139
+ ] ) ;
140
+
141
+ return didSync ;
142
+ } ;
0 commit comments