77 lines
3.6 KiB
TypeScript
77 lines
3.6 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { DownloadIcon } from "lucide-react";
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
|
|
|
type InstallEvent = Event & {
|
|
prompt: () => Promise<void>;
|
|
userChoice: Promise<{ outcome: string }>;
|
|
};
|
|
|
|
export function SiteInstallButton() {
|
|
const [open, setOpen] = useState(false);
|
|
const [installed, setInstalled] = useState(false);
|
|
const [installEvent, setInstallEvent] = useState<InstallEvent | null>(null);
|
|
const [busy, setBusy] = useState(false);
|
|
const [error, setError] = useState("");
|
|
|
|
useEffect(() => {
|
|
const displayMode = window.matchMedia("(display-mode: standalone)");
|
|
const frame = requestAnimationFrame(() => setInstalled(displayMode.matches ||
|
|
(navigator as Navigator & { standalone?: boolean }).standalone === true));
|
|
const beforeInstall = (event: Event) => {
|
|
event.preventDefault();
|
|
setInstallEvent(event as InstallEvent);
|
|
};
|
|
const onInstalled = () => { setInstalled(true); setOpen(false); setInstallEvent(null); };
|
|
window.addEventListener("beforeinstallprompt", beforeInstall);
|
|
window.addEventListener("appinstalled", onInstalled);
|
|
return () => {
|
|
cancelAnimationFrame(frame);
|
|
window.removeEventListener("beforeinstallprompt", beforeInstall);
|
|
window.removeEventListener("appinstalled", onInstalled);
|
|
};
|
|
}, []);
|
|
|
|
async function install() {
|
|
if (!installEvent || busy) return;
|
|
setBusy(true);
|
|
setError("");
|
|
try {
|
|
await installEvent.prompt();
|
|
const result = await installEvent.userChoice;
|
|
setInstallEvent(null);
|
|
if (result.outcome === "accepted") setOpen(false);
|
|
} catch { setError("ติดตั้งไม่สำเร็จ กรุณาลองจากเมนูของเบราว์เซอร์"); }
|
|
finally { setBusy(false); }
|
|
}
|
|
|
|
if (installed) return null;
|
|
|
|
return <>
|
|
<Button variant="ghost" size="sm" onClick={() => setOpen(true)} aria-label="ติดตั้ง Buzz Guide">
|
|
<DownloadIcon data-icon="inline-start" />ติดตั้งแอป
|
|
</Button>
|
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>ติดตั้ง Buzz Guide</DialogTitle>
|
|
<DialogDescription>เพิ่มไกด์ลงหน้าจอหลักเพื่อเปิดอ่านได้สะดวก</DialogDescription>
|
|
</DialogHeader>
|
|
{!installEvent && <div className="flex flex-col gap-3 text-sm text-muted-foreground">
|
|
<p>บน iPhone/iPad เปิดใน Safari แตะปุ่มแชร์ แล้วเลือก “เพิ่มไปยังหน้าจอโฮม”</p>
|
|
<p>บน Android หรือคอมพิวเตอร์ เปิดเมนูของเบราว์เซอร์ แล้วเลือก “ติดตั้งแอป” หรือ “เพิ่มไปยังหน้าจอหลัก” หากมีตัวเลือกนี้</p>
|
|
</div>}
|
|
{error && <p role="alert" className="text-sm text-destructive">{error}</p>}
|
|
<DialogFooter>
|
|
<Button variant="outline" onClick={() => setOpen(false)}>ปิด</Button>
|
|
{installEvent && <Button disabled={busy} onClick={() => void install()}>{busy ? "กำลังติดตั้ง…" : "ติดตั้งแอป"}</Button>}
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</>;
|
|
}
|