widgetAnmelden({ id, titel, wo, sichtbar, laden, leer, inhalt, takt }) —
der Rahmen macht daraus ein Fenster in der Spalte (Terminal) oder ein
Schild (Desktop), kennt lädt/fertig/nichts da/Fehler und haelt den
Nachladetakt. Benutzer, Wetter und Systemzustand sind umgestellt; aus
index.html sind vier feste Kaesten verschwunden und aus dem Raster die
Einzelplaetze.
Zwei Dinge, die erst dabei sichtbar wurden:
- Der Server meldete 'kein Ort eingestellt' und 'Dienst nicht
erreichbar' beide als 204. Ein Ausfall sah damit aus wie ein
abgeschaltetes Widget. Ausfall ist jetzt 502, mit Test.
- Die Reihenfolge haengt nicht mehr an der Modulauswertung: Seit
glyphs.js seine Raster mit await auf oberster Ebene laedt, laufen
Module ohne Symbole vorher durch — der Systemzustand stand deshalb
ueber dem Benutzer. widgetsOrdnen() macht die Reihenfolge zur Angabe,
und genau dort haengt spaeter die im Server gespeicherte ein.
152 lines
5.3 KiB
JavaScript
152 lines
5.3 KiB
JavaScript
/* ── Der Widget-Rahmen ─────────────────────────────────────────────────────
|
|
*
|
|
* Ein Widget ist hier eine **Beschreibung**, kein Stück HTML im Dokument.
|
|
* Vorher standen Benutzer und Wetter fest in index.html, jedes mit eigener
|
|
* Zeigen-und-Verstecken-Logik in seiner Datei — beim dritten Widget hätte man
|
|
* dieselben zwanzig Zeilen ein drittes Mal geschrieben.
|
|
*
|
|
* Der Rahmen kümmert sich um das, was jedes Widget braucht und keines für sich
|
|
* lösen sollte:
|
|
*
|
|
* · beide Betriebsarten — Fenster im Terminal, Schild auf dem Schreibtisch
|
|
* · die vier Zustände: lädt · fertig · nichts da · Fehler
|
|
* · Reihenfolge und Nachladen im Takt
|
|
*
|
|
* Ein Widget beschreibt sich so:
|
|
*
|
|
* {
|
|
* id, titel,
|
|
* wo: ["terminal", "desktop"] // wo es erscheinen darf
|
|
* sichtbar: () => true // Schalter aus → gar nicht erst da
|
|
* laden: async () => false|void // false = es gibt nichts zu zeigen
|
|
* leer: "verbergen" | "Text" // was dann passiert
|
|
* inhalt: () => "<html>"
|
|
* takt: 600000 // ms, optional
|
|
* }
|
|
*
|
|
* Absichtlich offen gelassen: eine dritte Form. Die Widgets sollen später auch
|
|
* als Aufklappfeld in der HoneyBar leben können — deshalb erzeugt `inhalt()`
|
|
* nur den Kern, und die Verpackung entsteht hier.
|
|
*/
|
|
import { $, state } from "../core.js";
|
|
|
|
const REGISTER = [];
|
|
/** id → "laedt" | "fertig" | "leer" | "fehler" */
|
|
const zustand = {};
|
|
const uhren = {};
|
|
|
|
export function widgetAnmelden(def) {
|
|
REGISTER.push(def);
|
|
zustand[def.id] = def.laden ? "laedt" : "fertig";
|
|
}
|
|
|
|
/** Nur zum Nachsehen von außen — die Liste selbst gehört dem Rahmen. */
|
|
export function widgetListe() {
|
|
return REGISTER.map((w) => w.id);
|
|
}
|
|
|
|
/**
|
|
* Legt die Reihenfolge fest. Was nicht genannt ist, hängt sich hinten an.
|
|
*
|
|
* Warum überhaupt? Weil die Reihenfolge sonst davon abhinge, in welcher
|
|
* Reihenfolge die Module ausgewertet werden — und das ist **nicht** die
|
|
* Reihenfolge der Importzeilen: `glyphs.js` lädt seine Raster mit `await` auf
|
|
* oberster Ebene, also warten alle Module, die es brauchen, während die
|
|
* anderen schon durchlaufen. Der Systemzustand braucht keine Symbole und stand
|
|
* deshalb vor dem Benutzer, obwohl er darunter importiert wird.
|
|
*
|
|
* Hier ist die Reihenfolge damit eine Angabe statt einer Nebenwirkung — und
|
|
* genau die Stelle, an der später die im Server gespeicherte Reihenfolge
|
|
* einhaken kann.
|
|
*/
|
|
export function widgetsOrdnen(ids) {
|
|
const platz = (w) => {
|
|
const i = ids.indexOf(w.id);
|
|
return i < 0 ? ids.length : i;
|
|
};
|
|
REGISTER.sort((a, b) => platz(a) - platz(b));
|
|
}
|
|
|
|
/**
|
|
* Lädt ein Widget (oder alle) und zeichnet neu.
|
|
*
|
|
* Fehler bleiben hier: Ein Widget, das seinen Server nicht erreicht, sagt das
|
|
* in seinem eigenen Rahmen — es darf weder den Schreibtisch aufhalten noch die
|
|
* anderen Widgets mitreißen.
|
|
*/
|
|
export async function widgetsLaden(nurId) {
|
|
const dran = REGISTER.filter((w) => w.laden && (!nurId || w.id === nurId));
|
|
await Promise.all(
|
|
dran.map(async (w) => {
|
|
zustand[w.id] = "laedt";
|
|
widgetsZeichnen();
|
|
try {
|
|
zustand[w.id] = (await w.laden()) === false ? "leer" : "fertig";
|
|
} catch {
|
|
zustand[w.id] = "fehler";
|
|
}
|
|
})
|
|
);
|
|
widgetsZeichnen();
|
|
}
|
|
|
|
/** Startet die Nachlade-Takte. Einmal beim Aufbau des Schreibtischs. */
|
|
export function widgetsStarten() {
|
|
REGISTER.forEach((w) => {
|
|
if (!w.takt || uhren[w.id]) return;
|
|
uhren[w.id] = setInterval(() => widgetsLaden(w.id), w.takt);
|
|
});
|
|
}
|
|
|
|
function zustandHtml(w) {
|
|
switch (zustand[w.id]) {
|
|
case "laedt":
|
|
return `<p class="w-zustand">lädt …</p>`;
|
|
case "fehler":
|
|
return `<p class="w-zustand w-fehler">nicht erreichbar</p>`;
|
|
case "leer":
|
|
return `<p class="w-zustand">${w.leer}</p>`;
|
|
default:
|
|
return w.inhalt();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Zeichnet alle Widgets in die Ansicht, die gerade gilt.
|
|
*
|
|
* Beide Container werden bei jedem Durchgang neu gefüllt statt einzeln
|
|
* nachgeführt: Bei einer Handvoll Widgets ist das billiger als Buchführung
|
|
* darüber, was sich geändert hat — und es kann nicht auseinanderlaufen.
|
|
*/
|
|
export function widgetsZeichnen() {
|
|
const desktop = state.settings.mode === "desktop";
|
|
const modus = desktop ? "desktop" : "terminal";
|
|
const spalte = $("widgetSpalte");
|
|
const schilder = $("deskwidgets");
|
|
if (!spalte || !schilder) return;
|
|
|
|
const dran = REGISTER.filter((w) => {
|
|
if (w.wo && !w.wo.includes(modus)) return false;
|
|
if (w.sichtbar && !w.sichtbar()) return false;
|
|
// „nichts da" heißt bei manchen Widgets: gar nicht erst hinstellen. Eine
|
|
// leere Wetterkachel wäre schlechter als keine.
|
|
return !(zustand[w.id] === "leer" && w.leer === "verbergen");
|
|
});
|
|
|
|
spalte.innerHTML = desktop
|
|
? ""
|
|
: dran
|
|
.map(
|
|
(w) => `
|
|
<section class="win win-widget" data-widget="${w.id}">
|
|
<div class="win-bar"><span class="win-title">${w.titel}</span><span class="win-dots">●●●</span></div>
|
|
<div class="win-body">${zustandHtml(w)}</div>
|
|
</section>`
|
|
)
|
|
.join("");
|
|
|
|
schilder.innerHTML = desktop
|
|
? dran.map((w) => `<div class="deskwidget" data-widget="${w.id}">${zustandHtml(w)}</div>`).join("")
|
|
: "";
|
|
}
|