From d4edf69f2cea2515a00435ee974116948057148d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Sep 2025 19:50:40 +0000 Subject: [PATCH] feat(frontend): Add version-based cache invalidation for PWA updates Co-authored-by: elisiariocouto <818914+elisiariocouto@users.noreply.github.com> --- frontend/src/hooks/usePWA.ts | 29 +++++++++++++++++++ frontend/src/hooks/useVersionCheck.ts | 40 +++++++++++++++++++++++++++ frontend/src/routes/__root.tsx | 6 +++- 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 frontend/src/hooks/useVersionCheck.ts diff --git a/frontend/src/hooks/usePWA.ts b/frontend/src/hooks/usePWA.ts index 4325acf..6ea8931 100644 --- a/frontend/src/hooks/usePWA.ts +++ b/frontend/src/hooks/usePWA.ts @@ -3,6 +3,7 @@ import { useEffect, useState } from "react"; interface PWAUpdate { updateAvailable: boolean; updateSW: () => Promise; + forceReload: () => Promise; } export function usePWA(): PWAUpdate { @@ -11,6 +12,33 @@ export function usePWA(): PWAUpdate { () => async () => {}, ); + const forceReload = async (): Promise => { + try { + // Clear all caches + if ('caches' in window) { + const cacheNames = await caches.keys(); + await Promise.all( + cacheNames.map(cacheName => caches.delete(cacheName)) + ); + console.log("All caches cleared"); + } + + // Unregister service worker + if ('serviceWorker' in navigator) { + const registrations = await navigator.serviceWorker.getRegistrations(); + await Promise.all(registrations.map(registration => registration.unregister())); + console.log("All service workers unregistered"); + } + + // Force reload + window.location.reload(); + } catch (error) { + console.error("Error during force reload:", error); + // Fallback: just reload the page + window.location.reload(); + } + }; + useEffect(() => { // Check if SW registration is available if ("serviceWorker" in navigator) { @@ -37,5 +65,6 @@ export function usePWA(): PWAUpdate { return { updateAvailable, updateSW, + forceReload, }; } diff --git a/frontend/src/hooks/useVersionCheck.ts b/frontend/src/hooks/useVersionCheck.ts new file mode 100644 index 0000000..c304b8c --- /dev/null +++ b/frontend/src/hooks/useVersionCheck.ts @@ -0,0 +1,40 @@ +import { useEffect } from "react"; +import { useQuery } from "@tanstack/react-query"; +import { apiClient } from "../lib/api"; + +const VERSION_STORAGE_KEY = "leggen_app_version"; + +export function useVersionCheck(forceReload: () => Promise) { + const { + data: healthStatus, + isSuccess: healthSuccess, + } = useQuery({ + queryKey: ["health"], + queryFn: apiClient.getHealth, + refetchInterval: 30000, + retry: false, + staleTime: 0, // Always consider data stale to ensure fresh version checks + }); + + useEffect(() => { + if (healthSuccess && healthStatus?.version) { + const currentVersion = healthStatus.version; + const storedVersion = localStorage.getItem(VERSION_STORAGE_KEY); + + if (storedVersion && storedVersion !== currentVersion) { + console.log(`Version mismatch detected: stored=${storedVersion}, current=${currentVersion}`); + console.log("Clearing cache and reloading..."); + + // Update stored version first + localStorage.setItem(VERSION_STORAGE_KEY, currentVersion); + + // Force reload to clear cache + forceReload(); + } else if (!storedVersion) { + // First time loading, store the version + localStorage.setItem(VERSION_STORAGE_KEY, currentVersion); + console.log(`Version stored: ${currentVersion}`); + } + } + }, [healthSuccess, healthStatus?.version, forceReload]); +} \ No newline at end of file diff --git a/frontend/src/routes/__root.tsx b/frontend/src/routes/__root.tsx index 9153646..8640530 100644 --- a/frontend/src/routes/__root.tsx +++ b/frontend/src/routes/__root.tsx @@ -3,10 +3,14 @@ import { AppSidebar } from "../components/AppSidebar"; import { SiteHeader } from "../components/SiteHeader"; import { PWAInstallPrompt, PWAUpdatePrompt } from "../components/PWAPrompts"; import { usePWA } from "../hooks/usePWA"; +import { useVersionCheck } from "../hooks/useVersionCheck"; import { SidebarInset, SidebarProvider } from "../components/ui/sidebar"; function RootLayout() { - const { updateAvailable, updateSW } = usePWA(); + const { updateAvailable, updateSW, forceReload } = usePWA(); + + // Check for version mismatches and force reload if needed + useVersionCheck(forceReload); const handlePWAInstall = () => { console.log("PWA installed successfully");