From 9ee6b9a6c9da1ee3835f85da8b6662de5fb49987 Mon Sep 17 00:00:00 2001 From: ayang <473033518@qq.com> Date: Fri, 16 May 2025 14:32:11 +0800 Subject: [PATCH] feat: add file upload failure handling and alert message --- src/components/Assistant/FileList.tsx | 89 ++++++++++++++++++--------- src/components/Common/Tooltip2.tsx | 42 +++++++++++++ src/components/Search/InputExtra.tsx | 7 ++- src/locales/en/translation.json | 4 ++ src/locales/zh/translation.json | 4 ++ src/stores/chatStore.ts | 4 +- 6 files changed, 118 insertions(+), 32 deletions(-) create mode 100644 src/components/Common/Tooltip2.tsx diff --git a/src/components/Assistant/FileList.tsx b/src/components/Assistant/FileList.tsx index 7335158a..8bdebe07 100644 --- a/src/components/Assistant/FileList.tsx +++ b/src/components/Assistant/FileList.tsx @@ -4,10 +4,11 @@ import { X } from "lucide-react"; import { useAsyncEffect } from "ahooks"; import { useTranslation } from "react-i18next"; -import { useChatStore } from "@/stores/chatStore"; +import { UploadFile, useChatStore } from "@/stores/chatStore"; import { useConnectStore } from "@/stores/connectStore"; import FileIcon from "../Common/Icons/FileIcon"; import platformAdapter from "@/utils/platformAdapter"; +import Tooltip2 from "../Common/Tooltip2"; interface FileListProps { sessionId: string; @@ -39,29 +40,42 @@ const FileList = (props: FileListProps) => { if (uploaded) continue; - const attachmentIds: any = await platformAdapter.commands( - "upload_attachment", - { - serverId, - sessionId, - filePaths: [path], + try { + const attachmentIds: any = await platformAdapter.commands( + "upload_attachment", + { + serverId, + sessionId, + filePaths: [path], + } + ); + + if (!attachmentIds) { + throw new Error("Failed to get attachment id"); + } else { + Object.assign(item, { + uploaded: true, + attachmentId: attachmentIds[0], + }); } - ); - if (!attachmentIds) continue; - - Object.assign(item, { - uploaded: true, - attachmentId: attachmentIds[0], - }); - - setUploadFiles(uploadFiles); + setUploadFiles(uploadFiles); + } catch (error) { + Object.assign(item, { + uploadFailed: true, + failedMessage: String(error), + }); + } } }, [uploadFiles]); - const deleteFile = async (id: string, attachmentId: string) => { + const deleteFile = async (file: UploadFile) => { + const { id, uploadFailed, attachmentId } = file; + setUploadFiles(uploadFiles.filter((file) => file.id !== id)); + if (uploadFailed) return; + platformAdapter.commands("delete_attachment", { serverId, id: attachmentId, @@ -71,16 +85,25 @@ const FileList = (props: FileListProps) => { return (
{uploadFiles.map((file) => { - const { id, name, extname, size, uploaded, attachmentId } = file; + const { + id, + name, + extname, + size, + uploaded, + attachmentId, + uploadFailed, + failedMessage, + } = file; return (
- {attachmentId && ( + {(uploadFailed || attachmentId) && (
{ - deleteFile(id, attachmentId); + deleteFile(file); }} > @@ -94,16 +117,24 @@ const FileList = (props: FileListProps) => { {name}
-
- {uploaded ? ( -
- {extname && {extname}} - - {filesize(size, { standard: "jedec", spacer: "" })} - -
+
+ {uploadFailed && failedMessage ? ( + + Upload Failed + ) : ( - {t("assistant.fileList.uploading")} +
+ {uploaded ? ( +
+ {extname && {extname}} + + {filesize(size, { standard: "jedec", spacer: "" })} + +
+ ) : ( + {t("assistant.fileList.uploading")} + )} +
)}
diff --git a/src/components/Common/Tooltip2.tsx b/src/components/Common/Tooltip2.tsx new file mode 100644 index 00000000..0db6c2ab --- /dev/null +++ b/src/components/Common/Tooltip2.tsx @@ -0,0 +1,42 @@ +import { + Popover, + PopoverButton, + PopoverPanel, + PopoverPanelProps, +} from "@headlessui/react"; +import { useBoolean } from "ahooks"; +import clsx from "clsx"; +import { FC, ReactNode } from "react"; + +interface Tooltip2Props extends PopoverPanelProps { + content: string; + children: ReactNode; +} + +const Tooltip2: FC = (props) => { + const { content, children, anchor = "top", ...rest } = props; + const [visible, { setTrue, setFalse }] = useBoolean(false); + + return ( + + + {children} + + + {content} + + + ); +}; + +export default Tooltip2; diff --git a/src/components/Search/InputExtra.tsx b/src/components/Search/InputExtra.tsx index 1eb41a7a..09f9b8d5 100644 --- a/src/components/Search/InputExtra.tsx +++ b/src/components/Search/InputExtra.tsx @@ -73,6 +73,7 @@ const InputExtra = ({ const modifierKeyPressed = useShortcutsStore((state) => { return state.modifierKeyPressed; }); + const addError = useAppStore((state) => state.addError); const state = useReactive({ screenshotableMonitors: [], @@ -104,6 +105,8 @@ const InputExtra = ({ const stat = await getFileMetadata(path); if (stat.size / 1024 / 1024 > 100) { + addError(t("search.input.uploadFileHints.maxSize")); + continue; } @@ -184,8 +187,8 @@ const InputExtra = ({ return ( - - + +