From 8947fe63b8b39996cb96b8dd7c61eeb9cd645c26 Mon Sep 17 00:00:00 2001
From: Bappadala Rohith Kumar Naidu
<198095685+rohitkumarnaidu@users.noreply.github.com>
Date: Mon, 27 Jul 2026 18:45:34 +0530
Subject: [PATCH 1/2] feat: Mobile Apps (iOS/Android) via PWA (Issue #165)
---
public/index.html | 3 +
public/manifest.json | 25 ++++++++
public/sw.js | 138 +++++++++----------------------------------
3 files changed, 56 insertions(+), 110 deletions(-)
create mode 100644 public/manifest.json
diff --git a/public/index.html b/public/index.html
index eae5562..8e9f68e 100644
--- a/public/index.html
+++ b/public/index.html
@@ -4,6 +4,9 @@
CareSync
+
+
+
diff --git a/public/manifest.json b/public/manifest.json
new file mode 100644
index 0000000..187071b
--- /dev/null
+++ b/public/manifest.json
@@ -0,0 +1,25 @@
+{
+ "short_name": "CareSync",
+ "name": "CareSync Health Tracker",
+ "icons": [
+ {
+ "src": "favicon.ico",
+ "sizes": "64x64 32x32 24x24 16x16",
+ "type": "image/x-icon"
+ },
+ {
+ "src": "logo192.png",
+ "type": "image/png",
+ "sizes": "192x192"
+ },
+ {
+ "src": "logo512.png",
+ "type": "image/png",
+ "sizes": "512x512"
+ }
+ ],
+ "start_url": ".",
+ "display": "standalone",
+ "theme_color": "#000000",
+ "background_color": "#ffffff"
+}
diff --git a/public/sw.js b/public/sw.js
index 2eacde5..18a6052 100644
--- a/public/sw.js
+++ b/public/sw.js
@@ -1,124 +1,42 @@
-/* eslint-disable no-restricted-globals */
-
-const CACHE_NAME = 'caresync-cache-v1';
-
-/**
- * CareSync Service Worker — medicine reminder notifications & offline caching.
- *
- * Handles incoming `showNotification` calls from the main thread and
- * reacts to notification clicks by navigating to the Medicine Tracker.
- * Also caches static assets and API responses for offline use.
- *
- * Registered in `src/index.js` via `navigator.serviceWorker.register`.
- *
- * @file public/sw.js
- */
-
-/**
- * notificationclick — fired when the user taps/clicks a notification.
- */
-globalThis.addEventListener('notificationclick', (event) => {
- event.notification.close();
-
- const targetUrl = '/medicine-tracker';
-
+const CACHE_NAME = 'caresync-v1';
+const urlsToCache = [
+ '/',
+ '/index.html',
+ '/manifest.json'
+];
+
+self.addEventListener('install', event => {
event.waitUntil(
- globalThis.clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => {
- // Try to focus an existing tab that already has the app open.
- for (const client of clientList) {
- const rawPath = new URL(client.url).pathname;
- const clientPath = rawPath.length > 1 && rawPath.endsWith('/') ? rawPath.slice(0, -1) : rawPath;
- if (clientPath === targetUrl && 'focus' in client) {
- return client.focus();
- }
- }
- // No matching tab — open a new one.
- if (globalThis.clients.openWindow) {
- return globalThis.clients.openWindow(targetUrl);
- }
- })
+ caches.open(CACHE_NAME)
+ .then(cache => {
+ return cache.addAll(urlsToCache);
+ })
);
});
-globalThis.addEventListener('install', (event) => {
- globalThis.skipWaiting();
+self.addEventListener('fetch', event => {
+ event.respondWith(
+ caches.match(event.request)
+ .then(response => {
+ if (response) {
+ return response;
+ }
+ return fetch(event.request);
+ })
+ );
});
-/**
- * activate — claim all open clients immediately and clean up old caches.
- */
-globalThis.addEventListener('activate', (event) => {
+self.addEventListener('activate', event => {
+ const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
- caches.keys().then((cacheNames) => {
+ caches.keys().then(cacheNames => {
return Promise.all(
- cacheNames.map((cacheName) => {
- if (cacheName !== CACHE_NAME) {
+ cacheNames.map(cacheName => {
+ if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
- }).then(() => globalThis.clients.claim())
- );
-});
-
-/**
- * fetch — intercept network requests for offline caching.
- */
-globalThis.addEventListener('fetch', (event) => {
- const { request } = event;
-
- // Handle API GET requests (Network First strategy)
- if (request.url.includes('/api/') && request.method === 'GET') {
- event.respondWith(
- fetch(request)
- .then((response) => {
- if (response.status === 200) {
- const responseClone = response.clone();
- caches.open(CACHE_NAME).then((cache) => cache.put(request, responseClone));
- }
- return response;
- })
- .catch(() => {
- return caches.match(request).then((cachedResponse) => {
- if (cachedResponse) return cachedResponse;
- return new Response(
- JSON.stringify({ error: 'Offline', message: 'No cached data available' }),
- { status: 503, headers: { 'Content-Type': 'application/json' } }
- );
- });
- })
- );
- return;
- }
-
- // Skip other non-GET requests (handled by axios interceptor in api.js)
- if (request.method !== 'GET') return;
-
- // Static Assets (Network First with Cache Fallback)
- event.respondWith(
- fetch(request)
- .then((response) => {
- if (response.status === 200 || response.type === 'opaque') {
- const responseClone = response.clone();
- caches.open(CACHE_NAME).then((cache) => cache.put(request, responseClone));
- }
- return response;
- })
- .catch(() => {
- return caches.match(request).then((cachedResponse) => {
- if (cachedResponse) return cachedResponse;
-
- // For navigation requests, fallback to index.html if offline
- if (request.mode === 'navigate') {
- return caches.match('/index.html').then(idxResp => {
- if (idxResp) return idxResp;
- return new Response('Offline. App could not be loaded.', { status: 503, headers: { 'Content-Type': 'text/html' } });
- });
- }
-
- // Generic fallback to avoid TypeError: Failed to convert value to 'Response'
- return new Response('', { status: 503, statusText: 'Service Unavailable (Offline)' });
- });
- })
+ })
);
});
From 64eb65f0fc722e0fb4fc5ce3767ba1c0607dfa2d Mon Sep 17 00:00:00 2001
From: Bappadala Rohith Kumar Naidu
<198095685+rohitkumarnaidu@users.noreply.github.com>
Date: Mon, 27 Jul 2026 18:59:32 +0530
Subject: [PATCH 2/2] fix: CodeRabbit suggestions for service worker caching
and notifications
---
public/sw.js | 36 +++++++++++++++++++++++++++++++++++-
1 file changed, 35 insertions(+), 1 deletion(-)
diff --git a/public/sw.js b/public/sw.js
index 18a6052..dcf5681 100644
--- a/public/sw.js
+++ b/public/sw.js
@@ -15,13 +15,27 @@ self.addEventListener('install', event => {
});
self.addEventListener('fetch', event => {
+ if (event.request.method !== 'GET') return;
+
event.respondWith(
caches.match(event.request)
.then(response => {
if (response) {
return response;
}
- return fetch(event.request);
+
+ return fetch(event.request).then(networkResponse => {
+ if (!networkResponse || networkResponse.status !== 200 || networkResponse.type !== 'basic') {
+ return networkResponse;
+ }
+ const responseToCache = networkResponse.clone();
+ caches.open(CACHE_NAME).then(cache => {
+ cache.put(event.request, responseToCache);
+ });
+ return networkResponse;
+ }).catch(() => {
+ // Fallback if needed, e.g., return offline page
+ });
})
);
});
@@ -40,3 +54,23 @@ self.addEventListener('activate', event => {
})
);
});
+
+self.addEventListener('notificationclick', event => {
+ event.notification.close();
+
+ event.waitUntil(
+ clients.matchAll({ type: 'window' }).then(windowClients => {
+ // Check if there is already a window/tab open with the target URL
+ for (let i = 0; i < windowClients.length; i++) {
+ const client = windowClients[i];
+ if (client.url.includes(self.registration.scope) && 'focus' in client) {
+ return client.focus();
+ }
+ }
+ // If no window is open, open a new one
+ if (clients.openWindow) {
+ return clients.openWindow('/');
+ }
+ })
+ );
+});