Name each install, fix the notification badge, switch to Archivo

- Settings > General: an App name (super admin) for the installed app.
  The manifest's name/short_name, the iOS head tags and the loading
  page's title use it, so two installs on one phone can be told apart.
- Notifications get a real badge (white stone silhouette): Android masks
  the badge by alpha, so the opaque app icon showed as a square.
- The loading and offline pages load the app's font.
- The font is now Archivo (variable, one request for every weight),
  matching the marketing site.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-25 10:17:40 +01:00
co-authored by Claude Opus 5.5
parent 89c4e26d06
commit 3b7c05bfa0
23 changed files with 440 additions and 16 deletions
@@ -1,6 +1,9 @@
import path from "path";
import { AppData } from "@/src/data/app-data";
import { PwaLoadingPath } from "@/src/data/pwa-data";
import { PwaLoadingNamePlaceholder, PwaLoadingPath } from "@/src/data/pwa-data";
import grabInstallName from "@/src/functions/backend/db/app-settings/grab-install-name";
import installDisplayName from "@/src/utils/install-display-name";
import escapeHtml from "@/src/utils/escape-html";
import { getCookie } from "@/src/utils/cookies";
import needsPrecachePage from "@/src/utils/needs-precache-page";
import grabPrecacheManifest from "./grab-precache-manifest";
@@ -53,7 +56,15 @@ export default async function handlePrecacheGate({
total: manifest.files.length,
}).replace(/</g, "\\u003c");
return new Response((await file.text()).replace(BuildPlaceholder, build), {
const name = escapeHtml({
text: installDisplayName({ install_name: await grabInstallName().catch(() => "") }),
});
const page = (await file.text())
.replace(BuildPlaceholder, build)
.replace(PwaLoadingNamePlaceholder, name);
return new Response(page, {
headers: {
"Content-Type": "text/html; charset=utf-8",
"Cache-Control": "no-store, max-age=0, must-revalidate",
+24 -1
View File
@@ -1,5 +1,7 @@
import path from "path";
import { PwaManifestPath, PwaServiceWorkerPath } from "@/src/data/pwa-data";
import grabInstallName from "@/src/functions/backend/db/app-settings/grab-install-name";
import namePwaManifest from "@/src/utils/name-pwa-manifest";
const PUBLIC_DIR = path.resolve(import.meta.dir, "../../../../public");
@@ -77,7 +79,11 @@ export default async function handlePwaFile({
headers.set("Service-Worker-Allowed", "/");
}
const response = new Response(development_stub || file, { headers });
const response = new Response(
development_stub ||
(file_name == "manifest.webmanifest" ? await namedManifest({ file }) : file),
{ headers },
);
/**
* `Bun.file` adds a filename disposition; a manifest is inline data.
@@ -86,3 +92,20 @@ export default async function handlePwaFile({
return response;
}
/**
* The generated manifest under the install's own name (Settings >
* General). Unnamed, or when the file isn't JSON, it goes out as it is.
*/
async function namedManifest({ file }: { file: ReturnType<typeof Bun.file> }) {
const text = await file.text();
const install_name = await grabInstallName().catch(() => "");
if (!install_name) return text;
try {
return JSON.stringify(namePwaManifest({ manifest: JSON.parse(text), install_name }), null, 4);
} catch {
return text;
}
}