From d3047821439bb04086e88b33c00873ebafdf5ee2 Mon Sep 17 00:00:00 2001 From: icarus Date: Fri, 22 Aug 2025 17:29:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(hooks):=20=E6=B7=BB=E5=8A=A0useFiles?= =?UTF-8?q?=E9=92=A9=E5=AD=90=E7=94=A8=E4=BA=8E=E6=96=87=E4=BB=B6=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/renderer/src/hooks/useFiles.ts | 70 ++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/renderer/src/hooks/useFiles.ts diff --git a/src/renderer/src/hooks/useFiles.ts b/src/renderer/src/hooks/useFiles.ts new file mode 100644 index 0000000000..d887cdad51 --- /dev/null +++ b/src/renderer/src/hooks/useFiles.ts @@ -0,0 +1,70 @@ +import { FileMetadata } from '@renderer/types' +import { filterSupportedFiles } from '@renderer/utils' +import { useCallback, useMemo, useState } from 'react' +import { useTranslation } from 'react-i18next' + +type Props = { + /** 支持选择的扩展名 */ + extensions?: string[] + multipleSelections?: boolean +} + +export const useFiles = ({ extensions, multipleSelections = true }: Props) => { + const { t } = useTranslation() + + const [files, setFiles] = useState([]) + const [selecting, setSelecting] = useState(false) + + const selectProps: Electron.OpenDialogOptions['properties'] = useMemo( + () => (multipleSelections ? ['openFile', 'multiSelections'] : ['openFile']), + [multipleSelections] + ) + + const onSelectFile = useCallback(async () => { + if (selecting) { + return + } + const supportedExtensions = extensions ?? ['*'] + + // when the number of extensions is greater than 20, use *.* to avoid selecting window lag + const useAllFiles = supportedExtensions.length > 20 + + setSelecting(true) + const _files: FileMetadata[] = await window.api.file.select({ + properties: selectProps, + filters: [ + { + name: 'Files', + extensions: useAllFiles ? ['*'] : supportedExtensions.map((i) => i.replace('.', '')) + } + ] + }) + setSelecting(false) + + if (_files) { + if (!useAllFiles) { + setFiles([...files, ..._files]) + return + } + const supportedFiles = await filterSupportedFiles(_files, supportedExtensions) + if (supportedFiles.length > 0) { + setFiles([...files, ...supportedFiles]) + } + + if (supportedFiles.length !== _files.length) { + window.message.info({ + key: 'file_not_supported', + content: t('chat.input.file_not_supported_count', { + count: _files.length - supportedFiles.length + }) + }) + } + } + }, [extensions, files, selectProps, selecting, t]) + + return { + files, + setFiles, + onSelectFile + } +}