diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx
index d9a821ec8..0eb1bb900 100644
--- a/src/frontend/src/CustomNodes/GenericNode/index.tsx
+++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx
@@ -1,4 +1,3 @@
-import { cloneDeep } from "lodash";
import { useContext, useEffect, useState } from "react";
import { NodeToolbar, useUpdateNodeInternals } from "reactflow";
import ShadTooltip from "../../components/ShadTooltipComponent";
@@ -14,11 +13,7 @@ import { undoRedoContext } from "../../contexts/undoRedoContext";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
import { validationStatusType } from "../../types/components";
import { NodeDataType } from "../../types/flow";
-import {
- cleanEdges,
- handleKeyDown,
- scapedJSONStringfy,
-} from "../../utils/reactflowUtils";
+import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils";
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
import { classNames, getFieldTitle } from "../../utils/utils";
import ParameterComponent from "./components/parameterComponent";
@@ -34,7 +29,8 @@ export default function GenericNode({
xPos: number;
yPos: number;
}): JSX.Element {
- const { updateFlow, flows, tabId } = useContext(FlowsContext);
+ const { updateFlow, flows, tabId, saveCurrentFlow } =
+ useContext(FlowsContext);
const updateNodeInternals = useUpdateNodeInternals();
const { types, deleteNode, reactFlowInstance, setFilterEdge, getFilterEdge } =
useContext(typesContext);
@@ -87,18 +83,15 @@ export default function GenericNode({
// State for outline color
const { sseData, isBuilding } = useSSE();
- useEffect(() => {
- let myFlow = flows.find((flow) => flow.id === tabId);
- if (reactFlowInstance && myFlow) {
- let flow = cloneDeep(myFlow);
- flow.data = reactFlowInstance.toObject();
+ /* useEffect(() => {
+ let flow = flows.find((flow) => flow.id === tabId);
+ if (reactFlowInstance && flow && flow.data) {
cleanEdges({
flow: {
- edges: flow.data.edges,
- nodes: flow.data.nodes,
+ edges: flow.data!.edges,
+ nodes: flow.data!.nodes,
},
updateEdge: (edge) => {
- flow.data!.edges = edge;
reactFlowInstance.setEdges(edge);
updateNodeInternals(data.id);
},
@@ -106,7 +99,7 @@ export default function GenericNode({
updateFlow(flow);
}
countHandles();
- }, [data]);
+ }, [data]); */
useEffect(() => {
setNodeDescription(data.node!.description);
@@ -138,6 +131,7 @@ export default function GenericNode({
deleteNode={(id) => {
takeSnapshot();
deleteNode(id);
+ saveCurrentFlow();
}}
setShowNode={(show: boolean) => {
data.showNode = show;
@@ -150,10 +144,7 @@ export default function GenericNode({
diff --git a/src/frontend/src/components/chatComponent/buildTrigger/index.tsx b/src/frontend/src/components/chatComponent/buildTrigger/index.tsx
index 21ea64e01..f1e374a03 100644
--- a/src/frontend/src/components/chatComponent/buildTrigger/index.tsx
+++ b/src/frontend/src/components/chatComponent/buildTrigger/index.tsx
@@ -76,11 +76,8 @@ export default function BuildTrigger({
}
async function streamNodeData(flow: FlowType) {
// Step 1: Make a POST request to send the flow data and receive a unique session ID
- const id = saveFlow({ ...flow, data: reactFlowInstance!.toObject() }, true);
- const response = await postBuildInit({
- ...flow,
- data: reactFlowInstance!.toObject(),
- });
+ const id = saveFlow(flow, true);
+ const response = await postBuildInit(flow);
const { flowId } = response.data;
// Step 2: Use the session ID to establish an SSE connection using EventSource
let validationResults: boolean[] = [];
diff --git a/src/frontend/src/contexts/flowsContext.tsx b/src/frontend/src/contexts/flowsContext.tsx
index 429e84905..10cc0ca28 100644
--- a/src/frontend/src/contexts/flowsContext.tsx
+++ b/src/frontend/src/contexts/flowsContext.tsx
@@ -82,6 +82,7 @@ const FlowsContextInitialValue: FlowsContextType = {
setLastCopiedSelection: (selection: any) => {},
tabsState: {},
setTabsState: (state: FlowsState) => {},
+ saveCurrentFlow: () => {},
getNodeId: (nodeType: string) => "",
setTweak: (tweak: any) => {},
getTweak: [],
@@ -638,9 +639,14 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
});
}
- async function saveFlow(newFlow: FlowType, silent?: boolean) {
- if (newFlow?.data?.nodes?.length === 0) return;
+ function saveCurrentFlow() {
+ const currentFlow = flows.find((flow) => flow.id === tabId);
+ if (currentFlow && reactFlowInstance && currentFlow.data) {
+ updateFlow({ ...currentFlow, data: reactFlowInstance?.toObject()! });
+ }
+ }
+ async function saveFlow(newFlow: FlowType, silent?: boolean) {
try {
// updates flow in db
const updatedFlow = await updateFlowInDatabase(newFlow);
@@ -712,6 +718,7 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
setIsBuilt,
lastCopiedSelection,
setLastCopiedSelection,
+ saveCurrentFlow,
hardReset,
tabId,
setTabId,
diff --git a/src/frontend/src/modals/exportModal/index.tsx b/src/frontend/src/modals/exportModal/index.tsx
index f58a9cac2..5ea23b644 100644
--- a/src/frontend/src/modals/exportModal/index.tsx
+++ b/src/frontend/src/modals/exportModal/index.tsx
@@ -68,7 +68,7 @@ const ExportModal = forwardRef(
downloadFlow(
{
id: tabId,
- data: reactFlowInstance?.toObject()!,
+ data: flow!.data!,
description,
name,
last_tested_version: version,
@@ -85,7 +85,7 @@ const ExportModal = forwardRef(
downloadFlow(
removeApiKeys({
id: tabId,
- data: reactFlowInstance?.toObject()!,
+ data: flow!.data!,
description,
name,
last_tested_version: version,
diff --git a/src/frontend/src/modals/flowSettingsModal/index.tsx b/src/frontend/src/modals/flowSettingsModal/index.tsx
index 929f6db4a..d4a4df9bf 100644
--- a/src/frontend/src/modals/flowSettingsModal/index.tsx
+++ b/src/frontend/src/modals/flowSettingsModal/index.tsx
@@ -34,7 +34,7 @@ export default function FlowSettingsModal({
useEffect(() => {
const tempNameList: string[] = [];
flows.forEach((flow: FlowType) => {
- tempNameList.push(flow.name);
+ if ((flow.is_component ?? false) === false) tempNameList.push(flow.name);
});
setNameList(tempNameList.filter((name) => name !== flow!.name));
}, [flows]);
diff --git a/src/frontend/src/modals/formModal/index.tsx b/src/frontend/src/modals/formModal/index.tsx
index f6f7c214f..09592e640 100644
--- a/src/frontend/src/modals/formModal/index.tsx
+++ b/src/frontend/src/modals/formModal/index.tsx
@@ -393,7 +393,7 @@ export default function FormModal({
const message = inputs;
addChatHistory(message!, true, chatKey!, template.current);
sendAll({
- ...reactFlowInstance?.toObject()!,
+ ...flow.data!,
inputs: inputs!,
chatHistory,
name: flow.name,
diff --git a/src/frontend/src/modals/shareModal/index.tsx b/src/frontend/src/modals/shareModal/index.tsx
index c27a36c78..9f73137cb 100644
--- a/src/frontend/src/modals/shareModal/index.tsx
+++ b/src/frontend/src/modals/shareModal/index.tsx
@@ -8,6 +8,7 @@ import { Checkbox } from "../../components/ui/checkbox";
import { alertContext } from "../../contexts/alertContext";
import { FlowsContext } from "../../contexts/flowsContext";
import { StoreContext } from "../../contexts/storeContext";
+import { typesContext } from "../../contexts/typesContext";
import {
getStoreComponents,
getStoreTags,
@@ -41,6 +42,7 @@ export default function ShareModal({
const { version, addFlow } = useContext(FlowsContext);
const { hasApiKey, hasStore } = useContext(StoreContext);
const { setSuccessData, setErrorData } = useContext(alertContext);
+ const { reactFlowInstance } = useContext(typesContext);
const [checked, setChecked] = useState(false);
const [name, setName] = useState(component?.name ?? "");
const [description, setDescription] = useState(component?.description ?? "");
@@ -84,7 +86,8 @@ export default function ShareModal({
filterByUser: true,
}).then((res) => {
res?.results?.forEach((element: any) => {
- unavaliableNames.push({ name: element.name, id: element.id });
+ if ((element.is_component ?? false) === is_component)
+ unavaliableNames.push({ name: element.name, id: element.id });
});
setUnavaliableNames(unavaliableNames);
setLoadingNames(false);
@@ -128,6 +131,8 @@ export default function ShareModal({
});
}
+ await saveFlow({ ...flows.find((flow) => flow.id === tabId)! }, true);
+
if (!update)
saveFlowStore(flow!, getTagsIds(selectedTags, tags), sharePublic).then(
successShare,
diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx
index 7b852a0c2..c0a821801 100644
--- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx
+++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx
@@ -60,7 +60,6 @@ export default function Page({
let {
updateFlow,
uploadFlow,
- addFlow,
getNodeId,
paste,
lastCopiedSelection,
@@ -69,7 +68,7 @@ export default function Page({
saveFlow,
setTabsState,
tabId,
- flows,
+ saveCurrentFlow,
} = useContext(FlowsContext);
const {
types,
@@ -89,6 +88,12 @@ export default function Page({
const [lastSelection, setLastSelection] =
useState(null);
+ const saveCurrentFlowTimeout = () => {
+ setTimeout(() => {
+ saveCurrentFlow();
+ }, 500); // need to do this because ReactFlow is not asynchronous.
+ };
+
useEffect(() => {
const onKeyDown = (event: KeyboardEvent) => {
if (
@@ -132,6 +137,7 @@ export default function Page({
takeSnapshot();
deleteNode(lastSelection.nodes.map((node) => node.id));
deleteEdge(lastSelection.edges.map((edge) => edge.id));
+ saveCurrentFlowTimeout();
}
}
};
@@ -147,7 +153,12 @@ export default function Page({
document.removeEventListener("keydown", onKeyDown);
document.removeEventListener("mousemove", handleMouseMove);
};
- }, [lastCopiedSelection, lastSelection, takeSnapshot]);
+ }, [
+ lastCopiedSelection,
+ lastSelection,
+ takeSnapshot,
+ saveCurrentFlowTimeout,
+ ]);
const [selectionMenuVisible, setSelectionMenuVisible] = useState(false);
@@ -190,16 +201,8 @@ export default function Page({
}, [flow, reactFlowInstance]);
useEffect(() => {
- const index = flows.findIndex((flowId) => flowId.id === flow.id);
-
const interval = setInterval(() => {
- saveFlow(
- {
- ...flows[index]!,
- data: reactFlowInstance ? reactFlowInstance!.toObject() : flow!.data,
- },
- true
- );
+ saveFlow(flow, true);
}, 30000);
return () => {
@@ -209,10 +212,6 @@ export default function Page({
const onEdgesChangeMod = useCallback(
(change: EdgeChange[]) => {
- updateFlow({
- ...flow!,
- data: reactFlowInstance ? reactFlowInstance!.toObject() : flow!.data,
- });
onEdgesChange(change);
//@ts-ignore
setTabsState((prev: FlowsState) => {
@@ -220,18 +219,18 @@ export default function Page({
...prev,
[tabId]: {
...prev[tabId],
- isPending: false,
+ isPending: true,
},
};
});
+ saveCurrentFlowTimeout();
},
- [onEdgesChange, setNodes, setTabsState, tabId]
+ [onEdgesChange, setNodes, setTabsState, saveCurrentFlowTimeout, tabId]
);
const onNodesChangeMod = useCallback(
(change: NodeChange[]) => {
const changeString = JSON.stringify(change);
-
if (changeString !== nodesOnFlow) {
onNodesChange(change);
updateNodeFlow(changeString);
@@ -245,9 +244,10 @@ export default function Page({
},
};
});
+ saveCurrentFlowTimeout();
}
},
- [onNodesChange, setTabsState, tabId, updateNodeFlow]
+ [onNodesChange, setTabsState, tabId, updateNodeFlow, saveCurrentFlowTimeout]
);
function updateNodeFlow(changeString: string) {
@@ -283,7 +283,7 @@ export default function Page({
return newX;
});
},
- [setEdges, setNodes, takeSnapshot]
+ [setEdges, setNodes, takeSnapshot, addEdge]
);
const onNodeDragStart: NodeDragHandler = useCallback(() => {
@@ -394,10 +394,7 @@ export default function Page({
return () => {
if (tabsState && tabsState[flow.id]?.isPending) {
- saveFlow({
- ...flow!,
- data: reactFlowInstance ? reactFlowInstance!.toObject() : flow!.data,
- });
+ saveFlow(flow);
}
};
}, []);
@@ -475,11 +472,7 @@ export default function Page({
{
- if (reactFlowInstance)
- updateFlow({
- ...flow,
- data: reactFlowInstance.toObject(),
- });
+ saveCurrentFlowTimeout();
}}
edges={edges}
onNodesChange={onNodesChangeMod}
diff --git a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx
index 274eac3c1..9db547c68 100644
--- a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx
+++ b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx
@@ -289,32 +289,34 @@ export default function ExtraSidebar(): JSX.Element {
-
-
-
+
+
+ )}
{ModalMemo}
diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx
index 230081be7..dc01727ef 100644
--- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx
+++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx
@@ -86,7 +86,7 @@ export default function NodeToolbarComponent({
break;
case "show":
takeSnapshot();
- setShowNode(data.showNode ? false : true);
+ setShowNode(data.showNode ?? true ? false : true);
updateNodeInternals(data.id);
break;
case "Download":
diff --git a/src/frontend/src/pages/MainPage/components/components/index.tsx b/src/frontend/src/pages/MainPage/components/components/index.tsx
index 8cada7571..b4c19f181 100644
--- a/src/frontend/src/pages/MainPage/components/components/index.tsx
+++ b/src/frontend/src/pages/MainPage/components/components/index.tsx
@@ -26,7 +26,7 @@ export default function ComponentsComponent({
useEffect(() => {
if (isLoading) return;
const all = flows
- .filter((f) => f.is_component === is_component)
+ .filter((f) => (f.is_component ?? false) === is_component)
.sort((a, b) => {
if (a?.updated_at && b?.updated_at) {
return (
@@ -179,7 +179,8 @@ export default function ComponentsComponent({
pageSize={pageSize}
rowsCount={[10, 20, 50, 100]}
totalRowsCount={
- flows.filter((f) => f.is_component === is_component).length
+ flows.filter((f) => (f.is_component ?? false) === is_component)
+ .length
}
paginate={(pageSize, pageIndex) => {
setPageIndex(pageIndex);
diff --git a/src/frontend/src/types/tabs/index.ts b/src/frontend/src/types/tabs/index.ts
index a3d764690..d4e120c1c 100644
--- a/src/frontend/src/types/tabs/index.ts
+++ b/src/frontend/src/types/tabs/index.ts
@@ -26,6 +26,7 @@ export type FlowsContextType = {
uploadFlows: () => void;
isBuilt: boolean;
setIsBuilt: (state: boolean) => void;
+ saveCurrentFlow: () => void;
uploadFlow: ({
newProject,
file,