From b277b1f8f0f61b7161b9b753ae797a3ff1750d25 Mon Sep 17 00:00:00 2001 From: Fredrik Wahlberg Date: Fri, 31 Jan 2025 18:59:16 +0100 Subject: [PATCH] Enhance task synchronization and logging; update service worker caching strategy --- public/js/main.js | 26 +++++++++++++++++++++----- public/service-worker.js | 36 +++++++++++++++++++++++------------- routes/tasks.js | 2 +- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/public/js/main.js b/public/js/main.js index 784808c..e719a28 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1,6 +1,7 @@ import { checkSession, login, logout } from './auth.js'; import { saveTask } from './tasks.js'; import { saveTags, loadTags } from './tags.js'; +import { idb } from './utils.js'; if ('serviceWorker' in navigator) { window.addEventListener('load', () => { @@ -32,8 +33,7 @@ if ('serviceWorker' in navigator) { }); } - -document.addEventListener('DOMContentLoaded', function() { +document.addEventListener('DOMContentLoaded', async function() { const loginForm = document.getElementById('loginForm'); const loginContainer = document.getElementById('loginContainer'); const appContainer = document.getElementById('appContainer'); @@ -55,12 +55,13 @@ document.addEventListener('DOMContentLoaded', function() { const tomorrow = new Date(today); tomorrow.setDate(today.getDate() + 1); - // Initialize datepicker with tomorrow as the default date + // Initialize datepicker with tomorrow as the default date and disable past dates M.Datepicker.init(document.querySelectorAll('.datepicker'), { format: 'yyyy-mm-dd', defaultDate: tomorrow, setDefaultDate: true, - firstDay: 1 + firstDay: 1, + minDate: today // Disable past dates }); // Initialize timepicker @@ -187,7 +188,7 @@ document.addEventListener('DOMContentLoaded', function() { // Save task to server or IndexedDB if offline try { const data = await saveTask(taskData); - document.getElementById('responseMessage').textContent = "Task saved successfully!"; + document.getElementById('responseMessage').textContent = data.message; taskForm.reset(); // Reset the form after saving the task } catch (error) { if (error.status === 401) { @@ -199,4 +200,19 @@ document.addEventListener('DOMContentLoaded', function() { } } }); + + // Synchronize tasks when back online + window.addEventListener('online', async () => { + const db = await idb.openDB('org-todo-pwa', 1); + const tasks = await db.getAll('tasks'); + for (const task of tasks) { + try { + await saveTask(task); + await db.delete('tasks', task.id); + console.log(`Task synchronized: ${task.subject}`); + } catch (error) { + console.error('Error synchronizing task:', error); + } + } + }); }); \ No newline at end of file diff --git a/public/service-worker.js b/public/service-worker.js index 8ac9553..1f0284e 100644 --- a/public/service-worker.js +++ b/public/service-worker.js @@ -2,9 +2,14 @@ const CACHE_NAME = 'org-todo-pwa-cache-v1'; const urlsToCache = [ '/', '/index.html', - '/style.css', - '/app.js', - '/manifest.json' + '/css/style.css', + '/js/auth.js', + '/js/tasks.js', + '/js/tags.js', + '/js/utils.js', + '/js/main.js', + 'https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css', + 'https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js' ]; self.addEventListener('install', event => { @@ -14,22 +19,21 @@ self.addEventListener('install', event => { return cache.addAll(urlsToCache); }) ); - self.skipWaiting(); // Force the waiting service worker to become the active service worker }); self.addEventListener('activate', event => { + const cacheWhitelist = [CACHE_NAME]; event.waitUntil( caches.keys().then(cacheNames => { return Promise.all( cacheNames.map(cacheName => { - if (cacheName !== CACHE_NAME) { + if (!cacheWhitelist.includes(cacheName)) { return caches.delete(cacheName); } }) ); }) ); - self.clients.claim(); // Take control of all clients immediately }); self.addEventListener('fetch', event => { @@ -39,13 +43,19 @@ self.addEventListener('fetch', event => { if (response) { return response; } - return fetch(event.request); + return fetch(event.request).then( + response => { + if (!response || response.status !== 200 || response.type !== 'basic') { + return response; + } + const responseToCache = response.clone(); + caches.open(CACHE_NAME) + .then(cache => { + cache.put(event.request, responseToCache); + }); + return response; + } + ); }) ); }); - -self.addEventListener('message', event => { - if (event.data === 'skipWaiting') { - self.skipWaiting(); - } -}); diff --git a/routes/tasks.js b/routes/tasks.js index 026ff9e..742c93b 100644 --- a/routes/tasks.js +++ b/routes/tasks.js @@ -52,13 +52,13 @@ router.post('/add-task', auth, async (req, res) => { - State "TODO" from "TODO" [${currentDateTime}] :END: `; - logger.info(`Task added: ${orgFormattedData}`); } const filePath = path.join(dataDir, 'tasks.org'); try { await fs.promises.appendFile(filePath, orgFormattedData); res.json({ message: 'Task added successfully' }); + logger.info(`Task added: ${orgFormattedData}`); } catch (error) { logger.error('Error writing to tasks.org file:', error); res.status(500).json({ message: 'Error adding task' });