From d3e6a27d8839ebb9dbd567d8e67483a7d33dec59 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 8 Dec 2023 16:43:07 -0300 Subject: [PATCH 1/5] Fixed name of component and flow not being the same --- src/frontend/src/modals/flowSettingsModal/index.tsx | 2 +- src/frontend/src/modals/shareModal/index.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/modals/flowSettingsModal/index.tsx b/src/frontend/src/modals/flowSettingsModal/index.tsx index 929f6db4a..dd29d4a78 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) tempNameList.push(flow.name); }); setNameList(tempNameList.filter((name) => name !== flow!.name)); }, [flows]); diff --git a/src/frontend/src/modals/shareModal/index.tsx b/src/frontend/src/modals/shareModal/index.tsx index e64c650d4..9f27d82bc 100644 --- a/src/frontend/src/modals/shareModal/index.tsx +++ b/src/frontend/src/modals/shareModal/index.tsx @@ -53,7 +53,6 @@ export default function ShareModal({ const [unavaliableNames, setUnavaliableNames] = useState([]); const { saveFlow, flows, tabId } = useContext(FlowsContext); - const [nameIsAvailable, setNameIsAvailable] = useState(false); const [loadingNames, setLoadingNames] = useState(false); useEffect(() => { @@ -79,7 +78,8 @@ export default function ShareModal({ await getStoreComponents({ fields: ["name"], filterByUser: true }).then( (res) => { res?.results?.forEach((element: any) => { - unavaliableNames.push(element.name); + if (element.is_component === is_component) + unavaliableNames.push(element.name); }); setUnavaliableNames(unavaliableNames); setLoadingNames(false); From f83a8568a09f4fb8db3e6aa54eff77420be9c30e Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 8 Dec 2023 16:57:56 -0300 Subject: [PATCH 2/5] Removed minimize transition to fix handle position --- src/frontend/src/CustomNodes/GenericNode/index.tsx | 5 +---- .../pages/FlowPage/components/nodeToolbarComponent/index.tsx | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index d9a821ec8..464f01e1f 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -150,10 +150,7 @@ export default function GenericNode({
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": From 02f68b63a064aff43f5d02231ad24b59215c2c98 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 8 Dec 2023 17:09:43 -0300 Subject: [PATCH 3/5] Added check when is_component equals null --- src/frontend/src/contexts/flowsContext.tsx | 3 +-- src/frontend/src/modals/flowSettingsModal/index.tsx | 2 +- src/frontend/src/modals/shareModal/index.tsx | 3 ++- .../src/pages/MainPage/components/components/index.tsx | 5 +++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/frontend/src/contexts/flowsContext.tsx b/src/frontend/src/contexts/flowsContext.tsx index 9c909831f..c1d320ff1 100644 --- a/src/frontend/src/contexts/flowsContext.tsx +++ b/src/frontend/src/contexts/flowsContext.tsx @@ -639,8 +639,7 @@ export function FlowsProvider({ children }: { children: ReactNode }) { } async function saveFlow(newFlow: FlowType, silent?: boolean) { - if (newFlow?.data?.nodes?.length === 0) return; - + console.log(newFlow); try { // updates flow in db const updatedFlow = await updateFlowInDatabase(newFlow); diff --git a/src/frontend/src/modals/flowSettingsModal/index.tsx b/src/frontend/src/modals/flowSettingsModal/index.tsx index dd29d4a78..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) => { - if (flow.is_component === false) 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/shareModal/index.tsx b/src/frontend/src/modals/shareModal/index.tsx index 90f05f1a0..d85d62698 100644 --- a/src/frontend/src/modals/shareModal/index.tsx +++ b/src/frontend/src/modals/shareModal/index.tsx @@ -84,7 +84,8 @@ export default function ShareModal({ filterByUser: true, }).then((res) => { res?.results?.forEach((element: any) => { - if (element.is_component === is_component) + console.log(element, is_component); + if ((element.is_component ?? false) === is_component) unavaliableNames.push({ name: element.name, id: element.id }); }); setUnavaliableNames(unavaliableNames); 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); From 154d77b57988bbf9d0e2934117dd21ebfc003b9a Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 8 Dec 2023 17:11:02 -0300 Subject: [PATCH 4/5] Removed unused console log --- src/frontend/src/modals/shareModal/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/frontend/src/modals/shareModal/index.tsx b/src/frontend/src/modals/shareModal/index.tsx index d85d62698..834970c51 100644 --- a/src/frontend/src/modals/shareModal/index.tsx +++ b/src/frontend/src/modals/shareModal/index.tsx @@ -84,7 +84,6 @@ export default function ShareModal({ filterByUser: true, }).then((res) => { res?.results?.forEach((element: any) => { - console.log(element, is_component); if ((element.is_component ?? false) === is_component) unavaliableNames.push({ name: element.name, id: element.id }); }); From d6ea72cbea51cbea26ebb699c36517d8ec142f25 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 8 Dec 2023 19:10:32 -0300 Subject: [PATCH 5/5] Fixed Flows state not being updated always --- .../src/CustomNodes/GenericNode/index.tsx | 26 ++++----- .../chatComponent/buildTrigger/index.tsx | 7 +-- src/frontend/src/contexts/flowsContext.tsx | 12 +++-- src/frontend/src/modals/exportModal/index.tsx | 4 +- src/frontend/src/modals/formModal/index.tsx | 2 +- src/frontend/src/modals/shareModal/index.tsx | 5 +- .../components/PageComponent/index.tsx | 53 ++++++++----------- .../extraSidebarComponent/index.tsx | 48 +++++++++-------- src/frontend/src/types/tabs/index.ts | 1 + 9 files changed, 77 insertions(+), 81 deletions(-) diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index 464f01e1f..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; 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 c1d320ff1..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,8 +639,14 @@ export function FlowsProvider({ children }: { children: ReactNode }) { }); } + 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) { - console.log(newFlow); try { // updates flow in db const updatedFlow = await updateFlowInDatabase(newFlow); @@ -668,8 +675,6 @@ export function FlowsProvider({ children }: { children: ReactNode }) { }, }; }); - - console.log(tabsState); } } catch (err) { setErrorData({ @@ -713,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/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 834970c51..e9dae7cb5 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 ?? ""); @@ -127,7 +129,8 @@ export default function ShareModal({ }); } - await saveFlow(flows.find((flow) => flow.id === tabId)!, true); + 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 {
- -
- -
-
+ onClick={(event) => { + saveFlow(flow!); + }} + > + 0 + ? " " + : " extra-side-bar-save-disable") + } + /> + +
+ + )}
{ModalMemo}
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,