mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
Fix: Delete the uploaded file in the chat input box, the corresponding file ID is not deleted #9701 (#9702)
### What problem does this PR solve? Fix: Delete the uploaded file in the chat input box, the corresponding file ID is not deleted #9701 ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
This commit is contained in:
@ -34,6 +34,7 @@ interface IProps {
|
|||||||
createConversationBeforeUploadDocument?(message: string): Promise<any>;
|
createConversationBeforeUploadDocument?(message: string): Promise<any>;
|
||||||
stopOutputMessage?(): void;
|
stopOutputMessage?(): void;
|
||||||
onUpload?: NonNullable<FileUploadProps['onUpload']>;
|
onUpload?: NonNullable<FileUploadProps['onUpload']>;
|
||||||
|
removeFile?(file: File): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NextMessageInput({
|
export function NextMessageInput({
|
||||||
@ -47,6 +48,7 @@ export function NextMessageInput({
|
|||||||
onInputChange,
|
onInputChange,
|
||||||
stopOutputMessage,
|
stopOutputMessage,
|
||||||
onPressEnter,
|
onPressEnter,
|
||||||
|
removeFile,
|
||||||
}: IProps) {
|
}: IProps) {
|
||||||
const [files, setFiles] = React.useState<File[]>([]);
|
const [files, setFiles] = React.useState<File[]>([]);
|
||||||
|
|
||||||
@ -77,6 +79,13 @@ export function NextMessageInput({
|
|||||||
[submit],
|
[submit],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleRemoveFile = React.useCallback(
|
||||||
|
(file: File) => () => {
|
||||||
|
removeFile?.(file);
|
||||||
|
},
|
||||||
|
[removeFile],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FileUpload
|
<FileUpload
|
||||||
value={files}
|
value={files}
|
||||||
@ -121,6 +130,7 @@ export function NextMessageInput({
|
|||||||
variant="secondary"
|
variant="secondary"
|
||||||
size="icon"
|
size="icon"
|
||||||
className="-top-1 -right-1 absolute size-4 shrink-0 cursor-pointer rounded-full"
|
className="-top-1 -right-1 absolute size-4 shrink-0 cursor-pointer rounded-full"
|
||||||
|
onClick={handleRemoveFile(file)}
|
||||||
>
|
>
|
||||||
<X className="size-2.5" />
|
<X className="size-2.5" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -390,7 +390,7 @@ export const useUploadCanvasFileWithProgress = (
|
|||||||
files.forEach((file) => {
|
files.forEach((file) => {
|
||||||
onError(file, error as Error);
|
onError(file, error as Error);
|
||||||
});
|
});
|
||||||
message.error('error', error?.message);
|
message.error(error?.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { FileUploadProps } from '@/components/file-upload';
|
||||||
import message from '@/components/ui/message';
|
import message from '@/components/ui/message';
|
||||||
import { ChatSearchParams } from '@/constants/chat';
|
import { ChatSearchParams } from '@/constants/chat';
|
||||||
import {
|
import {
|
||||||
@ -14,7 +15,7 @@ import { buildMessageListWithUuid, getConversationId } from '@/utils/chat';
|
|||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { useDebounce } from 'ahooks';
|
import { useDebounce } from 'ahooks';
|
||||||
import { has } from 'lodash';
|
import { has } from 'lodash';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useParams, useSearchParams } from 'umi';
|
import { useParams, useSearchParams } from 'umi';
|
||||||
import {
|
import {
|
||||||
@ -395,9 +396,14 @@ export const useDeleteMessage = () => {
|
|||||||
return { data, loading, deleteMessage: mutateAsync };
|
return { data, loading, deleteMessage: mutateAsync };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type UploadParameters = Parameters<NonNullable<FileUploadProps['onUpload']>>;
|
||||||
|
|
||||||
|
type X = { file: UploadParameters[0][0]; options: UploadParameters[1] };
|
||||||
|
|
||||||
export function useUploadAndParseFile() {
|
export function useUploadAndParseFile() {
|
||||||
const { conversationId } = useGetChatSearchParams();
|
const { conversationId } = useGetChatSearchParams();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const controller = useRef(new AbortController());
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
@ -405,22 +411,48 @@ export function useUploadAndParseFile() {
|
|||||||
mutateAsync,
|
mutateAsync,
|
||||||
} = useMutation({
|
} = useMutation({
|
||||||
mutationKey: [ChatApiAction.UploadAndParse],
|
mutationKey: [ChatApiAction.UploadAndParse],
|
||||||
mutationFn: async (file: File) => {
|
mutationFn: async ({
|
||||||
const formData = new FormData();
|
file,
|
||||||
formData.append('file', file);
|
options: { onProgress, onSuccess, onError },
|
||||||
formData.append('conversation_id', conversationId);
|
}: X) => {
|
||||||
|
try {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
formData.append('conversation_id', conversationId);
|
||||||
|
|
||||||
const { data } = await chatService.uploadAndParse(formData);
|
const { data } = await chatService.uploadAndParse(
|
||||||
|
{
|
||||||
|
signal: controller.current.signal,
|
||||||
|
data: formData,
|
||||||
|
onUploadProgress: ({ progress }) => {
|
||||||
|
onProgress(file, (progress || 0) * 100 - 1);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
if (data.code === 0) {
|
onProgress(file, 100);
|
||||||
message.success(t(`message.uploaded`));
|
|
||||||
|
if (data.code === 0) {
|
||||||
|
onSuccess(file);
|
||||||
|
message.success(t(`message.uploaded`));
|
||||||
|
} else {
|
||||||
|
onError(file, new Error(data.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
onError(file, error as Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return { data, loading, uploadAndParseFile: mutateAsync };
|
const cancel = useCallback(() => {
|
||||||
|
controller.current.abort();
|
||||||
|
controller.current = new AbortController();
|
||||||
|
}, [controller]);
|
||||||
|
|
||||||
|
return { data, loading, uploadAndParseFile: mutateAsync, cancel };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useFetchExternalChatInfo = () => {
|
export const useFetchExternalChatInfo = () => {
|
||||||
|
|||||||
@ -36,6 +36,7 @@ export function SingleChatBox({ controller }: IProps) {
|
|||||||
removeMessageById,
|
removeMessageById,
|
||||||
stopOutputMessage,
|
stopOutputMessage,
|
||||||
handleUploadFile,
|
handleUploadFile,
|
||||||
|
removeFile,
|
||||||
} = useSendMessage(controller);
|
} = useSendMessage(controller);
|
||||||
const { data: userInfo } = useFetchUserInfo();
|
const { data: userInfo } = useFetchUserInfo();
|
||||||
const { data: currentDialog } = useFetchDialog();
|
const { data: currentDialog } = useFetchDialog();
|
||||||
@ -97,6 +98,7 @@ export function SingleChatBox({ controller }: IProps) {
|
|||||||
stopOutputMessage={stopOutputMessage}
|
stopOutputMessage={stopOutputMessage}
|
||||||
onUpload={handleUploadFile}
|
onUpload={handleUploadFile}
|
||||||
isUploading={isUploading}
|
isUploading={isUploading}
|
||||||
|
removeFile={removeFile}
|
||||||
/>
|
/>
|
||||||
{visible && (
|
{visible && (
|
||||||
<PdfDrawer
|
<PdfDrawer
|
||||||
|
|||||||
@ -138,7 +138,7 @@ export const useSendMessage = (controller: AbortController) => {
|
|||||||
const { conversationId, isNew } = useGetChatSearchParams();
|
const { conversationId, isNew } = useGetChatSearchParams();
|
||||||
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
||||||
|
|
||||||
const { handleUploadFile, fileIds, clearFileIds, isUploading } =
|
const { handleUploadFile, fileIds, clearFileIds, isUploading, removeFile } =
|
||||||
useUploadFile();
|
useUploadFile();
|
||||||
|
|
||||||
const { send, answer, done } = useSendMessageWithSse(
|
const { send, answer, done } = useSendMessageWithSse(
|
||||||
@ -287,5 +287,6 @@ export const useSendMessage = (controller: AbortController) => {
|
|||||||
stopOutputMessage,
|
stopOutputMessage,
|
||||||
handleUploadFile,
|
handleUploadFile,
|
||||||
isUploading,
|
isUploading,
|
||||||
|
removeFile,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,16 +3,21 @@ import { useUploadAndParseFile } from '@/hooks/use-chat-request';
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
export function useUploadFile() {
|
export function useUploadFile() {
|
||||||
const { uploadAndParseFile, loading } = useUploadAndParseFile();
|
const { uploadAndParseFile, loading, cancel } = useUploadAndParseFile();
|
||||||
const [fileIds, setFileIds] = useState<string[]>([]);
|
const [fileIds, setFileIds] = useState<string[]>([]);
|
||||||
|
const [fileMap, setFileMap] = useState<Map<File, string>>(new Map());
|
||||||
|
|
||||||
const handleUploadFile: NonNullable<FileUploadProps['onUpload']> =
|
const handleUploadFile: NonNullable<FileUploadProps['onUpload']> =
|
||||||
useCallback(
|
useCallback(
|
||||||
async (files) => {
|
async (files, options) => {
|
||||||
if (Array.isArray(files) && files.length) {
|
if (Array.isArray(files) && files.length) {
|
||||||
const ret = await uploadAndParseFile(files[0]);
|
const ret = await uploadAndParseFile({ file: files[0], options });
|
||||||
if (ret.code === 0 && Array.isArray(ret.data)) {
|
if (ret.code === 0 && Array.isArray(ret.data)) {
|
||||||
setFileIds((list) => [...list, ...ret.data]);
|
setFileIds((list) => [...list, ...ret.data]);
|
||||||
|
setFileMap((map) => {
|
||||||
|
map.set(files[0], ret.data[0]);
|
||||||
|
return map;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -21,7 +26,28 @@ export function useUploadFile() {
|
|||||||
|
|
||||||
const clearFileIds = useCallback(() => {
|
const clearFileIds = useCallback(() => {
|
||||||
setFileIds([]);
|
setFileIds([]);
|
||||||
|
setFileMap(new Map());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return { handleUploadFile, clearFileIds, fileIds, isUploading: loading };
|
const removeFile = useCallback(
|
||||||
|
(file: File) => {
|
||||||
|
if (loading) {
|
||||||
|
cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const id = fileMap.get(file);
|
||||||
|
if (id) {
|
||||||
|
setFileIds((list) => list.filter((item) => item !== id));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[cancel, fileMap, loading],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleUploadFile,
|
||||||
|
clearFileIds,
|
||||||
|
fileIds,
|
||||||
|
isUploading: loading,
|
||||||
|
removeFile,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user