mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-04-05 15:27:20 +02:00
📝 fix: Properly Restore Draft Text When Switching Conversations (#12384)
Right now, if you have draft text in conversation A, but no draft text in conversation B, then switching from A -> B inserts the draft from A into B (oops). This was caused by a bug in the `restoreText()` logic which did not restore *blank* text as the saved draft. Now, it'll always restore whatever is found as a draft (or set to blank if there is no draft).
This commit is contained in:
parent
261941c05f
commit
162ac9c253
2 changed files with 111 additions and 5 deletions
110
client/src/hooks/Input/useAutoSave.spec.ts
Normal file
110
client/src/hooks/Input/useAutoSave.spec.ts
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
jest.mock('recoil', () => ({
|
||||
...jest.requireActual('recoil'),
|
||||
useRecoilValue: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('~/store', () => ({
|
||||
saveDrafts: { key: 'saveDrafts', default: true },
|
||||
}));
|
||||
|
||||
jest.mock('~/Providers', () => ({
|
||||
useChatFormContext: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('~/data-provider', () => ({
|
||||
useGetFiles: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('~/utils', () => ({
|
||||
...jest.requireActual('~/utils'),
|
||||
getDraft: jest.fn(),
|
||||
setDraft: jest.fn(),
|
||||
clearDraft: jest.fn(),
|
||||
clearAllDrafts: jest.fn(),
|
||||
}));
|
||||
|
||||
import React from 'react';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useChatFormContext } from '~/Providers';
|
||||
import { useGetFiles } from '~/data-provider';
|
||||
import { getDraft, setDraft } from '~/utils';
|
||||
import store from '~/store';
|
||||
import { useAutoSave } from '~/hooks';
|
||||
|
||||
const mockSetValue = jest.fn();
|
||||
const mockGetDraft = getDraft as jest.Mock;
|
||||
const mockSetDraft = setDraft as jest.Mock;
|
||||
|
||||
const makeTextAreaRef = (value = '') =>
|
||||
({
|
||||
current: { value, addEventListener: jest.fn(), removeEventListener: jest.fn() },
|
||||
}) as unknown as React.RefObject<HTMLTextAreaElement>;
|
||||
|
||||
beforeEach(() => {
|
||||
(useRecoilValue as jest.Mock).mockImplementation((atom) => {
|
||||
if (atom === store.saveDrafts) return true;
|
||||
return undefined;
|
||||
});
|
||||
(useChatFormContext as jest.Mock).mockReturnValue({ setValue: mockSetValue });
|
||||
(useGetFiles as jest.Mock).mockReturnValue({ data: [] });
|
||||
mockGetDraft.mockReturnValue('');
|
||||
});
|
||||
|
||||
describe('useAutoSave — conversation switching', () => {
|
||||
it('clears the textarea when switching to a conversation with no draft', () => {
|
||||
const { rerender } = renderHook(
|
||||
({ conversationId }: { conversationId: string }) =>
|
||||
useAutoSave({
|
||||
conversationId,
|
||||
textAreaRef: makeTextAreaRef(),
|
||||
files: new Map(),
|
||||
setFiles: jest.fn(),
|
||||
}),
|
||||
{ initialProps: { conversationId: 'convo-1' } },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
rerender({ conversationId: 'convo-2' });
|
||||
});
|
||||
|
||||
expect(mockSetValue).toHaveBeenLastCalledWith('text', '');
|
||||
});
|
||||
|
||||
it('restores the saved draft when switching to a conversation with one', () => {
|
||||
mockGetDraft.mockImplementation((id: string) => (id === 'convo-2' ? 'Hello, world!' : ''));
|
||||
|
||||
const { rerender } = renderHook(
|
||||
({ conversationId }: { conversationId: string }) =>
|
||||
useAutoSave({
|
||||
conversationId,
|
||||
textAreaRef: makeTextAreaRef(),
|
||||
files: new Map(),
|
||||
setFiles: jest.fn(),
|
||||
}),
|
||||
{ initialProps: { conversationId: 'convo-1' } },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
rerender({ conversationId: 'convo-2' });
|
||||
});
|
||||
|
||||
expect(mockSetValue).toHaveBeenLastCalledWith('text', 'Hello, world!');
|
||||
});
|
||||
|
||||
it('saves the current textarea content before switching away', () => {
|
||||
const textAreaRef = makeTextAreaRef('draft in progress');
|
||||
|
||||
const { rerender } = renderHook(
|
||||
({ conversationId }: { conversationId: string }) =>
|
||||
useAutoSave({ conversationId, textAreaRef, files: new Map(), setFiles: jest.fn() }),
|
||||
{ initialProps: { conversationId: 'convo-1' } },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
rerender({ conversationId: 'convo-2' });
|
||||
});
|
||||
|
||||
expect(mockSetDraft).toHaveBeenCalledWith({ id: 'convo-1', value: 'draft in progress' });
|
||||
});
|
||||
});
|
||||
|
|
@ -73,11 +73,7 @@ export const useAutoSave = ({
|
|||
|
||||
const restoreText = useCallback(
|
||||
(id: string) => {
|
||||
const savedDraft = getDraft(id);
|
||||
if (!savedDraft) {
|
||||
return;
|
||||
}
|
||||
setValue('text', savedDraft);
|
||||
setValue('text', getDraft(id) ?? '');
|
||||
},
|
||||
[setValue],
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue