mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix: debounce sync on subblock and dropdown zustand load
This commit is contained in:
+32
-14
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo } from 'react'
|
||||
import { useMemo } from 'react'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -18,22 +18,13 @@ interface DropdownProps {
|
||||
}
|
||||
|
||||
export function Dropdown({ options, defaultValue, blockId, subBlockId }: DropdownProps) {
|
||||
const [value, setValue] = useSubBlockValue(blockId, subBlockId, true)
|
||||
const [value, setValue] = useSubBlockValue<string>(blockId, subBlockId, true)
|
||||
|
||||
// Evaluate options if it's a function
|
||||
const evaluatedOptions = useMemo(() => {
|
||||
return typeof options === 'function' ? options() : options
|
||||
}, [options])
|
||||
|
||||
// Set the value to the first option if it's not set
|
||||
useEffect(() => {
|
||||
if (!value && evaluatedOptions.length > 0) {
|
||||
const firstOption = evaluatedOptions[0]
|
||||
const firstValue = typeof firstOption === 'string' ? firstOption : firstOption.id
|
||||
setValue(firstValue)
|
||||
}
|
||||
}, [value, evaluatedOptions, defaultValue, setValue])
|
||||
|
||||
const getOptionValue = (option: string | { label: string; id: string }) => {
|
||||
return typeof option === 'string' ? option : option.id
|
||||
}
|
||||
@@ -42,11 +33,38 @@ export function Dropdown({ options, defaultValue, blockId, subBlockId }: Dropdow
|
||||
return typeof option === 'string' ? option : option.label
|
||||
}
|
||||
|
||||
// Calculate the effective value to use in the dropdown
|
||||
// Priority: 1. Stored value (value) > 2. Provided defaultValue > 3. First option
|
||||
const effectiveValue = useMemo(() => {
|
||||
// If we have a value from the store, use that
|
||||
if (value !== null && value !== undefined) {
|
||||
return value
|
||||
}
|
||||
|
||||
// Fall back to provided defaultValue
|
||||
if (defaultValue !== undefined) {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// Last resort: use first option value if available
|
||||
if (evaluatedOptions.length > 0) {
|
||||
return getOptionValue(evaluatedOptions[0])
|
||||
}
|
||||
|
||||
// No valid value available
|
||||
return undefined
|
||||
}, [value, defaultValue, evaluatedOptions])
|
||||
|
||||
// Handle the case where evaluatedOptions changes and the current selection is no longer valid
|
||||
const isValueInOptions = useMemo(() => {
|
||||
if (!effectiveValue || evaluatedOptions.length === 0) return false
|
||||
return evaluatedOptions.some((opt) => getOptionValue(opt) === effectiveValue)
|
||||
}, [effectiveValue, evaluatedOptions])
|
||||
|
||||
return (
|
||||
<Select
|
||||
value={value as string | undefined}
|
||||
defaultValue={defaultValue ?? getOptionValue(evaluatedOptions[0])}
|
||||
onValueChange={(value) => setValue(value)}
|
||||
value={isValueInOptions ? effectiveValue : undefined}
|
||||
onValueChange={(newValue) => setValue(newValue)}
|
||||
>
|
||||
<SelectTrigger className="text-left">
|
||||
<SelectValue placeholder="Select an option" />
|
||||
|
||||
+2
@@ -57,6 +57,8 @@ export function useSubBlockValue<T = any>(
|
||||
? null
|
||||
: (typeof newValue === 'object' ? JSON.parse(JSON.stringify(newValue)) : newValue)
|
||||
|
||||
// Update the subblock store with the new value
|
||||
// The store's setValue method will now trigger the debounced sync automatically
|
||||
useSubBlockStore.getState().setValue(blockId, subBlockId, valueCopy)
|
||||
|
||||
if (triggerWorkflowUpdate) {
|
||||
|
||||
@@ -3,6 +3,11 @@ import { devtools, persist } from 'zustand/middleware'
|
||||
import { SubBlockConfig } from '@/blocks/types'
|
||||
import { loadSubblockValues, saveSubblockValues } from '../persistence'
|
||||
import { useWorkflowRegistry } from '../registry/store'
|
||||
import { workflowSync } from '../sync'
|
||||
|
||||
// Add debounce utility for syncing
|
||||
let syncDebounceTimer: NodeJS.Timeout | null = null
|
||||
const DEBOUNCE_DELAY = 500 // 500ms delay for sync
|
||||
|
||||
/**
|
||||
* SubBlockState stores values for all subblocks in workflows
|
||||
@@ -24,6 +29,8 @@ interface SubBlockStore extends SubBlockState {
|
||||
getValue: (blockId: string, subBlockId: string) => any
|
||||
clear: () => void
|
||||
initializeFromWorkflow: (workflowId: string, blocks: Record<string, any>) => void
|
||||
// Add debounced sync function
|
||||
syncWithDB: () => void
|
||||
}
|
||||
|
||||
export const useSubBlockStore = create<SubBlockStore>()(
|
||||
@@ -52,6 +59,9 @@ export const useSubBlockStore = create<SubBlockStore>()(
|
||||
// Persist to localStorage for backup
|
||||
const currentValues = get().workflowValues[activeWorkflowId] || {}
|
||||
saveSubblockValues(activeWorkflowId, currentValues)
|
||||
|
||||
// Trigger debounced sync to DB
|
||||
get().syncWithDB()
|
||||
},
|
||||
|
||||
getValue: (blockId: string, subBlockId: string) => {
|
||||
@@ -73,6 +83,9 @@ export const useSubBlockStore = create<SubBlockStore>()(
|
||||
}))
|
||||
|
||||
saveSubblockValues(activeWorkflowId, {})
|
||||
|
||||
// Trigger sync to DB immediately on clear
|
||||
workflowSync.sync()
|
||||
},
|
||||
|
||||
initializeFromWorkflow: (workflowId: string, blocks: Record<string, any>) => {
|
||||
@@ -108,6 +121,20 @@ export const useSubBlockStore = create<SubBlockStore>()(
|
||||
// Save to localStorage
|
||||
saveSubblockValues(workflowId, values)
|
||||
},
|
||||
|
||||
// Debounced sync function to trigger DB sync
|
||||
syncWithDB: () => {
|
||||
// Clear any existing timeout
|
||||
if (syncDebounceTimer) {
|
||||
clearTimeout(syncDebounceTimer)
|
||||
}
|
||||
|
||||
// Set new timeout
|
||||
syncDebounceTimer = setTimeout(() => {
|
||||
// Trigger workflow sync to DB
|
||||
workflowSync.sync()
|
||||
}, DEBOUNCE_DELAY)
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: 'subblock-store',
|
||||
|
||||
Reference in New Issue
Block a user