Ziehen zum Aktualisieren, Nachtmodus-Zeilen, Parkplatznamen (2026.8.30.10)

Ziehen zum Aktualisieren funktionierte auf keinem Geraet, in keiner der
beiden Anwendungen - und der Grund entwertet zugleich, wie es bisher geprueft
wurde. Beide trieben die Geste ueber pointermove und riefen dort
preventDefault(). Das verhindert laut Spezifikation kein Scrollen; darueber
entscheidet allein touch-action. Auf echtem Touch uebernimmt der Browser die
senkrechte Geste, schickt pointercancel und stellt pointermove ein - der Zug
wird zurueckgesetzt, lange bevor er die Schwelle erreicht. Eine Maus kennt
diese Uebernahme nicht, synthetische PointerEvents ebenso wenig: genau
deshalb lief die Geste 2026-08-17 im Test sauber durch und am Geraet nie.

Jetzt laeuft sie auf Touch-Ereignissen, touchmove zwingend mit passive:false
(sonst ist preventDefault() wirkungslos); der Zeigerweg bleibt fuer Maus und
Stift und ignoriert pointerType "touch". Mit echten TouchEvents geprueft,
inklusive defaultPrevented - dem einzigen Signal, das belegt, dass das native
Ueberscrollen wirklich unterbunden ist.

Der zuletzt nicht nachstellbare Befund war nachtmodus-spezifisch: --tile ist
dort ein durchscheinender Schleier, und die Wischzeilen malten ihn ein
zweites Mal auf die Kachel, auf der sie ohnehin liegen. Am Tag sind beide
deckend weiss, deshalb war nichts zu sehen. Das Panel nutzt an dieser Stelle
--tile-deckend; dieses Token fehlte im Design-System und ist jetzt da.

Ausserdem: steht das Auto auf einem benannten Platz, zeigen beide dessen
Namen statt der naechsten Hausnummer - Nominatim liefert ihn mit, die
Pruefung auf die Art der Flaeche verhindert, dass irgendein benanntes
Gebaeude die Anschrift verdraengt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-30 23:22:52 +02:00
parent a08922e9a0
commit 0fd81df700
9 changed files with 224 additions and 45 deletions
@@ -1 +1 @@
{"version":"2026.8.30.9","sha256":"63b67b2116bdc2970ebcb9c02dc8c2d1ec1f0156fd70c59f88db101307054474","bytes":256695,"gebaut":"2026-08-30T21:09:06Z"}
{"version":"2026.8.30.10","sha256":"0a9cdbbccf4e91b07152699e0331bf75ff62908832880e6c2408981b76347710","bytes":257008,"gebaut":"2026-08-30T21:20:20Z"}
@@ -786,6 +786,10 @@ function standortErfassenFallsNoetig() {
dokumentierte Leaflet-über-CDN-Vorgehen (siehe AGENTS.md). Cache-Schlüssel
auf 3 Nachkommastellen (~110 m) gerundet, damit ein erneutes Öffnen an
derselben Stelle keinen zweiten Abruf auslöst. */
/* Arten, bei denen der Eigenname aussagekraeftiger ist als die Anschrift. */
const PLATZ_ARTEN = new Set(["parking", "parking_space", "parking_entrance",
"bicycle_parking", "motorcycle_parking", "rest_area", "services", "fuel",
"charging_station"]);
async function standortAdresseAufloesen(lat, lon) {
const key = lat.toFixed(3) + "," + lon.toFixed(3);
if (STANDORT_ADRESSE_KEY === key) return STANDORT_ADRESSE;
@@ -808,7 +812,16 @@ async function standortAdresseAufloesen(lat, lon) {
const daten = await res.json();
const a = daten.address || {};
const strasse = [a.road, a.house_number].filter(Boolean).join(" ");
STANDORT_ADRESSE = [strasse, [a.postcode, a.city || a.town || a.village].filter(Boolean).join(" ")].filter(Boolean).join(", ") || daten.display_name || "";
const ort = [a.postcode, a.city || a.town || a.village].filter(Boolean).join(" ");
/* Steht das Auto auf einem benannten Platz - Parkplatz, Parkhaus,
Rastplatz -, sagt dessen Name mehr als die naechstgelegene Hausnummer.
Nominatim liefert ihn im Feld name; die Pruefung auf die Art der
Flaeche verhindert, dass jedes zufaellig benannte Gebaeude die
Anschrift verdraengt. */
const platz = PLATZ_ARTEN.has(daten.type) ? (daten.name || "").trim() : "";
STANDORT_ADRESSE = platz
? [platz, ort].filter(Boolean).join(", ")
: [strasse, ort].filter(Boolean).join(", ") || daten.display_name || "";
} catch (e) {
// Den Schluessel bewusst NICHT setzen: sonst gilt ein einzelner
// fehlgeschlagener Abruf (Rate-Limit, kurzer Netzausfall) fuer diese Zelle
@@ -4803,32 +4816,16 @@ function ereignisseVerdrahten() {
ROOT.addEventListener("pointerdown", (e) => {
// Ziehen zum Aktualisieren (nur Übersicht, nur wenn ganz oben gescrollt) -
// unabhängig von der Wischgeste unten, da hier keine .swipe-wrap nötig ist.
if (e.isPrimary && route.name === "home" && !ptrLaedt) {
const ansicht = $("#view");
if (ansicht && ansicht.scrollTop <= 0) { ptrY0 = e.clientY; ptrDrag = 0; }
}
// Nur Maus/Stift. Der Finger laeuft ueber die Touch-Ereignisse weiter
// unten, weil nur dort preventDefault() das native Ueberscrollen wirklich
// verhindert - siehe ptrBewegen().
if (e.isPrimary && e.pointerType !== "touch") ptrStarten(e.clientY);
const wrap = e.target.closest(".swipe-wrap");
if (!wrap || !e.isPrimary) return;
swipeAktivWrap = wrap; swipeStartX = e.clientX; swipeStartY = e.clientY; swipeGeste = null; swipeDX = 0;
});
ROOT.addEventListener("pointermove", (e) => {
if (ptrY0 !== null && e.isPrimary) {
const ansicht = $("#view"), dy = e.clientY - ptrY0;
if (dy > 0 && ansicht && ansicht.scrollTop <= 0) {
e.preventDefault();
ptrDrag = Math.min(90, dy * 0.5); // Widerstand, wie bei iOS
const ptr = $("#ptr");
if (ptr) {
ptr.style.transition = "none";
ptr.style.height = ptrDrag + "px";
ptr.querySelector("span").textContent = ptrDrag >= 56 ? "Loslassen zum Aktualisieren" : "Ziehen zum Aktualisieren";
}
} else {
ptrY0 = null; ptrDrag = 0;
const ptr = $("#ptr");
if (ptr) { ptr.style.transition = ""; ptr.style.height = "0"; }
}
}
if (e.pointerType !== "touch") ptrBewegen(e.clientY);
if (!swipeAktivWrap || !e.isPrimary) return;
const dx = e.clientX - swipeStartX, dy = e.clientY - swipeStartY;
if (swipeGeste === null) {
@@ -4863,6 +4860,39 @@ function ereignisseVerdrahten() {
// auf den gerade erst aufgedeckten Löschen-Knopf) fälschlich verschluckt.
if (swipeWarGezogen) setTimeout(() => { swipeWarGezogen = false; }, 0);
}
function ptrStarten(y) {
if (route.name !== "home" || ptrLaedt) return;
const ansicht = $("#view");
if (ansicht && ansicht.scrollTop <= 0) { ptrY0 = y; ptrDrag = 0; }
}
/* Gibt true zurueck, wenn die Geste uns gehoert - der Aufrufer muss dann
preventDefault() rufen. Warum das ueber Touch-Ereignisse laufen muss:
preventDefault() auf einem Pointer-Ereignis verhindert laut Spezifikation
KEIN Scrollen; darueber entscheidet allein touch-action. Auf einem echten
Touch-Geraet uebernimmt der Browser die senkrechte Geste deshalb sofort,
schickt pointercancel und stellt pointermove ein - die Geste wurde
zurueckgesetzt, bevor sie die 56px erreichen konnte. Am Rechner faellt das
nie auf, weil eine Maus keine Scroll-Uebernahme kennt; auch synthetische
PointerEvents durchlaufen sie nicht. Auf touchmove dagegen wirkt
preventDefault() und haelt das native Ueberscrollen an. */
function ptrBewegen(y) {
if (ptrY0 === null) return false;
const ansicht = $("#view"), dy = y - ptrY0;
if (dy > 0 && ansicht && ansicht.scrollTop <= 0) {
ptrDrag = Math.min(90, dy * 0.5); // Widerstand, wie bei iOS
const ptr = $("#ptr");
if (ptr) {
ptr.style.transition = "none";
ptr.style.height = ptrDrag + "px";
ptr.querySelector("span").textContent = ptrDrag >= 56 ? "Loslassen zum Aktualisieren" : "Ziehen zum Aktualisieren";
}
return true;
}
ptrY0 = null; ptrDrag = 0;
const ptr = $("#ptr");
if (ptr) { ptr.style.transition = ""; ptr.style.height = "0"; }
return false;
}
function ptrBeenden() {
if (ptrY0 === null) return;
ptrY0 = null;
@@ -4886,6 +4916,19 @@ function ereignisseVerdrahten() {
ROOT.addEventListener("pointerup", (e) => { swipeBeenden(); ptrBeenden(); });
ROOT.addEventListener("pointercancel", (e) => { swipeBeenden(); ptrBeenden(); });
// Ziehen zum Aktualisieren auf dem Finger: eigener Weg ueber Touch-Ereignisse
// (Begruendung in ptrBewegen()). passive:false ist zwingend - ohne das ist
// preventDefault() auf touchmove wirkungslos und der Browser scrollt doch.
ROOT.addEventListener("touchstart", (e) => {
if (e.touches.length === 1) ptrStarten(e.touches[0].clientY);
}, { passive: true });
ROOT.addEventListener("touchmove", (e) => {
if (e.touches.length !== 1) return;
if (ptrBewegen(e.touches[0].clientY)) e.preventDefault();
}, { passive: false });
ROOT.addEventListener("touchend", () => ptrBeenden());
ROOT.addEventListener("touchcancel", () => ptrBeenden());
// Batteriespannungs-Verlauf: Pinch-Zoom (2 Finger) / Ziehen zum Verschieben
// (1 Finger) / Mausrad-Zoom, per pointerId in bvZeiger nachverfolgt statt
// über die swipe*-Variablen oben, die für horizontales Wischen in Listen
@@ -1,7 +1,7 @@
{
"domain": "audi_dashboard",
"name": "Audi Dashboard",
"version": "2026.8.30.9",
"version": "2026.8.30.10",
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
"codeowners": ["@paul"],