diff --git a/public/sw.js b/public/sw.js
index ee4e211..4c918f5 100644
--- a/public/sw.js
+++ b/public/sw.js
@@ -1,4 +1,4 @@
-const CACHE_NAME = 'brew-journal-v1';
+const CACHE_NAME = 'brew-journal-v2';
// Static assets to cache immediately
const PRECACHE_ASSETS = [
@@ -20,7 +20,7 @@ self.addEventListener('install', (e) => {
self.skipWaiting();
});
-// Activate Event
+// Activate Event — clean old caches and notify clients of update
self.addEventListener('activate', (e) => {
e.waitUntil(
caches.keys().then((keys) => {
@@ -31,9 +31,18 @@ self.addEventListener('activate', (e) => {
}
})
);
+ }).then(() => {
+ // Take control of all open clients immediately
+ return self.clients.claim();
+ }).then(() => {
+ // Notify all clients that a new version is now active
+ return self.clients.matchAll({ type: 'window' }).then((clients) => {
+ clients.forEach((client) => {
+ client.postMessage({ type: 'SW_UPDATED' });
+ });
+ });
})
);
- self.clients.claim();
});
// Fetch Event
@@ -46,7 +55,23 @@ self.addEventListener('fetch', (e) => {
return;
}
- // Stale-while-revalidate for static assets
+ // Network-first for HTML documents so users always get fresh markup
+ if (e.request.mode === 'navigate') {
+ e.respondWith(
+ fetch(e.request).then((networkResponse) => {
+ const responseToCache = networkResponse.clone();
+ caches.open(CACHE_NAME).then((cache) => {
+ cache.put(e.request, responseToCache);
+ });
+ return networkResponse;
+ }).catch(() => {
+ return caches.match(e.request);
+ })
+ );
+ return;
+ }
+
+ // Stale-while-revalidate for other static assets
e.respondWith(
caches.match(e.request).then((cachedResponse) => {
if (cachedResponse) {
@@ -54,11 +79,11 @@ self.addEventListener('fetch', (e) => {
fetch(e.request).then((networkResponse) => {
if (networkResponse.status === 200) {
caches.open(CACHE_NAME).then((cache) => {
- cache.put(e.request, networkResponse);
+ cache.put(e.request, networkResponse.clone());
});
}
}).catch(() => {/* Ignore network errors offline */});
-
+
return cachedResponse;
}
@@ -67,12 +92,12 @@ self.addEventListener('fetch', (e) => {
if (!networkResponse || networkResponse.status !== 200 || networkResponse.type !== 'basic') {
return networkResponse;
}
-
+
const responseToCache = networkResponse.clone();
caches.open(CACHE_NAME).then((cache) => {
cache.put(e.request, responseToCache);
});
-
+
return networkResponse;
});
})
diff --git a/src/App.jsx b/src/App.jsx
index 03cc4bf..dcefd79 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -14,6 +14,7 @@ import BeanDetail from "./components/BeanDetail";
import BrewCard from "./components/BrewCard";
import BottomNav from "./components/BottomNav";
import IosPromptModal from "./components/IosPromptModal";
+import UpdatePrompt from "./components/UpdatePrompt";
// Import constants
@@ -343,6 +344,9 @@ export default function CoffeeLogbook() {
{/* iOS/iPadOS PWA Install Prompt */}