diff --git a/public/app.js b/public/app.js index b74bc14..d881df1 100644 --- a/public/app.js +++ b/public/app.js @@ -3,6 +3,24 @@ if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/service-worker.js') .then(registration => { console.log('ServiceWorker registration successful with scope: ', registration.scope); + + registration.onupdatefound = () => { + const installingWorker = registration.installing; + installingWorker.onstatechange = () => { + if (installingWorker.state === 'installed') { + if (navigator.serviceWorker.controller) { + // New update available + console.log('New content is available; please refresh.'); + if (confirm('New version available. Do you want to update?')) { + window.location.reload(); + } + } else { + // Content is cached for offline use + console.log('Content is cached for offline use.'); + } + } + }; + }; }) .catch(error => { console.log('ServiceWorker registration failed: ', error); diff --git a/public/service-worker.js b/public/service-worker.js index fb575a2..8ac9553 100644 --- a/public/service-worker.js +++ b/public/service-worker.js @@ -1,28 +1,51 @@ -self.addEventListener('install', (event) => { +const CACHE_NAME = 'org-todo-pwa-cache-v1'; +const urlsToCache = [ + '/', + '/index.html', + '/style.css', + '/app.js', + '/manifest.json' +]; + +self.addEventListener('install', event => { event.waitUntil( - caches.open('task-manager-cache').then((cache) => { - return cache.addAll([ - '/', - '/index.html', - '/style.css', - '/app.js', - '/manifest.json', - '/icons/icon-192x192.png', - '/icons/icon-512x512.png' - ]); + caches.open(CACHE_NAME) + .then(cache => { + return cache.addAll(urlsToCache); + }) + ); + self.skipWaiting(); // Force the waiting service worker to become the active service worker +}); + +self.addEventListener('activate', event => { + event.waitUntil( + caches.keys().then(cacheNames => { + return Promise.all( + cacheNames.map(cacheName => { + if (cacheName !== CACHE_NAME) { + return caches.delete(cacheName); + } + }) + ); }) ); + self.clients.claim(); // Take control of all clients immediately +}); + +self.addEventListener('fetch', event => { + event.respondWith( + caches.match(event.request) + .then(response => { + if (response) { + return response; + } + return fetch(event.request); + }) + ); }); -self.addEventListener('fetch', (event) => { - if (event.request.url.includes('/get-tags') || event.request.url.includes('/save-tags')) { - // Bypass cache for tags endpoints - event.respondWith(fetch(event.request)); - } else { - event.respondWith( - caches.match(event.request).then((cachedResponse) => { - return cachedResponse || fetch(event.request); - }) - ); +self.addEventListener('message', event => { + if (event.data === 'skipWaiting') { + self.skipWaiting(); } }); diff --git a/routes/tasks.js b/routes/tasks.js index 03daf64..2774af7 100644 --- a/routes/tasks.js +++ b/routes/tasks.js @@ -15,16 +15,30 @@ if (!fs.existsSync(dataDir)) { // Ensure the tags.json file exists const tagsFilePath = path.join(dataDir, 'tags.json'); if (!fs.existsSync(tagsFilePath)) { - fs.writeFileSync(tagsFilePath, JSON.stringify([]));} - + fs.writeFileSync(tagsFilePath, JSON.stringify([])); +} // Protect the /add-task endpoint with authentication router.post('/add-task', auth, async (req, res) => { const { subject, description, scheduled } = req.body; const currentDateTime = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, ''); + // Format the scheduled date and time for Org mode + const scheduledDate = new Date(scheduled); + const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; + const dayName = dayNames[scheduledDate.getUTCDay()]; + + let formattedScheduled; + if (scheduledDate.getUTCHours() === 0 && scheduledDate.getUTCMinutes() === 0) { + // No time provided, format without time + formattedScheduled = `<${scheduledDate.getUTCFullYear()}-${String(scheduledDate.getUTCMonth() + 1).padStart(2, '0')}-${String(scheduledDate.getUTCDate()).padStart(2, '0')} ${dayName}>`; + } else { + // Time provided, format with time + formattedScheduled = `<${scheduledDate.getUTCFullYear()}-${String(scheduledDate.getUTCMonth() + 1).padStart(2, '0')}-${String(scheduledDate.getUTCDate()).padStart(2, '0')} ${dayName} ${String(scheduledDate.getUTCHours()).padStart(2, '0')}:${String(scheduledDate.getUTCMinutes()).padStart(2, '0')}>`; + } + let orgFormattedData = `* TODO ${subject} - SCHEDULED: <${scheduled}> + SCHEDULED: ${formattedScheduled} :LOGBOOK: - State "TODO" from "TODO" [${currentDateTime}] :END: @@ -33,7 +47,7 @@ router.post('/add-task', auth, async (req, res) => { if (description) { orgFormattedData = `* TODO ${subject} ${description} - SCHEDULED: <${scheduled}> + SCHEDULED: ${formattedScheduled} :LOGBOOK: - State "TODO" from "TODO" [${currentDateTime}] :END: @@ -43,10 +57,10 @@ router.post('/add-task', auth, async (req, res) => { const filePath = path.join(dataDir, 'tasks.org'); try { await fs.promises.appendFile(filePath, orgFormattedData); - res.send({ message: 'Task added successfully!' }); - } catch (err) { - logger.error('Error writing to file:', err); - res.status(500).send('Error writing to file.'); + res.json({ message: 'Task added successfully' }); + } catch (error) { + logger.error('Error writing to tasks.org file:', error); + res.status(500).json({ message: 'Error adding task' }); } });