AUDIT / src /components /InstallPrompt.tsx
Arypulka98's picture
feat(audit): deploy full backend cluster node
aea470f verified
Raw
History Blame Contribute Delete
3.54 kB
/**
* InstallPrompt.tsx — X3: PWA install banner
* Appare dopo 45s di uso cumulativo. Android: "Installa" button.
* iOS Safari: istruzioni "Condividi → Aggiungi a schermata Home".
* Dismissable permanente via localStorage.
*/
import { useState, useEffect, useRef } from "react";
import { Z_INDEX } from "@/lib/zindex";
import { getCapabilities } from "@/lib/capabilities";
const DISMISSED_KEY = "pwa_install_dismissed_v1";
const USAGE_KEY = "pwa_usage_seconds";
export default function InstallPrompt() {
const [show, setShow] = useState(false);
const [isIos, setIsIos] = useState(false);
const deferredRef = useRef<any>(null);
useEffect(() => {
try {
const caps = getCapabilities();
if (caps.isStandalone) return;
if (localStorage.getItem(DISMISSED_KEY)) return;
setIsIos(!!(caps.isIos && caps.isSafari));
const handler = (e: Event) => {
e.preventDefault();
deferredRef.current = e;
};
window.addEventListener("beforeinstallprompt", handler);
const prev = parseInt(localStorage.getItem(USAGE_KEY) ?? "0", 10);
const start = Date.now();
const tick = setInterval(() => {
const elapsed = Math.floor((Date.now() - start) / 1000) + prev;
localStorage.setItem(USAGE_KEY, String(elapsed));
if (elapsed >= 45) {
setShow(true);
clearInterval(tick);
}
}, 5000);
return () => {
window.removeEventListener("beforeinstallprompt", handler);
clearInterval(tick);
};
} catch { /* non-blocking */ }
return undefined;
}, []);
if (!show) return null;
const dismiss = () => {
setShow(false);
try { localStorage.setItem(DISMISSED_KEY, "1"); } catch { /* */ }
};
const install = async () => {
if (deferredRef.current) {
try {
await deferredRef.current.prompt();
const { outcome } = await deferredRef.current.userChoice;
if (outcome === "accepted") dismiss();
} catch { /* user dismissed */ }
}
};
return (
<div style={{
position: "fixed", bottom: 72, left: 12, right: 12,
background: "rgba(8,8,22,0.96)",
border: "1px solid rgba(99,102,241,0.30)",
borderRadius: 14, padding: "14px 16px",
zIndex: Z_INDEX.CRITICAL_TOAST, boxShadow: "0 8px 32px rgba(0,0,0,0.6)",
display: "flex", flexDirection: "column", gap: 10,
}}>
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
<div>
<div style={{ fontSize: "0.85rem", fontWeight: 700, color: "#e2e8f0" }}>
Installa l'app
</div>
<div style={{ fontSize: "0.72rem", color: "rgba(148,163,184,0.7)", marginTop: 3 }}>
{isIos
? 'Tocca Condividi → "Aggiungi a schermata Home"'
: "Accesso rapido, funziona offline"}
</div>
</div>
<button onClick={dismiss} style={{
all: "unset", cursor: "pointer",
color: "rgba(148,163,184,0.4)", fontSize: "1.2rem", padding: "0 4px",
lineHeight: 1,
}}>×</button>
</div>
{!isIos && (
<button onClick={install} style={{
all: "unset", cursor: "pointer", textAlign: "center",
background: "rgba(99,102,241,0.2)", border: "1px solid rgba(99,102,241,0.4)",
color: "#a5b4fc", borderRadius: 8, padding: "8px 0",
fontSize: "0.78rem", fontWeight: 600,
}}>
Installa
</button>
)}
</div>
);
}