11import { useCallback , useMemo } from 'react'
2+ import { useQueries } from '@tanstack/react-query'
23import {
34 buildCanonicalIndex ,
45 evaluateSubBlockCondition ,
@@ -7,13 +8,18 @@ import {
78 isSubBlockVisibleForMode ,
89} from '@/lib/workflows/subblocks/visibility'
910import type { BlockConfig , SubBlockConfig , SubBlockType } from '@/blocks/types'
10- import { useWorkspaceCredential } from '@/hooks/queries/credentials'
11+ import { type WorkspaceCredential , workspaceCredentialKeys } from '@/hooks/queries/credentials'
12+ import { fetchJson } from '@/hooks/selectors/helpers'
1113import { usePermissionConfig } from '@/hooks/use-permission-config'
1214import { useWorkflowDiffStore } from '@/stores/workflow-diff'
1315import { useSubBlockStore } from '@/stores/workflows/subblock/store'
1416import { mergeSubblockState } from '@/stores/workflows/utils'
1517import { useWorkflowStore } from '@/stores/workflows/workflow/store'
1618
19+ interface CredentialResponse {
20+ credential ?: WorkspaceCredential | null
21+ }
22+
1723/**
1824 * Evaluates reactive conditions for subblocks. Always calls the same hooks
1925 * regardless of whether a reactive condition exists (Rules of Hooks).
@@ -26,45 +32,86 @@ function useReactiveConditions(
2632 activeWorkflowId : string | null ,
2733 blockSubBlockValues : Record < string , unknown >
2834) : Set < string > {
29- const reactiveSubBlock = useMemo (
30- ( ) => subBlocks . find ( ( sb ) => sb . reactiveCondition ) ,
35+ const reactiveSubBlocks = useMemo (
36+ ( ) => subBlocks . filter ( ( subBlock ) => subBlock . reactiveCondition ) ,
3137 [ subBlocks ]
3238 )
33- const reactiveCond = reactiveSubBlock ?. reactiveCondition
3439
3540 // Subscribe to watched field values — always called (stable hook count)
36- const watchedCredentialId = useSubBlockStore (
41+ const watchedCredentialIdsBySubBlock = useSubBlockStore (
3742 useCallback (
3843 ( state ) => {
39- if ( ! reactiveCond || ! activeWorkflowId ) return ''
44+ if ( ! activeWorkflowId || reactiveSubBlocks . length === 0 ) return { }
4045 const blockValues = state . workflowValues [ activeWorkflowId ] ?. [ blockId ] ?? { }
4146 const merged = { ...blockSubBlockValues , ...blockValues }
42- for ( const field of reactiveCond . watchFields ) {
43- const val = merged [ field ]
44- if ( val && typeof val === 'string' ) return val
45- }
46- return ''
47+ return reactiveSubBlocks . reduce < Record < string , string > > ( ( acc , subBlock ) => {
48+ const reactiveCondition = subBlock . reactiveCondition
49+ if ( ! reactiveCondition ) return acc
50+
51+ for ( const field of reactiveCondition . watchFields ) {
52+ const value = merged [ field ]
53+ if ( value && typeof value === 'string' ) {
54+ acc [ subBlock . id ] = value
55+ break
56+ }
57+ }
58+
59+ return acc
60+ } , { } )
4761 } ,
48- [ reactiveCond , activeWorkflowId , blockId , blockSubBlockValues ]
62+ [ activeWorkflowId , blockId , blockSubBlockValues , reactiveSubBlocks ]
4963 )
5064 )
5165
52- // Always call useWorkspaceCredential (stable hook count), disable when not needed
53- const { data : credential } = useWorkspaceCredential (
54- watchedCredentialId || undefined ,
55- Boolean ( reactiveCond && watchedCredentialId )
66+ const watchedCredentialIds = useMemo (
67+ ( ) => Array . from ( new Set ( Object . values ( watchedCredentialIdsBySubBlock ) ) ) ,
68+ [ watchedCredentialIdsBySubBlock ]
5669 )
5770
71+ const credentialQueries = useQueries ( {
72+ queries : watchedCredentialIds . map ( ( credentialId ) => ( {
73+ queryKey : workspaceCredentialKeys . detail ( credentialId ) ,
74+ queryFn : async ( { signal } : { signal : AbortSignal } ) => {
75+ const data = await fetchJson < CredentialResponse > ( `/api/credentials/${ credentialId } ` , {
76+ signal,
77+ } )
78+ return data . credential ?? null
79+ } ,
80+ enabled : Boolean ( credentialId ) && reactiveSubBlocks . length > 0 ,
81+ staleTime : 60 * 1000 ,
82+ } ) ) ,
83+ } )
84+
85+ const credentialTypeById = useMemo ( ( ) => {
86+ const typeById = new Map < string , WorkspaceCredential [ 'type' ] > ( )
87+ watchedCredentialIds . forEach ( ( credentialId , index ) => {
88+ const credential = credentialQueries [ index ] ?. data
89+ if ( credential ?. type ) {
90+ typeById . set ( credentialId , credential . type )
91+ }
92+ } )
93+ return typeById
94+ } , [ credentialQueries , watchedCredentialIds ] )
95+
5896 return useMemo ( ( ) => {
5997 const hidden = new Set < string > ( )
60- if ( ! reactiveSubBlock || ! reactiveCond ) return hidden
98+ if ( reactiveSubBlocks . length === 0 ) return hidden
6199
62- const conditionMet = credential ?. type === reactiveCond . requiredType
63- if ( ! conditionMet ) {
64- hidden . add ( reactiveSubBlock . id )
100+ for ( const subBlock of reactiveSubBlocks ) {
101+ const reactiveCondition = subBlock . reactiveCondition
102+ if ( ! reactiveCondition ) continue
103+
104+ const watchedCredentialId = watchedCredentialIdsBySubBlock [ subBlock . id ]
105+ const credentialType = watchedCredentialId
106+ ? credentialTypeById . get ( watchedCredentialId )
107+ : undefined
108+ if ( credentialType !== reactiveCondition . requiredType ) {
109+ hidden . add ( subBlock . id )
110+ }
65111 }
112+
66113 return hidden
67- } , [ reactiveSubBlock , reactiveCond , credential ?. type ] )
114+ } , [ credentialTypeById , reactiveSubBlocks , watchedCredentialIdsBySubBlock ] )
68115}
69116
70117/**
0 commit comments