Canvas: fix empty config tab

Change-Id: I7b59790577478d0b75c0b62e970bc7e95282f65a
diff --git a/apps/canvas/front/src/Config.tsx b/apps/canvas/front/src/Config.tsx
index 2b8b1f7..b92e99e 100644
--- a/apps/canvas/front/src/Config.tsx
+++ b/apps/canvas/front/src/Config.tsx
@@ -1,25 +1,13 @@
-import { useNodes } from "@xyflow/react";
-import { AppNode, useEnv, useProjectId } from "./lib/state";
+import { useStateStore } from "./lib/state";
 import { generateDodoConfig } from "./lib/config";
-import { useEffect, useMemo, useState } from "react";
 import JSONView from "@microlink/react-json-view";
 
 export function Config() {
-	const env = useEnv();
-	const projectId = useProjectId();
-	const [nodes, setNodes] = useState<AppNode[]>([]);
-	const n = useNodes<AppNode>();
-	useEffect(() => {
-		console.log(n);
-		if (n && n.length > 0) {
-			setNodes(n);
-		}
-	}, [n, setNodes]);
-	const config = useMemo(() => generateDodoConfig(projectId, nodes, env) || {}, [projectId, nodes, env]);
+	const store = useStateStore();
 	return (
 		<div className="h-full p-4 bg-muted rounded-lg overflow-auto">
 			<JSONView
-				src={config as object}
+				src={generateDodoConfig(store.projectId, store.nodes, store.env) as object}
 				theme="rjv-default"
 				name={false}
 				displayDataTypes={false}
diff --git a/apps/canvas/front/src/ProjectSelect.tsx b/apps/canvas/front/src/ProjectSelect.tsx
index 699fb04..6903c5e 100644
--- a/apps/canvas/front/src/ProjectSelect.tsx
+++ b/apps/canvas/front/src/ProjectSelect.tsx
@@ -1,7 +1,6 @@
 import { ChangeEvent, useCallback, useEffect, useState } from "react";
 import { Project, useProjectId, useStateStore } from "./lib/state";
 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./components/ui/select";
-import { useReactFlow } from "@xyflow/react";
 import { Input } from "./components/ui/input";
 import { Button } from "./components/ui/button";
 import { Dialog, DialogContent, DialogTrigger } from "./components/ui/dialog";
@@ -37,7 +36,6 @@
 		refreshProjects();
 	}, [refreshProjects]);
 
-	const project = useProjectId();
 	const [createNewOpen, setCreateNewOpen] = useState(false);
 	const onSelect = useCallback(
 		(projectId: string) => {
@@ -49,26 +47,6 @@
 		},
 		[store],
 	);
-	const instance = useReactFlow();
-	const restoreSaved = useCallback(
-		async (projectId: string) => {
-			const resp = await fetch(`/api/project/${projectId}/saved`, {
-				method: "GET",
-			});
-			const inst = await resp.json();
-			const { x = 0, y = 0, zoom = 1 } = inst.viewport;
-			instance.setNodes(inst.nodes || []);
-			instance.setEdges(inst.edges || []);
-			instance.setViewport({ x, y, zoom });
-		},
-		[instance],
-	);
-	useEffect(() => {
-		if (project == null) {
-			return;
-		}
-		restoreSaved(project);
-	}, [project, restoreSaved]);
 	const [name, setName] = useState<string | undefined>(undefined);
 	const updateName = useCallback(
 		(e: ChangeEvent<HTMLInputElement>) => {
@@ -111,9 +89,9 @@
 			});
 	}, [name, setCreateNewOpen, toast, store, refreshProjects]);
 	return (
-		<Select onValueChange={onSelect} value={project}>
+		<Select onValueChange={onSelect} value={projectId}>
 			<SelectTrigger className="w-[200px] !border-none !shadow-none !focus:ring-0 !focus:ring-offset-0">
-				<SelectValue placeholder="Choose Project" defaultValue={project} />
+				<SelectValue placeholder="Choose Project" defaultValue={projectId} />
 			</SelectTrigger>
 			<SelectContent>
 				{projects.map((p) => (
diff --git a/apps/canvas/front/src/lib/state.ts b/apps/canvas/front/src/lib/state.ts
index fb461c2..428ae17 100644
--- a/apps/canvas/front/src/lib/state.ts
+++ b/apps/canvas/front/src/lib/state.ts
@@ -418,11 +418,24 @@
 
 export const useStateStore = create<AppState>((set, get): AppState => {
 	const setN = (nodes: AppNode[]) => {
-		set((state) => ({
-			...state,
+		if (nodes.length == 0) {
+			console.trace("setN", nodes);
+		}
+		set({
 			nodes,
 			messages: v(nodes),
-		}));
+		});
+	};
+
+	const restoreSaved = async () => {
+		const resp = await fetch(`/api/project/${get().projectId}/saved`, {
+			method: "GET",
+		});
+		const inst = await resp.json();
+		// const { x = 0, y = 0, zoom = 1 } = inst.viewport;
+		setN(inst.nodes || []);
+		get().setEdges(inst.edges || []);
+		// instance.setViewport({ x, y, zoom });
 	};
 
 	function updateNodeData<T extends NodeType>(id: string, data: NodeDataUpdate<T>): void {
@@ -688,11 +701,14 @@
 			} catch (error) {
 				console.error("Failed to fetch integrations:", error);
 			} finally {
-				set({ env: env });
-				if (env.integrations.github) {
-					set({ githubService: new GitHubServiceImpl(projectId!) });
-				} else {
-					set({ githubService: null });
+				if (JSON.stringify(get().env) !== JSON.stringify(env)) {
+					set({ env });
+
+					if (env.integrations.github) {
+						set({ githubService: new GitHubServiceImpl(projectId!) });
+					} else {
+						set({ githubService: null });
+					}
 				}
 			}
 		},
@@ -702,6 +718,12 @@
 			});
 			if (projectId) {
 				get().refreshEnv();
+				restoreSaved();
+			} else {
+				set({
+					nodes: [],
+					edges: [],
+				});
 			}
 		},
 	};