First version of Karten App

This commit is contained in:
2026-10-09 04:20:40 +02:00
commit 83a36c7a22
41 changed files with 10834 additions and 0 deletions
+99
View File
@@ -0,0 +1,99 @@
import "ol/ol.css";
import "./style.css";
import { registerSW } from "virtual:pwa-register";
import { createCacheStorage } from "./adapters/cache-storage";
import { createKachelFetch } from "./adapters/kachel-fetch";
import { createMarksDb } from "./adapters/marks-db";
import { toEtrs, watchStandort } from "./adapters/standort";
import { shippedConfig } from "./config";
import { openKarte } from "./karte/session";
import type { Point, Uebersicht } from "./karte/types";
import { mountScreen } from "./ui/screen";
void main();
async function main(): Promise<void> {
const root = document.querySelector("#app");
if (!(root instanceof HTMLElement)) return;
if (navigator.storage?.persist) void navigator.storage.persist();
registerSW({ immediate: true });
try {
const [uebersicht, standort] = await Promise.all([
loadUebersicht(),
readOpeningStandort(),
]);
const session = await openKarte({
uebersicht,
cache: createCacheStorage(),
marks: createMarksDb(),
kacheln: createKachelFetch(),
config: shippedConfig,
viewport: {
widthPx: window.innerWidth,
heightPx: window.innerHeight,
},
...(standort !== undefined ? { standort } : {}),
online: navigator.onLine,
appOpen: document.visibilityState === "visible",
});
mountScreen(root, session, uebersicht);
bindDevice(session);
} catch {
root.replaceChildren();
const message = document.createElement("p");
message.className = "boot-error";
message.textContent = "Die Karte konnte nicht geöffnet werden.";
root.append(message);
}
}
async function loadUebersicht(): Promise<Uebersicht> {
const response = await fetch("/uebersicht.json");
if (!response.ok) throw new Error("Übersicht fehlt");
return (await response.json()) as Uebersicht;
}
function readOpeningStandort(): Promise<Point | "denied" | undefined> {
if (!navigator.geolocation) return Promise.resolve("denied");
return new Promise((resolve) => {
navigator.geolocation.getCurrentPosition(
(position) => {
resolve(toEtrs(position.coords.longitude, position.coords.latitude));
},
(error) => {
if (error.code === error.TIMEOUT) resolve(undefined);
else resolve("denied");
},
{ maximumAge: 30_000, timeout: 2_000 },
);
});
}
function bindDevice(session: Awaited<ReturnType<typeof openKarte>>): void {
let stopWatch: (() => void) | null = null;
const watch = (open: boolean) => {
stopWatch?.();
stopWatch = null;
if (!open) return;
stopWatch = watchStandort((standort) => session.reportStandort(standort));
};
document.addEventListener("visibilitychange", () => {
const open = document.visibilityState === "visible";
if (!open) {
watch(false);
session.setAppOpen(false);
return;
}
session.setAppOpen(true);
watch(true);
});
window.addEventListener("online", () => session.setOnline(true));
window.addEventListener("offline", () => session.setOnline(false));
watch(document.visibilityState === "visible");
}