import { useEffect, useRef } from "react"; import type { Doc } from "@/convex/_generated/dataModel"; import { toast } from "@/lib/toast"; import { msg } from "@/lib/toast-messages"; import { GENERATION_FAILURE_THRESHOLD, GENERATION_FAILURE_WINDOW_MS, } from "./canvas-helpers"; export function useGenerationFailureWarnings( convexNodes: Doc<"nodes">[] | undefined, ): void { const recentGenerationFailureTimestampsRef = useRef([]); const previousNodeStatusRef = useRef>(new Map()); const hasInitializedGenerationFailureTrackingRef = useRef(false); useEffect(() => { if (!convexNodes) return; const nextNodeStatusMap = new Map(); let detectedGenerationFailures = 0; for (const node of convexNodes) { nextNodeStatusMap.set(node._id, node.status); if (node.type !== "ai-image") { continue; } const previousStatus = previousNodeStatusRef.current.get(node._id); if ( hasInitializedGenerationFailureTrackingRef.current && node.status === "error" && previousStatus !== "error" ) { detectedGenerationFailures += 1; } } previousNodeStatusRef.current = nextNodeStatusMap; if (!hasInitializedGenerationFailureTrackingRef.current) { hasInitializedGenerationFailureTrackingRef.current = true; return; } if (detectedGenerationFailures === 0) { return; } const now = Date.now(); const recentFailures = recentGenerationFailureTimestampsRef.current.filter( (timestamp) => now - timestamp <= GENERATION_FAILURE_WINDOW_MS, ); for (let index = 0; index < detectedGenerationFailures; index += 1) { recentFailures.push(now); } if (recentFailures.length >= GENERATION_FAILURE_THRESHOLD) { toast.warning(msg.ai.openrouterIssues.title, msg.ai.openrouterIssues.desc); recentGenerationFailureTimestampsRef.current = []; return; } recentGenerationFailureTimestampsRef.current = recentFailures; }, [convexNodes]); }