From 32f5cdc494af735fe46dfacd0384a7f5d203c7f1 Mon Sep 17 00:00:00 2001 From: RobbyV2 Date: Sat, 19 Jul 2025 20:14:36 -0700 Subject: [PATCH 1/2] Saving and Loading Progress Bar --- src/components/ProgressPopup.tsx | 26 +++++++--- src/components/SettingsModal.tsx | 84 +++++++++++++++++++++++++++++--- src/utils/indexedDB.ts | 51 +++++++++++++++++-- 3 files changed, 142 insertions(+), 19 deletions(-) diff --git a/src/components/ProgressPopup.tsx b/src/components/ProgressPopup.tsx index aedc4e2..394c4f0 100644 --- a/src/components/ProgressPopup.tsx +++ b/src/components/ProgressPopup.tsx @@ -8,9 +8,11 @@ interface ProgressPopupProps { estimatedTimeRemaining?: string; onCancel: () => void; isProcessing: boolean; + statusMessage?: string; + operationType?: 'sync' | 'load'; } -export function ProgressPopup({ isOpen, progress, estimatedTimeRemaining, onCancel, isProcessing }: ProgressPopupProps) { +export function ProgressPopup({ isOpen, progress, estimatedTimeRemaining, onCancel, isProcessing, statusMessage, operationType }: ProgressPopupProps) { return ( -
+
@@ -33,12 +35,20 @@ export function ProgressPopup({ isOpen, progress, estimatedTimeRemaining, onCanc />
-
- {Math.round(progress)}% complete - {estimatedTimeRemaining &&
- - {` ~${estimatedTimeRemaining}`} -
} +
+ {operationType && ( + + {operationType === 'sync' ? 'Saving to Server' : 'Loading from Server'} + + )} + {statusMessage && {statusMessage}} +
+ {Math.round(progress)}% complete + {estimatedTimeRemaining &&
+ + {` ~${estimatedTimeRemaining}`} +
} +
} @@ -498,6 +549,27 @@ export function SettingsModal() { confirmText="Delete" isDangerous={true} /> + + { + if (abortController) { + abortController.abort(); + } + setShowProgress(false); + setProgress(0); + setIsSyncing(false); + setIsLoading(false); + setStatusMessage(''); + setOperationType('sync'); + setAbortController(null); + }} + isProcessing={isSyncing || isLoading} + statusMessage={statusMessage} + operationType={operationType} + /> ); } diff --git a/src/utils/indexedDB.ts b/src/utils/indexedDB.ts index 6e3b875..973df19 100644 --- a/src/utils/indexedDB.ts +++ b/src/utils/indexedDB.ts @@ -635,11 +635,13 @@ class IndexedDBService { }); } - async syncToServer(): Promise<{ lastSync: number }> { + async syncToServer(onProgress?: (progress: number, status?: string) => void, signal?: AbortSignal): Promise<{ lastSync: number }> { const pdfDocs = await this.getAllDocuments(); const epubDocs = await this.getAllEPUBDocuments(); const documents = []; + const totalDocs = pdfDocs.length + epubDocs.length; + let processedDocs = 0; // Process PDF documents - convert ArrayBuffer to array for JSON serialization for (const doc of pdfDocs) { @@ -648,6 +650,10 @@ class IndexedDBService { type: 'pdf', data: Array.from(new Uint8Array(doc.data)) }); + processedDocs++; + if (onProgress) { + onProgress((processedDocs / totalDocs) * 50, `Processing ${processedDocs}/${totalDocs} documents...`); + } } // Process EPUB documents @@ -657,31 +663,57 @@ class IndexedDBService { type: 'epub', data: Array.from(new Uint8Array(doc.data)) }); + processedDocs++; + if (onProgress) { + onProgress((processedDocs / totalDocs) * 50, `Processing ${processedDocs}/${totalDocs} documents...`); + } + } + + if (onProgress) { + onProgress(50, 'Uploading to server...'); } const response = await fetch('/api/documents', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ documents }) + body: JSON.stringify({ documents }), + signal }); if (!response.ok) { throw new Error('Failed to sync documents to server'); } + if (onProgress) { + onProgress(100, 'Upload complete!'); + } + return { lastSync: Date.now() }; } - async loadFromServer(): Promise<{ lastSync: number }> { - const response = await fetch('/api/documents'); + async loadFromServer(onProgress?: (progress: number, status?: string) => void, signal?: AbortSignal): Promise<{ lastSync: number }> { + if (onProgress) { + onProgress(10, 'Starting download...'); + } + + const response = await fetch('/api/documents', { signal }); if (!response.ok) { throw new Error('Failed to fetch documents from server'); } + if (onProgress) { + onProgress(30, 'Download complete'); + } + const { documents } = await response.json(); + if (onProgress) { + onProgress(40, 'Parsing documents...'); + } + // Process each document - for (const doc of documents) { + for (let i = 0; i < documents.length; i++) { + const doc = documents[i]; // Convert the numeric array back to ArrayBuffer const uint8Array = new Uint8Array(doc.data); const documentData = { @@ -700,6 +732,15 @@ class IndexedDBService { } else { console.warn(`Unknown document type: ${doc.type}`); } + + if (onProgress) { + // Progress from 40% to 90% for document processing + onProgress(40 + ((i + 1) / documents.length) * 50, `Processing document ${i + 1}/${documents.length}...`); + } + } + + if (onProgress) { + onProgress(100, 'Load complete!'); } return { lastSync: Date.now() }; From 7b703f015f2cfb18830c572f847bc5e2aa78512c Mon Sep 17 00:00:00 2001 From: RobbyV2 Date: Sat, 19 Jul 2025 21:04:03 -0700 Subject: [PATCH 2/2] Add cancelText parameter --- src/components/DocumentSettings.tsx | 1 + src/components/ProgressPopup.tsx | 17 +++++++---------- src/components/SettingsModal.tsx | 1 + 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/components/DocumentSettings.tsx b/src/components/DocumentSettings.tsx index 9f2d34c..02b6870 100644 --- a/src/components/DocumentSettings.tsx +++ b/src/components/DocumentSettings.tsx @@ -133,6 +133,7 @@ export function DocumentSettings({ isOpen, setIsOpen, epub, html }: { estimatedTimeRemaining={estimatedTimeRemaining || undefined} onCancel={handleCancel} isProcessing={epub ? isAudioCombining : isPDFAudioCombining} + cancelText="Cancel and download" /> diff --git a/src/components/ProgressPopup.tsx b/src/components/ProgressPopup.tsx index 394c4f0..65ded31 100644 --- a/src/components/ProgressPopup.tsx +++ b/src/components/ProgressPopup.tsx @@ -10,9 +10,10 @@ interface ProgressPopupProps { isProcessing: boolean; statusMessage?: string; operationType?: 'sync' | 'load'; + cancelText?: string; } -export function ProgressPopup({ isOpen, progress, estimatedTimeRemaining, onCancel, isProcessing, statusMessage, operationType }: ProgressPopupProps) { +export function ProgressPopup({ isOpen, progress, estimatedTimeRemaining, onCancel, isProcessing, statusMessage, operationType, cancelText = 'Cancel' }: ProgressPopupProps) { return (
diff --git a/src/components/SettingsModal.tsx b/src/components/SettingsModal.tsx index fb6620f..ab16939 100644 --- a/src/components/SettingsModal.tsx +++ b/src/components/SettingsModal.tsx @@ -569,6 +569,7 @@ export function SettingsModal() { isProcessing={isSyncing || isLoading} statusMessage={statusMessage} operationType={operationType} + cancelText="Cancel" /> );