144 lines
5.6 KiB
JavaScript
144 lines
5.6 KiB
JavaScript
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') });
|
|
}
|
|
});
|