Erste Produktivversion

This commit is contained in:
2026-08-08 20:31:58 +02:00
parent d8ded2816d
commit 7b21e2d1b4
110 changed files with 18170 additions and 644 deletions

51
web/html/js/store.js Normal file
View File

@@ -0,0 +1,51 @@
// Ein einziger Zustandsspeicher. Ansichten lesen daraus und zeichnen sich
// neu, wenn er sich ändert - niemand fasst fremde DOM-Knoten an.
//
// Das ist im Kern das, was ein Framework auch tut. Der Unterschied ist,
// dass hier nichts passiert, was nicht in dieser Datei steht.
"use strict";
const listeners = new Set();
export const state = {
/** Aus /api/config, mit Rückfallwert für den ersten Start ohne Netz. */
appName: "Einkaufsliste",
user: null,
lists: [],
/** Ansicht der geöffneten Liste, so wie /view sie liefert. */
view: null,
markets: [],
categories: [],
online: navigator.onLine,
/** "ok" | "offline" | "error" */
syncState: navigator.onLine ? "ok" : "offline",
/** Einmalige Meldung aus der Synchronisation, wird von der Ansicht
* abgeholt und zurückgesetzt. */
syncProblem: null,
lastSync: null,
};
export function subscribe(fn) {
listeners.add(fn);
return () => listeners.delete(fn);
}
/** Nach jeder Zustandsänderung genau einmal aufrufen. Mehrere Aufrufe im
* selben Tick werden zusammengefasst, damit nicht mehrfach gezeichnet wird. */
let scheduled = false;
export function notify() {
if (scheduled) return;
scheduled = true;
queueMicrotask(() => {
scheduled = false;
for (const fn of listeners) fn(state);
});
}
export function set(patch) {
Object.assign(state, patch);
notify();
}
window.addEventListener("online", () => set({ online: true }));
window.addEventListener("offline", () => set({ online: false }));