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
73 lines
3 KiB
JavaScript
73 lines
3 KiB
JavaScript
// 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);
|
||
}
|