Make the PWA installable as a standalone iOS home-screen app (#202)
iOS only grants Web Push to a site the user has added to the Home Screen via Safari (16.4+), and it needs the apple-mobile-web-app meta tags to launch standalone rather than in a Safari tab. Those were absent. Adds them to both header families — the five standalone frontend pages and base.html.j2 — alongside the existing theme-color: apple-mobile-web-app-capable, mobile-web-app-capable, apple-mobile-web-app-status-bar-style, apple-mobile-web-app-title Adds ios-install.js: a dismissible hint shown only to real iOS Safari visitors who haven't installed yet (skips Chrome/Firefox-iOS and standalone launches), pointing them at Share -> Add to Home Screen and noting it's how to enable alerts. There is no beforeinstallprompt on iOS, so a pointer to the control is the only nudge available. The dismissal persists under the thermograph: localStorage prefix — not tg:, which cache.js sweeps on every load. No backend change: an installed iOS home-screen app reuses the existing service worker and VAPID web-push stack as-is. Claude-Session: https://claude.ai/code/session_013dRZmX9D3JEntfMKWMTWZ8
This commit is contained in:
parent
9823fd47ec
commit
f44e701b12
7 changed files with 136 additions and 0 deletions
|
|
@ -6,6 +6,12 @@
|
|||
<title>Thermograph — 2-year calendar</title>
|
||||
<meta name="description" content="A two-year calendar of daily weather grades for any location — see at a glance which days were unusually hot, cold, wet, or normal versus ~45 years of local climate history." />
|
||||
<meta name="theme-color" content="#f0803c" />
|
||||
<!-- iOS/Android home-screen install: run standalone (no browser chrome) when
|
||||
added to the Home Screen. On iOS 16.4+ this is what unlocks Web Push. -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="Thermograph" />
|
||||
<!-- Link previews (Discord, Slack, iMessage…). The server fills the og:url /
|
||||
og:image origins in from this request's scheme://host + base path —
|
||||
crawlers need absolute URLs and don't run JS. -->
|
||||
|
|
@ -112,5 +118,6 @@
|
|||
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script type="module" src="calendar.js"></script>
|
||||
<script type="module" src="ios-install.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,12 @@
|
|||
<title>Thermograph — compare comfort</title>
|
||||
<meta name="description" content="Compare how well two or more places hit your comfort temperature over any date range, side by side, each graded against its own ~45-year climate history." />
|
||||
<meta name="theme-color" content="#f0803c" />
|
||||
<!-- iOS/Android home-screen install: run standalone (no browser chrome) when
|
||||
added to the Home Screen. On iOS 16.4+ this is what unlocks Web Push. -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="Thermograph" />
|
||||
<!-- Link previews (Discord, Slack, iMessage…). The server fills the og:url /
|
||||
og:image origins in from this request's scheme://host + base path —
|
||||
crawlers need absolute URLs and don't run JS. -->
|
||||
|
|
@ -172,5 +178,6 @@
|
|||
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script type="module" src="compare.js"></script>
|
||||
<script type="module" src="ios-install.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,12 @@
|
|||
<title>Thermograph — a single day</title>
|
||||
<meta name="description" content="One day's weather in full detail: every percentile tier for that date and place, and exactly where the day's high, low, feels-like, wind and rain landed against local history." />
|
||||
<meta name="theme-color" content="#f0803c" />
|
||||
<!-- iOS/Android home-screen install: run standalone (no browser chrome) when
|
||||
added to the Home Screen. On iOS 16.4+ this is what unlocks Web Push. -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="Thermograph" />
|
||||
<!-- Link previews (Discord, Slack, iMessage…). The server fills the og:url /
|
||||
og:image origins in from this request's scheme://host + base path —
|
||||
crawlers need absolute URLs and don't run JS. -->
|
||||
|
|
@ -78,5 +84,6 @@
|
|||
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script type="module" src="day.js"></script>
|
||||
<script type="module" src="ios-install.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
73
static/ios-install.js
Normal file
73
static/ios-install.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
// A small, dismissible "Add to Home Screen" hint for iPhone/iPad visitors.
|
||||
//
|
||||
// iOS only grants Web Push to a site the user has added to the Home Screen via
|
||||
// Safari's Share sheet (iOS 16.4+). There is no beforeinstallprompt on iOS, so
|
||||
// the only nudge available is telling people where the control is. This module
|
||||
// shows that nudge — and only when it can actually help: real iOS Safari, not
|
||||
// already installed, not previously dismissed. Everything degrades to nothing.
|
||||
|
||||
// Not the "tg:" prefix — cache.js sweeps that namespace on every load. The
|
||||
// durable app preferences ("thermograph:unit", "thermograph:loc") use this one.
|
||||
const KEY = "thermograph:ios-a2hs";
|
||||
|
||||
function isIOS() {
|
||||
const ua = navigator.userAgent || "";
|
||||
// iPadOS 13+ reports as "Macintosh"; a touch point count disambiguates it.
|
||||
return /iphone|ipad|ipod/i.test(ua) ||
|
||||
(/Macintosh/.test(ua) && (navigator.maxTouchPoints || 0) > 1);
|
||||
}
|
||||
|
||||
function isStandalone() {
|
||||
return window.navigator.standalone === true ||
|
||||
(window.matchMedia && window.matchMedia("(display-mode: standalone)").matches);
|
||||
}
|
||||
|
||||
function isSafari() {
|
||||
// Add to Home Screen only works from Safari proper — not Chrome (CriOS),
|
||||
// Firefox (FxiOS), Edge (EdgiOS), Opera (OPiOS), or most in-app webviews.
|
||||
const ua = navigator.userAgent || "";
|
||||
if (/crios|fxios|edgios|opios/i.test(ua)) return false;
|
||||
return /safari/i.test(ua);
|
||||
}
|
||||
|
||||
function dismissed() {
|
||||
try { return localStorage.getItem(KEY) === "off"; } catch (e) { return false; }
|
||||
}
|
||||
function remember() {
|
||||
try { localStorage.setItem(KEY, "off"); } catch (e) {}
|
||||
}
|
||||
|
||||
function build() {
|
||||
const el = document.createElement("div");
|
||||
el.className = "ios-a2hs";
|
||||
el.setAttribute("role", "note");
|
||||
el.setAttribute("aria-label", "Install Thermograph on your Home Screen");
|
||||
// The share glyph mirrors iOS's own Share icon so the instruction is scannable.
|
||||
el.innerHTML =
|
||||
'<svg class="ios-a2hs-share" viewBox="0 0 24 24" aria-hidden="true" fill="none" ' +
|
||||
'stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' +
|
||||
'<path d="M12 3v12"/><path d="M8 7l4-4 4 4"/>' +
|
||||
'<path d="M5 12v7a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-7"/></svg>' +
|
||||
'<div class="ios-a2hs-text"><b>Add Thermograph to your Home Screen</b>' +
|
||||
'<span>Tap Share, then <b>Add to Home Screen</b> — it’s how to get weather alerts on iPhone.</span></div>' +
|
||||
'<button type="button" class="ios-a2hs-x" aria-label="Dismiss">×</button>';
|
||||
el.querySelector(".ios-a2hs-x").addEventListener("click", () => {
|
||||
remember();
|
||||
el.remove();
|
||||
});
|
||||
return el;
|
||||
}
|
||||
|
||||
function maybeShow() {
|
||||
if (!isIOS() || isStandalone() || !isSafari() || dismissed()) return;
|
||||
if (document.querySelector(".ios-a2hs")) return;
|
||||
document.body.appendChild(build());
|
||||
}
|
||||
|
||||
// Wait a beat so the hint never competes with first paint, and only on an
|
||||
// interactive document.
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", () => setTimeout(maybeShow, 1200));
|
||||
} else {
|
||||
setTimeout(maybeShow, 1200);
|
||||
}
|
||||
|
|
@ -6,6 +6,12 @@
|
|||
<title>Thermograph — reading the grades</title>
|
||||
<meta name="description" content="How Thermograph grades weather: percentiles of a location's own ±7-day seasonal history, the scale from Below Normal to Near Record, and what every metric means." />
|
||||
<meta name="theme-color" content="#f0803c" />
|
||||
<!-- iOS/Android home-screen install: run standalone (no browser chrome) when
|
||||
added to the Home Screen. On iOS 16.4+ this is what unlocks Web Push. -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="Thermograph" />
|
||||
<!-- Link previews (Discord, Slack, iMessage…). The server fills the og:url /
|
||||
og:image origins in from this request's scheme://host + base path —
|
||||
crawlers need absolute URLs and don't run JS. -->
|
||||
|
|
@ -109,5 +115,6 @@
|
|||
document.getElementById("temp-scale").innerHTML = SCALE_TEMP.map(row).join("");
|
||||
document.getElementById("rain-scale").innerHTML = SCALE_RAIN.map(row).join("");
|
||||
</script>
|
||||
<script type="module" src="ios-install.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -2034,3 +2034,31 @@ details.hub-country > summary:hover { color: var(--accent); }
|
|||
}
|
||||
.digest-note { margin: 0; font-size: 12px; }
|
||||
.digest-status { margin: 0; font-size: 14px; color: var(--accent); }
|
||||
|
||||
/* iOS "Add to Home Screen" hint (frontend/ios-install.js) — a slim dismissible
|
||||
card pinned above the home indicator, shown only to iOS Safari visitors who
|
||||
haven't installed yet. Themed like the rest of the surface chrome. */
|
||||
.ios-a2hs {
|
||||
position: fixed; z-index: 950;
|
||||
left: 50%; transform: translateX(-50%);
|
||||
bottom: calc(12px + env(safe-area-inset-bottom));
|
||||
width: min(440px, calc(100vw - 24px));
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 12px 14px;
|
||||
background: var(--surface); color: var(--text);
|
||||
border: 1px solid var(--border); border-radius: 14px;
|
||||
box-shadow: 0 8px 30px rgba(0, 0, 0, .35);
|
||||
animation: ios-a2hs-in .25s ease-out;
|
||||
}
|
||||
@keyframes ios-a2hs-in { from { opacity: 0; transform: translate(-50%, 12px); } }
|
||||
.ios-a2hs-share { width: 26px; height: 26px; flex: 0 0 auto; color: var(--accent); }
|
||||
.ios-a2hs-text { display: flex; flex-direction: column; gap: 2px; font-size: 13px; line-height: 1.3; }
|
||||
.ios-a2hs-text b { font-weight: 700; }
|
||||
.ios-a2hs-text span { color: var(--muted); }
|
||||
.ios-a2hs-x {
|
||||
flex: 0 0 auto; margin-left: auto;
|
||||
width: 32px; height: 32px; border-radius: 8px;
|
||||
background: none; border: none; color: var(--muted);
|
||||
font-size: 22px; line-height: 1; cursor: pointer;
|
||||
}
|
||||
.ios-a2hs-x:hover { color: var(--text); background: var(--surface-2); }
|
||||
|
|
|
|||
|
|
@ -6,6 +6,12 @@
|
|||
<title>Thermograph — weather alerts</title>
|
||||
<meta name="description" content="Subscribe to a city and get notified when its weather is unusually extreme — above the percentile you choose." />
|
||||
<meta name="theme-color" content="#f0803c" />
|
||||
<!-- iOS/Android home-screen install: run standalone (no browser chrome) when
|
||||
added to the Home Screen. On iOS 16.4+ this is what unlocks Web Push. -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="Thermograph" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="Thermograph" />
|
||||
<meta property="og:title" content="Thermograph — weather alerts" />
|
||||
|
|
@ -58,5 +64,6 @@
|
|||
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script type="module" src="subscriptions.js"></script>
|
||||
<script type="module" src="ios-install.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in a new issue