diff --git a/src/app/admin/import/furni/nitro-editor-dialog.tsx b/src/app/admin/import/furni/nitro-editor-dialog.tsx index 4b3991b455..4ae1dfce8a 100644 --- a/src/app/admin/import/furni/nitro-editor-dialog.tsx +++ b/src/app/admin/import/furni/nitro-editor-dialog.tsx @@ -16,7 +16,7 @@ import { Save, Sparkles, } from "lucide-react"; -import { useCallback, useEffect, useState } from "react"; +import { useEffect, useEffectEvent, useState } from "react"; import { toast } from "sonner"; import { NitroQualityPanel } from "@/components/admin/studio/nitro-quality-panel"; import { NitroScale32Generator } from "@/components/admin/studio/nitro-scale32-generator"; @@ -188,40 +188,43 @@ export function NitroEditorDialog({ ); }; - const fetchMetadata = useCallback(async () => { - if (!classname) return; + const closeAfterLoadError = useEffectEvent(() => onOpenChange(false)); + useEffect(() => { + if (!open || !classname) return; + const controller = new AbortController(); + setActiveTab("visual"); setLoading(true); setJsonError(null); - try { - const res = await adminFetch( - `/api/admin/import/furni/nitro-editor?classname=${encodeURIComponent(classname)}`, - ); - if (!res.ok) { - const err = await res.json().catch(() => ({})); - toast.error(err.error || "Failed to load .nitro metadata"); - onOpenChange(false); - return; + setAutoDetectResult(null); + void (async () => { + try { + const res = await adminFetch( + `/api/admin/import/furni/nitro-editor?classname=${encodeURIComponent(classname)}`, + { signal: controller.signal }, + ); + const data = await res.json(); + if (controller.signal.aborted) return; + if (!res.ok) { + toast.error(data.error || "Failed to load .nitro metadata"); + closeAfterLoadError(); + return; + } + setMetadata(data.metadata); + setOriginalMetadata(structuredClone(data.metadata)); + setJsonText(JSON.stringify(data.metadata, null, 2)); + setFlags(data.flags); + setOriginalFlags({ ...data.flags }); + } catch { + if (!controller.signal.aborted) { + toast.error("Network error loading .nitro metadata"); + closeAfterLoadError(); + } + } finally { + if (!controller.signal.aborted) setLoading(false); } - const data = await res.json(); - setMetadata(data.metadata); - setOriginalMetadata(JSON.parse(JSON.stringify(data.metadata))); - setJsonText(JSON.stringify(data.metadata, null, 2)); - setFlags(data.flags); - setOriginalFlags({ ...data.flags }); - } catch { - toast.error("Network error loading .nitro metadata"); - onOpenChange(false); - } finally { - setLoading(false); - } - }, [classname, onOpenChange]); - - useEffect(() => { - if (open && classname) { - setActiveTab("visual"); - fetchMetadata(); - } - }, [open, classname, fetchMetadata]); + })(); + return () => controller.abort(); + }, [open, classname]); const handleTabChange = (newTab: string) => { if (activeTab === "json" && newTab !== "json") {