diff --git a/.changeset/odd-hounds-enjoy.md b/.changeset/odd-hounds-enjoy.md new file mode 100644 index 0000000..fb333b8 --- /dev/null +++ b/.changeset/odd-hounds-enjoy.md @@ -0,0 +1,6 @@ +--- +"@pulse-editor/shared-utils": patch +"@pulse-editor/react-api": patch +--- + +Fix snapshot restore triggers at every state changes diff --git a/.changeset/pre.json b/.changeset/pre.json index fd367c3..2160fcb 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -39,6 +39,7 @@ "large-moose-tap", "lazy-zebras-mate", "mighty-ghosts-crash", + "odd-hounds-enjoy", "petite-memes-fix", "polite-lines-dance", "polite-rules-switch", diff --git a/npm-packages/react-api/CHANGELOG.md b/npm-packages/react-api/CHANGELOG.md index b039a94..a9ad7a6 100644 --- a/npm-packages/react-api/CHANGELOG.md +++ b/npm-packages/react-api/CHANGELOG.md @@ -1,5 +1,13 @@ # @pulse-editor/react-api +## 0.1.1-alpha.47 + +### Patch Changes + +- Fix snapshot restore triggers at every state changes +- Updated dependencies + - @pulse-editor/shared-utils@0.1.1-alpha.47 + ## 0.1.1-alpha.46 ### Patch Changes diff --git a/npm-packages/react-api/package.json b/npm-packages/react-api/package.json index 336d6f2..e046152 100644 --- a/npm-packages/react-api/package.json +++ b/npm-packages/react-api/package.json @@ -1,6 +1,6 @@ { "name": "@pulse-editor/react-api", - "version": "0.1.1-alpha.46", + "version": "0.1.1-alpha.47", "main": "dist/main.js", "files": [ "dist" @@ -38,7 +38,7 @@ "typescript-eslint": "^8.30.1" }, "peerDependencies": { - "@pulse-editor/shared-utils": "0.1.1-alpha.46", + "@pulse-editor/shared-utils": "0.1.1-alpha.47", "react": "^19.0.0", "react-dom": "^19.0.0" } diff --git a/npm-packages/react-api/src/hooks/editor/use-snapshot-state.ts b/npm-packages/react-api/src/hooks/editor/use-snapshot-state.ts index 94df3f5..a5f5a3e 100644 --- a/npm-packages/react-api/src/hooks/editor/use-snapshot-state.ts +++ b/npm-packages/react-api/src/hooks/editor/use-snapshot-state.ts @@ -1,4 +1,4 @@ -import { useContext, useEffect, useState } from "react"; +import { useContext, useEffect, useRef, useState } from "react"; import { SnapshotContext } from "../../providers/snapshot-provider"; export default function useSnapShotState( @@ -7,25 +7,23 @@ export default function useSnapShotState( onRestore?: (value: T) => void ) { const snapshotContext = useContext(SnapshotContext); - if (!snapshotContext) { throw new Error("useSnapShotState must be used within a SnapshotProvider"); } const { states, setStates } = snapshotContext; - - // Initialize state with the value from context or the initial value const [state, setState] = useState( states[key] !== undefined ? states[key] : initialValue ); - // Update context whenever state changes + const isLocalUpdate = useRef(false); + const setSnapshotState: React.Dispatch> = (value) => { setState((prev) => { const newValue = typeof value === "function" ? (value as (prev: T) => T)(prev) : value; - // Defer the setStates call to next microtask, outside render phase + isLocalUpdate.current = true; // mark as local Promise.resolve().then(() => { setStates((prevStates) => ({ ...prevStates, @@ -37,26 +35,31 @@ export default function useSnapShotState( }); }; - // Set the initial value in context if not already set + // Initialize context with initial value useEffect(() => { - // Only set if the key does not exist in the context if (states[key] === undefined && initialValue !== undefined) { - setStates((prevStates) => ({ - ...prevStates, + setStates((prev) => ({ + ...prev, [key]: initialValue, })); } }, []); - // Restore state from context when key or states change + // Only restore when external changes occur useEffect(() => { - console.log("Restoring state for key:", key, states[key]); + const contextValue = states[key]; + if (contextValue === undefined) return; + + if (isLocalUpdate.current) { + // skip this run because we caused it ourselves + isLocalUpdate.current = false; + return; + } - if (states[key] !== undefined && states[key] !== state) { - setState(states[key]); - if (onRestore) { - onRestore(states[key]); - } + if (contextValue !== state) { + console.log("Restoring state for key:", key, contextValue); + setState(contextValue); + onRestore?.(contextValue); } }, [states[key]]); diff --git a/npm-packages/shared-utils/CHANGELOG.md b/npm-packages/shared-utils/CHANGELOG.md index 4bf88b2..f2e5c9f 100644 --- a/npm-packages/shared-utils/CHANGELOG.md +++ b/npm-packages/shared-utils/CHANGELOG.md @@ -1,5 +1,11 @@ # @pulse-editor/shared-utils +## 0.1.1-alpha.47 + +### Patch Changes + +- Fix snapshot restore triggers at every state changes + ## 0.1.1-alpha.46 ### Patch Changes diff --git a/npm-packages/shared-utils/package.json b/npm-packages/shared-utils/package.json index 50861d4..8130ddf 100644 --- a/npm-packages/shared-utils/package.json +++ b/npm-packages/shared-utils/package.json @@ -1,6 +1,6 @@ { "name": "@pulse-editor/shared-utils", - "version": "0.1.1-alpha.46", + "version": "0.1.1-alpha.47", "main": "dist/main.js", "files": [ "dist" diff --git a/web/components/marketplace/app/app-gallery.tsx b/web/components/marketplace/app/app-gallery.tsx index 379b325..0b2197e 100644 --- a/web/components/marketplace/app/app-gallery.tsx +++ b/web/components/marketplace/app/app-gallery.tsx @@ -143,6 +143,8 @@ export default function AppGallery() {
| undefined >(initialWorkflowContent?.defaultEntryPoint); + const [isRestored, setIsRestored] = useState(false); + // Update entry points useEffect(() => { debouncedGetEntryPoint(); @@ -47,14 +49,17 @@ export default function useCanvasWorkflow( useEffect(() => { async function restore() { if (!imcContext) return; + else if (isRestored) return; + else if (!initialWorkflowContent) return; + setIsRestored(true); - if (initialWorkflowContent?.snapshotStates) { + if (initialWorkflowContent.snapshotStates) { await restoreAppsSnapshotStates(initialWorkflowContent); } } restore(); - }, [initialWorkflowContent, imcContext]); + }, [initialWorkflowContent, imcContext, isRestored]); async function startWorkflow() { // DAG traversal using Kahn's algorithm (topological sort)