mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-31 08:29:07 +08:00
fix: update file upload handling and remove unused root selection
This commit is contained in:
parent
2863961349
commit
bcf1fa90ed
@ -574,12 +574,6 @@ const NotesPage: FC = () => {
|
||||
[dispatch, handleToggleExpanded, invalidateFileContent]
|
||||
)
|
||||
|
||||
// 选中根(清空选择)
|
||||
const handleSelectRoot = useCallback(() => {
|
||||
dispatch(setActiveFilePath(undefined))
|
||||
setSelectedFolderId(null)
|
||||
}, [dispatch])
|
||||
|
||||
// 删除节点
|
||||
const handleDeleteNode = useCallback(
|
||||
async (nodeId: string) => {
|
||||
@ -658,14 +652,19 @@ const NotesPage: FC = () => {
|
||||
|
||||
// 处理文件上传
|
||||
const handleUploadFiles = useCallback(
|
||||
async (files: File[]) => {
|
||||
async (
|
||||
files:
|
||||
| File[]
|
||||
| Array<{ fullPath: string; isFile: boolean; isDirectory: boolean; systemPath: string }>,
|
||||
overrideTargetFolderPath?: string
|
||||
) => {
|
||||
try {
|
||||
if (!files || files.length === 0) {
|
||||
window.toast.warning(t('notes.no_file_selected'))
|
||||
return
|
||||
}
|
||||
|
||||
const targetFolderPath = getTargetFolderPath()
|
||||
const targetFolderPath = overrideTargetFolderPath || getTargetFolderPath()
|
||||
if (!targetFolderPath) {
|
||||
throw new Error('No folder path selected')
|
||||
}
|
||||
@ -937,7 +936,6 @@ const NotesPage: FC = () => {
|
||||
notesTree={notesTree}
|
||||
selectedFolderId={selectedFolderId}
|
||||
onSelectNode={handleSelectNode}
|
||||
onSelectRoot={handleSelectRoot}
|
||||
onCreateFolder={handleCreateFolder}
|
||||
onCreateNote={handleCreateNote}
|
||||
onDeleteNode={handleDeleteNode}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { DynamicVirtualList } from '@renderer/components/VirtualList'
|
||||
import { useActiveNode } from '@renderer/hooks/useNotesQuery'
|
||||
import NotesSidebarHeader from '@renderer/pages/notes/NotesSidebarHeader'
|
||||
import { findNode } from '@renderer/services/NotesService'
|
||||
import { findNode, findParent } from '@renderer/services/NotesTreeService'
|
||||
import { useAppSelector } from '@renderer/store'
|
||||
import { selectSortType } from '@renderer/store/note'
|
||||
import type { NotesSortType, NotesTreeNode } from '@renderer/types/note'
|
||||
@ -32,14 +32,16 @@ interface NotesSidebarProps {
|
||||
onCreateFolder: (name: string, targetFolderId?: string) => void
|
||||
onCreateNote: (name: string, targetFolderId?: string) => void
|
||||
onSelectNode: (node: NotesTreeNode) => void
|
||||
onSelectRoot: () => void
|
||||
onDeleteNode: (nodeId: string) => void
|
||||
onRenameNode: (nodeId: string, newName: string) => void
|
||||
onToggleExpanded: (nodeId: string) => void
|
||||
onToggleStar: (nodeId: string) => void
|
||||
onMoveNode: (sourceNodeId: string, targetNodeId: string, position: 'before' | 'after' | 'inside') => void
|
||||
onSortNodes: (sortType: NotesSortType) => void
|
||||
onUploadFiles: (files: File[]) => void
|
||||
onUploadFiles: (
|
||||
files: File[] | Array<{ fullPath: string; isFile: boolean; isDirectory: boolean; systemPath: string }>,
|
||||
targetFolderPath?: string
|
||||
) => void
|
||||
notesTree: NotesTreeNode[]
|
||||
selectedFolderId?: string | null
|
||||
notesPath?: string
|
||||
@ -50,7 +52,6 @@ const NotesSidebar: FC<NotesSidebarProps> = ({
|
||||
onCreateFolder,
|
||||
onCreateNote,
|
||||
onSelectNode,
|
||||
onSelectRoot,
|
||||
onDeleteNode,
|
||||
onRenameNode,
|
||||
onToggleExpanded,
|
||||
@ -97,6 +98,7 @@ const NotesSidebar: FC<NotesSidebarProps> = ({
|
||||
onUploadFiles,
|
||||
setIsDragOverSidebar,
|
||||
getTargetFolderPath: () => {
|
||||
// Default target folder path (used by select dialogs and as fallback)
|
||||
if (selectedFolderId) {
|
||||
const selectedNode = findNode(notesTree, selectedFolderId)
|
||||
if (selectedNode && selectedNode.type === 'folder') {
|
||||
@ -394,7 +396,24 @@ const NotesSidebar: FC<NotesSidebarProps> = ({
|
||||
onDragLeave={() => setIsDragOverSidebar(false)}
|
||||
onDrop={(e) => {
|
||||
if (!draggedNodeId) {
|
||||
handleDropFiles(e)
|
||||
// External drop: determine precise target folder by hit element
|
||||
let overrideTarget: string | undefined
|
||||
const targetEl = (e.target as HTMLElement).closest('[data-node-id]') as HTMLElement | null
|
||||
if (targetEl) {
|
||||
const nodeId = targetEl.getAttribute('data-node-id') || ''
|
||||
const node = nodeId ? findNode(notesTree, nodeId) : null
|
||||
if (node) {
|
||||
if (node.type === 'folder') {
|
||||
overrideTarget = node.externalPath
|
||||
} else {
|
||||
const parent = findParent(notesTree, node.id)
|
||||
if (parent && parent.type === 'folder') {
|
||||
overrideTarget = parent.externalPath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
handleDropFiles(e, overrideTarget)
|
||||
}
|
||||
}}>
|
||||
<NotesSidebarHeader
|
||||
@ -436,40 +455,44 @@ const NotesSidebar: FC<NotesSidebarProps> = ({
|
||||
trigger={['contextMenu']}
|
||||
open={openDropdownKey === 'empty-area'}
|
||||
onOpenChange={(open) => setOpenDropdownKey(open ? 'empty-area' : null)}>
|
||||
<DynamicVirtualList
|
||||
ref={virtualListRef}
|
||||
list={flattenedNodes}
|
||||
size={0}
|
||||
estimateSize={() => 28}
|
||||
scrollerStyle={{ flex: 1, minHeight: 0, height: 'auto' }}
|
||||
itemContainerStyle={{ padding: '8px 8px 0 8px' }}
|
||||
overscan={10}
|
||||
isSticky={isSticky}
|
||||
getItemDepth={getItemDepth}
|
||||
containerProps={{
|
||||
onContextMenu: (e) => {
|
||||
const target = e.target as HTMLElement
|
||||
if (!target.closest('[data-index]')) {
|
||||
onSelectRoot()
|
||||
setOpenDropdownKey('empty-area')
|
||||
}
|
||||
},
|
||||
onClick: (e) => {
|
||||
const target = e.target as HTMLElement
|
||||
if (!target.closest('[data-index]')) {
|
||||
onSelectRoot()
|
||||
}
|
||||
<div
|
||||
style={{ flex: 1, minHeight: 0 }}
|
||||
onContextMenu={(e) => {
|
||||
const target = e.target as HTMLElement
|
||||
// Right-click on empty space should not change selection
|
||||
if (!target.closest('[data-index]')) {
|
||||
setOpenDropdownKey('empty-area')
|
||||
e.preventDefault()
|
||||
}
|
||||
}}>
|
||||
{({ node, depth }) => (
|
||||
<TreeNode
|
||||
node={node}
|
||||
depth={depth}
|
||||
renderChildren={false}
|
||||
onHintClick={node.type === 'hint' ? handleSelectFolder : undefined}
|
||||
/>
|
||||
)}
|
||||
</DynamicVirtualList>
|
||||
<DynamicVirtualList
|
||||
ref={virtualListRef}
|
||||
list={flattenedNodes}
|
||||
estimateSize={() => 28}
|
||||
scrollerStyle={{ flex: 1, minHeight: 0, height: '100%' }}
|
||||
itemContainerStyle={{ padding: '8px 8px 0 8px' }}
|
||||
overscan={10}
|
||||
isSticky={isSticky}
|
||||
getItemDepth={getItemDepth}
|
||||
containerProps={{
|
||||
// Double click on empty area: create a new note (.md)
|
||||
onDoubleClick: (e) => {
|
||||
const target = e.target as HTMLElement
|
||||
if (!target.closest('[data-index]')) {
|
||||
handleCreateNote()
|
||||
}
|
||||
}
|
||||
}}>
|
||||
{({ node, depth }) => (
|
||||
<TreeNode
|
||||
node={node}
|
||||
depth={depth}
|
||||
renderChildren={false}
|
||||
onHintClick={node.type === 'hint' ? handleSelectFolder : undefined}
|
||||
/>
|
||||
)}
|
||||
</DynamicVirtualList>
|
||||
</div>
|
||||
</Dropdown>
|
||||
</NotesTreeContainer>
|
||||
|
||||
|
||||
@ -5,7 +5,10 @@ import { useTranslation } from 'react-i18next'
|
||||
const logger = loggerService.withContext('useNotesFileUpload')
|
||||
|
||||
interface UseNotesFileUploadProps {
|
||||
onUploadFiles: (files: File[]) => void
|
||||
onUploadFiles: (
|
||||
files: File[] | Array<{ fullPath: string; isFile: boolean; isDirectory: boolean; systemPath: string }>,
|
||||
targetFolderPath?: string
|
||||
) => void
|
||||
setIsDragOverSidebar: (isDragOver: boolean) => void
|
||||
getTargetFolderPath?: () => string | null
|
||||
refreshTree?: () => Promise<void>
|
||||
@ -25,7 +28,7 @@ export const useNotesFileUpload = ({
|
||||
* This ensures dragging ~/Users/me/tmp/xxx creates target/tmp/xxx
|
||||
*/
|
||||
const handleDropFiles = useCallback(
|
||||
async (e: React.DragEvent) => {
|
||||
async (e: React.DragEvent, overrideTargetFolderPath?: string) => {
|
||||
e.preventDefault()
|
||||
setIsDragOverSidebar(false)
|
||||
|
||||
@ -113,14 +116,14 @@ export const useNotesFileUpload = ({
|
||||
await Promise.all(promises)
|
||||
|
||||
if (entryDataList.length > 0) {
|
||||
// Pass entry data list to parent for recursive upload
|
||||
onUploadFiles(entryDataList as any)
|
||||
// Pass entry data list to parent for recursive upload with optional target override
|
||||
onUploadFiles(entryDataList as any, overrideTargetFolderPath)
|
||||
}
|
||||
} else {
|
||||
// Fallback for browsers without FileSystemEntry API
|
||||
const regularFiles = Array.from(e.dataTransfer.files)
|
||||
if (regularFiles.length > 0) {
|
||||
onUploadFiles(regularFiles)
|
||||
onUploadFiles(regularFiles, overrideTargetFolderPath)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user