-
Notifications
You must be signed in to change notification settings - Fork 0
feat: IndexedDB file queue for offline uploads (#142) #154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Add fileQueue table to IndexedDB schema (version 3) - Implement FileQueueEntry interface with upload states - Create fileQueue utilities (add, get, update, retry, process) - Add exponential backoff for failed uploads (max 5 retries) - Implement storage quota monitoring - Add 17 comprehensive tests with 100% coverage - Placeholder for future Secret API integration Related to #142
- Install idb dependency for Service Worker IndexedDB access - Integrate FileQueue into Service Worker Share Target handler - Store shared files directly in IndexedDB (replaces sessionStorage) - Add Background Sync event listener for offline uploads - Create useFileQueue() React hook with Dexie live queries - Support Background Sync registration from client - Add file IDs to shared file metadata Related to #142
- Update useShareTarget to receive files via SW messages - Remove sessionStorage dependency for file sharing - Add file queue IDs to SharedFile interface - Update CHANGELOG with comprehensive FileQueue documentation - Document migration from sessionStorage to IndexedDB Related to #142
💡 Tip: Consider Using Draft PRsBenefits of opening PRs as drafts initially:
How to convert:
This is just a friendly reminder - feel free to continue as is! 😊 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements a robust IndexedDB-based persistent file queue for offline-first file uploads, replacing the previous sessionStorage approach with a more capable and reliable solution that survives browser restarts and provides better storage capacity.
Key changes:
- Extended IndexedDB schema to version 3 with a new
fileQueuetable supporting upload state tracking and retry logic - Implemented comprehensive file queue utilities with exponential backoff retry mechanism (max 5 attempts)
- Integrated Background Sync API in Service Worker for automatic upload when network connectivity is restored
Reviewed Changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/lib/db.ts | Added FileQueueEntry and FileMetadata interfaces; extended DB schema to version 3 with fileQueue table |
| src/lib/fileQueue.ts | Implemented file queue utilities with retry logic, state management, and storage quota monitoring |
| src/lib/fileQueue.test.ts | Added 17 comprehensive unit tests covering all file queue operations |
| src/sw.ts | Modified Share Target to store files in IndexedDB; added Background Sync handler for offline queue processing |
| src/hooks/useShareTarget.ts | Migrated from sessionStorage to Service Worker message-based file handling |
| src/hooks/useFileQueue.ts | Created React hook for real-time queue monitoring using Dexie live queries |
| package.json | Added idb@^8.0.2 dependency for Service Worker IndexedDB access |
| package-lock.json | Updated lock file with idb dependency and dependency metadata changes |
| CHANGELOG.md | Documented all new features and changes for IndexedDB file queue implementation |
Comments suppressed due to low confidence (1)
src/sw.ts:176
- The Service Worker's
handleShareTargetPostfunction generates file metadata for preview with a 2MB limit for Base64 encoding, but then processesallowedFilestwice - once to store in IndexedDB (lines 142-152) and again to generate metadata with previews (lines 155-176). This results in reading each file twice. Consider combining these operations or reusing the file data to avoid redundant file I/O.
const fileIds = await Promise.all(
allowedFiles.map(async (file) => {
const id = await storeFileInQueue(file, {
name: file.name,
type: file.type,
size: file.size,
timestamp: Date.now(),
});
return id;
})
);
// Generate lightweight file metadata for client notification
const processedFiles = await Promise.all(
allowedFiles.map(async (file, index) => {
// Convert file to Base64 for preview only for images and limited size
// Reduced to 2MB to prevent memory issues (Base64 is ~33% larger)
let dataUrl: string | undefined;
if (file.type.startsWith("image/") && file.size < 2 * 1024 * 1024) {
try {
dataUrl = await fileToBase64(file);
} catch {
// If conversion fails, omit preview but keep metadata
dataUrl = undefined;
}
}
return {
id: fileIds[index],
name: file.name,
type: file.type,
size: file.size,
dataUrl,
};
})
- Remove redundant File→Blob conversion (File extends Blob) - Extract DB version constant (DB_VERSION = 3) with sync warning - Add MAX_RETRY_COUNT constant (5 retries) to prevent infinite loops - Check max retries in syncFileQueue before processing - Add useCallback to event handlers (prevent listener re-creation) - Add schema sync risk warning comments in Service Worker Addresses review comments #2, #4, #5, #8, #9 from PR #154
Address all remaining Copilot review nitpicks: - Parallel file processing with concurrency limit (default: 3) - Add processWithConcurrency helper function - Prevents sequential bottleneck for large queues - Configurable via parameter (Comment #3) - Make quota update interval configurable - Add options parameter to useFileQueue hook - Default: 30s, customizable per use case (Comment #1) - Enhance sync error handling - FILE_QUEUE_SYNCED message now reports success/failed counts - Add FILE_QUEUE_SYNC_ERROR message handler - Better visibility for background sync issues (Comment #6) - Clarify exponential backoff logic - Improve comment to explain retry 0-4 timing - Document first retry vs subsequent retries (Comment #7) All 17 fileQueue tests passing ✅ Addresses review comments #1, #3, #6, #7 from PR #154
- Update db.test.ts to expect version 3 and fileQueue table - Fix useShareTarget.test.ts for new SW message architecture - Replace mockFiles expectations with undefined (files via SW) - Skip obsolete sessionStorage tests (now IndexedDB) - Skip replaceState tests (require SW message mocking) - Fix fileQueue.ts TypeScript type guard All 196 tests passing ✅ (11 skipped - require SW integration) Fixes CI test failures
Fix npm ci failure in CI caused by package-lock.json mismatch
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
- Replace obsolete sessionStorage tests with SW message mocking - Add comprehensive Service Worker message handler tests (7 tests) - Add history.replaceState tests with SW integration (3 tests) - Test shareId matching/mismatching logic - Test SW message listener registration/cleanup - Test URL parameter combinations with files - Test empty string handling in URL params Coverage improved from 62.5% (48% on Codecov) to 97.5% Only 2 lines uncovered (error edge cases) 26 tests passing, all new tests use proper SW mocking
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 10 out of 11 changed files in this pull request and generated 13 comments.
Critical fixes: - Create db-constants.ts to share DB_VERSION, MAX_RETRY_COUNT between app and SW - Fix Service Worker retry logic: only mark failed after actual upload attempt - Add exponential backoff cap (60s max) to prevent extreme delays - Replace concurrency control with robust worker pool pattern - Fix SW message fields: succeeded/failed instead of success/failed Service Worker improvements: - Validate trusted window clients before processing sync - Distinguish transient vs permanent errors for retry logic - Send detailed sync stats (succeeded, failed) to clients - Use shared constants from db-constants.ts Hook improvements: - Add runtime check for Background Sync API availability - Improve useCallback documentation for URL reading pattern - Track 'skipped' files (backoff) separately from 'pending' - Handle FILE_QUEUE_SYNC_ERROR messages Code quality: - Better error handling for corrupted IndexedDB - Improved comments explaining empty dependency arrays - Worker pool prevents concurrency limit violations - Type safety improvements for Background Sync API Refs: PR #154 review comments #2532254365-2532284285
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 11 out of 12 changed files in this pull request and generated 7 comments.
Critical fixes: - Change placeholder uploadSucceeded to true to prevent retry exhaustion during testing (Comment #2532671518: false would mark all files failed after 5 syncs) Documentation improvements: - Add detailed schema documentation in storeFileInQueue with all fields listed (Comment #2532671538: Document duplicated schema to aid sync verification) - Clarify exponential backoff comment about retry 0 meaning first attempt after failure (Comment #2532671525: 'first retry' was misleading) - Document design decision to only upload when window clients exist (Comment #2532671535: Prevents uploads without user context/auth) - Add note about DB connection opened per call (acceptable for 1-3 files) (Comment #2532671520: Future optimization opportunity documented) Code simplifications: - Remove redundant instance-level sync check (prototype check sufficient) (Comment #2532671531: Prototype check guarantees instance has property) - Fix ESLint disable comment to use correct rule name (Comment #2532671530: react-hooks/set-state-in-effect not set-state-in-effect) All changes maintain test coverage and fix issues identified in second Copilot review.
Summary
Implements IndexedDB-based persistent file queue for offline-first file uploads, replacing sessionStorage with IndexedDB for robust offline support.
Changes
Core Infrastructure
fileQueuetable with upload states (pending, uploading, failed, completed)File Queue Utilities (
src/lib/fileQueue.ts)addFileToQueue()- Store files persistently in IndexedDBgetPendingFiles()/getFailedFiles()- Query queue by stateupdateFileUploadState()- Track upload progressretryFileUpload()- Exponential backoff (1s → 16s, max 5 retries)processFileQueue()- Batch upload processing with statsclearCompletedUploads()- Queue cleanupgetStorageQuota()- Monitor IndexedDB usageService Worker Integration (
src/sw.ts)React Hook (
src/hooks/useFileQueue.ts)pending,failed,allFilesarrays auto-updateprocessQueue(),clearCompleted(),deleteFile()Migration (
src/hooks/useShareTarget.ts)idfield toSharedFileinterface (IndexedDB queue ID)Testing
Dependencies
idb@^8.0.2for Service Worker IndexedDB accessBenefits
Future Work
POST /api/v1/secrets/{id}/files)Related
Checklist