feat: Refactor folder tree management to use FoldersManager and remove onToggle functionality
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import React, { useRef, useSyncExternalStore, useCallback } from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import { useAppShell } from '../lib/context/AppShellContext';
|
||||
import FoldersManager from '../documents/FoldersManager';
|
||||
import { usePanelResizeBindings } from '../app/PanelManagerContext';
|
||||
import type { Identifier } from '../types/identifiers';
|
||||
import SidebarFolderList from './components/SidebarFolderList';
|
||||
@@ -11,10 +10,7 @@ import { TenantOption } from './components/SidebarMenu';
|
||||
import SidebarHeader from './components/SidebarHeader';
|
||||
import SidebarSearch from './components/SidebarSearch';
|
||||
|
||||
|
||||
|
||||
interface SidebarProps {
|
||||
onToggle: (folderId: FolderIdentifier) => void;
|
||||
onSelect: (folderId: FolderIdentifier) => void;
|
||||
onDrop: (event: React.DragEvent<HTMLDivElement>, folderId: FolderIdentifier) => void;
|
||||
onDragOver: (event: React.DragEvent<HTMLDivElement>, folderId: FolderIdentifier) => void;
|
||||
@@ -30,7 +26,6 @@ interface SidebarProps {
|
||||
}
|
||||
|
||||
const Sidebar: React.FC<SidebarProps> = ({
|
||||
onToggle,
|
||||
onSelect,
|
||||
onDrop,
|
||||
onDragOver,
|
||||
@@ -51,7 +46,6 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||
handleLogout,
|
||||
tags = [],
|
||||
correspondents = [],
|
||||
foldersManager,
|
||||
tenant,
|
||||
tenants,
|
||||
tenantOptions,
|
||||
@@ -60,19 +54,6 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||
handleFileSelection, // Used for upload
|
||||
} = useAppShell();
|
||||
|
||||
// Subscribe to folder tree updates
|
||||
const subscribeToTree = useCallback((callback: () => void) => {
|
||||
if (!foldersManager) return () => { };
|
||||
return (foldersManager as FoldersManager).subscribe(callback);
|
||||
}, [foldersManager]);
|
||||
|
||||
const getTreeSnapshot = useCallback(() => {
|
||||
if (!foldersManager) return [];
|
||||
return (foldersManager as FoldersManager).getTreeSnapshot();
|
||||
}, [foldersManager]);
|
||||
|
||||
const roots = useSyncExternalStore(subscribeToTree, getTreeSnapshot);
|
||||
|
||||
const onManageTags = openTagsModal;
|
||||
const onManageCorrespondents = openCorrespondentsModal;
|
||||
|
||||
@@ -81,7 +62,6 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||
const effectiveTenants = (tenants || tenantOptions || []);
|
||||
const activeTenantId = (tenant as TenantOption)?.id;
|
||||
|
||||
// Upload handler wrapper (if needed, or pass handleFileSelection directly if signature matches)
|
||||
const onUploadFiles = handleFileSelection;
|
||||
|
||||
const sidebarRef = useRef<HTMLDivElement | null>(null);
|
||||
@@ -125,9 +105,7 @@ const Sidebar: React.FC<SidebarProps> = ({
|
||||
<SidebarSearch />
|
||||
|
||||
<SidebarFolderList
|
||||
roots={roots}
|
||||
selectedFolder={selectedFolder}
|
||||
onToggle={onToggle}
|
||||
onSelect={onSelect}
|
||||
onDrop={onDrop}
|
||||
onDragOver={onDragOver}
|
||||
|
||||
@@ -4,11 +4,12 @@ import type { Identifier } from '../../types/identifiers';
|
||||
import { FolderPlusIcon } from '../../components/icons';
|
||||
import FolderNode, { FolderIdentifier } from './SidebarFolderNode';
|
||||
import type { FolderTreeNode } from '../../lib/api/apiTypes';
|
||||
import { useAppShell } from '../../lib/context/AppShellContext';
|
||||
import FoldersManager from '../../documents/FoldersManager';
|
||||
import { useSyncExternalStore } from 'react';
|
||||
|
||||
interface SidebarFolderListProps {
|
||||
roots: FolderTreeNode[];
|
||||
selectedFolder: FolderIdentifier | null;
|
||||
onToggle: (folderId: FolderIdentifier) => void;
|
||||
onSelect: (folderId: FolderIdentifier) => void;
|
||||
onDrop: (event: React.DragEvent<HTMLDivElement>, folderId: FolderIdentifier) => void;
|
||||
onDragOver: (event: React.DragEvent<HTMLDivElement>, folderId: FolderIdentifier) => void;
|
||||
@@ -23,9 +24,7 @@ interface SidebarFolderListProps {
|
||||
}
|
||||
|
||||
const SidebarFolderList: React.FC<SidebarFolderListProps> = ({
|
||||
roots,
|
||||
selectedFolder,
|
||||
onToggle,
|
||||
onSelect,
|
||||
onDrop,
|
||||
onDragOver,
|
||||
@@ -38,17 +37,56 @@ const SidebarFolderList: React.FC<SidebarFolderListProps> = ({
|
||||
onCreateFolder,
|
||||
creatingFolder,
|
||||
}) => {
|
||||
// Local expansion state for the UI
|
||||
const { foldersManager } = useAppShell();
|
||||
|
||||
const getTreeSnapshot = useCallback(() => {
|
||||
if (!foldersManager) return [];
|
||||
return (foldersManager as FoldersManager).getTreeSnapshot();
|
||||
}, [foldersManager]);
|
||||
|
||||
const getFolderMap = useCallback(() => {
|
||||
if (!foldersManager) return new Map();
|
||||
return (foldersManager as FoldersManager).getSnapshot();
|
||||
}, [foldersManager]);
|
||||
|
||||
const subscribeToTree = useCallback((callback: () => void) => {
|
||||
if (!foldersManager) return () => { };
|
||||
return (foldersManager as FoldersManager).subscribe(callback);
|
||||
}, [foldersManager]);
|
||||
|
||||
const roots = useSyncExternalStore(subscribeToTree, getTreeSnapshot);
|
||||
const folderMap = useSyncExternalStore(subscribeToTree, getFolderMap);
|
||||
const [expandedIds, setExpandedIds] = React.useState<Set<string>>(new Set(['root']));
|
||||
|
||||
// Override validation or external toggle if needed
|
||||
// Auto-expand ancestors when selected folder changes
|
||||
React.useEffect(() => {
|
||||
if (selectedFolder) {
|
||||
// ensure parent paths are expanded?
|
||||
// Without a parent map, we can't easily auto-expand up.
|
||||
// Rely on user or initial state for now, or add parent pointers.
|
||||
if (!selectedFolder || !folderMap) return;
|
||||
|
||||
const ancestors = new Set<string>();
|
||||
let current = folderMap.get(String(selectedFolder));
|
||||
|
||||
while (current) {
|
||||
const parentId = current.parentId || current.parent_id;
|
||||
if (!parentId || parentId === 'root') break;
|
||||
|
||||
ancestors.add(String(parentId));
|
||||
current = folderMap.get(String(parentId));
|
||||
}
|
||||
}, [selectedFolder]);
|
||||
|
||||
if (ancestors.size > 0) {
|
||||
setExpandedIds((prev) => {
|
||||
const next = new Set(prev);
|
||||
let changed = false;
|
||||
ancestors.forEach(id => {
|
||||
if (!next.has(id)) {
|
||||
next.add(id);
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
return changed ? next : prev;
|
||||
});
|
||||
}
|
||||
}, [selectedFolder, folderMap]);
|
||||
|
||||
const handleToggle = useCallback((folderId: FolderIdentifier) => {
|
||||
setExpandedIds((prev) => {
|
||||
@@ -61,8 +99,7 @@ const SidebarFolderList: React.FC<SidebarFolderListProps> = ({
|
||||
}
|
||||
return next;
|
||||
});
|
||||
onToggle(folderId); // propagate event just in case parent needs it
|
||||
}, [onToggle]);
|
||||
}, []);
|
||||
|
||||
const renderNodes = useCallback(
|
||||
(nodes: FolderTreeNode[], depth: number): ReactNode =>
|
||||
@@ -126,7 +163,7 @@ const SidebarFolderList: React.FC<SidebarFolderListProps> = ({
|
||||
) : null}
|
||||
</div>
|
||||
<ul className="folder-tree">
|
||||
{renderNodes(roots, 0)}
|
||||
{renderNodes(roots as FolderTreeNode[], 0)}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,14 +2,12 @@ import { useMemo } from 'react';
|
||||
import type { DragEvent } from 'react';
|
||||
import type { Identifier } from '../types/identifiers';
|
||||
|
||||
type FolderToggleHandler = (folderId: Identifier) => void | Promise<void>;
|
||||
type FolderDropHandler = (
|
||||
event: DragEvent<HTMLElement>,
|
||||
folderId: Identifier,
|
||||
) => void | Promise<void>;
|
||||
|
||||
interface FolderClickHandlers {
|
||||
onToggle: FolderToggleHandler;
|
||||
onSelect: (folderId: Identifier | null) => void;
|
||||
onDrop: FolderDropHandler;
|
||||
onDragOver: (event: DragEvent<HTMLElement>, folderId: Identifier) => void;
|
||||
@@ -30,7 +28,6 @@ interface UseSidebarPropsArgs {
|
||||
}
|
||||
|
||||
interface SidebarHookResult {
|
||||
onToggle: FolderClickHandlers['onToggle'];
|
||||
onSelect: FolderClickHandlers['onSelect'];
|
||||
onDrop: FolderClickHandlers['onDrop'];
|
||||
onDragOver: FolderClickHandlers['onDragOver'];
|
||||
@@ -45,44 +42,23 @@ interface SidebarHookResult {
|
||||
creatingFolder: boolean;
|
||||
}
|
||||
|
||||
const useSidebarProps = ({
|
||||
folderClickHandlers,
|
||||
handleFolderDelete,
|
||||
handleFolderRename,
|
||||
selectedFolder,
|
||||
handleFolderDragStart,
|
||||
handleFolderDragEnd,
|
||||
draggedFolderId,
|
||||
handlePromptCreateFolder,
|
||||
creatingFolder,
|
||||
}: UseSidebarPropsArgs): SidebarHookResult =>
|
||||
const useSidebarProps = (args: UseSidebarPropsArgs): SidebarHookResult =>
|
||||
useMemo(
|
||||
() => ({
|
||||
onToggle: folderClickHandlers.onToggle,
|
||||
onSelect: folderClickHandlers.onSelect,
|
||||
onDrop: folderClickHandlers.onDrop,
|
||||
onDragOver: folderClickHandlers.onDragOver,
|
||||
onDragLeave: folderClickHandlers.onDragLeave,
|
||||
onDeleteFolder: handleFolderDelete,
|
||||
onRenameFolder: handleFolderRename,
|
||||
selectedFolder,
|
||||
onFolderDragStart: handleFolderDragStart,
|
||||
onFolderDragEnd: handleFolderDragEnd,
|
||||
draggedFolderId,
|
||||
onCreateFolder: (parentId) => handlePromptCreateFolder?.(parentId),
|
||||
creatingFolder,
|
||||
onSelect: args.folderClickHandlers.onSelect,
|
||||
onDrop: args.folderClickHandlers.onDrop,
|
||||
onDragOver: args.folderClickHandlers.onDragOver,
|
||||
onDragLeave: args.folderClickHandlers.onDragLeave,
|
||||
onDeleteFolder: args.handleFolderDelete,
|
||||
onRenameFolder: args.handleFolderRename,
|
||||
selectedFolder: args.selectedFolder,
|
||||
onFolderDragStart: args.handleFolderDragStart,
|
||||
onFolderDragEnd: args.handleFolderDragEnd,
|
||||
draggedFolderId: args.draggedFolderId,
|
||||
onCreateFolder: (parentId) => args.handlePromptCreateFolder?.(parentId),
|
||||
creatingFolder: args.creatingFolder,
|
||||
}),
|
||||
[
|
||||
creatingFolder,
|
||||
folderClickHandlers,
|
||||
handleFolderDragEnd,
|
||||
handleFolderDragStart,
|
||||
handleFolderDelete,
|
||||
handleFolderRename,
|
||||
handlePromptCreateFolder,
|
||||
draggedFolderId,
|
||||
selectedFolder,
|
||||
],
|
||||
[args],
|
||||
);
|
||||
|
||||
export default useSidebarProps;
|
||||
|
||||
Reference in New Issue
Block a user