From d8bd04edefd2ee914c72186a95f46a1e5f04be02 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Sun, 10 Mar 2024 01:09:01 -0300 Subject: [PATCH] Add lodash cloneDeep and useUpdateNodeInternals to GenericNode component, and updateNodeCode to CodeTabsComponent and CodeAreaModal components --- .../src/CustomNodes/GenericNode/index.tsx | 58 ++++++++++++++++++- .../components/codeTabsComponent/index.tsx | 2 - .../src/modals/codeAreaModal/index.tsx | 3 - 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index 397ba5b80..7775e7f94 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -1,5 +1,6 @@ +import { cloneDeep } from "lodash"; import { useCallback, useEffect, useState } from "react"; -import { NodeToolbar } from "reactflow"; +import { NodeToolbar, useUpdateNodeInternals } from "reactflow"; import ShadTooltip from "../../components/ShadTooltipComponent"; import IconComponent from "../../components/genericIconComponent"; import InputComponent from "../../components/inputComponent"; @@ -9,6 +10,7 @@ import Loading from "../../components/ui/loading"; import { Textarea } from "../../components/ui/textarea"; import Xmark from "../../components/ui/xmark"; import { + NATIVE_CATEGORIES, RUN_TIMESTAMP_PREFIX, STATUS_BUILD, STATUS_BUILDING, @@ -20,6 +22,7 @@ import { useDarkStore } from "../../stores/darkStore"; import useFlowStore from "../../stores/flowStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore"; import { useTypesStore } from "../../stores/typesStore"; +import { APIClassType } from "../../types/api"; import { validationStatusType } from "../../types/components"; import { NodeDataType } from "../../types/flow"; import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils"; @@ -39,10 +42,16 @@ export default function GenericNode({ yPos: number; }): JSX.Element { const types = useTypesStore((state) => state.types); + const templates = useTypesStore((state) => state.templates); const deleteNode = useFlowStore((state) => state.deleteNode); const flowPool = useFlowStore((state) => state.flowPool); const buildFlow = useFlowStore((state) => state.buildFlow); const setNode = useFlowStore((state) => state.setNode); + const addToOutdatedNodes = useFlowStore((state) => state.addToOutdatedNodes); + const updateNodeInternals = useUpdateNodeInternals(); + const removeFromOutdatedNodes = useFlowStore( + (state) => state.removeFromOutdatedNodes + ); const setErrorData = useAlertStore((state) => state.setErrorData); const name = nodeIconsLucide[data.type] ? data.type : types[data.type]; const [inputName, setInputName] = useState(false); @@ -51,6 +60,7 @@ export default function GenericNode({ const [nodeDescription, setNodeDescription] = useState( data.node?.description! ); + const [isOutdated, setIsOutdated] = useState(false); const buildStatus = useFlowStore( (state) => state.flowBuildStatus[data.id]?.status ); @@ -65,6 +75,51 @@ export default function GenericNode({ const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); + useEffect(() => { + // This one should run only once + // first check if data.type in NATIVE_CATEGORIES + // if not return + if (!NATIVE_CATEGORIES.includes(types[data.type])) return; + const thisNodeTemplate = templates[data.type].template; + // if the template does not have a code key + // return + if (!thisNodeTemplate.code) return; + + const currentCode = thisNodeTemplate.code.value; + const thisNodesCode = data.node!.template.code.value; + if (currentCode !== thisNodesCode) { + addToOutdatedNodes(data.id); + setIsOutdated(true); + } else { + // remove the node from the outdatedNodes + removeFromOutdatedNodes(data.id); + setIsOutdated(false); + } + // template.code can be undefined + }, [data.node?.template.code.value]); + + const updateNodeCode = useCallback( + (newNodeClass: APIClassType, code: string, name: string) => { + 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; + }); + + updateNodeInternals(data.id); + }, + [data.id, data.node, setNode] + ); + if (!data.node!.template) { setErrorData({ title: `Error in component ${data.node!.display_name}`, @@ -305,6 +360,7 @@ export default function GenericNode({ showNode={showNode} openAdvancedModal={false} onCloseAdvancedModal={() => {}} + updateNodeCode={updateNodeCode} selected={selected} > diff --git a/src/frontend/src/components/codeTabsComponent/index.tsx b/src/frontend/src/components/codeTabsComponent/index.tsx index a83a36fc2..736f23d3a 100644 --- a/src/frontend/src/components/codeTabsComponent/index.tsx +++ b/src/frontend/src/components/codeTabsComponent/index.tsx @@ -55,8 +55,6 @@ export default function CodeTabsComponent({ const dark = useDarkStore((state) => state.dark); const unselectAll = useFlowStore((state) => state.unselectAll); - const setNodes = useFlowStore((state) => state.setNodes); - const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); useEffect(() => { diff --git a/src/frontend/src/modals/codeAreaModal/index.tsx b/src/frontend/src/modals/codeAreaModal/index.tsx index 3705f5530..e68592efa 100644 --- a/src/frontend/src/modals/codeAreaModal/index.tsx +++ b/src/frontend/src/modals/codeAreaModal/index.tsx @@ -23,7 +23,6 @@ import { import { postCustomComponent, postValidateCode } from "../../controllers/API"; import useAlertStore from "../../stores/alertStore"; import { useDarkStore } from "../../stores/darkStore"; -import useFlowStore from "../../stores/flowStore"; import { CodeErrorDataTypeAPI } from "../../types/api"; import { codeAreaModalPropsType } from "../../types/components"; import BaseModal from "../baseModal"; @@ -45,14 +44,12 @@ export default function CodeAreaModal({ ? [myOpen, mySetOpen] : useState(false); const dark = useDarkStore((state) => state.dark); - const unselectAll = useFlowStore((state) => state.unselectAll); const [height, setHeight] = useState(null); const setSuccessData = useAlertStore((state) => state.setSuccessData); const setErrorData = useAlertStore((state) => state.setErrorData); const [error, setError] = useState<{ detail: CodeErrorDataTypeAPI; } | null>(null); - const nodes = useFlowStore((state) => state.nodes); useEffect(() => { // if nodeClass.template has more fields other than code and dynamic is true