From dbc3eaa6736955de02579353f651bbd7516ba7a2 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 13 Oct 2023 00:13:59 -0300 Subject: [PATCH 01/17] Transformed paste in 3 functions and fixed problem with edges --- src/frontend/src/contexts/tabsContext.tsx | 159 +++++++++++++--------- 1 file changed, 92 insertions(+), 67 deletions(-) diff --git a/src/frontend/src/contexts/tabsContext.tsx b/src/frontend/src/contexts/tabsContext.tsx index 79b39ad60..4a583c55c 100644 --- a/src/frontend/src/contexts/tabsContext.tsx +++ b/src/frontend/src/contexts/tabsContext.tsx @@ -300,7 +300,7 @@ export function TabsProvider({ children }: { children: ReactNode }) { let fileData = JSON.parse(text); if (fileData.flows) { fileData.flows.forEach((flow: FlowType) => { - id = addFlow(flow, newProject); + id = addFlow(newProject, flow); }); } // parse the text into a JSON object @@ -376,6 +376,7 @@ export function TabsProvider({ children }: { children: ReactNode }) { selectionInstance: { nodes: Node[]; edges: Edge[] }, position: { x: number; y: number; paneX?: number; paneY?: number } ) { + console.log(selectionInstance); let minimumX = Infinity; let minimumY = Infinity; let idsMap = {}; @@ -395,80 +396,104 @@ export function TabsProvider({ children }: { children: ReactNode }) { : reactFlowInstance!.project({ x: position.x, y: position.y }); selectionInstance.nodes.forEach((node: NodeType) => { - // Generate a unique node ID - let newId = getNodeId(node.data.type); - idsMap[node.id] = newId; - - // Create a new node object - const newNode: NodeType = { - id: newId, - type: "genericNode", - position: { - x: insidePosition.x + node.position!.x - minimumX, - y: insidePosition.y + node.position!.y - minimumY, - }, - data: { - ..._.cloneDeep(node.data), - id: newId, - }, - }; - - // Add the new node to the list of nodes in state - nodes = nodes - .map((node) => ({ ...node, selected: false })) - .concat({ ...newNode, selected: false }); + nodes = updateNodeId( + node, + idsMap, + nodes, + insidePosition, + minimumX, + minimumY + ); }); + reactFlowInstance!.setNodes(nodes); selectionInstance.edges.forEach((edge: Edge) => { - let source = idsMap[edge.source]; - let target = idsMap[edge.target]; - const sourceHandleObject: sourceHandleType = scapeJSONParse( - edge.sourceHandle! - ); - let sourceHandle = scapedJSONStringfy({ - ...sourceHandleObject, - id: source, - }); - sourceHandleObject.id = source; - edge.data.sourceHandle = sourceHandleObject; - const targetHandleObject: targetHandleType = scapeJSONParse( - edge.targetHandle! - ); - let targetHandle = scapedJSONStringfy({ - ...targetHandleObject, - id: target, - }); - targetHandleObject.id = target; - edge.data.targetHandle = targetHandleObject; - let id = - "reactflow__edge-" + - source + - sourceHandle + - "-" + - target + - targetHandle; - edges = addEdge( - { - source, - target, - sourceHandle, - targetHandle, - id, - style: { stroke: "#555" }, - className: - targetHandleObject.type === "Text" - ? "stroke-gray-800 " - : "stroke-gray-900 ", - animated: targetHandleObject.type === "Text", - selected: false, - }, - edges.map((edge) => ({ ...edge, selected: false })) - ); + edges = updateEdgeId(edge, idsMap, edges); }); + reactFlowInstance!.setEdges(edges); } + const updateNodeId = ( + node: NodeType, + idsMap, + nodes, + insidePosition, + minimumX, + minimumY + ) => { + // Generate a unique node ID + let newId = getNodeId(node.data.type); + idsMap[node.id] = newId; + + // Create a new node object + const newNode: NodeType = { + id: newId, + type: "genericNode", + position: { + x: insidePosition.x + node.position!.x - minimumX, + y: insidePosition.y + node.position!.y - minimumY, + }, + data: { + ..._.cloneDeep(node.data), + id: newId, + }, + }; + + // Add the new node to the list of nodes in state + nodes = nodes + .map((node) => ({ ...node, selected: false })) + .concat({ ...newNode, selected: false }); + + return nodes; + }; + + const updateEdgeId = (edge: Edge, idsMap, edges) => { + let source = idsMap[edge.source]; + let target = idsMap[edge.target]; + const sourceHandleObject: sourceHandleType = scapeJSONParse( + edge.sourceHandle! + ); + let sourceHandle = scapedJSONStringfy({ + ...sourceHandleObject, + id: source, + }); + sourceHandleObject.id = source; + edge.data.sourceHandle = sourceHandleObject; + const targetHandleObject: targetHandleType = scapeJSONParse( + edge.targetHandle! + ); + let targetHandle = scapedJSONStringfy({ + ...targetHandleObject, + id: target, + }); + targetHandleObject.id = target; + edge.data.targetHandle = targetHandleObject; + let id = + "reactflow__edge-" + source + sourceHandle + "-" + target + targetHandle; + edges = addEdge( + { + data: edge.data, + source, + target, + sourceHandle, + targetHandle, + id, + style: { stroke: "#555" }, + className: + targetHandleObject.type === "Text" + ? "stroke-gray-800 " + : "stroke-gray-900 ", + animated: targetHandleObject.type === "Text", + selected: false, + }, + edges.map((edge) => ({ ...edge, selected: false })) + ); + + return edges; + }; + const addFlow = async ( newProject: Boolean, flow?: FlowType From 46c538c4a7f0fd5854ae5ec6cefa93a2c2eba03b Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 13 Oct 2023 00:23:56 -0300 Subject: [PATCH 02/17] Recursive ID updating implemented on nodes --- src/frontend/src/contexts/tabsContext.tsx | 25 +++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/frontend/src/contexts/tabsContext.tsx b/src/frontend/src/contexts/tabsContext.tsx index 4a583c55c..282cdb008 100644 --- a/src/frontend/src/contexts/tabsContext.tsx +++ b/src/frontend/src/contexts/tabsContext.tsx @@ -427,6 +427,26 @@ export function TabsProvider({ children }: { children: ReactNode }) { let newId = getNodeId(node.data.type); idsMap[node.id] = newId; + let data = { + ..._.cloneDeep(node.data), + id: newId, + }; + + if (data.node?.flow) { + let internNodes = []; + data.node.flow.data!.nodes.map((node) => { + internNodes = updateNodeId( + node, + idsMap, + internNodes, + insidePosition, + minimumX, + minimumY + ); + }); + data.node.flow.data!.nodes = internNodes; + } + // Create a new node object const newNode: NodeType = { id: newId, @@ -435,10 +455,7 @@ export function TabsProvider({ children }: { children: ReactNode }) { x: insidePosition.x + node.position!.x - minimumX, y: insidePosition.y + node.position!.y - minimumY, }, - data: { - ..._.cloneDeep(node.data), - id: newId, - }, + data, }; // Add the new node to the list of nodes in state From 0d7c6422f4d4ba3f907762cef8938ceddf4ba61d Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 13 Oct 2023 00:30:30 -0300 Subject: [PATCH 03/17] Added recursive edge updating on group nodes --- src/frontend/src/contexts/tabsContext.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/contexts/tabsContext.tsx b/src/frontend/src/contexts/tabsContext.tsx index 282cdb008..2fe74af60 100644 --- a/src/frontend/src/contexts/tabsContext.tsx +++ b/src/frontend/src/contexts/tabsContext.tsx @@ -376,7 +376,6 @@ export function TabsProvider({ children }: { children: ReactNode }) { selectionInstance: { nodes: Node[]; edges: Edge[] }, position: { x: number; y: number; paneX?: number; paneY?: number } ) { - console.log(selectionInstance); let minimumX = Infinity; let minimumY = Infinity; let idsMap = {}; @@ -445,6 +444,12 @@ export function TabsProvider({ children }: { children: ReactNode }) { ); }); data.node.flow.data!.nodes = internNodes; + + let internEdges = []; + data.node.flow.data!.edges.map((edge) => { + internEdges = updateEdgeId(edge, idsMap, internEdges); + }); + data.node.flow.data!.edges = internEdges; } // Create a new node object From 6572c5223af4ba7ca4f44649025bb256be132356 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 13 Oct 2023 00:31:41 -0300 Subject: [PATCH 04/17] Removed console logs --- src/frontend/src/utils/reactflowUtils.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts index d3a26b358..80de701ad 100644 --- a/src/frontend/src/utils/reactflowUtils.ts +++ b/src/frontend/src/utils/reactflowUtils.ts @@ -913,7 +913,6 @@ export function expandGroupNode( const { template, flow } = _.cloneDeep(groupNode.node!); const gNodes: NodeType[] = flow?.data?.nodes!; const gEdges = flow!.data!.edges; - console.log(gEdges); //redirect edges to correct proxy node let updatedEdges: Edge[] = []; ReactFlowInstance.getEdges().forEach((edge) => { @@ -1001,7 +1000,6 @@ export function expandGroupNode( ...gEdges, ...updatedEdges, ]; - console.log(edges); ReactFlowInstance.setNodes(nodes); ReactFlowInstance.setEdges(edges); } @@ -1024,7 +1022,6 @@ export function getGroupStatus( let status = { valid: true, params: "Built sucessfully ✨" }; const { nodes } = flow.data!; const ids = nodes.map((n: NodeType) => n.data.id); - ids.forEach((id) => console.log(ssData[id])); ids.forEach((id) => { if (!ssData[id]) { status = ssData[id]; From 3c4d3ab5e2d4b37ffb328b6c548ffaaf17ce0ba7 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 13 Oct 2023 01:06:22 -0300 Subject: [PATCH 05/17] Snapshot took when updating node name and inputs --- .../GenericNode/components/parameterComponent/index.tsx | 4 ++++ src/frontend/src/CustomNodes/GenericNode/index.tsx | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index 61f784d58..5360849b5 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -25,6 +25,7 @@ import { Button } from "../../../../components/ui/button"; import { TOOLTIP_EMPTY } from "../../../../constants/constants"; import { TabsContext } from "../../../../contexts/tabsContext"; import { typesContext } from "../../../../contexts/typesContext"; +import { undoRedoContext } from "../../../../contexts/undoRedoContext"; import { ParameterComponentType } from "../../../../types/components"; import { TabsState } from "../../../../types/tabs"; import { @@ -88,9 +89,12 @@ export default function ParameterComponent({ const { data: myData } = useContext(typesContext); + const { takeSnapshot } = useContext(undoRedoContext); + const handleOnNewValue = ( newValue: string | string[] | boolean | Object[] ): void => { + takeSnapshot(); let newData = cloneDeep(data); newData.node!.template[name].value = newValue; setData(newData); diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index 7e6c04767..ce374021e 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -9,6 +9,7 @@ import { Textarea } from "../../components/ui/textarea"; import { useSSE } from "../../contexts/SSEContext"; import { TabsContext } from "../../contexts/tabsContext"; import { typesContext } from "../../contexts/typesContext"; +import { undoRedoContext } from "../../contexts/undoRedoContext"; import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent"; import { validationStatusType } from "../../types/components"; import { NodeDataType } from "../../types/flow"; @@ -50,6 +51,8 @@ export default function GenericNode({ const [handles, setHandles] = useState([]); let numberOfInputs: boolean[] = []; + const { takeSnapshot } = useContext(undoRedoContext); + function countHandles(): void { numberOfInputs = Object.keys(data.node!.template) .filter((templateField) => templateField.charAt(0) !== "_") @@ -189,6 +192,7 @@ export default function GenericNode({ } else { setNodeName(data.node!.display_name); } + takeSnapshot(); }} value={nodeName} onChange={setNodeName} From 455da5a8e849c90354aa3ba4c1fef19afc74b8ec Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 13 Oct 2023 01:24:15 -0300 Subject: [PATCH 06/17] Removed internal state of nodes, to fix undo/redo of fields --- .../GenericNode/components/parameterComponent/index.tsx | 9 +-------- src/frontend/src/CustomNodes/GenericNode/index.tsx | 9 +-------- src/frontend/src/modals/EditNodeModal/index.tsx | 2 -- .../FlowPage/components/nodeToolbarComponent/index.tsx | 2 -- src/frontend/src/types/components/index.ts | 2 -- 5 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index 5360849b5..779034c6d 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -1,4 +1,3 @@ -import { cloneDeep } from "lodash"; import React, { ReactNode, useContext, @@ -46,7 +45,6 @@ export default function ParameterComponent({ left, id, data, - setData, tooltipTitle, title, color, @@ -95,9 +93,7 @@ export default function ParameterComponent({ newValue: string | string[] | boolean | Object[] ): void => { takeSnapshot(); - let newData = cloneDeep(data); - newData.node!.template[name].value = newValue; - setData(newData); + data.node!.template[name].value = newValue; // Set state to pending //@ts-ignore setTabsState((prev: TabsState) => { @@ -444,9 +440,6 @@ export default function ParameterComponent({ field_name={name} setNodeClass={(nodeClass) => { data.node = nodeClass; - const clone = cloneDeep(data); - clone.node = nodeClass; - setData(clone); }} nodeClass={data.node} disabled={disabled} diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index ce374021e..c543fb27b 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -23,7 +23,7 @@ import { classNames, toTitleCase } from "../../utils/utils"; import ParameterComponent from "./components/parameterComponent"; export default function GenericNode({ - data: olddata, + data, xPos, yPos, selected, @@ -33,7 +33,6 @@ export default function GenericNode({ xPos: number; yPos: number; }): JSX.Element { - const [data, setData] = useState(olddata); const { updateFlow, flows, tabId } = useContext(TabsContext); const updateNodeInternals = useUpdateNodeInternals(); const { types, deleteNode, reactFlowInstance, setFilterEdge, getFilterEdge } = @@ -89,7 +88,6 @@ export default function GenericNode({ // State for outline color const { sseData, isBuilding } = useSSE(); useEffect(() => { - olddata.node = data.node; let myFlow = flows.find((flow) => flow.id === tabId); if (reactFlowInstance && myFlow) { let flow = cloneDeep(myFlow); @@ -132,7 +130,6 @@ export default function GenericNode({ 0 diff --git a/src/frontend/src/modals/EditNodeModal/index.tsx b/src/frontend/src/modals/EditNodeModal/index.tsx index 6e00e6504..bf1d5506f 100644 --- a/src/frontend/src/modals/EditNodeModal/index.tsx +++ b/src/frontend/src/modals/EditNodeModal/index.tsx @@ -48,14 +48,12 @@ const EditNodeModal = forwardRef( ( { data, - setData, nodeLength, children, open, onClose, }: { data: NodeDataType; - setData: (data: NodeDataType) => void; nodeLength: number; children: ReactNode; open?: boolean; diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx index ffd5ea2cb..d61392683 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -19,7 +19,6 @@ import { classNames, getRandomKeyByssmm } from "../../../../utils/utils"; export default function NodeToolbarComponent({ data, - setData, deleteNode, position, setShowNode, @@ -235,7 +234,6 @@ export default function NodeToolbarComponent({ {showModalAdvanced && ( { diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index 7f53ff77f..865cc1f5e 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -38,7 +38,6 @@ export type DropDownComponentType = { }; export type ParameterComponentType = { data: NodeDataType; - setData: (value: NodeDataType) => void; title: string; id: sourceHandleType | targetHandleType; color: string; @@ -433,7 +432,6 @@ export type fileCardPropsType = { export type nodeToolbarPropsType = { data: NodeDataType; deleteNode: (idx: string) => void; - setData: (newState: NodeDataType) => void; position: XYPosition; setShowNode: (boolean: any) => void; numberOfHandles: boolean[] | []; From 2b10c6337f3ac08ea52e67dbd77f64b0d6d3b4cb Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 13 Oct 2023 01:31:24 -0300 Subject: [PATCH 07/17] Fixed state of name and description changers on node --- .../src/CustomNodes/GenericNode/index.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index c543fb27b..693677d12 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -114,6 +114,14 @@ export default function GenericNode({ }, 300); }, [showNode]); + useEffect(() => { + setNodeDescription(data.node!.description); + }, [data.node!.description]); + + useEffect(() => { + setNodeName(data.node!.display_name); + }, [data.node!.display_name]); + // New useEffect to watch for changes in sseData and update validation status useEffect(() => { const relevantData = sseData[data.id]; @@ -189,7 +197,6 @@ export default function GenericNode({ } else { setNodeName(data.node!.display_name); } - takeSnapshot(); }} value={nodeName} onChange={setNodeName} @@ -201,7 +208,10 @@ export default function GenericNode({
setInputName(true)} + onDoubleClick={() => { + setInputName(true); + takeSnapshot(); + }} > {data.node?.display_name}
@@ -412,7 +422,10 @@ export default function GenericNode({ ) : (
setInputDescription(true)} + onDoubleClick={() => { + setInputDescription(true); + takeSnapshot(); + }} > {data.node?.description}
From b431839bcea7e4b9c089a3ede7bb74725d6b6fcf Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Thu, 19 Oct 2023 17:20:21 -0300 Subject: [PATCH 08/17] Fixed bug where it does not open and does not save correctly --- src/frontend/src/contexts/undoRedoContext.tsx | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/frontend/src/contexts/undoRedoContext.tsx b/src/frontend/src/contexts/undoRedoContext.tsx index 83dfd8f32..d348d12c3 100644 --- a/src/frontend/src/contexts/undoRedoContext.tsx +++ b/src/frontend/src/contexts/undoRedoContext.tsx @@ -54,11 +54,21 @@ export function UndoRedoProvider({ children }) { // push the current graph to the past state setPast((old) => { let newPast = cloneDeep(old); - newPast[tabIndex] = old[tabIndex].slice( - old[tabIndex].length - defaultOptions.maxHistorySize + 1, - old[tabIndex].length - ); - newPast[tabIndex].push({ nodes: getNodes(), edges: getEdges() }); + let newState = { + nodes: cloneDeep(getNodes()), + edges: cloneDeep(getEdges()), + }; + if ( + old[tabIndex] && + JSON.stringify(old[tabIndex][old[tabIndex].length - 1]) !== + JSON.stringify(newState) + ) { + newPast[tabIndex] = old[tabIndex].slice( + old[tabIndex].length - defaultOptions.maxHistorySize + 1, + old[tabIndex].length + ); + newPast[tabIndex].push(newState); + } return newPast; }); From 3229487cf0960aec927a7b68cdc8050583bc6457 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 20 Oct 2023 19:00:57 -0300 Subject: [PATCH 09/17] Fixed undo not working correctly after first undo --- src/frontend/src/contexts/undoRedoContext.tsx | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/frontend/src/contexts/undoRedoContext.tsx b/src/frontend/src/contexts/undoRedoContext.tsx index d348d12c3..26133a29a 100644 --- a/src/frontend/src/contexts/undoRedoContext.tsx +++ b/src/frontend/src/contexts/undoRedoContext.tsx @@ -52,25 +52,23 @@ export function UndoRedoProvider({ children }) { const takeSnapshot = useCallback(() => { // push the current graph to the past state - setPast((old) => { - let newPast = cloneDeep(old); - let newState = { - nodes: cloneDeep(getNodes()), - edges: cloneDeep(getEdges()), - }; - if ( - old[tabIndex] && - JSON.stringify(old[tabIndex][old[tabIndex].length - 1]) !== - JSON.stringify(newState) - ) { - newPast[tabIndex] = old[tabIndex].slice( - old[tabIndex].length - defaultOptions.maxHistorySize + 1, - old[tabIndex].length - ); - newPast[tabIndex].push(newState); - } - return newPast; - }); + let newPast = cloneDeep(past); + let newState = { + nodes: cloneDeep(getNodes()), + edges: cloneDeep(getEdges()), + }; + if ( + past[tabIndex] && + JSON.stringify(past[tabIndex][past[tabIndex].length - 1]) !== + JSON.stringify(newState) + ) { + newPast[tabIndex] = past[tabIndex].slice( + past[tabIndex].length - defaultOptions.maxHistorySize + 1, + past[tabIndex].length + ); + newPast[tabIndex].push(newState); + } + setPast(newPast); // whenever we take a new snapshot, the redo operations need to be cleared to avoid state mismatches setFuture((old) => { From 8c1454cb131338eb109e5f89af683ead280051b6 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 20 Oct 2023 19:45:28 -0300 Subject: [PATCH 10/17] Fixed undo not working when grouping components --- .../src/pages/FlowPage/components/PageComponent/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index 20fb826e1..6432423f9 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -499,6 +499,7 @@ export default function Page({ isVisible={selectionMenuVisible} nodes={lastSelection?.nodes} onClick={() => { + takeSnapshot(); if ( validateSelection(lastSelection!, edges).length === 0 ) { From e12b1fd5222a1bafe7efa8c0612c75e0fc0e6560 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Thu, 23 Nov 2023 14:40:09 -0300 Subject: [PATCH 11/17] Removed unused parameters --- src/frontend/src/components/keypairListComponent/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/frontend/src/components/keypairListComponent/index.tsx b/src/frontend/src/components/keypairListComponent/index.tsx index ca63c362c..2f175ea67 100644 --- a/src/frontend/src/components/keypairListComponent/index.tsx +++ b/src/frontend/src/components/keypairListComponent/index.tsx @@ -12,8 +12,6 @@ export default function KeypairListComponent({ disabled, editNode = false, duplicateKey, - advanced = false, - dataValue, }: KeyPairListComponentType): JSX.Element { useEffect(() => { if (disabled) { From 775bcee035276bafc8d8e5d3ee028ba583badb7b Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Thu, 23 Nov 2023 15:10:35 -0300 Subject: [PATCH 12/17] Removed key and autofocus --- src/frontend/src/CustomNodes/GenericNode/index.tsx | 1 - src/frontend/src/pages/FlowPage/index.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index 9c12ca77f..01b7738cc 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -188,7 +188,6 @@ export default function GenericNode({ {data.node?.flow && inputName ? (
{ setInputName(false); if (nodeName.trim() !== "") { diff --git a/src/frontend/src/pages/FlowPage/index.tsx b/src/frontend/src/pages/FlowPage/index.tsx index b0ecb666a..9c11f66da 100644 --- a/src/frontend/src/pages/FlowPage/index.tsx +++ b/src/frontend/src/pages/FlowPage/index.tsx @@ -20,7 +20,7 @@ export default function FlowPage(): JSX.Element { {flows.length > 0 && tabId !== "" && flows.findIndex((flow) => flow.id === tabId) !== -1 && ( - flow.id === tabId)!} /> + flow.id === tabId)!} /> )} Date: Thu, 23 Nov 2023 15:17:24 -0300 Subject: [PATCH 13/17] Removed fit view --- .../src/pages/FlowPage/components/PageComponent/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index 923dc87c9..4490dc4eb 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -170,7 +170,6 @@ export default function Page({ setEdges(flow?.data?.edges ?? []); if (reactFlowInstance) { setViewport(flow?.data?.viewport ?? { x: 1, y: 0, zoom: 0.5 }); - reactFlowInstance.fitView(); } }, [flow, reactFlowInstance, setEdges, setNodes, setViewport]); //set extra sidebar From ee3209b579f2edee782ba8f860402f485bab20ce Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 28 Nov 2023 11:05:19 -0300 Subject: [PATCH 14/17] Fixed filter edge and added debug consoles --- .../components/PageComponent/index.tsx | 73 +++++++++++-------- .../extraSidebarComponent/index.tsx | 1 - 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index fa1e878a4..3792c85cf 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -85,11 +85,15 @@ export default function Page({ const { takeSnapshot } = useContext(undoRedoContext); + const [position, setPosition] = useState({ x: 0, y: 0 }); const [lastSelection, setLastSelection] = useState(null); + + useEffect(() => { + console.log("1") // this effect is used to attach the global event handlers const onKeyDown = (event: KeyboardEvent) => { @@ -152,28 +156,25 @@ export default function Page({ const [nodes, setNodes, onNodesChange] = useNodesState( flow.data?.nodes ?? [] ); + const [edges, setEdges, onEdgesChange] = useEdgesState( flow.data?.edges ?? [] ); const { setViewport } = useReactFlow(); const edgeUpdateSuccessful = useRef(true); - useEffect(() => { - if (reactFlowInstance && flow) { - flow.data = reactFlowInstance.toObject(); - updateFlow(flow); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [edges]); + //update flow when tabs change useEffect(() => { + console.log("2") setNodes(flow?.data?.nodes ?? []); setEdges(flow?.data?.edges ?? []); if (reactFlowInstance) { setViewport(flow?.data?.viewport ?? { x: 1, y: 0, zoom: 0.5 }); } - }, [flow, reactFlowInstance, setEdges, setNodes, setViewport]); + }, [flow, reactFlowInstance]); //set extra sidebar useEffect(() => { + console.log("3") setExtraComponent(); setExtraNavigation({ title: "Components" }); }, [setExtraComponent, setExtraNavigation]); @@ -181,28 +182,20 @@ export default function Page({ const [seconds, setSeconds] = useState(0); useEffect(() => { + console.log("4") const index = flows.findIndex((flowId) => flowId.id === flow.id); const interval = setInterval(() => { - setSeconds((prevSeconds) => { - let updatedSeconds = prevSeconds + 1; - - if (updatedSeconds % 30 === 0) { - saveFlow( - { - ...flows[index]!, - data: reactFlowInstance - ? reactFlowInstance!.toObject() - : flow!.data, - }, - true - ); - updatedSeconds = 0; - } - - return updatedSeconds; - }); - }, 1000); + saveFlow( + { + ...flows[index]!, + data: reactFlowInstance + ? reactFlowInstance!.toObject() + : flow!.data, + }, + true + ); + }, 30000); return () => { clearInterval(interval); @@ -211,11 +204,12 @@ export default function Page({ const onEdgesChangeMod = useCallback( (change: EdgeChange[]) => { - onEdgesChange(change); - setNodes((node) => { - let newX = _.cloneDeep(node); - return newX; + console.log("5") + updateFlow({ + ...flow!, + data: reactFlowInstance ? reactFlowInstance!.toObject() : flow!.data, }); + onEdgesChange(change); //@ts-ignore setTabsState((prev: FlowsState) => { return { @@ -232,6 +226,7 @@ export default function Page({ const onNodesChangeMod = useCallback( (change: NodeChange[]) => { + console.log("6") onNodesChange(change); //@ts-ignore setTabsState((prev: FlowsState) => { @@ -249,6 +244,7 @@ export default function Page({ const onConnect = useCallback( (params: Connection) => { + console.log("7") takeSnapshot(); setEdges((eds) => addEdge( @@ -280,22 +276,26 @@ export default function Page({ ); const onNodeDragStart: NodeDragHandler = useCallback(() => { + console.log("8") // 👇 make dragging a node undoable takeSnapshot(); // 👉 you can place your event handlers here }, [takeSnapshot]); const onSelectionDragStart: SelectionDragHandler = useCallback(() => { + console.log("9") // 👇 make dragging a selection undoable takeSnapshot(); }, [takeSnapshot]); const onEdgesDelete: OnEdgesDelete = useCallback(() => { + console.log("10") // 👇 make deleting edges undoable takeSnapshot(); }, [takeSnapshot]); const onDragOver = useCallback((event: React.DragEvent) => { + console.log("11") event.preventDefault(); if (event.dataTransfer.types.some((types) => types === "nodedata")) { event.dataTransfer.dropEffect = "move"; @@ -306,6 +306,7 @@ export default function Page({ const onDrop = useCallback( (event: React.DragEvent) => { + console.log("12") event.preventDefault(); if (event.dataTransfer.types.some((types) => types === "nodedata")) { takeSnapshot(); @@ -382,6 +383,7 @@ export default function Page({ ); useEffect(() => { + console.log("13") return () => { if (tabsState && tabsState[flow.id]?.isPending) { saveFlow({ @@ -394,6 +396,7 @@ export default function Page({ const onDelete = useCallback( (mynodes: Node[]) => { + console.log("14") takeSnapshot(); setEdges( edges.filter( @@ -408,11 +411,13 @@ export default function Page({ ); const onEdgeUpdateStart = useCallback(() => { + console.log("15") edgeUpdateSuccessful.current = false; }, []); const onEdgeUpdate = useCallback( (oldEdge: Edge, newConnection: Connection) => { + console.log("16") if (isValidConnection(newConnection, reactFlowInstance!)) { edgeUpdateSuccessful.current = true; setEdges((els) => updateEdge(oldEdge, newConnection, els)); @@ -422,6 +427,7 @@ export default function Page({ ); const onEdgeUpdateEnd = useCallback((_, edge: Edge): void => { + console.log("17") if (!edgeUpdateSuccessful.current) { setEdges((eds) => eds.filter((edg) => edg.id !== edge.id)); } @@ -431,15 +437,18 @@ export default function Page({ const [selectionEnded, setSelectionEnded] = useState(true); const onSelectionEnd = useCallback(() => { + console.log("18") setSelectionEnded(true); }, []); const onSelectionStart = useCallback((event: MouseEvent) => { + console.log("19") event.preventDefault(); setSelectionEnded(false); }, []); // Workaround to show the menu only after the selection has ended. useEffect(() => { + console.log("20") if (selectionEnded && lastSelection && lastSelection.nodes.length > 1) { setSelectionMenuVisible(true); } else { @@ -449,12 +458,14 @@ export default function Page({ const onSelectionChange = useCallback( (flow: OnSelectionChangeParams): void => { + console.log("21") setLastSelection(flow); }, [] ); const onPaneClick = useCallback((flow) => { + console.log("22") setFilterEdge([]); }, []); diff --git a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx index 3c73139af..f691937f5 100644 --- a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx @@ -97,7 +97,6 @@ export default function ExtraSidebar(): JSX.Element { useEffect(() => { if (getFilterEdge.length === 0 && search === "") { setFilterData(data); - setFilterEdge([]); setSearch(""); } }, [getFilterEdge]); From 733e6945c6f2244cadeceaafb0ab62aeea7e00a6 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 28 Nov 2023 20:31:27 -0300 Subject: [PATCH 15/17] Fixed unnecessary updates --- src/frontend/src/contexts/flowsContext.tsx | 2 +- .../components/PageComponent/index.tsx | 62 ++++++------------- 2 files changed, 19 insertions(+), 45 deletions(-) diff --git a/src/frontend/src/contexts/flowsContext.tsx b/src/frontend/src/contexts/flowsContext.tsx index 0ed9cb783..f6f9660d7 100644 --- a/src/frontend/src/contexts/flowsContext.tsx +++ b/src/frontend/src/contexts/flowsContext.tsx @@ -418,7 +418,7 @@ export function FlowsProvider({ children }: { children: ReactNode }) { const insidePosition = position.paneX ? { x: position.paneX + position.x, y: position.paneY! + position.y } - : reactFlowInstance!.project({ x: position.x, y: position.y }); + : reactFlowInstance!.screenToFlowPosition({ x: position.x, y: position.y }); selectionInstance.nodes.forEach((node: NodeType) => { // Generate a unique node ID diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index 3792c85cf..0513dd66c 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -85,17 +85,11 @@ export default function Page({ const { takeSnapshot } = useContext(undoRedoContext); - const [position, setPosition] = useState({ x: 0, y: 0 }); const [lastSelection, setLastSelection] = useState(null); - - useEffect(() => { - console.log("1") - // this effect is used to attach the global event handlers - const onKeyDown = (event: KeyboardEvent) => { if (!isWrappedWithClass(event, "nocopy")) { if ( @@ -112,10 +106,9 @@ export default function Page({ lastCopiedSelection ) { event.preventDefault(); - let bounds = reactFlowWrapper.current?.getBoundingClientRect(); paste(lastCopiedSelection, { - x: position.x - bounds!.left, - y: position.y - bounds!.top, + x: position.x, + y: position.y, }); } if ( @@ -137,18 +130,26 @@ export default function Page({ } } }; + + document.addEventListener("keydown", onKeyDown); + + return () => { + document.removeEventListener("keydown", onKeyDown); + }; + }, [lastCopiedSelection, lastSelection]); + + useEffect(() => { const handleMouseMove = (event) => { setPosition({ x: event.clientX, y: event.clientY }); }; - document.addEventListener("keydown", onKeyDown); document.addEventListener("mousemove", handleMouseMove); return () => { - document.removeEventListener("keydown", onKeyDown); document.removeEventListener("mousemove", handleMouseMove); }; - }, [position, lastCopiedSelection, lastSelection]); + }, [position]); + const [selectionMenuVisible, setSelectionMenuVisible] = useState(false); const { setExtraComponent, setExtraNavigation } = useContext(locationContext); @@ -165,33 +166,21 @@ export default function Page({ //update flow when tabs change useEffect(() => { - console.log("2") setNodes(flow?.data?.nodes ?? []); setEdges(flow?.data?.edges ?? []); if (reactFlowInstance) { setViewport(flow?.data?.viewport ?? { x: 1, y: 0, zoom: 0.5 }); } }, [flow, reactFlowInstance]); - //set extra sidebar - useEffect(() => { - console.log("3") - setExtraComponent(); - setExtraNavigation({ title: "Components" }); - }, [setExtraComponent, setExtraNavigation]); - - const [seconds, setSeconds] = useState(0); useEffect(() => { - console.log("4") const index = flows.findIndex((flowId) => flowId.id === flow.id); const interval = setInterval(() => { saveFlow( { ...flows[index]!, - data: reactFlowInstance - ? reactFlowInstance!.toObject() - : flow!.data, + data: reactFlowInstance ? reactFlowInstance!.toObject() : flow!.data, }, true ); @@ -204,8 +193,7 @@ export default function Page({ const onEdgesChangeMod = useCallback( (change: EdgeChange[]) => { - console.log("5") - updateFlow({ + updateFlow({ ...flow!, data: reactFlowInstance ? reactFlowInstance!.toObject() : flow!.data, }); @@ -226,7 +214,6 @@ export default function Page({ const onNodesChangeMod = useCallback( (change: NodeChange[]) => { - console.log("6") onNodesChange(change); //@ts-ignore setTabsState((prev: FlowsState) => { @@ -244,7 +231,6 @@ export default function Page({ const onConnect = useCallback( (params: Connection) => { - console.log("7") takeSnapshot(); setEdges((eds) => addEdge( @@ -276,26 +262,22 @@ export default function Page({ ); const onNodeDragStart: NodeDragHandler = useCallback(() => { - console.log("8") // 👇 make dragging a node undoable takeSnapshot(); // 👉 you can place your event handlers here }, [takeSnapshot]); const onSelectionDragStart: SelectionDragHandler = useCallback(() => { - console.log("9") // 👇 make dragging a selection undoable takeSnapshot(); }, [takeSnapshot]); const onEdgesDelete: OnEdgesDelete = useCallback(() => { - console.log("10") // 👇 make deleting edges undoable takeSnapshot(); }, [takeSnapshot]); const onDragOver = useCallback((event: React.DragEvent) => { - console.log("11") event.preventDefault(); if (event.dataTransfer.types.some((types) => types === "nodedata")) { event.dataTransfer.dropEffect = "move"; @@ -306,7 +288,6 @@ export default function Page({ const onDrop = useCallback( (event: React.DragEvent) => { - console.log("12") event.preventDefault(); if (event.dataTransfer.types.some((types) => types === "nodedata")) { takeSnapshot(); @@ -383,7 +364,9 @@ export default function Page({ ); useEffect(() => { - console.log("13") + setExtraComponent(); + setExtraNavigation({ title: "Components" }); + return () => { if (tabsState && tabsState[flow.id]?.isPending) { saveFlow({ @@ -396,7 +379,6 @@ export default function Page({ const onDelete = useCallback( (mynodes: Node[]) => { - console.log("14") takeSnapshot(); setEdges( edges.filter( @@ -411,13 +393,11 @@ export default function Page({ ); const onEdgeUpdateStart = useCallback(() => { - console.log("15") edgeUpdateSuccessful.current = false; }, []); const onEdgeUpdate = useCallback( (oldEdge: Edge, newConnection: Connection) => { - console.log("16") if (isValidConnection(newConnection, reactFlowInstance!)) { edgeUpdateSuccessful.current = true; setEdges((els) => updateEdge(oldEdge, newConnection, els)); @@ -427,7 +407,6 @@ export default function Page({ ); const onEdgeUpdateEnd = useCallback((_, edge: Edge): void => { - console.log("17") if (!edgeUpdateSuccessful.current) { setEdges((eds) => eds.filter((edg) => edg.id !== edge.id)); } @@ -437,18 +416,15 @@ export default function Page({ const [selectionEnded, setSelectionEnded] = useState(true); const onSelectionEnd = useCallback(() => { - console.log("18") setSelectionEnded(true); }, []); const onSelectionStart = useCallback((event: MouseEvent) => { - console.log("19") event.preventDefault(); setSelectionEnded(false); }, []); // Workaround to show the menu only after the selection has ended. useEffect(() => { - console.log("20") if (selectionEnded && lastSelection && lastSelection.nodes.length > 1) { setSelectionMenuVisible(true); } else { @@ -458,14 +434,12 @@ export default function Page({ const onSelectionChange = useCallback( (flow: OnSelectionChangeParams): void => { - console.log("21") setLastSelection(flow); }, [] ); const onPaneClick = useCallback((flow) => { - console.log("22") setFilterEdge([]); }, []); From 857fa403a1c7295d05c34eb8064f3b6f9e623840 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 28 Nov 2023 20:36:47 -0300 Subject: [PATCH 16/17] Added takeSnapshot on ungroup and show and hide node --- src/frontend/src/CustomNodes/GenericNode/index.tsx | 12 ++++-------- .../components/nodeToolbarComponent/index.tsx | 4 ++++ src/frontend/src/types/flow/index.ts | 1 + 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index c39249b22..9e60bb7d2 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -46,7 +46,6 @@ export default function GenericNode({ ); const [validationStatus, setValidationStatus] = useState(null); - const [showNode, setShowNode] = useState(true); const [handles, setHandles] = useState([]); let numberOfInputs: boolean[] = []; @@ -108,12 +107,6 @@ export default function GenericNode({ countHandles(); }, [data]); - useEffect(() => { - setTimeout(() => { - updateNodeInternals(data.id); - }, 300); - }, [showNode]); - useEffect(() => { setNodeDescription(data.node!.description); }, [data.node!.description]); @@ -132,6 +125,9 @@ export default function GenericNode({ setValidationStatus(null); } }, [sseData, data.id]); + + const showNode = data.showNode ?? false; + return ( <> @@ -139,7 +135,7 @@ export default function GenericNode({ position={{ x: xPos, y: yPos }} data={data} deleteNode={deleteNode} - setShowNode={setShowNode} + setShowNode={(showNode: boolean) => {data.showNode = showNode}} numberOfHandles={handles} showNode={showNode} > diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx index 0b450fcd1..fb43309fd 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -23,6 +23,7 @@ import { updateFlowPosition, } from "../../../../utils/reactflowUtils"; import { classNames } from "../../../../utils/utils"; +import { undoRedoContext } from "../../../../contexts/undoRedoContext"; export default function NodeToolbarComponent({ data, @@ -65,6 +66,7 @@ export default function NodeToolbarComponent({ const isGroup = data.node?.flow ? true : false; const { paste, saveComponent, version, flows } = useContext(FlowsContext); + const { takeSnapshot } = useContext(undoRedoContext); const reactFlowInstance = useReactFlow(); const [showModalAdvanced, setShowModalAdvanced] = useState(false); const [showconfirmShare, setShowconfirmShare] = useState(false); @@ -83,6 +85,7 @@ export default function NodeToolbarComponent({ setShowModalAdvanced(true); break; case "show": + takeSnapshot(); setShowNode((prev) => !prev); updateNodeInternals(data.id); break; @@ -98,6 +101,7 @@ export default function NodeToolbarComponent({ case "disabled": break; case "ungroup": + takeSnapshot(); updateFlowPosition(position, data.node?.flow!); expandGroupNode(data, reactFlowInstance, getNodeId); break; diff --git a/src/frontend/src/types/flow/index.ts b/src/frontend/src/types/flow/index.ts index e66c96304..9f1821a08 100644 --- a/src/frontend/src/types/flow/index.ts +++ b/src/frontend/src/types/flow/index.ts @@ -22,6 +22,7 @@ export type NodeType = { }; export type NodeDataType = { + showNode?: boolean; type: string; node?: APIClassType; id: string; From b7e70d35117646e5fdcdcc56e9394f1918b3da77 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 28 Nov 2023 20:40:35 -0300 Subject: [PATCH 17/17] Fixed nodes being minimized by default --- src/frontend/src/CustomNodes/GenericNode/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index 9e60bb7d2..679e3863b 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -125,8 +125,8 @@ export default function GenericNode({ setValidationStatus(null); } }, [sseData, data.id]); - - const showNode = data.showNode ?? false; + + const showNode = data.showNode ?? true; return ( <>