import { useEffect, useState } from "react"; import { BeforeInstallPromptEvent, isAppleTouchDevice, isStandaloneDisplay, } from "../pwa/install"; /** * Desktop/tablet install affordance: * - Chrome/Edge/Android: native install via beforeinstallprompt * - iPad/iOS: tip to Add to Home Screen * - Already installed: show 已安装标识 */ export default function InstallBadge() { const [standalone, setStandalone] = useState(isStandaloneDisplay); const [deferred, setDeferred] = useState( null, ); const [tipOpen, setTipOpen] = useState(false); const [busy, setBusy] = useState(false); const apple = isAppleTouchDevice(); useEffect(() => { const mq = window.matchMedia("(display-mode: standalone)"); const sync = () => setStandalone(isStandaloneDisplay()); mq.addEventListener?.("change", sync); sync(); const onBip = (e: Event) => { e.preventDefault(); setDeferred(e as BeforeInstallPromptEvent); }; const onInstalled = () => { setDeferred(null); setStandalone(true); setTipOpen(false); }; window.addEventListener("beforeinstallprompt", onBip); window.addEventListener("appinstalled", onInstalled); return () => { mq.removeEventListener?.("change", sync); window.removeEventListener("beforeinstallprompt", onBip); window.removeEventListener("appinstalled", onInstalled); }; }, []); if (standalone) { return ( 已安装 ); } const canNativeInstall = Boolean(deferred); async function onInstall() { if (!deferred) { setTipOpen((v) => !v); return; } setBusy(true); try { await deferred.prompt(); await deferred.userChoice; setDeferred(null); } finally { setBusy(false); } } return (
{tipOpen && !canNativeInstall ? (
{apple ? ( <>

iPad / iPhone:用 Safari 打开本站,点分享 →「添加到主屏幕」。

安装后从主屏幕打开即为独立应用窗口。

) : ( <>

电脑:Chrome / Edge 地址栏右侧「安装」图标,或菜单 → 安装应用。

安卓平板:浏览器菜单 →「安装应用」/「添加到主屏幕」。

需 HTTPS(或本机 localhost)且已加载完成。

)}
) : null}
); }