Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/sim/app/workspace/[workspaceId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client'

import { CustomToolsLoader } from '@/app/workspace/[workspaceId]/providers/custom-tools-loader'
import { GlobalCommandsProvider } from '@/app/workspace/[workspaceId]/providers/global-commands-provider'
import { ProviderModelsLoader } from '@/app/workspace/[workspaceId]/providers/provider-models-loader'
import { SettingsLoader } from '@/app/workspace/[workspaceId]/providers/settings-loader'
Expand All @@ -11,6 +12,7 @@ export default function WorkspaceLayout({ children }: { children: React.ReactNod
<>
<SettingsLoader />
<ProviderModelsLoader />
<CustomToolsLoader />
<GlobalCommandsProvider>
<div className='flex h-screen w-full bg-[var(--bg)]'>
<WorkspacePermissionsProvider>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
'use client'

import { useEffect, useRef } from 'react'
import { useParams } from 'next/navigation'
import { syncCustomToolsToStore, useCustomTools } from '@/hooks/queries/custom-tools'

/**
* Loads custom tools from database and syncs to Zustand store once per workspace.
* This ensures custom tools are available for non-React code (executor handlers, utilities)
* that access the store via getState().
*/
export function CustomToolsLoader() {
const params = useParams()
const workspaceId = params.workspaceId as string
const lastSyncedDataRef = useRef<string | null>(null)

const { data: customTools } = useCustomTools(workspaceId)

useEffect(() => {
if (!customTools) return

// Only sync if data has actually changed (compare by JSON to handle array reference changes)
const dataKey = JSON.stringify(customTools.map((t) => t.id).sort())
if (dataKey === lastSyncedDataRef.current) return
Comment thread
waleedlatif1 marked this conversation as resolved.
Outdated

lastSyncedDataRef.current = dataKey
syncCustomToolsToStore(customTools)
}, [customTools])

return null
}
10 changes: 2 additions & 8 deletions apps/sim/hooks/queries/custom-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ function normalizeCustomTool(tool: ApiCustomTool, workspaceId: string): CustomTo
}
}

function syncCustomToolsToStore(tools: CustomToolDefinition[]) {
export function syncCustomToolsToStore(tools: CustomToolDefinition[]) {
useCustomToolsStore.getState().setTools(tools)
}

Expand Down Expand Up @@ -134,19 +134,13 @@ async function fetchCustomTools(workspaceId: string): Promise<CustomToolDefiniti
* Hook to fetch custom tools
*/
export function useCustomTools(workspaceId: string) {
const query = useQuery<CustomToolDefinition[]>({
return useQuery<CustomToolDefinition[]>({
queryKey: customToolsKeys.list(workspaceId),
queryFn: () => fetchCustomTools(workspaceId),
enabled: !!workspaceId,
staleTime: 60 * 1000, // 1 minute - tools don't change frequently
placeholderData: keepPreviousData,
})

if (query.data) {
syncCustomToolsToStore(query.data)
}

return query
}

/**
Expand Down
32 changes: 0 additions & 32 deletions apps/sim/hooks/use-collaborative-workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,6 @@ export function useCollaborativeWorkflow() {
const {
isConnected,
currentWorkflowId,
presenceUsers,
joinWorkflow,
leaveWorkflow,
emitWorkflowOperation,
emitSubblockUpdate,
emitVariableUpdate,
Expand Down Expand Up @@ -143,13 +140,7 @@ export function useCollaborativeWorkflow() {
// Track if we're applying remote changes to avoid infinite loops
const isApplyingRemoteChange = useRef(false)

// Track last applied position timestamps to prevent out-of-order updates
const lastPositionTimestamps = useRef<Map<string, number>>(new Map())

// Operation queue
const {
queue,
hasOperationError,
addToQueue,
confirmOperation,
failOperation,
Expand All @@ -161,22 +152,6 @@ export function useCollaborativeWorkflow() {
return !!currentWorkflowId && activeWorkflowId === currentWorkflowId
}, [currentWorkflowId, activeWorkflowId])

// Clear position timestamps when switching workflows
// Note: Workflow joining is now handled automatically by socket connect event based on URL
useEffect(() => {
if (activeWorkflowId && currentWorkflowId !== activeWorkflowId) {
logger.info(`Active workflow changed to: ${activeWorkflowId}`, {
isConnected,
currentWorkflowId,
activeWorkflowId,
presenceUsers: presenceUsers.length,
})

// Clear position timestamps when switching workflows
lastPositionTimestamps.current.clear()
}
}, [activeWorkflowId, isConnected, currentWorkflowId])

// Register emit functions with operation queue store
useEffect(() => {
registerEmitFunctions(
Expand Down Expand Up @@ -1621,15 +1596,8 @@ export function useCollaborativeWorkflow() {
)

return {
// Connection status
isConnected,
currentWorkflowId,
presenceUsers,
hasOperationError,

// Workflow management
joinWorkflow,
leaveWorkflow,

// Collaborative operations
collaborativeBatchUpdatePositions,
Expand Down