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 { 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 { const response = await fetch("/uebersicht.json"); if (!response.ok) throw new Error("Übersicht fehlt"); return (await response.json()) as Uebersicht; } function readOpeningStandort(): Promise { 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>): 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"); }