diff --git a/server/app/main.py b/server/app/main.py index 2e7aad2..a0d2b7c 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -18,7 +18,7 @@ from __future__ import annotations import logging from fastapi import FastAPI, Request -from fastapi.responses import HTMLResponse, RedirectResponse +from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from sqlalchemy import select @@ -60,6 +60,13 @@ def health() -> dict: return {"status": "ok"} +@app.get("/sw.js") +def service_worker() -> FileResponse: + # Served from / rather than /static/sw.js so its default scope is the + # whole app -- a SW can only ever control paths at or below its own URL. + return FileResponse("app/static/sw.js", media_type="application/javascript") + + def _device_credential_redirect(request: Request, db, allow_legacy: bool) -> str | None: """The on-frame manage QR points at the server root with the device's own credentials (new firmware: ?id=&token=; deployed firmware: diff --git a/server/app/static/common.js b/server/app/static/common.js index 56e2ace..a9d6372 100644 --- a/server/app/static/common.js +++ b/server/app/static/common.js @@ -58,6 +58,15 @@ } })(); +// Registering this is what makes Chrome/Android offer the "Add to Home +// screen" install prompt -- a manifest link alone isn't enough. Served +// from /sw.js (not /static/sw.js) so its scope is the whole app. +if ("serviceWorker" in navigator) { + window.addEventListener("load", function () { + navigator.serviceWorker.register("/sw.js"); + }); +} + // Shared display names for widget_type, everywhere one shows up in the // UI (Layout canvas, Add-a-widget buttons, button-assignment dropdowns). const WIDGET_LABELS = { diff --git a/server/app/static/icons/apple-touch-icon.png b/server/app/static/icons/apple-touch-icon.png new file mode 100644 index 0000000..d2e387c Binary files /dev/null and b/server/app/static/icons/apple-touch-icon.png differ diff --git a/server/app/static/icons/favicon.png b/server/app/static/icons/favicon.png new file mode 100644 index 0000000..1225225 Binary files /dev/null and b/server/app/static/icons/favicon.png differ diff --git a/server/app/static/icons/icon-192.png b/server/app/static/icons/icon-192.png new file mode 100644 index 0000000..b67b201 Binary files /dev/null and b/server/app/static/icons/icon-192.png differ diff --git a/server/app/static/icons/icon-512.png b/server/app/static/icons/icon-512.png new file mode 100644 index 0000000..99bfaab Binary files /dev/null and b/server/app/static/icons/icon-512.png differ diff --git a/server/app/static/manifest.json b/server/app/static/manifest.json new file mode 100644 index 0000000..c1b2e47 --- /dev/null +++ b/server/app/static/manifest.json @@ -0,0 +1,14 @@ +{ + "name": "ESPresso Frame", + "short_name": "ESPresso", + "description": "Manage your e-ink photo frames.", + "start_url": "/", + "scope": "/", + "display": "standalone", + "background_color": "#f5f6f8", + "theme_color": "#2563eb", + "icons": [ + { "src": "/static/icons/icon-192.png", "sizes": "192x192", "type": "image/png" }, + { "src": "/static/icons/icon-512.png", "sizes": "512x512", "type": "image/png" } + ] +} diff --git a/server/app/static/sw.js b/server/app/static/sw.js new file mode 100644 index 0000000..bbff74a --- /dev/null +++ b/server/app/static/sw.js @@ -0,0 +1,8 @@ +// Presence-only service worker: satisfies the "installable" requirement +// (Chrome/Android in particular checks for a controlling SW with a fetch +// handler) without adding an offline cache -- every request just goes to +// the network as normal. Served from / (see app/main.py's /sw.js route) +// so its scope covers the whole app, not just /static/. +self.addEventListener("install", () => self.skipWaiting()); +self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim())); +self.addEventListener("fetch", (event) => event.respondWith(fetch(event.request))); diff --git a/server/app/templates/app_base.html b/server/app/templates/app_base.html index 48b9793..ae85d1f 100644 --- a/server/app/templates/app_base.html +++ b/server/app/templates/app_base.html @@ -17,6 +17,14 @@ })(); + + + + + + + + {% block extra_head %}{% endblock %} diff --git a/server/app/templates/base.html b/server/app/templates/base.html index b3e3afc..45a8010 100644 --- a/server/app/templates/base.html +++ b/server/app/templates/base.html @@ -17,6 +17,14 @@ })(); + + + + + + + + {% block extra_head %}{% endblock %}