aboutsummaryrefslogtreecommitdiffstats
path: root/src/app.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-06-23 08:21:58 -0700
committerAdam Soltys <[email protected]>2026-06-23 08:21:58 -0700
commit43acfb9e9e9205e02c39c398c18e3325211f40f1 (patch)
tree68dffae1150033f6dea65a7c0a0eb071053f1575 /src/app.js
parent7c542410dc78711bcf017dd3d23d560ed6349669 (diff)
downloadcoinosv3-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.js35
1 files changed, 34 insertions, 1 deletions
diff --git a/src/app.js b/src/app.js
index 8343290..89da6d0 100644
--- a/src/app.js
+++ b/src/app.js
@@ -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;