48 lines
1.6 KiB
TypeScript
48 lines
1.6 KiB
TypeScript
import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
|
const DISMISSED_KEY = "pwa-install-dismissed";
|
|
|
|
interface BeforeInstallPromptEvent extends Event {
|
|
prompt(): Promise<{ outcome: "accepted" | "dismissed" }>;
|
|
}
|
|
|
|
export function useInstallPrompt() {
|
|
const deferredPrompt = useRef<BeforeInstallPromptEvent | null>(null);
|
|
const [canInstall, setCanInstall] = useState(false);
|
|
const [isDismissed, setIsDismissed] = useState(
|
|
() => localStorage.getItem(DISMISSED_KEY) === "true"
|
|
);
|
|
|
|
const isStandalone =
|
|
typeof window !== "undefined" &&
|
|
typeof window.matchMedia === "function" &&
|
|
(window.matchMedia("(display-mode: standalone)")?.matches ?? false);
|
|
|
|
const isIOS = typeof navigator !== "undefined" && /iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
|
|
useEffect(() => {
|
|
const handler = (e: Event) => {
|
|
e.preventDefault();
|
|
deferredPrompt.current = e as BeforeInstallPromptEvent;
|
|
setCanInstall(true);
|
|
};
|
|
window.addEventListener("beforeinstallprompt", handler);
|
|
return () => window.removeEventListener("beforeinstallprompt", handler);
|
|
}, []);
|
|
|
|
const promptInstall = useCallback(async () => {
|
|
if (!deferredPrompt.current) return;
|
|
const result = await deferredPrompt.current.prompt();
|
|
if (result.outcome === "accepted") {
|
|
deferredPrompt.current = null;
|
|
setCanInstall(false);
|
|
}
|
|
}, []);
|
|
|
|
const dismiss = useCallback(() => {
|
|
setIsDismissed(true);
|
|
localStorage.setItem(DISMISSED_KEY, "true");
|
|
}, []);
|
|
|
|
return { canInstall, isStandalone, isIOS, isDismissed, promptInstall, dismiss };
|
|
}
|