fix(pwa): guarantee mobile install banner visibility, capture early beforeinstallprompt in head, and provide install guide modal
This commit is contained in:
1 parent
4d4b8c860d
commit
704d3b9eca
2 files changed
+62
-18
No files matched your search
@@ -42,6 +42,23 @@ export default function RootLayout({
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
window.__everclone_install_prompt = null;
|
||||
window.addEventListener('beforeinstallprompt', function(e) {
|
||||
e.preventDefault();
|
||||
window.__everclone_install_prompt = e;
|
||||
window.dispatchEvent(new CustomEvent('everclone:installable', { detail: true }));
|
||||
console.log('[Everclone PWA] Early beforeinstallprompt captured!');
|
||||
});
|
||||
window.addEventListener('appinstalled', function() {
|
||||
window.__everclone_install_prompt = null;
|
||||
window.dispatchEvent(new CustomEvent('everclone:installable', { detail: false }));
|
||||
});
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<PwaRegister />
|
||||
|
||||
@@ -102,6 +102,7 @@ export default function EvercloneWorkspace() {
|
||||
const [newUserEmail, setNewUserEmail] = useState('');
|
||||
const [triageModalOpen, setTriageModalOpen] = useState(false);
|
||||
const [canInstallPwa, setCanInstallPwa] = useState(false);
|
||||
const [installHelpModalOpen, setInstallHelpModalOpen] = useState(false);
|
||||
const [shareToast, setShareToast] = useState<string | null>(null);
|
||||
const [historyStack, setHistoryStack] = useState<string[]>([]);
|
||||
const [historyIndex, setHistoryIndex] = useState<number>(-1);
|
||||
@@ -122,12 +123,37 @@ export default function EvercloneWorkspace() {
|
||||
// PWA In-App Installation Listener
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') return;
|
||||
if (window.__everclone_install_prompt) {
|
||||
setCanInstallPwa(true);
|
||||
}
|
||||
|
||||
const checkInstallState = () => {
|
||||
const isStandalone =
|
||||
window.matchMedia('(display-mode: standalone)').matches ||
|
||||
(navigator as unknown as { standalone?: boolean }).standalone === true;
|
||||
|
||||
if (isStandalone) {
|
||||
setCanInstallPwa(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const dismissed = localStorage.getItem('evc_dismiss_install');
|
||||
if (dismissed === '1') {
|
||||
if (window.__everclone_install_prompt) {
|
||||
setCanInstallPwa(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (window.innerWidth < 768 || window.__everclone_install_prompt) {
|
||||
setCanInstallPwa(true);
|
||||
}
|
||||
};
|
||||
|
||||
checkInstallState();
|
||||
|
||||
const onInstallable = (e: Event) => {
|
||||
const detail = (e as CustomEvent).detail;
|
||||
setCanInstallPwa(Boolean(detail));
|
||||
if (detail) {
|
||||
setCanInstallPwa(true);
|
||||
}
|
||||
};
|
||||
window.addEventListener('everclone:installable', onInstallable);
|
||||
return () => window.removeEventListener('everclone:installable', onInstallable);
|
||||
@@ -135,21 +161,22 @@ export default function EvercloneWorkspace() {
|
||||
|
||||
const handleInstallPwa = async () => {
|
||||
const promptEvent = window.__everclone_install_prompt;
|
||||
if (!promptEvent) {
|
||||
alert("To install Everclone on Android, tap your browser's menu (⋮) and select 'Install app' or 'Add to Home screen'.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await promptEvent.prompt();
|
||||
const choice = await promptEvent.userChoice;
|
||||
if (choice.outcome === 'accepted') {
|
||||
setCanInstallPwa(false);
|
||||
window.__everclone_install_prompt = null;
|
||||
setShareToast('Everclone installed! You can now share directly to Everclone.');
|
||||
setTimeout(() => setShareToast(null), 5000);
|
||||
if (promptEvent) {
|
||||
try {
|
||||
await promptEvent.prompt();
|
||||
const choice = await promptEvent.userChoice;
|
||||
if (choice.outcome === 'accepted') {
|
||||
setCanInstallPwa(false);
|
||||
window.__everclone_install_prompt = null;
|
||||
setShareToast('Everclone installed! Native Share Target active.');
|
||||
setTimeout(() => setShareToast(null), 5000);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Install prompt failed:', err);
|
||||
setInstallHelpModalOpen(true);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Install prompt failed:', err);
|
||||
} else {
|
||||
setInstallHelpModalOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user