diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
index b2b78bb..62b82c8 100644
--- a/.github/workflows/playwright.yml
+++ b/.github/workflows/playwright.yml
@@ -5,7 +5,7 @@ on:
pull_request:
branches: [ main, master ]
jobs:
- test:
+ e2e-testing:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
diff --git a/src/components/PDFViewer.tsx b/src/components/PDFViewer.tsx
index c160cf2..358bbb8 100644
--- a/src/components/PDFViewer.tsx
+++ b/src/components/PDFViewer.tsx
@@ -36,7 +36,7 @@ export function PDFViewer({ zoomLevel }: PDFViewerProps) {
clearHighlights,
handleTextClick,
onDocumentLoadSuccess,
- currDocURL,
+ currDocData,
currDocPages,
currDocText,
currDocPage,
@@ -163,7 +163,7 @@ export function PDFViewer({ zoomLevel }: PDFViewerProps) {
}
noData={}
- file={currDocURL}
+ file={currDocData}
onLoadSuccess={(pdf) => {
onDocumentLoadSuccess(pdf);
}}
diff --git a/src/contexts/PDFContext.tsx b/src/contexts/PDFContext.tsx
index e789024..9d7f910 100644
--- a/src/contexts/PDFContext.tsx
+++ b/src/contexts/PDFContext.tsx
@@ -29,7 +29,6 @@ import { useTTS } from '@/contexts/TTSContext';
import { useConfig } from '@/contexts/ConfigContext';
import {
extractTextFromPDF,
- convertPDFDataToURL,
highlightPattern,
clearHighlights,
handleTextClick,
@@ -43,7 +42,7 @@ import { combineAudioChunks } from '@/utils/audio';
*/
interface PDFContextType {
// Current document state
- currDocURL: string | undefined;
+ currDocData: ArrayBuffer | undefined;
currDocName: string | undefined;
currDocPages: number | undefined;
currDocPage: number;
@@ -99,7 +98,7 @@ export function PDFProvider({ children }: { children: ReactNode }) {
} = useConfig();
// Current document state
- const [currDocURL, setCurrDocURL] = useState();
+ const [currDocData, setCurrDocData] = useState();
const [currDocName, setCurrDocName] = useState();
const [currDocText, setCurrDocText] = useState();
const [pdfDocument, setPdfDocument] = useState();
@@ -147,14 +146,14 @@ export function PDFProvider({ children }: { children: ReactNode }) {
* Triggers text extraction and processing when either the document URL or page changes
*/
useEffect(() => {
- if (currDocURL) {
+ if (currDocData) {
loadCurrDocText();
}
- }, [currDocPage, currDocURL, loadCurrDocText]);
+ }, [currDocPage, currDocData, loadCurrDocText]);
/**
* Sets the current document based on its ID
- * Retrieves document from IndexedDB and converts it to a viewable URL
+ * Retrieves document from IndexedDB
*
* @param {string} id - The unique identifier of the document to set
* @returns {Promise}
@@ -163,12 +162,11 @@ export function PDFProvider({ children }: { children: ReactNode }) {
try {
const doc = await indexedDBService.getDocument(id);
if (doc) {
- const url = await convertPDFDataToURL(doc.data);
setCurrDocName(doc.name);
- setCurrDocURL(url);
+ setCurrDocData(doc.data);
}
} catch (error) {
- console.error('Failed to get document URL:', error);
+ console.error('Failed to get document:', error);
}
}, []);
@@ -178,7 +176,7 @@ export function PDFProvider({ children }: { children: ReactNode }) {
*/
const clearCurrDoc = useCallback(() => {
setCurrDocName(undefined);
- setCurrDocURL(undefined);
+ setCurrDocData(undefined);
setCurrDocText(undefined);
setCurrDocPages(undefined);
setPdfDocument(undefined);
@@ -306,7 +304,7 @@ export function PDFProvider({ children }: { children: ReactNode }) {
() => ({
onDocumentLoadSuccess,
setCurrentDocument,
- currDocURL,
+ currDocData,
currDocName,
currDocPages,
currDocPage,
@@ -322,7 +320,7 @@ export function PDFProvider({ children }: { children: ReactNode }) {
[
onDocumentLoadSuccess,
setCurrentDocument,
- currDocURL,
+ currDocData,
currDocName,
currDocPages,
currDocPage,
diff --git a/src/hooks/pdf/usePDFDocuments.ts b/src/hooks/pdf/usePDFDocuments.ts
index 45d6ec3..e2fa4ea 100644
--- a/src/hooks/pdf/usePDFDocuments.ts
+++ b/src/hooks/pdf/usePDFDocuments.ts
@@ -39,13 +39,15 @@ export function usePDFDocuments() {
*/
const addDocument = useCallback(async (file: File): Promise => {
const id = uuidv4();
+ const arrayBuffer = await file.arrayBuffer();
+
const newDoc: PDFDocument = {
id,
type: 'pdf',
name: file.name,
size: file.size,
lastModified: file.lastModified,
- data: new Blob([file], { type: file.type }),
+ data: arrayBuffer,
};
try {
diff --git a/src/types/documents.ts b/src/types/documents.ts
index 5097115..07cbd41 100644
--- a/src/types/documents.ts
+++ b/src/types/documents.ts
@@ -11,7 +11,7 @@ export interface BaseDocument {
export interface PDFDocument extends BaseDocument {
type: 'pdf';
- data: Blob;
+ data: ArrayBuffer;
}
export interface EPUBDocument extends BaseDocument {
diff --git a/src/utils/indexedDB.ts b/src/utils/indexedDB.ts
index 6df16b9..e60aa5a 100644
--- a/src/utils/indexedDB.ts
+++ b/src/utils/indexedDB.ts
@@ -43,7 +43,7 @@ class IndexedDBService {
request.onupgradeneeded = (event) => {
console.log('Upgrading IndexedDB schema...');
const db = (event.target as IDBOpenDBRequest).result;
-
+
if (!db.objectStoreNames.contains(PDF_STORE_NAME)) {
console.log('Creating PDF documents store...');
db.createObjectStore(PDF_STORE_NAME, { keyPath: 'id' });
@@ -75,7 +75,12 @@ class IndexedDBService {
console.log('Adding document to IndexedDB:', document.name);
const transaction = this.db!.transaction([PDF_STORE_NAME], 'readwrite');
const store = transaction.objectStore(PDF_STORE_NAME);
- const request = store.put(document);
+
+ // Create a structured clone of the document to ensure proper storage
+ const request = store.put({
+ ...document,
+ data: document.data // Store ArrayBuffer directly
+ });
request.onerror = (event) => {
const error = (event.target as IDBRequest).error;
@@ -165,7 +170,7 @@ class IndexedDBService {
// Create two transactions - one for document deletion, one for location cleanup
const docTransaction = this.db!.transaction([PDF_STORE_NAME], 'readwrite');
const configTransaction = this.db!.transaction([CONFIG_STORE_NAME], 'readwrite');
-
+
const docStore = docTransaction.objectStore(PDF_STORE_NAME);
const configStore = configTransaction.objectStore(CONFIG_STORE_NAME);
@@ -221,7 +226,7 @@ class IndexedDBService {
const transaction = this.db!.transaction([EPUB_STORE_NAME], 'readwrite');
const store = transaction.objectStore(EPUB_STORE_NAME);
-
+
// Create a structured clone of the document to ensure proper storage
const request = store.put({
...document,
@@ -315,7 +320,7 @@ class IndexedDBService {
// Create two transactions - one for document deletion, one for location cleanup
const docTransaction = this.db!.transaction([EPUB_STORE_NAME], 'readwrite');
const configTransaction = this.db!.transaction([CONFIG_STORE_NAME], 'readwrite');
-
+
const docStore = docTransaction.objectStore(EPUB_STORE_NAME);
const configStore = configTransaction.objectStore(CONFIG_STORE_NAME);
@@ -477,17 +482,15 @@ class IndexedDBService {
async syncToServer(): Promise<{ lastSync: number }> {
const pdfDocs = await this.getAllDocuments();
const epubDocs = await this.getAllEPUBDocuments();
-
+
const documents = [];
-
- // Process PDF documents - store the raw PDF data
+
+ // Process PDF documents - convert ArrayBuffer to array for JSON serialization
for (const doc of pdfDocs) {
- const arrayBuffer = await doc.data.arrayBuffer();
- const uint8Array = new Uint8Array(arrayBuffer);
documents.push({
...doc,
type: 'pdf',
- data: Array.from(uint8Array) // Convert to regular array for JSON serialization
+ data: Array.from(new Uint8Array(doc.data))
});
}
@@ -496,7 +499,7 @@ class IndexedDBService {
documents.push({
...doc,
type: 'epub',
- data: Array.from(new Uint8Array(doc.data)) // Convert to regular array for JSON serialization
+ data: Array.from(new Uint8Array(doc.data))
});
}
@@ -520,31 +523,26 @@ class IndexedDBService {
}
const { documents } = await response.json();
-
+
// Process each document
for (const doc of documents) {
+ // Convert the numeric array back to ArrayBuffer
+ const uint8Array = new Uint8Array(doc.data);
+ const documentData = {
+ id: doc.id,
+ type: doc.type,
+ name: doc.name,
+ size: doc.size,
+ lastModified: doc.lastModified,
+ data: uint8Array.buffer
+ };
+
if (doc.type === 'pdf') {
- // Create a Blob from the raw binary data
- const blob = new Blob([new Uint8Array(doc.data)], { type: 'application/pdf' });
- await this.addDocument({
- id: doc.id,
- type: doc.type,
- name: doc.name,
- size: doc.size,
- lastModified: doc.lastModified,
- data: blob
- });
+ await this.addDocument(documentData);
} else if (doc.type === 'epub') {
- // Convert the numeric array back to ArrayBuffer for EPUB
- const uint8Array = new Uint8Array(doc.data);
- await this.addEPUBDocument({
- id: doc.id,
- type: doc.type,
- name: doc.name,
- size: doc.size,
- lastModified: doc.lastModified,
- data: uint8Array.buffer
- });
+ await this.addEPUBDocument(documentData);
+ } else {
+ console.warn(`Unknown document type: ${doc.type}`);
}
}
diff --git a/src/utils/pdf.ts b/src/utils/pdf.ts
index 24eac9a..1d1515d 100644
--- a/src/utils/pdf.ts
+++ b/src/utils/pdf.ts
@@ -15,16 +15,6 @@ interface TextMatch {
lengthDiff: number;
}
-// URL Conversion functions
-export function convertPDFDataToURL(pdfData: Blob): Promise {
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.onload = () => resolve(reader.result as string);
- reader.onerror = () => reject(reader.error);
- reader.readAsDataURL(pdfData);
- });
-}
-
// Text Processing functions
export async function extractTextFromPDF(
pdf: PDFDocumentProxy,
diff --git a/tests/e2e.spec.ts b/tests/e2e.spec.ts
index baa0b28..92ee4b7 100644
--- a/tests/e2e.spec.ts
+++ b/tests/e2e.spec.ts
@@ -11,7 +11,7 @@ async function uploadFile(page: Page, filePath: string) {
test.describe('Document Upload and Display', () => {
test.beforeEach(async ({ page }) => {
// Navigate to the home page before each test
- await page.goto('http://localhost:3003');
+ await page.goto('/');
// Click the "done" button to dismiss the welcome message
await page.getByText('Done').click();
@@ -23,7 +23,7 @@ test.describe('Document Upload and Display', () => {
await uploadFile(page, './public/sample.pdf');
// Verify upload success
- await expect(page.getByText('sample.pdf')).toBeVisible();
+ await expect(page.getByText('sample.pdf')).toBeVisible({ timeout: 10000 });
});
test('should display a PDF document', async ({ page }) => {
@@ -31,11 +31,11 @@ test.describe('Document Upload and Display', () => {
await uploadFile(page, './public/sample.pdf');
// Click on the uploaded document
- await page.getByText('sample.pdf').click();
+ await page.getByText('sample.pdf').click({ timeout: 10000 });
// Verify PDF viewer is displayed
- await expect(page.locator('.react-pdf__Document')).toBeVisible();
- await expect(page.locator('.react-pdf__Page')).toBeVisible();
+ await expect(page.locator('.react-pdf__Document')).toBeVisible({ timeout: 10000 });
+ await expect(page.locator('.react-pdf__Page')).toBeVisible({ timeout: 10000 });
});
});
@@ -45,7 +45,7 @@ test.describe('Document Upload and Display', () => {
await uploadFile(page, './public/sample.epub');
// Verify upload success
- await expect(page.getByText('sample.epub')).toBeVisible();
+ await expect(page.getByText('sample.epub')).toBeVisible({ timeout: 10000 });
});
test('should display an EPUB document', async ({ page }) => {
@@ -53,10 +53,10 @@ test.describe('Document Upload and Display', () => {
await uploadFile(page, './public/sample.epub');
// Click on the uploaded document
- await page.getByText('sample.epub').click();
+ await page.getByText('sample.epub').click({ timeout: 10000 });
// Verify EPUB viewer is displayed
- await expect(page.locator('.epub-container')).toBeVisible();
+ await expect(page.locator('.epub-container')).toBeVisible({ timeout: 10000 });
});
});
});