mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-04 14:57:42 +00:00
fix: Preserve avatar reset drafts
This commit is contained in:
parent
018edee845
commit
adc115c878
4 changed files with 64 additions and 5 deletions
|
|
@ -320,16 +320,26 @@ export default function AgentPanel() {
|
|||
}, [clearDraftsForAgentIds, reset, setCurrentAgentId]);
|
||||
|
||||
const shouldPersistDraft = hasPersistableDirtyFields(dirtyFields);
|
||||
const shouldPersistAvatarResetDraft =
|
||||
dirtyFields?.avatar_action === true && getValues('avatar_action') === 'reset';
|
||||
|
||||
const isDirtyPersistableDraftField = useCallback(
|
||||
(name?: string): boolean => {
|
||||
const [fieldName] = name?.split('.') ?? [];
|
||||
if (fieldName === 'avatar_action') {
|
||||
return (
|
||||
getFieldState(name as FieldPath<AgentForm>).isDirty &&
|
||||
getValues('avatar_action') === 'reset'
|
||||
);
|
||||
}
|
||||
|
||||
if (!isPersistableDraftField(name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getFieldState(name as FieldPath<AgentForm>).isDirty;
|
||||
},
|
||||
[getFieldState],
|
||||
[getFieldState, getValues],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -367,13 +377,13 @@ export default function AgentPanel() {
|
|||
}, [hasDraft]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!shouldPersistDraft) {
|
||||
if (!shouldPersistDraft && !shouldPersistAvatarResetDraft) {
|
||||
return;
|
||||
}
|
||||
|
||||
shouldPersistDraftRef.current = true;
|
||||
persistAgentDraft(currentAgentIdRef.current, getValues());
|
||||
}, [getValues, persistAgentDraft, shouldPersistDraft]);
|
||||
}, [getValues, persistAgentDraft, shouldPersistAvatarResetDraft, shouldPersistDraft]);
|
||||
|
||||
useEffect(() => {
|
||||
const subscription = watch((_, { name }) => {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ const AGENTS_BUTTON_LABEL = 'Agents';
|
|||
const FILES_BUTTON_LABEL = 'Files';
|
||||
const FILES_PANEL_LABEL = 'Files panel';
|
||||
const PROGRAMMATIC_UPDATE_LABEL = 'Programmatic agent update';
|
||||
const AVATAR_ACTION_LABEL = 'Avatar action';
|
||||
const RESET_AVATAR_LABEL = 'Reset avatar';
|
||||
const MOCK_USER_ID = 'user-123';
|
||||
|
||||
type MockButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||
|
|
@ -166,13 +168,20 @@ jest.mock('../AgentConfig', () => ({
|
|||
const { useFormContext } = jest.requireActual(
|
||||
'react-hook-form',
|
||||
) as typeof import('react-hook-form');
|
||||
const { register } = useFormContext();
|
||||
const { register, setValue } = useFormContext<AgentForm>();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input aria-label="Draft name" {...register('name')} />
|
||||
<textarea aria-label="Draft instructions" {...register('instructions')} />
|
||||
<input aria-label="Draft model" {...register('model')} />
|
||||
<input aria-label={AVATAR_ACTION_LABEL} {...register('avatar_action')} />
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setValue('avatar_action', 'reset', { shouldDirty: true })}
|
||||
>
|
||||
{RESET_AVATAR_LABEL}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
@ -376,4 +385,22 @@ describe('AgentPanel draft preservation', () => {
|
|||
expect(getAgentDraft(undefined, 'user-456')).toBeUndefined();
|
||||
expect(getAgentDraft(undefined, MOCK_USER_ID)?.name).toBe('First user draft');
|
||||
});
|
||||
|
||||
it('restores avatar reset-only drafts after switching to Files and back', async () => {
|
||||
render(<Harness />);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: RESET_AVATAR_LABEL }));
|
||||
await waitFor(() => {
|
||||
expect(getAgentDraft(undefined, MOCK_USER_ID)?.avatar_action).toBe('reset');
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: FILES_BUTTON_LABEL }));
|
||||
expect(screen.getByText(FILES_PANEL_LABEL)).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: AGENTS_BUTTON_LABEL }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText(AVATAR_ACTION_LABEL)).toHaveValue('reset');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -93,4 +93,21 @@ describe('agent drafts', () => {
|
|||
expect(getAgentDraft('agent-1', 'user-a')?.name).toBe('User A saved-agent draft');
|
||||
expect(getAgentDraft('agent-1', 'user-b')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('preserves avatar reset intent without upload files', () => {
|
||||
saveAgentDraft(
|
||||
undefined,
|
||||
createForm({
|
||||
avatar_action: 'reset',
|
||||
avatar_file: null,
|
||||
avatar_preview: '/images/avatar.png',
|
||||
}),
|
||||
);
|
||||
|
||||
const draft = getAgentDraft(undefined);
|
||||
|
||||
expect(draft?.avatar_action).toBe('reset');
|
||||
expect(draft?.avatar_preview).toBe('');
|
||||
expect(draft?.avatar_file).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -21,10 +21,15 @@ export function sanitizeAgentDraft(values: AgentForm): AgentDraftValues {
|
|||
const {
|
||||
avatar_file: _avatarFile,
|
||||
avatar_preview: _avatarPreview,
|
||||
avatar_action: _avatarAction,
|
||||
avatar_action: avatarAction,
|
||||
...draft
|
||||
} = values;
|
||||
|
||||
if (avatarAction === 'reset') {
|
||||
draft.avatar_action = avatarAction;
|
||||
draft.avatar_preview = '';
|
||||
}
|
||||
|
||||
if (typeof draft.agent === 'object' && draft.agent != null) {
|
||||
draft.agent = {
|
||||
...draft.agent,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue