diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index ed8930c16..54458cc01 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -35,7 +35,6 @@ import { APIClassType } from "../../../../types/api"; import { ParameterComponentType } from "../../../../types/components"; import { NodeDataType } from "../../../../types/flow"; import { - cleanEdges, convertObjToArray, convertValuesToNumbers, hasDuplicateKeys, @@ -71,8 +70,15 @@ export default function ParameterComponent({ const { setErrorData, modalContextOpen } = useContext(alertContext); const updateNodeInternals = useUpdateNodeInternals(); const [position, setPosition] = useState(0); - const { setTabsState, tabId, flows, tabsState, updateFlow, nodes, edges, setEdges } = - useContext(FlowsContext); + const { + tabId, + flows, + updateFlow, + nodes, + edges, + setEdges, + setNode, + } = useContext(FlowsContext); const [dataRef, setDataRef] = useState(data); @@ -98,12 +104,10 @@ export default function ParameterComponent({ const { setFilterEdge } = useContext(typesContext); let disabled = - edges - .some( - (edge) => - edge.targetHandle === - scapedJSONStringfy(proxy ? { ...id, proxy } : id) - ) ?? false; + edges.some( + (edge) => + edge.targetHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id) + ) ?? false; const { data: myData } = useContext(typesContext); @@ -132,68 +136,50 @@ export default function ParameterComponent({ if (data.node!.template[name].value !== newValue) { takeSnapshot(); } - data.node!.template[name].value = newValue; - updateNodeInternals(data.id); + + setNode(data.id, (oldNode) => { + let newNode = cloneDeep(oldNode); + + newNode.data = { + ...newNode.data + } + + newNode.data.node.template[name].value = newValue; + + return newNode; + }); setDataRef((old) => { let newData = cloneDeep(old); newData.node!.template[name].value = newValue; return newData; }); - - // Set state to pending - //@ts-ignore - if (data.node!.template[name].value !== newValue) { - const tabs = cloneDeep(tabsState); - tabs[tabId].isPending = false; - tabs[tabId].formKeysData = tabsState[tabId].formKeysData; - setTabsState({ - ...tabs, - }); - } + renderTooltips(); }; const handleNodeClass = (newNodeClass: APIClassType, code?: string): void => { if (!data.node) return; - if (data.node!.template[name].value !== newNodeClass.template[name].value) { + if (data.node!.template[name].value !== code) { takeSnapshot(); } - data.node! = { - ...newNodeClass, - description: newNodeClass.description ?? data.node!.description, - display_name: newNodeClass.display_name ?? data.node!.display_name, - }; - data.node!.template[name].value = code; - updateNodeInternals(data.id); - // Set state to pending - //@ts-ignore - if (data.node!.template[name].value !== code) { - const tabs = cloneDeep(tabsState); - tabs[tabId].isPending = false; - tabs[tabId].formKeysData = tabsState[tabId].formKeysData; - setTabsState({ - ...tabs, - }); - } + + setNode(data.id, (oldNode) => { + let newNode = cloneDeep(oldNode); + + newNode.data = { + ...newNode.data, + node: newNodeClass, + description: newNodeClass.description ?? data.node!.description, + display_name: newNodeClass.display_name ?? data.node!.display_name, + }; + + newNode.data.node.template[name].value = code; + + return newNode; + }); + renderTooltips(); - let flow = flows.find((flow) => flow.id === tabId); - setTimeout(() => { - //timeout necessary because ReactFlow updates are not async - if (flow && flow.data) { - cleanEdges({ - flow: { - edges: flow.data!.edges, - nodes: flow.data!.nodes, - }, - updateEdge: (edge) => { - setEdges(edge); - updateNodeInternals(data.id); - }, - }); - updateFlow(flow); - } - }, 50); }; const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); diff --git a/src/frontend/src/contexts/flowsContext.tsx b/src/frontend/src/contexts/flowsContext.tsx index 974ce6595..955f32fb3 100644 --- a/src/frontend/src/contexts/flowsContext.tsx +++ b/src/frontend/src/contexts/flowsContext.tsx @@ -43,6 +43,7 @@ import { FlowsContextType, FlowsState } from "../types/tabs"; import { addVersionToDuplicates, checkOldEdgesHandles, + cleanEdges, createFlowComponent, removeFileNameFromComponents, scapeJSONParse, @@ -85,6 +86,8 @@ const FlowsContextInitialValue: FlowsContextType = { saveFlow: async (flow: FlowType, silent?: boolean) => {}, lastCopiedSelection: null, setLastCopiedSelection: (selection: any) => {}, + isPending: false, + setPending: (pending: boolean) => {}, tabsState: {}, setTabsState: (state: FlowsState) => {}, saveCurrentFlow: () => {}, @@ -100,6 +103,8 @@ const FlowsContextInitialValue: FlowsContextType = { version: "", nodes: [], setNodes: () => {}, + setNode: () => {}, + getNode: () => undefined, onNodesChange: () => {}, edges: [], setEdges: () => {}, @@ -111,8 +116,7 @@ export const FlowsContext = createContext( ); export function FlowsProvider({ children }: { children: ReactNode }) { - const { setErrorData, setSuccessData } = - useContext(alertContext); + const { setErrorData, setSuccessData } = useContext(alertContext); const { getAuthentication, isAuthenticated } = useContext(AuthContext); const [tabId, setTabId] = useState(""); @@ -131,41 +135,67 @@ export function FlowsProvider({ children }: { children: ReactNode }) { const [edges, setEdgesInternal, onEdgesChangeInternal] = useEdgesState([]); - const onNodesChange = useCallback((nodes: NodeChange[]) => { - onNodesChangeInternal(nodes); - //@ts-ignore - setTabsState((prev: FlowsState) => { - return { - ...prev, - [tabId]: { - ...prev[tabId], - isPending: true, - }, - }; - }); - //SAVE FLOW IN FLOWS - }, [onNodesChangeInternal, setTabsState, tabId]); - - const onEdgesChange = useCallback( - (edges: EdgeChange[]) => { - onEdgesChangeInternal(edges); + const setPending = useCallback( + (pending: boolean) => { //@ts-ignore setTabsState((prev: FlowsState) => { return { ...prev, [tabId]: { ...prev[tabId], - isPending: true, + isPending: pending, }, }; }); }, + [setTabsState] + ); + + const isPending = tabsState[tabId]?.isPending ?? false; + + const onNodesChange = useCallback( + (nodes: NodeChange[]) => { + onNodesChangeInternal(nodes); + console.log("nodesChangou") + + setPending(true); + }, + [onNodesChangeInternal, setTabsState, tabId] + ); + + const onEdgesChange = useCallback( + (edges: EdgeChange[]) => { + onEdgesChangeInternal(edges); + console.log("edgesChangou") + setPending(true); + }, [onEdgesChangeInternal, setTabsState, tabId] ); - const setNodes = (nodes: Node[] | ((oldState: Node[]) => Node[])) => { - setNodesInternal(nodes) - } + const setNodes = (change: Node[] | ((oldState: Node[]) => Node[])) => { + let newChange = typeof change === "function" ? change(nodes) : change; + + + setEdgesInternal(cleanEdges(newChange, edges)); + setNodesInternal(newChange); + }; + + const setNode = (id: string, change: Node | ((oldState: Node) => Node)) => { + let newChange = typeof change === "function" ? change(nodes.find((node) => node.id === id)!) : change; + + setNodes((oldNodes) => + oldNodes.map((node) => { + if (node.id === id) { + return newChange; + } + return node; + }) + ); + }; + + const getNode = (id: string) => { + return nodes.find((node) => node.id === id); + }; const setEdges = (edges: Edge[] | ((oldState: Edge[]) => Edge[])) => { setEdgesInternal(edges); @@ -787,6 +817,8 @@ export function FlowsProvider({ children }: { children: ReactNode }) { uploadFlows, uploadFlow, getNodeId, + isPending, + setPending, tabsState, setTabsState, paste, @@ -797,6 +829,8 @@ export function FlowsProvider({ children }: { children: ReactNode }) { deleteComponent, nodes, setNodes, + setNode, + getNode, onNodesChange, edges, setEdges, diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index ed1700be4..d1a5a2c2d 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -228,10 +228,6 @@ export default function Page({ eds ) ); - setNodes((node) => { - let newX = _.cloneDeep(node); - return newX; - }); //@ts-ignore setTabsState((prev: FlowsState) => { return { @@ -244,7 +240,7 @@ export default function Page({ }); saveCurrentFlowTimeout(); }, - [setEdges, setNodes, takeSnapshot, addEdge] + [setEdges, takeSnapshot, addEdge] ); const onNodeDragStart: NodeDragHandler = useCallback(() => { diff --git a/src/frontend/src/types/tabs/index.ts b/src/frontend/src/types/tabs/index.ts index 732ddbb80..41b6726ec 100644 --- a/src/frontend/src/types/tabs/index.ts +++ b/src/frontend/src/types/tabs/index.ts @@ -43,6 +43,8 @@ export type FlowsContextType = { }) => Promise; hardReset: () => void; getNodeId: (nodeType: string) => string; + isPending: boolean; + setPending: (pending: boolean) => void; tabsState: FlowsState; setTabsState: (state: FlowsState) => void; paste: ( @@ -61,6 +63,8 @@ export type FlowsContextType = { version: string; nodes: Array; setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void; + setNode: (id: string, update: Node | ((oldState: Node) => Node)) => void; + getNode: (id: string) => Node | undefined; onNodesChange: OnChange; edges: Array; setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void; diff --git a/src/frontend/src/types/utils/reactflowUtils.ts b/src/frontend/src/types/utils/reactflowUtils.ts index 627c5dc3a..fda6b75a8 100644 --- a/src/frontend/src/types/utils/reactflowUtils.ts +++ b/src/frontend/src/types/utils/reactflowUtils.ts @@ -1,14 +1,6 @@ import { Edge, Node } from "reactflow"; import { FlowType, NodeType } from "../flow"; -export type cleanEdgesType = { - flow: { - edges: Edge[]; - nodes: NodeType[]; - }; - updateEdge: (edge: Edge[]) => void; -}; - export type unselectAllNodesType = { updateNodes: (nodes: Node[]) => void; data: Node[]; diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts index ea99c12a4..4f6fb7bc6 100644 --- a/src/frontend/src/utils/reactflowUtils.ts +++ b/src/frontend/src/utils/reactflowUtils.ts @@ -22,7 +22,6 @@ import { targetHandleType, } from "../types/flow"; import { - cleanEdgesType, findLastNodeType, generateFlowType, unselectAllNodesType, @@ -30,10 +29,7 @@ import { } from "../types/utils/reactflowUtils"; import { getFieldTitle, toTitleCase } from "./utils"; -export function cleanEdges({ - flow: { edges, nodes }, - updateEdge, -}: cleanEdgesType) { +export function cleanEdges(nodes: Node[], edges: Edge[]) { let newEdges = _.cloneDeep(edges); edges.forEach((edge) => { // check if the source and target node still exists @@ -73,7 +69,7 @@ export function cleanEdges({ } } }); - updateEdge(newEdges); + return newEdges; } export function unselectAllNodes({ updateNodes, data }: unselectAllNodesType) {