First version of Karten App
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import type { CachePort } from "../karte/types";
|
||||
|
||||
export const kachelCacheName = "kacheln";
|
||||
|
||||
export function kachelUrl(id: string): string {
|
||||
return `/kacheln/${id}.png`;
|
||||
}
|
||||
|
||||
function idFromPathname(pathname: string): string | null {
|
||||
const prefix = "/kacheln/";
|
||||
const suffix = ".png";
|
||||
if (!pathname.startsWith(prefix) || !pathname.endsWith(suffix)) return null;
|
||||
const id = pathname.slice(prefix.length, -suffix.length);
|
||||
return id.length > 0 ? id : null;
|
||||
}
|
||||
|
||||
export function createCacheStorage(): CachePort {
|
||||
return {
|
||||
async ids() {
|
||||
const cache = await caches.open(kachelCacheName);
|
||||
const requests = await cache.keys();
|
||||
const ids: string[] = [];
|
||||
for (const request of requests) {
|
||||
const id = idFromPathname(new URL(request.url).pathname);
|
||||
if (id) ids.push(id);
|
||||
}
|
||||
return ids;
|
||||
},
|
||||
|
||||
async put(id, bytes) {
|
||||
const cache = await caches.open(kachelCacheName);
|
||||
const body = new Uint8Array(bytes);
|
||||
await cache.put(
|
||||
kachelUrl(id),
|
||||
new Response(body, {
|
||||
headers: { "Content-Type": "image/png" },
|
||||
}),
|
||||
);
|
||||
},
|
||||
|
||||
async delete(ids) {
|
||||
const cache = await caches.open(kachelCacheName);
|
||||
await Promise.all(ids.map((id) => cache.delete(kachelUrl(id))));
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { KachelPort } from "../karte/types";
|
||||
import { kachelUrl } from "./cache-storage";
|
||||
|
||||
export function createKachelFetch(): KachelPort {
|
||||
return {
|
||||
async fetch(id, signal) {
|
||||
const response = await fetch(kachelUrl(id), { signal, cache: "no-store" });
|
||||
if (!response.ok) throw new Error(`Kachel ${id} konnte nicht geladen werden.`);
|
||||
return new Uint8Array(await response.arrayBuffer());
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import type { MarksPort } from "../karte/types";
|
||||
|
||||
const DB_NAME = "karte";
|
||||
const STORE = "marks";
|
||||
const KEY = "heruntergeladen";
|
||||
|
||||
function openDatabase(): Promise<IDBDatabase> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(DB_NAME, 1);
|
||||
request.onupgradeneeded = () => {
|
||||
const db = request.result;
|
||||
if (!db.objectStoreNames.contains(STORE)) db.createObjectStore(STORE);
|
||||
};
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error ?? new Error("IndexedDB"));
|
||||
});
|
||||
}
|
||||
|
||||
function stringsFrom(value: unknown): string[] {
|
||||
if (!Array.isArray(value)) return [];
|
||||
return value.filter((item): item is string => typeof item === "string");
|
||||
}
|
||||
|
||||
function settle(tx: IDBTransaction, value: () => unknown): Promise<unknown> {
|
||||
return new Promise((resolve, reject) => {
|
||||
tx.oncomplete = () => resolve(value());
|
||||
tx.onerror = () => reject(tx.error ?? new Error("IndexedDB"));
|
||||
tx.onabort = () => reject(tx.error ?? new Error("IndexedDB"));
|
||||
});
|
||||
}
|
||||
|
||||
export function createMarksDb(): MarksPort {
|
||||
return {
|
||||
async read() {
|
||||
const db = await openDatabase();
|
||||
try {
|
||||
const tx = db.transaction(STORE, "readonly");
|
||||
const request = tx.objectStore(STORE).get(KEY);
|
||||
const value = await settle(tx, () => request.result);
|
||||
return { heruntergeladen: stringsFrom(value) };
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
},
|
||||
|
||||
async write(state) {
|
||||
const db = await openDatabase();
|
||||
try {
|
||||
const tx = db.transaction(STORE, "readwrite");
|
||||
tx.objectStore(STORE).put(state.heruntergeladen, KEY);
|
||||
await settle(tx, () => undefined);
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import proj4 from "proj4";
|
||||
import type { Point } from "../karte/types";
|
||||
|
||||
const ETRS25832 =
|
||||
"+proj=utm +zone=32 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs";
|
||||
|
||||
proj4.defs("EPSG:25832", ETRS25832);
|
||||
|
||||
export function toEtrs(lon: number, lat: number): Point {
|
||||
const projected = proj4("EPSG:4326", "EPSG:25832", [lon, lat]);
|
||||
return { easting: projected[0], northing: projected[1] };
|
||||
}
|
||||
|
||||
export function watchStandort(
|
||||
onStandort: (standort: Point | "denied") => void,
|
||||
): () => void {
|
||||
if (!navigator.geolocation) {
|
||||
onStandort("denied");
|
||||
return () => {};
|
||||
}
|
||||
|
||||
const id = navigator.geolocation.watchPosition(
|
||||
(position) => {
|
||||
onStandort(toEtrs(position.coords.longitude, position.coords.latitude));
|
||||
},
|
||||
(error) => {
|
||||
if (error.code === error.PERMISSION_DENIED) onStandort("denied");
|
||||
},
|
||||
{ enableHighAccuracy: true },
|
||||
);
|
||||
|
||||
return () => navigator.geolocation.clearWatch(id);
|
||||
}
|
||||
Reference in New Issue
Block a user