From 9c49f329e2d30f2f914b950dd70417562f5d2819 Mon Sep 17 00:00:00 2001 From: Reece Browne Date: Wed, 20 Aug 2025 17:13:06 +0100 Subject: [PATCH] Fix file handling for none pdfs --- .../components/pageEditor/FileThumbnail.tsx | 35 ++++++---- frontend/src/contexts/file/fileActions.ts | 66 ++++++++++++------- frontend/src/contexts/file/lifecycle.ts | 26 +++++--- frontend/src/utils/thumbnailUtils.ts | 4 +- 4 files changed, 84 insertions(+), 47 deletions(-) diff --git a/frontend/src/components/pageEditor/FileThumbnail.tsx b/frontend/src/components/pageEditor/FileThumbnail.tsx index 7d450104d..7aca80565 100644 --- a/frontend/src/components/pageEditor/FileThumbnail.tsx +++ b/frontend/src/components/pageEditor/FileThumbnail.tsx @@ -201,6 +201,11 @@ const FileThumbnail = ({ src={file.thumbnail} alt={file.name} draggable={false} + onError={(e) => { + // Hide broken image if blob URL was revoked + const img = e.target as HTMLImageElement; + img.style.display = 'none'; + }} style={{ maxWidth: '100%', maxHeight: '100%', @@ -210,20 +215,22 @@ const FileThumbnail = ({ /> - {/* Page count badge */} - - {file.pageCount} {file.pageCount === 1 ? 'page' : 'pages'} - + {/* Page count badge - only show for PDFs */} + {file.pageCount > 0 && ( + + {file.pageCount} {file.pageCount === 1 ? 'page' : 'pages'} + + )} {/* Unsupported badge */} {!isSupported && ( diff --git a/frontend/src/contexts/file/fileActions.ts b/frontend/src/contexts/file/fileActions.ts index 8f32fa521..1a678fa76 100644 --- a/frontend/src/contexts/file/fileActions.ts +++ b/frontend/src/contexts/file/fileActions.ts @@ -92,14 +92,28 @@ export async function addFiles( let thumbnail: string | undefined; let pageCount: number = 1; - try { - if (DEBUG) console.log(`📄 Generating immediate thumbnail and metadata for ${file.name}`); - const result = await generateThumbnailWithMetadata(file); - thumbnail = result.thumbnail; - pageCount = result.pageCount; - if (DEBUG) console.log(`📄 Generated immediate metadata for ${file.name}: ${pageCount} pages, thumbnail: ${!!thumbnail}`); - } catch (error) { - if (DEBUG) console.warn(`📄 Failed to generate immediate metadata for ${file.name}:`, error); + // Route based on file type - PDFs through full metadata pipeline, non-PDFs through simple path + if (file.type.startsWith('application/pdf')) { + try { + if (DEBUG) console.log(`📄 Generating PDF metadata for ${file.name}`); + const result = await generateThumbnailWithMetadata(file); + thumbnail = result.thumbnail; + pageCount = result.pageCount; + if (DEBUG) console.log(`📄 Generated PDF metadata for ${file.name}: ${pageCount} pages, thumbnail: ${!!thumbnail}`); + } catch (error) { + if (DEBUG) console.warn(`📄 Failed to generate PDF metadata for ${file.name}:`, error); + } + } else { + // Non-PDF files: simple thumbnail generation, no page count + try { + if (DEBUG) console.log(`📄 Generating simple thumbnail for non-PDF file ${file.name}`); + const { generateThumbnailForFile } = await import('../../utils/thumbnailUtils'); + thumbnail = await generateThumbnailForFile(file); + pageCount = 0; // Non-PDFs have no page count + if (DEBUG) console.log(`📄 Generated simple thumbnail for ${file.name}: no page count, thumbnail: ${!!thumbnail}`); + } catch (error) { + if (DEBUG) console.warn(`📄 Failed to generate simple thumbnail for ${file.name}:`, error); + } } // Create record with immediate thumbnail and page metadata @@ -198,22 +212,28 @@ export async function addFiles( } } - // Generate processedFile metadata for stored files using PDF worker manager - // This ensures stored files have proper page information and avoids cancellation conflicts + // Generate processedFile metadata for stored files let pageCount: number = 1; - try { - if (DEBUG) console.log(`📄 addFiles(stored): Generating metadata for stored file ${file.name}`); - - // Use PDF worker manager directly for page count (avoids fileProcessingService conflicts) - const arrayBuffer = await file.arrayBuffer(); - const { pdfWorkerManager } = await import('../../services/pdfWorkerManager'); - const pdf = await pdfWorkerManager.createDocument(arrayBuffer); - pageCount = pdf.numPages; - pdfWorkerManager.destroyDocument(pdf); - - if (DEBUG) console.log(`📄 addFiles(stored): Found ${pageCount} pages in stored file ${file.name}`); - } catch (error) { - if (DEBUG) console.warn(`📄 addFiles(stored): Failed to generate metadata for ${file.name}:`, error); + + // Only process PDFs through PDF worker manager, non-PDFs have no page count + if (file.type.startsWith('application/pdf')) { + try { + if (DEBUG) console.log(`📄 addFiles(stored): Generating PDF metadata for stored file ${file.name}`); + + // Use PDF worker manager directly for page count (avoids fileProcessingService conflicts) + const arrayBuffer = await file.arrayBuffer(); + const { pdfWorkerManager } = await import('../../services/pdfWorkerManager'); + const pdf = await pdfWorkerManager.createDocument(arrayBuffer); + pageCount = pdf.numPages; + pdfWorkerManager.destroyDocument(pdf); + + if (DEBUG) console.log(`📄 addFiles(stored): Found ${pageCount} pages in PDF ${file.name}`); + } catch (error) { + if (DEBUG) console.warn(`📄 addFiles(stored): Failed to generate PDF metadata for ${file.name}:`, error); + } + } else { + pageCount = 0; // Non-PDFs have no page count + if (DEBUG) console.log(`📄 addFiles(stored): Non-PDF file ${file.name}, no page count`); } // Create processedFile metadata with correct page count diff --git a/frontend/src/contexts/file/lifecycle.ts b/frontend/src/contexts/file/lifecycle.ts index 1879896a2..a984d48cd 100644 --- a/frontend/src/contexts/file/lifecycle.ts +++ b/frontend/src/contexts/file/lifecycle.ts @@ -144,11 +144,14 @@ export class FileLifecycleManager { if (stateRef) { const record = stateRef.current.files.byId[fileId]; if (record) { - // Revoke blob URLs from file record + // Defer revocation of thumbnail blob URLs to prevent image loading race conditions if (record.thumbnailUrl && record.thumbnailUrl.startsWith('blob:')) { try { - URL.revokeObjectURL(record.thumbnailUrl); - if (DEBUG) console.log(`🗂️ Revoked thumbnail blob URL for file: ${fileId}`); + // Add a small delay to ensure images have time to load + setTimeout(() => { + URL.revokeObjectURL(record.thumbnailUrl); + if (DEBUG) console.log(`🗂️ Revoked thumbnail blob URL for file: ${fileId}`); + }, 1000); // 1 second delay } catch (error) { if (DEBUG) console.warn('Error revoking thumbnail URL:', error); } @@ -156,20 +159,27 @@ export class FileLifecycleManager { if (record.blobUrl && record.blobUrl.startsWith('blob:')) { try { - URL.revokeObjectURL(record.blobUrl); - if (DEBUG) console.log(`🗂️ Revoked file blob URL for file: ${fileId}`); + // Add a small delay to ensure any pending operations complete + setTimeout(() => { + URL.revokeObjectURL(record.blobUrl); + if (DEBUG) console.log(`🗂️ Revoked file blob URL for file: ${fileId}`); + }, 1000); // 1 second delay } catch (error) { if (DEBUG) console.warn('Error revoking file URL:', error); } } - // Clean up processed file thumbnails + // Clean up processed file thumbnails with delay if (record.processedFile?.pages) { record.processedFile.pages.forEach((page: ProcessedFilePage, index: number) => { if (page.thumbnail && page.thumbnail.startsWith('blob:')) { try { - URL.revokeObjectURL(page.thumbnail); - if (DEBUG) console.log(`🗂️ Revoked page ${index} thumbnail for file: ${fileId}`); + const thumbnailUrl = page.thumbnail; + // Add delay for page thumbnails too + setTimeout(() => { + URL.revokeObjectURL(thumbnailUrl); + if (DEBUG) console.log(`🗂️ Revoked page ${index} thumbnail for file: ${fileId}`); + }, 1000); // 1 second delay } catch (error) { if (DEBUG) console.warn('Error revoking page thumbnail URL:', error); } diff --git a/frontend/src/utils/thumbnailUtils.ts b/frontend/src/utils/thumbnailUtils.ts index 3335e2bb6..adccead4f 100644 --- a/frontend/src/utils/thumbnailUtils.ts +++ b/frontend/src/utils/thumbnailUtils.ts @@ -368,10 +368,10 @@ export async function generateThumbnailForFile(file: File): Promise { - // Non-PDF files default to 1 page + // Non-PDF files have no page count if (!file.type.startsWith('application/pdf')) { const thumbnail = await generateThumbnailForFile(file); - return { thumbnail, pageCount: 1 }; + return { thumbnail, pageCount: 0 }; } // Skip very large files