Skip to content

Commit 4747bf0

Browse files
author
bndct-devops
committed
feat: service worker — offline cache, rest-timer notifications, auto-reload on deploy
- public/sw.js: offline-first caching (static cache-first, API network-first, navigation network-first); SCHEDULE/CANCEL_NOTIFICATION for rest timer; notificationclick focuses/opens app - main.jsx: SW update detection via updatefound + statechange reloads clients automatically when new SW activates (zero-stale deploys) - nginx: sw.js and index.html both served with no-store/no-cache; hashed assets keep public+immutable; sw.js exempt from broad .js immutable rule - TODO.md: changelog + removed SW and per-exercise chart from pending list
1 parent e039ae7 commit 4747bf0

3 files changed

Lines changed: 40 additions & 3 deletions

File tree

TODO.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,26 @@
5555
---
5656

5757
## Possible next things
58-
- Service Worker for proper offline support + background push notifications (fixes iOS ding limitation)
58+
59+
### Infrastructure
60+
- **v1.0.1 release** — merge `dev``main`, tag, write release notes (icon fix, Lifty theme, PWA polish, auth, refactor, etc.); update hardcoded `v1.0.0` strings in App.jsx (about modal pill + release link)
61+
- **CI: explicit frontend build job** — add `npm ci && npm run build` step before `build-and-push` for faster, cheaper feedback on broken JSX/imports (currently only caught inside the Docker build)
62+
63+
### UX / missing features
64+
- **JSON backup / restore** — full JSON dump of exercises + workouts + sets per profile; CSV export exists but can't be re-imported; needed for migrating between instances
65+
- **System theme auto-follow** — "System" option in theme picker reads `prefers-color-scheme` and maps to Light/Dark; single media query, no backend change
66+
- **Exercise picker: body part filter chips** — horizontal scrollable chip row (All / Chest / Back / …) above the list in the add-exercise bottom sheet; reuses existing filter logic from Exercises tab
67+
- **Exercise picker: inline "New" quick-create** — "+ New Exercise" button inside the picker sheet; opens a small inline form (name + body part) so users don't have to exit the workout to create a custom exercise
68+
69+
### Tech debt
70+
- **Frontend Vitest tests** — no component tests exist; add Vitest + React Testing Library; smoke tests for `fmtWeight`, `parseWeight`, set-log flow would catch regressions
71+
72+
---
73+
## Changelog (6 Mar 2026)
74+
- **Per-exercise 1RM chart** — tap any PR row in the Progress tab → BottomSheet with pure SVG line chart (accent fill + polyline, date labels) + recent-sessions table (date · sets · top weight · est. 1RM); fetches `GET /api/exercises/{id}/history?limit=60`; `TrendingUp` icon hint on PR rows
75+
- **Service Worker** (`public/sw.js`) — offline-first caching: static assets cache-first, API network-first with stale-cache fallback, navigation network-first; background rest-timer: page posts `SCHEDULE_NOTIFICATION`/`CANCEL_NOTIFICATION`, SW fires `showNotification` via `setTimeout` wrapped in `e.waitUntil`; `notificationclick` focuses existing window or opens `/`; SW works on non-EU iOS 16.4+ for locked-screen rest-timer ding
76+
- **SW update → auto-reload** (`main.jsx`) — `updatefound` + `statechange` listener reloads all open clients when a new SW version activates, so deploys propagate immediately
77+
- **nginx cache strategy**`index.html` + `sw.js` served with `no-store, no-cache, must-revalidate`; hashed JS/CSS/assets served with `public, immutable` (1 year); `sw.js` exempt from the broad `.js` immutable rule via an earlier `location = /sw.js` block
5978

6079
---
6180
## Changelog (2 Mar 2026) — session 3

frontend/nginx.conf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ server {
1010
try_files $uri /index.html;
1111
}
1212

13+
# Service worker must never be cached — browser needs to re-fetch it to detect updates
14+
location = /sw.js {
15+
add_header Cache-Control "no-store, no-cache, must-revalidate";
16+
try_files $uri =404;
17+
}
18+
1319
# Hashed assets (JS/CSS) — cache forever, cache-busting is in the filename
1420
location ~* \.(js|css|woff2?|png|svg|ico|webmanifest)$ {
1521
expires 1y;

frontend/src/main.jsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,22 @@ import { createRoot } from 'react-dom/client'
33
import App from './App'
44
import './styles.css'
55

6-
// Register service worker
6+
// Register service worker + auto-reload when a new version activates
77
if ('serviceWorker' in navigator) {
88
window.addEventListener('load', () => {
9-
navigator.serviceWorker.register('/sw.js').catch(() => {})
9+
navigator.serviceWorker.register('/sw.js').then(reg => {
10+
// When a new SW is found, wait for it to install then reload all clients
11+
reg.addEventListener('updatefound', () => {
12+
const next = reg.installing
13+
if (!next) return
14+
next.addEventListener('statechange', () => {
15+
// 'installed' + existing controller = update ready, reload to activate
16+
if (next.state === 'installed' && navigator.serviceWorker.controller) {
17+
window.location.reload()
18+
}
19+
})
20+
})
21+
}).catch(() => {})
1022
})
1123
}
1224

0 commit comments

Comments
 (0)