Fix canvas generation cleanup
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { type Dispatch, type SetStateAction, useEffect, useRef, useState } from "react";
|
||||
import { type Dispatch, type SetStateAction, useCallback, useEffect, useRef, useState } from "react";
|
||||
import type {
|
||||
CanvasImageGenerationState,
|
||||
CanvasImageNode,
|
||||
@@ -66,6 +66,8 @@ export function useCanvasGeneration(params: UseCanvasGenerationParams) {
|
||||
const videoGenerationInFlightRef = useRef(new Set<string>());
|
||||
const textGenerationInFlightRef = useRef(new Set<string>());
|
||||
const textGenerationAbortControllersRef = useRef(new Map<string, AbortController>());
|
||||
const imageGenerationAbortRef = useRef(new Map<string, { current: boolean }>());
|
||||
const videoGenerationAbortRef = useRef(new Map<string, { current: boolean }>());
|
||||
const canvasGenKeepaliveRestoredRef = useRef(false);
|
||||
|
||||
const setTextGenerationStatus = (nodeId: string, state: CanvasTextGenerationState) => {
|
||||
@@ -80,6 +82,15 @@ export function useCanvasGeneration(params: UseCanvasGenerationParams) {
|
||||
setVideoGenerationState((current) => ({ ...current, [nodeId]: state }));
|
||||
};
|
||||
|
||||
const abortAllGenerationPollers = useCallback(() => {
|
||||
textGenerationAbortControllersRef.current.forEach((c) => c.abort());
|
||||
textGenerationAbortControllersRef.current.clear();
|
||||
imageGenerationAbortRef.current.forEach((ref) => { ref.current = true; });
|
||||
imageGenerationAbortRef.current.clear();
|
||||
videoGenerationAbortRef.current.forEach((ref) => { ref.current = true; });
|
||||
videoGenerationAbortRef.current.clear();
|
||||
}, []);
|
||||
|
||||
// Toast auto-dismiss
|
||||
useEffect(() => {
|
||||
if (!generationToast) return undefined;
|
||||
@@ -103,11 +114,14 @@ export function useCanvasGeneration(params: UseCanvasGenerationParams) {
|
||||
}
|
||||
if (entry.nodeKind === "image") {
|
||||
imageGenerationInFlightRef.current.add(entry.nodeId);
|
||||
const abortRef = { current: false };
|
||||
imageGenerationAbortRef.current.set(entry.nodeId, abortRef);
|
||||
setImageGenerationStatus(entry.nodeId, { status: "running", message: "正在恢复图片生成", progress: 20 });
|
||||
void waitForImageTaskResult(entry.taskId, (status) => {
|
||||
const progress = Math.max(18, Math.min(status.status === "completed" ? 100 : 96, Math.trunc(status.progress || 0)));
|
||||
setImageGenerationStatus(entry.nodeId, { status: "running", message: "图片生成中", progress });
|
||||
}).then(async (outputUrl) => {
|
||||
}, abortRef).then(async (outputUrl) => {
|
||||
if (abortRef.current || !outputUrl) return;
|
||||
removeCanvasGenKeepalive(entry.taskId);
|
||||
setImageGenerationStatus(entry.nodeId, { status: "success", message: "生成完成", progress: 100 });
|
||||
const ref = createCanvasAssetRefFromGeneratedResult({
|
||||
@@ -128,18 +142,23 @@ export function useCanvasGeneration(params: UseCanvasGenerationParams) {
|
||||
));
|
||||
}
|
||||
}).catch(() => {
|
||||
if (abortRef.current) return;
|
||||
removeCanvasGenKeepalive(entry.taskId);
|
||||
setImageGenerationStatus(entry.nodeId, { status: "error", message: "图片生成失败" });
|
||||
}).finally(() => {
|
||||
imageGenerationInFlightRef.current.delete(entry.nodeId);
|
||||
imageGenerationAbortRef.current.delete(entry.nodeId);
|
||||
});
|
||||
} else if (entry.nodeKind === "video") {
|
||||
videoGenerationInFlightRef.current.add(entry.nodeId);
|
||||
const abortRef = { current: false };
|
||||
videoGenerationAbortRef.current.set(entry.nodeId, abortRef);
|
||||
setVideoGenerationStatus(entry.nodeId, { status: "running", message: "正在恢复视频生成", progress: 20 });
|
||||
void waitForVideoTaskResult(entry.taskId, (status) => {
|
||||
const progress = Math.max(18, Math.min(status.status === "completed" ? 100 : 96, Math.trunc(status.progress || 0)));
|
||||
setVideoGenerationStatus(entry.nodeId, { status: "running", message: "视频生成中", progress });
|
||||
}).then(async (outputUrl) => {
|
||||
}, abortRef).then(async (outputUrl) => {
|
||||
if (abortRef.current || !outputUrl) return;
|
||||
removeCanvasGenKeepalive(entry.taskId);
|
||||
setVideoGenerationStatus(entry.nodeId, { status: "success", message: "生成完成", progress: 100 });
|
||||
const ref = createCanvasAssetRefFromGeneratedResult({
|
||||
@@ -160,18 +179,19 @@ export function useCanvasGeneration(params: UseCanvasGenerationParams) {
|
||||
));
|
||||
}
|
||||
}).catch(() => {
|
||||
if (abortRef.current) return;
|
||||
removeCanvasGenKeepalive(entry.taskId);
|
||||
setVideoGenerationStatus(entry.nodeId, { status: "error", message: "视频生成失败" });
|
||||
}).finally(() => {
|
||||
videoGenerationInFlightRef.current.delete(entry.nodeId);
|
||||
videoGenerationAbortRef.current.delete(entry.nodeId);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const resetGenerationState = () => {
|
||||
textGenerationAbortControllersRef.current.forEach((c) => c.abort());
|
||||
textGenerationAbortControllersRef.current.clear();
|
||||
abortAllGenerationPollers();
|
||||
textGenerationInFlightRef.current.clear();
|
||||
imageGenerationInFlightRef.current.clear();
|
||||
videoGenerationInFlightRef.current.clear();
|
||||
@@ -180,11 +200,18 @@ export function useCanvasGeneration(params: UseCanvasGenerationParams) {
|
||||
setVideoGenerationState({});
|
||||
};
|
||||
|
||||
// Stop all in-flight front-end polling/setState when the canvas unmounts (route change).
|
||||
// Keepalive records are intentionally preserved so restoreKeepaliveTasks can resume on return.
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
abortAllGenerationPollers();
|
||||
};
|
||||
}, [abortAllGenerationPollers]);
|
||||
|
||||
useEffect(() => {
|
||||
const handlePageHide = () => {
|
||||
cancelCanvasGenKeepaliveOnUnload();
|
||||
textGenerationAbortControllersRef.current.forEach((controller) => controller.abort());
|
||||
textGenerationAbortControllersRef.current.clear();
|
||||
abortAllGenerationPollers();
|
||||
textGenerationInFlightRef.current.clear();
|
||||
imageGenerationInFlightRef.current.clear();
|
||||
videoGenerationInFlightRef.current.clear();
|
||||
@@ -202,7 +229,7 @@ export function useCanvasGeneration(params: UseCanvasGenerationParams) {
|
||||
window.removeEventListener("pagehide", handlePageHide);
|
||||
window.removeEventListener("online", handleOnline);
|
||||
};
|
||||
}, []);
|
||||
}, [abortAllGenerationPollers]);
|
||||
|
||||
return {
|
||||
textGenerationState,
|
||||
@@ -214,6 +241,8 @@ export function useCanvasGeneration(params: UseCanvasGenerationParams) {
|
||||
videoGenerationInFlightRef,
|
||||
textGenerationInFlightRef,
|
||||
textGenerationAbortControllersRef,
|
||||
imageGenerationAbortRef,
|
||||
videoGenerationAbortRef,
|
||||
canvasGenKeepaliveRestoredRef,
|
||||
setTextGenerationStatus,
|
||||
setImageGenerationStatus,
|
||||
|
||||
Reference in New Issue
Block a user