mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
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.
This commit is contained in:
@@ -19,7 +19,7 @@ export const FurniEditorView: FC<{}> = () =>
|
|||||||
selectedItem, setSelectedItem, furniDataEntry, furniDataDiagnostic,
|
selectedItem, setSelectedItem, furniDataEntry, furniDataDiagnostic,
|
||||||
interactions,
|
interactions,
|
||||||
searchItems, loadDetail, loadBySpriteId, updateItem, deleteItem, loadInteractions,
|
searchItems, loadDetail, loadBySpriteId, updateItem, deleteItem, loadInteractions,
|
||||||
updateFurnidata, revertFurnidata, importText, importResult
|
updateFurnidata, revertFurnidata, syncPublicName, importText, importResult
|
||||||
} = useFurniEditor();
|
} = useFurniEditor();
|
||||||
|
|
||||||
const isMod = useHasPermission('acc_catalogfurni');
|
const isMod = useHasPermission('acc_catalogfurni');
|
||||||
@@ -159,6 +159,7 @@ export const FurniEditorView: FC<{}> = () =>
|
|||||||
onBack={ handleBack }
|
onBack={ handleBack }
|
||||||
onUpdateFurnidata={ updateFurnidata }
|
onUpdateFurnidata={ updateFurnidata }
|
||||||
onRevertFurnidata={ revertFurnidata }
|
onRevertFurnidata={ revertFurnidata }
|
||||||
|
onSyncPublicName={ syncPublicName }
|
||||||
onImportText={ importText }
|
onImportText={ importText }
|
||||||
importResult={ importResult }
|
importResult={ importResult }
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ interface FurniEditorEditViewProps
|
|||||||
onBack: () => void;
|
onBack: () => void;
|
||||||
onUpdateFurnidata: (id: number, name: string, description: string) => void;
|
onUpdateFurnidata: (id: number, name: string, description: string) => void;
|
||||||
onRevertFurnidata: (id: number) => void;
|
onRevertFurnidata: (id: number) => void;
|
||||||
|
onSyncPublicName: (id: number, name: string) => void;
|
||||||
onImportText: (id: number) => void;
|
onImportText: (id: number) => void;
|
||||||
importResult: { found: boolean; name: string; description: string; classname: string; nonce: number } | null;
|
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<FurniEditorEditViewProps> = props =>
|
export const FurniEditorEditView: FC<FurniEditorEditViewProps> = 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 saveRef = useRef<() => void>(null);
|
||||||
|
|
||||||
const [ form, setForm ] = useState({
|
const [ form, setForm ] = useState({
|
||||||
@@ -251,6 +252,15 @@ export const FurniEditorEditView: FC<FurniEditorEditViewProps> = props =>
|
|||||||
// classname), which stays locked to avoid an id collision.
|
// classname), which stays locked to avoid an id collision.
|
||||||
const furnidataCreatable = useMemo(() => !furniDataEntry, [ furniDataEntry ]);
|
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
|
// 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
|
// entry. Used to gate the Save button: saving an unchanged value makes the
|
||||||
// server writer return false, which the handler misreports as "Classname not
|
// server writer return false, which the handler misreports as "Classname not
|
||||||
@@ -427,6 +437,10 @@ export const FurniEditorEditView: FC<FurniEditorEditViewProps> = props =>
|
|||||||
<div>
|
<div>
|
||||||
<label className={ labelClass }>Public Name (DB fallback)</label>
|
<label className={ labelClass }>Public Name (DB fallback)</label>
|
||||||
<CopyValue value={ form.publicName } />
|
<CopyValue value={ form.publicName } />
|
||||||
|
{ canSyncPublicName &&
|
||||||
|
<Button variant="secondary" disabled={ loading } className="mt-1 w-full" onClick={ () => onSyncPublicName(item.id, String(furniDataEntry?.name ?? '')) }>
|
||||||
|
Sync from furnidata
|
||||||
|
</Button> }
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className={ labelClass }>Sprite ID</label>
|
<label className={ labelClass }>Sprite ID</label>
|
||||||
|
|||||||
@@ -291,6 +291,17 @@ export const useFurniEditor = () =>
|
|||||||
SendMessageComposer(new FurniEditorRevertFurnidataComposer(id));
|
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) =>
|
const importText = useCallback((id: number) =>
|
||||||
{
|
{
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -323,6 +334,6 @@ export const useFurniEditor = () =>
|
|||||||
selectedItem, setSelectedItem, catalogItems, furniDataEntry, furniDataDiagnostic,
|
selectedItem, setSelectedItem, catalogItems, furniDataEntry, furniDataDiagnostic,
|
||||||
interactions,
|
interactions,
|
||||||
searchItems, loadDetail, loadBySpriteId, updateItem, deleteItem, loadInteractions,
|
searchItems, loadDetail, loadBySpriteId, updateItem, deleteItem, loadInteractions,
|
||||||
updateFurnidata, revertFurnidata, importText, importResult
|
updateFurnidata, revertFurnidata, syncPublicName, importText, importResult
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user