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
+21 -18
@@ -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);
setAutoDetectResult(null);
void (async () => {
try { try {
const res = await adminFetch( const res = await adminFetch(
`/api/admin/import/furni/nitro-editor?classname=${encodeURIComponent(classname)}`, `/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) { if (!res.ok) {
const err = await res.json().catch(() => ({})); toast.error(data.error || "Failed to load .nitro metadata");
toast.error(err.error || "Failed to load .nitro metadata"); closeAfterLoadError();
onOpenChange(false);
return; return;
} }
const data = await res.json();
setMetadata(data.metadata); setMetadata(data.metadata);
setOriginalMetadata(JSON.parse(JSON.stringify(data.metadata))); setOriginalMetadata(structuredClone(data.metadata));
setJsonText(JSON.stringify(data.metadata, null, 2)); setJsonText(JSON.stringify(data.metadata, null, 2));
setFlags(data.flags); setFlags(data.flags);
setOriginalFlags({ ...data.flags }); setOriginalFlags({ ...data.flags });
} catch { } catch {
if (!controller.signal.aborted) {
toast.error("Network error loading .nitro metadata"); toast.error("Network error loading .nitro metadata");
onOpenChange(false); closeAfterLoadError();
}
} finally { } finally {
setLoading(false); if (!controller.signal.aborted) setLoading(false);
} }
}, [classname, onOpenChange]); })();
return () => controller.abort();
useEffect(() => { }, [open, classname]);
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") {