added saveFlow function to Zustand state and implemented it on the project
This commit is contained in:
parent
e3c3b18bdc
commit
d44648d6c1
16 changed files with 141 additions and 62 deletions
|
|
@ -16,7 +16,6 @@ import {
|
|||
FETCH_ERROR_MESSAGE,
|
||||
} from "./constants/constants";
|
||||
import { AuthContext } from "./contexts/authContext";
|
||||
import { FlowsContext } from "./contexts/flowsContext";
|
||||
import { locationContext } from "./contexts/locationContext";
|
||||
import { getHealth, getVersion } from "./controllers/API";
|
||||
import Router from "./routes";
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import {
|
|||
LANGFLOW_SUPPORTED_TYPES,
|
||||
TOOLTIP_EMPTY,
|
||||
} from "../../../../constants/constants";
|
||||
import { FlowsContext } from "../../../../contexts/flowsContext";
|
||||
import { undoRedoContext } from "../../../../contexts/undoRedoContext";
|
||||
import { postCustomComponentUpdate } from "../../../../controllers/API";
|
||||
import useAlertStore from "../../../../stores/alertStore";
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ export default function BuildTrigger({
|
|||
isBuilt: boolean;
|
||||
}): JSX.Element {
|
||||
const { updateSSEData, isBuilding, setIsBuilding, sseData } = useSSE();
|
||||
const { saveFlow } = useContext(FlowsContext);
|
||||
const nodes = useFlowStore((state) => state.nodes);
|
||||
const edges = useFlowStore((state) => state.edges);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
|
|
@ -34,6 +33,7 @@ export default function BuildTrigger({
|
|||
const setCurrentFlowState = useFlowsManagerStore(
|
||||
(state) => state.setCurrentFlowState
|
||||
);
|
||||
const saveFlow = useFlowsManagerStore((state) => state.saveFlow);
|
||||
|
||||
const [isIconTouched, setIsIconTouched] = useState(false);
|
||||
const eventClick = isBuilding ? "pointer-events-none" : "";
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import BuildTrigger from "./buildTrigger";
|
|||
import ChatTrigger from "./chatTrigger";
|
||||
|
||||
import * as _ from "lodash";
|
||||
import { FlowsContext } from "../../contexts/flowsContext";
|
||||
import { getBuildStatus } from "../../controllers/API";
|
||||
import FormModal from "../../modals/formModal";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import {
|
|||
undoRedoContextType,
|
||||
} from "../types/typesContext";
|
||||
import { isWrappedWithClass } from "../utils/utils";
|
||||
import { FlowsContext } from "./flowsContext";
|
||||
import useFlowsManagerStore from "../stores/flowsManagerStore";
|
||||
|
||||
const initialValue = {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export default function FlowSettingsModal({
|
|||
open,
|
||||
setOpen,
|
||||
}: FlowSettingsPropsType): JSX.Element {
|
||||
const { saveFlow } = useContext(FlowsContext);
|
||||
const saveFlow = useFlowsManagerStore((state) => state.saveFlow);
|
||||
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||
const flows = useFlowsManagerStore((state) => state.flows);
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import { getTagsIds } from "../../utils/storeUtils";
|
|||
import ConfirmationModal from "../ConfirmationModal";
|
||||
import BaseModal from "../baseModal";
|
||||
import { useDarkStore } from "../../stores/darkStore";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
|
||||
export default function ShareModal({
|
||||
component,
|
||||
|
|
@ -57,7 +58,7 @@ export default function ShareModal({
|
|||
const [unavaliableNames, setUnavaliableNames] = useState<
|
||||
{ id: string; name: string }[]
|
||||
>([]);
|
||||
const { saveFlow } = useContext(FlowsContext);
|
||||
const saveFlow = useFlowsManagerStore((state) => state.saveFlow);
|
||||
|
||||
const [loadingNames, setLoadingNames] = useState(false);
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import {
|
|||
ADMIN_HEADER_TITLE,
|
||||
} from "../../constants/constants";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { FlowsContext } from "../../contexts/flowsContext";
|
||||
import {
|
||||
addUser,
|
||||
deleteUser,
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import ConnectionLineComponent from "../ConnectionLineComponent";
|
|||
import SelectionMenu from "../SelectionMenuComponent";
|
||||
import ExtraSidebar from "../extraSidebarComponent";
|
||||
import { useTypesStore } from "../../../../stores/typesStore";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
|
||||
const nodeTypes = {
|
||||
genericNode: GenericNode,
|
||||
|
|
@ -54,7 +55,8 @@ export default function Page({
|
|||
flow: FlowType;
|
||||
view?: boolean;
|
||||
}): JSX.Element {
|
||||
let { uploadFlow, saveFlow } = useContext(FlowsContext);
|
||||
let { uploadFlow } = useContext(FlowsContext);
|
||||
const autoSaveCurrentFlow = useFlowsManagerStore((state) => state.autoSaveCurrentFlow);
|
||||
const types = useTypesStore((state) => state.types);
|
||||
const templates = useTypesStore((state) => state.templates);
|
||||
const setFilterEdge = useTypesStore((state) => state.setFilterEdge);
|
||||
|
|
@ -202,7 +204,7 @@ export default function Page({
|
|||
|
||||
const onNodeDragStop: NodeDragHandler = useCallback(() => {
|
||||
// 👇 make dragging a node undoable
|
||||
saveFlow(undefined, true);
|
||||
autoSaveCurrentFlow(nodes, edges, reactFlowInstance?.getViewport()!);
|
||||
// 👉 you can place your event handlers here
|
||||
}, [takeSnapshot]);
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
const templates = useTypesStore((state) => state.templates);
|
||||
const getFilterEdge = useTypesStore((state) => state.getFilterEdge);
|
||||
const setFilterEdge = useTypesStore((state) => state.setFilterEdge);
|
||||
const { uploadFlow, saveFlow } = useContext(FlowsContext);
|
||||
const { uploadFlow } = useContext(FlowsContext);
|
||||
const saveFlow = useFlowsManagerStore((state) => state.saveFlow);
|
||||
const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance);
|
||||
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
|
||||
const hasStore = useStoreStore((state) => state.hasStore);
|
||||
const hasApiKey = useStoreStore((state) => state.hasApiKey);
|
||||
|
|
@ -302,7 +304,7 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
(isPending ? "" : "button-disable")
|
||||
}
|
||||
onClick={(event) => {
|
||||
saveFlow();
|
||||
saveFlow({...currentFlow, data: {...currentFlow.data!, viewport: reactFlowInstance?.getViewport()!} }, true);
|
||||
}}
|
||||
>
|
||||
<IconComponent
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import {
|
|||
} from "../../../../utils/reactflowUtils";
|
||||
import { classNames } from "../../../../utils/utils";
|
||||
import { useDarkStore } from "../../../../stores/darkStore";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
|
||||
export default function NodeToolbarComponent({
|
||||
data,
|
||||
|
|
@ -73,7 +74,8 @@ export default function NodeToolbarComponent({
|
|||
const setNodes = useFlowStore((state) => state.setNodes);
|
||||
const setEdges = useFlowStore((state) => state.setEdges);
|
||||
|
||||
const { saveComponent, flows } = useContext(FlowsContext);
|
||||
const { saveComponent } = useContext(FlowsContext);
|
||||
const flows = useFlowsManagerStore((state) => state.flows);
|
||||
const version = useDarkStore((state) => state.version);
|
||||
const { takeSnapshot } = useContext(undoRedoContext);
|
||||
const [showModalAdvanced, setShowModalAdvanced] = useState(false);
|
||||
|
|
|
|||
|
|
@ -9,14 +9,16 @@ import { Button } from "../../../../components/ui/button";
|
|||
import { FlowsContext } from "../../../../contexts/flowsContext";
|
||||
import useAlertStore from "../../../../stores/alertStore";
|
||||
import { FlowType } from "../../../../types/flow";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
|
||||
export default function ComponentsComponent({
|
||||
is_component = true,
|
||||
}: {
|
||||
is_component?: boolean;
|
||||
}) {
|
||||
const { flows, removeFlow, uploadFlow, addFlow, isLoading } =
|
||||
const { removeFlow, uploadFlow, addFlow, isLoading } =
|
||||
useContext(FlowsContext);
|
||||
const flows = useFlowsManagerStore((state) => state.flows);
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import {
|
|||
scapeJSONParse,
|
||||
scapedJSONStringfy,
|
||||
} from "../utils/reactflowUtils";
|
||||
import useFlowsManagerStore from "./flowsManagerStore";
|
||||
|
||||
// this is our useStore hook that we can use in our components to get parts of the store and call actions
|
||||
const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||
|
|
@ -55,11 +56,27 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
|
||||
set({ edges: newEdges });
|
||||
set({ nodes: newChange });
|
||||
|
||||
useFlowsManagerStore
|
||||
.getState()
|
||||
.autoSaveCurrentFlow(
|
||||
newChange,
|
||||
newEdges,
|
||||
get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 }
|
||||
);
|
||||
},
|
||||
setEdges: (change) => {
|
||||
let newChange = typeof change === "function" ? change(get().edges) : change;
|
||||
|
||||
set({ edges: newChange });
|
||||
|
||||
useFlowsManagerStore
|
||||
.getState()
|
||||
.autoSaveCurrentFlow(
|
||||
get().nodes,
|
||||
newChange,
|
||||
get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 }
|
||||
);
|
||||
},
|
||||
setNode: (id: string, change: Node | ((oldState: Node) => Node)) => {
|
||||
let newChange =
|
||||
|
|
|
|||
|
|
@ -1,40 +1,19 @@
|
|||
import { cloneDeep } from "lodash";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import { AxiosError } from "axios";
|
||||
import { Edge, Node, Viewport } from "reactflow";
|
||||
import { create } from "zustand";
|
||||
import { readFlowsFromDatabase } from "../controllers/API";
|
||||
import { APIClassType } from "../types/api";
|
||||
import { FlowType, NodeDataType } from "../types/flow";
|
||||
import {
|
||||
readFlowsFromDatabase,
|
||||
updateFlowInDatabase,
|
||||
} from "../controllers/API";
|
||||
import { FlowType } from "../types/flow";
|
||||
import { FlowState } from "../types/tabs";
|
||||
import { FlowsManagerStoreType } from "../types/zustand/flowsManager";
|
||||
import { processDataFromFlow } from "../utils/reactflowUtils";
|
||||
import { createRandomKey } from "../utils/utils";
|
||||
import { useTypesStore } from "./typesStore";
|
||||
import { processFlows } from "../utils/reactflowUtils";
|
||||
import useAlertStore from "./alertStore";
|
||||
import useFlowStore from "./flowStore";
|
||||
import { useTypesStore } from "./typesStore";
|
||||
|
||||
const uid = new ShortUniqueId({ length: 5 });
|
||||
|
||||
const processFlows = (DbData: FlowType[], skipUpdate = true) => {
|
||||
let savedComponents: { [key: string]: APIClassType } = {};
|
||||
DbData.forEach((flow: FlowType) => {
|
||||
try {
|
||||
if (!flow.data) {
|
||||
return;
|
||||
}
|
||||
if (flow.data && flow.is_component) {
|
||||
(flow.data.nodes[0].data as NodeDataType).node!.display_name =
|
||||
flow.name;
|
||||
savedComponents[
|
||||
createRandomKey((flow.data.nodes[0].data as NodeDataType).type, uid())
|
||||
] = cloneDeep((flow.data.nodes[0].data as NodeDataType).node!);
|
||||
return;
|
||||
}
|
||||
if (!skipUpdate) processDataFromFlow(flow, false);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
return { data: savedComponents, flows: DbData };
|
||||
};
|
||||
let saveTimeoutId: NodeJS.Timeout | null = null;
|
||||
|
||||
const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
||||
currentFlowId: "",
|
||||
|
|
@ -69,21 +48,73 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
|||
refreshFlows: () => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
set({ isLoading: true });
|
||||
readFlowsFromDatabase().then((dbData) => {
|
||||
if (dbData) {
|
||||
const { data, flows } = processFlows(dbData, false);
|
||||
set({ flows, isLoading: false });
|
||||
useTypesStore.setState((state) => ({
|
||||
data: { ...state.data, ["saved_components"]: data },
|
||||
}));
|
||||
resolve();
|
||||
}
|
||||
}).catch((e) => {
|
||||
useAlertStore.getState().setErrorData({
|
||||
title: "Could not load flows from database",
|
||||
readFlowsFromDatabase()
|
||||
.then((dbData) => {
|
||||
if (dbData) {
|
||||
const { data, flows } = processFlows(dbData, false);
|
||||
set({ flows, isLoading: false });
|
||||
useTypesStore.setState((state) => ({
|
||||
data: { ...state.data, ["saved_components"]: data },
|
||||
}));
|
||||
resolve();
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
useAlertStore.getState().setErrorData({
|
||||
title: "Could not load flows from database",
|
||||
});
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
},
|
||||
autoSaveCurrentFlow: (nodes: Node[], edges: Edge[], viewport: Viewport) => {
|
||||
// Clear the previous timeout if it exists.
|
||||
if (saveTimeoutId) {
|
||||
clearTimeout(saveTimeoutId);
|
||||
}
|
||||
|
||||
// Set up a new timeout.
|
||||
saveTimeoutId = setTimeout(() => {
|
||||
if (get().currentFlow) {
|
||||
get().saveFlow(
|
||||
{ ...get().currentFlow!, data: { nodes, edges, viewport } },
|
||||
true
|
||||
);
|
||||
}
|
||||
}, 300); // Delay of 300ms.
|
||||
},
|
||||
saveFlow: (flow: FlowType, silent?: boolean) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
updateFlowInDatabase(flow)
|
||||
.then((updatedFlow) => {
|
||||
if (updatedFlow) {
|
||||
// updates flow in state
|
||||
if (!silent) {
|
||||
useAlertStore
|
||||
.getState()
|
||||
.setSuccessData({ title: "Changes saved successfully" });
|
||||
}
|
||||
set((oldState) => ({
|
||||
flows: oldState.flows.map((flow) => {
|
||||
if (flow.id === updatedFlow.id) {
|
||||
return updatedFlow;
|
||||
}
|
||||
return flow;
|
||||
}),
|
||||
}));
|
||||
//update tabs state
|
||||
|
||||
useFlowStore.setState({ isPending: false });
|
||||
resolve();
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
useAlertStore.getState().setErrorData({
|
||||
title: "Error while saving changes",
|
||||
list: [(err as AxiosError).message],
|
||||
});
|
||||
reject(err);
|
||||
});
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
},
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { Node, Edge, Viewport } from "reactflow";
|
||||
import { FlowType } from "../../flow";
|
||||
import { FlowState, FlowsState } from "../../tabs";
|
||||
|
||||
|
|
@ -12,4 +13,6 @@ export type FlowsManagerStoreType = {
|
|||
currentFlowState: FlowState | undefined;
|
||||
setCurrentFlowState: (state: FlowState | ((oldState: FlowState | undefined) => FlowState)) => void;
|
||||
refreshFlows: () => Promise<void>;
|
||||
saveFlow: (flow: FlowType, silent?: boolean) => Promise<void>;
|
||||
autoSaveCurrentFlow: (nodes: Node[], edges: Edge[], viewport: Viewport) => void;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import _ from "lodash";
|
||||
import _, { cloneDeep } from "lodash";
|
||||
import {
|
||||
Connection,
|
||||
Edge,
|
||||
|
|
@ -13,6 +13,7 @@ import {
|
|||
specialCharsRegex,
|
||||
} from "../constants/constants";
|
||||
import {
|
||||
APIClassType,
|
||||
APIKindType,
|
||||
APIObjectType,
|
||||
APITemplateType,
|
||||
|
|
@ -31,7 +32,7 @@ import {
|
|||
unselectAllNodesType,
|
||||
updateEdgesHandleIdsType,
|
||||
} from "../types/utils/reactflowUtils";
|
||||
import { getFieldTitle, toTitleCase } from "./utils";
|
||||
import { createRandomKey, getFieldTitle, toTitleCase } from "./utils";
|
||||
const uid = new ShortUniqueId({ length: 5 });
|
||||
|
||||
export function cleanEdges(nodes: Node[], edges: Edge[]) {
|
||||
|
|
@ -153,6 +154,29 @@ export function updateTemplate(
|
|||
return clonedObject;
|
||||
}
|
||||
|
||||
export const processFlows = (DbData: FlowType[], skipUpdate = true) => {
|
||||
let savedComponents: { [key: string]: APIClassType } = {};
|
||||
DbData.forEach((flow: FlowType) => {
|
||||
try {
|
||||
if (!flow.data) {
|
||||
return;
|
||||
}
|
||||
if (flow.data && flow.is_component) {
|
||||
(flow.data.nodes[0].data as NodeDataType).node!.display_name =
|
||||
flow.name;
|
||||
savedComponents[
|
||||
createRandomKey((flow.data.nodes[0].data as NodeDataType).type, uid())
|
||||
] = cloneDeep((flow.data.nodes[0].data as NodeDataType).node!);
|
||||
return;
|
||||
}
|
||||
if (!skipUpdate) processDataFromFlow(flow, false);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
return { data: savedComponents, flows: DbData };
|
||||
};
|
||||
|
||||
export const processDataFromFlow = (flow: FlowType, refreshIds = true) => {
|
||||
let data = flow?.data ? flow.data : null;
|
||||
if (data) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue