diff --git a/src/components/PwaUpdater.tsx b/src/components/PwaUpdater.tsx
new file mode 100644
index 0000000..d86cae6
--- /dev/null
+++ b/src/components/PwaUpdater.tsx
@@ -0,0 +1,110 @@
+import { type Component, createEffect, onCleanup } from 'solid-js';
+import { useRegisterSW } from 'virtual:pwa-register/solid';
+
+// We export a function so other parts of the app can manually trigger an update
+let manualUpdateFn: (() => void) | undefined;
+
+export const forceServiceWorkerUpdate = () => {
+ if (manualUpdateFn) {
+ manualUpdateFn();
+ } else {
+ console.warn("PWA Updater not initialized yet");
+ }
+};
+
+export const PwaUpdater: Component = () => {
+ let registration: ServiceWorkerRegistration | undefined;
+
+ const {
+ needRefresh: [needRefresh],
+ updateServiceWorker,
+ } = useRegisterSW({
+ onRegistered(r: ServiceWorkerRegistration | undefined) {
+ console.log('SW Registered:', r);
+ registration = r;
+
+ // Periodically check for updates (every 1 hour)
+ if (r) {
+ setInterval(() => {
+ console.log('Periodic SW update check...');
+ r.update();
+ }, 60 * 60 * 1000);
+ }
+ },
+ onRegisterError(error: Error | any) {
+ console.error('SW registration error', error);
+ },
+ });
+
+ createEffect(() => {
+ // Expose function globally
+ manualUpdateFn = () => {
+ if (needRefresh()) {
+ console.log('User manually forced an update. Reloading now...');
+ updateServiceWorker(true);
+ } else if (registration) {
+ console.log('User manually asked to check for updates...');
+ registration.update();
+ }
+ };
+ });
+
+ createEffect(() => {
+ // Check for updates when the app regains focus/visibility
+ const handleVisibilityFocus = () => {
+ if (document.visibilityState === 'visible' && registration) {
+ console.log('App became visible, checking for SW updates...');
+ registration.update();
+ }
+ };
+
+ document.addEventListener('visibilitychange', handleVisibilityFocus);
+ window.addEventListener('focus', handleVisibilityFocus);
+
+ onCleanup(() => {
+ document.removeEventListener('visibilitychange', handleVisibilityFocus);
+ window.removeEventListener('focus', handleVisibilityFocus);
+ });
+ });
+
+ createEffect(() => {
+ if (needRefresh()) {
+ console.log('New PWA version available. Monitoring for idle/hidden state to update...');
+
+ let idleTimeout: number | undefined;
+
+ const triggerUpdate = () => {
+ console.log('Triggering PWA update...');
+ updateServiceWorker(true);
+ };
+
+ const resetIdle = () => {
+ if (idleTimeout) window.clearTimeout(idleTimeout);
+ // 10 minutes idle
+ idleTimeout = window.setTimeout(triggerUpdate, 10 * 60 * 1000);
+ };
+
+ const handleVisibility = () => {
+ if (document.visibilityState === 'hidden') {
+ triggerUpdate();
+ }
+ };
+
+ // Start idle timer
+ resetIdle();
+
+ // Listen for user activity
+ const events = ['mousedown', 'mousemove', 'keydown', 'scroll', 'touchstart'];
+ events.forEach(e => window.addEventListener(e, resetIdle, { passive: true }));
+ document.addEventListener('visibilitychange', handleVisibility);
+
+ onCleanup(() => {
+ if (idleTimeout) window.clearTimeout(idleTimeout);
+ events.forEach(e => window.removeEventListener(e, resetIdle));
+ document.removeEventListener('visibilitychange', handleVisibility);
+ });
+ }
+ });
+
+ return null;
+};
diff --git a/src/index.tsx b/src/index.tsx
index 81ed90b..9febf77 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -3,12 +3,14 @@ import './index.css'
import App from './App.tsx'
import { ThemeProvider } from './components/ThemeProvider'
import { Toaster } from './components/ui/sonner'
+import { PwaUpdater } from './components/PwaUpdater'
const root = document.getElementById('root')
render(() => {
return (