feat: Refactor DocumentsPanel to use a shared application shell context for state management

This commit is contained in:
2025-12-12 02:57:10 +01:00
parent 34e6c3037f
commit 6ba8084caa
7 changed files with 109 additions and 338 deletions
+46 -63
View File
@@ -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<Identifier, Correspondent>;
}
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<DocumentsPanelInnerProps> = (props) => {
interface DocumentsPanelProps {
headerLeading?: ReactNode;
}
const DocumentsPanelInner: React.FC<DocumentsPanelProps> = (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<DocumentsPanelInnerProps> = (props) => {
tagHandlers,
scrollRef,
hasDocumentEntries,
} = useDocumentsContextValues(props);
} = useDocumentsContextValues();
const {
clearSelection,
@@ -119,7 +94,6 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = (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<DocumentsPanelInnerProps> = (props) => {
toggleIncludeDescendants,
],
);
const { tagLookupById, correspondentLookupById } = (shell as any).tags;
const floatingActions = useMemo(() => (
<SelectionFloatingPanel
documentLookup={documentLookup}
tags={tags}
tagLookupById={props.tagLookupById}
tagLookupById={tagLookupById}
correspondents={correspondents}
correspondentLookupById={props.correspondentLookupById}
correspondentLookupById={correspondentLookupById}
onBulkTagAdd={onBulkTagAdd}
onBulkTagRemove={onBulkTagRemove}
onBulkCorrespondentAdd={onBulkCorrespondentAdd}
@@ -170,9 +147,9 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = (props) => {
), [
documentLookup,
tags,
props.tagLookupById,
tagLookupById,
correspondents,
props.correspondentLookupById,
correspondentLookupById,
onBulkTagAdd,
onBulkTagRemove,
onBulkCorrespondentAdd,
@@ -183,6 +160,7 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = (props) => {
onMoveDocumentsToFolder,
clearSelection,
]);
const headerConfig: DocumentsPanelHeaderConfig = useMemo(() => ({
title: headerTitle,
subtitle: null,
@@ -230,14 +208,14 @@ const DocumentsPanelInner: React.FC<DocumentsPanelInnerProps> = (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<DocumentsPanelInnerProps> = (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<DocumentsPanelInnerProps> = (props) => {
);
};
const DocumentsPanel: React.FC<DocumentsPanelProps> = ({ selectionValue, ...rest }) => (
<WorkspaceSelectionProvider value={selectionValue}>
<DocumentsPanelInner {...rest} />
</WorkspaceSelectionProvider>
);
const DocumentsPanel: React.FC<DocumentsPanelProps> = (props) => {
const shell = useAppShell();
const selectionValue = (shell as any).selection?.selectionValue;
return (
<WorkspaceSelectionProvider value={selectionValue}>
<DocumentsPanelInner {...props} />
</WorkspaceSelectionProvider>
);
};
export default DocumentsPanel;