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:
+65
-18
@@ -53,12 +53,8 @@ function useZiehenAktualisieren(
|
||||
const [laedt, setzeLaedt] = useState(false)
|
||||
const start = useRef<number | null>(null)
|
||||
|
||||
if (!beiAktualisieren) {
|
||||
return { zug: 0, laedt: false, handler: {} as Record<string, never> }
|
||||
}
|
||||
|
||||
const beenden = () => {
|
||||
if (start.current === null) return
|
||||
if (start.current === null || !beiAktualisieren) return
|
||||
start.current = null
|
||||
if (zug >= PTR_SCHWELLE) {
|
||||
setzeLaedt(true)
|
||||
@@ -71,26 +67,77 @@ function useZiehenAktualisieren(
|
||||
}
|
||||
}
|
||||
|
||||
const starten = (y: number) => {
|
||||
if (laedt) return
|
||||
if ((inhalt.current?.scrollTop ?? 0) > 0) return
|
||||
start.current = y
|
||||
setzeZug(0)
|
||||
}
|
||||
/* Gibt true zurück, wenn die Geste uns gehört — der Aufrufer muss dann
|
||||
preventDefault() rufen. */
|
||||
const bewegen = (y: number): boolean => {
|
||||
if (start.current === null) return false
|
||||
const dy = y - start.current
|
||||
if (dy > 0 && (inhalt.current?.scrollTop ?? 0) <= 0) {
|
||||
// Widerstand wie bei iOS: halbe Strecke, gedeckelt bei 90px.
|
||||
setzeZug(Math.min(90, dy * 0.5))
|
||||
return true
|
||||
}
|
||||
start.current = null
|
||||
setzeZug(0)
|
||||
return false
|
||||
}
|
||||
|
||||
/* Auf dem Finger läuft die Geste über Touch-Ereignisse, nicht über Pointer.
|
||||
Grund: preventDefault() auf einem Pointer-Ereignis verhindert laut
|
||||
Spezifikation kein Scrollen — darüber entscheidet allein touch-action. Auf
|
||||
einem echten Gerät übernimmt der Browser die senkrechte Geste deshalb
|
||||
sofort, schickt pointercancel und stellt pointermove ein; der Zug wurde
|
||||
zurückgesetzt, bevor er die Schwelle erreichte. Am Rechner fällt das nie
|
||||
auf, eine Maus kennt keine Scroll-Übernahme. Der Listener muss von Hand
|
||||
und mit passive:false hängen — React registriert touchmove passiv, dort
|
||||
wäre preventDefault() wirkungslos. */
|
||||
useEffect(() => {
|
||||
const el = inhalt.current
|
||||
// Der Aussteiger sitzt IM Effekt, nicht davor: ein vorgezogenes return
|
||||
// wuerde diesen Hook je nach Bildschirmbreite mal aufrufen und mal nicht
|
||||
// und damit die Hook-Reihenfolge brechen.
|
||||
if (!el || !beiAktualisieren) return undefined
|
||||
const beiStart = (e: TouchEvent) => {
|
||||
if (e.touches.length === 1) starten(e.touches[0]!.clientY)
|
||||
}
|
||||
const beiBewegung = (e: TouchEvent) => {
|
||||
if (e.touches.length !== 1) return
|
||||
if (bewegen(e.touches[0]!.clientY)) e.preventDefault()
|
||||
}
|
||||
el.addEventListener("touchstart", beiStart, { passive: true })
|
||||
el.addEventListener("touchmove", beiBewegung, { passive: false })
|
||||
el.addEventListener("touchend", beenden)
|
||||
el.addEventListener("touchcancel", beenden)
|
||||
return () => {
|
||||
el.removeEventListener("touchstart", beiStart)
|
||||
el.removeEventListener("touchmove", beiBewegung)
|
||||
el.removeEventListener("touchend", beenden)
|
||||
el.removeEventListener("touchcancel", beenden)
|
||||
}
|
||||
})
|
||||
|
||||
if (!beiAktualisieren) {
|
||||
return { zug: 0, laedt: false, handler: {} as Record<string, never> }
|
||||
}
|
||||
|
||||
return {
|
||||
zug,
|
||||
laedt,
|
||||
handler: {
|
||||
// Nur Maus/Stift — der Finger läuft über die Touch-Ereignisse oben.
|
||||
onPointerDown: (e: ReactPointerEvent) => {
|
||||
if (!e.isPrimary || laedt) return
|
||||
if ((inhalt.current?.scrollTop ?? 0) > 0) return
|
||||
start.current = e.clientY
|
||||
setzeZug(0)
|
||||
if (!e.isPrimary || e.pointerType === "touch") return
|
||||
starten(e.clientY)
|
||||
},
|
||||
onPointerMove: (e: ReactPointerEvent) => {
|
||||
if (start.current === null || !e.isPrimary) return
|
||||
const dy = e.clientY - start.current
|
||||
if (dy > 0 && (inhalt.current?.scrollTop ?? 0) <= 0) {
|
||||
// Widerstand wie bei iOS: halbe Strecke, gedeckelt bei 90px.
|
||||
setzeZug(Math.min(90, dy * 0.5))
|
||||
} else {
|
||||
start.current = null
|
||||
setzeZug(0)
|
||||
}
|
||||
if (!e.isPrimary || e.pointerType === "touch") return
|
||||
bewegen(e.clientY)
|
||||
},
|
||||
onPointerUp: beenden,
|
||||
onPointerCancel: beenden,
|
||||
|
||||
@@ -23,6 +23,20 @@ export interface Koordinate {
|
||||
|
||||
const CACHE_SCHLUESSEL = "dm360.geocache"
|
||||
|
||||
/* Arten, bei denen der Eigenname aussagekräftiger ist als die Anschrift
|
||||
(Feld type der Nominatim-Antwort). */
|
||||
const PLATZ_ARTEN = new Set([
|
||||
"parking",
|
||||
"parking_space",
|
||||
"parking_entrance",
|
||||
"bicycle_parking",
|
||||
"motorcycle_parking",
|
||||
"rest_area",
|
||||
"services",
|
||||
"fuel",
|
||||
"charging_station",
|
||||
])
|
||||
|
||||
function cacheLesen(): Record<string, Koordinate | string | null> {
|
||||
try {
|
||||
const roh = localStorage.getItem(CACHE_SCHLUESSEL)
|
||||
@@ -75,13 +89,25 @@ export async function koordinatenAufloesen(lat: number, lon: number): Promise<st
|
||||
{ headers: { "Accept-Language": "de" } },
|
||||
)
|
||||
const daten = (await antwort.json()) as {
|
||||
name?: string
|
||||
type?: string
|
||||
address?: Record<string, string>
|
||||
display_name?: string
|
||||
}
|
||||
const a = daten.address ?? {}
|
||||
const strasse = [a["road"], a["house_number"]].filter(Boolean).join(" ")
|
||||
const ort = [a["postcode"], a["city"] ?? a["town"] ?? a["village"]].filter(Boolean).join(" ")
|
||||
const adresse = [strasse, ort].filter(Boolean).join(", ") || daten.display_name || null
|
||||
/* Steht das Auto auf einem benannten Platz — Parkplatz, Parkhaus,
|
||||
Rastplatz, Ladesäule —, sagt dessen Name mehr als die nächstgelegene
|
||||
Hausnummer: "Parkhaus Westentor" statt "Rathausstraße 4". Nominatim
|
||||
liefert ihn im Feld name, zusammen mit der Art der getroffenen Fläche;
|
||||
ohne die Prüfung auf diese Art würde auch jedes beliebige Gebäude, das
|
||||
zufällig einen Namen trägt, die Anschrift verdrängen. Der Ort bleibt
|
||||
angehängt, damit erkennbar ist, wo der Platz liegt. */
|
||||
const platz = PLATZ_ARTEN.has(daten.type ?? "") ? (daten.name ?? "").trim() : ""
|
||||
const adresse = platz
|
||||
? [platz, ort].filter(Boolean).join(", ")
|
||||
: [strasse, ort].filter(Boolean).join(", ") || daten.display_name || null
|
||||
// Nur echte Treffer merken: eine gedrosselte Antwort ist kein "hier gibt
|
||||
// es keine Adresse", die soll beim nächsten Mal erneut versucht werden.
|
||||
if (adresse) adresseMerken(schluessel, adresse)
|
||||
|
||||
Reference in New Issue
Block a user