fix(canvas): 画布同步 - 加载云端最新版时保留编辑器并防止并发内容覆盖
This commit is contained in:
1 parent
da1560aa56
commit
025b5e844a
4 files changed
+113
-28
No files matched your search
@@ -96,17 +96,25 @@ export function useCanvasProjectLifecycle({
|
||||
const observedContentRef = useRef<CanvasHistorySnapshot | null>(null);
|
||||
const loadLatestRef = useRef(false);
|
||||
const historyRestoreRef = useRef<{ snapshotId: string; revision: number; resolve: () => void; reject: (error: unknown) => void } | null>(null);
|
||||
const pendingReloadRef = useRef<{ resolve: () => void; reject: (error: unknown) => void } | null>(null);
|
||||
const editorReadyRef = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hydrated || !sessionHydrated) return;
|
||||
let cancelled = false;
|
||||
setProjectLoaded(false);
|
||||
setLoadError("");
|
||||
// Keep load intent on the refs until this attempt finishes. React Strict
|
||||
// Mode remounts the effect; consuming the flags here would turn "load
|
||||
// latest" into a normal open and immediately recreate the conflict.
|
||||
const latest = loadLatestRef.current;
|
||||
loadLatestRef.current = false;
|
||||
const historyRestore = historyRestoreRef.current;
|
||||
historyRestoreRef.current = null;
|
||||
observedContentRef.current = null;
|
||||
const pendingReload = pendingReloadRef.current;
|
||||
const keepEditor = editorReadyRef.current && (latest || Boolean(historyRestore));
|
||||
if (!keepEditor) {
|
||||
editorReadyRef.current = false;
|
||||
setProjectLoaded(false);
|
||||
setLoadError("");
|
||||
observedContentRef.current = null;
|
||||
}
|
||||
const applyRestoredProject = (targetProject: CanvasProject) => {
|
||||
if (cancelled) return;
|
||||
const fallbackTheme = useCanvasThemeStore.getState().theme;
|
||||
@@ -142,6 +150,7 @@ export function useCanvasProjectLifecycle({
|
||||
setShowImageInfo(snapshot.showImageInfo);
|
||||
setViewport(targetProject.viewport);
|
||||
resetHistory(snapshot);
|
||||
editorReadyRef.current = true;
|
||||
setProjectLoaded(true);
|
||||
};
|
||||
|
||||
@@ -152,8 +161,11 @@ export function useCanvasProjectLifecycle({
|
||||
applyRestoredProject(cachedProject);
|
||||
}
|
||||
const loadedProject = await loadCanvasProjectForEditing(projectId, { latest, historyRestore: historyRestore || undefined, onLoad: applyRestoredProject });
|
||||
historyRestore?.resolve();
|
||||
if (cancelled) return;
|
||||
if (historyRestoreRef.current === historyRestore) {
|
||||
historyRestoreRef.current = null;
|
||||
historyRestore?.resolve();
|
||||
}
|
||||
if (!loadedProject) {
|
||||
if (!cachedProject) navigate("/canvas", { replace: true });
|
||||
return;
|
||||
@@ -173,14 +185,33 @@ export function useCanvasProjectLifecycle({
|
||||
if (!cancelled) message.warning("部分助手会话素材恢复失败,已使用项目记录继续打开");
|
||||
});
|
||||
};
|
||||
void load().catch((error) => {
|
||||
historyRestore?.reject(error);
|
||||
if (!cancelled && useSyncProgressStore.getState().syncingProjects[projectId]?.phase !== "conflict") useSyncProgressStore.getState().setProjectProgress(projectId, { phase: "error", message: error instanceof Error ? error.message : "读取云端版本失败" });
|
||||
if (!cancelled) setLoadError(error instanceof Error ? error.message : "读取画布失败,请重试");
|
||||
});
|
||||
void load()
|
||||
.then(() => {
|
||||
if (cancelled) return;
|
||||
loadLatestRef.current = false;
|
||||
if (pendingReloadRef.current === pendingReload) {
|
||||
pendingReloadRef.current = null;
|
||||
pendingReload?.resolve();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (cancelled) return;
|
||||
loadLatestRef.current = false;
|
||||
if (historyRestoreRef.current === historyRestore) {
|
||||
historyRestoreRef.current = null;
|
||||
historyRestore?.reject(error);
|
||||
}
|
||||
if (pendingReloadRef.current === pendingReload) {
|
||||
pendingReloadRef.current = null;
|
||||
pendingReload?.reject(error);
|
||||
}
|
||||
if (useSyncProgressStore.getState().syncingProjects[projectId]?.phase !== "conflict") useSyncProgressStore.getState().setProjectProgress(projectId, { phase: "error", message: error instanceof Error ? error.message : "读取云端版本失败" });
|
||||
const detail = error instanceof Error ? error.message : "读取画布失败,请重试";
|
||||
if (keepEditor) message.error(detail);
|
||||
else setLoadError(detail);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
historyRestore?.reject(new Error("画布页面已关闭,请重新打开查看恢复结果"));
|
||||
};
|
||||
}, [hydrated, sessionHydrated, loadAttempt, message, navigate, openProject, projectId, resetHistory, setActiveChatId, setBackgroundMode, setCanvasAppearance, setChatSessions, setConnections, setNodes, setShowImageInfo, setViewport]);
|
||||
|
||||
@@ -301,8 +332,12 @@ export function useCanvasProjectLifecycle({
|
||||
|
||||
const reloadLatestCanvasProject = useCallback(async () => {
|
||||
await persistLocalEdits();
|
||||
loadLatestRef.current = true;
|
||||
setLoadAttempt((value) => value + 1);
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
pendingReloadRef.current?.reject(new Error("已有新的加载请求"));
|
||||
loadLatestRef.current = true;
|
||||
pendingReloadRef.current = { resolve, reject };
|
||||
setLoadAttempt((value) => value + 1);
|
||||
});
|
||||
}, [persistLocalEdits]);
|
||||
|
||||
const restoreCanvasProjectVersion = useCallback(async (snapshotId: string, revision: number) => {
|
||||
|
||||
Reference in new issue
Block a user