diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx
index 4f3187b58..97d019d45 100644
--- a/src/frontend/src/App.tsx
+++ b/src/frontend/src/App.tsx
@@ -18,10 +18,10 @@ import {
import { AuthContext } from "./contexts/authContext";
import { FlowsContext } from "./contexts/flowsContext";
import { locationContext } from "./contexts/locationContext";
-import { typesContext } from "./contexts/typesContext";
import { getVersion } from "./controllers/API";
import Router from "./routes";
import useAlertStore from "./stores/alertStore";
+import { useTypesStore } from "./stores/typesStore";
export default function App() {
let { setCurrent, setShowSideBar, setIsStackedOpen } =
@@ -43,8 +43,7 @@ export default function App() {
const successOpen = useAlertStore((state) => state.successOpen);
const setSuccessOpen = useAlertStore((state) => state.setSuccessOpen);
const loading = useAlertStore((state) => state.loading);
-
- const { fetchError } = useContext(typesContext);
+ const fetchError = useAlertStore((state) => state.fetchError);
// Initialize state variable for the list of alerts
const [alertsList, setAlertsList] = useState<
@@ -132,14 +131,15 @@ export default function App() {
const { isAuthenticated } = useContext(AuthContext);
const { refreshFlows, setVersion } = useContext(FlowsContext);
- const { getTypes } = useContext(typesContext);
+ const getTypes = useTypesStore((state) => state.getTypes);
useEffect(() => {
// If the user is authenticated, fetch the types. This code is important to check if the user is auth because of the execution order of the useEffect hooks.
if (isAuthenticated === true) {
// get data from db
- refreshFlows();
- getTypes();
+ getTypes().then(() => {
+ refreshFlows();
+ });
}
getVersion().then((data) => {
@@ -156,16 +156,14 @@ export default function App() {
}}
FallbackComponent={CrashErrorComponent}
>
- {loading ? (
+ {fetchError ? (
+
+ ) : loading ? (
- {fetchError ? (
-
- ) : (
-
- )}
+
) : (
<>
diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx
index 8e73c40e5..6994276ca 100644
--- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx
+++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx
@@ -27,7 +27,6 @@ import {
TOOLTIP_EMPTY,
} from "../../../../constants/constants";
import { FlowsContext } from "../../../../contexts/flowsContext";
-import { typesContext } from "../../../../contexts/typesContext";
import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import { postCustomComponentUpdate } from "../../../../controllers/API";
import useAlertStore from "../../../../stores/alertStore";
@@ -48,6 +47,7 @@ import {
nodeNames,
} from "../../../../utils/styleUtils";
import { classNames, groupByFamily } from "../../../../utils/utils";
+import { useTypesStore } from "../../../../stores/typesStore";
export default function ParameterComponent({
left,
@@ -78,14 +78,15 @@ export default function ParameterComponent({
const groupedEdge = useRef(null);
- const { setFilterEdge } = useContext(typesContext);
+ const setFilterEdge = useTypesStore((state) => state.setFilterEdge);
+
let disabled =
edges.some(
(edge) =>
edge.targetHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id)
) ?? false;
- const { data: myData } = useContext(typesContext);
+ const myData = useTypesStore((state) => state.data);
const { takeSnapshot } = useContext(undoRedoContext);
diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx
index f3b47836f..3a90c9ce4 100644
--- a/src/frontend/src/CustomNodes/GenericNode/index.tsx
+++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx
@@ -7,7 +7,6 @@ import InputComponent from "../../components/inputComponent";
import { Textarea } from "../../components/ui/textarea";
import { priorityFields } from "../../constants/constants";
import { useSSE } from "../../contexts/SSEContext";
-import { typesContext } from "../../contexts/typesContext";
import { undoRedoContext } from "../../contexts/undoRedoContext";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
import useFlowStore from "../../stores/flowStore";
@@ -17,6 +16,7 @@ import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils";
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
import { classNames, cn, getFieldTitle } from "../../utils/utils";
import ParameterComponent from "./components/parameterComponent";
+import { useTypesStore } from "../../stores/typesStore";
export default function GenericNode({
data,
@@ -29,7 +29,7 @@ export default function GenericNode({
xPos: number;
yPos: number;
}): JSX.Element {
- const { types } = useContext(typesContext);
+ const types = useTypesStore((state) => state.types);
const deleteNode = useFlowStore((state) => state.deleteNode);
const setNode = useFlowStore((state) => state.setNode);
const name = nodeIconsLucide[data.type] ? data.type : types[data.type];
diff --git a/src/frontend/src/contexts/flowsContext.tsx b/src/frontend/src/contexts/flowsContext.tsx
index 489699135..4c1750fce 100644
--- a/src/frontend/src/contexts/flowsContext.tsx
+++ b/src/frontend/src/contexts/flowsContext.tsx
@@ -35,7 +35,7 @@ import {
getRandomDescription,
getRandomName,
} from "../utils/utils";
-import { typesContext } from "./typesContext";
+import { useTypesStore } from "../stores/typesStore";
const uid = new ShortUniqueId({ length: 5 });
@@ -76,8 +76,7 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
const [isLoading, setIsLoading] = useState(false);
const [flows, setFlows] = useState>([]);
const [tabsState, setTabsState] = useState({});
- const { setData } = useContext(typesContext);
-
+ const setData = useTypesStore((state) => state.setData);
const nodes = useFlowStore((state) => state.nodes);
const edges = useFlowStore((state) => state.edges);
const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance);
diff --git a/src/frontend/src/contexts/index.tsx b/src/frontend/src/contexts/index.tsx
index 63870e94d..0bc1913fb 100644
--- a/src/frontend/src/contexts/index.tsx
+++ b/src/frontend/src/contexts/index.tsx
@@ -8,7 +8,6 @@ import { AuthProvider } from "./authContext";
import { FlowsProvider } from "./flowsContext";
import { LocationProvider } from "./locationContext";
-import { TypesProvider } from "./typesContext";
import { UndoRedoProvider } from "./undoRedoContext";
export default function ContextWrapper({ children }: { children: ReactNode }) {
@@ -19,16 +18,14 @@ export default function ContextWrapper({ children }: { children: ReactNode }) {
-
-
-
-
-
- {children}
-
-
-
-
+
+
+
+
+ {children}
+
+
+
diff --git a/src/frontend/src/contexts/typesContext.tsx b/src/frontend/src/contexts/typesContext.tsx
deleted file mode 100644
index 16deecf00..000000000
--- a/src/frontend/src/contexts/typesContext.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-import _ from "lodash";
-import {
- createContext,
- ReactNode,
- useContext,
- useEffect,
- useState,
-} from "react";
-import { getAll, getHealth } from "../controllers/API";
-import useAlertStore from "../stores/alertStore";
-import { APIKindType } from "../types/api";
-import { typesContextType } from "../types/typesContext";
-import { AuthContext } from "./authContext";
-
-//context to share types adn functions from nodes to flow
-
-const initialValue: typesContextType = {
- types: {},
- setTypes: () => {},
- templates: {},
- setTemplates: () => {},
- data: {},
- setData: () => {},
- getTypes: () => {},
- setFetchError: () => {},
- fetchError: false,
- setFilterEdge: (filter) => {},
- getFilterEdge: [],
-};
-
-export const typesContext = createContext(initialValue);
-
-export function TypesProvider({ children }: { children: ReactNode }) {
- const [types, setTypes] = useState({});
- const [templates, setTemplates] = useState({});
- const [data, setData] = useState({});
- const [fetchError, setFetchError] = useState(false);
- const setLoading = useAlertStore((state) => state.setLoading);
- const [getFilterEdge, setFilterEdge] = useState([]);
-
- async function getTypes(): Promise {
- // We will keep a flag to handle the case where the component is unmounted before the API call resolves.
- let isMounted = true;
- try {
- const result = await getAll();
- // Make sure to only update the state if the component is still mounted.
- if (isMounted && result?.status === 200) {
- setLoading(false);
- let { data } = _.cloneDeep(result);
- setData((old) => ({ ...old, ...data }));
- setTemplates(
- Object.keys(data).reduce((acc, curr) => {
- Object.keys(data[curr]).forEach((c: keyof APIKindType) => {
- //prevent wrong overwriting of the component template by a group of the same type
- if (!data[curr][c].flow) acc[c] = data[curr][c];
- });
- return acc;
- }, {})
- );
- // Set the types by reducing over the keys of the result data and updating the accumulator.
- setTypes(
- // Reverse the keys so the tool world does not overlap
- Object.keys(data)
- .reverse()
- .reduce((acc, curr) => {
- Object.keys(data[curr]).forEach((c: keyof APIKindType) => {
- acc[c] = curr;
- // Add the base classes to the accumulator as well.
- data[curr][c].base_classes?.forEach((b) => {
- acc[b] = curr;
- });
- });
- return acc;
- }, {})
- );
- }
- } catch (error) {
- console.error("An error has occurred while fetching types.");
- console.log(error);
- await getHealth().catch((e) => {
- setFetchError(true);
- });
- }
- }
-
- return (
-
- {children}
-
- );
-}
diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx
index 2a2544997..d5d9f0f26 100644
--- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx
+++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx
@@ -25,7 +25,6 @@ import Chat from "../../../../components/chatComponent";
import Loading from "../../../../components/ui/loading";
import { FlowsContext } from "../../../../contexts/flowsContext";
import { locationContext } from "../../../../contexts/locationContext";
-import { typesContext } from "../../../../contexts/typesContext";
import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import useAlertStore from "../../../../stores/alertStore";
import useFlowStore from "../../../../stores/flowStore";
@@ -42,6 +41,7 @@ import { cn, getRandomName, isWrappedWithClass } from "../../../../utils/utils";
import ConnectionLineComponent from "../ConnectionLineComponent";
import SelectionMenu from "../SelectionMenuComponent";
import ExtraSidebar from "../extraSidebarComponent";
+import { useTypesStore } from "../../../../stores/typesStore";
const nodeTypes = {
genericNode: GenericNode,
@@ -55,7 +55,9 @@ export default function Page({
view?: boolean;
}): JSX.Element {
let { uploadFlow, saveFlow } = useContext(FlowsContext);
- const { types, templates, setFilterEdge } = useContext(typesContext);
+ const types = useTypesStore((state) => state.types);
+ const templates = useTypesStore((state) => state.templates);
+ const setFilterEdge = useTypesStore((state) => state.setFilterEdge);
const reactFlowWrapper = useRef(null);
const [lastCopiedSelection, setLastCopiedSelection] = useState<{
diff --git a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx
index 070c3e962..710b9fd9a 100644
--- a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx
+++ b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx
@@ -5,7 +5,6 @@ import IconComponent from "../../../../components/genericIconComponent";
import { Input } from "../../../../components/ui/input";
import { Separator } from "../../../../components/ui/separator";
import { FlowsContext } from "../../../../contexts/flowsContext";
-import { typesContext } from "../../../../contexts/typesContext";
import ApiModal from "../../../../modals/ApiModal";
import ExportModal from "../../../../modals/exportModal";
import ShareModal from "../../../../modals/shareModal";
@@ -25,10 +24,13 @@ import {
} from "../../../../utils/utils";
import DisclosureComponent from "../DisclosureComponent";
import SidebarDraggableComponent from "./sideBarDraggableComponent";
+import { useTypesStore } from "../../../../stores/typesStore";
export default function ExtraSidebar(): JSX.Element {
- const { data, templates, getFilterEdge, setFilterEdge } =
- useContext(typesContext);
+ const data = useTypesStore((state) => state.data);
+ const templates = useTypesStore((state) => state.templates);
+ const getFilterEdge = useTypesStore((state) => state.getFilterEdge);
+ const setFilterEdge = useTypesStore((state) => state.setFilterEdge);
const { flows, tabId, uploadFlow, saveFlow } = useContext(FlowsContext);
const hasStore = useStoreStore((state) => state.hasStore);
diff --git a/src/frontend/src/stores/alertStore.ts b/src/frontend/src/stores/alertStore.ts
index 63c58e28c..a5c2ae41a 100644
--- a/src/frontend/src/stores/alertStore.ts
+++ b/src/frontend/src/stores/alertStore.ts
@@ -21,6 +21,10 @@ const useAlertStore = create((set, get) => ({
notificationCenter: false,
notificationList: [],
loading: true,
+ fetchError: false,
+ setFetchError: (newState: boolean) => {
+ set({ fetchError: newState });
+ },
setErrorData: (newState: { title: string; list?: Array }) => {
if (newState.title && newState.title !== "") {
set({
diff --git a/src/frontend/src/stores/typesStore.ts b/src/frontend/src/stores/typesStore.ts
new file mode 100644
index 000000000..f28791ba0
--- /dev/null
+++ b/src/frontend/src/stores/typesStore.ts
@@ -0,0 +1,52 @@
+import { create } from "zustand";
+import { getAll, getHealth } from "../controllers/API";
+import { APIDataType, APIKindType } from "../types/api";
+import { TypesStoreType } from "../types/zustand/types";
+import useAlertStore from "./alertStore";
+import { templatesGenerator, typesGenerator } from "../utils/reactflowUtils";
+
+export const useTypesStore = create((set, get) => ({
+ types: {},
+ templates: {},
+ data: {},
+ getFilterEdge: [],
+ getTypes: () => {
+ return new Promise(async (resolve, reject) => {
+ getAll()
+ .then((response) => {
+ const data = response.data;
+ useAlertStore.setState({ loading: false });
+ set((old) => ({
+ types: typesGenerator(data),
+ data: { ...old.data, ...data },
+ templates: templatesGenerator(data),
+ }));
+ resolve();
+ })
+ .catch((error) => {
+ console.error("An error has occurred while fetching types.");
+ console.log(error);
+ getHealth().catch((e) => {
+ useAlertStore.setState({
+ fetchError: true,
+ loading: false,
+ });
+ reject();
+ });
+ });
+ });
+ },
+ setTypes: (newState: {}) => {
+ set({ types: newState });
+ },
+ setTemplates: (newState: {}) => {
+ set({ templates: newState });
+ },
+ setData: (change: APIDataType | ((old: APIDataType) => APIDataType)) => {
+ let newChange = typeof change === "function" ? change(get().data) : change;
+ set({ data: newChange });
+ },
+ setFilterEdge: (newState) => {
+ set({ getFilterEdge: newState });
+ },
+}));
diff --git a/src/frontend/src/types/api/index.ts b/src/frontend/src/types/api/index.ts
index 38cd59bda..16c840e37 100644
--- a/src/frontend/src/types/api/index.ts
+++ b/src/frontend/src/types/api/index.ts
@@ -2,8 +2,8 @@ import { Edge, Node, Viewport } from "reactflow";
import { FlowType } from "../flow";
//kind and class are just representative names to represent the actual structure of the object received by the API
export type APIDataType = { [key: string]: APIKindType };
-export type APIObjectType = { kind: APIKindType; [key: string]: APIKindType };
-export type APIKindType = { class: APIClassType; [key: string]: APIClassType };
+export type APIObjectType = { [key: string]: APIKindType };
+export type APIKindType = { [key: string]: APIClassType };
export type APITemplateType = {
[key: string]: TemplateVariableType;
};
diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts
index 314150b4a..986260665 100644
--- a/src/frontend/src/types/components/index.ts
+++ b/src/frontend/src/types/components/index.ts
@@ -3,8 +3,8 @@ import { ReactFlowJsonObject, XYPosition } from "reactflow";
import { APIClassType, APITemplateType, TemplateVariableType } from "../api";
import { ChatMessageType } from "../chat";
import { FlowStyleType, FlowType, NodeDataType, NodeType } from "../flow/index";
-import { typesContextType } from "../typesContext";
import { sourceHandleType, targetHandleType } from "./../flow/index";
+import { TypesStoreType } from "../zustand/types";
export type InputComponentType = {
autoFocus?: boolean;
onBlur?: (event: React.FocusEvent) => void;
@@ -48,7 +48,6 @@ export type ParameterComponentType = {
required?: boolean;
name?: string;
tooltipTitle: string | undefined;
- dataContext?: typesContextType;
optionalHandle?: Array | null;
info?: string;
proxy?: { field: string; id: string };
diff --git a/src/frontend/src/types/typesContext/index.ts b/src/frontend/src/types/typesContext/index.ts
index e0d9644ef..c7314fea0 100644
--- a/src/frontend/src/types/typesContext/index.ts
+++ b/src/frontend/src/types/typesContext/index.ts
@@ -1,24 +1,5 @@
import { Edge, Node } from "reactflow";
import { AlertItemType } from "../alerts";
-import { APIClassType, APIDataType } from "../api";
-
-const types: { [char: string]: string } = {};
-const template: { [char: string]: APIClassType } = {};
-const data: { [char: string]: string } = {};
-
-export type typesContextType = {
- types: typeof types;
- setTypes: (newState: {}) => void;
- templates: typeof template;
- setTemplates: (newState: {}) => void;
- data: APIDataType;
- setData: (newState: {}) => void;
- getTypes: () => void;
- fetchError: boolean;
- setFetchError: (newState: boolean) => void;
- setFilterEdge: (newState) => void;
- getFilterEdge: any[];
-};
export type alertContextType = {
errorData: { title: string; list?: Array };
diff --git a/src/frontend/src/types/zustand/alert/index.ts b/src/frontend/src/types/zustand/alert/index.ts
index 1e273cc4e..cdecff157 100644
--- a/src/frontend/src/types/zustand/alert/index.ts
+++ b/src/frontend/src/types/zustand/alert/index.ts
@@ -20,4 +20,6 @@ export type AlertStoreType = {
removeFromNotificationList: (index: string) => void;
loading: boolean;
setLoading: (newState: boolean) => void;
+ fetchError: boolean;
+ setFetchError: (newState: boolean) => void;
};
diff --git a/src/frontend/src/types/zustand/types/index.ts b/src/frontend/src/types/zustand/types/index.ts
new file mode 100644
index 000000000..904e3058e
--- /dev/null
+++ b/src/frontend/src/types/zustand/types/index.ts
@@ -0,0 +1,13 @@
+import { APIClassType, APIDataType } from "../../api";
+
+export type TypesStoreType = {
+ types: { [char: string]: string };
+ setTypes: (newState: {}) => void;
+ templates: { [char: string]: APIClassType };
+ setTemplates: (newState: {}) => void;
+ data: APIDataType;
+ setData: (newState: {}) => void;
+ getTypes: () => Promise;
+ setFilterEdge: (newState) => void;
+ getFilterEdge: any[];
+}
\ No newline at end of file
diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts
index 550443455..d9dcfe2ec 100644
--- a/src/frontend/src/utils/reactflowUtils.ts
+++ b/src/frontend/src/utils/reactflowUtils.ts
@@ -12,7 +12,12 @@ import {
LANGFLOW_SUPPORTED_TYPES,
specialCharsRegex,
} from "../constants/constants";
-import { APITemplateType, TemplateVariableType } from "../types/api";
+import {
+ APIKindType,
+ APIObjectType,
+ APITemplateType,
+ TemplateVariableType,
+} from "../types/api";
import {
FlowType,
NodeDataType,
@@ -1138,3 +1143,28 @@ export function removeFileNameFromComponents(flow: FlowType) {
}
});
}
+
+export function typesGenerator(data: APIObjectType) {
+ return Object.keys(data)
+ .reverse()
+ .reduce((acc, curr) => {
+ Object.keys(data[curr]).forEach((c: keyof APIKindType) => {
+ acc[c] = curr;
+ // Add the base classes to the accumulator as well.
+ data[curr][c].base_classes?.forEach((b) => {
+ acc[b] = curr;
+ });
+ });
+ return acc;
+ }, {});
+}
+
+export function templatesGenerator(data: APIObjectType) {
+ return Object.keys(data).reduce((acc, curr) => {
+ Object.keys(data[curr]).forEach((c: keyof APIKindType) => {
+ //prevent wrong overwriting of the component template by a group of the same type
+ if (!data[curr][c].flow) acc[c] = data[curr][c];
+ });
+ return acc;
+ }, {});
+}