import { fetchAllTasks, getTokenForSource, completeTask, deleteTask } from './apis/index.js'; import { getSettings, getStoredTasks, setStoredTasks, setLastSync, addDismissedTask, addArchivedTask } from './storage.js'; import { getReviewTasks, getAutoArchiveCandidates } from './scoring.js'; // Background service worker for TaskDecay // Handles periodic sync, badge count, and alarm-based review reminders chrome.runtime.onInstalled.addListener(() => { chrome.alarms.create('taskdecay-sync', { periodInMinutes: 60 }); chrome.alarms.create('taskdecay-review-reminder', { periodInMinutes: 60 * 24 }); updateBadge(); }); chrome.alarms.onAlarm.addListener(async (alarm) => { if (alarm.name === 'taskdecay-sync') { await syncTasks(); } else if (alarm.name === 'taskdecay-review-reminder') { await checkReviewReminder(); } }); chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { const msg = message; if (msg.type === 'sync') { syncTasks().then(sendResponse).catch(e => sendResponse({ error: e.message })); return true; } if (msg.type === 'getReviewTasks') { getReviewData().then(sendResponse).catch(e => sendResponse({ error: e.message })); return true; } if (msg.type === 'dismiss') { addDismissedTask(msg.taskId).then(() => sendResponse({ ok: true })).catch(e => sendResponse({ error: e.message })); return true; } if (msg.type === 'archive') { archiveTask(msg.taskId).then(() => sendResponse({ ok: true })).catch(e => sendResponse({ error: e.message })); return true; } if (msg.type === 'complete') { completeTaskById(msg.taskId).then(() => sendResponse({ ok: true })).catch(e => sendResponse({ error: e.message })); return true; } if (msg.type === 'delete') { deleteTaskById(msg.taskId).then(() => sendResponse({ ok: true })).catch(e => sendResponse({ error: e.message })); return true; } if (msg.type === 'openReview') { chrome.tabs.create({ url: chrome.runtime.getURL('src/review.html') }); sendResponse({ ok: true }); return true; } }); async function syncTasks() { const settings = await getSettings(); const tasks = await fetchAllTasks(settings.providers); await setStoredTasks(tasks); await setLastSync(new Date().toISOString()); await updateBadge(); return { count: tasks.length, syncedAt: new Date().toISOString() }; } async function getReviewData() { const settings = await getSettings(); const tasks = await getStoredTasks(); const review = getReviewTasks(tasks, settings); const autoArchive = getAutoArchiveCandidates(tasks, settings); return { review, autoArchiveCount: autoArchive.length, total: tasks.length }; } async function archiveTask(taskId) { const settings = await getSettings(); const tasks = await getStoredTasks(); const task = tasks.find(t => t.id === taskId); if (!task) throw new Error('Task not found'); const token = getTokenForSource(task.source, settings.providers); if (!token) throw new Error('No token for source'); await completeTask(task, token); await addArchivedTask(taskId); await updateBadge(); } async function completeTaskById(taskId) { const settings = await getSettings(); const tasks = await getStoredTasks(); const task = tasks.find(t => t.id === taskId); if (!task) throw new Error('Task not found'); const token = getTokenForSource(task.source, settings.providers); if (!token) throw new Error('No token for source'); await completeTask(task, token); await addArchivedTask(taskId); await updateBadge(); } async function deleteTaskById(taskId) { const settings = await getSettings(); const tasks = await getStoredTasks(); const task = tasks.find(t => t.id === taskId); if (!task) throw new Error('Task not found'); const token = getTokenForSource(task.source, settings.providers); if (!token) throw new Error('No token for source'); await deleteTask(task, token); await addArchivedTask(taskId); await updateBadge(); } async function checkReviewReminder() { const settings = await getSettings(); if (!settings.lastReviewDate) return; const last = new Date(settings.lastReviewDate); const now = new Date(); const daysSince = (now.getTime() - last.getTime()) / (1000 * 60 * 60 * 24); if (daysSince >= settings.reviewIntervalDays) { const tasks = await getStoredTasks(); const review = getReviewTasks(tasks, settings); if (review.length > 0) { chrome.notifications.create('review-reminder', { type: 'basic', iconUrl: 'icon128.png', title: 'TaskDecay Review Ready', message: `${review.length} tasks need your attention. Open the review session to clean your backlog.`, buttons: [{ title: 'Start Review' }], }); } } } async function updateBadge() { try { const settings = await getSettings(); const tasks = await getStoredTasks(); const review = getReviewTasks(tasks, settings); const count = review.length; chrome.action.setBadgeText({ text: count > 0 ? String(count) : '' }); chrome.action.setBadgeBackgroundColor({ color: '#e74c3c' }); } catch { chrome.action.setBadgeText({ text: '' }); } } chrome.notifications.onButtonClicked.addListener((notificationId) => { if (notificationId === 'review-reminder') { chrome.tabs.create({ url: chrome.runtime.getURL('src/review.html') }); } });