fix(catalog): keep Nitro editor stable during background polling
CI / check (push) Successful in 1m32s
CI / deploy (push) Successful in 1m23s
CI / e2e (push) Successful in 21s

This commit is contained in:
Simo committed 2026-09-05 18:00:39 +02:00
1 parent dcb5f46eed
commit 3c24df9b1d
1 file changed
+35 -32
@@ -16,7 +16,7 @@ import {
Save, Save,
Sparkles, Sparkles,
} from "lucide-react"; } from "lucide-react";
import { useCallback, useEffect, useState } from "react"; import { useEffect, useEffectEvent, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { NitroQualityPanel } from "@/components/admin/studio/nitro-quality-panel"; import { NitroQualityPanel } from "@/components/admin/studio/nitro-quality-panel";
import { NitroScale32Generator } from "@/components/admin/studio/nitro-scale32-generator"; import { NitroScale32Generator } from "@/components/admin/studio/nitro-scale32-generator";
@@ -188,40 +188,43 @@ export function NitroEditorDialog({
); );
}; };
const fetchMetadata = useCallback(async () => { const closeAfterLoadError = useEffectEvent(() => onOpenChange(false));
if (!classname) return; useEffect(() => {
if (!open || !classname) return;
const controller = new AbortController();
setActiveTab("visual");
setLoading(true); setLoading(true);
setJsonError(null); setJsonError(null);
try { setAutoDetectResult(null);
const res = await adminFetch( void (async () => {
`/api/admin/import/furni/nitro-editor?classname=${encodeURIComponent(classname)}`, try {
); const res = await adminFetch(
if (!res.ok) { `/api/admin/import/furni/nitro-editor?classname=${encodeURIComponent(classname)}`,
const err = await res.json().catch(() => ({})); { signal: controller.signal },
toast.error(err.error || "Failed to load .nitro metadata"); );
onOpenChange(false); const data = await res.json();
return; 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); return () => controller.abort();
setOriginalMetadata(JSON.parse(JSON.stringify(data.metadata))); }, [open, classname]);
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]);
const handleTabChange = (newTab: string) => { const handleTabChange = (newTab: string) => {
if (activeTab === "json" && newTab !== "json") { if (activeTab === "json" && newTab !== "json") {