Canvas: Implement streaming state updates
Change-Id: I2bc5a51b5792839bde93f927f5ffea22b3250fe2
diff --git a/apps/canvas/front/src/lib/state.ts b/apps/canvas/front/src/lib/state.ts
index cd5f29c..0132721 100644
--- a/apps/canvas/front/src/lib/state.ts
+++ b/apps/canvas/front/src/lib/state.ts
@@ -209,6 +209,7 @@
githubRepositories: GitHubRepository[];
githubRepositoriesLoading: boolean;
githubRepositoriesError: string | null;
+ stateEventSource: EventSource | null;
setHighlightCategory: (name: string, active: boolean) => void;
onNodesChange: OnNodesChange<AppNode>;
onEdgesChange: OnEdgesChange;
@@ -409,24 +410,6 @@
});
};
- const restoreSaved = async () => {
- const { projectId } = get();
- const resp = await fetch(`/api/project/${projectId}/saved/${get().mode === "deploy" ? "deploy" : "draft"}`, {
- method: "GET",
- });
- const inst = await resp.json();
- setN(inst.state.nodes);
- set({ edges: inst.state.edges });
- injectNetworkNodes();
- if (
- get().zoom.x !== inst.state.viewport.x ||
- get().zoom.y !== inst.state.viewport.y ||
- get().zoom.zoom !== inst.state.viewport.zoom
- ) {
- set({ zoom: inst.state.viewport });
- }
- };
-
function updateNodeData<T extends NodeType>(id: string, data: NodeDataUpdate<T>): void {
setN(
get().nodes.map((n) => {
@@ -629,6 +612,43 @@
}
};
+ const disconnectFromStateStream = () => {
+ const { stateEventSource } = get();
+ if (stateEventSource) {
+ stateEventSource.close();
+ set({ stateEventSource: null });
+ }
+ };
+
+ const connectToStateStream = (projectId: string, mode: "deploy" | "edit") => {
+ disconnectFromStateStream();
+
+ const eventSource = new EventSource(
+ `/api/project/${projectId}/state/stream/${mode === "edit" ? "draft" : "deploy"}`,
+ );
+ set({ stateEventSource: eventSource });
+
+ eventSource.onmessage = (event) => {
+ const inst = JSON.parse(event.data);
+ setN(inst.nodes);
+ set({ edges: inst.edges });
+ injectNetworkNodes();
+ if (
+ get().zoom.x !== inst.viewport.x ||
+ get().zoom.y !== inst.viewport.y ||
+ get().zoom.zoom !== inst.viewport.zoom
+ ) {
+ set({ zoom: inst.viewport });
+ }
+ };
+
+ eventSource.onerror = (err) => {
+ console.error("EventSource failed:", err);
+ eventSource.close();
+ set({ stateEventSource: null });
+ };
+ };
+
return {
projectId: undefined,
mode: "edit",
@@ -654,6 +674,7 @@
githubRepositories: [],
githubRepositoriesLoading: false,
githubRepositoriesError: null,
+ stateEventSource: null,
setViewport: (viewport) => {
const { viewport: vp } = get();
if (
@@ -780,7 +801,12 @@
}
},
setMode: (mode) => {
+ disconnectFromStateStream();
set({ mode });
+ const projectId = get().projectId;
+ if (projectId) {
+ connectToStateStream(projectId, mode);
+ }
},
setProject: async (projectId) => {
const currentProjectId = get().projectId;
@@ -788,6 +814,7 @@
return;
}
stopRefreshEnvInterval();
+ disconnectFromStateStream();
set({
projectId,
githubRepositories: [],
@@ -801,7 +828,8 @@
} else {
set({ mode: "edit" });
}
- restoreSaved();
+ const mode = get().mode;
+ connectToStateStream(projectId, mode);
startRefreshEnvInterval();
} else {
set({