Files
buzz-sheet/components/site-entry-prompt.tsx
T
gunshiz 7729068219
CI / Verify (push) Successful in 1m59s
CI / Build immutable images and deploy (push) Successful in 3m44s
feat : add dont ask me again for install web app
2026-10-05 21:17:40 +07:00

157 lines
7.1 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import { usePathname } from "next/navigation";
import { syncCommissionPush } from "@/components/commission/push-client";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Field, FieldLabel } from "@/components/ui/field";
import { Switch } from "@/components/ui/switch";
type InstallEvent = Event & { prompt: () => Promise<void>; userChoice: Promise<{ outcome: string }> };
const dismissedKey = "site-entry-prompt-dismissed";
const dontAskAgainKey = "site-entry-prompt-dont-ask-again";
function isPromptDismissed() {
return sessionStorage.getItem(dismissedKey) === "1" ||
localStorage.getItem(dontAskAgainKey) === "1";
}
function isInstalled() {
return window.matchMedia("(display-mode: standalone)").matches ||
(navigator as Navigator & { standalone?: boolean }).standalone === true;
}
export function SiteEntryPrompt() {
const pathname = usePathname();
const [open, setOpen] = useState(false);
const [installed, setInstalled] = useState(false);
const [installEvent, setInstallEvent] = useState<InstallEvent | null>(null);
const [permission, setPermission] = useState<NotificationPermission | null>(null);
const [busy, setBusy] = useState(false);
const [error, setError] = useState("");
const [dontAskAgain, setDontAskAgain] = useState(false);
useEffect(() => {
// Commission pages have their own prompt, tied to the signed-in user's messages.
if (pathname !== "/") {
const frame = requestAnimationFrame(() => setOpen(false));
return () => cancelAnimationFrame(frame);
}
const installedNow = isInstalled();
const ios = /iPad|iPhone|iPod/.test(navigator.userAgent) ||
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
const canNotify = window.isSecureContext && "Notification" in window &&
"PushManager" in window && "serviceWorker" in navigator;
const frame = requestAnimationFrame(() => {
setInstalled(installedNow);
setPermission(canNotify ? Notification.permission : null);
if (!isPromptDismissed() &&
((ios && !installedNow) || (canNotify && Notification.permission === "default"))) {
setOpen(true);
}
});
const beforeInstall = (event: Event) => {
event.preventDefault();
setInstallEvent(event as InstallEvent);
if (!isPromptDismissed()) setOpen(true);
};
const onInstalled = () => {
setInstalled(true);
setInstallEvent(null);
if (!canNotify || Notification.permission !== "default") setOpen(false);
};
window.addEventListener("beforeinstallprompt", beforeInstall);
window.addEventListener("appinstalled", onInstalled);
return () => {
cancelAnimationFrame(frame);
window.removeEventListener("beforeinstallprompt", beforeInstall);
window.removeEventListener("appinstalled", onInstalled);
};
}, [pathname]);
function dismiss() {
sessionStorage.setItem(dismissedKey, "1");
setOpen(false);
}
function toggleDontAskAgain(checked: boolean) {
setDontAskAgain(checked);
if (checked) localStorage.setItem(dontAskAgainKey, "1");
else localStorage.removeItem(dontAskAgainKey);
}
async function install() {
if (!installEvent) return;
await installEvent.prompt();
await installEvent.userChoice;
setInstallEvent(null);
}
async function enableNotifications() {
setBusy(true);
setError("");
try {
const result = await Notification.requestPermission();
setPermission(result);
if (result !== "granted") {
if (!showInstall) setOpen(false);
return;
}
// A signed-in commission user can receive messages immediately. Guests can
// subscribe when they later sign in and visit their commission page.
const response = await fetch("/api/commission/push-subscription", { cache: "no-store" });
if (response.ok) {
const config = await response.json() as { publicKey: string; canAdmin: boolean };
await syncCommissionPush(config.canAdmin ? "admin" : "customer", config.publicKey);
}
if (!showInstall) setOpen(false);
} catch {
setError("เปิดการแจ้งเตือนไม่สำเร็จ กรุณาลองอีกครั้ง");
} finally {
setBusy(false);
}
}
const ios = typeof navigator !== "undefined" &&
(/iPad|iPhone|iPod/.test(navigator.userAgent) ||
(navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1));
const showInstall = !installed && (ios || installEvent !== null);
const canAskNotification = permission === "default" && (!ios || installed);
return <Dialog open={open} onOpenChange={(next) => { if (!next) dismiss(); }}>
<DialogContent>
<DialogHeader>
<DialogTitle>{showInstall ? "ติดตั้ง Buzz Guide และรับการแจ้งเตือน" : "รับการแจ้งเตือนจาก Buzz Guide"}</DialogTitle>
<DialogDescription>
{showInstall ?
"เพิ่มเว็บไซต์ลงหน้าจอหลักเพื่อเปิดใช้งานได้สะดวก และรับการแจ้งเตือนเมื่อมีข้อความใหม่ใน Commission หลังเข้าสู่ระบบ" :
"รับการแจ้งเตือนเมื่อมีข้อความใหม่ใน Commission หลังเข้าสู่ระบบ"}
</DialogDescription>
</DialogHeader>
{!installed && ios && <p className="text-sm text-muted-foreground">
บน iPhone/iPad แตะปุ่มแชร์ใน Safari แล้วเลือก ‘เพิ่มไปยังหน้าจอโฮม’ จากนั้นเปิดแอปที่ติดตั้ง
</p>}
{ios && !installed && <p className="text-sm text-muted-foreground">
ติดตั้งและเปิดจากหน้าจอโฮมก่อน จึงจะเปิดการแจ้งเตือนได้บน iPhone/iPad
</p>}
{error && <p role="alert" className="text-sm text-destructive">{error}</p>}
<Field orientation="horizontal">
<FieldLabel htmlFor="site-entry-dont-ask-again">ไม่ต้องถามอีก</FieldLabel>
<Switch id="site-entry-dont-ask-again" checked={dontAskAgain} onCheckedChange={toggleDontAskAgain} />
</Field>
<DialogFooter>
<Button type="button" variant="outline" disabled={busy} onClick={dismiss}>ไว้ก่อน</Button>
{installEvent && !installed && <Button type="button" variant="secondary" disabled={busy} onClick={() => void install()}>ติดตั้งแอป</Button>}
{canAskNotification && <Button type="button" disabled={busy} onClick={() => void enableNotifications()}>เปิดการแจ้งเตือน</Button>}
</DialogFooter>
</DialogContent>
</Dialog>;
}