✍️ feat: Automatic Save and Restore for Chat (#2942)

* feat: added "Save draft locally" to Message settings

* feat: add hook to save chat input as draft every second

* fix: use filepath if the file does not have a preview prop

* fix: not to delete temporary files when navigating to a new chat

* chore: translations

* chore: import order

* chore: import order

---------

Co-authored-by: Danny Avila <danacordially@gmail.com>
Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
Yuichi Oneda 2024-06-13 06:52:30 -07:00 committed by GitHub
parent e9bbf39618
commit 29e71e98ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 345 additions and 5 deletions

View file

@ -1,6 +1,6 @@
import { useForm } from 'react-hook-form';
import { useRecoilState, useRecoilValue } from 'recoil';
import { memo, useCallback, useRef, useMemo } from 'react';
import { memo, useCallback, useRef, useMemo, useState, useEffect } from 'react';
import {
supportsFiles,
mergeFileConfig,
@ -8,6 +8,7 @@ import {
fileConfig as defaultFileConfig,
} from 'librechat-data-provider';
import { useChatContext, useAssistantsMapContext } from '~/Providers';
import { useAutoSave } from '~/hooks/Input/useAutoSave';
import { useRequiresKey, useTextarea } from '~/hooks';
import { TextareaAutosize } from '~/components/ui';
import { useGetFileConfig } from '~/data-provider';
@ -56,6 +57,14 @@ const ChatForm = ({ index = 0 }) => {
handleStopGenerating,
} = useChatContext();
const { clearDraft } = useAutoSave({
conversationId: useMemo(() => conversation?.conversationId, [conversation]),
textAreaRef,
setValue: methods.setValue,
files,
setFiles,
});
const assistantMap = useAssistantsMapContext();
const submitMessage = useCallback(
@ -65,8 +74,9 @@ const ChatForm = ({ index = 0 }) => {
}
ask({ text: data.text });
methods.reset();
clearDraft();
},
[ask, methods],
[ask, methods, clearDraft],
);
const { endpoint: _endpoint, endpointType } = conversation ?? { endpoint: null };

View file

@ -83,7 +83,7 @@ export default function FileRow({
return (
<Image
key={index}
url={file.preview}
url={file.preview || file.filepath}
onDelete={handleDelete}
progress={file.progress}
source={file.source}

View file

@ -4,6 +4,7 @@ import { SettingsTabValues } from 'librechat-data-provider';
import SendMessageKeyEnter from './EnterToSend';
import ShowCodeSwitch from './ShowCodeSwitch';
import { ForkSettings } from './ForkSettings';
import SaveDraft from './SaveDraft';
function Messages() {
return (
@ -15,6 +16,9 @@ function Messages() {
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<ShowCodeSwitch />
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<SaveDraft />
</div>
<ForkSettings />
</div>
</Tabs.Content>

View file

@ -0,0 +1,33 @@
import { useRecoilState } from 'recoil';
import { Switch } from '~/components/ui/Switch';
import useLocalize from '~/hooks/useLocalize';
import store from '~/store';
export default function SaveDraft({
onCheckedChange,
}: {
onCheckedChange?: (value: boolean) => void;
}) {
const [saveDrafts, setSaveDrafts] = useRecoilState<boolean>(store.saveDrafts);
const localize = useLocalize();
const handleCheckedChange = (value: boolean) => {
setSaveDrafts(value);
if (onCheckedChange) {
onCheckedChange(value);
}
};
return (
<div className="flex items-center justify-between">
<div>{localize('com_nav_save_drafts')}</div>
<Switch
id="saveDrafts"
checked={saveDrafts}
onCheckedChange={handleCheckedChange}
className="ml-4 mt-2"
data-testid="saveDrafts"
/>
</div>
);
}