From b27f48f2a2b3992d6fc29a2c5d5b0fd8dc9b134d Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sun, 14 Jun 2026 16:41:55 +0200 Subject: [PATCH] feat(furni-editor): sync empty public_name from furnidata name When a furni has a matching furnidata entry with a display name but its items_base.public_name (the DB fallback) is empty, the editor now shows a 'Sync from furnidata' button next to the Public Name field. It reuses the generic item update (a partial { publicName } payload) to fill the DB column from the stored furnidata name, so the read-only fallback stops being blank. Button shows only when the entry's classname matches, the DB field is empty, and the furnidata name is present; it disappears after the sync re-fetch. --- src/components/furni-editor/FurniEditorView.tsx | 3 ++- .../furni-editor/views/FurniEditorEditView.tsx | 16 +++++++++++++++- src/hooks/furni-editor/useFurniEditor.ts | 13 ++++++++++++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/components/furni-editor/FurniEditorView.tsx b/src/components/furni-editor/FurniEditorView.tsx index 75db342..c6ee646 100644 --- a/src/components/furni-editor/FurniEditorView.tsx +++ b/src/components/furni-editor/FurniEditorView.tsx @@ -19,7 +19,7 @@ export const FurniEditorView: FC<{}> = () => selectedItem, setSelectedItem, furniDataEntry, furniDataDiagnostic, interactions, searchItems, loadDetail, loadBySpriteId, updateItem, deleteItem, loadInteractions, - updateFurnidata, revertFurnidata, importText, importResult + updateFurnidata, revertFurnidata, syncPublicName, importText, importResult } = useFurniEditor(); const isMod = useHasPermission('acc_catalogfurni'); @@ -159,6 +159,7 @@ export const FurniEditorView: FC<{}> = () => onBack={ handleBack } onUpdateFurnidata={ updateFurnidata } onRevertFurnidata={ revertFurnidata } + onSyncPublicName={ syncPublicName } onImportText={ importText } importResult={ importResult } /> diff --git a/src/components/furni-editor/views/FurniEditorEditView.tsx b/src/components/furni-editor/views/FurniEditorEditView.tsx index 928ac8e..99aad65 100644 --- a/src/components/furni-editor/views/FurniEditorEditView.tsx +++ b/src/components/furni-editor/views/FurniEditorEditView.tsx @@ -15,6 +15,7 @@ interface FurniEditorEditViewProps onBack: () => void; onUpdateFurnidata: (id: number, name: string, description: string) => void; onRevertFurnidata: (id: number) => void; + onSyncPublicName: (id: number, name: string) => void; onImportText: (id: number) => void; importResult: { found: boolean; name: string; description: string; classname: string; nonce: number } | null; } @@ -122,7 +123,7 @@ const CopyValue: FC<{ value: string | number }> = ({ value }) => export const FurniEditorEditView: FC = props => { - const { item, furniDataEntry, furniDataDiagnostic, interactions, loading, onUpdate, onDelete, onBack, onUpdateFurnidata, onRevertFurnidata, onImportText, importResult } = props; + const { item, furniDataEntry, furniDataDiagnostic, interactions, loading, onUpdate, onDelete, onBack, onUpdateFurnidata, onRevertFurnidata, onSyncPublicName, onImportText, importResult } = props; const saveRef = useRef<() => void>(null); const [ form, setForm ] = useState({ @@ -251,6 +252,15 @@ export const FurniEditorEditView: FC = props => // classname), which stays locked to avoid an id collision. const furnidataCreatable = useMemo(() => !furniDataEntry, [ furniDataEntry ]); + // Show a one-click "sync" when the DB public_name is empty but the (matching) + // furnidata entry already has a name — fills items_base.public_name from the + // stored furnidata name so the DB fallback stops being blank. + const canSyncPublicName = useMemo(() => + furnidataEditable && + !String(form.publicName ?? '').trim() && + !!String(furniDataEntry?.name ?? '').trim(), + [ furnidataEditable, form.publicName, furniDataEntry ]); + // True only when the name/description actually differ from the stored furnidata // entry. Used to gate the Save button: saving an unchanged value makes the // server writer return false, which the handler misreports as "Classname not @@ -427,6 +437,10 @@ export const FurniEditorEditView: FC = props =>
+ { canSyncPublicName && + }
diff --git a/src/hooks/furni-editor/useFurniEditor.ts b/src/hooks/furni-editor/useFurniEditor.ts index becc17e..d6a210e 100644 --- a/src/hooks/furni-editor/useFurniEditor.ts +++ b/src/hooks/furni-editor/useFurniEditor.ts @@ -291,6 +291,17 @@ export const useFurniEditor = () => SendMessageComposer(new FurniEditorRevertFurnidataComposer(id)); }, []); + // Fill an empty items_base.public_name from the furnidata display name. Reuses + // the generic item update (a partial { publicName } payload is accepted), so the + // existing 'update' result path shows the toast and re-fetches the detail. + const syncPublicName = useCallback((id: number, name: string) => + { + setLoading(true); + setError(null); + pendingActionRef.current = { action: 'update', itemId: id }; + SendMessageComposer(new FurniEditorUpdateComposer(id, JSON.stringify({ publicName: name }))); + }, []); + const importText = useCallback((id: number) => { setLoading(true); @@ -323,6 +334,6 @@ export const useFurniEditor = () => selectedItem, setSelectedItem, catalogItems, furniDataEntry, furniDataDiagnostic, interactions, searchItems, loadDetail, loadBySpriteId, updateItem, deleteItem, loadInteractions, - updateFurnidata, revertFurnidata, importText, importResult + updateFurnidata, revertFurnidata, syncPublicName, importText, importResult }; };