From 22f0c040a235c75008dd810dd1d7bd693579d20c Mon Sep 17 00:00:00 2001 From: Nils Schneider Date: Mon, 24 Nov 2025 23:13:11 +0100 Subject: [PATCH] refactor: Standardize document and folder identification with a new `entryKey` module. --- frontend/src/app/entryKey.ts | 24 +++++ frontend/src/app/useDocumentSelection.ts | 93 +++++++++---------- frontend/src/app/useWorkspaceSelection.ts | 47 ++++------ frontend/src/app/workspaceUtils.ts | 23 ----- frontend/src/detail/useDetailWorkspace.ts | 11 +-- .../documents/hooks/useDocumentsSelection.ts | 33 +++---- frontend/src/documents/useEntryPointer.ts | 9 +- .../documents/useDocumentDragHandlers.ts | 13 +-- .../hooks/documents/useDocumentMutations.ts | 18 ++-- .../hooks/documents/useDocumentsWorkspace.ts | 29 +----- frontend/src/hooks/documents/useFolderTree.ts | 27 +++--- .../hooks/documents/useWorkspaceDeskProps.ts | 21 +++-- 12 files changed, 146 insertions(+), 202 deletions(-) create mode 100644 frontend/src/app/entryKey.ts diff --git a/frontend/src/app/entryKey.ts b/frontend/src/app/entryKey.ts new file mode 100644 index 0000000..55bfbf6 --- /dev/null +++ b/frontend/src/app/entryKey.ts @@ -0,0 +1,24 @@ +// Entry key utilities for workspace selection +// Entry keys are strings in the format "document:id" or "folder:id" + +const ENTRY_KEY_SEPARATOR = ':'; + +// Create entry key strings +export const createDocumentEntryKey = (documentId: string | number): string => + `document${ENTRY_KEY_SEPARATOR}${documentId}`; + +export const createFolderEntryKey = (folderId: string | number): string => + `folder${ENTRY_KEY_SEPARATOR}${folderId}`; + +// Type guards for entry key strings +export const isDocumentEntry = (key: string): boolean => + key.split(ENTRY_KEY_SEPARATOR, 1)[0] === 'document'; + +export const isFolderEntry = (key: string): boolean => + key.split(ENTRY_KEY_SEPARATOR, 1)[0] === 'folder'; + +// Extract ID from entry key string +export const getEntryId = (key: string): string => { + const parts = key.split(ENTRY_KEY_SEPARATOR); + return parts.slice(1).join(ENTRY_KEY_SEPARATOR); +}; diff --git a/frontend/src/app/useDocumentSelection.ts b/frontend/src/app/useDocumentSelection.ts index 36a4077..11f9285 100644 --- a/frontend/src/app/useDocumentSelection.ts +++ b/frontend/src/app/useDocumentSelection.ts @@ -1,6 +1,12 @@ import { useCallback, useRef, useState } from 'react'; +import { + createDocumentEntryKey, + createFolderEntryKey, + isDocumentEntry, + isFolderEntry, + getEntryId, +} from './entryKey'; -type RowKey = string; type DocumentId = string | number; interface SelectionEventLike { @@ -11,46 +17,36 @@ interface SelectionEventLike { } interface UseDocumentSelectionOptions { - resolveDocumentRowKey: (id: DocumentId | null) => RowKey | null; - resolveFolderRowKey: (id: DocumentId | null) => RowKey | null; - isDocumentRowKey: (key?: RowKey | null) => boolean; - isFolderRowKey: (key?: RowKey | null) => boolean; - getRowId: (key?: RowKey | null) => DocumentId | null; - initialEntries?: RowKey[]; + initialEntries?: string[]; } interface ApplySelectionOptions { - anchor?: RowKey | null; - interactedKeys?: RowKey[]; + anchor?: string; + interactedKeys?: string[]; } -const DEFAULT_INITIAL_ENTRIES: RowKey[] = []; +const DEFAULT_INITIAL_ENTRIES: string[] = []; export const useDocumentSelection = ({ - resolveDocumentRowKey, - resolveFolderRowKey, - isDocumentRowKey, - isFolderRowKey, - getRowId, initialEntries = DEFAULT_INITIAL_ENTRIES, -}: UseDocumentSelectionOptions) => { - const [selectedEntries, setSelectedEntries] = useState(initialEntries); - const [selectionOrder, setSelectionOrder] = useState(initialEntries); - const selectionOrderRef = useRef(initialEntries); - const selectionAnchorRef = useRef(null); +}: UseDocumentSelectionOptions = {}) => { + const [selectedEntries, setSelectedEntries] = useState(initialEntries); + const [selectionOrder, setSelectionOrder] = useState(initialEntries); + const selectionOrderRef = useRef(initialEntries); + const selectionAnchorRef = useRef(null); const selectionInitializedRef = useRef(false); - const [focusedDocumentId, setFocusedDocumentId] = useState(null); - const [focusedRowKey, setFocusedRowKey] = useState(null); + const [focusedDocumentId, setFocusedDocumentId] = useState(undefined); + const [focusedRowKey, setFocusedRowKey] = useState(undefined); - const visibleRowKeySetRef = useRef>(new Set()); - const navigableRowKeysRef = useRef([]); + const visibleRowKeySetRef = useRef>(new Set()); + const navigableRowKeysRef = useRef([]); const configureSelectionEnvironment = useCallback(({ visibleRowKeySet, navigableRowKeys, }: { - visibleRowKeySet?: Set; - navigableRowKeys?: RowKey[]; + visibleRowKeySet?: Set; + navigableRowKeys?: string[]; }) => { if (visibleRowKeySet) { visibleRowKeySetRef.current = visibleRowKeySet; @@ -60,7 +56,7 @@ export const useDocumentSelection = ({ } }, []); - const updateSelectionOrder = useCallback((nextSelection: RowKey[], interactedKeys: RowKey[] = []) => { + const updateSelectionOrder = useCallback((nextSelection: string[], interactedKeys: string[] = []) => { const nextSet = new Set(nextSelection); const previousOrder = selectionOrderRef.current.filter((id) => nextSet.has(id)); const interacted = (interactedKeys || []).filter((id, index, array) => array.indexOf(id) === index); @@ -93,23 +89,23 @@ export const useDocumentSelection = ({ const applySelection = useCallback( ( - rowKeys: Array, + rowKeys: Array, { anchor, interactedKeys = [] }: ApplySelectionOptions = {}, ) => { const visibleRowKeySet = visibleRowKeySetRef.current; - const unique: RowKey[] = []; + const unique: string[] = []; (rowKeys || []).forEach((key) => { if (!key) return; - let canonicalKey: RowKey | null = null; + let canonicalKey: string | null = null; if (visibleRowKeySet.has(key)) { canonicalKey = key; - } else if (isDocumentRowKey(key)) { - const id = getRowId(key); - canonicalKey = id ? resolveDocumentRowKey(id) : null; - } else if (isFolderRowKey(key)) { - const id = getRowId(key); - canonicalKey = id ? resolveFolderRowKey(id) : null; + } else if (isDocumentEntry(key)) { + const id = getEntryId(key); + canonicalKey = id ? createDocumentEntryKey(id) : null; + } else if (isFolderEntry(key)) { + const id = getEntryId(key); + canonicalKey = id ? createFolderEntryKey(id) : null; } if (!canonicalKey || !visibleRowKeySet.has(canonicalKey)) { @@ -131,18 +127,18 @@ export const useDocumentSelection = ({ const nextFocusedDocumentId: DocumentId | null = (() => { if (focusedDocumentId) { - const focusKey = resolveDocumentRowKey(focusedDocumentId); + const focusKey = createDocumentEntryKey(focusedDocumentId); if (focusKey && unique.includes(focusKey)) { return focusedDocumentId; } } - if (resolvedAnchor && isDocumentRowKey(resolvedAnchor)) { - return getRowId(resolvedAnchor) ?? null; + if (resolvedAnchor && isDocumentEntry(resolvedAnchor)) { + return getEntryId(resolvedAnchor) ?? null; } - const lastDocKey = [...unique].reverse().find((key) => isDocumentRowKey(key)) ?? null; - return lastDocKey ? getRowId(lastDocKey) ?? null : null; + const lastDocKey = [...unique].reverse().find((key) => isDocumentEntry(key)) ?? null; + return lastDocKey ? getEntryId(lastDocKey) ?? null : null; })(); setFocusedDocumentId(nextFocusedDocumentId); @@ -159,11 +155,6 @@ export const useDocumentSelection = ({ }, [ focusedDocumentId, - getRowId, - isDocumentRowKey, - isFolderRowKey, - resolveDocumentRowKey, - resolveFolderRowKey, updateSelectionOrder, ], ); @@ -174,7 +165,7 @@ export const useDocumentSelection = ({ }, [applySelection]); const handleEntrySelection = useCallback( - (rowKey: RowKey | null, event?: SelectionEventLike) => { + (rowKey: string, event?: SelectionEventLike) => { const visibleRowKeySet = visibleRowKeySetRef.current; const navigableRowKeys = navigableRowKeysRef.current; if (!rowKey || !visibleRowKeySet.has(rowKey)) { @@ -200,8 +191,8 @@ export const useDocumentSelection = ({ anchorKey = rowKey; } - let nextKeys: RowKey[] = []; - let interactedKeys: RowKey[] = []; + let nextKeys: string[] = []; + let interactedKeys: string[] = []; if (shiftKey && anchorKey) { const anchorIndex = navigableRowKeys.indexOf(anchorKey); @@ -245,7 +236,7 @@ export const useDocumentSelection = ({ const promoteSelectionOrder = useCallback( (docId?: DocumentId | null) => { if (!docId) return; - const rowKey = resolveDocumentRowKey(docId); + const rowKey = createDocumentEntryKey(docId); if (!rowKey) return; if (!selectedEntries.includes(rowKey)) { @@ -254,7 +245,7 @@ export const useDocumentSelection = ({ updateSelectionOrder(selectedEntries, [rowKey]); }, - [resolveDocumentRowKey, selectedEntries, updateSelectionOrder], + [selectedEntries, updateSelectionOrder], ); return { diff --git a/frontend/src/app/useWorkspaceSelection.ts b/frontend/src/app/useWorkspaceSelection.ts index d80c354..d53a1e3 100644 --- a/frontend/src/app/useWorkspaceSelection.ts +++ b/frontend/src/app/useWorkspaceSelection.ts @@ -1,19 +1,15 @@ import { useCallback, useMemo } from 'react'; import { useDocumentSelection } from './useDocumentSelection'; - -type RowKey = string; +import { isDocumentEntry, isFolderEntry, getEntryId } from './entryKey'; interface SelectionEntry { - rowKey?: RowKey; + entryKey?: string; + // Legacy field for compatibility + rowKey?: string; [key: string]: unknown; } interface WorkspaceSelectionOptions { - resolveDocumentRowKey?: (id: string | number) => RowKey | null; - resolveFolderRowKey?: (id: string | number) => RowKey | null; - isDocumentRowKey?: (key: RowKey | SelectionEntry) => boolean; - isFolderRowKey?: (key: RowKey | SelectionEntry) => boolean; - getRowId?: (key: RowKey | SelectionEntry) => string | number | null; onDocumentActivate?: (id: string | number) => void; onInspectFolder?: (id: string | number) => void; } @@ -21,21 +17,10 @@ interface WorkspaceSelectionOptions { const identity = (value: T) => value; export const useWorkspaceSelection = ({ - resolveDocumentRowKey, - resolveFolderRowKey, - isDocumentRowKey = () => false, - isFolderRowKey = () => false, - getRowId = () => null, onDocumentActivate = identity, onInspectFolder = identity, }: WorkspaceSelectionOptions = {}) => { - const selection = useDocumentSelection({ - resolveDocumentRowKey, - resolveFolderRowKey, - isDocumentRowKey, - isFolderRowKey, - getRowId, - }); + const selection = useDocumentSelection(); const { selectedEntries, @@ -59,26 +44,26 @@ export const useWorkspaceSelection = ({ const selectedDocumentIds = useMemo( () => selectedEntries - .filter((entry) => isDocumentRowKey(entry)) - .map((entry) => getRowId(entry)) + .filter((entry) => isDocumentEntry(entry)) + .map((entry) => getEntryId(entry)) .filter(Boolean), - [selectedEntries, isDocumentRowKey, getRowId], + [selectedEntries], ); const selectedFolderIds = useMemo( () => selectedEntries - .filter((entry) => isFolderRowKey(entry)) - .map((entry) => getRowId(entry)) + .filter((entry) => isFolderEntry(entry)) + .map((entry) => getEntryId(entry)) .filter(Boolean), - [selectedEntries, isFolderRowKey, getRowId], + [selectedEntries], ); const selectEntry = useCallback( - (entry: SelectionEntry | string | null, event?: unknown) => { + (entry: SelectionEntry | string, event?: unknown) => { const rowKey = entry && Object(entry) === entry - ? (entry as SelectionEntry).rowKey ?? null - : (entry as string | null); + ? (entry as SelectionEntry).rowKey ?? undefined + : (entry as string); if (!rowKey) return; handleEntrySelection(rowKey, event); }, @@ -86,7 +71,7 @@ export const useWorkspaceSelection = ({ ); const inspectDocument = useCallback( - (documentId?: string | number | null) => { + (documentId?: string | number) => { if (!documentId) return; onDocumentActivate(documentId); }, @@ -94,7 +79,7 @@ export const useWorkspaceSelection = ({ ); const inspectFolder = useCallback( - (folderId?: string | number | null) => { + (folderId?: string | number) => { if (!folderId) return; onInspectFolder(folderId); }, diff --git a/frontend/src/app/workspaceUtils.ts b/frontend/src/app/workspaceUtils.ts index a81b2e8..b30fedb 100644 --- a/frontend/src/app/workspaceUtils.ts +++ b/frontend/src/app/workspaceUtils.ts @@ -4,29 +4,6 @@ export const DEFAULT_SORT_DIRECTION = 'asc'; export const SORT_FIELD_VALUES = ['title', 'issued_at', 'created_at', 'updated_at']; export const TAG_FILTER_UNTAGGED = '__UNTAGGED__'; -const ROW_KEY_SEPARATOR = ':'; -const DOCUMENT_ROW_PREFIX = 'document'; -const FOLDER_ROW_PREFIX = 'folder'; - -const makeRowKey = (type, id) => - id ? `${type}${ROW_KEY_SEPARATOR}${id}` : `${type}${ROW_KEY_SEPARATOR}`; - -const getRowType = (key: string | null) => (key ?? '').split(ROW_KEY_SEPARATOR, 1)[0] ?? ''; - -export const getRowId = (key: string) => { - const parts = key.split(ROW_KEY_SEPARATOR); - return parts.slice(1).join(ROW_KEY_SEPARATOR); -}; - -export const isDocumentRowKey = (key) => getRowType(key) === DOCUMENT_ROW_PREFIX; -export const isFolderRowKey = (key) => getRowType(key) === FOLDER_ROW_PREFIX; - -export const resolveDocumentRowKey = (documentId) => - documentId ? makeRowKey(DOCUMENT_ROW_PREFIX, documentId) : null; - -export const resolveFolderRowKey = (folderId) => - folderId ? makeRowKey(FOLDER_ROW_PREFIX, folderId) : null; - export const hasFiles = (event) => Array.from(event.dataTransfer?.types || []).includes('Files'); diff --git a/frontend/src/detail/useDetailWorkspace.ts b/frontend/src/detail/useDetailWorkspace.ts index 2c4916b..3693636 100644 --- a/frontend/src/detail/useDetailWorkspace.ts +++ b/frontend/src/detail/useDetailWorkspace.ts @@ -2,11 +2,8 @@ import { useCallback, useEffect, useMemo } from 'react'; import type { MutableRefObject } from 'react'; import { resolveDocumentAssetUrl } from '../asset_manager'; import { useDetailPanel } from '../app/useDetailPanel'; -import { - DEFAULT_FOLDER_NAME, - getRowId, - isDocumentRowKey, -} from '../app/workspaceUtils'; +import { DEFAULT_FOLDER_NAME } from '../app/workspaceUtils'; +import { getEntryId, isDocumentEntry } from '../app/entryKey'; import type { DocumentInfoPanelProps } from '../documents/DocumentInfoPanel'; import type { EnsureAssetUrl, GetDocumentAsset } from '../utils/ocr'; @@ -101,10 +98,10 @@ const useDetailWorkspace = ({ }; selectionOrder.forEach((key) => { - if (!isDocumentRowKey(key)) { + if (!isDocumentEntry(key)) { return; } - const docId = getRowId(key); + const docId = getEntryId(key); const doc = documentLookup.get(docId) || null; pushDoc(doc); }); diff --git a/frontend/src/documents/hooks/useDocumentsSelection.ts b/frontend/src/documents/hooks/useDocumentsSelection.ts index be316cf..46c874d 100644 --- a/frontend/src/documents/hooks/useDocumentsSelection.ts +++ b/frontend/src/documents/hooks/useDocumentsSelection.ts @@ -1,4 +1,5 @@ import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { createDocumentEntryKey, createFolderEntryKey, isFolderEntry } from '../../app/entryKey'; interface FolderEntry { id: string | number; @@ -17,11 +18,9 @@ interface NavigableRow { } interface UseDocumentsSelectionOptions { - showingSearchResults: boolean; - currentSubfolders: FolderEntry[]; - visibleDocuments: DocumentEntry[]; - resolveFolderRowKey: (id: string | number) => string | null; - resolveDocumentRowKey: (id: string | number) => string | null; + showingSearchResults?: boolean; + currentSubfolders?: FolderEntry[]; + visibleDocuments?: DocumentEntry[]; configureSelectionEnvironment: (config: { visibleRowKeySet: Set; navigableRowKeys: string[] }) => void; visibleRowKeySet: Set; selectedEntries: string[]; @@ -33,15 +32,12 @@ interface UseDocumentsSelectionOptions { focusedDocumentId: string | number | null; setFocusedRowKey: (value: string | null | ((current: string | null) => string | null)) => void; focusedRowKey: string | null; - isFolderRowKey: (key: string | null) => boolean; } const useDocumentsSelection = ({ showingSearchResults, currentSubfolders, visibleDocuments, - resolveFolderRowKey, - resolveDocumentRowKey, configureSelectionEnvironment, visibleRowKeySet, selectedEntries, @@ -53,26 +49,25 @@ const useDocumentsSelection = ({ focusedDocumentId, setFocusedRowKey, focusedRowKey, - isFolderRowKey, }: UseDocumentsSelectionOptions) => { const navigableRows = useMemo(() => { const entries: NavigableRow[] = []; if (!showingSearchResults) { currentSubfolders.forEach((folder) => { - const key = resolveFolderRowKey(folder.id); + const key = createFolderEntryKey(folder.id); if (key) { entries.push({ key, type: 'folder', id: folder.id }); } }); } visibleDocuments.forEach((doc) => { - const key = resolveDocumentRowKey(doc.id); + const key = createDocumentEntryKey(doc.id); if (key) { entries.push({ key, type: 'document', id: doc.id }); } }); return entries; - }, [showingSearchResults, currentSubfolders, visibleDocuments, resolveFolderRowKey, resolveDocumentRowKey]); + }, [showingSearchResults, currentSubfolders, visibleDocuments]); const navigableRowKeys = useMemo( () => navigableRows.map((entry) => entry.key), @@ -90,14 +85,14 @@ const useDocumentsSelection = ({ (docId: string | number | null) => { if (!docId) return; promoteSelectionOrderRaw(docId); - const rowKey = resolveDocumentRowKey(docId); + const rowKey = createDocumentEntryKey(docId); if (rowKey) { selectionAnchorRef.current = rowKey; } setFocusedDocumentId(docId); setActivePreviewId(docId); }, - [promoteSelectionOrderRaw, resolveDocumentRowKey, selectionAnchorRef, setFocusedDocumentId, setActivePreviewId], + [promoteSelectionOrderRaw, selectionAnchorRef, setFocusedDocumentId, setActivePreviewId], ); const clearDocumentSelection = useCallback(() => { @@ -112,11 +107,11 @@ const useDocumentsSelection = ({ } prevFocusedDocIdRef.current = focusedDocumentId; if (focusedDocumentId) { - setFocusedRowKey(resolveDocumentRowKey(focusedDocumentId)); + setFocusedRowKey(createDocumentEntryKey(focusedDocumentId)); } else { - setFocusedRowKey((current) => (isFolderRowKey(current) ? current : null)); + setFocusedRowKey((current) => (isFolderEntry(current) ? current : null)); } - }, [focusedDocumentId, resolveDocumentRowKey, setFocusedRowKey, isFolderRowKey]); + }, [focusedDocumentId, setFocusedRowKey]); useEffect(() => { if (!navigableRowKeys.length) { @@ -130,7 +125,7 @@ const useDocumentsSelection = ({ return; } - const docKey = focusedDocumentId ? resolveDocumentRowKey(focusedDocumentId) : null; + const docKey = focusedDocumentId ? createDocumentEntryKey(focusedDocumentId) : null; if (docKey && navigableRowKeys.includes(docKey)) { setFocusedRowKey(docKey); return; @@ -145,7 +140,7 @@ const useDocumentsSelection = ({ if (focusedRowKey) { setFocusedRowKey(null); } - }, [focusedRowKey, focusedDocumentId, navigableRowKeys, selectedEntries, setFocusedRowKey, resolveDocumentRowKey]); + }, [focusedRowKey, focusedDocumentId, navigableRowKeys, selectedEntries, setFocusedRowKey]); return { navigableRows, diff --git a/frontend/src/documents/useEntryPointer.ts b/frontend/src/documents/useEntryPointer.ts index b79bccc..35f3cdf 100644 --- a/frontend/src/documents/useEntryPointer.ts +++ b/frontend/src/documents/useEntryPointer.ts @@ -1,4 +1,5 @@ import { useCallback } from 'react'; +import { createDocumentEntryKey, createFolderEntryKey } from '../app/entryKey'; export type PointerEventLike = MouseEvent | PointerEvent; @@ -26,8 +27,6 @@ export interface WorkspaceEntry { } interface UseEntryPointerOptions { - resolveDocumentRowKey?: (id: string | number) => string | null; - resolveFolderRowKey?: (id: string | number) => string | null; onSelectEntry?: (entry: WorkspaceEntry, event?: PointerEventLike | null, metadata?: EntryPointerMetadata) => void; onDocumentActivate?: (id: string | number, metadata?: EntryPointerMetadata) => void; } @@ -41,8 +40,6 @@ export interface EntryPointerMetadata { } export const useEntryPointer = ({ - resolveDocumentRowKey, - resolveFolderRowKey, onSelectEntry, onDocumentActivate, }: UseEntryPointerOptions) => @@ -58,7 +55,7 @@ export const useEntryPointer = ({ } const rowKey = entry.key - || (type === 'document' ? resolveDocumentRowKey?.(id) : resolveFolderRowKey?.(id)); + || (type === 'document' ? createDocumentEntryKey(id) : createFolderEntryKey(id)); if (!rowKey) { return; } @@ -73,7 +70,7 @@ export const useEntryPointer = ({ onDocumentActivate?.(id, metadata); } }, - [resolveDocumentRowKey, resolveFolderRowKey, onSelectEntry, onDocumentActivate], + [onSelectEntry, onDocumentActivate], ); export default useEntryPointer; diff --git a/frontend/src/hooks/documents/useDocumentDragHandlers.ts b/frontend/src/hooks/documents/useDocumentDragHandlers.ts index 1ff8d28..d598466 100644 --- a/frontend/src/hooks/documents/useDocumentDragHandlers.ts +++ b/frontend/src/hooks/documents/useDocumentDragHandlers.ts @@ -1,5 +1,6 @@ import { useCallback, useEffect, useMemo, useRef } from 'react'; import type { DragEvent } from 'react'; +import { createDocumentEntryKey, createFolderEntryKey } from '../../app/entryKey'; type Identifier = string | number; type FolderIdentifier = string | 'root'; @@ -30,8 +31,6 @@ interface UseDocumentDragHandlersOptions { documentLookup: Map; setDraggedDocumentIds: (ids: Identifier[] | []) => void; setDraggedFolderId: (id: FolderIdentifier | null) => void; - resolveDocumentRowKey: (id: Identifier) => string | null; - resolveFolderRowKey: (id: FolderIdentifier) => string | null; documentsViewMode: string; } @@ -44,8 +43,6 @@ const useDocumentDragHandlers = ({ documentLookup, setDraggedDocumentIds, setDraggedFolderId, - resolveDocumentRowKey, - resolveFolderRowKey, documentsViewMode, }: UseDocumentDragHandlersOptions) => { const dragPreviewRef = useRef(null); @@ -213,7 +210,7 @@ const useDocumentDragHandlers = ({ return; } - const documentKey = resolveDocumentRowKey(documentId); + const documentKey = createDocumentEntryKey(documentId); if (!documentKey) { return; } @@ -279,7 +276,6 @@ const useDocumentDragHandlers = ({ setDraggedFolderId, setDraggedDocumentIds, documentsViewMode, - resolveDocumentRowKey, ], ); @@ -300,7 +296,7 @@ const useDocumentDragHandlers = ({ return; } event.stopPropagation(); - const folderKey = resolveFolderRowKey(normalizedFolderId); + const folderKey = createFolderEntryKey(normalizedFolderId); const isAlreadySelected = folderKey ? selectedEntries.includes(folderKey) : false; let effectiveFolderSelection: FolderIdentifier[] = normalizedFolderIds; @@ -309,7 +305,7 @@ const useDocumentDragHandlers = ({ if (!isAlreadySelected && folderKey) { effectiveFolderSelection = [normalizedFolderId]; effectiveDocumentSelection = []; - handleEntrySelection(folderKey, { preventDefault: () => {} }); + handleEntrySelection(folderKey, { preventDefault: () => { } }); } const uniqueFolders = effectiveFolderSelection.length @@ -363,7 +359,6 @@ const useDocumentDragHandlers = ({ setDraggedDocumentIds, documentLookup, createDragPreview, - resolveFolderRowKey, ], ); diff --git a/frontend/src/hooks/documents/useDocumentMutations.ts b/frontend/src/hooks/documents/useDocumentMutations.ts index 6e1649a..2f333a2 100644 --- a/frontend/src/hooks/documents/useDocumentMutations.ts +++ b/frontend/src/hooks/documents/useDocumentMutations.ts @@ -1,7 +1,7 @@ import { useCallback } from 'react'; - import type { Dispatch, MutableRefObject, SetStateAction } from 'react'; -import { DEFAULT_FOLDER_NAME, getRowId, isDocumentRowKey } from '../../app/workspaceUtils'; +import { DEFAULT_FOLDER_NAME } from '../../app/workspaceUtils'; +import { getEntryId, isDocumentEntry } from '../../app/entryKey'; import { addDocumentTags, createTag, @@ -275,10 +275,10 @@ const useDocumentMutations = ({ const pruneRow = (collection: string[]): string[] => collection.filter((key) => { - if (!isDocumentRowKey(key)) { + if (!isDocumentEntry(key)) { return true; } - const id = getRowId(key); + const id = getEntryId(key); return id ? !uniqueIdSet.has(id as DocumentId) : true; }); try { @@ -348,8 +348,8 @@ const useDocumentMutations = ({ selectionOrderRef.current = nextSelectionOrder; if ( selectionAnchorRef.current && - isDocumentRowKey(selectionAnchorRef.current) && - uniqueIdSet.has(getRowId(selectionAnchorRef.current) as DocumentId) + isDocumentEntry(selectionAnchorRef.current) && + uniqueIdSet.has(getEntryId(selectionAnchorRef.current) as DocumentId) ) { selectionAnchorRef.current = null; } @@ -358,8 +358,8 @@ const useDocumentMutations = ({ } if ( focusedRowKey && - isDocumentRowKey(focusedRowKey) && - uniqueIdSet.has(getRowId(focusedRowKey) as DocumentId) + isDocumentEntry(focusedRowKey) && + uniqueIdSet.has(getEntryId(focusedRowKey) as DocumentId) ) { setFocusedRowKey(null); } @@ -617,7 +617,7 @@ const useDocumentMutations = ({ if (!source || source.id == null) { return null; } - const labelText = `${source.label ?? ''}`.trim(); + const labelText = `${source.label ?? ''} `.trim(); if (!labelText) { return null; } diff --git a/frontend/src/hooks/documents/useDocumentsWorkspace.ts b/frontend/src/hooks/documents/useDocumentsWorkspace.ts index 77a0071..37fb461 100644 --- a/frontend/src/hooks/documents/useDocumentsWorkspace.ts +++ b/frontend/src/hooks/documents/useDocumentsWorkspace.ts @@ -29,17 +29,12 @@ import useDocumentsPanelProps from '../../documents/hooks/useDocumentsPanelProps import useDocumentPreview from '../../app/useDocumentPreview'; import useSidebarProps from '../../sidebar/useSidebarProps'; import { - DEFAULT_SORT_DIRECTION, DEFAULT_SORT_FIELD, createRootNode, - getRowId, - isDocumentRowKey, - isFolderRowKey, mergeAssetIntoDocument, - resolveDocumentRowKey, - resolveFolderRowKey, } from '../../app/workspaceUtils'; +import { createDocumentEntryKey, createFolderEntryKey } from '../../app/entryKey'; import useDocumentsSearch from '../../app/useDocumentsSearch'; import useDocumentsStore from './store/useDocumentsStore'; import useAuthManager from './useAuthManager'; @@ -252,13 +247,7 @@ const useDocumentsWorkspace = ({ } const tagManager = tagManagerRef.current; - const selection = useWorkspaceSelection({ - resolveDocumentRowKey, - resolveFolderRowKey, - isDocumentRowKey, - isFolderRowKey, - getRowId, - }); + const selection = useWorkspaceSelection(); const { selectedEntries, @@ -415,7 +404,7 @@ const useDocumentsWorkspace = ({ ); const visibleDocumentKeys = useMemo( - () => visibleDocumentIds.map((id) => resolveDocumentRowKey(id)).filter(Boolean), + () => visibleDocumentIds.map((id) => createDocumentEntryKey(id)).filter(Boolean), [visibleDocumentIds], ); @@ -424,7 +413,7 @@ const useDocumentsWorkspace = ({ showingSearchResults ? [] : currentSubfolders - .map((folder) => resolveFolderRowKey(folder.id)) + .map((folder) => createFolderEntryKey(folder.id)) .filter(Boolean), [showingSearchResults, currentSubfolders], ); @@ -599,8 +588,6 @@ const useDocumentsWorkspace = ({ documentLookup, setDraggedDocumentIds, setDraggedFolderId, - resolveDocumentRowKey, - resolveFolderRowKey, documentsViewMode, }); @@ -827,8 +814,6 @@ const useDocumentsWorkspace = ({ showingSearchResults, currentSubfolders, visibleDocuments: viewDocuments, - resolveFolderRowKey, - resolveDocumentRowKey, configureSelectionEnvironment, visibleRowKeySet, selectedEntries, @@ -840,7 +825,6 @@ const useDocumentsWorkspace = ({ focusedDocumentId, setFocusedRowKey, focusedRowKey, - isFolderRowKey, }); const initializeAfterLogin = useCallback(async () => { await Promise.all([refreshTags(), refreshCorrespondents()]); @@ -1191,12 +1175,10 @@ const useDocumentsWorkspace = ({ ); const handleEntryPointerCore = useEntryPointerCore({ - resolveDocumentRowKey, - resolveFolderRowKey, onSelectEntry: (entry, event, { rowKey, modifierClick, primaryClick }) => { const { type, id } = entry; const key = rowKey - || (type === EntryType.document ? resolveDocumentRowKey(id) : resolveFolderRowKey(id)); + || (type === EntryType.document ? createDocumentEntryKey(id) : createFolderEntryKey(id)); if (key) { handleEntrySelection(key, event); } @@ -1238,7 +1220,6 @@ const useDocumentsWorkspace = ({ selectedEntries, selectionAnchorRef, applySelection, - resolveDocumentRowKey, showingSearchResults, searchQuery, activeTagFilters, diff --git a/frontend/src/hooks/documents/useFolderTree.ts b/frontend/src/hooks/documents/useFolderTree.ts index 277c072..7ac8dd4 100644 --- a/frontend/src/hooks/documents/useFolderTree.ts +++ b/frontend/src/hooks/documents/useFolderTree.ts @@ -1,14 +1,13 @@ import { useCallback, useMemo, useState } from 'react'; import type { Dispatch, MutableRefObject, SetStateAction } from 'react'; +import { createRootNode, DEFAULT_FOLDER_NAME } from '../../app/workspaceUtils'; import { - DEFAULT_FOLDER_NAME, - createRootNode, - getRowId, - isDocumentRowKey, - isFolderRowKey, - resolveDocumentRowKey, - resolveFolderRowKey, -} from '../../app/workspaceUtils'; + getEntryId, + isDocumentEntry, + isFolderEntry, + createDocumentEntryKey, + createFolderEntryKey, +} from '../../app/entryKey'; type Identifier = string | number; type FolderId = Identifier | 'root'; @@ -118,12 +117,12 @@ const useFolderTree = ({ setCurrentFolder(folderInfo); const availableDocKeys = docs - .map((doc) => resolveDocumentRowKey(doc?.id as Identifier)) + .map((doc) => createDocumentEntryKey(doc?.id as Identifier)) .filter(Boolean); const availableDocKeySet = new Set(availableDocKeys); const availableFolderKeys = new Set( subfolders - .map((folder) => resolveFolderRowKey(folder?.id as Identifier)) + .map((folder) => createFolderEntryKey(folder?.id as Identifier)) .filter(Boolean), ); @@ -132,22 +131,22 @@ const useFolderTree = ({ setSelectedEntries((previous) => { const previousFolderKeys = previous - .filter(isFolderRowKey) + .filter(isFolderEntry) .filter((key) => availableFolderKeys.has(key)); - const previousDocKeys = previous.filter(isDocumentRowKey); + const previousDocKeys = previous.filter(isDocumentEntry); nextDocKeys = previousDocKeys.filter((key) => availableDocKeySet.has(key)); mergedSelection = [...previousFolderKeys, ...nextDocKeys]; return mergedSelection; }); const nextFocus = (() => { - const currentFocusedKey = resolveDocumentRowKey(focusedDocumentId); + const currentFocusedKey = createDocumentEntryKey(focusedDocumentId); if (currentFocusedKey && availableDocKeySet.has(currentFocusedKey)) { return focusedDocumentId; } if (nextDocKeys.length) { const lastDocKey = nextDocKeys[nextDocKeys.length - 1]; - return getRowId(lastDocKey) || null; + return getEntryId(lastDocKey) || null; } return null; })(); diff --git a/frontend/src/hooks/documents/useWorkspaceDeskProps.ts b/frontend/src/hooks/documents/useWorkspaceDeskProps.ts index 655b969..3a37360 100644 --- a/frontend/src/hooks/documents/useWorkspaceDeskProps.ts +++ b/frontend/src/hooks/documents/useWorkspaceDeskProps.ts @@ -1,16 +1,20 @@ import { useCallback, useMemo } from 'react'; import type { MutableRefObject } from 'react'; +import { createDocumentEntryKey } from '../../app/entryKey'; type Identifier = string | number; +interface ApplySelectionFn { + (keys: string[], options?: { anchor?: string | null; interactedKeys?: string[] }): unknown; +} + interface UseWorkspaceDeskPropsArgs { - viewDocuments: any[]; - inspectDocumentForDesk: (doc: any) => void; + viewDocuments: unknown[]; + inspectDocumentForDesk?: (id: Identifier | null) => void; handleEntryPointer: (params: { rowKey?: string | null; id?: Identifier | null; type?: string; event?: any }) => void; - selectedEntries: Array; + selectedEntries: string[]; selectionAnchorRef: MutableRefObject; - applySelection: (rowKeys: Array, options?: { anchor?: Identifier | string | null; interactedKeys?: Array }) => void; - resolveDocumentRowKey: (id?: Identifier | null) => string | null; + applySelection: ApplySelectionFn; showingSearchResults: boolean; searchQuery: string; activeTagFilters: Array; @@ -32,7 +36,6 @@ const useWorkspaceDeskProps = ({ selectedEntries, selectionAnchorRef, applySelection, - resolveDocumentRowKey, showingSearchResults, searchQuery, activeTagFilters, @@ -53,7 +56,7 @@ const useWorkspaceDeskProps = ({ } const rowKeys = docIds - .map((id) => resolveDocumentRowKey(id as Identifier)) + .map((id) => createDocumentEntryKey(id as Identifier)) .filter((value): value is string => typeof value === 'string'); if (!rowKeys.length) { @@ -69,14 +72,14 @@ const useWorkspaceDeskProps = ({ const anchor = (rowKeys[0] || selectionAnchorRef.current - || nextKeys[nextKeys.length - 1]) as Identifier | string | null; + || nextKeys[nextKeys.length - 1]) as string | null; applySelection(nextKeys, { anchor, interactedKeys: rowKeys, }); }, - [applySelection, resolveDocumentRowKey, selectedEntries, selectionAnchorRef], + [applySelection, selectedEntries, selectionAnchorRef], ); const deskViewId = useMemo(() => {