mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-06 21:35:52 +08:00
refactor: use TypeScript function overloading for uploadNotes type safety (#11833)
* Initial plan * refactor: use TypeScript function overloading for uploadNotes Co-authored-by: DeJeune <67425183+DeJeune@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: DeJeune <67425183+DeJeune@users.noreply.github.com>
This commit is contained in:
parent
377614713b
commit
82628edbd2
@ -13,7 +13,8 @@ import {
|
||||
loadTree,
|
||||
renameNode as renameEntry,
|
||||
sortTree,
|
||||
uploadNotes
|
||||
uploadNotes,
|
||||
type FileEntryData
|
||||
} from '@renderer/services/NotesService'
|
||||
import {
|
||||
addUniquePath,
|
||||
@ -652,10 +653,7 @@ const NotesPage: FC = () => {
|
||||
|
||||
// 处理文件上传
|
||||
const handleUploadFiles = useCallback(
|
||||
async (
|
||||
files: File[] | Array<{ fullPath: string; isFile: boolean; isDirectory: boolean; systemPath: string }>,
|
||||
overrideTargetFolderPath?: string
|
||||
) => {
|
||||
async (files: File[] | FileEntryData[], overrideTargetFolderPath?: string) => {
|
||||
try {
|
||||
if (!files || files.length === 0) {
|
||||
window.toast.warning(t('notes.no_file_selected'))
|
||||
|
||||
@ -2,6 +2,7 @@ import { DynamicVirtualList } from '@renderer/components/VirtualList'
|
||||
import { useActiveNode } from '@renderer/hooks/useNotesQuery'
|
||||
import NotesSidebarHeader from '@renderer/pages/notes/NotesSidebarHeader'
|
||||
import { findNode, findParent } from '@renderer/services/NotesTreeService'
|
||||
import { type FileEntryData } from '@renderer/services/NotesService'
|
||||
import { useAppSelector } from '@renderer/store'
|
||||
import { selectSortType } from '@renderer/store/note'
|
||||
import type { NotesSortType, NotesTreeNode } from '@renderer/types/note'
|
||||
@ -38,10 +39,7 @@ interface NotesSidebarProps {
|
||||
onToggleStar: (nodeId: string) => void
|
||||
onMoveNode: (sourceNodeId: string, targetNodeId: string, position: 'before' | 'after' | 'inside') => void
|
||||
onSortNodes: (sortType: NotesSortType) => void
|
||||
onUploadFiles: (
|
||||
files: File[] | Array<{ fullPath: string; isFile: boolean; isDirectory: boolean; systemPath: string }>,
|
||||
targetFolderPath?: string
|
||||
) => void
|
||||
onUploadFiles: (files: File[] | FileEntryData[], targetFolderPath?: string) => void
|
||||
notesTree: NotesTreeNode[]
|
||||
selectedFolderId?: string | null
|
||||
notesPath?: string
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
import { loggerService } from '@logger'
|
||||
import { type FileEntryData } from '@renderer/services/NotesService'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const logger = loggerService.withContext('useNotesFileUpload')
|
||||
|
||||
interface UseNotesFileUploadProps {
|
||||
onUploadFiles: (
|
||||
files: File[] | Array<{ fullPath: string; isFile: boolean; isDirectory: boolean; systemPath: string }>,
|
||||
targetFolderPath?: string
|
||||
) => void
|
||||
onUploadFiles: (files: File[] | FileEntryData[], targetFolderPath?: string) => void
|
||||
setIsDragOverSidebar: (isDragOver: boolean) => void
|
||||
getTargetFolderPath?: () => string | null
|
||||
refreshTree?: () => Promise<void>
|
||||
@ -36,12 +34,7 @@ export const useNotesFileUpload = ({
|
||||
if (items.length === 0) return
|
||||
|
||||
// Collect all entries with their fullPath preserved
|
||||
const entryDataList: Array<{
|
||||
fullPath: string
|
||||
isFile: boolean
|
||||
isDirectory: boolean
|
||||
systemPath: string
|
||||
}> = []
|
||||
const entryDataList: FileEntryData[] = []
|
||||
|
||||
const processEntry = async (entry: FileSystemEntry): Promise<void> => {
|
||||
if (entry.isFile) {
|
||||
@ -117,7 +110,7 @@ export const useNotesFileUpload = ({
|
||||
|
||||
if (entryDataList.length > 0) {
|
||||
// Pass entry data list to parent for recursive upload with optional target override
|
||||
onUploadFiles(entryDataList as any, overrideTargetFolderPath)
|
||||
onUploadFiles(entryDataList, overrideTargetFolderPath)
|
||||
}
|
||||
} else {
|
||||
// Fallback for browsers without FileSystemEntry API
|
||||
|
||||
@ -15,6 +15,13 @@ export interface UploadResult {
|
||||
folderCount: number
|
||||
}
|
||||
|
||||
export interface FileEntryData {
|
||||
fullPath: string
|
||||
isFile: boolean
|
||||
isDirectory: boolean
|
||||
systemPath: string
|
||||
}
|
||||
|
||||
export async function loadTree(rootPath: string): Promise<NotesTreeNode[]> {
|
||||
return window.api.file.getDirectoryStructure(normalizePath(rootPath))
|
||||
}
|
||||
@ -83,13 +90,16 @@ export async function renameNode(node: NotesTreeNode, newName: string): Promise<
|
||||
return { path: `${parentDir}/${safeName}`, name: safeName }
|
||||
}
|
||||
|
||||
// Function overloads for type safety
|
||||
export async function uploadNotes(files: File[], targetPath: string): Promise<UploadResult>
|
||||
export async function uploadNotes(entries: FileEntryData[], targetPath: string): Promise<UploadResult>
|
||||
|
||||
// Implementation signature
|
||||
export async function uploadNotes(
|
||||
files: File[] | Array<{ fullPath: string; isFile: boolean; isDirectory: boolean; systemPath: string }>,
|
||||
filesOrEntries: File[] | FileEntryData[],
|
||||
targetPath: string
|
||||
): Promise<UploadResult> {
|
||||
const basePath = normalizePath(targetPath)
|
||||
|
||||
if (files.length === 0) {
|
||||
if (filesOrEntries.length === 0) {
|
||||
return {
|
||||
uploadedNodes: [],
|
||||
totalFiles: 0,
|
||||
@ -99,23 +109,26 @@ export async function uploadNotes(
|
||||
}
|
||||
}
|
||||
|
||||
const firstItem = files[0]
|
||||
const isEntryDataList =
|
||||
typeof firstItem === 'object' && 'fullPath' in firstItem && 'systemPath' in firstItem && 'isFile' in firstItem
|
||||
|
||||
if (isEntryDataList) {
|
||||
const entries = files as Array<{ fullPath: string; isFile: boolean; isDirectory: boolean; systemPath: string }>
|
||||
return uploadNotesRecursive(entries, targetPath)
|
||||
// Check if we're dealing with FileEntryData by looking at the first item
|
||||
const firstItem = filesOrEntries[0]
|
||||
if ('fullPath' in firstItem && 'systemPath' in firstItem) {
|
||||
return uploadNotesFromEntries(filesOrEntries as FileEntryData[], targetPath)
|
||||
}
|
||||
|
||||
// Legacy approach: File objects (for browser File API compatibility)
|
||||
const fileList = files as File[]
|
||||
const totalFiles = fileList.length
|
||||
return uploadNotesFromFiles(filesOrEntries as File[], targetPath)
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload notes from File objects (browser File API)
|
||||
*/
|
||||
async function uploadNotesFromFiles(files: File[], targetPath: string): Promise<UploadResult> {
|
||||
const basePath = normalizePath(targetPath)
|
||||
const totalFiles = files.length
|
||||
|
||||
try {
|
||||
const filePaths: string[] = []
|
||||
|
||||
for (const file of fileList) {
|
||||
for (const file of files) {
|
||||
const filePath = window.api.file.getPathForFile(file)
|
||||
|
||||
if (filePath) {
|
||||
@ -156,7 +169,7 @@ export async function uploadNotes(
|
||||
folderCount: result.folderCount
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Legacy file upload failed:', error as Error)
|
||||
logger.error('File upload failed:', error as Error)
|
||||
return {
|
||||
uploadedNodes: [],
|
||||
totalFiles,
|
||||
@ -167,14 +180,18 @@ export async function uploadNotes(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload notes from FileEntryData (drag-and-drop with directory structure)
|
||||
*/
|
||||
async function uploadNotesFromEntries(entries: FileEntryData[], targetPath: string): Promise<UploadResult> {
|
||||
return uploadNotesRecursive(entries, targetPath)
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive upload for drag-and-drop with fullPath preserved (VS Code approach)
|
||||
* Uses batch processing for better performance
|
||||
*/
|
||||
async function uploadNotesRecursive(
|
||||
entryDataList: Array<{ fullPath: string; isFile: boolean; isDirectory: boolean; systemPath: string }>,
|
||||
targetPath: string
|
||||
): Promise<UploadResult> {
|
||||
async function uploadNotesRecursive(entryDataList: FileEntryData[], targetPath: string): Promise<UploadResult> {
|
||||
const basePath = normalizePath(targetPath)
|
||||
|
||||
try {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user