From 3b28b398d9dc406ebb9dc5967454114fea9ef57f Mon Sep 17 00:00:00 2001 From: James Brunton Date: Fri, 29 Aug 2025 16:39:19 +0100 Subject: [PATCH] Automatically select added files (#4325) # Description of Changes When adding files, the user probably wants to use them straight away in the next tool that they use, so automatically select any added files (in addition to whatever they previously had selected). --- frontend/src/contexts/FileContext.tsx | 24 ++++++++++++++++++++--- frontend/src/contexts/file/fileActions.ts | 10 ++++++++-- frontend/src/hooks/useFileHandler.ts | 6 +++--- frontend/src/types/fileContext.ts | 4 ++-- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/frontend/src/contexts/FileContext.tsx b/frontend/src/contexts/FileContext.tsx index 05fdc9a9a..0698310b4 100644 --- a/frontend/src/contexts/FileContext.tsx +++ b/frontend/src/contexts/FileContext.tsx @@ -25,7 +25,7 @@ import { // Import modular components import { fileContextReducer, initialFileContextState } from './file/FileReducer'; import { createFileSelectors } from './file/fileSelectors'; -import { addFiles, consumeFiles, createFileActions } from './file/fileActions'; +import { AddedFile, addFiles, consumeFiles, createFileActions } from './file/fileActions'; import { FileLifecycleManager } from './file/lifecycle'; import { FileStateContext, FileActionsContext } from './file/contexts'; import { IndexedDBProvider, useIndexedDB } from './IndexedDBContext'; @@ -72,9 +72,21 @@ function FileContextInner({ dispatch({ type: 'SET_UNSAVED_CHANGES', payload: { hasChanges } }); }, []); + const selectFiles = (addedFilesWithIds: AddedFile[]) => { + const currentSelection = stateRef.current.ui.selectedFileIds; + const newFileIds = addedFilesWithIds.map(({ id }) => id); + dispatch({ type: 'SET_SELECTED_FILES', payload: { fileIds: [...currentSelection, ...newFileIds] } }); + } + // File operations using unified addFiles helper with persistence - const addRawFiles = useCallback(async (files: File[], options?: { insertAfterPageId?: string }): Promise => { + const addRawFiles = useCallback(async (files: File[], options?: { insertAfterPageId?: string; selectFiles?: boolean }): Promise => { const addedFilesWithIds = await addFiles('raw', { files, ...options }, stateRef, filesRef, dispatch, lifecycleManager); + + // Auto-select the newly added files if requested + if (options?.selectFiles && addedFilesWithIds.length > 0) { + selectFiles(addedFilesWithIds); + } + // Persist to IndexedDB if enabled if (indexedDB && enablePersistence && addedFilesWithIds.length > 0) { await Promise.all(addedFilesWithIds.map(async ({ file, id, thumbnail }) => { @@ -94,8 +106,14 @@ function FileContextInner({ return result.map(({ file }) => file); }, []); - const addStoredFiles = useCallback(async (filesWithMetadata: Array<{ file: File; originalId: FileId; metadata: any }>): Promise => { + const addStoredFiles = useCallback(async (filesWithMetadata: Array<{ file: File; originalId: FileId; metadata: any }>, options?: { selectFiles?: boolean }): Promise => { const result = await addFiles('stored', { filesWithMetadata }, stateRef, filesRef, dispatch, lifecycleManager); + + // Auto-select the newly added files if requested + if (options?.selectFiles && result.length > 0) { + selectFiles(result); + } + return result.map(({ file }) => file); }, []); diff --git a/frontend/src/contexts/file/fileActions.ts b/frontend/src/contexts/file/fileActions.ts index d0c565783..8d91f8855 100644 --- a/frontend/src/contexts/file/fileActions.ts +++ b/frontend/src/contexts/file/fileActions.ts @@ -88,6 +88,12 @@ interface AddFileOptions { insertAfterPageId?: string; } +export interface AddedFile { + file: File; + id: FileId; + thumbnail?: string; +} + /** * Unified file addition helper - replaces addFiles/addProcessedFiles/addStoredFiles */ @@ -98,13 +104,13 @@ export async function addFiles( filesRef: React.MutableRefObject>, dispatch: React.Dispatch, lifecycleManager: FileLifecycleManager -): Promise> { +): Promise { // Acquire mutex to prevent race conditions await addFilesMutex.lock(); try { const fileRecords: FileRecord[] = []; - const addedFiles: Array<{ file: File; id: FileId; thumbnail?: string }> = []; + const addedFiles: AddedFile[] = []; // Build quickKey lookup from existing files for deduplication const existingQuickKeys = buildQuickKeySet(stateRef.current.files.byId); diff --git a/frontend/src/hooks/useFileHandler.ts b/frontend/src/hooks/useFileHandler.ts index 475affa6f..8c2a4e2a1 100644 --- a/frontend/src/hooks/useFileHandler.ts +++ b/frontend/src/hooks/useFileHandler.ts @@ -9,12 +9,12 @@ export const useFileHandler = () => { const addToActiveFiles = useCallback(async (file: File) => { // Let FileContext handle deduplication with quickKey logic - await actions.addFiles([file]); + await actions.addFiles([file], { selectFiles: true }); }, [actions.addFiles]); const addMultipleFiles = useCallback(async (files: File[]) => { // Let FileContext handle deduplication with quickKey logic - await actions.addFiles(files); + await actions.addFiles(files, { selectFiles: true }); }, [actions.addFiles]); // Add stored files preserving their original IDs to prevent session duplicates @@ -25,7 +25,7 @@ export const useFileHandler = () => { }); if (newFiles.length > 0) { - await actions.addStoredFiles(newFiles); + await actions.addStoredFiles(newFiles, { selectFiles: true }); } console.log(`📁 Added ${newFiles.length} stored files (${filesWithMetadata.length - newFiles.length} skipped as duplicates)`); diff --git a/frontend/src/types/fileContext.ts b/frontend/src/types/fileContext.ts index 644b3dbfe..d9bd8751e 100644 --- a/frontend/src/types/fileContext.ts +++ b/frontend/src/types/fileContext.ts @@ -214,9 +214,9 @@ export type FileContextAction = export interface FileContextActions { // File management - lightweight actions only - addFiles: (files: File[], options?: { insertAfterPageId?: string }) => Promise; + addFiles: (files: File[], options?: { insertAfterPageId?: string; selectFiles?: boolean }) => Promise; addProcessedFiles: (filesWithThumbnails: Array<{ file: File; thumbnail?: string; pageCount?: number }>) => Promise; - addStoredFiles: (filesWithMetadata: Array<{ file: File; originalId: FileId; metadata: FileMetadata }>) => Promise; + addStoredFiles: (filesWithMetadata: Array<{ file: File; originalId: FileId; metadata: FileMetadata }>, options?: { selectFiles?: boolean }) => Promise; removeFiles: (fileIds: FileId[], deleteFromStorage?: boolean) => Promise; updateFileRecord: (id: FileId, updates: Partial) => void; reorderFiles: (orderedFileIds: FileId[]) => void;