fix: persist canvas deletions before refresh
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-02-19 13:56:17 +08:00
parent d68928233a
commit ddadac3a67

View File

@ -133,7 +133,7 @@ export default function CanvasEditorPage({ params }: PageProps) {
const [negativePrompt, setNegativePrompt] = useState("");
const [aspectRatio, setAspectRatio] = useState("1:1");
const [imageCount, setImageCount] = useState(1);
const previousNodeCountRef = useRef(0);
const previousNodeIdsRef = useRef<Set<string>>(new Set());
// 获取选中的图片节点
const selectedImageNode = selectedNodeIds.length === 1
@ -174,15 +174,26 @@ export default function CanvasEditorPage({ params }: PageProps) {
loadProject();
}, [id]); // 只依赖 id,使用 getState() 直接调用 store actions
const persistCanvasState = useCallback(
async (reason: "autosave" | "delete" | "beforeunload") => {
const canvasData = getCanvasState();
const result = await canvasService.saveCanvasState(id, canvasData);
if (!result.success) {
console.error(`[Canvas] ${reason} save failed:`, result.error);
}
return result.success;
},
[getCanvasState, id]
);
// 自动保存逻辑
useEffect(() => {
if (isLoading) return;
const saveTimer = setTimeout(async () => {
setIsSaving(true);
const canvasData = getCanvasState();
try {
await canvasService.saveCanvasState(id, canvasData);
await persistCanvasState("autosave");
} catch (error) {
console.error("Auto save failed:", error);
}
@ -190,24 +201,30 @@ export default function CanvasEditorPage({ params }: PageProps) {
}, 3000); // 3秒自动保存
return () => clearTimeout(saveTimer);
}, [nodes, id, isLoading, getCanvasState]);
}, [nodes, isLoading, persistCanvasState]);
// 删除节点时立即保存,避免用户立刻刷新后看到被删除节点又出现
// 删除节点时立即保存(按节点ID差集检测),避免删除后快速刷新被回滚
useEffect(() => {
if (isLoading) {
previousNodeCountRef.current = nodes.length;
previousNodeIdsRef.current = new Set(nodes.map((node) => node.id));
return;
}
const previousCount = previousNodeCountRef.current;
const currentCount = nodes.length;
previousNodeCountRef.current = currentCount;
const previousIds = previousNodeIdsRef.current;
const currentIds = new Set(nodes.map((node) => node.id));
const removedIds: string[] = [];
previousIds.forEach((nodeId) => {
if (!currentIds.has(nodeId)) {
removedIds.push(nodeId);
}
});
previousNodeIdsRef.current = currentIds;
if (previousCount > currentCount) {
if (removedIds.length > 0) {
const persistAfterDelete = async () => {
setIsSaving(true);
try {
await canvasService.saveCanvasState(id, getCanvasState());
await persistCanvasState("delete");
} catch (error) {
console.error("Delete immediate save failed:", error);
}
@ -215,7 +232,33 @@ export default function CanvasEditorPage({ params }: PageProps) {
};
void persistAfterDelete();
}
}, [nodes.length, id, isLoading, getCanvasState]);
}, [nodes, isLoading, persistCanvasState]);
// 页面关闭/刷新前尝试做一次 keepalive 保存,降低“刚删就刷新”回滚概率
useEffect(() => {
if (isLoading) return;
const handleBeforeUnload = () => {
try {
const canvasData = getCanvasState();
void fetch(`/api/canvas/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ canvasData }),
keepalive: true,
});
} catch (error) {
console.error("[Canvas] beforeunload save failed:", error);
}
};
window.addEventListener("beforeunload", handleBeforeUnload);
window.addEventListener("pagehide", handleBeforeUnload);
return () => {
window.removeEventListener("beforeunload", handleBeforeUnload);
window.removeEventListener("pagehide", handleBeforeUnload);
};
}, [getCanvasState, id, isLoading]);
// 智能命名与缩略图逻辑
useEffect(() => {