diff options
| author | Adam Soltys <[email protected]> | 2026-06-23 08:21:58 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-06-23 08:21:58 -0700 |
| commit | 43acfb9e9e9205e02c39c398c18e3325211f40f1 (patch) | |
| tree | 68dffae1150033f6dea65a7c0a0eb071053f1575 /src/app.js | |
| parent | 7c542410dc78711bcf017dd3d23d560ed6349669 (diff) | |
| download | coinosv3-43acfb9e9e9205e02c39c398c18e3325211f40f1.tar.gz coinosv3-43acfb9e9e9205e02c39c398c18e3325211f40f1.zip | |
Add PWA install affordance; serve manifest as manifest+json
Chrome no longer shows an automatic install prompt, so catch
beforeinstallprompt and surface an "Install app" link in the footer
that replays the prompt on a user tap (hides once installed).
(nginx on the host now serves manifest.webmanifest as
application/manifest+json instead of octet-stream.)
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'src/app.js')
| -rw-r--r-- | src/app.js | 35 |
1 files changed, 34 insertions, 1 deletions
@@ -93,11 +93,44 @@ function footer() { h( 'div', { style: 'margin-top:4px' }, - h('button', { class: 'linklike', style: 'font-weight:400', onClick: openHowItWorks }, t('howItWorks')) + h('button', { class: 'linklike', style: 'font-weight:400', onClick: openHowItWorks }, t('howItWorks')), + // Chrome no longer prompts to install on its own โ surface our own link + // once it reports the app is installable (beforeinstallprompt fired). + installPrompt + ? h('span', {}, h('span', { class: 'faint' }, ' ยท '), + h('button', { class: 'linklike', style: 'font-weight:400', onClick: triggerInstall }, t('installApp'))) + : null ) ); } +// PWA install. Chrome fires beforeinstallprompt when the app qualifies; we stash +// the event and reveal an "Install app" link, then replay it on a user tap (the +// browser requires a gesture). beforeinstallprompt only fires when not already +// installed, so the link naturally hides once installed. +let installPrompt = null; +if (typeof window !== 'undefined') { + window.addEventListener('beforeinstallprompt', (e) => { + e.preventDefault(); + installPrompt = e; + render(); + }); + window.addEventListener('appinstalled', () => { + installPrompt = null; + render(); + }); +} +async function triggerInstall() { + const e = installPrompt; + if (!e) return; + installPrompt = null; + render(); + e.prompt(); + try { + await e.userChoice; + } catch {} +} + // Open the How it works page, remembering where to return to. function openHowItWorks() { if (ui.screen === 'howItWorks') return; |
