diff --git a/frontend/src/app/components/global_ui/InstallPrompt.tsx b/frontend/src/app/components/global_ui/InstallPrompt.tsx index deb1b5ec..b130e684 100644 --- a/frontend/src/app/components/global_ui/InstallPrompt.tsx +++ b/frontend/src/app/components/global_ui/InstallPrompt.tsx @@ -8,11 +8,24 @@ interface BeforeInstallPromptEvent extends Event { userChoice: Promise<{ outcome: "accepted" | "dismissed" }>; } +export const DISMISSED_STORAGE_KEY = "install-prompt-dismissed"; + +export const isInstallPromptDismissed = (): boolean => { + if (typeof window === "undefined") return false; + try { + return window.localStorage.getItem(DISMISSED_STORAGE_KEY) === "true"; + } catch { + return false; + } +}; + export function InstallPrompt() { const [deferredPrompt, setDeferredPrompt] = useState(null); const [dismissed, setDismissed] = useState(false); useEffect(() => { + setDismissed(isInstallPromptDismissed()); + const handler = (e: Event) => { e.preventDefault(); setDeferredPrompt(e as BeforeInstallPromptEvent); @@ -25,13 +38,22 @@ export function InstallPrompt() { if (!deferredPrompt || dismissed) return null; const handleInstall = async () => { - deferredPrompt.prompt(); + await deferredPrompt.prompt(); const { outcome } = await deferredPrompt.userChoice; if (outcome === "accepted") { setDeferredPrompt(null); } }; + const handleDismiss = () => { + setDismissed(true); + try { + window.localStorage.setItem(DISMISSED_STORAGE_KEY, "true"); + } catch { + // localStorage may be unavailable; dismissal still applies for this session. + } + }; + return (
@@ -47,7 +69,7 @@ export function InstallPrompt() {