Files
eth_hedge_sim/frontend/src/components/InstallBadge.tsx
T

119 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<BeforeInstallPromptEvent | null>(
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 (
<span className="install-badge installed" title="已安装为独立应用">
</span>
);
}
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 (
<div className="install-wrap">
<button
type="button"
className="btn ghost install-btn"
disabled={busy}
onClick={() => void onInstall()}
title={
canNativeInstall
? "安装到电脑或平板"
: apple
? "添加到主屏幕(平板/手机)"
: "安装说明"
}
>
{canNativeInstall ? (busy ? "安装中…" : "安装应用") : "安装应用"}
</button>
{tipOpen && !canNativeInstall ? (
<div className="install-tip" role="dialog" aria-label="安装说明">
{apple ? (
<>
<p>iPad / iPhone Safari </p>
<p className="muted"></p>
</>
) : (
<>
<p>
Chrome / Edge
</p>
<p>
/
</p>
<p className="muted"> HTTPS localhost</p>
</>
)}
<button
type="button"
className="btn ghost"
onClick={() => setTipOpen(false)}
>
</button>
</div>
) : null}
</div>
);
}