From 6ba8084caa6d02c3b237499ef2e7e62c091cb963 Mon Sep 17 00:00:00 2001 From: Nils Schneider Date: Fri, 12 Dec 2025 02:57:10 +0100 Subject: [PATCH] feat: Refactor DocumentsPanel to use a shared application shell context for state management --- .../src/app/WorkspaceSelectionContext.tsx | 2 +- frontend/src/app/useDocumentsShell.ts | 7 +- frontend/src/app/useWorkspaceSurface.tsx | 1 - .../documents/logic/useDocumentsPanelProps.ts | 192 ------------------ .../src/documents/panel/DocumentsPanel.tsx | 109 +++++----- .../documents/panel/DocumentsPanelHeader.tsx | 2 +- .../panel/useDocumentsContextValues.ts | 134 ++++++------ 7 files changed, 109 insertions(+), 338 deletions(-) delete mode 100644 frontend/src/documents/logic/useDocumentsPanelProps.ts diff --git a/frontend/src/app/WorkspaceSelectionContext.tsx b/frontend/src/app/WorkspaceSelectionContext.tsx index c73fef9..c562f31 100644 --- a/frontend/src/app/WorkspaceSelectionContext.tsx +++ b/frontend/src/app/WorkspaceSelectionContext.tsx @@ -2,7 +2,7 @@ import React from 'react'; import type { useWorkspaceSelection } from './useWorkspaceSelection'; import { createSafeContext } from '../utils/createSafeContext'; -export type WorkspaceSelectionValue = ReturnType; +type WorkspaceSelectionValue = ReturnType; const [WorkspaceSelectionContext, useWorkspaceSelectionContext] = createSafeContext('WorkspaceSelection'); diff --git a/frontend/src/app/useDocumentsShell.ts b/frontend/src/app/useDocumentsShell.ts index 743f36e..c7b2494 100644 --- a/frontend/src/app/useDocumentsShell.ts +++ b/frontend/src/app/useDocumentsShell.ts @@ -4,8 +4,6 @@ import type { DocumentsFilterValue } from '../documents/context/DocumentsFilterC import type { UseWorkspaceSurfaceArgs } from './useWorkspaceSurface'; import type { Identifier } from '../types/identifiers'; -import useDocumentsPanelProps from '../documents/logic/useDocumentsPanelProps'; - type WorkspaceSurfaceConfig = Omit & { openDetailPanel?: (documentId: Identifier) => void; closeDetailPanel?: () => void; @@ -21,11 +19,10 @@ interface DocumentsShellView { const useDocumentsShell = (): DocumentsShellView => { const shell = useAppShell() as any; - const documentsPanelProps = useDocumentsPanelProps(shell as any); return useMemo(() => { const surfaceConfig: WorkspaceSurfaceConfig = { - documentsTableProps: documentsPanelProps, + documentsTableProps: {}, detailPanelProps: (shell.detailPanel?.detailPanelProps ?? null) as WorkspaceSurfaceConfig['detailPanelProps'], detailPanelOpen: Boolean(shell.detailPanel?.detailPanelOpen), openDetailPanel: shell.detailPanel?.openDetailPanel as WorkspaceSurfaceConfig['openDetailPanel'], @@ -46,7 +43,7 @@ const useDocumentsShell = (): DocumentsShellView => { documentsManager: shell.managers?.documentsManager, foldersManager: shell.folderTree?.foldersManager, }; - }, [shell, documentsPanelProps]); + }, [shell]); }; export default useDocumentsShell; diff --git a/frontend/src/app/useWorkspaceSurface.tsx b/frontend/src/app/useWorkspaceSurface.tsx index e4e8747..ceea65e 100644 --- a/frontend/src/app/useWorkspaceSurface.tsx +++ b/frontend/src/app/useWorkspaceSurface.tsx @@ -150,7 +150,6 @@ export const useWorkspaceSurface = ({ return { content: ( ), diff --git a/frontend/src/documents/logic/useDocumentsPanelProps.ts b/frontend/src/documents/logic/useDocumentsPanelProps.ts deleted file mode 100644 index 2adf457..0000000 --- a/frontend/src/documents/logic/useDocumentsPanelProps.ts +++ /dev/null @@ -1,192 +0,0 @@ -import type { WorkspaceSelectionValue } from '../../app/WorkspaceSelectionContext'; -import type { Identifier } from '../../types/identifiers'; - -interface Breadcrumb { - id?: Identifier; - name?: string; - label?: string; - title?: string; -} - -interface FolderClickHandlers { - onDrop?: (...args: unknown[]) => void; - onDragOver?: (...args: unknown[]) => void; - onDragLeave?: (...args: unknown[]) => void; -} - -interface UseDocumentsPanelPropsArgs { - currentFolderName?: string | null; - breadcrumbs?: Breadcrumb[]; - refreshCurrentFolder?: () => void | Promise; - currentSubfolders?: unknown[]; - documents?: unknown[]; - searchQuery?: string; - searchResultIds?: Identifier[] | null; - folderClickHandlers: FolderClickHandlers; - selectedFolder?: Identifier | 'root' | null; - selectFolder?: (...args: unknown[]) => void; - handleFolderDragStart?: (...args: unknown[]) => void; - handleFolderDragEnd?: (...args: unknown[]) => void; - draggedFolderId?: Identifier | null; - handleFolderRename?: (...args: unknown[]) => void; - handleDocumentTitleUpdate?: (...args: unknown[]) => void; - focusedRowKey?: Identifier | string | null; - draggedDocumentIds?: Identifier[]; - handleDocumentDragStart?: (...args: unknown[]) => void; - handleDocumentDragEnd?: (...args: unknown[]) => void; - searchLoading?: boolean; - tagLookupById?: unknown; - activeTagFilters?: Identifier[]; - activeCorrespondentFilters?: Identifier[]; - ensureAssetUrl?: (...args: unknown[]) => void; - getDocumentAsset?: (...args: unknown[]) => unknown; - handleDocumentTagAttach?: (docId: Identifier, tagId: Identifier) => void; - handleDocumentTagDetach?: (docId: Identifier, tagId: Identifier) => void; - documentsViewMode?: string; - documentsSortField?: string; - documentsSortDirection?: string; - handleDocumentsSortFieldChange?: (field: string) => void; - handleDocumentsSortDirectionToggle?: () => void; - handleDocumentsViewModeChange?: (mode: string) => void; - clearDocumentSelection?: () => void; - handleDeleteSelection?: () => void; - handleEntryPointerCore?: (...args: unknown[]) => void; - tags?: unknown[]; - correspondents?: unknown[]; - correspondentLookupById?: unknown; - handleBulkTagAddFromDetail?: (...args: unknown[]) => void; - handleBulkTagRemoveFromDetail?: (...args: unknown[]) => void; - handleBulkCorrespondentAdd?: (...args: unknown[]) => void; - handleBulkCorrespondentRemove?: (...args: unknown[]) => void; - handleBulkSelectionReanalyze?: (...args: unknown[]) => void; - folderOptions?: unknown[]; - moveDocumentsToFolder?: (...args: unknown[]) => void; - documentLookup?: unknown; - selectionValue: WorkspaceSelectionValue; -} - -const useDocumentsPanelProps = (props: UseDocumentsPanelPropsArgs) => { - const { - folderTree: { - currentFolderName, - breadcrumbs, - currentSubfolders, - folderClickHandlers, - selectedFolder, - selectFolder, - handleFolderDragStart, - handleFolderDragEnd, - draggedFolderId, - handleFolderRename, - folderOptions, - moveDocumentsToFolder, - } = {}, - search: { - documents, - searchQuery, - searchResultIds, - searchLoading, - documentsViewMode, - documentsSortField, - documentsSortDirection, - handleDocumentsSortFieldChange, - handleDocumentsSortDirectionToggle, - handleDocumentsViewModeChange, - } = {}, - ui: { - refreshCurrentFolder, - } = {}, - mutations: { - handleDocumentTitleUpdate, - draggedDocumentIds, - handleDocumentDragStart, - handleDocumentDragEnd, - } = {}, - selection: { - selectionValue, - handleDeleteSelection, - handleEntryPointerCore, - handleBulkSelectionReanalyze, - } = {}, - tags: { - tagLookupById, - activeTagFilters, - tags, - handleDocumentTagAttach, - handleDocumentTagDetach, - handleBulkTagAddFromDetail, - handleBulkTagRemoveFromDetail, - } = {}, - correspondents: { - activeCorrespondentFilters, - correspondents, - correspondentLookupById, - handleBulkCorrespondentAdd, - handleBulkCorrespondentRemove, - } = {}, - preview: { - ensureAssetUrl, - getDocumentAsset, - } = {}, - managers: { - documentLookup, - } = {}, - } = props as any; - - const focusedRowKey = selectionValue?.focusedEntryKey || null; - - return { - currentFolderName, - breadcrumbs, - onRefresh: refreshCurrentFolder, - subfolders: currentSubfolders, - documents, - searchQuery, - searchResultIds, - selectedFolder, - onFolderSelect: selectFolder, - onFolderDrop: folderClickHandlers.onDrop, - onFolderDragOver: folderClickHandlers.onDragOver, - onFolderDragLeave: folderClickHandlers.onDragLeave, - onFolderDragStart: handleFolderDragStart, - onFolderDragEnd: handleFolderDragEnd, - draggedFolderId, - onFolderRename: handleFolderRename, - onDocumentRename: handleDocumentTitleUpdate, - focusedRowKey, - draggingDocumentIds: draggedDocumentIds, - onDocumentDragStart: handleDocumentDragStart, - onDocumentDragEnd: handleDocumentDragEnd, - isSearchLoading: searchLoading, - tagLookupById, - activeTagFilters, - activeCorrespondentFilters, - activeCorrespondentIds: activeCorrespondentFilters, - ensureAssetUrl, - getDocumentAsset, - onDocumentTagAttach: handleDocumentTagAttach, - onDocumentTagDetach: handleDocumentTagDetach, - viewMode: documentsViewMode, - sortField: documentsSortField, - sortDirection: documentsSortDirection, - onSortFieldChange: handleDocumentsSortFieldChange, - onSortDirectionToggle: handleDocumentsSortDirectionToggle, - onViewModeChange: handleDocumentsViewModeChange, - onDeleteSelection: handleDeleteSelection, - onEntryPointer: handleEntryPointerCore, - tags, - correspondents, - correspondentLookupById, - documentLookup, - onBulkTagAdd: handleBulkTagAddFromDetail, - onBulkTagRemove: handleBulkTagRemoveFromDetail, - onBulkCorrespondentAdd: handleBulkCorrespondentAdd, - onBulkCorrespondentRemove: handleBulkCorrespondentRemove, - onBulkReanalyze: handleBulkSelectionReanalyze, - folderOptions, - onMoveDocumentsToFolder: moveDocumentsToFolder, - selectionValue, - }; -}; - -export default useDocumentsPanelProps; diff --git a/frontend/src/documents/panel/DocumentsPanel.tsx b/frontend/src/documents/panel/DocumentsPanel.tsx index d76f40a..1d7bb71 100644 --- a/frontend/src/documents/panel/DocumentsPanel.tsx +++ b/frontend/src/documents/panel/DocumentsPanel.tsx @@ -1,27 +1,29 @@ import React, { - useCallback, useMemo, useState, useRef, useEffect, + useCallback, } from 'react'; import TagRemovalZone from '../components/TagRemovalZone'; import { DocumentsList, DocumentsGrid } from '../DocumentsView'; import type { ReactNode } from 'react'; import type { DocumentsListEntry, - Correspondent, } from '../../types/documents'; -import type { Identifier } from '../../types/identifiers'; + +const EntryType = { + folder: 'folder' as const, + document: 'document' as const, +}; + import DesktopWorkspace from '../../desktop/components/DesktopWorkspace'; import { WorkspaceSelectionProvider, useWorkspaceSelectionContext, - type WorkspaceSelectionValue, } from '../../app/WorkspaceSelectionContext'; import DocumentsPanelHeader, { DocumentsPanelHeaderConfig, - DocumentsHeaderBreadcrumb, } from './DocumentsPanelHeader'; import { SelectionFloatingPanel } from '../features/selection/SelectionFloatingActions'; import { createDocumentsTableHeaderActions } from './DocumentsToolbar'; @@ -35,58 +37,31 @@ import { DocumentsAssetContext } from '../context/DocumentsAssetContext'; import { DocumentsViewStateContext } from '../context/DocumentsViewStateContext'; import { DocumentsCommandContext } from '../context/DocumentsCommandContext'; import { useDocumentsContextValues } from './useDocumentsContextValues'; - -const EntryType = { - folder: 'folder' as const, - document: 'document' as const, -}; - -export interface DocumentsPanelInnerProps { - headerLeading?: ReactNode; - onBreadcrumbNavigate?: (crumb: DocumentsHeaderBreadcrumb) => void; - [key: string]: any; -} - -interface DocumentsPanelProps extends DocumentsPanelInnerProps { - selectionValue: WorkspaceSelectionValue; - correspondentLookupById?: Map; -} - +import { useAppShell } from '../../lib/context/AppShellContext'; import type { TagInteractionHandlers } from '../interactions/useTagInteractions'; export interface DocumentsViewProps { entries: DocumentsListEntry[]; tagHandlers?: TagInteractionHandlers; + [key: string]: any; } -const DocumentsPanelInner: React.FC = (props) => { +interface DocumentsPanelProps { + headerLeading?: ReactNode; +} + +const DocumentsPanelInner: React.FC = (props) => { + const { headerLeading } = props; + const shell = useAppShell(); const { - headerLeading = null, - onBreadcrumbNavigate, - currentFolderName, - breadcrumbs, - subfolders, - documents, - searchResultIds, - onRefresh = () => { }, - sortField, - sortDirection, - onSortFieldChange, - onSortDirectionToggle, - onDeleteSelection, - documentLookup, - tags, - correspondents, - onBulkTagAdd, - onBulkTagRemove, - onBulkCorrespondentAdd, - onBulkCorrespondentRemove, - onBulkReanalyze, - folderOptions, - onMoveDocumentsToFolder, - viewMode = 'list', - onViewModeChange, - } = props; + search: { documents, searchResultIds, documentsViewMode: viewMode, documentsSortField: sortField, documentsSortDirection: sortDirection, handleDocumentsViewModeChange: onViewModeChange, handleDocumentsSortFieldChange: onSortFieldChange, handleDocumentsSortDirectionToggle: onSortDirectionToggle }, + folderTree: { currentFolderName, breadcrumbs, currentSubfolders: subfolders, folderOptions, moveDocumentsToFolder: onMoveDocumentsToFolder, refreshCurrentFolder: onRefresh, handleBreadcrumbNavigate: onBreadcrumbNavigate }, + selection: { handleDeleteSelection: onDeleteSelection }, + managers: { documentLookup }, + tags: { tags, handleBulkTagAddFromDetail: onBulkTagAdd, handleBulkTagRemoveFromDetail: onBulkTagRemove }, + correspondents: { correspondents, handleBulkCorrespondentAdd: onBulkCorrespondentAdd, handleBulkCorrespondentRemove: onBulkCorrespondentRemove }, + mutations: { handleBulkSelectionReanalyze: onBulkReanalyze }, + } = shell as any; const { assetContextValue, @@ -95,7 +70,7 @@ const DocumentsPanelInner: React.FC = (props) => { tagHandlers, scrollRef, hasDocumentEntries, - } = useDocumentsContextValues(props); + } = useDocumentsContextValues(); const { clearSelection, @@ -119,7 +94,6 @@ const DocumentsPanelInner: React.FC = (props) => { const showingSearchResults = Array.isArray(searchResultIds); const rows = showingSearchResults && searchDocuments ? searchDocuments : documents; - const headerTitle = showingSearchResults ? 'Search results' : currentFolderName || 'Documents'; @@ -150,13 +124,16 @@ const DocumentsPanelInner: React.FC = (props) => { toggleIncludeDescendants, ], ); + + const { tagLookupById, correspondentLookupById } = (shell as any).tags; + const floatingActions = useMemo(() => ( = (props) => { ), [ documentLookup, tags, - props.tagLookupById, + tagLookupById, correspondents, - props.correspondentLookupById, + correspondentLookupById, onBulkTagAdd, onBulkTagRemove, onBulkCorrespondentAdd, @@ -183,6 +160,7 @@ const DocumentsPanelInner: React.FC = (props) => { onMoveDocumentsToFolder, clearSelection, ]); + const headerConfig: DocumentsPanelHeaderConfig = useMemo(() => ({ title: headerTitle, subtitle: null, @@ -230,14 +208,14 @@ const DocumentsPanelInner: React.FC = (props) => { const entries = useMemo(() => { const list: DocumentsListEntry[] = []; // Explicit type if (!showingSearchResults) { - subfolders.forEach((folder: any) => { + (subfolders || []).forEach((folder: any) => { if (!folder || !folder.id) { return; } list.push({ type: EntryType.folder, id: folder.id, key: `folder:${folder.id}`, folder }); }); } - rows.forEach((doc: any) => { + (rows || []).forEach((doc: any) => { if (!doc || !doc.id) { return; } @@ -261,7 +239,7 @@ const DocumentsPanelInner: React.FC = (props) => { desk: DEFAULT_DESKTOP_CARD_SIZE, }); - const isSearchLoading = props.isSearchLoading || false; + const isSearchLoading = (shell as any).search?.isSearchLoading || false; const renderBody = () => { const hasEntries = entries.length > 0; @@ -328,10 +306,15 @@ const DocumentsPanelInner: React.FC = (props) => { ); }; -const DocumentsPanel: React.FC = ({ selectionValue, ...rest }) => ( - - - -); +const DocumentsPanel: React.FC = (props) => { + const shell = useAppShell(); + const selectionValue = (shell as any).selection?.selectionValue; + + return ( + + + + ); +}; export default DocumentsPanel; diff --git a/frontend/src/documents/panel/DocumentsPanelHeader.tsx b/frontend/src/documents/panel/DocumentsPanelHeader.tsx index a56def3..d5f2075 100644 --- a/frontend/src/documents/panel/DocumentsPanelHeader.tsx +++ b/frontend/src/documents/panel/DocumentsPanelHeader.tsx @@ -4,7 +4,7 @@ import PanelHeader from '../../components/PanelHeader'; import BreadcrumbTrail from '../../components/BreadcrumbTrail'; import type { Identifier } from '../../types/identifiers'; -export interface DocumentsHeaderBreadcrumb { +interface DocumentsHeaderBreadcrumb { id?: Identifier; name?: string; label?: string; diff --git a/frontend/src/documents/panel/useDocumentsContextValues.ts b/frontend/src/documents/panel/useDocumentsContextValues.ts index 1822a17..5854cc0 100644 --- a/frontend/src/documents/panel/useDocumentsContextValues.ts +++ b/frontend/src/documents/panel/useDocumentsContextValues.ts @@ -1,34 +1,28 @@ import { useMemo, useCallback, useRef, useEffect } from 'react'; -import type { DocumentsPanelInnerProps } from './DocumentsPanel'; import { isPointerModifierEvent, isPrimaryPointerEvent } from '../features/selection/useEntryPointer'; import { useDocumentsFilter } from '../context/DocumentsFilterContext'; import { useWorkspaceSelectionContext } from '../../app/WorkspaceSelectionContext'; -import type { Identifier } from '../../types/identifiers'; import { useStatusToast } from '../../lib/context/StatusToastContext'; import { useTagInteractions } from '../interactions/useTagInteractions'; import { subscribeToToast } from '../features/tagging/tagTransfer'; +import { useAppShell } from '../../lib/context/AppShellContext'; const EntryType = { folder: 'folder', document: 'document', }; -export const useDocumentsContextValues = (props: DocumentsPanelInnerProps) => { +export const useDocumentsContextValues = () => { + const shell = useAppShell(); const { - ensureAssetUrl, - getDocumentAsset, - isSearchLoading, - searchQuery = '', - activeTagFilters = [], - activeCorrespondentFilters = [], - selectedFolder = null, - onDocumentDragStart, - onDocumentDragEnd, - onEntryPointer, - activeCorrespondentIds = [], - draggingDocumentIds = [], - tagLookupById, - } = props; + preview: { ensureAssetUrl, getDocumentAsset }, + search: { isSearchLoading, searchQuery, activeTagFilters, activeCorrespondentFilters, searchResultIds, documents }, + folderTree: { selectedFolder }, + mutations: { handleDocumentDragStart, handleDocumentDragEnd, draggedDocumentIds: draggingDocumentIds, handleDocumentTagAttach, handleDocumentTagDetach }, + selection: { handleEntryPointerCore: onEntryPointer }, + correspondents: { activeCorrespondentIds, correspondentLookupById }, + tags: { tagLookupById }, + } = shell as any; const { setFocusedEntryKey, @@ -53,8 +47,8 @@ export const useDocumentsContextValues = (props: DocumentsPanelInnerProps) => { // Handlers const tagHandlers = useTagInteractions({ - onAssignTagToDocument: props.onDocumentTagAttach, - onRemoveTagFromDocument: props.onDocumentTagDetach, + onAssignTagToDocument: handleDocumentTagAttach, + onRemoveTagFromDocument: handleDocumentTagDetach, onTagClick: toggleTagFilter, }); @@ -67,14 +61,14 @@ export const useDocumentsContextValues = (props: DocumentsPanelInnerProps) => { () => new Set(activeCorrespondentIds || []), [activeCorrespondentIds], ); - const showingSearchResults = Array.isArray(props.searchResultIds); - const hasDocumentEntries = (props.documents || []).length > 0 || (showingSearchResults && (props.searchResultIds || []).length > 0); + const showingSearchResults = Array.isArray(searchResultIds); + const hasDocumentEntries = (documents || []).length > 0 || (showingSearchResults && (searchResultIds || []).length > 0); const viewId = useMemo(() => { if (showingSearchResults) { - const trimmedQuery = searchQuery.trim(); - const tagsKey = [...activeTagFilters].sort().join(','); - const correspondentsKey = [...activeCorrespondentFilters].sort().join(','); + const trimmedQuery = (searchQuery || '').trim(); + const tagsKey = [...(activeTagFilters || [])].sort().join(','); + const correspondentsKey = [...(activeCorrespondentFilters || [])].sort().join(','); return `search:${trimmedQuery}|tags:${tagsKey}|corr:${correspondentsKey}`; } const folderKey = selectedFolder && selectedFolder !== '' ? selectedFolder : 'root'; @@ -115,19 +109,19 @@ export const useDocumentsContextValues = (props: DocumentsPanelInnerProps) => { const handleDocumentDragStartLocal = useCallback( (event: any, doc: any) => { suppressDocumentClickRef.current = true; - onDocumentDragStart?.(event, doc); + handleDocumentDragStart?.(event, doc); }, - [onDocumentDragStart], + [handleDocumentDragStart], ); const handleDocumentDragEndLocal = useCallback( (event: any) => { - onDocumentDragEnd?.(event); + handleDocumentDragEnd?.(event); requestAnimationFrame(() => { suppressDocumentClickRef.current = false; }); }, - [onDocumentDragEnd], + [handleDocumentDragEnd], ); // Context Values Construction @@ -136,71 +130,61 @@ export const useDocumentsContextValues = (props: DocumentsPanelInnerProps) => { getDocumentAsset, }), [ensureAssetUrl, getDocumentAsset]); + const draggedFolderId = (shell as any).folderTree?.draggedFolderId; + const viewStateContextValue = useMemo(() => ({ viewId, scrollRef, tagLookupById, - correspondentLookupById: props.correspondentLookupById, + correspondentLookupById, activeCorrespondentIdSet, draggingDocumentIdsSet, - draggedFolderId: props.draggedFolderId, + draggedFolderId, }), [ viewId, scrollRef, tagLookupById, activeCorrespondentIdSet, draggingDocumentIdsSet, - props.draggedFolderId, - props.correspondentLookupById, + draggedFolderId, + correspondentLookupById, ]); - // Use refs to stabilize handlers and avoid massive dependency arrays - const latestPropsRef = useRef(props); - const latestHandlersRef = useRef({ + const commandContextValue = useMemo(() => { + const anyShell = shell as any; + return { + folder: { + onClick: handleFolderClick, + onSelect: anyShell.folderTree?.selectFolder, + onRename: anyShell.folderTree?.handleFolderRename, + onDrag: { + start: anyShell.folderTree?.handleFolderDragStart, + end: anyShell.folderTree?.handleFolderDragEnd, + over: anyShell.folderTree?.folderClickHandlers?.onDragOver, + leave: anyShell.folderTree?.folderClickHandlers?.onDragLeave, + drop: anyShell.folderTree?.folderClickHandlers?.onDrop, + }, + }, + document: { + onRename: anyShell.mutations?.handleDocumentTitleUpdate, + onDrag: { + start: handleDocumentDragStartLocal, + end: handleDocumentDragEndLocal, + }, + }, + correspondents: { + onClick: toggleCorrespondentFilter, + }, + onEntryPointer, + } + }, [ handleFolderClick, + shell, handleDocumentDragStartLocal, handleDocumentDragEndLocal, - tagHandlers, - toggleTagFilter, toggleCorrespondentFilter, - }); - - // Update refs on every render - latestPropsRef.current = props; - latestHandlersRef.current = { - handleFolderClick, - handleDocumentDragStartLocal, - handleDocumentDragEndLocal, - tagHandlers, - toggleTagFilter, - toggleCorrespondentFilter, - }; - - const commandContextValue = useMemo(() => ({ - folder: { - onClick: (f: any, e: any) => latestHandlersRef.current.handleFolderClick(f, e), - onSelect: (id: Identifier) => latestPropsRef.current.onFolderSelect?.(id), - onRename: (id: Identifier, name: string) => latestPropsRef.current.onFolderRename?.(id, name), - onDrag: { - start: (e: any, f: any) => latestPropsRef.current.onFolderDragStart?.(e, f), - end: (e: any) => latestPropsRef.current.onFolderDragEnd?.(e), - over: (e: any, f: any) => latestPropsRef.current.onFolderDragOver?.(e, f), - leave: (e: any) => latestPropsRef.current.onFolderDragLeave?.(e), - drop: (e: any, f: any) => latestPropsRef.current.onFolderDrop?.(e, f), - }, - }, - document: { - onRename: (id: Identifier, name: string) => latestPropsRef.current.onDocumentRename?.(id, name), - onDrag: { - start: (e: any, d: any) => latestHandlersRef.current.handleDocumentDragStartLocal(e, d), - end: (e: any) => latestHandlersRef.current.handleDocumentDragEndLocal(e), - }, - }, - correspondents: { - onClick: (c: any) => latestHandlersRef.current.toggleCorrespondentFilter(c), - }, - onEntryPointer: (entry: any, e: any) => latestPropsRef.current.onEntryPointer?.(entry, e), - }), []); + onEntryPointer, + ]); return { assetContextValue,