diff --git a/src/background/service-worker.ts b/src/background/service-worker.ts index a9e3b71..baa527d 100644 --- a/src/background/service-worker.ts +++ b/src/background/service-worker.ts @@ -11,6 +11,7 @@ import { getAllPDFs, getPendingPDFs, updatePDFStatus, + dismissPDF, removePDF, cleanupOldEntries, PDFStatus, @@ -129,6 +130,19 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { return true; } + // Add a PDF to the queue (from popup discovery) + if (request.action === 'addPDF') { + addPDF(request.pdf).then(entry => { + if (entry) { + console.log('[Service Worker] PDF added via message:', entry.filename); + } + return updateBadge().then(() => entry); + }).then(entry => { + sendResponse({ entry }); + }); + return true; + } + // Legacy: Get only actionable PDFs if (request.action === 'getPDFQueue') { getPendingPDFs().then(entries => { @@ -148,6 +162,15 @@ chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { return true; } + if (request.action === 'dismissPDF') { + dismissPDF(request.id).then(() => { + return updateBadge(); + }).then(() => { + sendResponse({ success: true }); + }); + return true; + } + if (request.action === 'removePDF') { removePDF(request.id).then(() => { return updateBadge(); diff --git a/src/popup/popup.ts b/src/popup/popup.ts index 73b87ce..a7adbf3 100644 --- a/src/popup/popup.ts +++ b/src/popup/popup.ts @@ -153,22 +153,20 @@ async function loadPDFQueue() { console.log('[Popup] Loading PDF queue...'); // Get all PDFs from background script (including completed ones) - const response = await chrome.runtime.sendMessage({ action: 'getAllPDFs' }); + let response = await chrome.runtime.sendMessage({ action: 'getAllPDFs' }); pdfQueue = response?.entries || []; console.log('[Popup] Got', pdfQueue.length, 'entries from background'); - // Also check current tab for PDF + // Check current tab for PDF and add to persistent queue via background const currentTabPDF = await checkCurrentTabForPDF(); if (currentTabPDF) { - // Check if already in queue - const exists = pdfQueue.some(p => p.url === currentTabPDF.url); - if (!exists) { - console.log('[Popup] Adding current tab PDF to queue:', currentTabPDF.filename); - const entry: PDFQueueEntry = { - ...currentTabPDF, - status: 'pending' - }; - pdfQueue.unshift(entry); + // Add via background service (will check for duplicates/dismissed) + const addResult = await chrome.runtime.sendMessage({ + action: 'addPDF', + pdf: currentTabPDF + }); + if (addResult?.entry) { + console.log('[Popup] Added current tab PDF to persistent queue:', currentTabPDF.filename); } } @@ -177,16 +175,19 @@ async function loadPDFQueue() { console.log('[Popup] Queue empty, checking recent downloads...'); const recentPDFs = await checkRecentDownloads(); for (const pdf of recentPDFs) { - const exists = pdfQueue.some(p => p.url === pdf.url); - if (!exists) { - pdfQueue.push({ - ...pdf, - status: 'pending' - }); - } + // Add each PDF via background service (will check for duplicates/dismissed) + await chrome.runtime.sendMessage({ + action: 'addPDF', + pdf + }); } } + // Reload queue after potential additions + response = await chrome.runtime.sendMessage({ action: 'getAllPDFs' }); + pdfQueue = response?.entries || []; + console.log('[Popup] Final queue count:', pdfQueue.length); + // Render the list renderPDFList(); } @@ -757,7 +758,17 @@ async function handleRefreshStatus(id: string) { * Handle dismiss PDF */ async function handleDismissPDF(id: string) { - await chrome.runtime.sendMessage({ action: 'removePDF', id }); + const pdf = pdfQueue.find(p => p.id === id); + if (!pdf) return; + + // For pending/failed/in_basket items, mark as dismissed to prevent re-showing + // For completed items (sent/canceled), actually remove from storage + if (pdf.status === 'sent' || pdf.status === 'canceled') { + await chrome.runtime.sendMessage({ action: 'removePDF', id }); + } else { + await chrome.runtime.sendMessage({ action: 'dismissPDF', id }); + } + pdfQueue = pdfQueue.filter(p => p.id !== id); renderPDFList(); } diff --git a/src/utils/pdf-queue.ts b/src/utils/pdf-queue.ts index 0606c31..ea353e7 100644 --- a/src/utils/pdf-queue.ts +++ b/src/utils/pdf-queue.ts @@ -11,6 +11,7 @@ const STORAGE_KEY = 'pdfQueue'; const MAX_ENTRIES = 50; const SENT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days const FAILED_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours +const DISMISSED_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days export type PDFStatus = | 'pending' // Not yet uploaded @@ -20,7 +21,8 @@ export type PDFStatus = | 'ordering' // Order in progress | 'in_production' // PRODUCTION_QUEUE or PRINTING | 'sent' // SENT - terminal - | 'canceled'; // CANCELED - terminal + | 'canceled' // CANCELED - terminal + | 'dismissed'; // User dismissed, don't show again export interface PDFQueueEntry extends DetectedPDF { status: PDFStatus; @@ -70,10 +72,10 @@ export async function addPDF(pdf: DetectedPDF): Promise { // Check for duplicate by URL const existing = state.entries.find(e => e.url === pdf.url); if (existing) { - // Skip if already uploaded (in basket, production, or completed) - const uploadedStatuses: PDFStatus[] = ['in_basket', 'ordering', 'in_production', 'sent', 'canceled']; - if (uploadedStatuses.includes(existing.status)) { - console.log('[PDF Queue] PDF already uploaded, skipping:', pdf.filename); + // Skip if already processed (uploaded, dismissed, etc.) + const processedStatuses: PDFStatus[] = ['in_basket', 'ordering', 'in_production', 'sent', 'canceled', 'dismissed']; + if (processedStatuses.includes(existing.status)) { + console.log('[PDF Queue] PDF already processed, skipping:', pdf.filename, existing.status); return null; } console.log('[PDF Queue] PDF already in queue:', pdf.filename); @@ -160,7 +162,24 @@ export async function updatePDFStatus( } /** - * Remove a PDF from the queue + * Dismiss a PDF (mark as dismissed so it won't reappear) + */ +export async function dismissPDF(id: string): Promise { + const state = await loadQueue(); + const entry = state.entries.find(e => e.id === id); + + if (!entry) { + console.warn('[PDF Queue] PDF not found for dismissal:', id); + return; + } + + entry.status = 'dismissed'; + await saveQueue(state); + console.log('[PDF Queue] Dismissed PDF:', id); +} + +/** + * Remove a PDF from the queue (complete removal, used for cleanup) */ export async function removePDF(id: string): Promise { const state = await loadQueue(); @@ -177,11 +196,12 @@ export async function removePDF(id: string): Promise { } /** - * Get all PDFs for display in popup (all non-terminal statuses + recent terminal) + * Get all PDFs for display in popup (excludes dismissed) */ export async function getAllPDFs(): Promise { const state = await loadQueue(); - return state.entries; + // Filter out dismissed entries - they're kept for duplicate detection but not displayed + return state.entries.filter(e => e.status !== 'dismissed'); } /** @@ -254,6 +274,14 @@ export async function cleanupOldEntries(): Promise { } } + // Remove dismissed entries older than 7 days + if (entry.status === 'dismissed') { + const age = now - entry.timestamp; + if (age > DISMISSED_MAX_AGE_MS) { + return false; + } + } + return true; });