Fix PWA install banner: persist dismiss, prevent duplicates

- Add appinstalled event listener so browser-initiated installs also
  permanently dismiss the banner
- Ensure only one banner shows at a time (update hides install first)
- Refactor dismiss logic into single helper function

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-04-01 12:07:17 -07:00
parent a4cd977d17
commit fef217798e
1 changed files with 19 additions and 12 deletions

View File

@ -361,6 +361,9 @@ export function renderShell(opts: ShellOptions): string {
if (!isStandalone) return; // Only show update prompt in installed PWA
const b = document.getElementById('pwa-update-banner');
if (!b || b.style.display !== 'none') return;
// Hide install banner first if somehow both would show
const ib = document.getElementById('pwa-install-banner');
if (ib) ib.style.display = 'none';
b.style.display = '';
document.body.classList.add('rspace-banner-visible');
}
@ -382,31 +385,34 @@ export function renderShell(opts: ShellOptions): string {
}
// Install banner — browser only (not shown in installed PWA)
// Permanently dismissed once closed or installed (localStorage key persists)
if (!isStandalone) {
const installDismissed = () => localStorage.getItem('rspace_install_dismissed') === '1';
const dismissInstall = () => {
const b = document.getElementById('pwa-install-banner');
if (b) b.style.display = 'none';
document.body.classList.remove('rspace-banner-visible');
localStorage.setItem('rspace_install_dismissed', '1');
};
window.addEventListener("beforeinstallprompt", (e) => {
e.preventDefault();
window.__rspaceInstallPrompt = () => { e.prompt(); return e.userChoice; };
window.dispatchEvent(new CustomEvent("rspace-install-available"));
if (localStorage.getItem('rspace_install_dismissed') !== '1') {
if (!installDismissed()) {
const b = document.getElementById('pwa-install-banner');
if (b) { b.style.display = ''; document.body.classList.add('rspace-banner-visible'); }
}
});
// User installed via browser UI (not our button) — still dismiss permanently
window.addEventListener("appinstalled", () => { dismissInstall(); });
document.getElementById('pwa-install-btn')?.addEventListener('click', async () => {
if (window.__rspaceInstallPrompt) {
const choice = await window.__rspaceInstallPrompt();
if (choice?.outcome === 'accepted') {
document.getElementById('pwa-install-banner').style.display = 'none';
document.body.classList.remove('rspace-banner-visible');
localStorage.setItem('rspace_install_dismissed', '1');
}
if (choice?.outcome === 'accepted') dismissInstall();
}
});
document.getElementById('pwa-install-close')?.addEventListener('click', () => {
document.getElementById('pwa-install-banner').style.display = 'none';
document.body.classList.remove('rspace-banner-visible');
localStorage.setItem('rspace_install_dismissed', '1');
});
document.getElementById('pwa-install-close')?.addEventListener('click', () => { dismissInstall(); });
}
// Update banner button handlers (always wire, shown conditionally above)
@ -546,9 +552,10 @@ export function renderShell(opts: ShellOptions): string {
// ── Invite acceptance on page load ──
(function() {
var params = new URLSearchParams(window.location.search);
var inviteToken = params.get('invite');
var inviteToken = params.get('inviteToken') || params.get('invite');
if (!inviteToken) return;
// Remove token from URL immediately
params.delete('inviteToken');
params.delete('invite');
var newUrl = window.location.pathname + (params.toString() ? '?' + params.toString() : '');
history.replaceState(null, '', newUrl);